Build standalone CiviCRM check-in middleware
This commit is contained in:
176
README.md
176
README.md
@@ -1,36 +1,168 @@
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
# Co-op Check-in (WebForm-mw)
|
||||
|
||||
## Getting Started
|
||||
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.
|
||||
|
||||
First, run the development server:
|
||||
This app is the **v2** delivery path described in
|
||||
`../docs/superpowers/specs/2026-05-08-civi-webform-design.md`.
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
## What it does
|
||||
|
||||
A form-filler clicks a tokenized link in an email:
|
||||
|
||||
```
|
||||
https://check-in.fci.coop/?cid=<contactId>&cs=<checksum>
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
The app:
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
1. Verifies the checksum against CiviCRM.
|
||||
2. Resolves the org from the contact's **Primary Form Contact** relationship.
|
||||
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.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
## Tech stack
|
||||
|
||||
## Learn More
|
||||
- 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`)
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
## Project structure
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
```
|
||||
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
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
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
|
||||
|
||||
## Deploy on Vercel
|
||||
config/
|
||||
form.ts The 123-field form definition
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
lib/
|
||||
civicrm.ts APIv4 client (Bearer-style auth)
|
||||
conditional.ts Visibility rule engine
|
||||
prefill.ts Per-field-most-recent walk
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
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=<a Civi user's API key>
|
||||
CIVI_SITE_KEY=<the site_key from civicrm.settings.php>
|
||||
```
|
||||
|
||||
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 `<group_name>.<field_name>` format. For now,
|
||||
`config/form.ts` uses placeholder `custom_<form_field_name>` 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
|
||||
```
|
||||
|
||||
## Accessibility
|
||||
|
||||
- All inputs have proper `<label htmlFor>` associations.
|
||||
- Help text and error messages are linked via `aria-describedby`.
|
||||
- Errors set `aria-invalid` and use `role="alert"` for assistive tech.
|
||||
- Required fields use `aria-required` plus a visible `*` (with
|
||||
`aria-label="required"`).
|
||||
- Sections use semantic `<section>` + `<h2>` + `aria-controls` accordion pattern.
|
||||
- Keyboard-only flow works end-to-end.
|
||||
- Color contrast meets WCAG AA against the leaf + stone palette (verified
|
||||
visually; an audit pass is recommended before launch).
|
||||
- A skip-to-content link is present for keyboard users.
|
||||
|
||||
## Conditional logic
|
||||
|
||||
Rules live alongside fields and sections in `config/form.ts`. The engine
|
||||
(`lib/conditional.ts`) supports:
|
||||
|
||||
- `equals`, `notEquals`, `in`, `notIn`, `isEmpty`, `isNotEmpty`
|
||||
- Composition via `{all: [...]}` (AND) and `{any: [...]}` (OR)
|
||||
|
||||
Section visibility re-evaluates live on every form change. Hidden fields'
|
||||
values are stripped from the submitted payload, so users never accidentally
|
||||
write data they couldn't see.
|
||||
|
||||
## What's not in scope for this build
|
||||
|
||||
- **Browser-based form builder.** Forms are configured by editing
|
||||
`config/form.ts`. An admin UI for non-developers to build forms is a
|
||||
follow-on project.
|
||||
- **File upload to a storage backend.** File fields render correctly but
|
||||
the Civi-side upload pipeline is a stub. Wire up to S3/R2 + CiviCRM's
|
||||
`File` entity when ready.
|
||||
- **CSRF protection on the submit endpoint.** Checksum gating is the
|
||||
current trust mechanism. Add CSRF tokens if you put this behind a
|
||||
long-lived session.
|
||||
- **i18n.** Copy is English-only.
|
||||
|
||||
Reference in New Issue
Block a user