Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,27 @@
<UApp class="bg-white text-slate-900 min-h-screen">
<UHeader class="bg-slate-900 text-white border-b border-slate-200 shadow-sm">
<template #left>
<NuxtLink class="flex items-center" to="/">

Check warning on line 19 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

'to' should be on a new line
<img src="/image/logo it.png" class="h-11 w-auto mx-5" alt="RMUTI logo">

Check warning on line 20 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

'alt' should be on a new line

Check warning on line 20 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

'class' should be on a new line
<span class="text-2xl">ระบบจัดวันสอนชดเชยกึ่งอัตโนมัติ</span>
<span class="text-xl">ระบบจัดวันสอนชดเชยกึ่งอัตโนมัติ</span>
</NuxtLink>
</template>

<template #right>
<UButton class="text-white" label="คู่มือ" size="xl" icon="i-heroicons-information-circle" color="primary"

Check warning on line 26 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

'color' should be on a new line

Check warning on line 26 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

'icon' should be on a new line

Check warning on line 26 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

'size' should be on a new line

Check warning on line 26 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

'label' should be on a new line

Check warning on line 26 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

Expected a linebreak before this attribute
variant="ghost" :ui="{ leadingIcon: 'text-primary' }" to="/guide" />

Check warning on line 27 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

'to' should be on a new line

Check warning on line 27 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

':ui' should be on a new line

Check failure on line 27 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

Expected indentation of 17 spaces but found 10 spaces
<UButton class="text-white" label="ปฏิทิน" size="xl" icon="i-heroicons-calendar" color="primary" variant="ghost"
:ui="{ leadingIcon: 'text-primary' }" to="/" />

Check failure on line 29 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

Expected indentation of 17 spaces but found 10 spaces
<UButton class="text-white" label="หลักสูตร" size="xl" icon="i-heroicons-academic-cap" color="primary"
variant="ghost" :ui="{ leadingIcon: 'text-primary' }" to="/curriculums" />

Check failure on line 31 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

Expected indentation of 17 spaces but found 10 spaces
<UButton class="text-white" label="ปีการศึกษา" size="xl" icon="i-lucide-layers" color="primary" variant="ghost"
:ui="{ leadingIcon: 'text-primary' }" to="/terms" />

Check failure on line 33 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

Expected indentation of 17 spaces but found 10 spaces
<UButton class="text-white" to="/sections" size="xl" variant="ghost" color="primary" label="ตารางเรียน"
:ui="{ leadingIcon: 'text-primary' }" icon="i-lucide-table" />

Check failure on line 35 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

Expected indentation of 17 spaces but found 10 spaces
<UButton class="text-white" to="/rooms" size="xl" variant="ghost" color="primary" label="ตารางห้องเรียน"
:ui="{ leadingIcon: 'text-primary' }" icon="i-heroicons-building-office-2" />

Check failure on line 37 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

Expected indentation of 17 spaces but found 10 spaces
<UButton class="text-white" label="ตารางสอน" size="xl" icon="i-heroicons-table-cells" color="primary"
variant="ghost" :ui="{ leadingIcon: 'text-primary' }" to="/teachers" />

Check failure on line 39 in app/app.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

Expected indentation of 17 spaces but found 10 spaces
<UColorModeButton />
</template>
</UHeader>
Expand Down
924 changes: 924 additions & 0 deletions app/pages/curriculums.vue

Large diffs are not rendered by default.

