Skip to content

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. null means 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 ​

CategoryBlocks jobs?Scope
CompanyYesAll products (shared validUntil)
ProductYesOnly linked products (via applicability in src/fixtures/documents.ts)
PersonalNoNever affects validUntil

Warning Severity ​

StateConditionSeverity
BlockedvalidUntil < todayerror (red)
ExpiringvalidUntil set AND expiringDocuments not emptywarning (yellow)
OKNeithernone

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, returns boolean. 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 via src/components/documents/CriticalDocumentWarning.tsx

Non-main installers see a different message on the Company tab directing them to contact their main installer (see calculateNonMainInstallerWarning()).

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=expired

See 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 ​

bash
xcrun simctl openurl booted "boxtinstaller://profile/my-documents?tab=Company&status=expired"