optimize diem danh
All checks were successful
Deploy on Master Change / deploy (push) Successful in 42s
All checks were successful
Deploy on Master Change / deploy (push) Successful in 42s
This commit is contained in:
@@ -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}>
|
||||||
|
|||||||
Reference in New Issue
Block a user