Skip to main content

Facility Pipeline — UX spec

Page structure, component reuse, the complete modal inventory, and the per-page state checklist. Grounds the annotated mockups in mockups/facility-pipeline-mockups.html. Follows docs/ux-ui-guidelines.md.

1. Surface & navigation

Extend /admin/crm (the existing CRM Kanban from ADR-0033) — do not add a competing "Facility Pipeline" nav item. Rename the existing CRM nav entry to "Pipeline" and host the experience as tabs:

  1. Today's Follow-ups — default landing when tasks are due (the rep's worklist). Smart default: open here if count(due tasks for me) > 0, else Signal Inbox.
  2. Signal Inbox — new + unmatched job_signals.
  3. Pipeline Board — the existing crm_leads Kanban (drag-and-drop already built).
  4. Prospect Discovery — high-score facilities with no open posting (CMS-only signals).
  5. Sequences — cadence templates + enrollments.

Facility 360 is the existing facility detail route (/admin/facilities/[id]), linked from cards — not a tab. Wrap everything in AdminShell (white sidebar — the standalone mockups' dark shell is not production). Tab labels use SVG icons from nav-icons, not emoji.

2. Component reuse (all verified in src/components/ui/)

NeedUseNot
Signal Inbox / Prospect listsTable (mobile card-stack via data-label)raw <table>
Role / urgency / tier / status chipsBadge (tones: danger/warning/neutral/success/info, dot variant)bespoke .pill
Feedbacknotify() + Toaster (aria-live)a custom toast div
Destructive confirm (Dismiss, Mark Lost, deactivate sequence)confirmDanger() + DangerConfirmHosta bare toast
Empty statesEmptyState (icon + title + description + action)ghost text
ModalsDialogwindow.prompt
Phone fieldsPhoneInputa text input
TabsTabscustom tab CSS
LoadingSpinner + route loading.tsx skeletonsnothing

Color/semantics fix: Tier-1 = brand indigo (high opportunity is positive), Tier-2 = amber, Tier-3 = neutral. Urgency chips follow the same discipline: urgency 3–4 = brand/info, urgency 1–2 = amber/warning, urgency 0 = neutral — never red (red is danger only, same as Tier-1); always keep the ⚡N text so it's never color-only. Never convey status by color alone — always pair with text/dot. Overdue tasks use a red left-border accent, not a full red row.

3. Trust signals / provenance (required by the guidelines)

  • Score chip shows "as of {date}" (score_updated_at); score recomputes on ingest + quarterly.
  • CMS-derived rows in the "why this score" panel show "CMS data as of {quarter}".
  • F-tag row tooltip: "Source: CMS CASPER survey results" (so it's not mistaken for a posting).
  • Signal cards show posting age + last-seen; the unmatched badge tooltip explains the failure reason (below_threshold / ambiguous) before the match modal opens.
  • All four KPI cards are deep-links to the filtered view they describe.

4. Complete modal inventory

#ModalPurposeKey fieldsPrimary / secondaryNotes
M-01Raise Leadsignal → crm_leadfacility (read-only), owner, stage, est. value, enroll-in-sequence (progressive)Raise lead / Canceldisable in-flight; active-client → upsell variant
M-02Match Signal to Facilityresolve an unmatched signalsignal summary (read-only), facility typeahead, top-3 auto-suggestions w/ confidence, "add as alias"Confirm match / Skipshows provenance + why match failed
M-03Log Activity / Callrecord a crm_activity + advance steptype (prefilled from task), contact, datetime, notes, outcome, advance-stepSave / Cancelstamps last_contacted_date + next_follow_up_date
M-04Enroll in Sequenceenroll facility/leadsequence, start date, ownerEnroll / Cancelshows step count + est. completion; blocks if contact-enrichment gate unmet; warns if already enrolled
M-05Review & Send Email Draftapprove AI draftto (read-only), subject (editable), body (editable), provenance lineSend (confirm consequence) / Save draft / Cancelnever auto-send; "Drafted by AI from {template} · {time}"
M-06Add / Edit Contactfacility_contactname*, title, role* (widened vocab), email, PhoneInput, LinkedIn, is-primarySave / Cancelrequired: name + role; inline email/URL validation
M-07Mark Wonstatus=won + convertconfirmed est. value, conversion date, link facility, intake requiredMark won / Cancelconsequence text; Undo (5s); reuse src/lib/crm/pipeline.ts
M-08Mark Loststatus=lost + reasonreason (select), notes (required if "other"), reheat dateMark lost / CancelconfirmDanger consequence; reversible
M-09Edit Sequence Stepedit a follow_up_stepday offset, channel, persona, template key, instructionsSave / Cancelwarns "N enrollments affected"; future-only vs all
M-10Dismiss Signalstatus=dismissedsignal summary, reason (incl. "already a client"), notesDismiss / KeepconfirmDanger consequence; recoverable via filter
M-11Snooze Taskstatus=snoozed + snoozed_untilsnooze-until (quick chips +1d/+3d/+1w/+2w), reasonSnooze / Cancelshows re-appear date; default +3d
M-12Create / Edit SequenceCRUD follow_up_sequencename, description, enrollment_trigger, active, step listSave / Cancelprogressive step editor; warn before deactivating w/ active enrollments
M-13Add Lead (manual)quick crm_lead w/o signalfacility typeahead (real), stage (prefilled), owner, est. valueAdd / Cancelreplaces the mockup's window.prompt

5. Per-page state checklist

Every interactive component must implement: default · hover · focus · active · disabled · loading · error · empty (the bolded three are what the draft missed everywhere). prefers-reduced-motion must be honored on the Kanban drag and tab fade.

  • Signal Inbox: loading skeleton (on load + after "Run ingestion"); error (ingestion failure); empty ("No new signals — last ran {time}. Run ingestion." with CTA); filtered-empty; row focus; action buttons disabled in-flight (prevent duplicate raise/dismiss); unmatched provenance tooltip; mobile card-stack via Table data-label.
  • Pipeline Board: loading skeleton cards; error (CRM query); empty board + empty column via EmptyState w/ CTA; card move optimistic with rollback + toast on failure; drag move announced to screen readers (aria-live); the ⋯ menu needs role=menu/menuitem + Esc; mobile horizontal scroll (one column visible) rather than a flat vertical dump; WIP soft-warning past a column cap.
  • Today's Follow-ups: loading; error; empty (positive: "Nothing due — check back tomorrow"); task-row hover; "Mark done" spinner (not just opacity); Snooze opens M-11 (not instant); facility link on each row.
  • Facility 360: skeletons for score bars / timeline / contacts; error (not found / score not computed); score "as of" + CMS vintage; empty timeline / contacts / signals; "no active sequence" → Enroll CTA; paused-sequence indicator.
  • Prospect Discovery: loading; empty ("No Tier-1 prospects without a lead — nice."); each row → Raise Lead / Enroll.
  • Sequences: loading; error; empty ("Create your first sequence" CTA); empty step list; per-enrollment count; deactivate confirm (confirmDanger); paused/inactive indicator.

6. Daily-flow notes

  • Land on Today's Follow-ups when tasks are due. On an empty pipeline, show a one-step setup card: "Run signal ingestion to find facilities hiring for MDS roles → raise leads from the Signal Inbox," with "Run ingestion" as the prominent primary CTA.
  • KPI cards (new signals, unmatched, Tier-1, due) are clickable into their filtered views.