From 865347f68282dfb62cc934dea8d98f80294d33fb Mon Sep 17 00:00:00 2001 From: Jonathan Hvid Date: Mon, 11 May 2026 15:46:53 +0200 Subject: [PATCH] =?UTF-8?q?feat(db):=20migration=200004=20=E2=80=94=20phas?= =?UTF-8?q?e=202=20schema=20+=204=20new=20tokens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit users: adds pull_quote, member_number (unique partial index, NULLs allowed), focus_tags. title already exists from 0003 — not re-added. Backfills member_number for existing cab users in COALESCE(cab_joined_date, created_at) asc, tiebreak id asc. Lars gets #1. events: rebuild required to widen the kind CHECK constraint (SQLite can't ALTER it in place). Adds working_session as a new kind. Same rebuild adds four new columns: audience, duration_label, action_label, notes_url. Data preserved. dispatches: new entity, status enum draft/published/archived, kind enum decision/update/behind_the_scenes/note. published_at nullable until publishPulse-equivalent stamps it. Indexes on (status, published_at) and author_id. Tokens (src/styles/tokens.css): adds --surface-card, --surface-card-border, --ink, --ink-text, --ink-muted. Spec called these out as the only additions; existing --radius-lg covers the spec's --border-radius-lg reference. Co-Authored-By: Claude Opus 4.7 (1M context) --- migrations/0004_phase_two.sql | 74 +++++++++++++++++++++++++++++++++++ src/styles/tokens.css | 7 ++++ 2 files changed, 81 insertions(+) create mode 100644 migrations/0004_phase_two.sql diff --git a/migrations/0004_phase_two.sql b/migrations/0004_phase_two.sql new file mode 100644 index 0000000..1270608 --- /dev/null +++ b/migrations/0004_phase_two.sql @@ -0,0 +1,74 @@ +-- Phase 2 — council portal revisions +-- Adds pull_quote/member_number/focus_tags to users (title already exists +-- from 0003), rebuilds events to widen the kind CHECK + add four columns, +-- creates dispatches, backfills member_numbers for existing cab users. + +-- ── users: new columns ───────────────────────────────────────────── +ALTER TABLE users ADD COLUMN pull_quote TEXT; +ALTER TABLE users ADD COLUMN member_number INTEGER; +ALTER TABLE users ADD COLUMN focus_tags TEXT; -- JSON array, capped 3 × 24 chars at app layer + +CREATE UNIQUE INDEX idx_users_member_number ON users(member_number) WHERE member_number IS NOT NULL; + +-- Backfill member_numbers for existing CAB users in member-since order, +-- tiebreaking on user.id ascending. Deterministic across machines. +WITH ranked AS ( + SELECT id, + ROW_NUMBER() OVER (ORDER BY COALESCE(cab_joined_date, created_at) ASC, id ASC) AS rn + FROM users + WHERE role = 'cab' +) +UPDATE users + SET member_number = (SELECT rn FROM ranked WHERE ranked.id = users.id) + WHERE role = 'cab' AND member_number IS NULL; + +-- ── events: rebuild for widened kind enum + 4 new columns ───────── +-- SQLite can't ALTER a CHECK constraint; full rebuild required. +CREATE TABLE events_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + slug TEXT UNIQUE NOT NULL, + title TEXT NOT NULL, + kind TEXT NOT NULL DEFAULT 'dinner' + CHECK(kind IN ('dinner','office_hours','summit','virtual','working_session')), + description TEXT NOT NULL DEFAULT '', + location TEXT NOT NULL DEFAULT '', + starts_at TEXT NOT NULL, + ends_at TEXT, + capacity INTEGER, + photo_url TEXT, + audience TEXT, + duration_label TEXT, + action_label TEXT, + notes_url TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + created_by INTEGER REFERENCES users(id) +); + +INSERT INTO events_new + (id, slug, title, kind, description, location, starts_at, ends_at, capacity, photo_url, created_at, created_by) +SELECT id, slug, title, kind, description, location, starts_at, ends_at, capacity, photo_url, created_at, created_by +FROM events; + +DROP TABLE events; +ALTER TABLE events_new RENAME TO events; + +CREATE INDEX idx_events_starts_at ON events(starts_at); + +-- ── dispatches ──────────────────────────────────────────────────── +CREATE TABLE dispatches ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + body TEXT NOT NULL, + excerpt TEXT, + kind TEXT NOT NULL DEFAULT 'note' + CHECK (kind IN ('decision','update','behind_the_scenes','note')), + author_id INTEGER NOT NULL REFERENCES users(id), + status TEXT NOT NULL DEFAULT 'draft' + CHECK (status IN ('draft','published','archived')), + published_at TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) +); + +CREATE INDEX idx_dispatches_published ON dispatches(status, published_at); +CREATE INDEX idx_dispatches_author ON dispatches(author_id); diff --git a/src/styles/tokens.css b/src/styles/tokens.css index fe79423..b5f92b7 100644 --- a/src/styles/tokens.css +++ b/src/styles/tokens.css @@ -41,6 +41,13 @@ --pigment-indigo: #5a6d83; --pigment-heather: #8d7a85; + /* --- Phase 2: white card surfaces + deep ink accent --- */ + --surface-card: #ffffff; + --surface-card-border: rgba(0, 0, 0, 0.08); + --ink: #2c3a52; /* deep indigo — membership card + event hero */ + --ink-text: #e8e0d0; /* readable cream on --ink */ + --ink-muted: #b8a989; /* muted label tone on --ink */ + /* --- Semantic state mappings --- */ --color-success: var(--pigment-copper); --color-warning: var(--pigment-ochre);