-- Learn Module Database Migration -- Learning Management System (LMS) for General Bots -- ============================================================================ -- CATEGORIES TABLE -- ============================================================================ CREATE TABLE IF NOT EXISTS learn_categories ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, description TEXT, icon TEXT, color TEXT, parent_id UUID REFERENCES learn_categories(id) ON DELETE SET NULL, sort_order INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_learn_categories_parent ON learn_categories(parent_id); CREATE INDEX idx_learn_categories_sort ON learn_categories(sort_order); -- Insert default categories INSERT INTO learn_categories (name, description, icon, color, sort_order) VALUES ('compliance', 'Treinamentos de Compliance e Conformidade', '📋', '#3b82f6', 1), ('security', 'Segurança da Informação e LGPD', '🔒', '#ef4444', 2), ('onboarding', 'Integração de Novos Colaboradores', '🚀', '#10b981', 3), ('skills', 'Desenvolvimento de Habilidades', '💡', '#f59e0b', 4), ('leadership', 'Liderança e Gestão', '👔', '#8b5cf6', 5), ('technical', 'Treinamentos Técnicos', '⚙️', '#6366f1', 6), ('soft-skills', 'Soft Skills e Comunicação', '💬', '#ec4899', 7), ('wellness', 'Bem-estar e Qualidade de Vida', '🧘', '#14b8a6', 8); -- ============================================================================ -- COURSES TABLE -- ============================================================================ CREATE TABLE IF NOT EXISTS learn_courses ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), organization_id UUID, title TEXT NOT NULL, description TEXT, category TEXT NOT NULL DEFAULT 'skills', difficulty TEXT NOT NULL DEFAULT 'beginner', duration_minutes INTEGER NOT NULL DEFAULT 0, thumbnail_url TEXT, is_mandatory BOOLEAN NOT NULL DEFAULT FALSE, due_days INTEGER, is_published BOOLEAN NOT NULL DEFAULT FALSE, created_by UUID, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT valid_difficulty CHECK (difficulty IN ('beginner', 'intermediate', 'advanced')) ); CREATE INDEX idx_learn_courses_org ON learn_courses(organization_id); CREATE INDEX idx_learn_courses_category ON learn_courses(category); CREATE INDEX idx_learn_courses_mandatory ON learn_courses(is_mandatory); CREATE INDEX idx_learn_courses_published ON learn_courses(is_published); CREATE INDEX idx_learn_courses_created ON learn_courses(created_at DESC); -- ============================================================================ -- LESSONS TABLE -- ============================================================================ CREATE TABLE IF NOT EXISTS learn_lessons ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), course_id UUID NOT NULL REFERENCES learn_courses(id) ON DELETE CASCADE, title TEXT NOT NULL, content TEXT, content_type TEXT NOT NULL DEFAULT 'text', lesson_order INTEGER NOT NULL DEFAULT 1, duration_minutes INTEGER NOT NULL DEFAULT 0, video_url TEXT, attachments JSONB NOT NULL DEFAULT '[]', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT valid_content_type CHECK (content_type IN ('text', 'video', 'pdf', 'slides', 'interactive')) ); CREATE INDEX idx_learn_lessons_course ON learn_lessons(course_id); CREATE INDEX idx_learn_lessons_order ON learn_lessons(course_id, lesson_order); -- ============================================================================ -- QUIZZES TABLE -- ============================================================================ CREATE TABLE IF NOT EXISTS learn_quizzes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), lesson_id UUID REFERENCES learn_lessons(id) ON DELETE CASCADE, course_id UUID NOT NULL REFERENCES learn_courses(id) ON DELETE CASCADE, title TEXT NOT NULL, passing_score INTEGER NOT NULL DEFAULT 70, time_limit_minutes INTEGER, max_attempts INTEGER, questions JSONB NOT NULL DEFAULT '[]', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT valid_passing_score CHECK (passing_score >= 0 AND passing_score <= 100) ); CREATE INDEX idx_learn_quizzes_course ON learn_quizzes(course_id); CREATE INDEX idx_learn_quizzes_lesson ON learn_quizzes(lesson_id); -- ============================================================================ -- USER PROGRESS TABLE -- ============================================================================ CREATE TABLE IF NOT EXISTS learn_user_progress ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL, course_id UUID NOT NULL REFERENCES learn_courses(id) ON DELETE CASCADE, lesson_id UUID REFERENCES learn_lessons(id) ON DELETE CASCADE, status TEXT NOT NULL DEFAULT 'not_started', quiz_score INTEGER, quiz_attempts INTEGER NOT NULL DEFAULT 0, time_spent_minutes INTEGER NOT NULL DEFAULT 0, started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), completed_at TIMESTAMPTZ, last_accessed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT valid_status CHECK (status IN ('not_started', 'in_progress', 'completed', 'failed')), CONSTRAINT valid_quiz_score CHECK (quiz_score IS NULL OR (quiz_score >= 0 AND quiz_score <= 100)) ); CREATE INDEX idx_learn_progress_user ON learn_user_progress(user_id); CREATE INDEX idx_learn_progress_course ON learn_user_progress(course_id); CREATE INDEX idx_learn_progress_user_course ON learn_user_progress(user_id, course_id); CREATE INDEX idx_learn_progress_status ON learn_user_progress(status); CREATE INDEX idx_learn_progress_last_accessed ON learn_user_progress(last_accessed_at DESC); -- Unique constraint for course-level progress (where lesson_id is null) CREATE UNIQUE INDEX idx_learn_progress_user_course_unique ON learn_user_progress(user_id, course_id) WHERE lesson_id IS NULL; -- Unique constraint for lesson-level progress CREATE UNIQUE INDEX idx_learn_progress_user_lesson_unique ON learn_user_progress(user_id, lesson_id) WHERE lesson_id IS NOT NULL; -- ============================================================================ -- COURSE ASSIGNMENTS TABLE -- ============================================================================ CREATE TABLE IF NOT EXISTS learn_course_assignments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), course_id UUID NOT NULL REFERENCES learn_courses(id) ON DELETE CASCADE, user_id UUID NOT NULL, assigned_by UUID, due_date TIMESTAMPTZ, is_mandatory BOOLEAN NOT NULL DEFAULT TRUE, assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), completed_at TIMESTAMPTZ, reminder_sent BOOLEAN NOT NULL DEFAULT FALSE, reminder_sent_at TIMESTAMPTZ ); CREATE INDEX idx_learn_assignments_user ON learn_course_assignments(user_id); CREATE INDEX idx_learn_assignments_course ON learn_course_assignments(course_id); CREATE INDEX idx_learn_assignments_due ON learn_course_assignments(due_date); CREATE INDEX idx_learn_assignments_pending ON learn_course_assignments(user_id, completed_at) WHERE completed_at IS NULL; CREATE INDEX idx_learn_assignments_overdue ON learn_course_assignments(due_date) WHERE completed_at IS NULL AND due_date IS NOT NULL; -- Unique constraint to prevent duplicate assignments CREATE UNIQUE INDEX idx_learn_assignments_unique ON learn_course_assignments(course_id, user_id); -- ============================================================================ -- CERTIFICATES TABLE -- ============================================================================ CREATE TABLE IF NOT EXISTS learn_certificates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL, course_id UUID NOT NULL REFERENCES learn_courses(id) ON DELETE CASCADE, issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), score INTEGER NOT NULL, certificate_url TEXT, verification_code TEXT NOT NULL UNIQUE, expires_at TIMESTAMPTZ, CONSTRAINT valid_cert_score CHECK (score >= 0 AND score <= 100) ); CREATE INDEX idx_learn_certificates_user ON learn_certificates(user_id); CREATE INDEX idx_learn_certificates_course ON learn_certificates(course_id); CREATE INDEX idx_learn_certificates_verification ON learn_certificates(verification_code); CREATE INDEX idx_learn_certificates_issued ON learn_certificates(issued_at DESC); -- Unique constraint to prevent duplicate certificates CREATE UNIQUE INDEX idx_learn_certificates_unique ON learn_certificates(user_id, course_id); -- ============================================================================ -- QUIZ ATTEMPTS TABLE (for detailed tracking) -- ============================================================================ CREATE TABLE IF NOT EXISTS learn_quiz_attempts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), quiz_id UUID NOT NULL REFERENCES learn_quizzes(id) ON DELETE CASCADE, user_id UUID NOT NULL, attempt_number INTEGER NOT NULL DEFAULT 1, score INTEGER NOT NULL, max_score INTEGER NOT NULL, passed BOOLEAN NOT NULL, time_taken_minutes INTEGER, answers JSONB NOT NULL DEFAULT '{}', started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), completed_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_learn_quiz_attempts_quiz ON learn_quiz_attempts(quiz_id); CREATE INDEX idx_learn_quiz_attempts_user ON learn_quiz_attempts(user_id); CREATE INDEX idx_learn_quiz_attempts_user_quiz ON learn_quiz_attempts(user_id, quiz_id); -- ============================================================================ -- LEARNING PATHS TABLE (for structured learning journeys) -- ============================================================================ CREATE TABLE IF NOT EXISTS learn_paths ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), organization_id UUID, name TEXT NOT NULL, description TEXT, thumbnail_url TEXT, is_published BOOLEAN NOT NULL DEFAULT FALSE, created_by UUID, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_learn_paths_org ON learn_paths(organization_id); CREATE INDEX idx_learn_paths_published ON learn_paths(is_published); -- ============================================================================ -- LEARNING PATH COURSES (junction table) -- ============================================================================ CREATE TABLE IF NOT EXISTS learn_path_courses ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), path_id UUID NOT NULL REFERENCES learn_paths(id) ON DELETE CASCADE, course_id UUID NOT NULL REFERENCES learn_courses(id) ON DELETE CASCADE, course_order INTEGER NOT NULL DEFAULT 1, is_required BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_learn_path_courses_path ON learn_path_courses(path_id); CREATE INDEX idx_learn_path_courses_course ON learn_path_courses(course_id); CREATE UNIQUE INDEX idx_learn_path_courses_unique ON learn_path_courses(path_id, course_id); -- ============================================================================ -- SAMPLE DATA FOR DEMONSTRATION -- ============================================================================ -- Sample mandatory compliance course INSERT INTO learn_courses (id, title, description, category, difficulty, duration_minutes, is_mandatory, due_days, is_published) VALUES ('11111111-1111-1111-1111-111111111111', 'LGPD - Lei Geral de Proteção de Dados', 'Treinamento obrigatório sobre a Lei Geral de Proteção de Dados Pessoais. Aprenda os conceitos fundamentais, direitos dos titulares e obrigações da empresa.', 'compliance', 'beginner', 45, TRUE, 30, TRUE), ('22222222-2222-2222-2222-222222222222', 'Código de Conduta e Ética', 'Conheça nosso código de conduta e ética empresarial. Este treinamento é obrigatório para todos os colaboradores.', 'compliance', 'beginner', 30, TRUE, 14, TRUE), ('33333333-3333-3333-3333-333333333333', 'Segurança da Informação', 'Aprenda as melhores práticas de segurança da informação para proteger dados da empresa e de clientes.', 'security', 'intermediate', 60, TRUE, 30, TRUE), ('44444444-4444-4444-4444-444444444444', 'Integração de Novos Colaboradores', 'Bem-vindo à empresa! Este curso apresenta nossa cultura, valores e processos fundamentais.', 'onboarding', 'beginner', 90, FALSE, NULL, TRUE), ('55555555-5555-5555-5555-555555555555', 'Comunicação Efetiva', 'Desenvolva habilidades de comunicação profissional para trabalhar melhor em equipe.', 'soft-skills', 'intermediate', 40, FALSE, NULL, TRUE); -- Sample lessons for LGPD course INSERT INTO learn_lessons (course_id, title, content, content_type, lesson_order, duration_minutes) VALUES ('11111111-1111-1111-1111-111111111111', 'Introdução à LGPD', '
A Lei Geral de Proteção de Dados (Lei nº 13.709/2018) é a legislação brasileira que regula as atividades de tratamento de dados pessoais.
Informação relacionada a pessoa natural identificada ou identificável.
Dados sobre origem racial, convicção religiosa, opinião política, filiação sindical, dados de saúde, vida sexual, dados genéticos ou biométricos.
Toda operação realizada com dados pessoais: coleta, armazenamento, uso, compartilhamento, exclusão, etc.
', 'text', 2, 15), ('11111111-1111-1111-1111-111111111111', 'Direitos dos Titulares', 'Como colaboradores, temos o dever de: