Skip to content

feat(events): support scheduling public events in advance#429

Open
jakehobbs wants to merge 13 commits into
mainfrom
jake/events-in-advance
Open

feat(events): support scheduling public events in advance#429
jakehobbs wants to merge 13 commits into
mainfrom
jake/events-in-advance

Conversation

@jakehobbs
Copy link
Copy Markdown
Member

@jakehobbs jakehobbs commented May 31, 2026

Adds a richer event model so events can be created ahead of time — start/end time, IANA timezone, description, online flag, public flag, and a deduped Google Place location (new locations table) — surfaced in the event form via a scheduled-event flow (Places address autocomplete + timezone-aware time fields) that appears when "Public event" is checked. After creating a public event the user now lands on a confirmation page summarizing the event with next-step choices (take attendance, edit, create another, done) instead of being dropped on the attendance page. Also introduces a Home hub listing today's events, repoints the post-login redirect and nav logo to /home, and serves a referrer-restricted Google Places key through the authed user-info endpoint. Backend changes include the migration, event model read/write of the new fields with a LEFT JOIN locations, and wipe/seed/Makefile housekeeping.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added event confirmation interface with event details and navigation options.
    • Introduced home dashboard displaying today's events with real-time "Happening now" status.
    • Enabled advanced event scheduling with date, time, and timezone support.
    • Added location selection for events using Google Places integration.
    • Implemented collapsible event details summary for easier event editing.
    • Updated event creation workflow with improved form organization and field gating.
  • Documentation

    • Updated setup instructions for Google Places API key configuration.

Add a richer event model so events can be created ahead of time:
start/end time, IANA timezone, description, online flag, public flag, and
a deduped Google Place location (new `locations` table). The event form
expands into a scheduled-event flow when "Public event" is checked, with
a Google Places address autocomplete and timezone-aware time fields.

After creating a public event, the user lands on a confirmation page
(name/date/time/location summary) offering "Take attendance now", "Edit
event", "Create another event", and "Done" instead of being dropped on
the attendance page. Adds a new Home hub listing today's events, points
the post-login redirect and nav logo at /home, and serves a
referrer-restricted Google Places key via the authed user-info endpoint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jakehobbs jakehobbs requested a review from alexsapps as a code owner May 31, 2026 00:02
Copilot AI review requested due to automatic review settings May 31, 2026 00:02
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Review Change Stack

Warning

Review limit reached

@jakehobbs, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 27 minutes and 34 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a0ce9d0d-d580-4547-8656-f40a8e3a065e

📥 Commits

Reviewing files that changed from the base of the PR and between 0b56cca and d3b3ba9.

📒 Files selected for processing (9)
  • .npmrc
  • Makefile
  • frontend-v2/.npmrc
  • frontend-v2/src/app/(authed)/events/event-form-schema.ts
  • frontend-v2/src/app/(authed)/events/event-form-sections.tsx
  • frontend-v2/src/app/(authed)/events/useEventForm.ts
  • pkg/shared/db-migrations/20260604120000_event_manual_location.down.sql
  • pkg/shared/db-migrations/20260604120000_event_manual_location.up.sql
  • server/src/model/event.go
📝 Walkthrough

Walkthrough

This PR implements scheduled public events with Google Places location integration, timezone-aware event discovery, and a confirmation flow. Changes span database schema (new locations table, extended events), server-side event model and validation, updated API contracts, frontend session/context wiring, timezone utilities, TimeField and PlacesAutocomplete components, refactored event form with schema-driven validation, and a new HomeHub discovery page.

Changes

Scheduled Public Events Feature

Layer / File(s) Summary
Development tooling, database schema, and wipe
Makefile, cli/cmd/db.go, pkg/shared/db-migrations/20260530120000_advance_events_and_locations.*, pkg/shared/wipe.go
Makefile run exports debug envs and optional .env; seed-data SQL uses explicit column list; up-migration creates locations table with deduplication and extends events with advance-event columns and FK; down-migration reverses; wipe drops locations after events.
Server event model and persistence
server/src/model/event.go
EventJSON and LocationJSON types extended; Event DB struct adds LocationID, IsOnline, Description, StartTime, EndTime, Timezone, IsPublic, and resolved location fields; getEvents performs LEFT JOIN locations; insertEvent/updateEvent set new columns; CleanEventData validates/normalizes time/timezone, upserts locations, enforces public-event rules.
Backend API contracts and handlers
server/src/main.go, server/src/config/config.go, frontend-v2/src/lib/api.ts
HomeHandler redirects to /v2/home; AuthedUserInfoHandler includes googlePlacesApiKey; API schemas updated with googlePlacesApiKey in user response, advance-event fields in event GET/list, and location submission in SaveEventParams; config comment clarifies referrer-restricted key usage.
Frontend session, context, and layout wiring
frontend-v2/src/app/session.ts, frontend-v2/src/app/authed-page-provider.tsx, frontend-v2/src/app/(authed)/layout.tsx
fetchSession returns googlePlacesApiKey from user data; AuthedPageContext extended with googlePlacesApiKey; layout passes the key to AuthedPageProvider.
Timezone utilities, TimeField, and PlacesAutocomplete
frontend-v2/src/lib/time.ts, frontend-v2/src/components/ui/time-field.tsx, frontend-v2/src/app/(authed)/events/places-autocomplete.tsx
Timezone module exports common timezones, todayInTimezone, formatWallClock, formatEventTimeRange, and isEventHappeningNow; TimeField component offers accessible HH:MM time input; PlacesAutocomplete loads Google Places library and emits PlaceValue on selection.
Event form schema, sections, and hook
frontend-v2/src/app/(authed)/events/event-form-schema.ts, frontend-v2/src/app/(authed)/events/event-form-sections.tsx, frontend-v2/src/app/(authed)/events/useEventForm.ts
EVENT_TYPES, constants, and EventFormMode exported; attendeeSchema and formSchema with Zod refinements (time ordering, public-event rules); exported sections (FieldError, clearLocation, EventDetailsSummaryBar, ScheduledEventFields, AttendeesSection); useEventForm hook manages TanStack Form state, fetches existing events, implements saveEventMutation with public-event confirmation routing.
EventForm component refactoring
frontend-v2/src/app/(authed)/events/event-form.tsx
EventForm adds startExpanded prop, uses useEventForm hook; conditionally renders collapsible detail fields, gates ScheduledEventFields and AttendeesSection based on form state.
Event confirmation and edit routing
frontend-v2/src/app/(authed)/events/[id]/confirmation/page.tsx, frontend-v2/src/app/(authed)/events/[id]/confirmation/event-confirmation.tsx, frontend-v2/src/app/(authed)/events/[id]/page.tsx
Confirmation page preloads event, hydrates EventConfirmation client component; EventConfirmation renders summary with navigation actions; edit page reads expanded query param to control form expansion.
HomeHub today's-events discovery
frontend-v2/src/app/(authed)/home/home-hub.tsx, frontend-v2/src/app/(authed)/home/page.tsx
HomeHub maintains ticking clock, computes viewer-timezone today, fetches and sorts today's events, renders with "Happening now" badges; HomePage wraps HomeHub in layout.
Navigation updates and documentation
frontend-v2/src/components/nav.tsx, frontend-v2/src/app/(authed)/events/new/page.tsx, README.md
Navbar logo becomes Link to /home; new event page title updated; README expanded with GOOGLE_PLACES_API_KEY setup.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • dxe/adb#313: Modifies AuthedPageProvider context shape; this PR extends that same context with googlePlacesApiKey and dependency injection.
  • dxe/adb#314: Introduces ApiClient.getEventList; this PR extends its response schema to include advance-event fields (start_time, end_time, timezone) and uses it in HomeHub discovery.

Suggested Reviewers

  • alexsapps

Poem

🐰 Hop along the event timeline, dear developer—
Timezones tamed, locations placed, and "happening now" rings true,
From database to today's discovery, the whole stack works together,
A calendar that knows your local sky, and users see what's new! 🗺️✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(events): support scheduling public events in advance' directly summarizes the main feature added in this PR: enabling advance scheduling of public events with new fields and a confirmation flow.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jake/events-in-advance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for scheduling public events in advance with rich metadata (start/end time, IANA timezone, description, online flag, public flag, and a deduped Google Place location), wires it through the event form (revealed when "Public event" is checked), adds a post-create confirmation page and a Home hub of today's events, and repoints the post-login redirect/nav logo to /home. A new locations table with a (chapter_id, google_place_id) unique key backs location dedup; the Google Places key is served from the authed user-info endpoint.

Changes:

  • Schema + model: new locations table and additional events columns; getEvents LEFT JOINs locations; CleanEventData validates time/timezone/description and upserts a location via GetOrCreateLocation.
  • Frontend: new home hub, events/[id]/confirmation page, PlacesAutocomplete, TimeField, and timezone helpers; EventForm extended with a collapsible details panel and scheduled-event fields.
  • Plumbing: googlePlacesApiKey flows through session/AuthedPageProvider; nav logo and post-login redirect now target /home; wipe, seed, and Makefile housekeeping.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pkg/shared/db-migrations/20260530120000_advance_events_and_locations.up.sql Creates locations table and adds scheduling/location columns to events.
pkg/shared/db-migrations/20260530120000_advance_events_and_locations.down.sql Reverts the up migration.
pkg/shared/wipe.go Drops the new locations table in the correct FK order.
server/src/model/event.go Adds JSON/DB fields, GetOrCreateLocation, time/timezone validation, and updates queries.
server/src/main.go Redirects to /home and exposes googlePlacesApiKey from authed user info.
cli/cmd/db.go Makes seed INSERT INTO events column-explicit for the new schema.
Makefile Sources optional .env alongside debug.env when running the server.
frontend-v2/src/lib/api.ts Adds location/scheduling fields to event schemas and save params; surfaces Places key.
frontend-v2/src/lib/timezone.ts New timezone helpers (browser tz, common zones, range/abbreviation formatting, happening-now).
frontend-v2/src/app/session.ts Threads googlePlacesApiKey through fetchSession.
frontend-v2/src/app/authed-page-provider.tsx Adds googlePlacesApiKey to the authed context.
frontend-v2/src/app/(authed)/layout.tsx Passes googlePlacesApiKey into the context.
frontend-v2/src/components/nav.tsx Makes the brand logo link to /home.
frontend-v2/src/components/ui/time-field.tsx New react-aria-based TimeField with a clear button.
frontend-v2/src/app/(authed)/events/places-autocomplete.tsx New Google Places autocomplete input that submits a deduped place.
frontend-v2/src/app/(authed)/events/event-form.tsx Adds scheduled-event fields, collapsible details, post-create confirmation routing.
frontend-v2/src/app/(authed)/events/new/page.tsx Updates title to "New event".
frontend-v2/src/app/(authed)/events/[id]/page.tsx Supports ?edit=1 to start the form expanded; title now "Event".
frontend-v2/src/app/(authed)/events/[id]/confirmation/page.tsx New confirmation page rendered after creating a public event.
frontend-v2/src/app/(authed)/events/[id]/confirmation/event-confirmation.tsx Confirmation UI with next-step actions.
frontend-v2/src/app/(authed)/home/page.tsx New Home route container.
frontend-v2/src/app/(authed)/home/home-hub.tsx Today's-events hub with "Happening now" badge and entry points.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread frontend-v2/src/app/(authed)/events/places-autocomplete.tsx
Comment thread frontend-v2/src/app/(authed)/home/home-hub.tsx Outdated
Comment thread server/src/model/event.go
Comment thread frontend-v2/src/app/(authed)/events/event-form.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend-v2/src/app/`(authed)/events/[id]/page.tsx:
- Around line 41-42: The page at /events/[id] currently always renders EventForm
with mode="event" so the post-create "Take attendance now" CTA (which posts to
/events/${eventId}) lands in the editor instead of starting attendance; fix by
routing the CTA to the actual attendance handler or restoring the attendance
view: either update the confirmation screen to send the CTA to the real
attendance route (e.g. /events/${eventId}/attendance or the route handled by
your AttendancePage/AttendanceView) or change page.tsx to conditionally render
the attendance component (e.g. render AttendanceView when a query param or state
indicates attendance flow) instead of always rendering <EventForm mode="event">
so the CTA starts the intended attendance flow.

In `@frontend-v2/src/app/`(authed)/events/event-form.tsx:
- Around line 32-36: The "Today" shortcut and related logic use the browser's
timezone instead of the event form's selected timezone; update usages that
compute today (notably where eventDate is set and the shortcut handler around
todayInTimezone()) to call todayInTimezone(formValues.timezone) (or the
equivalent helper) so the calendar uses the currently selected timezone; locate
and modify the code paths referencing eventDate and the Today shortcut handler
(and the date comparison/initialization logic that currently relies on new
Date() or getBrowserTimezone()) to pass the form's timezone value from the
timezone dropdown/state into the todayInTimezone helper so the selected event
timezone determines "Today".

In `@frontend-v2/src/app/`(authed)/events/places-autocomplete.tsx:
- Around line 182-187: The onChange handler currently only calls onClear() when
the input is emptied, so a user editing a committed place can change the visible
text while the component still holds the old googlePlaceId/address; update the
onChange in places-autocomplete.tsx to clear the committed place as soon as the
user types (call onClear()) whenever the input value changes from a selected
state (i.e., on any non-selection user edit) — ensure this happens inside the
existing onChange (alongside setText) and avoid interfering with the code path
that sets the text when a suggestion is chosen (so that selection still commits
the place).

In `@frontend-v2/src/app/`(authed)/home/home-hub.tsx:
- Line 30: The memoized today value (const today = useMemo(() =>
todayInTimezone(getBrowserTimezone()), [])) never updates after midnight, so
pages left open show yesterday’s events; modify the logic that computes today
(the today variable / useMemo for todayInTimezone and getBrowserTimezone) to
recompute on a daily tick—either add the existing clock state as a dependency to
the useMemo or schedule a timeout/interval that re-evaluates today at the next
local midnight—so that todayInTimezone(getBrowserTimezone()) is called again
when the date changes.
- Around line 47-50: upcomingHref currently computes the one-year-ahead end by
string-slicing today which produces invalid dates on leap day; change
upcomingHref to parse the today string into a proper Date (or use the project's
timezone helper/date library), add one year via date arithmetic (e.g., add 1
year using date-fns/dayjs/luxon or Date.setFullYear(Date.getFullYear()+1) to
handle leap-year rollover), then format the resulting date back into the same
YYYYMMDD string for the end query param; update the logic in the upcomingHref
useMemo (and any helper that reads/outputs today) to use this date-based
approach instead of string slicing.

In `@frontend-v2/src/lib/timezone.ts`:
- Around line 49-56: todayInTimezone and nowMinutesInTimezone call new
Intl.DateTimeFormat with unvalidated timeZone and can throw RangeError; wrap the
Intl.DateTimeFormat/format calls in try/catch and handle invalid zones by
returning a safe fallback (e.g. for todayInTimezone return the local YYYY-MM-DD
string or empty string, for nowMinutesInTimezone return a sentinel like null or
-1), and update isEventHappeningNow (used by HomeHub when passing
event.timezone) to catch/propagate these fallbacks and return false when the
timezone is invalid so a bad event.timezone cannot crash the render.

In `@pkg/shared/db-migrations/20260530120000_advance_events_and_locations.up.sql`:
- Around line 17-25: The current fk_events_location only checks location_id and
allows cross-chapter links; update the schema so events enforces chapter-scoped
locations by making the FK composite: have events reference (location_id,
chapter_id) -> locations (id, chapter_id) (rename or replace fk_events_location
accordingly), ensure events has a chapter_id column, and add a unique/index on
locations (id, chapter_id) so the composite FK can reference those columns.

In `@server/src/model/event.go`:
- Around line 607-656: The code validates fields in isolation but allows
inconsistent scheduled-public events; after calling cleanEventTime and resolving
Timezone/Location (symbols: cleanEventTime, e.StartTime, e.EndTime, e.Timezone,
e.IsOnline, e.LocationID, e.IsPublic), add a coherent-schedule validation: if
e.IsPublic is true then require a non-empty StartTime and a non-empty Timezone
and require a concrete location choice (either e.IsOnline == true or
e.LocationID != nil); if EndTime is present ensure EndTime >= StartTime by
parsing/comparing the two times in the same day context (use the same
format/parse logic as cleanEventTime) and return a clear fmt.Errorf when any of
these checks fail (e.g. "invalid scheduled public event: missing start_time",
"missing timezone", "must be online or have a physical location", or "end_time
before start_time").
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fce8d66b-61d7-45d4-930a-a2d67ea091d2

📥 Commits

Reviewing files that changed from the base of the PR and between c78e8e9 and 012fa83.

📒 Files selected for processing (22)
  • Makefile
  • cli/cmd/db.go
  • frontend-v2/src/app/(authed)/events/[id]/confirmation/event-confirmation.tsx
  • frontend-v2/src/app/(authed)/events/[id]/confirmation/page.tsx
  • frontend-v2/src/app/(authed)/events/[id]/page.tsx
  • frontend-v2/src/app/(authed)/events/event-form.tsx
  • frontend-v2/src/app/(authed)/events/new/page.tsx
  • frontend-v2/src/app/(authed)/events/places-autocomplete.tsx
  • frontend-v2/src/app/(authed)/home/home-hub.tsx
  • frontend-v2/src/app/(authed)/home/page.tsx
  • frontend-v2/src/app/(authed)/layout.tsx
  • frontend-v2/src/app/authed-page-provider.tsx
  • frontend-v2/src/app/session.ts
  • frontend-v2/src/components/nav.tsx
  • frontend-v2/src/components/ui/time-field.tsx
  • frontend-v2/src/lib/api.ts
  • frontend-v2/src/lib/timezone.ts
  • pkg/shared/db-migrations/20260530120000_advance_events_and_locations.down.sql
  • pkg/shared/db-migrations/20260530120000_advance_events_and_locations.up.sql
  • pkg/shared/wipe.go
  • server/src/main.go
  • server/src/model/event.go

Comment thread frontend-v2/src/app/(authed)/events/[id]/page.tsx Outdated
Comment thread frontend-v2/src/app/(authed)/events/event-form.tsx Outdated
Comment thread frontend-v2/src/app/(authed)/events/places-autocomplete.tsx
Comment thread frontend-v2/src/app/(authed)/home/home-hub.tsx Outdated
Comment thread frontend-v2/src/app/(authed)/home/home-hub.tsx
Comment thread frontend-v2/src/lib/timezone.ts Outdated
Comment thread pkg/shared/db-migrations/20260530120000_advance_events_and_locations.up.sql Outdated
Comment thread server/src/model/event.go
@jakehobbs jakehobbs marked this pull request as draft May 31, 2026 00:12
- Allow & and other chars in event descriptions (parameterized SQL +
  React-escaped output, so no injection risk); keep the check on names.
- Preserve stored location metadata on upsert instead of clobbering with
  blanks (IF(... <> '')/COALESCE).
- Enforce chapter-scoped location integrity via a composite FK
  (chapter_id, location_id) -> locations (chapter_id, id).
- Validate public events server-side: require start_time, timezone, and
  (in-person) a location. Overnight (end < start) intentionally deferred.
- Clear the committed place on manual edits to the location field so the
  displayed text and submitted location can't drift apart.
- Use the event's selected timezone for the "Today" shortcut.
- Roll the home page over to the next day at local midnight; guard invalid
  timezones in Intl calls; compute the one-year-ahead date via Date math.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jakehobbs jakehobbs marked this pull request as ready for review May 31, 2026 02:21
jakehobbs and others added 3 commits May 30, 2026 21:07
The home header date is derived from the current time and browser
timezone, so SSR and the client can land on different calendar days
near midnight or across zones. Gate the date label on a client mount
flag so the server and first client render agree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…atch

The "View upcoming events" link's date-range query is derived from the
current day, which differs between SSR and client near midnight. Use a
stable /events fallback until mounted, then the date-filtered URL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Frontend (frontend-v2):
- timezone.ts: reframe header for "upcoming" events and justify the
  wall-clock + IANA-zone storage choice over a UTC instant; drop the
  low-value Intl note; fix the misleading COMMON_TIMEZONES "fallback"
  wording; convert leading comments to JSDoc; move the en-CA note to its
  use; take an event object (Pick) instead of four positional strings.
- home-hub.tsx: drop a redundant comment, use sortedEvents?.map, and
  extract a TodayEventRow component.
- event-confirmation.tsx: pass the event to the time formatter, inline
  single-use locals, replace margins with parent gap, and navigate via
  <Link> buttons instead of router.push.
- event-form.tsx: correct the "optional" schema comment, drop a stale
  comment, rename the ?edit=1 query param to ?expanded=1, clarify the
  HH:MM slice and the onSubmit attendee check, and trim a verbose note.
- places-autocomplete.tsx: toast when Google Maps fails to load.
- events/[id]/page.tsx: Promise.all the params/searchParams awaits.
- api.ts: explain the empty-string default for googlePlacesApiKey.

Backend:
- model/event.go: reword "existing" to "older basic" attendance flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend-v2/src/lib/timezone.ts (1)

107-114: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Tighten wall-clock validation before formatting or badge math.

frontend-v2/src/lib/api.ts:293-309 only narrows start_time/end_time to nullable strings, so these helpers still need to reject impossible clocks. Right now Line 108 and Line 140 accept values like 25:99 or 09:30junk, which can surface bogus labels in formatEventTimeRange() and false positives in isEventHappeningNow().

Suggested fix
+function parseWallClock(
+  time: string,
+): { hours: number; minutes: number; minutesText: string } | null {
+  const match = /^(\d{1,2}):(\d{2})(?::\d{2})?$/.exec(time.trim())
+  if (!match) return null
+
+  const hours = Number(match[1])
+  const minutes = Number(match[2])
+  if (hours > 23 || minutes > 59) return null
+
+  return { hours, minutes, minutesText: match[2] }
+}
+
 export function formatWallClock(time: string): string {
-  const match = /^(\d{1,2}):(\d{2})/.exec(time.trim())
-  if (!match) return ''
-  const hours = Number(match[1])
-  const minutes = match[2]
+  const parsed = parseWallClock(time)
+  if (!parsed) return ''
+  const { hours, minutesText } = parsed
   const period = hours >= 12 ? 'PM' : 'AM'
   const hour12 = hours % 12 === 0 ? 12 : hours % 12
-  return `${hour12}:${minutes} ${period}`
+  return `${hour12}:${minutesText} ${period}`
 }
 ...
 function toMinutesSinceMidnight(time: string): number | null {
-  const match = /^(\d{1,2}):(\d{2})/.exec(time.trim())
-  if (!match) return null
-  return Number(match[1]) * 60 + Number(match[2])
+  const parsed = parseWallClock(time)
+  return parsed ? parsed.hours * 60 + parsed.minutes : null
 }

Also applies to: 139-142

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend-v2/src/lib/timezone.ts` around lines 107 - 114, formatWallClock
currently accepts invalid inputs like "25:99" or "09:30junk" because it uses a
loose regex; tighten validation by requiring a full-string match for 24-hour
clocks and valid minutes (e.g. regex like ^([01]?\d|2[0-3]):([0-5]\d)$) and
reject non-matching values (return ''). Update formatWallClock to trim the
input, run the strict regex, parse hours/minutes only after a match and then
convert to 12-hour with AM/PM as before; apply the same strict validation
approach to helpers that rely on it such as isEventHappeningNow and
formatEventTimeRange so they don't receive or accept bogus times.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@frontend-v2/src/lib/timezone.ts`:
- Around line 107-114: formatWallClock currently accepts invalid inputs like
"25:99" or "09:30junk" because it uses a loose regex; tighten validation by
requiring a full-string match for 24-hour clocks and valid minutes (e.g. regex
like ^([01]?\d|2[0-3]):([0-5]\d)$) and reject non-matching values (return '').
Update formatWallClock to trim the input, run the strict regex, parse
hours/minutes only after a match and then convert to 12-hour with AM/PM as
before; apply the same strict validation approach to helpers that rely on it
such as isEventHappeningNow and formatEventTimeRange so they don't receive or
accept bogus times.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: af29fe85-8bac-460a-9c09-dac49c412740

📥 Commits

Reviewing files that changed from the base of the PR and between 2e7ba52 and d3f0fd4.

📒 Files selected for processing (8)
  • frontend-v2/src/app/(authed)/events/[id]/confirmation/event-confirmation.tsx
  • frontend-v2/src/app/(authed)/events/[id]/page.tsx
  • frontend-v2/src/app/(authed)/events/event-form.tsx
  • frontend-v2/src/app/(authed)/events/places-autocomplete.tsx
  • frontend-v2/src/app/(authed)/home/home-hub.tsx
  • frontend-v2/src/lib/api.ts
  • frontend-v2/src/lib/timezone.ts
  • server/src/model/event.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • frontend-v2/src/app/(authed)/events/[id]/page.tsx
  • frontend-v2/src/app/(authed)/events/[id]/confirmation/event-confirmation.tsx
  • server/src/model/event.go
  • frontend-v2/src/lib/api.ts
  • frontend-v2/src/app/(authed)/events/places-autocomplete.tsx
  • frontend-v2/src/app/(authed)/events/event-form.tsx

Restore timeRange/locationLabel locals so the formatter and the location
fallback are each evaluated once rather than inlined twice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@alexsapps alexsapps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exciting times. thanks Jake!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is only the homepage for people with attendance access, and it's only related to events, how about a path like /events/home or /events/today ?

