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]); }, [classId, date, period]);
const loadAttendance = useCallback(async () => { const loadAttendance = useCallback(async (isBackground: boolean | any = false) => {
const isBg = isBackground === true;
try { try {
setLoading(true); if (!isBg) setLoading(true);
const res = await apiFetchAttendance(classId, date, period); const res = await apiFetchAttendance(classId, date, period);
setRows(res.data || []); setRows(res.data || []);
setShiftInfo(res.shift || null); setShiftInfo(res.shift || null);
setSelectedStudentRkIds([]); // Clear selection when data changes if (!isBg) setSelectedStudentRkIds([]); // Clear selection when data changes
} catch (e: any) { } catch (e: any) {
alert(e.message || 'Không tải được điểm danh'); alert(e.message || 'Không tải được điểm danh');
} finally { } finally {
setLoading(false); if (!isBg) setLoading(false);
} }
}, [classId, date, period]); }, [classId, date, period]);
@@ -93,30 +94,64 @@ export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) =>
}, []); }, []);
const handleStatusChange = async (studentRkId: number, status: number) => { 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 { try {
await apiUpdateAttendanceStatus(classId, { date, period, studentRkId, status }); await apiUpdateAttendanceStatus(classId, { date, period, studentRkId, status });
await loadAttendance(); await loadAttendance(true);
} catch (e: any) { } catch (e: any) {
alert(e.message || 'Cập nhật trạng thái thất bại'); 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) => { const handleBulkStatusChange = async (status: number) => {
if (selectedStudentRkIds.length === 0) return; 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 { try {
setLoading(true);
await apiUpdateAttendanceBulkStatus(classId, { await apiUpdateAttendanceBulkStatus(classId, {
date, date,
period, period,
studentRkIds: selectedStudentRkIds, studentRkIds: idsToUpdate,
status, status,
}); });
setSelectedStudentRkIds([]); await loadAttendance(true);
await loadAttendance();
} catch (e: any) { } catch (e: any) {
alert(e.message || 'Cập nhật hàng loạt thất bại'); alert(e.message || 'Cập nhật hàng loạt thất bại');
} finally { await loadAttendance(); // Rollback
setLoading(false);
} }
}; };
@@ -175,7 +210,7 @@ export const AttendancePanel: React.FC<AttendancePanelProps> = ({ classId }) =>
onChange={e => setSearchQuery(e.target.value)} onChange={e => setSearchQuery(e.target.value)}
/> />
</label> </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 Tải lại
</button> </button>
<button type="button" className="btn btn-primary" onClick={handlePushQLDT} disabled={pushing || rows.length === 0}> <button type="button" className="btn btn-primary" onClick={handlePushQLDT} disabled={pushing || rows.length === 0}>