-- Versioned tab payloads + immutable submit snapshots + workflow/taxonomy + artifact registry. -- Apply on existing DBs: psql "$INITIATIVE_DATABASE_URL" -f migrations/002_application_storage_extensions.sql -- (use sync driver URL, not asyncpg, for psql) -- ========= DRAFT TAB SNAPSHOTS (fe0: report | application | contribution) ========= CREATE TABLE IF NOT EXISTS draft_tab_snapshots ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), initiative_id UUID NOT NULL REFERENCES initiatives(id) ON DELETE CASCADE, draft_id UUID REFERENCES drafts(id) ON DELETE SET NULL, tab TEXT NOT NULL CHECK (tab IN ('report', 'application', 'contribution')), tab_version INTEGER NOT NULL DEFAULT 1, payload JSONB NOT NULL DEFAULT '{}'::jsonb, source TEXT NOT NULL DEFAULT 'autosave', captured_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_draft_tab_snapshots_init_tab_ver ON draft_tab_snapshots (initiative_id, tab, tab_version DESC); CREATE INDEX IF NOT EXISTS idx_draft_tab_snapshots_captured ON draft_tab_snapshots (captured_at DESC); -- ========= SUBMIT SNAPSHOTS (immutable row per successful submit) ========= CREATE TABLE IF NOT EXISTS application_submit_snapshots ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), initiative_id UUID NOT NULL REFERENCES initiatives(id) ON DELETE CASCADE, submission_record_id TEXT NOT NULL, merged_tabs JSONB NOT NULL DEFAULT '{}'::jsonb, submit_metadata JSONB NOT NULL DEFAULT '{}'::jsonb, full_pdf_uri TEXT NOT NULL, captured_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_submit_snapshots_init_time ON application_submit_snapshots (initiative_id, captured_at DESC); -- ========= WORKFLOW / LIST PROJECTION (council fields) ========= CREATE TABLE IF NOT EXISTS application_workflow ( initiative_id UUID PRIMARY KEY REFERENCES initiatives(id) ON DELETE CASCADE, review_status TEXT NOT NULL DEFAULT 'not_reviewed', review_deadline DATE, reviewer JSONB, supervisor JSONB, conference JSONB, updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- ========= TAXONOMY (subjectId, groupId, topicType from fe0 ApplicationItem) ========= CREATE TABLE IF NOT EXISTS application_taxonomy ( initiative_id UUID PRIMARY KEY REFERENCES initiatives(id) ON DELETE CASCADE, subject_id TEXT NOT NULL DEFAULT '', group_id TEXT NOT NULL DEFAULT '', topic_type TEXT NOT NULL DEFAULT '', updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- ========= ARTIFACTS (PDF + future abstract/poster URIs; complements evidence_files) ========= CREATE TABLE IF NOT EXISTS application_artifacts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), initiative_id UUID NOT NULL REFERENCES initiatives(id) ON DELETE CASCADE, role TEXT NOT NULL CHECK (role IN ( 'full_pdf', 'abstract', 'poster', 'textbook_evidence', 'research_evidence', 'technical_evidence', 'other' )), storage_uri TEXT NOT NULL, original_name TEXT, mime_type TEXT NOT NULL DEFAULT 'application/pdf', byte_size BIGINT, sha256 CHAR(64), uploaded_by UUID REFERENCES users(id), uploaded_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (initiative_id, role) ); CREATE INDEX IF NOT EXISTS idx_application_artifacts_init ON application_artifacts (initiative_id);