972 changes: 497 additions & 475 deletions app/pages/teacher/[id].vue

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions server/api/Subjects/[id].put.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ export default defineEventHandler(async (event) => {
const { id } = event.context.params
const body = await readBody(event)

// Validate input
if (!body.name_subject || body.name_subject.trim() === '') {
const newCurrId = body.curriculum_subject_id || null

if (!newCurrId) {
throw createError({
statusCode: 400,
statusMessage: 'name_subject is required'
statusMessage: 'curriculum_subject_id is required'
})
}

// 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 curriculum link (name_subject column is removed)
const stmt = db.prepare('UPDATE Subjects SET curriculum_subject_id = ? WHERE id_subject = ?')
stmt.run(newCurrId, id)

// Update Sections if provided
if (body.id_sections && Array.isArray(body.id_sections)) {
Expand Down
22 changes: 17 additions & 5 deletions server/api/Subjects/index.get.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export default defineEventHandler((event) => {
let baseQuery = `
SELECT s.*,
t.prefix, t.first_name, t.last_name,
cs.name_subject as curriculum_name_subject,
cs.subject_code,
(
SELECT GROUP_CONCAT(sec.section_name, ', ')
FROM SubjectSections ss
Expand All @@ -24,6 +26,7 @@ export default defineEventHandler((event) => {
) as sections_json
FROM Subjects s
LEFT JOIN teachers t ON s.id_teacher = t.id_teacher
LEFT JOIN curriculum_subjects cs ON s.curriculum_subject_id = cs.id_subject_curr
`

let whereClauses = []
Expand All @@ -45,9 +48,18 @@ export default defineEventHandler((event) => {
stmt = db.prepare(finalQuery)
subjects = stmt.all(...params)

// Parse JSON sections for each subject
return subjects.map(s => ({
...s,
sections: s.sections_json ? JSON.parse(s.sections_json) : []
}))
// Parse JSON sections for each subject and resolve dynamic name
return subjects.map(s => {
let resolvedName = 'Unknown'
if (s.curriculum_name_subject) {
// Strictly use its name (prepended with code if available)
resolvedName = s.subject_code ? `${s.subject_code} ${s.curriculum_name_subject}` : s.curriculum_name_subject
}

return {
...s,
name_subject: resolvedName,
sections: s.sections_json ? JSON.parse(s.sections_json) : []
}
})
})
29 changes: 22 additions & 7 deletions server/api/Subjects/index.post.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,31 @@ import db from '../../utils/db.js'
export default defineEventHandler(async (event) => {
try {
const body = await readBody(event)
if (!body.name_subject || !body.id_sections || body.id_sections.length === 0) {

// curriculum_subject_id is now strictly required as the sole name source
if (!body.curriculum_subject_id) {
throw createError({
statusCode: 400,
statusMessage: 'name subject and sections are required'
statusMessage: 'curriculum_subject_id is required'
})
}
if (!body.id_sections || body.id_sections.length === 0) {
throw createError({
statusCode: 400,
statusMessage: 'sections are required'
})
}

// Resolve name for the response payload to keep frontend happy
let resolvedName = 'Unknown'
const cs = db.prepare('SELECT subject_code, name_subject FROM curriculum_subjects WHERE id_subject_curr = ?').get(body.curriculum_subject_id)
if (cs) {
resolvedName = cs.subject_code ? `${cs.subject_code} ${cs.name_subject}` : cs.name_subject
}

// 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)
// 1. Insert Subject (name_subject column is removed)
const stmt = db.prepare('INSERT INTO Subjects (id_teacher, term, curriculum_subject_id) VALUES (?, ?, ?)')
const result = stmt.run(body.id_teacher, body.term || null, body.curriculum_subject_id)
const subjectId = result.lastInsertRowid

// 2. Insert SubjectSections (Join table)
Expand All @@ -26,15 +41,15 @@ export default defineEventHandler(async (event) => {

return {
id_subject: subjectId,
name_subject: body.name_subject,
name_subject: resolvedName,
id_sections: body.id_sections,
status: 1
}
} catch (error) {
if (error.code === 'SQLITE_CONSTRAINT_UNIQUE') {
throw createError({
statusCode: 409,
statusMessage: 'Name already exists'
statusMessage: 'Subject constraint error'
})
}
throw error
Expand Down
29 changes: 29 additions & 0 deletions server/api/curriculum-categories/[id].js
Original file line number Diff line number Diff line change
@@ -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 }
}
})
27 changes: 27 additions & 0 deletions server/api/curriculum-categories/index.js
Original file line number Diff line number Diff line change
@@ -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
}
}
})
29 changes: 29 additions & 0 deletions server/api/curriculum-subjects/[id].js
Original file line number Diff line number Diff line change
@@ -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 }
}
})
33 changes: 33 additions & 0 deletions server/api/curriculum-subjects/index.js
Original file line number Diff line number Diff line change
@@ -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
}
}
})
36 changes: 36 additions & 0 deletions server/api/curriculums/[id].js
Original file line number Diff line number Diff line change
@@ -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 }
}
})
29 changes: 29 additions & 0 deletions server/api/curriculums/index.js
Original file line number Diff line number Diff line change
@@ -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
}
}
})
8 changes: 6 additions & 2 deletions server/api/makeup-classes/[id].js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ export default defineEventHandler(async (event) => {
t.first_name,
t.last_name,
s.section_name,
sub.name_subject,
CASE WHEN cs.subject_code IS NOT NULL THEN cs.subject_code || ' ' || cs.name_subject ELSE cs.name_subject END as name_subject,
r.room_name
FROM makeup_classes mc
LEFT JOIN teachers t ON mc.teacher_id = t.id_teacher
LEFT JOIN sections s ON mc.section_id = s.id_section
LEFT JOIN Subjects sub ON mc.subject_id = sub.id_subject
LEFT JOIN curriculum_subjects cs ON sub.curriculum_subject_id = cs.id_subject_curr
LEFT JOIN rooms r ON mc.room_id = r.id_room
WHERE mc.id_makeup = ?
`)
Expand Down Expand Up @@ -142,9 +143,12 @@ export default defineEventHandler(async (event) => {
const syncCalendarEvent = async (makeupId) => {
// ค้นหาข้อมูล makeup class ปัจจุบัน
const cls = db.prepare(`
SELECT mc.*, s.name_subject, r.room_name, t.prefix, t.first_name, t.last_name
SELECT mc.*,
CASE WHEN cs.subject_code IS NOT NULL THEN cs.subject_code || ' ' || cs.name_subject ELSE cs.name_subject END as name_subject,
r.room_name, t.prefix, t.first_name, t.last_name
FROM makeup_classes mc
LEFT JOIN Subjects s ON mc.subject_id = s.id_subject
LEFT JOIN curriculum_subjects cs ON s.curriculum_subject_id = cs.id_subject_curr
LEFT JOIN rooms r ON mc.room_id = r.id_room
LEFT JOIN teachers t ON mc.teacher_id = t.id_teacher
WHERE mc.id_makeup = ?
Expand Down
3 changes: 2 additions & 1 deletion server/api/makeup-classes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ export default defineEventHandler(async (event) => {
t.first_name,
t.last_name,
s.section_name,
sub.name_subject,
CASE WHEN cs.subject_code IS NOT NULL THEN cs.subject_code || ' ' || cs.name_subject ELSE cs.name_subject END as name_subject,
r.room_name
FROM makeup_classes mc
LEFT JOIN teachers t ON mc.teacher_id = t.id_teacher
LEFT JOIN sections s ON mc.section_id = s.id_section
LEFT JOIN Subjects sub ON mc.subject_id = sub.id_subject
LEFT JOIN curriculum_subjects cs ON sub.curriculum_subject_id = cs.id_subject_curr
LEFT JOIN rooms r ON mc.room_id = r.id_room
WHERE 1=1
`
Expand Down
Loading
Loading