Adds an end-to-end project-optional path for Schriftsatz drafts:
- Migration 120 drops NOT NULL on paliad.submission_drafts.project_id
and rewrites the four RLS policies to gate purely on user_id when
project_id IS NULL, otherwise on paliad.can_see_project. Down
refuses to run if project-less rows exist (safer than silent
data corruption).
- SubmissionDraft.ProjectID becomes *uuid.UUID end-to-end. Service
layer skips project/parties/deadline lookups when nil and exposes
DraftPatch.ProjectID for the "Projekt zuweisen" affordance.
ListAllForUser LEFT JOINs paliad.projects so project-less drafts
surface in the global index next to project-scoped ones.
- New HTTP surface:
GET /submissions/new (picker page)
GET /submissions/draft/{draft_id} (editor for any draft)
GET /api/submissions/catalog (catalog without project)
POST /api/submission-drafts (project-less or attached)
GET/PATCH/DELETE /api/submission-drafts/{draft_id}
POST /api/submission-drafts/{draft_id}/export
Existing /api/projects/{id}/submissions/... routes remain bit-
identical so the project-scoped flow keeps working unchanged.
- Frontend: /submissions/new lists the full cross-proceeding catalog
grouped by proceeding, filterable by text + chip. Each row offers
"Ohne Projekt" (instant draft) or "Mit Projekt…" (modal picker
with autocomplete over visible projects). /submissions index gains
a prominent "Neuer Entwurf" CTA and an empty-state CTA pointing at
the picker. The editor renders a banner + "Projekt zuweisen"
action when project_id is null; assigning persists project_id and
redirects to the project-scoped URL.
Audit + project-event writes detect d.ProjectID == nil; the audit
row's scope flips to 'user' (scope_root = user_id) and the
project_events row is skipped entirely.
47 lines
1.9 KiB
SQL
47 lines
1.9 KiB
SQL
-- t-paliad-243 revert: restore NOT NULL on project_id.
|
|
--
|
|
-- The revert refuses to run if any project-less draft exists — those
|
|
-- rows would silently fail the NOT NULL re-imposition and corrupt the
|
|
-- migration runner's state. The safe revert path is to surface the
|
|
-- conflict to the operator who can decide whether to attach the rows
|
|
-- to a project or delete them before retrying the down.
|
|
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (
|
|
SELECT 1 FROM paliad.submission_drafts WHERE project_id IS NULL
|
|
) THEN
|
|
RAISE EXCEPTION
|
|
'cannot re-impose NOT NULL on paliad.submission_drafts.project_id: '
|
|
'project-less drafts exist. Attach them to a project or delete '
|
|
'them, then re-run the down migration.';
|
|
END IF;
|
|
END $$;
|
|
|
|
ALTER TABLE paliad.submission_drafts
|
|
ALTER COLUMN project_id SET NOT NULL;
|
|
|
|
DROP POLICY IF EXISTS submission_drafts_select ON paliad.submission_drafts;
|
|
CREATE POLICY submission_drafts_select
|
|
ON paliad.submission_drafts FOR SELECT TO authenticated
|
|
USING (paliad.can_see_project(project_id));
|
|
|
|
DROP POLICY IF EXISTS submission_drafts_insert ON paliad.submission_drafts;
|
|
CREATE POLICY submission_drafts_insert
|
|
ON paliad.submission_drafts FOR INSERT TO authenticated
|
|
WITH CHECK (
|
|
user_id = auth.uid()
|
|
AND paliad.can_see_project(project_id)
|
|
);
|
|
|
|
DROP POLICY IF EXISTS submission_drafts_update ON paliad.submission_drafts;
|
|
CREATE POLICY submission_drafts_update
|
|
ON paliad.submission_drafts FOR UPDATE TO authenticated
|
|
USING (user_id = auth.uid() AND paliad.can_see_project(project_id))
|
|
WITH CHECK (user_id = auth.uid() AND paliad.can_see_project(project_id));
|
|
|
|
DROP POLICY IF EXISTS submission_drafts_delete ON paliad.submission_drafts;
|
|
CREATE POLICY submission_drafts_delete
|
|
ON paliad.submission_drafts FOR DELETE TO authenticated
|
|
USING (user_id = auth.uid() AND paliad.can_see_project(project_id));
|