Implement vocab course and grammar exercise features in backend and frontend
- Added new course management functionalities in VocabController, including creating, updating, and deleting courses and lessons. - Implemented enrollment and progress tracking for courses, along with grammar exercise creation and management. - Updated database schema to include tables for courses, lessons, enrollments, and grammar exercises. - Enhanced frontend with new routes and views for course listing and details, including internationalization support for course-related texts. - Improved user experience by adding navigation to courses from the main vocab trainer view.
This commit is contained in:
132
backend/migrations/20260115000000-add-vocab-courses.cjs
Normal file
132
backend/migrations/20260115000000-add-vocab-courses.cjs
Normal file
@@ -0,0 +1,132 @@
|
||||
/* eslint-disable */
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface) {
|
||||
// Kurs-Tabelle
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE IF NOT EXISTS community.vocab_course (
|
||||
id SERIAL PRIMARY KEY,
|
||||
owner_user_id INTEGER NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
language_id INTEGER NOT NULL,
|
||||
difficulty_level INTEGER DEFAULT 1,
|
||||
is_public BOOLEAN DEFAULT false,
|
||||
share_code TEXT,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT vocab_course_owner_fk
|
||||
FOREIGN KEY (owner_user_id)
|
||||
REFERENCES community."user"(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vocab_course_language_fk
|
||||
FOREIGN KEY (language_id)
|
||||
REFERENCES community.vocab_language(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vocab_course_share_code_uniq UNIQUE (share_code)
|
||||
);
|
||||
`);
|
||||
|
||||
// Lektionen innerhalb eines Kurses
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE IF NOT EXISTS community.vocab_course_lesson (
|
||||
id SERIAL PRIMARY KEY,
|
||||
course_id INTEGER NOT NULL,
|
||||
chapter_id INTEGER NOT NULL,
|
||||
lesson_number INTEGER NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT vocab_course_lesson_course_fk
|
||||
FOREIGN KEY (course_id)
|
||||
REFERENCES community.vocab_course(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vocab_course_lesson_chapter_fk
|
||||
FOREIGN KEY (chapter_id)
|
||||
REFERENCES community.vocab_chapter(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vocab_course_lesson_unique UNIQUE (course_id, lesson_number)
|
||||
);
|
||||
`);
|
||||
|
||||
// Einschreibungen in Kurse
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE IF NOT EXISTS community.vocab_course_enrollment (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
course_id INTEGER NOT NULL,
|
||||
enrolled_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT vocab_course_enrollment_user_fk
|
||||
FOREIGN KEY (user_id)
|
||||
REFERENCES community."user"(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vocab_course_enrollment_course_fk
|
||||
FOREIGN KEY (course_id)
|
||||
REFERENCES community.vocab_course(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vocab_course_enrollment_unique UNIQUE (user_id, course_id)
|
||||
);
|
||||
`);
|
||||
|
||||
// Fortschritt pro User und Lektion
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE IF NOT EXISTS community.vocab_course_progress (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
course_id INTEGER NOT NULL,
|
||||
lesson_id INTEGER NOT NULL,
|
||||
completed BOOLEAN DEFAULT false,
|
||||
score INTEGER DEFAULT 0,
|
||||
last_accessed_at TIMESTAMP WITHOUT TIME ZONE,
|
||||
completed_at TIMESTAMP WITHOUT TIME ZONE,
|
||||
CONSTRAINT vocab_course_progress_user_fk
|
||||
FOREIGN KEY (user_id)
|
||||
REFERENCES community."user"(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vocab_course_progress_course_fk
|
||||
FOREIGN KEY (course_id)
|
||||
REFERENCES community.vocab_course(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vocab_course_progress_lesson_fk
|
||||
FOREIGN KEY (lesson_id)
|
||||
REFERENCES community.vocab_course_lesson(id)
|
||||
ON DELETE CASCADE,
|
||||
CONSTRAINT vocab_course_progress_unique UNIQUE (user_id, lesson_id)
|
||||
);
|
||||
`);
|
||||
|
||||
// Indizes
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE INDEX IF NOT EXISTS vocab_course_owner_idx
|
||||
ON community.vocab_course(owner_user_id);
|
||||
CREATE INDEX IF NOT EXISTS vocab_course_language_idx
|
||||
ON community.vocab_course(language_id);
|
||||
CREATE INDEX IF NOT EXISTS vocab_course_public_idx
|
||||
ON community.vocab_course(is_public);
|
||||
CREATE INDEX IF NOT EXISTS vocab_course_lesson_course_idx
|
||||
ON community.vocab_course_lesson(course_id);
|
||||
CREATE INDEX IF NOT EXISTS vocab_course_lesson_chapter_idx
|
||||
ON community.vocab_course_lesson(chapter_id);
|
||||
CREATE INDEX IF NOT EXISTS vocab_course_enrollment_user_idx
|
||||
ON community.vocab_course_enrollment(user_id);
|
||||
CREATE INDEX IF NOT EXISTS vocab_course_enrollment_course_idx
|
||||
ON community.vocab_course_enrollment(course_id);
|
||||
CREATE INDEX IF NOT EXISTS vocab_course_progress_user_idx
|
||||
ON community.vocab_course_progress(user_id);
|
||||
CREATE INDEX IF NOT EXISTS vocab_course_progress_course_idx
|
||||
ON community.vocab_course_progress(course_id);
|
||||
CREATE INDEX IF NOT EXISTS vocab_course_progress_lesson_idx
|
||||
ON community.vocab_course_progress(lesson_id);
|
||||
`);
|
||||
},
|
||||
|
||||
async down(queryInterface) {
|
||||
await queryInterface.sequelize.query(`
|
||||
DROP TABLE IF EXISTS community.vocab_course_progress CASCADE;
|
||||
DROP TABLE IF EXISTS community.vocab_course_enrollment CASCADE;
|
||||
DROP TABLE IF EXISTS community.vocab_course_lesson CASCADE;
|
||||
DROP TABLE IF EXISTS community.vocab_course CASCADE;
|
||||
`);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user