Skip to main content

16. Calendar, hour entry, and contractor assignments

Date: 2026-06-06

Status

Accepted

Context

Outsourced contractors log daily hours on a calendar; the entries feed the corrected pay engine and the weekly facility invoice. Hours must show weekly progress against the contracted limit (colour-coded), accept but flag overage, flag implausible single-day totals, and become read-only once a period is locked. Logging requires an active contractor→facility assignment (rate + weekly hours), which had no creation path yet — so this step also adds the admin "assign contractor" flow (versioned assignments per §5.6).

Decision

  • Pure calendar logic (src/lib/calendar, unit-tested): a day cell is coloured by its week's progress (green < 85%, amber 85–<100%, red ≥ 100%/over), plus Monday-week grouping and a single-day anomaly check. Reuses src/lib/dates UTC math.
  • Assignments: createAssignment (admin, service role) writes a versioned contractor_facility_assignments row — money as integer minor units (hourly_rate_cents, bimonthly_rate_centavos) — and end-dates the prior active assignment for that contractor+facility so history is preserved. Managed from a new admin contractor-detail page (/admin/contractors/[id]).
  • Hour entry: upsertTimeEntry (contractor, outsourced only — in-house hours come from Hubstaff) resolves the active assignment, recomputes the Monday–Sunday week total with the new value substituted in to detect overage (status → pending_approval), flags a single-day anomaly (threshold from settings, default 16h), and refuses to edit a locked entry. Writes go through the service-role action behind an ownership check (the magic-link overage approval itself is the next step).
  • UI: /contractor/calendar renders a month grid (CalendarBoard, client) with per-week progress, colour cells, anomaly/pending/locked badges, prev/next month navigation, and a single-column day modal with an inline overage warning before save. The board reuses the same pure helpers, so the colour a contractor sees matches the rule the server enforces.

Consequences

  • The pay engine now has a real data source; logged hours flow straight into the period-fraction calculation built earlier.
  • Overage is detected and marked here but the facility magic-link approval (creating approval_requests, emailing admin contacts) is deferred to the overage-approval step; until then overage entries simply sit in pending_approval.
  • In-house admins see the calendar read-only; their hours arrive once the Hubstaff integration lands (still blocked on credentials).
  • Live entry can be exercised once a contractor is active with an assignment; build/typecheck/lint and the pure week/anomaly logic are verified.