Clean up the schema: drop unused tables, rename scopes, publishable awards
Migrations 020–023, with the code that reads each: - 020 drops people_lists, people_list_members, v_chapters and v_person_affiliations. Nothing queried any of them. - 021 renames event_sections to event_scopes and events.section_id to scope_id, finishing what 015 described. The API sends `scopes` and `scope_id`, and useEvents, EventListCards and EventCalendar take `scope`. It also inserts national/regional/partner, which only the retired seed ever created: a database built from migrations alone had no scope for the Retreats bands. - 022 drops events.sort_order and people.sort_order. Events now sort by date (upcoming soonest first, past latest first, undated last) on /events, org pages and the countdown. People were only ever sorted by sort_name on the site. Every other sort_order stays. - 023 rebuilds teams with created_at and updated_at plus a touch trigger, so the teams editor gets the same optimistic concurrency as the other entities. Awards can be drafted: is_published (added in 011) is on both descriptor halves with a Publishing group, and the award list, award page, org awards and event awards leave drafts out. The migration runner now turns foreign keys off around the per-file transactions and runs foreign_key_check before each commit. PRAGMA foreign_keys is a no-op inside a transaction, so 009's warning was right and a rebuild of a referenced table (023) couldn't be written otherwise. CLAUDE.md is updated to match. Also removes the stray src/App.tsx.save. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8790f861fe
commit
25e592bfea
19 changed files with 339 additions and 423 deletions
23
server/src/migrations/020_drop_unused.sql
Normal file
23
server/src/migrations/020_drop_unused.sql
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
-- ═══════════════════════════════════════════════════════════════
|
||||
-- 020 DROP WHAT NOTHING READS
|
||||
--
|
||||
-- people_lists and people_list_members were planned for
|
||||
-- hand-picked rosters and never got a route, a descriptor or a
|
||||
-- component. v_chapters and v_person_affiliations have no query
|
||||
-- against them: the routes read organizations and affiliations
|
||||
-- directly.
|
||||
--
|
||||
-- Views first, then the child table before its parent. No trigger
|
||||
-- references either table, and nothing has a foreign key into
|
||||
-- them, so no rebuild and no PRAGMA foreign_keys dance.
|
||||
--
|
||||
-- PRAGMA user_version; -- reads 19 before this file
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
|
||||
DROP VIEW IF EXISTS v_chapters;
|
||||
DROP VIEW IF EXISTS v_person_affiliations;
|
||||
|
||||
DROP TABLE IF EXISTS people_list_members;
|
||||
DROP TABLE IF EXISTS people_lists;
|
||||
|
||||
PRAGMA user_version = 20; -- ← set to this migration's number
|
||||
32
server/src/migrations/021_event_scopes.sql
Normal file
32
server/src/migrations/021_event_scopes.sql
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
-- ═══════════════════════════════════════════════════════════════
|
||||
-- 021 event_sections → event_scopes
|
||||
--
|
||||
-- 015 established that the table is a scope list — whose gathering
|
||||
-- an event is — and kept the old name to avoid a rename across the
|
||||
-- codebase. This is that rename: the table, and events.section_id
|
||||
-- to scope_id. The API field and the useEvents filter follow in
|
||||
-- the same change.
|
||||
--
|
||||
-- Both renames run with legacy_alter_table off (the default), so
|
||||
-- SQLite carries them into the REFERENCES clause on events, the
|
||||
-- index on section_id, and v_events, which selects e.*.
|
||||
--
|
||||
-- It also inserts the three scopes 015 only relabelled. The
|
||||
-- retired seed script was what created national, regional and
|
||||
-- partner, so a database built from migrations alone had no row
|
||||
-- for the Retreats page's three bands to point at. INSERT OR
|
||||
-- IGNORE with 015's names and order: a no-op where they exist.
|
||||
--
|
||||
-- PRAGMA user_version; -- reads 20 before this file
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
|
||||
ALTER TABLE event_sections RENAME TO event_scopes;
|
||||
|
||||
ALTER TABLE events RENAME COLUMN section_id TO scope_id;
|
||||
|
||||
INSERT OR IGNORE INTO event_scopes (id, name, sort_order) VALUES
|
||||
('national', 'National', 10),
|
||||
('regional', 'Regional', 20),
|
||||
('partner', 'Partner', 50);
|
||||
|
||||
PRAGMA user_version = 21; -- ← set to this migration's number
|
||||
36
server/src/migrations/022_drop_sort_orders.sql
Normal file
36
server/src/migrations/022_drop_sort_orders.sql
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
-- ═══════════════════════════════════════════════════════════════
|
||||
-- 022 DROP events.sort_order AND people.sort_order
|
||||
--
|
||||
-- events.sort_order dates from the seed, when event dates were
|
||||
-- free text ("March/April 2026") and a typed number was the only
|
||||
-- way to order a list. Events have real dates now, and a new one
|
||||
-- landed at 0 — ahead of everything — until someone renumbered.
|
||||
-- The routes order by date instead.
|
||||
--
|
||||
-- people.sort_order was never read by the public site: people
|
||||
-- sort by sort_name everywhere. It only reordered the admin list.
|
||||
--
|
||||
-- Every other sort_order stays. On child collections it is the
|
||||
-- row's position, written by the admin engine from drag order;
|
||||
-- on organizations, teams, awards, timeline_entries and
|
||||
-- event_scopes it is an ordering the site actually uses.
|
||||
--
|
||||
-- SQLite refuses DROP COLUMN while an index names the column, so
|
||||
-- the three indexes that do go first and come back without it.
|
||||
-- No view names either column (v_events selects e.*).
|
||||
--
|
||||
-- PRAGMA user_version; -- reads 21 before this file
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
|
||||
DROP INDEX IF EXISTS events_section_idx;
|
||||
DROP INDEX IF EXISTS events_type_idx;
|
||||
DROP INDEX IF EXISTS people_sort_idx;
|
||||
|
||||
ALTER TABLE events DROP COLUMN sort_order;
|
||||
ALTER TABLE people DROP COLUMN sort_order;
|
||||
|
||||
CREATE INDEX events_scope_idx ON events (scope_id, is_published, starts_on);
|
||||
CREATE INDEX events_type_idx ON events (event_type, is_published, starts_on);
|
||||
CREATE INDEX people_sort_idx ON people (is_published, sort_name);
|
||||
|
||||
PRAGMA user_version = 22; -- ← set to this migration's number
|
||||
87
server/src/migrations/023_teams_updated_at.sql
Normal file
87
server/src/migrations/023_teams_updated_at.sql
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
-- ═══════════════════════════════════════════════════════════════
|
||||
-- 023 teams GETS created_at AND updated_at
|
||||
--
|
||||
-- Every other top-level entity has updated_at, which the admin
|
||||
-- engine compares on save so two people editing the same row
|
||||
-- can't silently overwrite each other. teams was left out because
|
||||
-- ADD COLUMN can't take DEFAULT (datetime('now')) on a STRICT
|
||||
-- table, so this is the rebuild — the recipe from the SQLite docs.
|
||||
--
|
||||
-- The runner turns foreign keys off around every migration and
|
||||
-- runs foreign_key_check before committing (see migrate() in
|
||||
-- db.js). So DROP TABLE doesn't touch affiliations, whose
|
||||
-- (team_id, org_id) reference keeps pointing at the name `teams`,
|
||||
-- which the rename puts back underneath it.
|
||||
--
|
||||
-- legacy_alter_table is on for the rename: v_org_leadership,
|
||||
-- v_timeline and the timeline_entries ref triggers name `teams`.
|
||||
-- Between the DROP and the RENAME that table doesn't exist, and
|
||||
-- the modern rename re-parses every view and trigger and fails on
|
||||
-- them. Legacy mode leaves them alone; they resolve to the new
|
||||
-- table by name. Turned back off before the end, since the
|
||||
-- connection outlives this file.
|
||||
--
|
||||
-- DROP TABLE takes the two AFTER DELETE triggers with it (and does
|
||||
-- not fire them), so they're recreated below exactly as 002 and
|
||||
-- 007 wrote them, along with teams_touch. Triggers go last: the
|
||||
-- runner may drop statements that follow a BEGIN...END body.
|
||||
--
|
||||
-- Verify after:
|
||||
--
|
||||
-- PRAGMA user_version; -- 23
|
||||
-- PRAGMA foreign_key_check; -- no rows
|
||||
-- SELECT name FROM sqlite_master WHERE tbl_name = 'teams';
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
|
||||
PRAGMA legacy_alter_table = ON;
|
||||
|
||||
CREATE TABLE teams_new (
|
||||
id TEXT PRIMARY KEY, -- slug: 'board', 'nw-leadership'
|
||||
org_id TEXT NOT NULL REFERENCES organizations (id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
tagline TEXT,
|
||||
color TEXT,
|
||||
logo TEXT,
|
||||
is_published INTEGER NOT NULL DEFAULT 1 CHECK (is_published IN (0, 1)),
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
UNIQUE (id, org_id)
|
||||
) STRICT;
|
||||
|
||||
-- Columns listed explicitly rather than SELECT *, so this breaks
|
||||
-- loudly if the old shape isn't what this file assumes. Existing
|
||||
-- rows get the migration time for both timestamps.
|
||||
INSERT INTO teams_new
|
||||
(id, org_id, name, tagline, color, logo, is_published, sort_order)
|
||||
SELECT
|
||||
id, org_id, name, tagline, color, logo, is_published, sort_order
|
||||
FROM teams;
|
||||
|
||||
DROP TABLE teams;
|
||||
|
||||
ALTER TABLE teams_new RENAME TO teams;
|
||||
|
||||
CREATE INDEX teams_org_idx ON teams (org_id, sort_order);
|
||||
|
||||
PRAGMA legacy_alter_table = OFF;
|
||||
|
||||
CREATE TRIGGER teams_cleanup
|
||||
AFTER DELETE ON teams
|
||||
BEGIN
|
||||
DELETE FROM content_blocks WHERE owner_kind = 'team' AND owner_id = old.id;
|
||||
DELETE FROM links WHERE owner_kind = 'team' AND owner_id = old.id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER timeline_teams_cleanup
|
||||
AFTER DELETE ON teams
|
||||
BEGIN
|
||||
DELETE FROM timeline_entries WHERE ref_kind = 'team' AND ref_id = old.id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER teams_touch
|
||||
AFTER UPDATE ON teams
|
||||
FOR EACH ROW WHEN new.updated_at = old.updated_at
|
||||
BEGIN
|
||||
UPDATE teams SET updated_at = datetime('now') WHERE id = new.id;
|
||||
END;
|
||||
Loading…
Add table
Add a link
Reference in a new issue