Worker Portal Mockups

On this page

Overview

The worker portal (services/canopy-web) is a server-rendered Askama + htmx BFF for caseworker workflows. Eight distinct page surfaces plus the Keycloak-mediated login: dashboard, case search, case detail (program- dependent tabset), application process, renewal queue, applications list, notices list, appeals list. Each section below captures the page’s layout regions, Orchard token usage, primary htmx interactions, and the caseworker actions exposed.

This is reference documentation for what’s in production today (services/canopy-web/templates/); proposed redesigns belong in a separate plan.

Orchard color tokens

Source of truth: rulesets/georgia/notices/components/orchard.typ:1-32.

Token Value Where it appears

primary

#1e5146 (deep evergreen)

App header, primary buttons, active tab underline, stat-card emphasis

accent

#ecbf44 (warm gold)

Secondary highlights (e.g. u-stat-accent on the renewals-due card), focus rings

body-color

#2d2d2d (near-black)

All body text

muted

#6b7280 (slate grey)

Label text, helper copy, inactive tabs

border-color

#d1d5db (light grey)

Card borders, table dividers, form-field borders

Tokens surface in CSS as --color-orchard-primary, --color-orchard-accent, etc. (see services/canopy-web/static/css/).

Page chrome (shared across all eight)

Diagram

The header uses primary as its background with light-on-dark text; the main content area uses a near-white surface with border-color dividers. No persistent left sidebar — navigation is via the header links into the five top-level pages (Dashboard / Cases / Applications / Renewals / Appeals + Notices).

Login

Keycloak-mediated OIDC redirect; canopy-web is the relying party. Caseworkers click "Sign in" → bounce to Keycloak realm canopy → return with a session cookie minted by tower-sessions (PG-backed per ADR-009).

Diagram

Orchard tokens: primary button background; accent for the "trouble signing in?" link.

Caseworker actions: Sign in only. There is no in-app registration — worker accounts are provisioned by Keycloak realm admins.

Dashboard

Entry surface after login. Two horizontal grids: caseload-summary stat cards on top, program-breakdown stat cards below, then a recent-activity table.

Diagram

Orchard tokens:

  • Stat cards: border-color border, body-color text, muted label

  • u-stat-accent (renewals-due card): accent foreground

  • u-stat-error (interim-contacts-due card): semantic error red (not an Orchard token; uses Tailwind’s --color-error)

  • Activity-table header: primary bottom border

htmx interactions: none on the dashboard itself — it’s a static page re-rendered on each navigation. Cards link via standard <a href> into case search or per-program lists.

Source: services/canopy-web/templates/dashboard.html

Filter-and-list page for active households. The form posts via hx-get="/cases/search" and re-renders only the result table.

Diagram

Orchard tokens:

  • Filter form: border-color field borders, primary "Search" button

  • Result rows: border-color separators; hover state lightens to border-color × 50%

htmx interactions: hx-get="/cases/search" hx-target="#results" hx-swap="innerHTML". Result rows include <a href="/cases/{household_id}"> to navigate into case detail.

Source: services/canopy-web/templates/cases/search.html + _results.html

Case Detail

The largest surface. Header band with household summary; then a tablist that shows a program-dependent subset of 16 tab partials in services/canopy-web/templates/cases/tab_*.html. Tab content swaps via htmx (hx-get="/cases/{id}/tab/{tab_id}").

Diagram

Tabs rendered, by program (the source-of-truth cases.rs::render_program_tab):

Program Tab IDs

SNAP

household, income, determination, notices, appeals, activity, abawd, guidance

TANF

SNAP set + work-req, time-limits (replaces determination with determination_tanf)

Medicaid

SNAP set + categories (replaces determination with determination_medicaid)

CAPS

SNAP set + authorization (replaces determination with determination_caps)

WIC

SNAP set + nutrition (replaces determination with determination_wic)

Synthetic IEVS-only rows on the income tab render an em-dash (—) in the Actions column (income_id is None so there’s no PUT/DELETE target).

Orchard tokens:

  • Tab underline (active): primary

  • Tab text (inactive): muted

  • <details> action toggles (Edit / Remove / Add): border-color border, primary confirm button, muted "Cancel"

  • Status badges in the header: accent for "Action Required", body-color for neutral

  • Tab partials' card borders: border-color

