fix: UI improvements and DingTalk attendance refresh for completed sessions
All checks were successful
CI / check (pull_request) Successful in 1m33s
All checks were successful
CI / check (pull_request) Successful in 1m33s
- DatePicker editors open immediately on double-click - Student archive drawer hides empty studentNo parentheses - Student add/edit form uses 2-column grid layout - Table horizontal scrollbars show only on hover/focus - Remove fake 近7日趋势 stats from attendance student detail - Allow DingTalk refresh to update completed attendance sessions (late-arriving punches can now change absent → present) - Switch dev command from concurrently to turbo run dev - Remove concurrently/wait-on dependencies
This commit is contained in:
@@ -252,8 +252,9 @@ describe('AttendanceService \u2014 DingTalk course attendance', () => {
|
||||
expect(result.records).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('returns a completed session as-is without refreshing', async () => {
|
||||
const { service, attendanceRepo, scheduleRepo, sessionRepo } = createService();
|
||||
it('refreshes a completed session when DingTalk punches arrive late', async () => {
|
||||
const { service, attendanceRepo, dingRawRepo, scheduleRepo, classStudentRepo, sessionRepo } =
|
||||
createService();
|
||||
scheduleRepo.findOne.mockResolvedValue(endedSchedule);
|
||||
sessionRepo.findOne.mockResolvedValue({
|
||||
id: 90,
|
||||
@@ -262,13 +263,27 @@ describe('AttendanceService \u2014 DingTalk course attendance', () => {
|
||||
lessonDate: '2026-07-11',
|
||||
status: 'completed',
|
||||
});
|
||||
attendanceRepo.find.mockResolvedValue([{ id: 1, attendanceSessionId: 90, status: 'present' }]);
|
||||
attendanceRepo.find.mockResolvedValue([
|
||||
{ id: 1, studentId: 1, attendanceSessionId: 90, status: 'absent', source: 'dingtalk' },
|
||||
]);
|
||||
classStudentRepo.find.mockResolvedValue([
|
||||
{ studentId: 1, student: { id: 1, name: '张三' } },
|
||||
]);
|
||||
dingRawRepo.find.mockResolvedValue([
|
||||
{
|
||||
matchedStudentId: 1,
|
||||
attendanceType: 'OnDuty',
|
||||
checkInTime: new Date('2026-07-11T08:55:00+08:00'),
|
||||
},
|
||||
]);
|
||||
|
||||
const result = await service.createLessonAttendanceFromDingTalk(4, '2026-07-11', 21);
|
||||
|
||||
expect(attendanceRepo.save).toHaveBeenCalledWith([
|
||||
expect.objectContaining({ studentId: 1, status: 'present', remark: null }),
|
||||
]);
|
||||
expect(sessionRepo.save).not.toHaveBeenCalled();
|
||||
expect(attendanceRepo.save).not.toHaveBeenCalled();
|
||||
expect(result.records).toHaveLength(1);
|
||||
expect(result.session.status).toBe('completed');
|
||||
});
|
||||
|
||||
it('refreshes an in_progress session from latest DingTalk data', async () => {
|
||||
@@ -616,13 +631,15 @@ describe('AttendanceService — attendance window boundaries', () => {
|
||||
process.env.TZ = 'UTC';
|
||||
jest.useFakeTimers().setSystemTime(new Date('2026-07-13T01:00:00.000Z'));
|
||||
try {
|
||||
const { service, scheduleRepo, sessionRepo, attendanceRepo } = createService();
|
||||
const { service, scheduleRepo, sessionRepo, attendanceRepo, dingRawRepo, classStudentRepo } = createService();
|
||||
scheduleRepo.findOne.mockResolvedValue({
|
||||
...endedSchedule, weekDay: 1, startTime: '08:30', endTime: '10:00',
|
||||
startDate: '2026-07-13', endDate: '2026-07-13',
|
||||
});
|
||||
sessionRepo.findOne.mockResolvedValue({ id: 90, status: 'completed' });
|
||||
attendanceRepo.find.mockResolvedValue([]);
|
||||
dingRawRepo.find.mockResolvedValue([]);
|
||||
classStudentRepo.find.mockResolvedValue([]);
|
||||
await expect(service.createLessonAttendanceFromDingTalk(4, '2026-07-13', 21))
|
||||
.resolves.toMatchObject({ records: [] });
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user