10 — Interaction Specification (Leave Module)
- 1. Navigation & transitions
- 2. List screens (My Requests, Approvals, Substitutions)
- 3. Request form
- 4. Decision interaction (Approvals Queue)
- 5. Assign substitution sheet
- 6. Calendar
- 7. Feedback & haptics
- 8. Offline matrix
- 9. Accessibility interactions
Interaction patterns per screen: gestures, transitions, feedback, offline behavior, optimistic updates, and edge handling. Motion tokens (
m-fast,m-base), haptics, and offline policy per 00-shared/08; QA baseline in 00-shared/10.
1. Navigation & transitions
| Route | Transition |
|---|---|
| Home → Leave workspace | platform default push (per 00-shared/08) |
| My Requests → New Request | push (m-base slide) |
| List → Detail | bottom sheet (mobile) / right pane (tablet) |
| Approvals → Assign Substitution | bottom sheet with form |
| Balance ↔ Requests | horizontal tabs or back-stack; no bottom-nav duplication |
2. List screens (My Requests, Approvals, Substitutions)
| Gesture/event | Behavior |
|---|---|
| Pull-to-refresh | RefreshIndicator; refetch active filter; m-fast fade-in on updated rows |
| Tap row | open detail sheet (My Requests) / nothing extra (Approvals rows open action sheet) |
| Filter chip tap | refetch ?status= server-side (leave.controller.ts:40); keep scroll position |
| Swipe | none (no destructive actions exist) |
| Offline open | cached snapshot + AppOfflineBanner; CTA disabled for mutating actions |
| Loading | AppSkeleton rows (never spinners for whole list) |
| Empty | AppEmptyState with contextual CTA ("Request leave", "No substitutions") |
3. Request form
| Interaction | Spec |
|---|---|
| Type picker | dropdown; types cached in LeaveTypesCubit after first GET /leave/types |
| Date pickers | DatePicker; select start → auto-suggest end ≥ start (clamp); days preview updates live |
| Live days preview | recompute on each date change (inclusive count, leave.service.ts:310-312) with weekend hint |
| Submit | AppButton loading state; no optimistic creation (id is server-assigned); on 200 → toast "Submitted — pending approval", pop to list where new row appears at top (createdAt desc, leave.service.ts:168) |
| 404 type | inline + auto-refetch types |
| Double-tap submit | guarded (in-flight flag) |
4. Decision interaction (Approvals Queue)
| Interaction | Spec |
|---|---|
| Approve | 1 tap → PATCH with {action:"approve"} → row shows neutral "processing" → on 200 flips to approved & leaves pending list; on 409 reverts + toast |
| Reject | tap → sheet: optional note + confirm → PATCH {action:"reject", note} |
| Optimistic policy | no optimistic status change — balance is affected (leave.service.ts:183-190); wait for 200, then animate chip swap (m-fast) |
| 409 insufficient balance | row gets inline error chip "Insufficient balance"; approve disabled for that row until refetch; reject remains available |
| 409 already decided | toast + row removed on refetch |
| 409 self-decision | row never rendered for self (client filter) |
5. Assign substitution sheet
| Interaction | Spec |
|---|---|
| Teacher picker | search-as-you-type against teachers module (forward-looking); shows clash-free candidates only where known |
| Date picker | clamped hint to approved leave range (client only — gap) |
| Time pickers | start→end chained; end defaults start+1 h |
| Submit | on 409 clash → inline error under time fields; on 404 → toast; on 200 → toast + switch to Substitutions list |
6. Calendar
| Interaction | Spec |
|---|---|
| Month pager | swipe (mobile) + chevrons; fetch from/to per month (leave.controller.ts:85-91) |
| Day tap | bottom sheet listing approved requests that day |
| Today | outlined cell; selected month highlighted |
| Refresh | pull-to-refresh refetches visible month |
7. Feedback & haptics
- Success toasts:
AppSnackbar(short); decision approve = subtle haptic (HapticFeedback.selectionClick); errors =mediumImpact+ inline error (00-shared/08). - No confetti; no destructive-confirm for approve (only reject).
8. Offline matrix
| Screen | Read | Write |
|---|---|---|
| Balance | cached snapshot (≤ 1 session) | n/a |
| My Requests | cached list; status may be stale → banner | block new request (needs live type/balance truth) |
| Approvals | cached list | block decisions (balance check server-side, leave.service.ts:184-190) |
| Substitutions | cached list | block |
| Calendar | cached month | n/a |
9. Accessibility interactions
- Focus order: filters → list → FAB/CTA; sheets trap focus; dismiss via swipe-down + Esc (desktop) + scrim tap.
- All mutations require a visible confirmation state (no silent writes).
- Reduced motion:
m-fastbecomes instant (per 00-shared/09).