Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-17 02:51:56 +00:00
co-authored by renee-png
parent ff3afeef11
commit f524dc8fd1
2 changed files with 630 additions and 0 deletions
@@ -0,0 +1,239 @@
-- Enums
CREATE TYPE public.task_priority AS ENUM ('low', 'normal', 'high', 'urgent');
CREATE TYPE public.task_status AS ENUM ('incomplete', 'complete');
CREATE TYPE public.comment_entity AS ENUM ('case', 'document');
CREATE TYPE public.notification_kind AS ENUM ('task_assigned', 'task_mention', 'task_comment', 'task_due', 'task_completed', 'case_mention', 'document_mention', 'message_mention', 'message');
-- Workflow templates
CREATE TABLE public.workflow_templates (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
name text NOT NULL,
description text,
created_by uuid,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE public.workflow_template_tasks (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
template_id uuid NOT NULL REFERENCES public.workflow_templates(id) ON DELETE CASCADE,
title text NOT NULL,
description text,
priority public.task_priority NOT NULL DEFAULT 'normal',
days_from_start integer NOT NULL DEFAULT 7,
default_assignee_id uuid,
sort_order integer NOT NULL DEFAULT 0,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
-- Tasks
CREATE TABLE public.tasks (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
title text NOT NULL,
description text,
status public.task_status NOT NULL DEFAULT 'incomplete',
priority public.task_priority NOT NULL DEFAULT 'normal',
due_date date,
case_id uuid REFERENCES public.cases(id) ON DELETE CASCADE,
parent_task_id uuid REFERENCES public.tasks(id) ON DELETE CASCADE,
workflow_template_id uuid REFERENCES public.workflow_templates(id) ON DELETE SET NULL,
created_by uuid,
completed_by uuid,
completed_at timestamptz,
sort_order integer NOT NULL DEFAULT 0,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_tasks_case_id ON public.tasks(case_id);
CREATE INDEX idx_tasks_parent ON public.tasks(parent_task_id);
CREATE INDEX idx_tasks_due_date ON public.tasks(due_date);
CREATE INDEX idx_tasks_status ON public.tasks(status);
-- Task assignees
CREATE TABLE public.task_assignees (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
task_id uuid NOT NULL REFERENCES public.tasks(id) ON DELETE CASCADE,
user_id uuid NOT NULL,
assigned_by uuid,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (task_id, user_id)
);
CREATE INDEX idx_task_assignees_user ON public.task_assignees(user_id);
-- Task comments
CREATE TABLE public.task_comments (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
task_id uuid NOT NULL REFERENCES public.tasks(id) ON DELETE CASCADE,
author_id uuid NOT NULL,
body text NOT NULL DEFAULT '',
edited_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_task_comments_task ON public.task_comments(task_id);
-- Task history (activity log)
CREATE TABLE public.task_history (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
task_id uuid NOT NULL REFERENCES public.tasks(id) ON DELETE CASCADE,
actor_id uuid,
event text NOT NULL,
detail jsonb NOT NULL DEFAULT '{}'::jsonb,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_task_history_task ON public.task_history(task_id);
-- Generic comments (cases / documents)
CREATE TABLE public.comments (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
entity_type public.comment_entity NOT NULL,
entity_id uuid NOT NULL,
case_id uuid REFERENCES public.cases(id) ON DELETE CASCADE,
author_id uuid NOT NULL,
body text NOT NULL DEFAULT '',
edited_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_comments_entity ON public.comments(entity_type, entity_id);
CREATE INDEX idx_comments_case ON public.comments(case_id);
-- Notifications (global inbox)
CREATE TABLE public.notifications (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL,
kind public.notification_kind NOT NULL,
title text NOT NULL,
body text,
link text,
task_id uuid REFERENCES public.tasks(id) ON DELETE CASCADE,
case_id uuid REFERENCES public.cases(id) ON DELETE CASCADE,
conversation_id uuid REFERENCES public.conversations(id) ON DELETE CASCADE,
read_at timestamptz,
created_by uuid,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_notifications_user_unread ON public.notifications(user_id, read_at);
-- Triggers for updated_at
CREATE TRIGGER trg_tasks_updated_at BEFORE UPDATE ON public.tasks FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at();
CREATE TRIGGER trg_workflow_templates_updated_at BEFORE UPDATE ON public.workflow_templates FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at();
CREATE TRIGGER trg_workflow_template_tasks_updated_at BEFORE UPDATE ON public.workflow_template_tasks FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at();
-- Helper: can_access_task
CREATE OR REPLACE FUNCTION public.can_access_task(_task_id uuid, _user_id uuid)
RETURNS boolean
LANGUAGE sql
STABLE SECURITY DEFINER
SET search_path = public
AS $$
SELECT
public.is_admin(_user_id)
OR EXISTS (
SELECT 1 FROM public.tasks t
WHERE t.id = _task_id
AND (
t.created_by = _user_id
OR (t.case_id IS NOT NULL AND public.can_access_case(t.case_id, _user_id))
OR EXISTS (SELECT 1 FROM public.task_assignees ta WHERE ta.task_id = t.id AND ta.user_id = _user_id)
)
)
$$;
-- Enable RLS
ALTER TABLE public.tasks ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.task_assignees ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.task_comments ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.task_history ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.comments ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.workflow_templates ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.workflow_template_tasks ENABLE ROW LEVEL SECURITY;
-- Tasks policies
CREATE POLICY tasks_select ON public.tasks FOR SELECT TO authenticated
USING (public.can_access_task(id, auth.uid()));
CREATE POLICY tasks_insert ON public.tasks FOR INSERT TO authenticated
WITH CHECK (
auth.uid() IS NOT NULL
AND (case_id IS NULL OR public.can_access_case(case_id, auth.uid()))
);
CREATE POLICY tasks_update ON public.tasks FOR UPDATE TO authenticated
USING (public.can_access_task(id, auth.uid()));
CREATE POLICY tasks_delete ON public.tasks FOR DELETE TO authenticated
USING (public.is_admin(auth.uid()) OR created_by = auth.uid());
-- Task assignees policies
CREATE POLICY task_assignees_select ON public.task_assignees FOR SELECT TO authenticated
USING (public.can_access_task(task_id, auth.uid()));
CREATE POLICY task_assignees_insert ON public.task_assignees FOR INSERT TO authenticated
WITH CHECK (public.can_access_task(task_id, auth.uid()));
CREATE POLICY task_assignees_delete ON public.task_assignees FOR DELETE TO authenticated
USING (public.can_access_task(task_id, auth.uid()));
-- Task comments policies
CREATE POLICY task_comments_select ON public.task_comments FOR SELECT TO authenticated
USING (public.can_access_task(task_id, auth.uid()));
CREATE POLICY task_comments_insert ON public.task_comments FOR INSERT TO authenticated
WITH CHECK (author_id = auth.uid() AND public.can_access_task(task_id, auth.uid()));
CREATE POLICY task_comments_update ON public.task_comments FOR UPDATE TO authenticated
USING (author_id = auth.uid());
CREATE POLICY task_comments_delete ON public.task_comments FOR DELETE TO authenticated
USING (author_id = auth.uid() OR public.is_admin(auth.uid()));
-- Task history policies
CREATE POLICY task_history_select ON public.task_history FOR SELECT TO authenticated
USING (public.can_access_task(task_id, auth.uid()));
CREATE POLICY task_history_insert ON public.task_history FOR INSERT TO authenticated
WITH CHECK (public.can_access_task(task_id, auth.uid()));
-- Generic comments policies
CREATE POLICY comments_select ON public.comments FOR SELECT TO authenticated
USING (
public.is_admin(auth.uid())
OR (case_id IS NOT NULL AND public.can_access_case(case_id, auth.uid()))
OR author_id = auth.uid()
);
CREATE POLICY comments_insert ON public.comments FOR INSERT TO authenticated
WITH CHECK (
author_id = auth.uid()
AND (case_id IS NULL OR public.can_access_case(case_id, auth.uid()))
);
CREATE POLICY comments_update ON public.comments FOR UPDATE TO authenticated
USING (author_id = auth.uid());
CREATE POLICY comments_delete ON public.comments FOR DELETE TO authenticated
USING (author_id = auth.uid() OR public.is_admin(auth.uid()));
-- Notifications policies (private to user)
CREATE POLICY notifications_select ON public.notifications FOR SELECT TO authenticated
USING (user_id = auth.uid());
CREATE POLICY notifications_insert ON public.notifications FOR INSERT TO authenticated
WITH CHECK (auth.uid() IS NOT NULL);
CREATE POLICY notifications_update ON public.notifications FOR UPDATE TO authenticated
USING (user_id = auth.uid());
CREATE POLICY notifications_delete ON public.notifications FOR DELETE TO authenticated
USING (user_id = auth.uid());
-- Workflow templates policies
CREATE POLICY wft_select ON public.workflow_templates FOR SELECT TO authenticated USING (true);
CREATE POLICY wft_insert ON public.workflow_templates FOR INSERT TO authenticated WITH CHECK (auth.uid() IS NOT NULL);
CREATE POLICY wft_update ON public.workflow_templates FOR UPDATE TO authenticated
USING (created_by = auth.uid() OR public.is_admin(auth.uid()));
CREATE POLICY wft_delete ON public.workflow_templates FOR DELETE TO authenticated
USING (created_by = auth.uid() OR public.is_admin(auth.uid()));
CREATE POLICY wftt_select ON public.workflow_template_tasks FOR SELECT TO authenticated USING (true);
CREATE POLICY wftt_insert ON public.workflow_template_tasks FOR INSERT TO authenticated
WITH CHECK (EXISTS (SELECT 1 FROM public.workflow_templates wt WHERE wt.id = template_id AND (wt.created_by = auth.uid() OR public.is_admin(auth.uid()))));
CREATE POLICY wftt_update ON public.workflow_template_tasks FOR UPDATE TO authenticated
USING (EXISTS (SELECT 1 FROM public.workflow_templates wt WHERE wt.id = template_id AND (wt.created_by = auth.uid() OR public.is_admin(auth.uid()))));
CREATE POLICY wftt_delete ON public.workflow_template_tasks FOR DELETE TO authenticated
USING (EXISTS (SELECT 1 FROM public.workflow_templates wt WHERE wt.id = template_id AND (wt.created_by = auth.uid() OR public.is_admin(auth.uid()))));
-- Realtime
ALTER PUBLICATION supabase_realtime ADD TABLE public.tasks;
ALTER PUBLICATION supabase_realtime ADD TABLE public.task_comments;
ALTER PUBLICATION supabase_realtime ADD TABLE public.task_assignees;
ALTER PUBLICATION supabase_realtime ADD TABLE public.task_history;
ALTER PUBLICATION supabase_realtime ADD TABLE public.comments;
ALTER PUBLICATION supabase_realtime ADD TABLE public.notifications;