Consolidated fix wave for 1 Critical + 6 Important findings from the final
review before merge:
- Critical: marking a booking completed no longer frees its booked_slots
mirror row or the unique-slot index, closing a double-booking hole
(new migration 20260812110508, with regression pgTAP tests added first
and confirmed failing against the old trigger/index).
- uploadSalonImage() now returns the storage-relative path instead of an
absolute public URL, so products/services.image stays one consistent
format.
- The product/service Id field is disabled while editing, preventing
accidental duplicate rows from an upsert keyed on a changed id.
- Cancel / Mark completed in AdminBookings now require window.confirm,
matching the existing delete-confirmation pattern.
- /admin and any unmatched /admin/* path now redirect to /admin/products
instead of rendering blank.
- Public signup is disabled in supabase/config.toml, and ProtectedRoute
adds a defense-in-depth check that the session belongs to the admin
user id, not just that a session exists.
- Product/service delete now surfaces a friendly message for FK-violation
errors (23503) via a new friendlyError() helper, instead of a raw
Postgres error string.
Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH
Substitutes the real admin UID everywhere the placeholder appeared,
points the Edge Functions base URL at the deployed project, and adds
production JWT verification: trigger functions now send the anon key
as a bearer token so verify_jwt can stay enabled instead of leaving
the functions open to unauthenticated calls from the public internet.
Also adds scripts/create-admin.ts (one-off helper used to create
Mira's admin auth user), documents the seed script's non-VITE env
vars in .env.example, and closes a gitignore gap where plain .env
wasn't excluded alongside .env.local.
Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH
Wires pg_net-based database triggers on bookings/inquiries to invoke the two
Edge Functions on insert. Two empirically-discovered fixes over the brief's
literal SQL were required to make this work locally:
- ALTER DATABASE ... SET for a custom GUC needs superuser, but the local
migration role (postgres) isn't superuser in this Supabase CLI stack (only
supabase_admin is) - inlined the base URL as a literal constant in each
trigger function instead.
- pg_net's http_post lives in schema `net`, not `extensions.net` - fixed the
function calls accordingly.
Also disabled verify_jwt for both functions in config.toml since they're
invoked by pg_net (no Authorization header), not by end-user clients.
Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH
Initializes the local Supabase project (supabase/config.toml) and adds
a thin src/lib/supabase.ts wrapper around @supabase/supabase-js that
reads connection details from Vite env vars, tested with a mocked
createClient.