Document Expiry & Warnings ​
How the app warns engineers about expired/expiring documents that block jobs.
Core Concept ​
The backend is the source of truth for document criticality. Two fields on UserType (src/types/user.ts) drive all warning logic:
validUntil— earliest date when any approved application loses validity.MIN(application.validUntil)across all approved, active products. When this date is in the past, jobs are blocked.nullmeans no blocking.expiringDocuments— document titles expiring within ~4 weeks. Includes all expiring docs, not just job-blocking ones (e.g. Asbestos Awareness appears here but doesn't block jobs).
The app never independently calculates the 4-week expiry window — it trusts expiringDocuments for that.
Document Categories ​
| Category | Blocks jobs? | Scope |
|---|---|---|
| Company | Yes | All products (shared validUntil) |
| Product | Yes | Only linked products (via applicability in src/fixtures/documents.ts) |
| Personal | No | Never affects validUntil |
Warning Severity ​
| State | Condition | Severity |
|---|---|---|
| Blocked | validUntil < today | error (red) |
| Expiring | validUntil set AND expiringDocuments not empty | warning (yellow) |
| OK | Neither | none |
The date comparison is date-level (startOfDay vs startOfToday), not time-level. Using isPast() would block engineers partway through their last valid day.
Entry Points ​
All warning logic lives in src/utils/documents/documentWarnings.ts:
useAreJobsBlocked()— lightweight hook, returnsboolean. Used by most UI indicators.getCriticalDocumentInfo(user)— heavy function, returns detailed warning with affected products and human-readable message. Used by the Home screen banner.calculateCriticalDocumentWarning()— per-tab warning for the Documents screen.
Single-document expiry checks use src/utils/documents/checkDocumentDateForExpiry.ts.
UI Indicators ​
- Home tab (
src/components/navigation/tab/JobsTabIcon.tsx) — job offers badge takes priority; alert icon shown only when no offers AND jobs blocked - Profile avatar (
src/components/profile/ProfileAvatar.tsx) — alert icon when jobs blocked - Profile "Documents" row (
src/components/profile/profile.tsx) — severity-aware description text - Home screen banner (
src/components/home/CriticalDocumentAlert.tsx) — detailed message with affected products, links to Documents screen - Documents screen (
src/app/(app)/profile/my-documents.tsx) — per-tab warning banners viasrc/components/documents/CriticalDocumentWarning.tsx
Non-main installers see a different message on the Company tab directing them to contact their main installer (see calculateNonMainInstallerWarning()).
Deep Links ​
Route: /profile/my-documentsOptional params: tab (Company | Personal | Product), status (expired | expiring | todo | submitted) — both case-sensitive.
Push notification data.screen format:
profile/my-documents?tab=Company&status=expiredSee src/utils/navigation/allowlist.js for the allowlist and src/utils/navigation/optionalParams.js for param definitions. The general deep link infrastructure is documented in Deep Links.
Fallbacks: unknown tab → first available tab; empty status → first status with count > 0. Works from all app states (foreground, background, cold start).
Testing ​
xcrun simctl openurl booted "boxtinstaller://profile/my-documents?tab=Company&status=expired"