Add is_skippable boolean field to Quiz schema, update quiz creation and update logic to handle the new field, and include it in student course content responses. Update seed data and type definitions accordingly.
411 lines
14 KiB
JavaScript
411 lines
14 KiB
JavaScript
const { PrismaClient } = require('@prisma/client');
|
|
const bcrypt = require('bcrypt');
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
console.log('🌱 Starting database seeding...');
|
|
|
|
// Clear existing data (in development only)
|
|
if (process.env.NODE_ENV === 'development') {
|
|
console.log('🗑️ Clearing existing data...');
|
|
await prisma.quizAttempt.deleteMany();
|
|
await prisma.choice.deleteMany();
|
|
await prisma.question.deleteMany();
|
|
await prisma.quiz.deleteMany();
|
|
await prisma.lessonProgress.deleteMany();
|
|
await prisma.lessonAttachment.deleteMany();
|
|
await prisma.lesson.deleteMany();
|
|
await prisma.chapter.deleteMany();
|
|
await prisma.announcementAttachment.deleteMany();
|
|
await prisma.announcement.deleteMany();
|
|
await prisma.certificate.deleteMany();
|
|
await prisma.enrollment.deleteMany();
|
|
await prisma.courseInstructor.deleteMany();
|
|
await prisma.course.deleteMany();
|
|
await prisma.category.deleteMany();
|
|
await prisma.payment.deleteMany();
|
|
await prisma.orderItem.deleteMany();
|
|
await prisma.order.deleteMany();
|
|
await prisma.withdrawalRequest.deleteMany();
|
|
await prisma.instructorBalance.deleteMany();
|
|
await prisma.userProfile.deleteMany();
|
|
await prisma.user.deleteMany();
|
|
await prisma.role.deleteMany();
|
|
}
|
|
|
|
// Seed Roles
|
|
console.log('👥 Seeding roles...');
|
|
const roles = await Promise.all([
|
|
prisma.role.upsert({
|
|
where: { code: 'ADMIN' },
|
|
update: {},
|
|
create: {
|
|
code: 'ADMIN',
|
|
name: { th: 'ผู้ดูแลระบบ', en: 'Administrator' },
|
|
description: { th: 'มีสิทธิ์เข้าถึงทุกฟังก์ชัน', en: 'Full system access' }
|
|
}
|
|
}),
|
|
prisma.role.upsert({
|
|
where: { code: 'INSTRUCTOR' },
|
|
update: {},
|
|
create: {
|
|
code: 'INSTRUCTOR',
|
|
name: { th: 'ผู้สอน', en: 'Instructor' },
|
|
description: { th: 'สามารถสร้างและจัดการคอร์สเรียน', en: 'Can create and manage courses' }
|
|
}
|
|
}),
|
|
prisma.role.upsert({
|
|
where: { code: 'STUDENT' },
|
|
update: {},
|
|
create: {
|
|
code: 'STUDENT',
|
|
name: { th: 'นักเรียน', en: 'Student' },
|
|
description: { th: 'สามารถลงทะเบียนและเรียนคอร์ส', en: 'Can enroll and learn courses' }
|
|
}
|
|
})
|
|
]);
|
|
|
|
// Seed Users
|
|
console.log('👤 Seeding users...');
|
|
const hashedPassword = await bcrypt.hash('admin123', 10);
|
|
|
|
const admin = await prisma.user.upsert({
|
|
where: { username: 'admin' },
|
|
update: {},
|
|
create: {
|
|
username: 'admin',
|
|
email: 'admin@elearning.local',
|
|
password: hashedPassword,
|
|
role_id: roles[0].id,
|
|
email_verified_at: new Date(),
|
|
profile: {
|
|
create: {
|
|
prefix: { th: 'นาย', en: 'Mr.' },
|
|
first_name: 'Admin',
|
|
last_name: 'User',
|
|
phone: '0812345678'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const instructor = await prisma.user.upsert({
|
|
where: { username: 'instructor' },
|
|
update: {},
|
|
create: {
|
|
username: 'instructor',
|
|
email: 'instructor@elearning.local',
|
|
password: hashedPassword,
|
|
role_id: roles[1].id,
|
|
email_verified_at: new Date(),
|
|
profile: {
|
|
create: {
|
|
prefix: { th: 'นาย', en: 'Mr.' },
|
|
first_name: 'John',
|
|
last_name: 'Doe',
|
|
phone: '0812345679'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const student = await prisma.user.upsert({
|
|
where: { username: 'student' },
|
|
update: {},
|
|
create: {
|
|
username: 'student',
|
|
email: 'student@elearning.local',
|
|
password: hashedPassword,
|
|
role_id: roles[2].id,
|
|
email_verified_at: new Date(),
|
|
profile: {
|
|
create: {
|
|
prefix: { th: 'นางสาว', en: 'Ms.' },
|
|
first_name: 'Jane',
|
|
last_name: 'Smith',
|
|
phone: '0812345680'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Seed Categories
|
|
console.log('📚 Seeding categories...');
|
|
const categories = await Promise.all([
|
|
prisma.category.upsert({
|
|
where: { slug: 'programming' },
|
|
update: {},
|
|
create: {
|
|
name: { th: 'การเขียนโปรแกรม', en: 'Programming' },
|
|
slug: 'programming',
|
|
description: { th: 'เรียนรู้การเขียนโปรแกรมและพัฒนาซอฟต์แวร์', en: 'Learn programming and software development' },
|
|
icon: 'code',
|
|
sort_order: 1,
|
|
created_by: admin.id
|
|
}
|
|
}),
|
|
prisma.category.upsert({
|
|
where: { slug: 'design' },
|
|
update: {},
|
|
create: {
|
|
name: { th: 'การออกแบบ', en: 'Design' },
|
|
slug: 'design',
|
|
description: { th: 'เรียนรู้การออกแบบกราฟิกและ UI/UX', en: 'Learn graphic design and UI/UX' },
|
|
icon: 'palette',
|
|
sort_order: 2,
|
|
created_by: admin.id
|
|
}
|
|
}),
|
|
prisma.category.upsert({
|
|
where: { slug: 'business' },
|
|
update: {},
|
|
create: {
|
|
name: { th: 'ธุรกิจ', en: 'Business' },
|
|
slug: 'business',
|
|
description: { th: 'เรียนรู้การบริหารธุรกิจและการตลาด', en: 'Learn business management and marketing' },
|
|
icon: 'briefcase',
|
|
sort_order: 3,
|
|
created_by: admin.id
|
|
}
|
|
})
|
|
]);
|
|
|
|
// Seed Sample Course
|
|
console.log('🎓 Seeding sample course...');
|
|
|
|
// Check if course already exists
|
|
let course = await prisma.course.findUnique({
|
|
where: { slug: 'javascript-fundamentals' },
|
|
include: { chapters: { include: { lessons: true } } }
|
|
});
|
|
|
|
if (!course) {
|
|
course = await prisma.course.create({
|
|
data: {
|
|
title: { th: 'พื้นฐาน JavaScript', en: 'JavaScript Fundamentals' },
|
|
slug: 'javascript-fundamentals',
|
|
description: {
|
|
th: 'เรียนรู้พื้นฐาน JavaScript ตั้งแต่เริ่มต้น รวมถึงตัวแปร ฟังก์ชัน และการจัดการ DOM',
|
|
en: 'Learn JavaScript fundamentals from scratch including variables, functions, and DOM manipulation'
|
|
},
|
|
price: 0,
|
|
is_free: true,
|
|
have_certificate: true,
|
|
status: 'APPROVED',
|
|
category_id: categories[0].id,
|
|
created_by: instructor.id,
|
|
approved_by: admin.id,
|
|
approved_at: new Date(),
|
|
instructors: {
|
|
create: {
|
|
user_id: instructor.id,
|
|
is_primary: true
|
|
}
|
|
},
|
|
chapters: {
|
|
create: [
|
|
{
|
|
title: { th: 'บทที่ 1: เริ่มต้น', en: 'Chapter 1: Getting Started' },
|
|
description: { th: 'แนะนำ JavaScript และการตั้งค่าสภาพแวดล้อม', en: 'Introduction to JavaScript and environment setup' },
|
|
sort_order: 1,
|
|
is_published: true,
|
|
lessons: {
|
|
create: [
|
|
{
|
|
title: { th: 'JavaScript คืออะไร', en: 'What is JavaScript' },
|
|
content: {
|
|
th: 'JavaScript เป็นภาษาโปรแกรมที่ใช้ในการพัฒนาเว็บไซต์',
|
|
en: 'JavaScript is a programming language used for web development'
|
|
},
|
|
type: 'VIDEO',
|
|
duration_minutes: 15,
|
|
sort_order: 1,
|
|
is_published: true
|
|
},
|
|
{
|
|
title: { th: 'ตัวแปรและชนิดข้อมูล', en: 'Variables and Data Types' },
|
|
content: {
|
|
th: 'เรียนรู้เกี่ยวกับตัวแปร let, const และชนิดข้อมูลต่างๆ',
|
|
en: 'Learn about let, const variables and different data types'
|
|
},
|
|
type: 'VIDEO',
|
|
duration_minutes: 20,
|
|
sort_order: 2,
|
|
is_published: true
|
|
},
|
|
{
|
|
title: { th: 'แบบทดสอบบทที่ 1', en: 'Chapter 1 Quiz' },
|
|
type: 'QUIZ',
|
|
duration_minutes: 10,
|
|
sort_order: 3,
|
|
is_published: true,
|
|
require_pass_quiz: true
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
title: { th: 'บทที่ 2: ฟังก์ชัน', en: 'Chapter 2: Functions' },
|
|
description: { th: 'เรียนรู้เกี่ยวกับฟังก์ชันและการใช้งาน', en: 'Learn about functions and their usage' },
|
|
sort_order: 2,
|
|
is_published: true,
|
|
lessons: {
|
|
create: [
|
|
{
|
|
title: { th: 'การสร้างฟังก์ชัน', en: 'Creating Functions' },
|
|
content: {
|
|
th: 'เรียนรู้วิธีสร้างและเรียกใช้ฟังก์ชัน',
|
|
en: 'Learn how to create and call functions'
|
|
},
|
|
type: 'VIDEO',
|
|
duration_minutes: 25,
|
|
sort_order: 1,
|
|
is_published: true
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
include: {
|
|
chapters: {
|
|
include: {
|
|
lessons: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Create a quiz for the quiz lesson
|
|
const quizLesson = await prisma.lesson.findFirst({
|
|
where: {
|
|
title: {
|
|
path: ['en'],
|
|
equals: 'Chapter 1 Quiz'
|
|
}
|
|
}
|
|
});
|
|
|
|
if (quizLesson) {
|
|
console.log('📝 Creating quiz...');
|
|
await prisma.quiz.create({
|
|
data: {
|
|
lesson_id: quizLesson.id,
|
|
title: { th: 'แบบทดสอบบทที่ 1', en: 'Chapter 1 Quiz' },
|
|
description: { th: 'ทดสอบความเข้าใจเกี่ยวกับพื้นฐาน JavaScript', en: 'Test your understanding of JavaScript basics' },
|
|
passing_score: 70,
|
|
time_limit: 10,
|
|
shuffle_questions: true,
|
|
shuffle_choices: true,
|
|
is_skippable: true,
|
|
created_by: instructor.id,
|
|
questions: {
|
|
create: [
|
|
{
|
|
question: {
|
|
th: 'JavaScript ใช้สำหรับอะไร?',
|
|
en: 'What is JavaScript used for?'
|
|
},
|
|
question_type: 'MULTIPLE_CHOICE',
|
|
score: 1,
|
|
sort_order: 1,
|
|
choices: {
|
|
create: [
|
|
{
|
|
text: { th: 'พัฒนาเว็บไซต์', en: 'Web development' },
|
|
is_correct: true,
|
|
sort_order: 1
|
|
},
|
|
{
|
|
text: { th: 'ทำกาแฟ', en: 'Making coffee' },
|
|
is_correct: false,
|
|
sort_order: 2
|
|
},
|
|
{
|
|
text: { th: 'ขับรถ', en: 'Driving cars' },
|
|
is_correct: false,
|
|
sort_order: 3
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
question: {
|
|
th: 'ตัวแปรใน JavaScript ประกาศด้วยคำสั่งใด?',
|
|
en: 'Which keyword is used to declare variables in JavaScript?'
|
|
},
|
|
question_type: 'MULTIPLE_CHOICE',
|
|
score: 1,
|
|
sort_order: 2,
|
|
choices: {
|
|
create: [
|
|
{
|
|
text: { th: 'let และ const', en: 'let and const' },
|
|
is_correct: true,
|
|
sort_order: 1
|
|
},
|
|
{
|
|
text: { th: 'int และ float', en: 'int and float' },
|
|
is_correct: false,
|
|
sort_order: 2
|
|
},
|
|
{
|
|
text: { th: 'variable', en: 'variable' },
|
|
is_correct: false,
|
|
sort_order: 3
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Create announcement
|
|
console.log('📢 Creating announcement...');
|
|
await prisma.announcement.create({
|
|
data: {
|
|
course_id: course.id,
|
|
title: { th: 'ยินดีต้อนรับสู่คอร์ส', en: 'Welcome to the Course' },
|
|
content: {
|
|
th: 'ยินดีต้อนรับทุกคนสู่คอร์ส JavaScript Fundamentals! เราจะเริ่มเรียนในสัปดาห์หน้า',
|
|
en: 'Welcome everyone to JavaScript Fundamentals! We will start next week'
|
|
},
|
|
status: 'PUBLISHED',
|
|
is_pinned: true,
|
|
published_at: new Date(),
|
|
created_by: instructor.id
|
|
}
|
|
});
|
|
} else {
|
|
console.log(' ⏭️ Course already exists, skipping...');
|
|
}
|
|
|
|
console.log('✅ Database seeding completed!');
|
|
console.log('\n📊 Summary:');
|
|
console.log(`- Roles: ${roles.length}`);
|
|
console.log(`- Users: 3 (admin, instructor, student)`);
|
|
console.log(`- Categories: ${categories.length}`);
|
|
console.log(`- Courses: 1`);
|
|
console.log(`- Chapters: 2`);
|
|
console.log(`- Lessons: 4`);
|
|
console.log(`- Quizzes: 1 (with 2 questions)`);
|
|
console.log(`- Announcements: 1`);
|
|
console.log('\n🔑 Test Credentials:');
|
|
console.log('Admin: admin / admin123');
|
|
console.log('Instructor: instructor / admin123');
|
|
console.log('Student: student / admin123');
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error('❌ Error seeding database:', e);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|