Files
myeasycms-v2/apps/web/supabase/migrations/20260413000001_sitzungsprotokolle.sql
Zaid Marzguioui ebd0fd4638
Some checks failed
Workflow / ʦ TypeScript (push) Failing after 6m26s
Workflow / ⚫️ Test (push) Has been skipped
feat: complete CMS v2 with Docker, Fischerei, Meetings, Verband modules + UX audit fixes
Major changes:
- Docker Compose: full Supabase stack (11 services) equivalent to supabase CLI
- Fischerei module: 16 DB tables, waters/species/stocking/catch books/competitions
- Sitzungsprotokolle module: meeting protocols, agenda items, task tracking
- Verbandsverwaltung module: federation management, member clubs, contacts, fees
- Per-account module activation via Modules page toggle
- Site Builder: live CMS data in Puck blocks (courses, events, membership registration)
- Public registration APIs: course signup, event registration, membership application
- Document generation: PDF member cards, Excel reports, HTML labels
- Landing page: real Com.BISS content (no filler text)
- UX audit fixes: AccountNotFound component, shared status badges, confirm dialog,
  pagination, duplicate heading removal, emoji→badge replacement, a11y fixes
- QA: healthcheck fix, API auth fix, enum mismatch fix, password required attribute
2026-03-31 16:35:46 +02:00

225 lines
8.4 KiB
SQL

