Skip to content

feat: submission analytics — drop-off, completion rate, median time #137

Description

@harshtandiya

Context

No visibility into form funnel today. Need metrics per form:

  • Completion rate — submitted / engaged sessions
  • Drop-off rate — per-field abandonment
  • Median time to submit
  • View count vs engaged (first interaction)

Proposed approach

Data model

New doctype FP Submission Session (one row per visit):

field purpose
form (Link) parent form
session_id (Data, unique) uuid from sessionStorage
user (Link, nullable) logged-in user
guest_hash (Data) hashed IP+UA for guest dedupe
started_at (Datetime) first interaction
last_activity_at (Datetime) rolling
submitted_at (Datetime, nullable) finalize
submission (Dynamic Link, nullable) resulting doc
last_field_reached (Data) last focused field
engaged (Check) first focus fired
abandoned (Check) cron-marked after stale
user_agent, referrer (Data) context

Optional follow-up doctype FP Submission Event (append-only) — session, event_type (focus/blur/submit), field, timestamp. Enables fine-grained drop-off later. Out of scope for first cut.

Lifecycle

  1. Page open — frontend generates session_id, stores in sessionStorage. No backend call yet.
  2. First field focus — POST /api/method/forms_pro.api.metrics.start_session → creates row with engaged=true.
  3. Field blur — throttled beacon updates last_field_reached, last_activity_at. Batch every 5s or on blur.
  4. Submit — existing submit_form_response sets submitted_at, links submission.
  5. Tab close / navigationvisibilitychangenavigator.sendBeacon() final flush.
  6. Stale sweeper — daily cron marks sessions with last_activity_at > 30min and no submitted_at as abandoned=true.

API

New module forms_pro/api/metrics.py:

  • start_session(form, session_id)allow_guest=True, idempotent
  • update_session(session_id, last_field, ...) — accepts beacon blob
  • get_form_metrics(form) — aggregate query for dashboard

Frontend

  • New composable useSubmissionMetrics(formId) in frontend/src/composables/
  • Wire into SubmissionPage.vue and PublicEdit.vue (skip edit mode for metrics)
  • visibilitychange listener fires sendBeacon with current state
  • Display metrics on ManageForm.vue overview

Metric queries

  • Completion rate = count(submitted_at) / count(engaged=true)
  • Median time = percentile_cont(0.5) within group (order by submitted_at - started_at)
  • Drop-off per field = count(last_field_reached=X AND submitted_at IS NULL) / count(reached X)

Edge cases

  • Guests — opaque session_id, no user link
  • Refresh mid-fillsessionStorage persists session_id; resume row
  • Edit mode — skip logging entirely
  • Multi-tab — separate session_ids, accepted
  • Botsengaged=false filters out view-only noise
  • Beacon drops (~5%) — cron sweeper backstops
  • Write amplification — throttle blur events, no per-keystroke writes
  • PII — log field names only, never values

Out of scope

  • Per-field event log (FP Submission Event) — follow-up
  • External analytics integration (PostHog)
  • A/B testing form variants
  • Geo / device breakdown

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions