Skip to content

Status/RSVP — POST /post-acceptance + real singleton reads #500

Description

@adityapat24

Context

Last sprint shipped the state machine and GET /api/v1/status. What's missing is the RSVP submission endpoint, plus swapping the mock singleton file for real Ticket 5 singleton reads.

Tasks

Swap mock singletons for real ones

  • In src/lib/status/service.ts, replace reads from mock-singletons.ts with getSingleton() calls to Ticket 5's singleton service.
    • What this accomplishes: The dashboard state machine now uses admin-configured dates instead of hardcoded ones. Changing dates in /admin/settings now actually affects what applicants see.
  • Delete or rename src/lib/status/mock-singletons.ts to make it obvious it's no longer used.
    • What this accomplishes: Prevents future confusion where a dev updates the mock file expecting behavior to change and doesn't realize it's dead code.
  • Handle the case where a singleton hasn't been set (returns null). Reasonable default behavior: if registration-open is null, treat as "not yet open." If confirm-by is null, treat as "no deadline yet" (which means past-deadline logic doesn't kick in).
    • What this accomplishes: The system doesn't crash if an admin forgot to set a date. It fails safe — applicants can't submit, admitted users can still RSVP.

RSVP submission endpoint

  • Implement saveRsvp(userId, payload) in src/lib/status/service.ts. Fetches the user, confirms decisionStatus === 'admitted' (only admitted users can RSVP), validates the payload against a Zod schema, checks confirm-by hasn't passed, sets rsvpStatus, postAcceptanceResponses, rsvpSubmissionTime.
    • What this accomplishes: The core RSVP logic. Non-admitted users can't RSVP, so a waitlisted user can't sneak in as if they'd been admitted. Deadline enforcement is server-side.
  • Define the Zod schema for RSVP responses. At minimum this includes attendance ('confirmed' | 'unconfirmed'), and any post-acceptance question fields (dietary restrictions, t-shirt size, etc.).
    • What this accomplishes: RSVP is a smaller form than the application, but still needs validation. The exact fields depend on what questions HackBeanpot wants to ask post-acceptance — coordinate with whoever knows the current requirements.
  • Wire POST /api/v1/post-acceptance to call saveRsvp. Returns { ok: true } on success, 400 on validation failure, 403 if the user isn't admitted, 410 (Gone) if past confirm-by.
    • What this accomplishes: The endpoint the RSVP form on /rsvp calls. Status codes let the frontend display specific errors.

Auth gating

  • Wire requireUser() into GET /api/v1/status and POST /api/v1/post-acceptance.
    • What this accomplishes: Only signed-in users can check their status or submit an RSVP. Session-based user identification replaces the placeholder query param.
  • Update getPortalStatus to read userId from the session instead of the query string.
    • What this accomplishes: Same as Ticket 2's cleanup — removes the temporary shim from the last sprint.

Read-your-own-writes verification

  • Manually test: sign in as an admitted user, submit an RSVP, refresh the dashboard, confirm the dashboard shows the post-RSVP state (whatever that looks like — confirmed attendance, "see you at the event," etc.).
    • What this accomplishes: End-to-end proof the state machine sees the RSVP write immediately, without cache issues or stale reads.

Definition of done

  • The status endpoint uses real singleton values, not the mock file.
  • Admitted users can submit an RSVP via the frontend and it persists.
  • Non-admitted users get 403 on the RSVP endpoint.
  • Past-deadline RSVPs get 410.
  • Both endpoints are gated by requireUser().

Metadata

Metadata

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions