Fast follow-up to the Phase 2A backend review (4 deferred Important items):
1. Add a partial unique index (date, time) where status = 'confirmed' on
public.bookings so two concurrent bookings for the same slot can no longer
both succeed. Previously sync_booked_slot()'s `on conflict do nothing`
masked the second insert as a silent no-op on the mirror table instead of
rejecting it.
2. Extend sync_booked_slot() (new migration, the applied 20260808000002 is
left untouched) to also fire on `update of status` and `delete`, removing
the booked_slots mirror row once no confirmed booking remains for a slot.
Also tightened the insert path to only mirror bookings that are actually
'confirmed' -- the original trigger mirrored every insert regardless of
status, so a booking inserted directly with status='cancelled' (allowed by
the public insert policy) would incorrectly block a slot nobody confirmed.
3. supabase/functions/{on-booking-created,on-inquiry-created}: guard
req.json() and payload.record with try/catch (400 + console.error on
failure), check the Resend response's .ok and log failures instead of
swallowing them, and skip the Resend call entirely (with a log) instead of
sending "Authorization: Bearer undefined" when RESEND_API_KEY is unset.
Still return 200 when only the notification leg fails, since the booking/
inquiry row itself already committed successfully by the time this handler
runs -- a non-200 would make pg_net retry, but retrying re-sends against
the same already-inserted row and won't fix a Resend outage or bad
payload, so it isn't the right signal.
4. README: document the Supabase-side dev loop (supabase start/db reset/test
db), npm run seed and its SUPABASE_URL/SUPABASE_SERVICE_ROLE_KEY env vars,
and npm run test:functions (Deno tests for the Edge Functions' shared
email builders).
pgTAP suite grew from 12 to 23 tests (11 new: 4 for the uniqueness
constraint, 7 for booked_slots cleanup on cancel/delete). Verified locally:
supabase db reset, supabase test db (23/23 pass), vitest run (51/51 pass),
npm run build, npm run lint, and npm run test:functions (Deno installed for
this session; 6/6 pass) all succeed. Nothing here touches the live Supabase
project -- deploying is a separate follow-up step.
Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH
1.5 KiB
1.5 KiB
Braids by Mira
Public marketing site for Braids by Mira — landing page, product showcase, booking flow, and salon policies.
Scripts
npm run dev— start the local dev servernpm test(ornpx vitest run) — run the frontend test suitenpm run build— type-check and build for productionnpm run lint— run ESLintnpm run test:functions— run the Deno tests for the Edge Functions' shared email builders (requires Deno installed)npm run seed— seed products, services, and the default schedule into a Supabase project (see below)
Backend (Supabase)
The backend lives in supabase/ — Postgres schema/RLS migrations, pgTAP tests, and Deno Edge Functions for booking/inquiry email notifications.
npx supabase start— start the local Docker-based Supabase dev stack (Postgres, Studio, Auth, Storage, Edge Functions runtime). Requires Docker.npx supabase db reset— replay all migrations from scratch against the local database.npx supabase test db— run the pgTAP test suite insupabase/tests/database/against the local database.
Environment variables
Copy .env.example to .env.local and fill in:
VITE_SUPABASE_URL,VITE_SUPABASE_ANON_KEY— frontend Supabase client config (exposed to the browser).SUPABASE_URL,SUPABASE_SERVICE_ROLE_KEY— used only byscripts/seed.ts(server-side, never expose the service role key to the browser). These are distinct from theVITE_-prefixed frontend variables above.