optimize diem danh
All checks were successful
Deploy on Master Change / deploy (push) Successful in 42s

This commit is contained in:
2026-07-03 06:50:04 +07:00
parent 9a994da7f8
commit 0fa4a9aae2

View File

@@ -49,17 +49,18 @@ export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) =>
}
}, [classId, date, period]);
const loadAttendance = useCallback(async () => {
const loadAttendance = useCallback(async (isBackground: boolean | any = false) => {
const isBg = isBackground === true;
try {
setLoading(true);
if (!isBg) setLoading(true);
const res = await apiFetchAttendance(classId, date, period);
setRows(res.data || []);
setShiftInfo(res.shift || null);
setSelectedStudentRkIds([]); // Clear selection when data changes
if (!isBg) setSelectedStudentRkIds([]); // Clear selection when data changes
} catch (e: any) {
alert(e.message || 'Không tải được điểm danh');
} finally {
setLoading(false);
if (!isBg) setLoading(false);
}
}, [classId, date, period]);
@@ -93,30 +94,64 @@ export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) =>
}, []);
const handleStatusChange = async (studentRkId: number, status: number) => {
// Optimistic state update so the UI reacts instantly
setRows(prevRows =>
prevRows.map(row => {
if (row.studentRkId === studentRkId) {
const matchedOpt = ATTENDANCE_STATUS_OPTIONS.find(o => o.value === status);
return {
...row,
status,
statusLabel: matchedOpt ? matchedOpt.label : row.statusLabel,
statusEditedByTeacher: true,
};
}
return row;
})
);
try {
await apiUpdateAttendanceStatus(classId, { date, period, studentRkId, status });
await loadAttendance();
await loadAttendance(true);
} catch (e: any) {
alert(e.message || 'Cập nhật trạng thái thất bại');
await loadAttendance(); // Rollback on failure
}
};
const handleBulkStatusChange = async (status: number) => {
if (selectedStudentRkIds.length === 0) return;
// Optimistic state update for selected students
setRows(prevRows =>
prevRows.map(row => {
if (selectedStudentRkIds.includes(row.studentRkId)) {
const matchedOpt = ATTENDANCE_STATUS_OPTIONS.find(o => o.value === status);
return {
...row,
status,
statusLabel: matchedOpt ? matchedOpt.label : row.statusLabel,
statusEditedByTeacher: true,
};
}
return row;
})
);
const idsToUpdate = [...selectedStudentRkIds];
setSelectedStudentRkIds([]);
try {
setLoading(true);
await apiUpdateAttendanceBulkStatus(classId, {
date,
period,
studentRkIds: selectedStudentRkIds,
studentRkIds: idsToUpdate,
status,
});
setSelectedStudentRkIds([]);
await loadAttendance();
await loadAttendance(true);
} catch (e: any) {
alert(e.message || 'Cập nhật hàng loạt thất bại');
} finally {
setLoading(false);
await loadAttendance(); // Rollback
}
};
@@ -175,7 +210,7 @@ export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) =>
onChange={e => setSearchQuery(e.target.value)}
/>
</label>
<button type="button" className="btn btn-secondary" onClick={loadAttendance} disabled={loading}>
<button type="button" className="btn btn-secondary" onClick={() => loadAttendance(false)} disabled={loading}>
Tải lại
</button>
<button type="button" className="btn btn-primary" onClick={handlePushQLDT} disabled={pushing || rows.length === 0}>