From 94e14f8ef9ac098f48d3c361c0087c3c3a8da67f Mon Sep 17 00:00:00 2001 From: Kyle2772547 Date: Tue, 26 May 2026 18:27:11 +0700 Subject: [PATCH 1/5] add: API --- server/api/Subjects/[id].put.js | 6 +-- server/api/Subjects/index.post.js | 4 +- server/api/curriculum-categories/[id].js | 29 +++++++++++++++ server/api/curriculum-categories/index.js | 27 ++++++++++++++ server/api/curriculum-subjects/[id].js | 29 +++++++++++++++ server/api/curriculum-subjects/index.js | 33 +++++++++++++++++ server/api/curriculums/[id].js | 36 ++++++++++++++++++ server/api/curriculums/index.js | 29 +++++++++++++++ server/api/study-plan-subjects/[id].js | 14 +++++++ server/api/study-plan-subjects/index.js | 45 +++++++++++++++++++++++ server/api/study-plans/[id].js | 29 +++++++++++++++ server/api/study-plans/index.js | 29 +++++++++++++++ 12 files changed, 305 insertions(+), 5 deletions(-) create mode 100644 server/api/curriculum-categories/[id].js create mode 100644 server/api/curriculum-categories/index.js create mode 100644 server/api/curriculum-subjects/[id].js create mode 100644 server/api/curriculum-subjects/index.js create mode 100644 server/api/curriculums/[id].js create mode 100644 server/api/curriculums/index.js create mode 100644 server/api/study-plan-subjects/[id].js create mode 100644 server/api/study-plan-subjects/index.js create mode 100644 server/api/study-plans/[id].js create mode 100644 server/api/study-plans/index.js diff --git a/server/api/Subjects/[id].put.js b/server/api/Subjects/[id].put.js index e4b9cf7..4b97976 100644 --- a/server/api/Subjects/[id].put.js +++ b/server/api/Subjects/[id].put.js @@ -12,9 +12,9 @@ export default defineEventHandler(async (event) => { }) } - // 1. Update Subject name - const stmt = db.prepare('UPDATE Subjects SET name_subject = ? WHERE id_subject = ?') - stmt.run(body.name_subject, id) + // 1. Update Subject name and curriculum link + const stmt = db.prepare('UPDATE Subjects SET name_subject = ?, curriculum_subject_id = ? WHERE id_subject = ?') + stmt.run(body.name_subject, body.curriculum_subject_id || null, id) // Update Sections if provided if (body.id_sections && Array.isArray(body.id_sections)) { diff --git a/server/api/Subjects/index.post.js b/server/api/Subjects/index.post.js index d75802c..1ec9262 100644 --- a/server/api/Subjects/index.post.js +++ b/server/api/Subjects/index.post.js @@ -11,8 +11,8 @@ export default defineEventHandler(async (event) => { } // 1. Insert Subject - const stmt = db.prepare('INSERT INTO Subjects (name_subject, id_teacher, term) VALUES (?, ?, ?)') - const result = stmt.run(body.name_subject, body.id_teacher, body.term || null) + const stmt = db.prepare('INSERT INTO Subjects (name_subject, id_teacher, term, curriculum_subject_id) VALUES (?, ?, ?, ?)') + const result = stmt.run(body.name_subject, body.id_teacher, body.term || null, body.curriculum_subject_id || null) const subjectId = result.lastInsertRowid // 2. Insert SubjectSections (Join table) diff --git a/server/api/curriculum-categories/[id].js b/server/api/curriculum-categories/[id].js new file mode 100644 index 0000000..de91474 --- /dev/null +++ b/server/api/curriculum-categories/[id].js @@ -0,0 +1,29 @@ +import db from '../../utils/db.js' + +export default defineEventHandler(async (event) => { + const id = event.context.params.id + + if (event.node.req.method === 'PUT') { + const body = await readBody(event) + if (!body.name_category) { + throw createError({ statusCode: 400, statusMessage: 'name_category is required' }) + } + + const stmt = db.prepare('UPDATE curriculum_categories SET name_category = ?, parent_id = ? WHERE id_category = ?') + const result = stmt.run(body.name_category, body.parent_id || null, id) + + if (result.changes === 0) { + throw createError({ statusCode: 404, statusMessage: 'Category not found' }) + } + return { success: true } + } + + if (event.node.req.method === 'DELETE') { + const stmt = db.prepare('DELETE FROM curriculum_categories WHERE id_category = ?') + const result = stmt.run(id) + if (result.changes === 0) { + throw createError({ statusCode: 404, statusMessage: 'Category not found' }) + } + return { success: true } + } +}) diff --git a/server/api/curriculum-categories/index.js b/server/api/curriculum-categories/index.js new file mode 100644 index 0000000..97fd4b0 --- /dev/null +++ b/server/api/curriculum-categories/index.js @@ -0,0 +1,27 @@ +import db from '../../utils/db.js' + +export default defineEventHandler(async (event) => { + if (event.node.req.method === 'GET') { + const query = getQuery(event) + if (query.id_curriculum) { + return db.prepare('SELECT * FROM curriculum_categories WHERE id_curriculum = ? ORDER BY id_category ASC').all(query.id_curriculum) + } + return db.prepare('SELECT * FROM curriculum_categories ORDER BY id_category ASC').all() + } + + if (event.node.req.method === 'POST') { + const body = await readBody(event) + if (!body.id_curriculum || !body.name_category) { + throw createError({ statusCode: 400, statusMessage: 'id_curriculum and name_category are required' }) + } + + const stmt = db.prepare('INSERT INTO curriculum_categories (id_curriculum, parent_id, name_category) VALUES (?, ?, ?)') + const result = stmt.run(body.id_curriculum, body.parent_id || null, body.name_category) + return { + id_category: result.lastInsertRowid, + id_curriculum: body.id_curriculum, + parent_id: body.parent_id || null, + name_category: body.name_category + } + } +}) diff --git a/server/api/curriculum-subjects/[id].js b/server/api/curriculum-subjects/[id].js new file mode 100644 index 0000000..57c007c --- /dev/null +++ b/server/api/curriculum-subjects/[id].js @@ -0,0 +1,29 @@ +import db from '../../utils/db.js' + +export default defineEventHandler(async (event) => { + const id = event.context.params.id + + if (event.node.req.method === 'PUT') { + const body = await readBody(event) + if (!body.name_subject || !body.id_category) { + throw createError({ statusCode: 400, statusMessage: 'name_subject and id_category are required' }) + } + + const stmt = db.prepare('UPDATE curriculum_subjects SET name_subject = ?, subject_code = ?, id_category = ? WHERE id_subject_curr = ?') + const result = stmt.run(body.name_subject, body.subject_code || null, body.id_category, id) + + if (result.changes === 0) { + throw createError({ statusCode: 404, statusMessage: 'Subject not found' }) + } + return { success: true } + } + + if (event.node.req.method === 'DELETE') { + const stmt = db.prepare('DELETE FROM curriculum_subjects WHERE id_subject_curr = ?') + const result = stmt.run(id) + if (result.changes === 0) { + throw createError({ statusCode: 404, statusMessage: 'Subject not found' }) + } + return { success: true } + } +}) diff --git a/server/api/curriculum-subjects/index.js b/server/api/curriculum-subjects/index.js new file mode 100644 index 0000000..55847dc --- /dev/null +++ b/server/api/curriculum-subjects/index.js @@ -0,0 +1,33 @@ +import db from '../../utils/db.js' + +export default defineEventHandler(async (event) => { + if (event.node.req.method === 'GET') { + const query = getQuery(event) + if (query.id_curriculum) { + return db.prepare(` + SELECT cs.* + FROM curriculum_subjects cs + JOIN curriculum_categories cc ON cs.id_category = cc.id_category + WHERE cc.id_curriculum = ? + ORDER BY cs.subject_code ASC + `).all(query.id_curriculum) + } + return db.prepare('SELECT * FROM curriculum_subjects ORDER BY subject_code ASC').all() + } + + if (event.node.req.method === 'POST') { + const body = await readBody(event) + if (!body.id_category || !body.name_subject) { + throw createError({ statusCode: 400, statusMessage: 'id_category and name_subject are required' }) + } + + const stmt = db.prepare('INSERT INTO curriculum_subjects (id_category, subject_code, name_subject) VALUES (?, ?, ?)') + const result = stmt.run(body.id_category, body.subject_code || null, body.name_subject) + return { + id_subject_curr: result.lastInsertRowid, + id_category: body.id_category, + subject_code: body.subject_code || null, + name_subject: body.name_subject + } + } +}) diff --git a/server/api/curriculums/[id].js b/server/api/curriculums/[id].js new file mode 100644 index 0000000..651f178 --- /dev/null +++ b/server/api/curriculums/[id].js @@ -0,0 +1,36 @@ +import db from '../../utils/db.js' + +export default defineEventHandler(async (event) => { + const id = event.context.params.id + + if (event.node.req.method === 'PUT') { + const body = await readBody(event) + if (!body.name_curriculum) { + throw createError({ statusCode: 400, statusMessage: 'name_curriculum is required' }) + } + + try { + const stmt = db.prepare('UPDATE curriculums SET name_curriculum = ?, description = ? WHERE id_curriculum = ?') + const result = stmt.run(body.name_curriculum, body.description || null, id) + + if (result.changes === 0) { + throw createError({ statusCode: 404, statusMessage: 'Curriculum not found' }) + } + return { success: true } + } catch (error) { + if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') { + throw createError({ statusCode: 409, statusMessage: 'Curriculum name already exists' }) + } + throw error + } + } + + if (event.node.req.method === 'DELETE') { + const stmt = db.prepare('DELETE FROM curriculums WHERE id_curriculum = ?') + const result = stmt.run(id) + if (result.changes === 0) { + throw createError({ statusCode: 404, statusMessage: 'Curriculum not found' }) + } + return { success: true } + } +}) diff --git a/server/api/curriculums/index.js b/server/api/curriculums/index.js new file mode 100644 index 0000000..d65cf63 --- /dev/null +++ b/server/api/curriculums/index.js @@ -0,0 +1,29 @@ +import db from '../../utils/db.js' + +export default defineEventHandler(async (event) => { + if (event.node.req.method === 'GET') { + return db.prepare('SELECT * FROM curriculums ORDER BY created_at DESC').all() + } + + if (event.node.req.method === 'POST') { + const body = await readBody(event) + if (!body.name_curriculum) { + throw createError({ statusCode: 400, statusMessage: 'name_curriculum is required' }) + } + + try { + const stmt = db.prepare('INSERT INTO curriculums (name_curriculum, description) VALUES (?, ?)') + const result = stmt.run(body.name_curriculum, body.description || null) + return { + id_curriculum: result.lastInsertRowid, + name_curriculum: body.name_curriculum, + description: body.description || null + } + } catch (error) { + if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') { + throw createError({ statusCode: 409, statusMessage: 'Curriculum name already exists' }) + } + throw error + } + } +}) diff --git a/server/api/study-plan-subjects/[id].js b/server/api/study-plan-subjects/[id].js new file mode 100644 index 0000000..0abd3a6 --- /dev/null +++ b/server/api/study-plan-subjects/[id].js @@ -0,0 +1,14 @@ +import db from '../../utils/db.js' + +export default defineEventHandler(async (event) => { + const id = event.context.params.id + + if (event.node.req.method === 'DELETE') { + const stmt = db.prepare('DELETE FROM study_plan_subjects WHERE id_plan_subject = ?') + const result = stmt.run(id) + if (result.changes === 0) { + throw createError({ statusCode: 404, statusMessage: 'Study Plan Subject not found' }) + } + return { success: true } + } +}) diff --git a/server/api/study-plan-subjects/index.js b/server/api/study-plan-subjects/index.js new file mode 100644 index 0000000..e3b3bd6 --- /dev/null +++ b/server/api/study-plan-subjects/index.js @@ -0,0 +1,45 @@ +import db from '../../utils/db.js' + +export default defineEventHandler(async (event) => { + if (event.node.req.method === 'GET') { + const query = getQuery(event) + if (query.id_plan) { + return db.prepare(` + SELECT sps.*, cs.subject_code, cs.name_subject, cc.name_category + FROM study_plan_subjects sps + JOIN curriculum_subjects cs ON sps.id_subject_curr = cs.id_subject_curr + LEFT JOIN curriculum_categories cc ON cs.id_category = cc.id_category + WHERE sps.id_plan = ? + ORDER BY cc.id_category ASC, cs.subject_code ASC + `).all(query.id_plan) + } + return [] + } + + if (event.node.req.method === 'POST') { + const body = await readBody(event) + if (!body.id_plan || !body.id_subject_curr) { + throw createError({ statusCode: 400, statusMessage: 'id_plan and id_subject_curr are required' }) + } + + try { + const stmt = db.prepare('INSERT INTO study_plan_subjects (id_plan, id_subject_curr) VALUES (?, ?)') + const result = stmt.run(body.id_plan, body.id_subject_curr) + + // Fetch details back + const inserted = db.prepare(` + SELECT sps.*, cs.subject_code, cs.name_subject + FROM study_plan_subjects sps + JOIN curriculum_subjects cs ON sps.id_subject_curr = cs.id_subject_curr + WHERE sps.id_plan_subject = ? + `).get(result.lastInsertRowid) + + return inserted + } catch (error) { + if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') { + throw createError({ statusCode: 409, statusMessage: 'Subject already exists in this plan' }) + } + throw error + } + } +}) diff --git a/server/api/study-plans/[id].js b/server/api/study-plans/[id].js new file mode 100644 index 0000000..1d42a9b --- /dev/null +++ b/server/api/study-plans/[id].js @@ -0,0 +1,29 @@ +import db from '../../utils/db.js' + +export default defineEventHandler(async (event) => { + const id = event.context.params.id + + if (event.node.req.method === 'PUT') { + const body = await readBody(event) + if (!body.name_plan || !body.level || !body.year || !body.semester) { + throw createError({ statusCode: 400, statusMessage: 'All fields are required' }) + } + + const stmt = db.prepare('UPDATE study_plans SET name_plan = ?, level = ?, year = ?, semester = ? WHERE id_plan = ?') + const result = stmt.run(body.name_plan, body.level, body.year, body.semester, id) + + if (result.changes === 0) { + throw createError({ statusCode: 404, statusMessage: 'Study Plan not found' }) + } + return { success: true } + } + + if (event.node.req.method === 'DELETE') { + const stmt = db.prepare('DELETE FROM study_plans WHERE id_plan = ?') + const result = stmt.run(id) + if (result.changes === 0) { + throw createError({ statusCode: 404, statusMessage: 'Study Plan not found' }) + } + return { success: true } + } +}) diff --git a/server/api/study-plans/index.js b/server/api/study-plans/index.js new file mode 100644 index 0000000..07a5745 --- /dev/null +++ b/server/api/study-plans/index.js @@ -0,0 +1,29 @@ +import db from '../../utils/db.js' + +export default defineEventHandler(async (event) => { + if (event.node.req.method === 'GET') { + const query = getQuery(event) + if (query.id_curriculum) { + return db.prepare('SELECT * FROM study_plans WHERE id_curriculum = ? ORDER BY year ASC, semester ASC').all(query.id_curriculum) + } + return db.prepare('SELECT * FROM study_plans ORDER BY id_curriculum ASC, year ASC, semester ASC').all() + } + + if (event.node.req.method === 'POST') { + const body = await readBody(event) + if (!body.id_curriculum || !body.name_plan || !body.level || !body.year || !body.semester) { + throw createError({ statusCode: 400, statusMessage: 'All fields are required' }) + } + + const stmt = db.prepare('INSERT INTO study_plans (id_curriculum, name_plan, level, year, semester) VALUES (?, ?, ?, ?, ?)') + const result = stmt.run(body.id_curriculum, body.name_plan, body.level, body.year, body.semester) + return { + id_plan: result.lastInsertRowid, + id_curriculum: body.id_curriculum, + name_plan: body.name_plan, + level: body.level, + year: body.year, + semester: body.semester + } + } +}) From fd9d80752f62b8145d0d59aa0bd53bfd13f0eea8 Mon Sep 17 00:00:00 2001 From: Kyle2772547 Date: Wed, 27 May 2026 02:14:37 +0700 Subject: [PATCH 2/5] feat: implement study plan management system --- app/app.vue | 4 +- app/pages/curriculums.vue | 924 ++++++++++++++++++++++++ app/pages/teacher/[id].vue | 685 +++++++----------- server/api/study-plan-subjects/index.js | 8 +- server/api/study-plans/[id].js | 8 +- server/api/study-plans/index.js | 16 +- server/data/data.db | Bin 331776 -> 368640 bytes server/utils/db.js | 73 ++ 8 files changed, 1296 insertions(+), 422 deletions(-) create mode 100644 app/pages/curriculums.vue diff --git a/app/app.vue b/app/app.vue index 739d312..85eebb3 100644 --- a/app/app.vue +++ b/app/app.vue @@ -18,7 +18,7 @@ useHead({ @@ -27,6 +27,8 @@ useHead({ variant="ghost" :ui="{ leadingIcon: 'text-primary' }" to="/guide" /> + + +
+ + +
+ +
+
+ +
+ + +
+
+
+ +
+ +
+ + +
+ + +
+
+

