Skip to main content

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

EntityPurpose
form_templatesLogical 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_versionsImmutable 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_submissionsA 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_mediaPhotos, 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_geometry flag 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_at set.

Submission flow

  1. POST /api/submissions creates the submission row. Schema validation server-side uses JSON Schema Draft 2020-12 (same dialect as @rjsf/core on the web, so client and server agree).
  2. POST /api/submissions/{id}/media is a multipart upload (one call per attachment) with fieldName and file. Files land in object storage at submissions/{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 data is filtered and mapped via the version's feature_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_id is 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.
info

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.

  • 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_id writes 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.