-- Phase 2D: adds site-editable content tables (site_content, testimonials, -- rules) and featured-product columns, all following the existing -- public-read/admin-write RLS pattern from the initial schema. Seed values -- match what the site currently shows via hardcoded frontend constants, so -- this migration doesn't blank any live content. create table public.site_content ( id text primary key default 'default', hero_headline text not null, hero_tagline text not null, about_bio text not null, about_portrait_image text not null, contact_email text not null, contact_hours text not null ); alter table public.site_content enable row level security; create policy "site_content_public_read" on public.site_content for select using (true); create policy "site_content_admin_write" on public.site_content for all using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) with check ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); insert into public.site_content (id, hero_headline, hero_tagline, about_bio, about_portrait_image, contact_email, contact_hours) values ( 'default', 'Vos cheveux, votre couronne', 'Coiffures protectrices par Mira', 'Avec plus d''une décennie d''expérience derrière le fauteuil, Mira aborde chaque installation comme une œuvre d''art — douce pour le cuir chevelu, précise à la racine, et achevée pour une couronne que vous aurez envie d''afficher.', 'about/mira-portrait.jpg', 'hello@braidsbymira.com', 'Mar–Sam, 9h à 18h' ); create table public.testimonials ( id uuid primary key default gen_random_uuid(), quote text not null, author text not null, avatar_image text not null, sort_order integer not null ); alter table public.testimonials enable row level security; create policy "testimonials_public_read" on public.testimonials for select using (true); create policy "testimonials_admin_write" on public.testimonials for all using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) with check ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); insert into public.testimonials (quote, author, avatar_image, sort_order) values ('Mira m''a fait sentir comme une reine sur son fauteuil. Mes tresses ont tenu 8 semaines !', 'Amara T.', 'testimonials/amara.jpg', 0), ('La tresseuse la plus douce et la plus précise que j''aie jamais consultée.', 'Keisha R.', 'testimonials/keisha.jpg', 1), ('La réservation était si simple, et le résultat parle de lui-même.', 'Nadia F.', 'testimonials/nadia.jpg', 2); create table public.rules ( id uuid primary key default gen_random_uuid(), title text not null, body text not null, icon_key text not null, sort_order integer not null ); alter table public.rules enable row level security; create policy "rules_public_read" on public.rules for select using (true); create policy "rules_admin_write" on public.rules for all using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) with check ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); insert into public.rules (title, body, icon_key, sort_order) values ('Acompte', 'Un acompte de 30 $ est exigé afin de confirmer toute réservation. Celui-ci n''est pas remboursable, mais transférable à une date ultérieure sur entente, pour un maximum d''une fois. Toute annulation moins de 48 heures avant le rendez-vous entraînera la perte de l''acompte, qui ne sera ni remboursé ni transféré. En cas de non-présentation (« no-show »), l''acompte est automatiquement perdu.', 'deposit', 0), ('Préalables cheveux', 'Prière de garder vos cheveux propres, démêlés et secs, prêts à être tressés. Le blowdry peut être facturé si cette règle n''est pas respectée. Pas de bains d''huiles la veille ou le jour du rendez-vous, s''il vous plaît. Merci de toujours préciser, photo à l''appui, vos caractéristiques spécifiques (cheveux courts, locks, etc.).', 'hair-prep', 1), ('Mèches et extensions', 'Les mèches sont à déposer 24 à 48h avant la prestation si ce n''est pas moi qui les fournis. Choisissez de préférence des mèches pré-étirées ; l''étirage des mèches vous sera facturé en fonction du nombre de paquets.', 'extensions', 2), ('Retard', 'Les retards sans notification préalable ne sont pas autorisés. Un retard de 15 à 20 minutes entraîne des frais supplémentaires, et à partir de 30 minutes, le rendez-vous est automatiquement annulé.', 'lateness', 3), ('Accompagnant', 'Vous êtes tenue de vous présenter seule à votre rendez-vous. Merci de ne pas venir accompagnée sans l''avoir signalé au préalable.', 'guests', 4), ('Paiement', 'Le paiement peut être fait en espèces ou par virement Interac.', 'payment', 5); alter table public.products add column featured boolean not null default false; alter table public.products add column featured_order integer; -- Grant SELECT to all public roles; write operations are restricted to admin via RLS grant select on public.site_content to anon, authenticated; grant select on public.testimonials to anon, authenticated; grant select on public.rules to anon, authenticated; -- Grant write permissions only to authenticated (admin) role; RLS policies further -- restrict these to the specific admin UUID grant update on public.site_content to authenticated; grant insert, update, delete on public.testimonials to authenticated; grant insert, update, delete on public.rules to authenticated; -- Grant all permissions to service_role (which bypasses RLS for admin tasks) grant all on public.site_content to service_role; grant all on public.testimonials to service_role; grant all on public.rules to service_role;