/*
* -------------------------------------------------------
* Sitzungsprotokolle (Meeting Protocols) Schema
* Meeting minutes, agenda items, tasks, attachments
* -------------------------------------------------------
*/
-- =====================================================
-- 1. Extend app_permissions
-- =====================================================
ALTER TYPE public.app_permissions ADD VALUE IF NOT EXISTS 'meetings.read';
ALTER TYPE public.app_permissions ADD VALUE IF NOT EXISTS 'meetings.write';
-- =====================================================
-- 2. Enums
-- =====================================================
CREATE TYPE public.meeting_item_status AS ENUM(
'offen',
'in_bearbeitung',
'erledigt',
'vertagt'
);
-- =====================================================
-- 3. meeting_protocols
-- =====================================================
CREATE TABLE IF NOT EXISTS public.meeting_protocols (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
-- Identity
title text NOT NULL,
protocol_number text,
-- Schedule
meeting_date date NOT NULL,
meeting_time time,
end_time time,
location text,
-- Participants
chair text,
recorder text,
attendees jsonb NOT NULL DEFAULT '[]'::jsonb,
absent jsonb NOT NULL DEFAULT '[]'::jsonb,
-- Content
summary text,
-- Status workflow
status text NOT NULL DEFAULT 'draft'
CHECK (status IN ('draft', 'review', 'approved', 'archived')),
-- Follow-up
next_meeting_date date,
-- Flags
is_archived boolean NOT NULL DEFAULT false,
-- Meta
created_by uuid REFERENCES auth.users(id) ON DELETE SET NULL,
updated_by uuid REFERENCES auth.users(id) ON DELETE SET NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX ix_meeting_protocols_account ON public.meeting_protocols(account_id);
CREATE INDEX ix_meeting_protocols_date ON public.meeting_protocols(account_id, meeting_date DESC);
CREATE INDEX ix_meeting_protocols_status ON public.meeting_protocols(account_id, status);
CREATE INDEX ix_meeting_protocols_archived ON public.meeting_protocols(account_id, is_archived);
ALTER TABLE public.meeting_protocols ENABLE ROW LEVEL SECURITY;
REVOKE ALL ON public.meeting_protocols FROM authenticated, service_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON public.meeting_protocols TO authenticated;
GRANT ALL ON public.meeting_protocols TO service_role;
CREATE POLICY meeting_protocols_select ON public.meeting_protocols FOR SELECT TO authenticated
USING (public.has_role_on_account(account_id));
CREATE POLICY meeting_protocols_insert ON public.meeting_protocols FOR INSERT TO authenticated
WITH CHECK (public.has_permission(auth.uid(), account_id, 'meetings.write'::public.app_permissions));
CREATE POLICY meeting_protocols_update ON public.meeting_protocols FOR UPDATE TO authenticated
USING (public.has_permission(auth.uid(), account_id, 'meetings.write'::public.app_permissions));
CREATE POLICY meeting_protocols_delete ON public.meeting_protocols FOR DELETE TO authenticated
USING (public.has_permission(auth.uid(), account_id, 'meetings.write'::public.app_permissions));
CREATE TRIGGER trg_meeting_protocols_updated_at
BEFORE UPDATE ON public.meeting_protocols
FOR EACH ROW EXECUTE FUNCTION public.update_account_settings_timestamp();
-- =====================================================
-- 4. meeting_protocol_items (agenda items / TOPs)
-- =====================================================
CREATE TABLE IF NOT EXISTS public.meeting_protocol_items (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
protocol_id uuid NOT NULL REFERENCES public.meeting_protocols(id) ON DELETE CASCADE,
-- Ordering
item_number integer NOT NULL DEFAULT 0,
sort_order integer NOT NULL DEFAULT 0,
-- Content
title text NOT NULL,
content text,
-- Classification
item_type text NOT NULL DEFAULT 'information'
CHECK (item_type IN ('information', 'discussion', 'decision', 'task')),
-- Decision tracking
decision_text text,
-- Task tracking
status public.meeting_item_status NOT NULL DEFAULT 'offen',
responsible_person text,
due_date date,
-- Meta
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX ix_meeting_protocol_items_protocol ON public.meeting_protocol_items(protocol_id);
CREATE INDEX ix_meeting_protocol_items_status ON public.meeting_protocol_items(status);
CREATE INDEX ix_meeting_protocol_items_type ON public.meeting_protocol_items(item_type);
CREATE INDEX ix_meeting_protocol_items_due ON public.meeting_protocol_items(due_date)
WHERE due_date IS NOT NULL AND status != 'erledigt';
ALTER TABLE public.meeting_protocol_items ENABLE ROW LEVEL SECURITY;
REVOKE ALL ON public.meeting_protocol_items FROM authenticated, service_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON public.meeting_protocol_items TO authenticated;
GRANT ALL ON public.meeting_protocol_items TO service_role;
CREATE POLICY meeting_protocol_items_select ON public.meeting_protocol_items FOR SELECT TO authenticated
USING (EXISTS (
SELECT 1 FROM public.meeting_protocols mp
WHERE mp.id = meeting_protocol_items.protocol_id
AND public.has_role_on_account(mp.account_id)
));
CREATE POLICY meeting_protocol_items_mutate ON public.meeting_protocol_items FOR ALL TO authenticated
USING (EXISTS (
SELECT 1 FROM public.meeting_protocols mp
WHERE mp.id = meeting_protocol_items.protocol_id
AND public.has_permission(auth.uid(), mp.account_id, 'meetings.write'::public.app_permissions)
));
CREATE TRIGGER trg_meeting_protocol_items_updated_at
BEFORE UPDATE ON public.meeting_protocol_items
FOR EACH ROW EXECUTE FUNCTION public.update_account_settings_timestamp();
-- =====================================================
-- 5. meeting_protocol_attachments
-- =====================================================
CREATE TABLE IF NOT EXISTS public.meeting_protocol_attachments (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
protocol_id uuid NOT NULL REFERENCES public.meeting_protocols(id) ON DELETE CASCADE,
item_id uuid REFERENCES public.meeting_protocol_items(id) ON DELETE SET NULL,
-- File info
file_name text NOT NULL,
file_path text NOT NULL,
file_size bigint,
content_type text,
-- Meta
created_by uuid REFERENCES auth.users(id) ON DELETE SET NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX ix_meeting_protocol_attachments_protocol ON public.meeting_protocol_attachments(protocol_id);
CREATE INDEX ix_meeting_protocol_attachments_item ON public.meeting_protocol_attachments(item_id)
WHERE item_id IS NOT NULL;
ALTER TABLE public.meeting_protocol_attachments ENABLE ROW LEVEL SECURITY;
REVOKE ALL ON public.meeting_protocol_attachments FROM authenticated, service_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON public.meeting_protocol_attachments TO authenticated;
GRANT ALL ON public.meeting_protocol_attachments TO service_role;
CREATE POLICY meeting_protocol_attachments_select ON public.meeting_protocol_attachments FOR SELECT TO authenticated
USING (EXISTS (
SELECT 1 FROM public.meeting_protocols mp
WHERE mp.id = meeting_protocol_attachments.protocol_id
AND public.has_role_on_account(mp.account_id)
));
CREATE POLICY meeting_protocol_attachments_mutate ON public.meeting_protocol_attachments FOR ALL TO authenticated
USING (EXISTS (
SELECT 1 FROM public.meeting_protocols mp
WHERE mp.id = meeting_protocol_attachments.protocol_id
AND public.has_permission(auth.uid(), mp.account_id, 'meetings.write'::public.app_permissions)
));
-- =====================================================
-- 6. View: open_meeting_tasks
-- =====================================================
CREATE OR REPLACE VIEW public.open_meeting_tasks AS
SELECT
mpi.id AS item_id,
mpi.protocol_id,
mp.account_id,
mp.title AS protocol_title,
mp.meeting_date,
mpi.item_number,
mpi.title AS task_title,
mpi.content AS task_description,
mpi.responsible_person,
mpi.due_date,
mpi.status,
CASE
WHEN mpi.due_date < current_date AND mpi.status != 'erledigt' THEN true
ELSE false
END AS is_overdue
FROM public.meeting_protocol_items mpi
JOIN public.meeting_protocols mp ON mp.id = mpi.protocol_id
WHERE mpi.item_type = 'task'
AND mpi.status != 'erledigt';
-- Grant access to the view (RLS on underlying tables still applies)
GRANT SELECT ON public.open_meeting_tasks TO authenticated;
GRANT SELECT ON public.open_meeting_tasks TO service_role;