From 012afea5ef2be77995de248c9227c004865e8425 Mon Sep 17 00:00:00 2001 From: Jenny Date: Mon, 22 Jun 2026 22:40:38 +0800 Subject: [PATCH] fix(events): align event timing with Luma EDT start/end instants - Use exact startsAt timestamp for upcoming visibility instead of UTC date string - Add endsAt field to CortexEvent (CONNEX: 9 AM - 9 PM EDT) - Show three countdown states: ticking clock, "Happening now", "Event ended" - Add countdown and upcoming test coverage --- skills/context/architecture-context.md | 2 +- skills/context/progress-tracker.md | 5 +- .../header/header-upcoming-event-link.tsx | 2 +- src/components/sections/events-section.tsx | 8 +- .../sections/events/event-countdown.tsx | 33 ++++-- src/lib/content/events.ts | 10 +- src/lib/events/countdown.test.ts | 111 ++++++++++++++++++ src/lib/events/countdown.ts | 43 +++---- src/lib/events/upcoming.test.ts | 97 +++++++++++++++ src/lib/events/upcoming.ts | 13 +- 10 files changed, 280 insertions(+), 44 deletions(-) create mode 100644 src/lib/events/countdown.test.ts create mode 100644 src/lib/events/upcoming.test.ts diff --git a/skills/context/architecture-context.md b/skills/context/architecture-context.md index c2dc94c..9d33b88 100644 --- a/skills/context/architecture-context.md +++ b/skills/context/architecture-context.md @@ -111,7 +111,7 @@ Recommended pattern: - no CMS or external content source yet - revisit content source later based on team workflow and editing needs -The root layout and homepage route export `revalidate = EVENTS_DATE_REVALIDATE_SECONDS` (3600) from `src/lib/events/upcoming.ts` so date-aware event UI—`HeaderUpcomingEvent` in the site header and `EventsSection` on the homepage—can re-run filtering after event dates pass without a manual rebuild. Event content is still hardcoded TypeScript, so hourly revalidation only refreshes which events are shown—not content from an external source. Events whose `date` is earlier than the current UTC `YYYY-MM-DD` are hidden from the Upcoming Events list; when no upcoming events remain, the Events section renders only its follow-up bridge copy. +The root layout and homepage route export `revalidate = EVENTS_DATE_REVALIDATE_SECONDS` (3600) from `src/lib/events/upcoming.ts` so date-aware event UI—`HeaderUpcomingEvent` in the site header and `EventsSection` on the homepage—can re-run filtering after event dates pass without a manual rebuild. Event content is still hardcoded TypeScript, so hourly revalidation only refreshes which events are shown—not content from an external source. Each event keeps two machine-readable instants sourced from Luma as UTC: `startsAt` and `endsAt`. Upcoming-event visibility filters by the exact `startsAt` timestamp (events disappear once their start instant passes). The client-side `EventCountdown` ticks every second and shows three states: a countdown clock before start, "Happening now" between `startsAt` and `endsAt`, and "Event ended" after `endsAt`. `dateLabel` remains display copy. ## Observability diff --git a/skills/context/progress-tracker.md b/skills/context/progress-tracker.md index e706699..bdd036d 100644 --- a/skills/context/progress-tracker.md +++ b/skills/context/progress-tracker.md @@ -101,5 +101,6 @@ All seven homepage sections are built, wired, and animated (scroll-driven entran - **Rate-limit IP hardening** — Parses `x-vercel-forwarded-for`, `Forwarded`, IPv4-with-port, bracketed IPv6; **skips loopback** in production-like runtimes. Unknown IP returns `null` identifier; **fail closed** in production (no shared `local` bucket). - **Footer hash nav** — `hash-navigation.ts` keeps `#footer` while footer is visible and while focus is in the newsletter/contact region (prevents demotion to `#events` during footer interaction). - **Resend re-subscribe** — Reactivates unsubscribed contacts only; reactivated members get success message, not already-submitted. -- **Tests** — `submit.test.ts`, `turnstile.test.ts`, `rate-limit.test.ts`, `resend.test.ts`. -- **Verified** — `bun run test`, `bun run typecheck`, `bun run build`. Localhost `#footer` Turnstile submit confirmed against Resend. +- **Event timing source** — Each event carries two Luma UTC instants: `startsAt` and `endsAt`. Upcoming-event visibility (`getUpcomingEvents`) filters by exact `startsAt` timestamp — events are removed once their start instant passes. The client-side `EventCountdown` uses `getEventTimingState` (discriminated union: `countdown` | `live` | `ended`) to show a ticking clock before start, "Happening now" between start and end, and "Event ended" after `endsAt`. CONNEX: `startsAt` `2026-06-27T13:00:00Z` (9 AM EDT), `endsAt` `2026-06-28T01:00:00Z` (9 PM EDT). +- **Tests** — `submit.test.ts`, `turnstile.test.ts`, `rate-limit.test.ts`, `resend.test.ts`, `countdown.test.ts`, `upcoming.test.ts`. +- **Verified** — `bun run test`, `bun run typecheck`, `bun run lint` (existing Monad `_section` warning only), `bun run build`. Localhost `#footer` Turnstile submit confirmed against Resend. diff --git a/src/components/layout/header/header-upcoming-event-link.tsx b/src/components/layout/header/header-upcoming-event-link.tsx index 7b2232c..22c5d3b 100644 --- a/src/components/layout/header/header-upcoming-event-link.tsx +++ b/src/components/layout/header/header-upcoming-event-link.tsx @@ -24,7 +24,7 @@ export function HeaderUpcomingEventLink({ {event.label} -