Aller au contenu

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

  • date parameter defaults to today if not provided
  • status filter accepts comma-separated values
  • size defaults 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.md
  • docs/standards/api-guidelines.md — cursor-based pagination (ADR-12)
  • docs/standards/react-typescript-guidelines.md
  • docs/standards/offline-sync-guidelines.md

Testing Requirements

Unit Tests

  • LateDetectionService: within tolerance (not flagged), outside tolerance (flagged), department override vs company default, late_minutes calculation
  • NoShowDetectionService: holiday -> FERIE, approved leave -> EN_CONGE, approved teletravail -> TELETRAVAIL, pending teletravail -> ABSENT_PENDING, no match -> ABSENT
  • DashboardService: 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 test is green
  • [ ] ./gradlew spotlessApply passes