-- 161_manual_module.sql -- Manual module: articles, steps and contextual relations CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE TABLE IF NOT EXISTS manual_articles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title VARCHAR(255) NOT NULL, slug VARCHAR(255) NOT NULL UNIQUE, content TEXT NOT NULL, summary TEXT, module VARCHAR(80) NOT NULL, tags JSONB NOT NULL DEFAULT '[]'::jsonb, difficulty VARCHAR(20) NOT NULL DEFAULT 'beginner' CHECK (difficulty IN ('beginner', 'advanced')), use_count INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMP WITH TIME ZONE DEFAULT NULL ); CREATE TABLE IF NOT EXISTS manual_steps ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), manual_id UUID NOT NULL REFERENCES manual_articles(id) ON DELETE CASCADE, step_number INTEGER NOT NULL CHECK (step_number > 0), title VARCHAR(255) NOT NULL, content TEXT NOT NULL, image_url TEXT, video_url TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, UNIQUE (manual_id, step_number) ); CREATE TABLE IF NOT EXISTS manual_relations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), manual_id UUID NOT NULL REFERENCES manual_articles(id) ON DELETE CASCADE, related_module VARCHAR(80), related_tag VARCHAR(120), related_sag_type VARCHAR(120), related_manual_id UUID REFERENCES manual_articles(id) ON DELETE SET NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_manual_articles_module ON manual_articles(module); CREATE INDEX IF NOT EXISTS idx_manual_articles_difficulty ON manual_articles(difficulty); CREATE INDEX IF NOT EXISTS idx_manual_articles_deleted_at ON manual_articles(deleted_at); CREATE INDEX IF NOT EXISTS idx_manual_articles_use_count ON manual_articles(use_count DESC); CREATE INDEX IF NOT EXISTS idx_manual_steps_manual_id_step ON manual_steps(manual_id, step_number); CREATE INDEX IF NOT EXISTS idx_manual_relations_manual_id ON manual_relations(manual_id); CREATE INDEX IF NOT EXISTS idx_manual_relations_related_module ON manual_relations(related_module); CREATE INDEX IF NOT EXISTS idx_manual_relations_related_tag ON manual_relations(related_tag); CREATE INDEX IF NOT EXISTS idx_manual_relations_related_sag_type ON manual_relations(related_sag_type); -- Keep article updated_at fresh on content changes. CREATE OR REPLACE FUNCTION bump_manual_article_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = CURRENT_TIMESTAMP; RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trg_manual_articles_updated_at ON manual_articles; CREATE TRIGGER trg_manual_articles_updated_at BEFORE UPDATE ON manual_articles FOR EACH ROW EXECUTE FUNCTION bump_manual_article_updated_at(); DROP TRIGGER IF EXISTS trg_manual_steps_updated_at ON manual_steps; CREATE TRIGGER trg_manual_steps_updated_at BEFORE UPDATE ON manual_steps FOR EACH ROW EXECUTE FUNCTION bump_manual_article_updated_at();