Story QR-009: Daily Attendance Dashboard — Summary + Employee List + Detection¶
@assignee: @m-kouassi
Module: qr-attendance Slice: QR-T5-001 + QR-T3-001 (late detection) + QR-T3-003 (no-show detection) Brand context: [BOTH] Papillon HR Suite + ALTARYS ENTERPRISE HR Suite Priority: 9 Depends on: QR-007 (clock-out + validation), QR-005 (holidays for no-show) Can develop concurrently with: QR-011 (regularization) Merge order: Before QR-010 Estimated complexity: L PRD User Stories: US-QR-010 (AC-001, AC-002, AC-005)
Objective¶
Build the daily attendance dashboard for HR and managers, combining a summary card row (present, late, absent, teletravail, on leave) with a filterable employee list. Simultaneously implement late arrival detection (with configurable tolerance) and no-show detection (cross-referencing holidays, leave, and teletravail). This is the primary operational view for daily attendance management.
Backend Scope¶
Entities & Records¶
No new tables. Extends DailyAttendanceService and adds DashboardService.
Flyway Migrations¶
None.
Repository Layer¶
No new repositories. Extends existing queries on DailyAttendance and Clock.
Service Layer¶
LateDetectionService:
- Employee is "En retard" if first clock-in > scheduled start + applicable tolerance
- Tolerance lookup: department-level (DepartmentAttendanceConfig.lateToleranceMinutes) > company-level (AttendanceModuleConfig.defaultLateToleranceMinutes)
- Late duration = clock-in time - scheduled start (NOT tolerance-adjusted). Example: schedule 08:00, tolerance 5min, clock-in 08:07 -> flagged RETARD, late_minutes=7
- If within tolerance: NOT flagged, actual time still recorded
- Updates DailyAttendance.status to RETARD, sets late_minutes
NoShowDetectionService:
- Daily check: employees with no clock events on a working day
- Cross-reference:
- Holidays (HolidayService.isHoliday) -> FERIE
- Leave (LeaveApproved cache) -> EN_CONGE
- Approved teletravail -> TELETRAVAIL
- Pending teletravail -> ABSENT_PENDING
- Else -> ABSENT
LeaveEventListener:
- Listens to LeaveApproved, LeaveCancelled from ABSMGT module
- Updates local leave cache for cross-reference
DashboardService:
- getTodayDashboard(tenantId, filters) — returns summary counts + employee list
- Summary: total, present, late, absent, teletravail, enConge, ferie, incomplet, absentPending
- Employee list: name, department, site, clock-in time, clock-out time, worked hours, status, anomalies
- Filtering: departmentId, siteId, status (comma-separated), search by name
- HR sees all employees; Manager sees direct reports only (enforcement deferred to QR-010)
- Keyset pagination (ADR-12)
API Endpoints¶
| Method | Path | Request Body | Response | Auth |
|---|---|---|---|---|
| GET | /api/v1/qrcontr/attendance/today |
?departmentId=&siteId=&status=&search=&cursor=&size= |
ApiResponse<DashboardResponse> |
HR, Manager |
| GET | /api/v1/qrcontr/attendance/daily |
?date=&departmentId=&siteId=&status=&cursor=&size= |
ApiResponse<CursorPage<DailyAttendanceResponse>> |
HR, Manager |
DTOs¶
DashboardResponse:
- date (LocalDate)
- summary (DashboardSummary) — total, present, late, absent, teletravail, enConge, ferie, incomplet, absentPending
- employees (List
EmployeeDashboardRow:
- employeeId (UUID)
- name (String)
- department (String)
- site (String)
- firstClockIn (LocalTime)
- lastClockOut (LocalTime)
- workedMinutes (int)
- status (String)
- anomalies (List
Validation Rules¶
dateparameter defaults to today if not providedstatusfilter accepts comma-separated valuessizedefaults to 20, max 100
Multi-Tenant Considerations¶
All dashboard queries filter by tenant_id. Leave event cache is tenant-scoped. Holiday cross-reference uses tenant's country code.
Frontend Scope¶
Pages & Routes¶
/qrcontr/dashboard— Daily attendance dashboard (HR/Manager)
Components¶
DashboardPage (design spec wireframes 3.3, 3.4):
- Status cards + employee list layout
StatusCardRow:
- Horizontal scroll cards: Presents (green), En retard (amber), Absents (red), Teletravail (blue), En conge (purple)
- Each shows count
EmployeeAttendanceList:
- Searchable, filterable list of employees with status badges
FilterBar:
- Search input + department/site/status filter chips
EmployeeAttendanceRow:
- Name, department, clock-in time, status badge, anomaly badges (if any)
UI States¶
| State | Behavior | French Copy |
|---|---|---|
| Loading | 5 skeleton cards (shimmer, stagger 50ms) + 5 skeleton rows (stagger 30ms) | — |
| Empty | Illustration + guidance | "Aucun pointage aujourd'hui. Les pointages apparaitront ici des que vos employes scanneront le QR code." + CTA "Configurer un site" if no sites |
| Error | Inline error card + retry | "Impossible de charger les donnees. Verifiez votre connexion." |
| Offline | Amber banner, cached data, "Derniere mise a jour: HH:MM" | "Hors ligne — vos pointages seront synchronises" |
| Success | Cards animate in stagger, list items fade in | — |
Responsive Behavior¶
- 360px: Horizontal scroll status cards (2 visible), simplified employee cards (name, time, status badge)
- 768px: 3 visible cards, employee cards with more detail
- 1024px+: 5 cards in row, full data table with sortable columns (Enterprise)
Interactions¶
- Filter chips: tap to toggle (active: filled amber, inactive: outlined). Multi-select for status, single for department/site.
- Employee row tap: slide-right to employee day detail
- Pull-to-refresh (mobile): amber spinner, refreshes all data
- Dashboard auto-refreshes via polling (30s interval when online)
Acceptance Criteria¶
AC-001: Dashboard summary counts
Given I open the daily attendance dashboard
When data loads
Then I see summary counts: Presents, En retard, Absents, Teletravail, En conge
AC-002: Dashboard filtering
Given I open the dashboard
When I view the employee list
Then I can filter by department, site, or status
AC-003: HR sees all employees
Given I am HR
When I view the dashboard
Then I see all employees across the company
OHADA & Regulatory Rules¶
- Late detection with configurable tolerance supports CI custom (Art. 16.1: rules must be in reglement interieur consulted with delegues)
- No-show correctly cross-references holidays, leave, teletravail before marking ABSENT
Standards & Conventions¶
docs/standards/java-spring-guidelines.mddocs/standards/api-guidelines.md— cursor-based pagination (ADR-12)docs/standards/react-typescript-guidelines.mddocs/standards/offline-sync-guidelines.md
Testing Requirements¶
Unit Tests¶
LateDetectionService: within tolerance (not flagged), outside tolerance (flagged), department override vs company default, late_minutes calculationNoShowDetectionService: holiday -> FERIE, approved leave -> EN_CONGE, approved teletravail -> TELETRAVAIL, pending teletravail -> ABSENT_PENDING, no match -> ABSENTDashboardService: summary aggregation counts, filtering by status/department/site, search by name
Integration Tests¶
- GET
/attendance/today— returns dashboard with summary and employee list - GET
/attendance/today?status=RETARD— filtered results - GET
/attendance/today?departmentId=...— filtered by department - GET
/attendance/daily?date=2026-03-30— historical date query - Tenant isolation: dashboard from tenant A does not include tenant B data
- Cursor pagination: verify keyset pagination works correctly
What QA Will Validate¶
- View dashboard, verify summary counts match employee list
- Apply filters, verify results update correctly
- Verify late detection with tolerance (clock in at tolerance boundary)
- Verify no-show cross-references (employee on holiday not marked absent)
- Pull-to-refresh on mobile
- Offline mode shows cached data with timestamp
Out of Scope¶
- Manager team-only filtering (QR-010)
- Anomaly detection (shared device, auto-close) (QR-010)
- Employee detail page from dashboard (QR-016)
- Regularization actions from dashboard (QR-012)
Definition of Done¶
- [ ] All backend endpoints implemented and returning correct responses
- [ ] All frontend pages render correctly at 360px, 768px, 1024px
- [ ] All 5 UI states implemented (loading, empty, error, offline, success)
- [ ] French micro-copy matches design spec exactly
- [ ] All acceptance criteria pass manually
- [ ] Unit tests written and passing
- [ ] Integration tests written and passing
- [ ] Multi-tenant isolation verified
- [ ] Offline: cached dashboard data with "Derniere mise a jour" timestamp
- [ ] No TypeScript
any, no Java raw types - [ ] API responses follow standard envelope format
- [ ] Audit trail entries for data mutations
- [ ]
./gradlew testis green - [ ]
./gradlew spotlessApplypasses