i also feel like the "Welcome" + name/chapter/date is just more boilerplate on a page that already has boilerplate (today's events being the content, and the "Today's events" title and new event / view future events links being boilerplate).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping this at /home — it's the post-login landing page (the logo links here), and everything it offers is reachable from the events pages too, so I'd rather not surface 'home'/'today' as a nav concept. I did make it earn its keep though: the events content now only renders for users with attendance access, so others just get the header. Leaving the greeting + date as a light orienting touch.

(posted by claude)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just for the boilerplate then, how about putting the chapter name and today's date underneath "Today's events" and removing "Welcome, name"? i feel like the current boilerplate is a little cramped and little cheesy lol.

Comment thread frontend-v2/src/app/(authed)/home/home-hub.tsx
Comment thread server/src/main.go Outdated
Comment thread frontend-v2/src/app/authed-page-provider.tsx Outdated
Comment thread Makefile
set -a && . server/debug.env && set +a && \
set -a && \
. server/debug.env && \
{ [ -f .env ] && . ./.env || true; } && \
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should launch.json be updated too? 🙏:)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left this one: the Go Server debug config loads a single envFile (server/debug.env), and the new .env is optional (only needed for the Places key locally). Pointing the debugger at a .env most people won't have would break 'Go Server' for them. If you want Places in the debugger, easiest is to drop GOOGLE_PLACES_API_KEY into your local server/debug.env. I documented the .env route in the README.