htmx interactions:

  • Tab clicks: hx-get="/cases/{id}/tab/{tab_id}" hx-target="#tabpanel" hx-swap="innerHTML"

  • Income actions (#409): POST /actions/income/{add,edit,remove} → 303 redirect to /cases/{household_id}

  • SNAP caseworker actions (#411 + #392): POST /actions/snap/{appeal,interim,change,abawd,discrepancy} and program-specific equivalents

  • The htmx-settling class shown during swap-in surfaces the brief loading state with border-color border-pulse

Source: services/canopy-web/templates/cases/detail.html + 16 tab_*.html partials

Application Process

Application-detail / decision surface used by eligibility specialists to finalize a case. Shows application summary, rules-engine result, verification status, and decision actions (Approve / Deny).

Diagram

Orchard tokens:

  • Cards: border-color border, body-color text, muted label

  • "Approve" button: primary background

  • "Deny" button: outline with body-color border + text (no destructive red — denial requires a reason, not visual alarm)

  • Result-status badges: primary for approved, accent for pending, semantic error for denied

htmx interactions: the form submits as a classic POST (full-page reload) so the post-decision redirect lands on a clean case detail. No htmx tab swap on this page.

Source: services/canopy-web/templates/applications/process.html

Renewal Queue

Three-button window filter (30 / 60 / 90 days) over the renewals list. Each filter is an htmx swap targeting #renewal-list.

Diagram

Orchard tokens:

  • Active filter button: primary background, white text

  • Inactive filter buttons: body-color text on white, border-color border

  • List rows: border-color separators; row text in body-color

  • "Days remaining" column tints accent once ≤ 14 days, semantic error once overdue

htmx interactions:

  • hx-get="/renewals?days={30|60|90}" hx-target="#renewal-list" hx-swap="innerHTML"

  • Each row’s "Start renewal" action is a plain link into the case-detail household tab.

Source: services/canopy-web/templates/renewals/queue.html

Applications List

Filterable list of all applications regardless of program. Columns: application ID short, applicant, program list, status, received_at.

Diagram

Orchard tokens: same shape as Renewal Queue — filter pills use primary for active, muted for inactive; status cells use primary / accent / semantic-error tinting.

htmx interactions: hx-get="/applications?…" with table swap into #app-list. Row click navigates to /applications/{id}/process.

Source: services/canopy-web/templates/applications/list.html

Notices List

Per-household notice history. Columns: notice type, generated_at, delivery channel, status. Worker can re-generate or download a PDF from each row.

Diagram

Orchard tokens: table-row scheme matches Applications List; download/regenerate buttons in primary outline.

htmx interactions: "Re-generate" is htmx hx-post that swaps the row in place with the updated status = 'queued' state; download is a plain anchor that triggers a Typst-rendered PDF stream from canopy-notices.

Source: services/canopy-web/templates/notices/list.html

Appeals List

Open fair-hearings + IPV cases. Columns: case ID, household, type, filed_at, hearing_date, status.

Diagram

Orchard tokens:

  • "Continued benefits" status badge: accent (financial urgency signal per the continued-benefits 30-day clock)

  • "Past 90-day clock" status: semantic error

  • Action-required rows tint the row background to border-color × 25%

htmx interactions: hx-get="/appeals?status=…" hx-target="#appeals-list". Row click navigates into the case-detail Appeals tab.

Source: services/canopy-web/templates/appeals/list.html

UAT review notes

When using these mockups for UAT:

  • Diagrams capture layout and interaction, not pixel fidelity. Caseworkers commenting on the diagrams should focus on whether the information regions are in the right order and whether the action set matches their workflow — not on font choices or spacing.

  • The 16 tab partials make Case Detail the heaviest review surface. If reviewers prefer to walk through one program at a time, the program → tab subset table above is the right entry point.

  • Color tokens are deliberately understated. The accent gold is for urgency signaling (renewals, continued benefits, action-required) — if reviewers see a surface that should signal urgency but doesn’t, flag the token assignment.

Future work

The Dioxus rewrite contemplated by ADR-008 is a constituent-facing surface (canopy-portal), not this worker portal. A separate worker-portal refresh would happen post-UAT, after caseworker feedback on these surfaces. This page is for documenting what exists today, not for proposing the refresh.

Edit this page · default