From a53baed0792253852e56c36aaca232aee906abf5 Mon Sep 17 00:00:00 2001 From: Nav0za Date: Tue, 21 Apr 2026 16:43:18 +0700 Subject: [PATCH] fix makeup detecting section --- app/components/AppCalendar.vue | 55 +++++++++++++++----------- create_test_data.js | 52 ------------------------ debug_output.txt | 2 - diagnostic_output.txt | 3 -- makeup.db | 0 server/api/teacher-classes-on-date.js | 46 ++++++++++++++------- server/data/data.db | Bin 323584 -> 323584 bytes server/utils/autoSchedule.js | 27 +++++++++---- tmp.txt | 0 9 files changed, 83 insertions(+), 102 deletions(-) delete mode 100644 create_test_data.js delete mode 100644 debug_output.txt delete mode 100644 diagnostic_output.txt delete mode 100644 makeup.db delete mode 100644 tmp.txt diff --git a/app/components/AppCalendar.vue b/app/components/AppCalendar.vue index 17a7553..70f5658 100644 --- a/app/components/AppCalendar.vue +++ b/app/components/AppCalendar.vue @@ -1454,6 +1454,7 @@ const saveAbsenceAndFindSlots = async () => { classes: selectedClasses.map(c => ({ subjectId: c.subjectId, sectionId: c.sectionId, + sectionIds: c.sectionIds, roomId: c.roomId, duration: c.duration, subjectName: c.subjectName @@ -1498,6 +1499,7 @@ const saveAbsenceAndFindSlots = async () => { classes: JSON.stringify(rescheduledClasses.map(c => ({ subjectId: c.subjectId, sectionId: c.sectionId, + sectionIds: c.sectionIds, roomId: c.roomId, duration: c.duration, subjectName: c.subjectName @@ -1657,32 +1659,41 @@ const saveMakeupClass = async () => { let endTime = addHours(currentTime, cls.duration) // ถ้าเวลาจบคร่อม 12:00 (หมายถึงช่วง 12:00-13:00) ให้บวกเพิ่มอีก 1 ชม. เพื่อข้ามพักเที่ยง - // เช่น เริ่ม 11:00 เรียน 2 ชม. ปกติจบ 13:00 แต่ต้องจบ 14:00 เพราะพัก 12:00-13:00 const startHour = parseInt(currentTime.split(':')[0]) const endHour = parseInt(endTime.split(':')[0]) if (startHour < 12 && endHour > 12) { endTime = addHours(endTime, 1) } - const res = await $fetch('/api/makeup-classes', { - method: 'POST', - body: { - original_date: selectedSlot.value.missedDate || absenceForm.value.date, - original_time_slot: '', - makeup_date: selectedSlot.value.date, - makeup_time_start: currentTime, - makeup_time_end: endTime, - teacher_id: absenceForm.value.teacherId, - section_id: cls.sectionId, - subject_id: cls.subjectId, - room_id: cls.roomId || null, - term: absenceForm.value.term, - status: 'confirmed', - notes: makeupForm.value.notes, - exclude_makeup_ids: createdIds // ป้องกัน false-positive จากวิชาก่อนหน้าในกลุ่มเดียวกัน - } - }) - createdIds.push(res.id_makeup) + const sectionIds = cls.sectionIds && cls.sectionIds.length > 0 ? cls.sectionIds : [cls.sectionId] + const makeupIdsForThisClass = [] + + for (const secId of sectionIds) { + if (!secId) continue + const res = await $fetch('/api/makeup-classes', { + method: 'POST', + body: { + original_date: selectedSlot.value.missedDate || absenceForm.value.date, + original_time_slot: '', + makeup_date: selectedSlot.value.date, + makeup_time_start: currentTime, + makeup_time_end: endTime, + teacher_id: absenceForm.value.teacherId, + section_id: secId, + subject_id: cls.subjectId, + room_id: cls.roomId || null, + term: absenceForm.value.term, + status: 'confirmed', + notes: makeupForm.value.notes, + exclude_makeup_ids: createdIds // ป้องกัน false-positive จากวิชาก่อนหน้าในกลุ่มเดียวกัน + } + }) + createdIds.push(res.id_makeup) + makeupIdsForThisClass.push(res.id_makeup) + } + + // Save the ids for the calendar event creation + cls._makeupIds = makeupIdsForThisClass currentTime = endTime } } else { @@ -1709,7 +1720,7 @@ const saveMakeupClass = async () => { let currentTime = selectedSlot.value.timeStart for (let i = 0; i < classes.length; i++) { const cls = classes[i] - const idMakeup = createdIds[i] + const idsMakeup = cls._makeupIds || [] if (currentTime === '12:00') currentTime = '13:00' @@ -1735,7 +1746,7 @@ const saveMakeupClass = async () => { originalDate: selectedSlot.value.missedDate || absenceForm.value.date, term: absenceForm.value.term, classes: JSON.stringify([cls]), - makeupClassIds: JSON.stringify([idMakeup]), + makeupClassIds: JSON.stringify(idsMakeup), roomId: cls.roomId, roomName: props.rooms?.find(r => Number(r.id_room) === Number(cls.roomId))?.room_name || '', description: `วิชา: ${cls.subjectName}${cls.roomId ? '\nห้อง: ' + (props.rooms?.find(r => Number(r.id_room) === Number(cls.roomId))?.room_name || cls.roomId) : ''}${makeupForm.value.notes ? '\n' + makeupForm.value.notes : ''}` diff --git a/create_test_data.js b/create_test_data.js deleted file mode 100644 index ddccaf2..0000000 --- a/create_test_data.js +++ /dev/null @@ -1,52 +0,0 @@ -import Database from 'better-sqlite3' -const db = new Database('./server/data/data.db') - -// Insert test makeup class data -const today = new Date().toISOString().split('T')[0] -const tomorrow = new Date(Date.now() + 86400000).toISOString().split('T')[0] - -console.log('Creating test makeup_classes data...') - -const teacher = db.prepare('SELECT id_teacher FROM teachers LIMIT 1').get() -const section = db.prepare('SELECT id_section FROM sections LIMIT 1').get() -const subject = db.prepare('SELECT id_subject FROM Subjects LIMIT 1').get() -const room = db.prepare('SELECT id_room FROM rooms LIMIT 1').get() - -if (!teacher || !section || !subject) { - console.error('Missing required data (teacher, section, or subject)') - process.exit(1) -} - -const insertStmt = db.prepare(` - INSERT INTO makeup_classes ( - original_date, original_time_slot, makeup_date, - makeup_time_start, makeup_time_end, teacher_id, - section_id, subject_id, room_id, status, notes - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -`) - -// Create 3 test makeup classes -for (let i = 1; i <= 3; i++) { - const result = insertStmt.run( - today, - '08:00-09:00', - tomorrow, - '08:00', - `0${8+i}:00`, - teacher.id_teacher, - section.id_section, - subject.id_subject, - room?.id_room || null, - 'confirmed', - `Test makeup class ${i}` - ) - console.log(` Created makeup_class ID: ${result.lastInsertRowid}`) -} - -// Verify -const count = db.prepare('SELECT COUNT(*) as cnt FROM makeup_classes').get() -console.log(`\nTotal makeup_classes: ${count.cnt}`) - -const sample = db.prepare('SELECT id_makeup, status FROM makeup_classes').all() -console.log('Sample:') -console.log(JSON.stringify(sample, null, 2)) diff --git a/debug_output.txt b/debug_output.txt deleted file mode 100644 index 739508b..0000000 --- a/debug_output.txt +++ /dev/null @@ -1,2 +0,0 @@ -Teacher found: { id_teacher: 21, name: 'ผศ.ดร.จารุวรรณ ธาระศัพท์', subject: null } -Schedule: [[{"value":42},{"value":32},{"value":37},{"value":37},{"value":null},{"value":37},{"value":43},{"value":43},{"value":41},{"value":41},{"value":39},{"value":39},{"value":39}],[{"value":35},{"value":35},{"value":35},{"value":35},{"value":null},{"value":31},{"value":31},{"value":40},{"value":null},{"value":34},{"value":34},{"value":42},{"value":42}],[{"value":32},{"value":32},{"value":38},{"value":38},{"value":null},{"value":33},{"value":33},{"value":33},{"value":31},{"value":31},{"value":41},{"value":41},{"value":41}],[{"value":38},{"value":29},{"value":29},{"value":29},{"value":null},{"value":42},{"value":35},{"value":35},{"value":35},{"value":35},{"value":35},{"value":35},{"value":44}],[{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null}],[{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null}],[{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null}]] diff --git a/diagnostic_output.txt b/diagnostic_output.txt deleted file mode 100644 index 6891a1e..0000000 --- a/diagnostic_output.txt +++ /dev/null @@ -1,3 +0,0 @@ ---- TEACHER 4 TERM 2/69 --- -ID: 2, Teacher: 4, Term: 2/69 -Data: [[{"value":7},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null}],[{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null}],[{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null}],[{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null}],[{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null}],[{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null}],[{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null},{"value":null}]] diff --git a/makeup.db b/makeup.db deleted file mode 100644 index e69de29..0000000 diff --git a/server/api/teacher-classes-on-date.js b/server/api/teacher-classes-on-date.js index c3bc72d..74e44f7 100644 --- a/server/api/teacher-classes-on-date.js +++ b/server/api/teacher-classes-on-date.js @@ -55,6 +55,7 @@ export default defineEventHandler(async (event) => { // นับจำนวนชั่วโมงติดต่อกันของแต่ละวิชา let currentSubject = null let currentRoom = null + let currentSectionIdsStr = null let startSlot = 0 let duration = 0 @@ -62,47 +63,58 @@ export default defineEventHandler(async (event) => { const slot = daySchedule[i] const slotValue = slot?.value const slotRoom = slot?.room_id || null + const slotSectionIds = slot?.section_ids || null // แปลงเป็น String เพื่อความชัวร์ในการเปรียบเทียบ (null/undefined จะกลายเป็น "null"/"undefined") const valStr = slotValue ? String(slotValue) : null + const secStr = slotSectionIds ? JSON.stringify(slotSectionIds) : null if (valStr && valStr !== 'null') { - // ถ้าเป็นวิชาเดิมและห้องเดิม - if (valStr === currentSubject && slotRoom === currentRoom) { + // ถ้าเป็นวิชาเดิมและห้องเดิมและกลุ่มเดิม + if (valStr === currentSubject && slotRoom === currentRoom && secStr === currentSectionIdsStr) { duration++ } else { // จบวิชาก่อนหน้า if (currentSubject) { - classes.push(createClassObj(currentSubject, startSlot, duration, teacher_id, date, currentRoom)) + classes.push(createClassObj(currentSubject, startSlot, duration, teacher_id, date, currentRoom, currentSectionIdsStr ? JSON.parse(currentSectionIdsStr) : null)) } // เริ่มวิชาใหม่ currentSubject = valStr currentRoom = slotRoom + currentSectionIdsStr = secStr startSlot = i duration = 1 } } else { // เจอช่องว่าง if (currentSubject) { - classes.push(createClassObj(currentSubject, startSlot, duration, teacher_id, date, currentRoom)) + classes.push(createClassObj(currentSubject, startSlot, duration, teacher_id, date, currentRoom, currentSectionIdsStr ? JSON.parse(currentSectionIdsStr) : null)) currentSubject = null currentRoom = null + currentSectionIdsStr = null } } } // เก็บตกตัวสุดท้าย if (currentSubject) { - classes.push(createClassObj(currentSubject, startSlot, duration, teacher_id, date, currentRoom)) + classes.push(createClassObj(currentSubject, startSlot, duration, teacher_id, date, currentRoom, currentSectionIdsStr ? JSON.parse(currentSectionIdsStr) : null)) } console.log('[teacher-classes-on-date] Merged classes:', classes) return classes }) -function createClassObj(subjectId, startSlot, duration, teacherId, date, roomId) { - const sectionId = getSectionForSubject(subjectId) +function createClassObj(subjectId, startSlot, duration, teacherId, date, roomId, slotSectionIds) { + let sectionIds = [] + if (Array.isArray(slotSectionIds) && slotSectionIds.length > 0) { + sectionIds = slotSectionIds + } else { + sectionIds = getSectionsForSubjectArray(subjectId) + } + + const sectionId = sectionIds[0] || null let hasMakeup = false if (teacherId && date) { @@ -121,7 +133,8 @@ function createClassObj(subjectId, startSlot, duration, teacherId, date, roomId) subjectId, subjectName: getSubjectName(subjectId), sectionId, - sectionName: getSectionName(sectionId, subjectId), + sectionIds, + sectionName: sectionIds.map(id => getSectionNameByKey(id)).join(', ') || getSectionName(sectionId, subjectId), roomId: finalRoomId, startSlot, duration, @@ -137,14 +150,17 @@ function getSubjectName(subjectId) { return result?.name_subject || 'ไม่ทราบชื่อวิชา' } -function getSectionForSubject(subjectId) { - const stmt = db.prepare('SELECT id_section FROM SubjectSections WHERE id_subject = ? LIMIT 1') - const result = stmt.get(subjectId) - if (result) return result.id_section +function getSectionsForSubjectArray(subjectId) { + const stmt = db.prepare('SELECT id_section FROM SubjectSections WHERE id_subject = ?') + const results = stmt.all(subjectId) + return results.map(r => r.id_section) +} - const fallbackStmt = db.prepare('SELECT id_section FROM Subjects WHERE id_subject = ?') - const fallbackResult = fallbackStmt.get(subjectId) - return fallbackResult?.id_section || null +function getSectionNameByKey(sectionId) { + if (!sectionId) return '' + const stmt = db.prepare('SELECT section_name FROM sections WHERE id_section = ?') + const result = stmt.get(sectionId) + return result?.section_name || '' } function getSectionName(sectionId, subjectId) { diff --git a/server/data/data.db b/server/data/data.db index 705bcca451341ea98f67a7075a14f15bb2d2d854..96d98e946c113f5088f2592257759271d7e21a70 100644 GIT binary patch delta 2122 zcmb7FYitx%6uysPyR)y`c9l}M-JMxVAK=<~l_Us@+&BMFG}`7c^hslS*wD`y`hg}+Y{+&Iw##*oXETwbV??Q`u6`K^0Jawl z?Ol}4W~8HPPTASeru-Oxv_0!S_aa*6y-u99^$wr4`)IYN|Y5+stb zY85?PBiVM;!VPH)S5o{%3ob5Ndkw|gl`F@*gBA2?vz1You3Sy=oD(3Y@n8v-p@pBB z#CtHG#l?D#mix0bq15`TB&>9hIr?_j0r6fZ^EAG&hE^0kP2?3ALTtWQ1vcNpk4(+0 zkg2yAnd)39XmSwGU;sH2(Ej?Q(D1moT7%vN5jWEwBtX3{0r`hgD<+!98{gqB?i}dv z>D~2QcUM;GZO$dy!n^sB5(ptsClj!|In@uBl>F~b@Mlv{P;Hrl{$Sb^^g8&DGX@W) z$??3%l_~OQ0{Z>8ZmhN>-~+cA-d?u2bn3+f4AkAap?S%JLI8+r4Z5K*GMHCxgx^3BEq>M!nq>Cxgx?#BEBP* z(29&t$=Z~)&Dj;$9O*~qq*cfqP>jsJE>xbfcG-CY`XOS!q%J;XcR6t>l4_;(;RX;t zaQPZcUcQEeQ5?W?YC#mY!nx$8D4xRV`~p6heCcKU%CiNYc@V%K0@X2#pGfx1;-mC0 z6M)I&!UDF5YrXa$aNgzk%(036nr*Ys*vIWb5+gO9Up+nU-`vOC5!a8dSDiD?7gX^* zW?FrH1H-G_W`?LAnNN@COzSdOv%;GBu55&sV!y!D7 KoM~oC&;0|6#}Cf{ delta 1265 zcma)5TSyd97(QptIJ)jSXEyP&yE?mT5Q=Wj&aPu>ZfR0I1QqyHl(!5sF(@qqsa-Ql zqO0Vwix%|m8dFX$VPFJ38Rbji!{Uoc7YszoL_4!vt(q;%Hyr-+&G&zY`M){-P^v$a zdcy|$Z)WX){f}$sH>cZD4JaQr)U`CW9;rK7(|EeEwe6I>x23QG4yZ$gl>)Z`08mn| z&Tq?ws#7>)RB!JYXK9sYP|qIt#L=oINgX@p+muFy4BrFrF22XGocM#A)Is0B;@B>$ zUFbd7H|vs%56P0OxFoO3UF?wLQn{$qTeJ?Szpeh{B` zmy}8pee4;?1OJReR;t}fgcsNH-Uy#fhc2zudm?-mD!FIj$X#c(3B zl%A}Ii01&F!wXTU=FkhAlYk&(?BP`mulnI8Mn8&UY&xTU9~DB${0F{+PuD@iXL+X9 zMji&?Mp8Be?T*-~*IFaefjFd=h;*4s#n`T7YYr(;( z0Qen#iJ$7ANj}1aj6#0znh{&qOq_O#e}j=S=@v(~8UJ-2|HoNb?W|Z^U-~i6!Kl4XZ zzNGZ0l$$0^SB%rfcH$gnUz6(Bt9{Q{-#T3R$Xk>JHHU{~613rVgcwa& UWcEWQkWH?+& 0) { + sectionIds = slotSectionIds + } else { + sectionIds = getSectionsForSubject(subjectId) + } return { subjectId, subjectName: subjectNameMap[subjectId] || getSubjectName(subjectId), @@ -474,7 +485,7 @@ export async function findAvailableSlotsForMultipleClasses(teacherId, missedDate } const totalDuration = classes.reduce((sum, c) => sum + c.duration, 0) - const allSectionIds = [...new Set(classes.map(c => c.sectionId).filter(Boolean))] + const allSectionIds = [...new Set(classes.flatMap(c => c.sectionIds || [c.sectionId]).filter(Boolean))] const teacherSchedulesRaw = db.prepare('SELECT id_teacher, scheduleData FROM schedules WHERE term = ?').all(term) const allTeacherSchedules = teacherSchedulesRaw.map((ts) => { diff --git a/tmp.txt b/tmp.txt deleted file mode 100644 index e69de29..0000000