หมวดวิชาในหลักสูตร

+ +
+ +
+
ยังไม่มีหมวดวิชา
+ + +
+
+

{{ index + 1 }}. {{ cat.name_category }}

+
+ + + + +
+
+ +
+
ยังไม่มีข้อมูลในหมวดนี้
+ + +
+
+ วิชาในหมวดหลัก +
+
+ + + + + + + + + + + + + + + +
รหัสวิชาชื่อวิชาจัดการ
{{ sub.subject_code || '-' }}{{ sub.name_subject }} + + +
+
+
+ + +
+
+

{{ index + 1 }}.{{ sub_index + 1 }} {{ + sub_cat.name_category }}

+
+ + + + +
+
+ +
+ +
+
+
{{ index + 1 }}.{{ sub_index + 1 }}.{{ + sub_sub_index + 1 }} {{ sub_sub_cat.name_category }}
+
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + +
รหัสวิชาชื่อวิชาจัดการ
{{ sub.subject_code || '-' }}{{ sub.name_subject }} + + +
+ ไม่มีรายวิชาในกลุ่มย่อยนี้
+
+
+ + + + + + + + + + + + + + + + + + + + +
รหัสวิชาชื่อวิชาจัดการ
{{ sub.subject_code || '-' }}{{ sub.name_subject }} + + +
+ ไม่มีรายวิชาในกลุ่มนี้
+
+
+
+
+
+
+ + +
+ +
+
+

รายการแผนการเรียน

+ +
+
+
ยังไม่มีแผนการเรียน
+
+
+ {{ plan.name_plan }} + +
+
+ {{ plan.level }} +
+
+
+
+ +
+
+ +

เลือกแผนการเรียนด้านซ้ายเพื่อจัดการรายวิชา

+
+ +
+ +
+
+
+

{{ selectedPlan.name_plan }}

+

ระดับ {{ selectedPlan.level }}

+
+ +
+
+
+ + +
+
+ + +
+ +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + +
หมวดวิชารหัสวิชาชื่อวิชาลบ
{{ ps.name_category || '-' }}{{ ps.subject_code || '-' }}{{ ps.name_subject }} + +
ยังไม่มีวิชาในปีที่ {{ + viewYear }} ภาคเรียนที่ {{ viewSemester }}
+
+
+
+
+ +
+ +

กรุณาเลือกหรือสร้างหลักสูตรเพื่อเริ่มต้นจัดการ

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + diff --git a/app/pages/teacher/[id].vue b/app/pages/teacher/[id].vue index 369b8ed..5b5bbb9 100644 --- a/app/pages/teacher/[id].vue +++ b/app/pages/teacher/[id].vue @@ -1,21 +1,15 @@