Update index.html
Browse files- index.html +50 -29
index.html
CHANGED
|
@@ -150,6 +150,7 @@
|
|
| 150 |
const [selectedStudents, setSelectedStudents] = useState([]);
|
| 151 |
const [teacherFilter, setTeacherFilter] = useState('all');
|
| 152 |
const [searchQuery, setSearchQuery] = useState('');
|
|
|
|
| 153 |
|
| 154 |
const CLASSES = useMemo(() => {
|
| 155 |
const sheetClasses = Object.keys(studentsData || {});
|
|
@@ -327,24 +328,29 @@
|
|
| 327 |
};
|
| 328 |
|
| 329 |
const saveTeacherLog = async () => {
|
| 330 |
-
if (!tempTeacher.name) return;
|
|
|
|
| 331 |
|
| 332 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
setAllClassLogs(prev => ({
|
| 334 |
...prev,
|
| 335 |
-
[
|
| 336 |
}));
|
|
|
|
| 337 |
|
| 338 |
await postLog({
|
| 339 |
-
className:
|
| 340 |
type: 'teacher',
|
| 341 |
-
name:
|
| 342 |
-
period:
|
| 343 |
-
subject:
|
| 344 |
action: 'set'
|
| 345 |
});
|
| 346 |
-
|
| 347 |
-
setIsTeacherModalOpen(false);
|
| 348 |
};
|
| 349 |
|
| 350 |
const openStudentModal = (slot) => {
|
|
@@ -354,44 +360,52 @@
|
|
| 354 |
};
|
| 355 |
|
| 356 |
const saveStudentLog = async () => {
|
| 357 |
-
if (selectedStudents.length === 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 358 |
const logId = Date.now();
|
| 359 |
const timeOutStr = `${today.getHours().toString().padStart(2, '0')}:${today.getMinutes().toString().padStart(2, '0')}`;
|
| 360 |
|
| 361 |
const newLog = {
|
| 362 |
id: logId,
|
| 363 |
-
names:
|
| 364 |
timeOut: timeOutStr,
|
| 365 |
timeIn: null
|
| 366 |
};
|
| 367 |
-
const updatedSlotLogs = [...(studentLogs[
|
| 368 |
-
const updatedStudentLogs = { ...studentLogs, [
|
| 369 |
|
| 370 |
setAllClassLogs(prev => ({
|
| 371 |
...prev,
|
| 372 |
-
[
|
| 373 |
}));
|
|
|
|
| 374 |
|
| 375 |
await postLog({
|
| 376 |
-
className:
|
| 377 |
type: 'student',
|
| 378 |
name: '',
|
| 379 |
-
period:
|
| 380 |
-
subject: teacherLogs[
|
| 381 |
action: 'exit',
|
| 382 |
-
students:
|
| 383 |
timeOut: timeOutStr,
|
| 384 |
logId: logId
|
| 385 |
});
|
| 386 |
-
|
| 387 |
-
setIsStudentModalOpen(false);
|
| 388 |
};
|
| 389 |
|
| 390 |
const confirmDelete = async () => {
|
| 391 |
-
if (!itemToDelete) return;
|
|
|
|
|
|
|
|
|
|
| 392 |
|
| 393 |
-
if (
|
| 394 |
-
const { slotId, logId } =
|
| 395 |
const updatedLogs = (studentLogs[slotId] || []).filter(log => log.id !== logId);
|
| 396 |
setAllClassLogs(prev => ({
|
| 397 |
...prev,
|
|
@@ -405,8 +419,8 @@
|
|
| 405 |
action: 'delete',
|
| 406 |
logId: logId
|
| 407 |
});
|
| 408 |
-
} else if (
|
| 409 |
-
const { slotId } =
|
| 410 |
const newTeacherLogs = { ...teacherLogs };
|
| 411 |
delete newTeacherLogs[slotId];
|
| 412 |
setAllClassLogs(prev => ({
|
|
@@ -421,11 +435,12 @@
|
|
| 421 |
action: 'delete'
|
| 422 |
});
|
| 423 |
}
|
| 424 |
-
|
| 425 |
};
|
| 426 |
|
| 427 |
const handleCopyPrevious = async (e, currentSlotId) => {
|
| 428 |
e.stopPropagation();
|
|
|
|
| 429 |
const currentIndex = currentTimeSlots.findIndex(s => s.id === currentSlotId);
|
| 430 |
let prevIndex = currentIndex - 1;
|
| 431 |
while (prevIndex >= 0 && (currentTimeSlots[prevIndex].id === 'R' || !teacherLogs[currentTimeSlots[prevIndex].id])) {
|
|
@@ -434,6 +449,7 @@
|
|
| 434 |
if (prevIndex >= 0) {
|
| 435 |
const prevLog = teacherLogs[currentTimeSlots[prevIndex].id];
|
| 436 |
if (prevLog && prevLog.name) {
|
|
|
|
| 437 |
const updatedTeacherLogs = { ...teacherLogs, [currentSlotId]: { ...prevLog } };
|
| 438 |
setAllClassLogs(prev => ({
|
| 439 |
...prev,
|
|
@@ -448,6 +464,7 @@
|
|
| 448 |
subject: prevLog.subject,
|
| 449 |
action: 'set'
|
| 450 |
});
|
|
|
|
| 451 |
}
|
| 452 |
}
|
| 453 |
};
|
|
@@ -1067,8 +1084,10 @@
|
|
| 1067 |
</select>
|
| 1068 |
|
| 1069 |
<div className="flex gap-3">
|
| 1070 |
-
<button onClick={() => setIsTeacherModalOpen(false)} className="flex-1 py-3 text-sm font-bold text-slate-600 bg-slate-100 rounded-xl hover:bg-slate-200">Batal</button>
|
| 1071 |
-
<button onClick={saveTeacherLog} className="flex-1 py-3 text-sm font-bold text-white bg-indigo-600 rounded-xl hover:bg-indigo-700">
|
|
|
|
|
|
|
| 1072 |
</div>
|
| 1073 |
</div>
|
| 1074 |
</div>
|
|
@@ -1113,7 +1132,9 @@
|
|
| 1113 |
|
| 1114 |
<div className="flex gap-3 mt-auto pt-2">
|
| 1115 |
<button onClick={() => setIsStudentModalOpen(false)} className="flex-1 py-3 text-sm font-bold text-slate-600 bg-slate-100 rounded-xl hover:bg-slate-200">Batal</button>
|
| 1116 |
-
<button onClick={saveStudentLog} disabled={selectedStudents.length === 0} className="flex-1 py-3 text-sm font-bold text-white bg-indigo-600 rounded-xl hover:bg-indigo-700 disabled:opacity-50 disabled:cursor-not-allowed">
|
|
|
|
|
|
|
| 1117 |
</div>
|
| 1118 |
</div>
|
| 1119 |
</div>
|
|
|
|
| 150 |
const [selectedStudents, setSelectedStudents] = useState([]);
|
| 151 |
const [teacherFilter, setTeacherFilter] = useState('all');
|
| 152 |
const [searchQuery, setSearchQuery] = useState('');
|
| 153 |
+
const [isSaving, setIsSaving] = useState(false);
|
| 154 |
|
| 155 |
const CLASSES = useMemo(() => {
|
| 156 |
const sheetClasses = Object.keys(studentsData || {});
|
|
|
|
| 328 |
};
|
| 329 |
|
| 330 |
const saveTeacherLog = async () => {
|
| 331 |
+
if (!tempTeacher.name || isSaving || !activeSlot) return;
|
| 332 |
+
setIsSaving(true);
|
| 333 |
|
| 334 |
+
const slotId = activeSlot.id;
|
| 335 |
+
const teacher = { ...tempTeacher };
|
| 336 |
+
const cls = selectedClass;
|
| 337 |
+
|
| 338 |
+
const updatedTeacherLogs = { ...teacherLogs, [slotId]: teacher };
|
| 339 |
setAllClassLogs(prev => ({
|
| 340 |
...prev,
|
| 341 |
+
[cls]: { ...(prev[cls] || {}), teacherLogs: updatedTeacherLogs, studentLogs: (prev[cls] || {}).studentLogs || {} }
|
| 342 |
}));
|
| 343 |
+
setIsTeacherModalOpen(false);
|
| 344 |
|
| 345 |
await postLog({
|
| 346 |
+
className: cls,
|
| 347 |
type: 'teacher',
|
| 348 |
+
name: teacher.name,
|
| 349 |
+
period: slotId,
|
| 350 |
+
subject: teacher.subject,
|
| 351 |
action: 'set'
|
| 352 |
});
|
| 353 |
+
setIsSaving(false);
|
|
|
|
| 354 |
};
|
| 355 |
|
| 356 |
const openStudentModal = (slot) => {
|
|
|
|
| 360 |
};
|
| 361 |
|
| 362 |
const saveStudentLog = async () => {
|
| 363 |
+
if (selectedStudents.length === 0 || isSaving || !activeSlot) return;
|
| 364 |
+
setIsSaving(true);
|
| 365 |
+
|
| 366 |
+
const slotId = activeSlot.id;
|
| 367 |
+
const students = [...selectedStudents];
|
| 368 |
+
const cls = selectedClass;
|
| 369 |
const logId = Date.now();
|
| 370 |
const timeOutStr = `${today.getHours().toString().padStart(2, '0')}:${today.getMinutes().toString().padStart(2, '0')}`;
|
| 371 |
|
| 372 |
const newLog = {
|
| 373 |
id: logId,
|
| 374 |
+
names: students,
|
| 375 |
timeOut: timeOutStr,
|
| 376 |
timeIn: null
|
| 377 |
};
|
| 378 |
+
const updatedSlotLogs = [...(studentLogs[slotId] || []), newLog];
|
| 379 |
+
const updatedStudentLogs = { ...studentLogs, [slotId]: updatedSlotLogs };
|
| 380 |
|
| 381 |
setAllClassLogs(prev => ({
|
| 382 |
...prev,
|
| 383 |
+
[cls]: { ...(prev[cls] || {}), teacherLogs: (prev[cls] || {}).teacherLogs || {}, studentLogs: updatedStudentLogs }
|
| 384 |
}));
|
| 385 |
+
setIsStudentModalOpen(false);
|
| 386 |
|
| 387 |
await postLog({
|
| 388 |
+
className: cls,
|
| 389 |
type: 'student',
|
| 390 |
name: '',
|
| 391 |
+
period: slotId,
|
| 392 |
+
subject: teacherLogs[slotId]?.subject || '',
|
| 393 |
action: 'exit',
|
| 394 |
+
students: students,
|
| 395 |
timeOut: timeOutStr,
|
| 396 |
logId: logId
|
| 397 |
});
|
| 398 |
+
setIsSaving(false);
|
|
|
|
| 399 |
};
|
| 400 |
|
| 401 |
const confirmDelete = async () => {
|
| 402 |
+
if (!itemToDelete || isSaving) return;
|
| 403 |
+
setIsSaving(true);
|
| 404 |
+
const toDelete = itemToDelete;
|
| 405 |
+
setItemToDelete(null);
|
| 406 |
|
| 407 |
+
if (toDelete.type === 'student') {
|
| 408 |
+
const { slotId, logId } = toDelete;
|
| 409 |
const updatedLogs = (studentLogs[slotId] || []).filter(log => log.id !== logId);
|
| 410 |
setAllClassLogs(prev => ({
|
| 411 |
...prev,
|
|
|
|
| 419 |
action: 'delete',
|
| 420 |
logId: logId
|
| 421 |
});
|
| 422 |
+
} else if (toDelete.type === 'teacher') {
|
| 423 |
+
const { slotId } = toDelete;
|
| 424 |
const newTeacherLogs = { ...teacherLogs };
|
| 425 |
delete newTeacherLogs[slotId];
|
| 426 |
setAllClassLogs(prev => ({
|
|
|
|
| 435 |
action: 'delete'
|
| 436 |
});
|
| 437 |
}
|
| 438 |
+
setIsSaving(false);
|
| 439 |
};
|
| 440 |
|
| 441 |
const handleCopyPrevious = async (e, currentSlotId) => {
|
| 442 |
e.stopPropagation();
|
| 443 |
+
if (isSaving) return;
|
| 444 |
const currentIndex = currentTimeSlots.findIndex(s => s.id === currentSlotId);
|
| 445 |
let prevIndex = currentIndex - 1;
|
| 446 |
while (prevIndex >= 0 && (currentTimeSlots[prevIndex].id === 'R' || !teacherLogs[currentTimeSlots[prevIndex].id])) {
|
|
|
|
| 449 |
if (prevIndex >= 0) {
|
| 450 |
const prevLog = teacherLogs[currentTimeSlots[prevIndex].id];
|
| 451 |
if (prevLog && prevLog.name) {
|
| 452 |
+
setIsSaving(true);
|
| 453 |
const updatedTeacherLogs = { ...teacherLogs, [currentSlotId]: { ...prevLog } };
|
| 454 |
setAllClassLogs(prev => ({
|
| 455 |
...prev,
|
|
|
|
| 464 |
subject: prevLog.subject,
|
| 465 |
action: 'set'
|
| 466 |
});
|
| 467 |
+
setIsSaving(false);
|
| 468 |
}
|
| 469 |
}
|
| 470 |
};
|
|
|
|
| 1084 |
</select>
|
| 1085 |
|
| 1086 |
<div className="flex gap-3">
|
| 1087 |
+
<button onClick={() => setIsTeacherModalOpen(false)} disabled={isSaving} className="flex-1 py-3 text-sm font-bold text-slate-600 bg-slate-100 rounded-xl hover:bg-slate-200 disabled:opacity-50">Batal</button>
|
| 1088 |
+
<button onClick={saveTeacherLog} disabled={isSaving || !tempTeacher.name} className="flex-1 py-3 text-sm font-bold text-white bg-indigo-600 rounded-xl hover:bg-indigo-700 disabled:opacity-50 disabled:cursor-not-allowed">
|
| 1089 |
+
{isSaving ? 'Menyimpan...' : 'Simpan'}
|
| 1090 |
+
</button>
|
| 1091 |
</div>
|
| 1092 |
</div>
|
| 1093 |
</div>
|
|
|
|
| 1132 |
|
| 1133 |
<div className="flex gap-3 mt-auto pt-2">
|
| 1134 |
<button onClick={() => setIsStudentModalOpen(false)} className="flex-1 py-3 text-sm font-bold text-slate-600 bg-slate-100 rounded-xl hover:bg-slate-200">Batal</button>
|
| 1135 |
+
<button onClick={saveStudentLog} disabled={selectedStudents.length === 0 || isSaving} className="flex-1 py-3 text-sm font-bold text-white bg-indigo-600 rounded-xl hover:bg-indigo-700 disabled:opacity-50 disabled:cursor-not-allowed">
|
| 1136 |
+
{isSaving ? 'Menyimpan...' : 'Rekod Keluar'}
|
| 1137 |
+
</button>
|
| 1138 |
</div>
|
| 1139 |
</div>
|
| 1140 |
</div>
|