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
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* -------------------------------------------------------
|
||||
* 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;
|
||||
Reference in New Issue
Block a user