(posted by claude)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so far makefile and launch.json behavior have been kept in sync and i'd like to keep it that way if possible, so we don't have situations where things work depending on how they're launched. perhaps we could have 'make deps' create the file if it's missing so it can be added to launch.json too?

added_attendees: addedAttendees,
deleted_attendees: deletedAttendees,
suppress_survey: value.suppressSurvey,
// Only send scheduled-event fields when they're in play, keeping the
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

premature optimization?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a guard rather than an optimization: the conditional keeps the connection and quick-attendance save payloads exactly as they were (no is_public/time/location fields), so those flows — coaching especially, which hits a different endpoint — don't start sending event-only fields. Always including them risks clobbering/confusing those paths, so I'd keep it.

(posted by claude)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about guarding with just "!isConnection" and removing the comment? it's just a lot to understand right now with the shape of the json depending on certain fields being set.

Comment thread frontend-v2/src/lib/time.ts
Comment thread frontend-v2/src/lib/time.ts
Comment thread server/src/model/event.go Outdated
Comment thread server/src/model/event.go
jakehobbs and others added 2 commits June 4, 2026 19:26
Frontend:
- Split event-form.tsx (~950 lines) into event-form-schema.ts, a
  useEventForm hook, and event-form-sections.tsx, plus a shared FieldError
  component and clearLocation helper.
