CREATE SCHEMA IF NOT EXISTS auth; CREATE TABLE IF NOT EXISTS auth.users ( id uuid PRIMARY KEY, email text NOT NULL UNIQUE, encrypted_password text NOT NULL, raw_user_meta_data jsonb NOT NULL DEFAULT '{}'::jsonb, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS public.users ( id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, email text NOT NULL UNIQUE, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), display_name text NOT NULL ); CREATE TABLE IF NOT EXISTS auth.sessions ( id uuid PRIMARY KEY, session_token text NOT NULL UNIQUE, user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), expires_at timestamptz NOT NULL ); CREATE INDEX IF NOT EXISTS auth_sessions_user_id_idx ON auth.sessions(user_id); CREATE TABLE IF NOT EXISTS public.tablos ( id uuid PRIMARY KEY, owner_id uuid NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, name text NOT NULL, color text NOT NULL, status text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz NULL ); CREATE INDEX IF NOT EXISTS tablos_owner_created_idx ON public.tablos (owner_id, created_at DESC) WHERE deleted_at IS NULL; CREATE TABLE IF NOT EXISTS public.tasks ( id uuid PRIMARY KEY, tablo_id uuid NOT NULL REFERENCES public.tablos(id) ON DELETE CASCADE, owner_id uuid NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, title text NOT NULL, description text NOT NULL DEFAULT '', status text NOT NULL CHECK (status IN ('todo', 'in_progress', 'in_review', 'done')), assignee_id uuid NULL REFERENCES public.users(id) ON DELETE SET NULL, is_etape boolean NOT NULL DEFAULT false, parent_task_id uuid NULL REFERENCES public.tasks(id) ON DELETE SET NULL, due_date date NULL, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), deleted_at timestamptz NULL, CHECK (NOT is_etape OR parent_task_id IS NULL) ); CREATE INDEX IF NOT EXISTS tasks_owner_tablo_deleted_idx ON public.tasks (owner_id, tablo_id, deleted_at); CREATE INDEX IF NOT EXISTS tasks_assignee_deleted_idx ON public.tasks (assignee_id, deleted_at); CREATE INDEX IF NOT EXISTS tasks_tablo_is_etape_deleted_idx ON public.tasks (tablo_id, is_etape, deleted_at); CREATE INDEX IF NOT EXISTS tasks_parent_task_id_idx ON public.tasks (parent_task_id); CREATE INDEX IF NOT EXISTS tasks_tablo_due_date_idx ON public.tasks (tablo_id, due_date); CREATE OR REPLACE FUNCTION public.validate_task_parent() RETURNS trigger LANGUAGE plpgsql AS $$ DECLARE parent_record public.tasks%ROWTYPE; BEGIN IF NEW.parent_task_id IS NULL THEN RETURN NEW; END IF; IF NEW.is_etape THEN RAISE EXCEPTION 'Etapes cannot have parent tasks'; END IF; SELECT * INTO parent_record FROM public.tasks WHERE id = NEW.parent_task_id; IF NOT FOUND OR parent_record.deleted_at IS NOT NULL THEN RAISE EXCEPTION 'Parent task is invalid'; END IF; IF parent_record.is_etape IS NOT TRUE THEN RAISE EXCEPTION 'Parent task must be an etape'; END IF; IF parent_record.owner_id <> NEW.owner_id THEN RAISE EXCEPTION 'Parent task owner must match child owner'; END IF; IF parent_record.tablo_id <> NEW.tablo_id THEN RAISE EXCEPTION 'Parent task tablo must match child tablo'; END IF; RETURN NEW; END; $$; DROP TRIGGER IF EXISTS tasks_validate_parent_trigger ON public.tasks; CREATE TRIGGER tasks_validate_parent_trigger BEFORE INSERT OR UPDATE ON public.tasks FOR EACH ROW EXECUTE FUNCTION public.validate_task_parent(); CREATE OR REPLACE FUNCTION public.handle_new_user() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ BEGIN INSERT INTO public.users (id, email, created_at, updated_at, display_name) VALUES ( NEW.id, NEW.email, NEW.created_at, NEW.updated_at, COALESCE(NEW.raw_user_meta_data ->> 'display_name', split_part(NEW.email, '@', 1)) ); RETURN NEW; END; $$; DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users; CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();