Forms
Forms are schema-driven field-capture templates. An admin builds the form once; field engineers fill it on mobile (online or offline); the resulting submissions flow back into the platform and can auto-create features in a linked layer.
Core data model
| Entity | Purpose |
|---|---|
form_templates | Logical form definition: name, slug, schema_json (JSON Schema describing fields), requires_geometry (bool), geometry_type (POINT / LINESTRING / POLYGON), optional linked_feature_class_id. |
form_template_versions | Immutable snapshot after publish. Carries schema_json, ui_schema_json (widget hints, field order, conditional logic), feature_property_map (form field to feature property), published_at (null while draft). |
form_submissions | A capture event tied to a specific version, with optional geometry, JSON data, submitter, timestamp, source (web / mobile / public), client_submission_id for idempotency, optional linked_feature_id. |
form_submission_media | Photos, video, audio, and scans, grouped by field_name, stored in object storage with presigned download URLs. |
Admins build Survey123-style custom forms without writing code; field engineers fill them on mobile; submissions flow into layers.
Why it matters
The feature-class system is great for static data the admin uploads once (the pipe register), but terrible for recurring data the field generates (every weekly valve check, every defect logged on a walk-around). Without forms, field engineers either skip the system entirely (paper, WhatsApp, a competing app) or someone re-keys their notes at the end of the day. Both options break the audit trail.
The win over standalone field-capture tools is the linkage. A submission isn't just a row in a submissions table; it can create or update a feature in any layer the form targets. That linkage makes the inspection workflow self-perpetuating: every weekly form fills the defects layer automatically, no separate data-entry job.
Daily users
- Field engineer / Inspector: mobile form runner, offline edits, photo capture, work orders.
- GIS analyst / Asset manager: designs templates, browses the published forms list, reviews submissions.
- Ops supervisor: sees submission volume on dashboards.
Form builder
- Schema-driven templates using
@rjsf/core. - Today: a JSON Schema textarea + live preview with AJV8 validation (power-user flexibility now); a drag-drop "simple mode" rows-builder is on our roadmap once real users ask.
- Field types: text, number, dropdown, checkbox, radio, date, photo (camera or upload), video, audio, GPS point, QR / barcode scan, signature, rating, linked-feature picker.
- Conditional logic: show field X if field Y = value.
- Validation: required, min / max, regex, custom.
Geometry
requires_geometryflag ties a submission to a specific location (point / line / polygon).- Optional: forms can capture data without geometry, or with required geometry that fails validation if missing.
Publishing and versioning
- Forms are mutable until published.
- After publish, edits create a new version (V2, V3, …).
- Every submission references a specific version, so historic submissions stay readable when the schema changes.
- Drafts can be discarded or published explicitly.
- Cannot submit against a draft: the version must have
published_atset.
Submission flow
POST /api/submissionscreates the submission row. Schema validation server-side uses JSON Schema Draft 2020-12 (same dialect as@rjsf/coreon the web, so client and server agree).POST /api/submissions/{id}/mediais a multipart upload (one call per attachment) withfieldNameandfile. Files land in object storage atsubmissions/{submissionId}/{mediaId}.{ext}; download URLs are 1-hour presigned and org-scoped.
Idempotency
clientSubmissionId(UUID generated on the device before submit): the same id resubmitted after connection loss returns the existing row, no duplicate.- Cross-org collision returns
409.
Linked-feature auto-creation
If the form's template has targetFeatureClass set:
- Submission location becomes the new feature's geometry.
- Submission
datais filtered and mapped via the version'sfeature_property_map: only mapped fields propagate to the feature; operational data (inspector name, scan time) stays on the submission. - Mapped properties are validated against the feature class schema.
- Best-effort, not atomic. If the feature can't be created (validation failure), the submission still succeeds and the failure logs at WARN. Capture is primary; the derived feature is secondary.
- Target class must be POINT today (LINESTRING and POLYGON support is on our roadmap).
- On success,
submission.linked_feature_idis set.
Drafts and offline
- Mobile offline queue holds drafts and queued submissions.
- A field engineer can save a partially-filled form, capture more data later, then submit.
Some advanced capture types (full file-upload pipeline, version-browser UI, drag-drop "simple mode" form builder) are in development. The JSON-Schema textarea form builder is shipped today; everything else falls back gracefully.
How it links to the others
- Forms → Submissions. A submission is always created by filling a published form, and is always tied to a specific form version (so historic data survives schema changes).
- Forms → Map. A form with
linked_feature_class_idwrites into that layer; the submission becomes a feature pin on the map. - Forms → Dashboards. Submission counts, completion rates, and submitted values feed dashboard widgets ("defects logged this week", "open inspections by region").
- Forms → Analysis. Submission locations are valid input geometries for spatial analysis (e.g. "buffer all defect submissions by 100 m, intersect with flood zones").
- Forms ↔ Twins. A linked-feature picker can target twin elements directly, so an inspection submission ties to a specific 3D component, not just a map pin.