- Group the scheduled-event fields in a nested sub-box (still gated by the
  "Publicly listed" checkbox); stop force-collapsing details on save.
- Show timezone abbreviations in the picker options; drop the redundant
  "Times are in…" line. Remove the "Event details" eyebrow and assorted
  help/boilerplate text.
- Home hub: only render the events section for users with attendance access.
- Rename lib/timezone.ts -> lib/time.ts.
- Drop the frontend HH:MM:SS slice now that the backend returns HH:MM.
- Convert the mount-gate and places-autocomplete syncs off setState-in-effect
  to satisfy the react-hooks lint rules.

Backend:
- Return start_time/end_time as HH:MM from EventJSON.
- Use ValidationErrorf for public-event input validation (400 not 500).
- Tidy Google Places key comments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 188-190: The fenced code block containing the environment variable
example (the triple-backtick block around "GOOGLE_PLACES_API_KEY=...") needs a
language tag to satisfy markdownlint MD040; update that fenced block (the code
block with the example env line) to include a language specifier such as "bash"
(e.g., ```bash) so the markdown renderer and linter accept it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7ba07f26-2b82-41c6-9853-b957c186f927

📥 Commits

Reviewing files that changed from the base of the PR and between d3f0fd4 and 0b56cca.

📒 Files selected for processing (13)
  • README.md
  • frontend-v2/src/app/(authed)/events/[id]/confirmation/event-confirmation.tsx
  • frontend-v2/src/app/(authed)/events/event-form-schema.ts
  • frontend-v2/src/app/(authed)/events/event-form-sections.tsx
  • frontend-v2/src/app/(authed)/events/event-form.tsx
  • frontend-v2/src/app/(authed)/events/places-autocomplete.tsx
  • frontend-v2/src/app/(authed)/events/useEventForm.ts
  • frontend-v2/src/app/(authed)/home/home-hub.tsx
  • frontend-v2/src/app/authed-page-provider.tsx
  • frontend-v2/src/lib/time.ts
  • server/src/config/config.go
  • server/src/main.go
  • server/src/model/event.go
💤 Files with no reviewable changes (1)
  • frontend-v2/src/app/authed-page-provider.tsx
✅ Files skipped from review due to trivial changes (1)
  • server/src/config/config.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • server/src/main.go
  • frontend-v2/src/app/(authed)/events/[id]/confirmation/event-confirmation.tsx
  • frontend-v2/src/app/(authed)/events/places-autocomplete.tsx
  • frontend-v2/src/app/(authed)/home/home-hub.tsx
  • server/src/model/event.go

Comment thread README.md
Some venues aren't a clean Google Place (an intersection, a patch of public
land), so allow entering a location by hand: a free-text name plus optional
coordinates. Manual locations are stored on the event itself (new
manual_location_name/lat/lng columns) rather than the place-keyed locations
table, since there's no google_place_id to dedupe on. An event uses either a
location_id (Google Place) or the manual fields, never both; ToJSON surfaces
whichever is set as a unified location.

The form's location field gains a toggle to switch between Google Places search
and manual entry; public in-person events accept either.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jakehobbs jakehobbs requested a review from alexsapps June 5, 2026 02:54
jakehobbs and others added 4 commits June 4, 2026 19:55
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pnpm prompts before wiping an out-of-sync node_modules, which hangs forever
in non-interactive setups (e.g. the Conductor devcontainer 'make deps' step).
Set confirm-modules-purge=false at the repo root and in frontend-v2 so the
reinstall proceeds without a TTY.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The .npmrc setting wasn't reliably suppressing pnpm's modules-purge prompt in
the Conductor devcontainer setup, so 'make deps' hung. Pass the flag explicitly
on the pnpm install commands, which is honored regardless of .npmrc resolution.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
<div className={buefyStyles['navbar-brand']}>
<div className={buefyStyles['navbar-item']}>
<Link
href="/home"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add in Vue nav component too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants