From 7b4bfe8b27816618843591a69fd316c8119e61ba Mon Sep 17 00:00:00 2001 From: Ivan Cacciari Date: Fri, 28 Aug 2026 17:27:58 +0200 Subject: [PATCH] Add M1 migration for giocatori_squadra roster table. Co-authored-by: Cursor --- .../20260828170400_m1_giocatori_squadra.sql | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 supabase/migrations/20260828170400_m1_giocatori_squadra.sql diff --git a/supabase/migrations/20260828170400_m1_giocatori_squadra.sql b/supabase/migrations/20260828170400_m1_giocatori_squadra.sql new file mode 100644 index 0000000..f37d510 --- /dev/null +++ b/supabase/migrations/20260828170400_m1_giocatori_squadra.sql @@ -0,0 +1,106 @@ +-- M1 — Anagrafica operativa squadra (Profilo Giocatore v1.1, DD-016) +-- Tabella additive: non modifica alcuna tabella v1.0 esistente. + +CREATE TABLE public.giocatori_squadra ( + id text PRIMARY KEY, + nome text NOT NULL, + cognome text NOT NULL, + numero integer NOT NULL CHECK (numero > 0), + ruolo text NOT NULL, + auth_user_id uuid UNIQUE REFERENCES auth.users(id) ON DELETE SET NULL, + attivo boolean NOT NULL DEFAULT true, + creato_il timestamptz NOT NULL DEFAULT now(), + aggiornato_il timestamptz NOT NULL DEFAULT now(), + CONSTRAINT giocatori_squadra_id_formato CHECK (id ~ '^g[0-9]+$') +); + +COMMENT ON TABLE public.giocatori_squadra IS + 'Anagrafica operativa della squadra (ID testuali g1..gN). Source of truth progressiva per la rosa.'; + +-- Enforce DD-016: il giocatore può solo reclamare uno slot libero (auth_user_id NULL → auth.uid()), +-- senza modificare altri campi. Gli admin possono aggiornare tutto, incluso il reset di auth_user_id. +CREATE OR REPLACE FUNCTION public.enforce_giocatori_squadra_update() +RETURNS TRIGGER +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public +AS $$ +BEGIN + IF public.has_role(auth.uid(), 'admin'::public.app_role) THEN + RETURN NEW; + END IF; + + IF OLD.auth_user_id IS NULL + AND NEW.auth_user_id = auth.uid() + AND NEW.id IS NOT DISTINCT FROM OLD.id + AND NEW.nome IS NOT DISTINCT FROM OLD.nome + AND NEW.cognome IS NOT DISTINCT FROM OLD.cognome + AND NEW.numero IS NOT DISTINCT FROM OLD.numero + AND NEW.ruolo IS NOT DISTINCT FROM OLD.ruolo + AND NEW.attivo IS NOT DISTINCT FROM OLD.attivo + AND NEW.creato_il IS NOT DISTINCT FROM OLD.creato_il THEN + RETURN NEW; + END IF; + + RAISE EXCEPTION 'Aggiornamento non autorizzato su giocatori_squadra'; +END; +$$; + +CREATE TRIGGER enforce_giocatori_squadra_update + BEFORE UPDATE ON public.giocatori_squadra + FOR EACH ROW + EXECUTE FUNCTION public.enforce_giocatori_squadra_update(); + +CREATE TRIGGER update_giocatori_squadra_aggiornato_il + BEFORE UPDATE ON public.giocatori_squadra + FOR EACH ROW + EXECUTE FUNCTION public.update_aggiornato_il(); + +GRANT SELECT, UPDATE ON public.giocatori_squadra TO authenticated; +GRANT ALL ON public.giocatori_squadra TO service_role; + +ALTER TABLE public.giocatori_squadra ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Authenticated can view active roster" + ON public.giocatori_squadra + FOR SELECT + TO authenticated + USING ( + attivo = true + OR public.has_role(auth.uid(), 'admin'::public.app_role) + ); + +CREATE POLICY "Users can claim unassigned roster slot" + ON public.giocatori_squadra + FOR UPDATE + TO authenticated + USING (auth_user_id IS NULL) + WITH CHECK (auth_user_id = auth.uid()); + +CREATE POLICY "Admins can manage roster" + ON public.giocatori_squadra + FOR ALL + TO authenticated + USING (public.has_role(auth.uid(), 'admin'::public.app_role)) + WITH CHECK (public.has_role(auth.uid(), 'admin'::public.app_role)); + +-- Seed da src/lib/crapp-data.ts (rosaCSI, ordine g1..g17). +-- Nome/cognome: primo token = nome, resto = cognome (es. "Carlo Di Castelnuovo" → Carlo / Di Castelnuovo). +INSERT INTO public.giocatori_squadra (id, nome, cognome, numero, ruolo) VALUES + ('g1', 'Salvador', 'Battistella', 88, 'Libero'), + ('g2', 'Mattias', 'Bologna', 73, 'Centrale'), + ('g3', 'Alessandra', 'Brunacci', 8, 'Palleggiatore'), + ('g4', 'Ivan', 'Cacciari', 23, 'Banda'), + ('g5', 'Mattia', 'Catalano', 21, 'Palleggiatore'), + ('g6', 'Silvia', 'Chilese', 11, 'Libero'), + ('g7', 'Alessio', 'Cocco', 77, 'Centrale'), + ('g8', 'Carlo', 'Di Castelnuovo', 14, 'Opposto'), + ('g9', 'Camilla', 'Esposito', 7, 'Palleggiatore'), + ('g10', 'Davide', 'Grilli', 1, 'Opposto'), + ('g11', 'Antonella', 'Loverre', 22, 'Banda'), + ('g12', 'Laura', 'Passabì', 5, 'Banda'), + ('g13', 'Nicola', 'Pezzoli', 4, 'Centrale'), + ('g14', 'Iacopo', 'Ricci', 2, 'Banda'), + ('g15', 'Cristina', 'Titone', 3, 'Libero'), + ('g16', 'Francesca', 'Tucci', 18, 'Centrale'), + ('g17', 'Giada', 'Valbonesi', 10, 'Opposto');