CREATE EXTENSION IF NOT EXISTS "pgcrypto"; CREATE TABLE IF NOT EXISTS pages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id TEXT NOT NULL, title TEXT NOT NULL, date DATE NOT NULL DEFAULT CURRENT_DATE, raw_text TEXT NOT NULL, annotations JSONB DEFAULT '{}'::jsonb, metadata JSONB DEFAULT '{}'::jsonb, -- category, summary, tags (for smart browser) created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_pages_user ON pages(user_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_pages_metadata ON pages USING gin(metadata); CREATE TABLE IF NOT EXISTS concepts ( id TEXT PRIMARY KEY, name TEXT NOT NULL, cefr_level TEXT NOT NULL, family TEXT NOT NULL, covered_on DATE ); CREATE TABLE IF NOT EXISTS exercises ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id TEXT NOT NULL, page_id UUID REFERENCES pages(id) ON DELETE CASCADE, kind TEXT NOT NULL, prompt TEXT, model_answer TEXT, content JSONB DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_exercises_user ON exercises(user_id, created_at DESC); -- Append-only participation ledger — never deduct points CREATE TABLE IF NOT EXISTS points ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id TEXT NOT NULL, reason TEXT NOT NULL, amount INT NOT NULL CHECK (amount > 0), earned_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_points_user ON points(user_id, earned_at DESC); -- Tracks which pre-generated sample images a user has already practiced -- with, so the matched-image visual exercise (Day 4) doesn't repeat one -- before cycling through the rest of the set. CREATE TABLE IF NOT EXISTS user_image_usage ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id TEXT NOT NULL, image_id TEXT NOT NULL, used_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_user_image_usage ON user_image_usage(user_id, image_id); -- PRIVATE table (defined here but never written by the public Space) CREATE TABLE IF NOT EXISTS mistakes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id TEXT NOT NULL, concept_id TEXT REFERENCES concepts(id), category TEXT NOT NULL, user_answer TEXT, correct_answer TEXT, explanation TEXT, made_on TIMESTAMPTZ DEFAULT NOW() );