diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index 2cbfb13..4ce7b79 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -412,6 +412,48 @@ export type Database = { }, ] } + client_field_values: { + Row: { + client_id: string + created_at: string + field_id: string + id: string + updated_at: string + value: string | null + } + Insert: { + client_id: string + created_at?: string + field_id: string + id?: string + updated_at?: string + value?: string | null + } + Update: { + client_id?: string + created_at?: string + field_id?: string + id?: string + updated_at?: string + value?: string | null + } + Relationships: [ + { + foreignKeyName: "client_field_values_client_id_fkey" + columns: ["client_id"] + isOneToOne: false + referencedRelation: "clients" + referencedColumns: ["id"] + }, + { + foreignKeyName: "client_field_values_field_id_fkey" + columns: ["field_id"] + isOneToOne: false + referencedRelation: "custom_client_fields" + referencedColumns: ["id"] + }, + ] + } clients: { Row: { address_line1: string | null @@ -1007,6 +1049,45 @@ export type Database = { } Relationships: [] } + custom_client_fields: { + Row: { + active: boolean + created_at: string + created_by: string | null + description: string | null + field_type: string + id: string + key: string + label: string + sort_order: number + updated_at: string + } + Insert: { + active?: boolean + created_at?: string + created_by?: string | null + description?: string | null + field_type?: string + id?: string + key: string + label: string + sort_order?: number + updated_at?: string + } + Update: { + active?: boolean + created_at?: string + created_by?: string | null + description?: string | null + field_type?: string + id?: string + key?: string + label?: string + sort_order?: number + updated_at?: string + } + Relationships: [] + } custom_form_templates: { Row: { body_html: string diff --git a/supabase/migrations/20260418171344_bd4ad166-596b-4c5c-8260-9f9ead6f6b73.sql b/supabase/migrations/20260418171344_bd4ad166-596b-4c5c-8260-9f9ead6f6b73.sql new file mode 100644 index 0000000..ddfa02d --- /dev/null +++ b/supabase/migrations/20260418171344_bd4ad166-596b-4c5c-8260-9f9ead6f6b73.sql @@ -0,0 +1,63 @@ +-- Custom client fields (admin-defined) +CREATE TABLE public.custom_client_fields ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + key text NOT NULL UNIQUE, + label text NOT NULL, + field_type text NOT NULL DEFAULT 'text', + description text, + sort_order integer NOT NULL DEFAULT 0, + active boolean NOT NULL DEFAULT true, + created_by uuid, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +ALTER TABLE public.custom_client_fields ENABLE ROW LEVEL SECURITY; + +CREATE POLICY ccf_client_select_auth ON public.custom_client_fields + FOR SELECT TO authenticated USING (true); +CREATE POLICY ccf_client_insert_admin ON public.custom_client_fields + FOR INSERT TO authenticated WITH CHECK (is_admin(auth.uid())); +CREATE POLICY ccf_client_update_admin ON public.custom_client_fields + FOR UPDATE TO authenticated USING (is_admin(auth.uid())); +CREATE POLICY ccf_client_delete_admin ON public.custom_client_fields + FOR DELETE TO authenticated USING (is_admin(auth.uid())); + +CREATE TRIGGER trg_ccf_client_updated_at + BEFORE UPDATE ON public.custom_client_fields + FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at(); + +-- Per-client values +CREATE TABLE public.client_field_values ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + client_id uuid NOT NULL REFERENCES public.clients(id) ON DELETE CASCADE, + field_id uuid NOT NULL REFERENCES public.custom_client_fields(id) ON DELETE CASCADE, + value text, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + UNIQUE (client_id, field_id) +); + +ALTER TABLE public.client_field_values ENABLE ROW LEVEL SECURITY; + +-- Anyone authenticated can view; admins or client creator can manage +CREATE POLICY clfv_select_auth ON public.client_field_values + FOR SELECT TO authenticated USING (true); +CREATE POLICY clfv_insert_auth ON public.client_field_values + FOR INSERT TO authenticated WITH CHECK (auth.uid() IS NOT NULL); +CREATE POLICY clfv_update_auth ON public.client_field_values + FOR UPDATE TO authenticated USING ( + is_admin(auth.uid()) + OR EXISTS (SELECT 1 FROM public.clients c WHERE c.id = client_id AND c.created_by = auth.uid()) + ); +CREATE POLICY clfv_delete_auth ON public.client_field_values + FOR DELETE TO authenticated USING ( + is_admin(auth.uid()) + OR EXISTS (SELECT 1 FROM public.clients c WHERE c.id = client_id AND c.created_by = auth.uid()) + ); + +CREATE TRIGGER trg_clfv_updated_at + BEFORE UPDATE ON public.client_field_values + FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at(); + +CREATE INDEX idx_client_field_values_client ON public.client_field_values(client_id); \ No newline at end of file