From 899dad2323278f074d14d4972b22a6fde80aa92c Mon Sep 17 00:00:00 2001 From: Joel Brock Date: Wed, 13 May 2026 13:19:11 -0700 Subject: [PATCH] README: refresh for current architecture, drop stale Framework Stage / mock-field notes Trimmed the in-the-weeds tech tour (file tree, lib names, custom-field inventory tutorial) and replaced with a brief functional overview, the visual/UX highlights that survived the polish passes, the accessibility features, an env-var table, and the minimum to run/deploy. Outdated material removed: org-side Framework Stage as authority, 'Org Engagement Submission' activity type, stage_at_submission write, placeholder custom_ civiField refs, Inter+SourceSerif typography, leaf+stone palette. Documents stage-from-activity, /report, locked future stages, draft auto-save, success destination, HTTP-Basic-Auth proxy env vars, HEALTH_TOKEN, and PREVIEW_ADMIN_TOKEN. --- README.md | 266 ++++++++++++++++++++++++------------------------------ 1 file changed, 117 insertions(+), 149 deletions(-) diff --git a/README.md b/README.md index 230a731..5ec3c24 100644 --- a/README.md +++ b/README.md @@ -1,168 +1,136 @@ -# Co-op Check-in (WebForm-mw) +# Co-op Check-in -Standalone Next.js middleware that lets external co-op contacts update their -organization's tracking data on CiviCRM, sidestepping the limitations of -Webform CiviCRM's admin UI. +A tokenized, mobile-friendly web form that lets external co-op contacts +update their organization's tracking data on CiviCRM, plus a read-only +activity report showing every value the co-op has shared over time. Built +to wrap CiviCRM's existing custom-field schema without modifying it. -This app is the **v2** delivery path described in -`../docs/superpowers/specs/2026-05-08-civi-webform-design.md`. +## How it works -## What it does +Each co-op has a designated **Primary Contact** (the individual) linked +to the **Organization** record in CiviCRM. Staff send that contact a +personalized link generated against the contact's CiviCRM checksum: -A form-filler clicks a tokenized link in an email: +- `https://check-in.fci.coop/?cid=&cs=` — the form +- `https://check-in.fci.coop/report?cid=&cs=` — the report -``` -https://check-in.fci.coop/?cid=&cs= -``` +When the link is opened, the app verifies the checksum against CiviCRM, +resolves the organization through the Primary Contact relationship, and +loads the right view. -The app: +**Form.** Sections are organized by the co-op development stages +(Inquiry → Convene & Prepare → Grow & Plan → Connect & Gather → Excite & +Build → Fulfill & Stabilize). The org's current stage controls which +sections are editable; past and current stages are open for editing, +future stages render as previews with their fields locked so the co-op +can see the framework ahead. Fields prefill with each value's most +recent non-empty entry from past check-ins. On submit, a new +"Check-in (organizing)" activity is created; nothing is overwritten. -1. Verifies the checksum against CiviCRM. -2. Resolves the org from the contact's **Primary Contact** relationship (Individual → Organization). Configurable via `FORM_CONTACT_RELATIONSHIP` in `config/form.ts`. -3. Reads the org's **Framework Stage** (`Food_Co_op_Organizing.Stage`) — text-keyed values like `Inquiry`, `Organizing`, `Feasibility`, `Business feasibility`, `Store Implementation`, `Stabilize newly opened co-op`. -4. Walks the org's past `Org Engagement Submission` activities DESC and assembles per-field-most-recent prefill values (the exact behaviour the WCM admin UI cannot express). -5. Renders the form with stage-conditional sections — Stage 0 (Inquiry / core check-in fields, ~32 of them) is always visible; Stages 1–5 each appear when `current_stage` is in their visibility set. -6. On submit, creates a **new immutable** `Org Engagement Submission` activity with the visible-field values plus a `stage_at_submission` audit field. +**Stage authority.** The current stage is derived from the most recent +"Check-in (organizing)" activity whose Stage field is set. Staff own +stage transitions by manually setting Stage on a check-in activity they +create; the form itself never writes Stage, so org self-submissions +can't override a staff transition. Orgs with no stage-bearing activity +default to "Inquiry." -## Tech stack +**Report.** A read-only view of every field that has ever held a value, +grouped by stage. Each row shows the most recent value prominently; an +"N earlier entries" disclosure expands a chronological list of prior +values with their dates. Empty stages and untouched fields are hidden so +the page stays calm. -- Next.js 16 (App Router) + React 19 + TypeScript -- Tailwind CSS 4 (CSS-based `@theme` config) -- `react-hook-form` for state + validation -- `axios` (only used in earlier scaffolding; this version uses native `fetch`) +## Visual & UX details -## Project structure - -``` -app/ - layout.tsx Root layout, fonts (Inter + Source Serif 4) - page.tsx Public form entry; reads cid+cs from URL - globals.css Tailwind 4 + theme tokens (leaf + stone palettes) - api/ - data/route.ts GET form data + per-field-most-recent prefill - submit/route.ts POST submission → creates new Civi activity - -components/ - EngagementForm.tsx Top-level orchestrator - StageSection.tsx Collapsible accordion card per stage section - SiteChrome.tsx Header + footer chrome (logo placeholder included) - fields/ - FieldRenderer.tsx One renderer for all 12 field types - -config/ - form.ts The 123-field form definition - -lib/ - civicrm.ts APIv4 client (Bearer-style auth) - conditional.ts Visibility rule engine - prefill.ts Per-field-most-recent walk - -types/ - form.ts FormConfig, FieldConfig, VisibilityRule, etc. -``` - -## Configuration - -The app falls back to **STUB MODE** with hardcoded mock data when any of -these environment variables are missing. The UI renders fully and the -form is interactive, but no CiviCRM calls are made. - -```bash -# .env.local -CIVI_BASE_URL=https://crm.fci.coop -CIVI_API_KEY= -CIVI_SITE_KEY= -``` - -The exact auth header strategy depends on which CiviCRM auth extension is -active. The default in `lib/civicrm.ts` uses the AuthX-style Bearer header -for the API key plus the legacy `X-Civi-Key` for the site key. If your -instance uses a different scheme (e.g. classic APIv3 `api_key`+`key` query -params), adjust `lib/civicrm.ts` accordingly. - -### Custom-field references (CiviCRM APIv4) - -Form fields write to CiviCRM custom fields via the `civiField` property. -The convention is APIv4's `.` format. For now, -`config/form.ts` uses placeholder `custom_` references -for all but the Framework Stage. **Before going live, replace each -`civiField` value with the actual APIv4 reference** for that custom -field on the `Org Engagement Submission` activity type. - -To inventory the actual field names, query APIv4 from the Civi UI: - -``` -/civicrm/api4#/explorer - → CustomField.get - select: ["name", "label", "custom_group_id:name"] - where: [["custom_group_id:name", "IN", ["Stage_0_Inquiry", "Stage_1_*", ...]]] -``` - -## Running locally - -```bash -npm install -npm run dev -# open http://localhost:3000/?cid=anything&cs=anything (any non-empty cs while in stub mode) -``` - -## Building - -```bash -npm run build -``` - -## Deploying - -The simplest deployment is Vercel. The app is a standard App Router project: - -```bash -vercel -``` - -Set the three CiviCRM env vars in the Vercel project settings; the app -auto-leaves stub mode the moment all three are present. - -For self-hosting, any Node 20+ environment supporting Next.js 16 will work: - -```bash -npm run build && npm start -``` +- **Field Almanac** aesthetic: warm cream paper, deep botanical green + ink, a sparing terracotta accent. Fraunces (display) and DM Sans + (body) at variable weights. Subtle paper-grain texture via layered + CSS gradients. +- **Journey rail** runs down the left side on desktop, with a marker + per stage and a "Now" pill at the current stage. Past stages are + filled with a checkmark, future stages are dashed and locked. A small + inter-card stem stands in for the rail on mobile. +- **Draft auto-save** to `localStorage` (30-day TTL) so partial answers + survive page refreshes; a "Draft restored" banner appears on return. +- **Successful submit** lands on a destination screen instead of a + reloaded form, preventing accidental duplicate submits from + back-button or autofill replay. +- **Currency live preview**, date min/max bounds (1900–2100), and + thousands-separator formatting on numeric fields. +- **Hand-drawn stage icons** for each of the six stages; FCI brand logo + in the header. ## Accessibility -- All inputs have proper `