-- Supabase schema for FIPI tasks and user practice attempts. CREATE EXTENSION IF NOT EXISTS pg_trgm; CREATE TABLE IF NOT EXISTS public.fipi_tasks ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, content TEXT NOT NULL, source_url TEXT UNIQUE NOT NULL, task_type TEXT DEFAULT 'other', images TEXT[] DEFAULT '{}', variants TEXT[] DEFAULT '{}', rubert_analysis JSONB DEFAULT '{}', scraped_at TIMESTAMPTZ DEFAULT NOW(), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_fipi_tasks_task_type ON public.fipi_tasks(task_type); CREATE INDEX IF NOT EXISTS idx_fipi_tasks_scraped_at ON public.fipi_tasks(scraped_at DESC); CREATE INDEX IF NOT EXISTS idx_fipi_tasks_source_url ON public.fipi_tasks(source_url); CREATE INDEX IF NOT EXISTS idx_fipi_tasks_title ON public.fipi_tasks USING gin(title gin_trgm_ops); CREATE INDEX IF NOT EXISTS idx_fipi_tasks_content ON public.fipi_tasks USING gin(content gin_trgm_ops); ALTER TABLE public.fipi_tasks ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Public can view all tasks" ON public.fipi_tasks; DROP POLICY IF EXISTS "Service key can insert tasks" ON public.fipi_tasks; DROP POLICY IF EXISTS "Service key can update tasks" ON public.fipi_tasks; DROP POLICY IF EXISTS "Service key can delete tasks" ON public.fipi_tasks; CREATE POLICY "Public can view all tasks" ON public.fipi_tasks FOR SELECT USING (true); CREATE POLICY "Service key can insert tasks" ON public.fipi_tasks FOR INSERT WITH CHECK (true); CREATE POLICY "Service key can update tasks" ON public.fipi_tasks FOR UPDATE USING (true); CREATE POLICY "Service key can delete tasks" ON public.fipi_tasks FOR DELETE USING (true); CREATE OR REPLACE FUNCTION public.update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS update_fipi_tasks_updated_at ON public.fipi_tasks; CREATE TRIGGER update_fipi_tasks_updated_at BEFORE UPDATE ON public.fipi_tasks FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column(); CREATE OR REPLACE VIEW public.fipi_tasks_stats AS SELECT COUNT(*) AS total_tasks, COUNT(*) FILTER (WHERE task_type = 'writing') AS writing_tasks, COUNT(*) FILTER (WHERE task_type = 'test') AS test_tasks, COUNT(*) FILTER (WHERE task_type = 'listening') AS listening_tasks, COUNT(*) FILTER (WHERE task_type = 'reading') AS reading_tasks, COUNT(*) FILTER (WHERE task_type = 'other') AS other_tasks, MAX(scraped_at) AS last_scrape FROM public.fipi_tasks; CREATE TABLE IF NOT EXISTS public.task_attempts ( id BIGSERIAL PRIMARY KEY, created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL, user_id UUID REFERENCES auth.users NOT NULL, task_id BIGINT REFERENCES public.fipi_tasks(id) ON DELETE CASCADE NOT NULL, submitted_answer TEXT NOT NULL, is_correct BOOLEAN NOT NULL, check_status TEXT NOT NULL DEFAULT 'not_checked' ); ALTER TABLE public.task_attempts ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "Users can insert their own task attempts" ON public.task_attempts; DROP POLICY IF EXISTS "Users can view their own task attempts" ON public.task_attempts; DROP POLICY IF EXISTS "Users can update their own task attempts" ON public.task_attempts; DROP POLICY IF EXISTS "Users can delete their own task attempts" ON public.task_attempts; CREATE POLICY "Users can insert their own task attempts" ON public.task_attempts FOR INSERT WITH CHECK (auth.uid() = user_id); CREATE POLICY "Users can view their own task attempts" ON public.task_attempts FOR SELECT USING (auth.uid() = user_id); CREATE POLICY "Users can update their own task attempts" ON public.task_attempts FOR UPDATE USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id); CREATE POLICY "Users can delete their own task attempts" ON public.task_attempts FOR DELETE USING (auth.uid() = user_id); CREATE INDEX IF NOT EXISTS idx_task_attempts_user_created_at ON public.task_attempts(user_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_task_attempts_task_id ON public.task_attempts(task_id);