Forms
Build and submit dynamic forms per location
Danvas forms provide a flexible system for building custom data collection workflows per location. Forms are defined with a JSON schema and rendered dynamically at runtime.
Submission behavior: The reusable pending, validation, success, reset, conflict, autosave, and rate-limit rules live in the canonical Form Submission UX Standard. This page is the published feature reference for dynamic forms.
Overview
Admins build forms using a visual form builder. Each form has a name, description, and array of field definitions. Staff then fill out forms on any device — submissions trigger Slack notifications and update compliance tracking.
Field Types
Danvas supports 9 field types, including the persisted-schema and renderer-supported rating_scale field:
| Type | Description | Validation |
|---|---|---|
text | Single-line text input | Max 500 chars |
textarea | Multi-line text input | Max 5000 chars |
number | Numeric input | Range validation available |
email | Email address | Email format check |
select | Single dropdown | Options defined in form |
multiselect | Multiple choice checkboxes | At least 1 required |
date | Date picker | ISO date format |
checkbox | Boolean toggle | Returns true/false |
rating_scale | Integer rating control | Configurable range, defaults to 1–5 |
Form Schema
Forms are stored as JSONB with this structure:
interface FormField {
id: string;
type: FieldType;
label: string;
placeholder?: string;
required: boolean;
options?: FormFieldOption[]; // For select/multiselect
validation?: {
min?: number;
max?: number;
pattern?: string;
}; // For number/rating_scale
}
interface Form {
id: string;
name: string;
description: string;
fields: FormField[];
isActive: boolean;
createdAt: Date;
updatedAt: Date;
}Dynamic Rendering
Forms are rendered dynamically using:
- React Hook Form — Form state management
- Zod — Schema validation generated from field definitions
- Server Actions — Form submission handling
The renderer maps each field type to its appropriate input component, applies validation rules, and handles submission to the server.
Draft Autosave
Forms support automatic draft saving to prevent data loss:
- Drafts are saved to localStorage as users type (throttled to once per 30 seconds)
- If the page is accidentally closed, the draft is restored on next visit
- Drafts are keyed by form ID + user ID for multi-form support
- Clearing a submitted form removes the draft
Forms do not support photo or file uploads. Use Shift Reports or Incidents for attachments.
CSV Export
Form submissions can be exported as CSV from the admin submissions view, making it easy to analyze collected data in spreadsheet tools.
Submission Flow
- User fills out form fields in the browser
- Client-side validation via Zod schema (generated from form JSON)
- Server action
submitForm()receives validated data - Data stored in
form_submissionstable with timestamp - Slack notification sent to configured channels (if locationId provided)
- Compliance row updated if form is a shift/manager report
Slack Notifications
When a form is submitted, a Slack message is sent using Block Kit formatting:
// apps/slack-bot/lib/templates/dynamic-form.ts
function buildDynamicFormBlocks(form: Form, submission: FormSubmission) {
return [
{ type: "header", text: { type: "plain_text", text: form.name } },
{ type: "section", fields: [...] },
{ type: "context", elements: [...] }
];
}Configure Slack routing in Admin → Notifications → Slack through the protected environment workflow. See the Slack notifications how-to for the setup checklist; the Slack integration reference owns delivery and variable details.
Seeded Form Examples
The database seed includes these demo forms:
| Form | Purpose | Fields |
|---|---|---|
| Opening Checklist | Pre-shift verification | textarea, select (ratings), checkbox, number (temp) |
| Incident Report | Safety/event reporting | text, date, select (severity), textarea |
| Shift Log | End-of-shift notes | Various text and number fields |
Configuration
Environment Variables
Environment-variable ownership and Slack secret handling live in the Slack integration reference and the protected environment workflow. Form-type routing is configured through the slack_configs admin surface.
Form Builder Permissions
Only admins can create/edit forms. Form submission is available to all authenticated users.
Related Files
| File | Purpose |
|---|---|
apps/app/src/app/(authenticated)/admin/forms/form-builder.tsx | Admin form builder UI |
apps/app/src/app/(authenticated)/admin/forms/actions.ts | Form CRUD server actions |
apps/app/src/app/(authenticated)/forms/[formId]/form-submission-client.tsx | Dynamic form renderer |
apps/app/src/app/(authenticated)/forms/form-actions.ts | Submit form server action |
packages/database/src/schema/forms.ts | forms and form_submissions tables |
apps/slack-bot/lib/templates/dynamic-form.ts | Slack message builder |