-- Department Dashboards: composable workspace for event departments -- Each department gets a dashboard with configurable module panels -- ============================================================ -- 1. Module types enum -- ============================================================ CREATE TYPE module_type AS ENUM ( 'kanban', 'files', 'checklist', 'notes', 'schedule', 'contacts' ); -- ============================================================ -- 2. Layout presets enum -- ============================================================ CREATE TYPE layout_preset AS ENUM ( 'single', 'split', 'grid', 'focus_sidebar', 'custom' ); -- ============================================================ -- 3. Department Dashboards -- ============================================================ CREATE TABLE department_dashboards ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), department_id UUID NOT NULL REFERENCES event_departments(id) ON DELETE CASCADE, layout layout_preset NOT NULL DEFAULT 'split', created_by UUID REFERENCES auth.users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now(), UNIQUE(department_id) ); CREATE INDEX idx_dept_dashboards_dept ON department_dashboards(department_id); -- ============================================================ -- 4. Dashboard Panels (modules placed on a dashboard) -- ============================================================ CREATE TABLE dashboard_panels ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), dashboard_id UUID NOT NULL REFERENCES department_dashboards(id) ON DELETE CASCADE, module module_type NOT NULL, position INT NOT NULL DEFAULT 0, width TEXT NOT NULL DEFAULT 'half' CHECK (width IN ('full', 'half', 'third', 'two_thirds')), config JSONB DEFAULT '{}', created_at TIMESTAMPTZ DEFAULT now(), UNIQUE(dashboard_id, position) ); CREATE INDEX idx_dashboard_panels_dashboard ON dashboard_panels(dashboard_id); -- ============================================================ -- 5. Checklists (scoped to department) -- ============================================================ CREATE TABLE department_checklists ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), department_id UUID NOT NULL REFERENCES event_departments(id) ON DELETE CASCADE, title TEXT NOT NULL DEFAULT 'Checklist', sort_order INT NOT NULL DEFAULT 0, created_by UUID REFERENCES auth.users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX idx_dept_checklists_dept ON department_checklists(department_id); CREATE TABLE department_checklist_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), checklist_id UUID NOT NULL REFERENCES department_checklists(id) ON DELETE CASCADE, content TEXT NOT NULL, is_completed BOOLEAN NOT NULL DEFAULT false, assigned_to UUID REFERENCES auth.users(id) ON DELETE SET NULL, due_date TIMESTAMPTZ, sort_order INT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX idx_dept_checklist_items_checklist ON department_checklist_items(checklist_id); CREATE INDEX idx_dept_checklist_items_assigned ON department_checklist_items(assigned_to); -- ============================================================ -- 6. Department Notes (simple rich text notes) -- ============================================================ CREATE TABLE department_notes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), department_id UUID NOT NULL REFERENCES event_departments(id) ON DELETE CASCADE, title TEXT NOT NULL DEFAULT 'Untitled Note', content TEXT DEFAULT '', sort_order INT NOT NULL DEFAULT 0, created_by UUID REFERENCES auth.users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX idx_dept_notes_dept ON department_notes(department_id); -- ============================================================ -- 7. Add enabled_modules to event_departments -- ============================================================ ALTER TABLE event_departments ADD COLUMN enabled_modules module_type[] NOT NULL DEFAULT ARRAY['kanban'::module_type, 'files'::module_type, 'checklist'::module_type]; -- ============================================================ -- 8. RLS Policies -- ============================================================ ALTER TABLE department_dashboards ENABLE ROW LEVEL SECURITY; ALTER TABLE dashboard_panels ENABLE ROW LEVEL SECURITY; ALTER TABLE department_checklists ENABLE ROW LEVEL SECURITY; ALTER TABLE department_checklist_items ENABLE ROW LEVEL SECURITY; ALTER TABLE department_notes ENABLE ROW LEVEL SECURITY; -- Helper: check if user is org member for a department -- (department → event → org → org_members) -- Department Dashboards CREATE POLICY "Org members can view department dashboards" ON department_dashboards FOR SELECT USING (EXISTS ( SELECT 1 FROM event_departments ed JOIN events e ON ed.event_id = e.id JOIN org_members om ON e.org_id = om.org_id WHERE ed.id = department_dashboards.department_id AND om.user_id = auth.uid() )); CREATE POLICY "Editors can manage department dashboards" ON department_dashboards FOR ALL USING (EXISTS ( SELECT 1 FROM event_departments ed JOIN events e ON ed.event_id = e.id JOIN org_members om ON e.org_id = om.org_id WHERE ed.id = department_dashboards.department_id AND om.user_id = auth.uid() AND om.role IN ('owner', 'admin', 'editor') )); -- Dashboard Panels CREATE POLICY "Org members can view dashboard panels" ON dashboard_panels FOR SELECT USING (EXISTS ( SELECT 1 FROM department_dashboards dd JOIN event_departments ed ON dd.department_id = ed.id JOIN events e ON ed.event_id = e.id JOIN org_members om ON e.org_id = om.org_id WHERE dd.id = dashboard_panels.dashboard_id AND om.user_id = auth.uid() )); CREATE POLICY "Editors can manage dashboard panels" ON dashboard_panels FOR ALL USING (EXISTS ( SELECT 1 FROM department_dashboards dd JOIN event_departments ed ON dd.department_id = ed.id JOIN events e ON ed.event_id = e.id JOIN org_members om ON e.org_id = om.org_id WHERE dd.id = dashboard_panels.dashboard_id AND om.user_id = auth.uid() AND om.role IN ('owner', 'admin', 'editor') )); -- Department Checklists CREATE POLICY "Org members can view department checklists" ON department_checklists FOR SELECT USING (EXISTS ( SELECT 1 FROM event_departments ed JOIN events e ON ed.event_id = e.id JOIN org_members om ON e.org_id = om.org_id WHERE ed.id = department_checklists.department_id AND om.user_id = auth.uid() )); CREATE POLICY "Editors can manage department checklists" ON department_checklists FOR ALL USING (EXISTS ( SELECT 1 FROM event_departments ed JOIN events e ON ed.event_id = e.id JOIN org_members om ON e.org_id = om.org_id WHERE ed.id = department_checklists.department_id AND om.user_id = auth.uid() AND om.role IN ('owner', 'admin', 'editor') )); -- Department Checklist Items CREATE POLICY "Org members can view dept checklist items" ON department_checklist_items FOR SELECT USING (EXISTS ( SELECT 1 FROM department_checklists dc JOIN event_departments ed ON dc.department_id = ed.id JOIN events e ON ed.event_id = e.id JOIN org_members om ON e.org_id = om.org_id WHERE dc.id = department_checklist_items.checklist_id AND om.user_id = auth.uid() )); CREATE POLICY "Editors can manage dept checklist items" ON department_checklist_items FOR ALL USING (EXISTS ( SELECT 1 FROM department_checklists dc JOIN event_departments ed ON dc.department_id = ed.id JOIN events e ON ed.event_id = e.id JOIN org_members om ON e.org_id = om.org_id WHERE dc.id = department_checklist_items.checklist_id AND om.user_id = auth.uid() AND om.role IN ('owner', 'admin', 'editor') )); -- Department Notes CREATE POLICY "Org members can view department notes" ON department_notes FOR SELECT USING (EXISTS ( SELECT 1 FROM event_departments ed JOIN events e ON ed.event_id = e.id JOIN org_members om ON e.org_id = om.org_id WHERE ed.id = department_notes.department_id AND om.user_id = auth.uid() )); CREATE POLICY "Editors can manage department notes" ON department_notes FOR ALL USING (EXISTS ( SELECT 1 FROM event_departments ed JOIN events e ON ed.event_id = e.id JOIN org_members om ON e.org_id = om.org_id WHERE ed.id = department_notes.department_id AND om.user_id = auth.uid() AND om.role IN ('owner', 'admin', 'editor') )); -- ============================================================ -- 9. Enable realtime -- ============================================================ ALTER PUBLICATION supabase_realtime ADD TABLE department_checklists; ALTER PUBLICATION supabase_realtime ADD TABLE department_checklist_items; ALTER PUBLICATION supabase_realtime ADD TABLE department_notes; ALTER PUBLICATION supabase_realtime ADD TABLE dashboard_panels; -- ============================================================ -- 10. Auto-create dashboard when department is created -- ============================================================ CREATE OR REPLACE FUNCTION public.create_department_dashboard() RETURNS TRIGGER AS $$ DECLARE dash_id UUID; mod module_type; pos INT := 0; BEGIN -- Create dashboard INSERT INTO public.department_dashboards (department_id, layout) VALUES (NEW.id, 'split') RETURNING id INTO dash_id; -- Create panels for each enabled module FOREACH mod IN ARRAY NEW.enabled_modules LOOP INSERT INTO public.dashboard_panels (dashboard_id, module, position, width) VALUES (dash_id, mod, pos, CASE WHEN pos = 0 THEN 'half' ELSE 'half' END); pos := pos + 1; END LOOP; -- Auto-create a default checklist if checklist module is enabled IF 'checklist' = ANY(NEW.enabled_modules) THEN INSERT INTO public.department_checklists (department_id, title, sort_order) VALUES (NEW.id, 'General', 0); END IF; -- Auto-create a default note if notes module is enabled IF 'notes' = ANY(NEW.enabled_modules) THEN INSERT INTO public.department_notes (department_id, title, content, sort_order) VALUES (NEW.id, 'Meeting Notes', '', 0); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql SECURITY DEFINER; CREATE TRIGGER on_department_created_setup_dashboard AFTER INSERT ON event_departments FOR EACH ROW EXECUTE FUNCTION public.create_department_dashboard();