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
28 lines
1.5 KiB
Markdown
28 lines
1.5 KiB
Markdown
# 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 server
|
|
- `npm test` (or `npx vitest run`) — run the frontend test suite
|
|
- `npm run build` — type-check and build for production
|
|
- `npm run lint` — run ESLint
|
|
- `npm run test:functions` — run the Deno tests for the Edge Functions' shared email builders (requires [Deno](https://deno.com) 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 in `supabase/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 by `scripts/seed.ts` (server-side, never expose the service role key to the browser). These are distinct from the `VITE_`-prefixed frontend variables above.
|