Merged in fix/SW-3115-form-tracking-changes (pull request #2470)

fix(SW-3115): add form complete tracking and removed input error tracking

* fix(SW-3115): add form complete tracking and removed input error tracking


Approved-by: Bianca Widstam
This commit is contained in:
Tobias Johansson
2025-07-01 06:44:08 +00:00
committed by Bianca Widstam
parent 86bd3fcea3
commit d272cd03ce
5 changed files with 63 additions and 40 deletions

View File

@@ -96,11 +96,12 @@ export default function SignupForm({ title }: SignUpFormProps) {
const { control, subscribe } = methods const { control, subscribe } = methods
useFormTracking("signup", subscribe, control) const { trackFormSubmit } = useFormTracking("signup", subscribe, control)
async function onSubmit(data: SignUpSchema) { async function onSubmit(data: SignUpSchema) {
const phoneNumber = formatPhoneNumber(data.phoneNumber, data.phoneNumberCC) const phoneNumber = formatPhoneNumber(data.phoneNumber, data.phoneNumberCC)
signup.mutate({ ...data, phoneNumber, language: lang }) signup.mutate({ ...data, phoneNumber, language: lang })
trackFormSubmit()
} }
return ( return (

View File

@@ -89,11 +89,20 @@ export default function Details() {
watch, watch,
} = methods } = methods
useFormTracking("checkout", subscribe, control, ` - room ${roomNr}`) const { trackFormSubmit } = useFormTracking(
"checkout",
subscribe,
control,
` - room ${roomNr}`
)
useEffect(() => { useEffect(() => {
addPreSubmitCallback(`${idx}-details`, trigger) function callback() {
}, [addPreSubmitCallback, idx, trigger]) trigger()
trackFormSubmit()
}
addPreSubmitCallback(`${idx}-details`, callback)
}, [addPreSubmitCallback, idx, trigger, trackFormSubmit])
const updateDetailsStore = useCallback(() => { const updateDetailsStore = useCallback(() => {
if (isValid) { if (isValid) {

View File

@@ -85,7 +85,7 @@ export default function Details({ user }: DetailsProps) {
watch, watch,
} = methods } = methods
useFormTracking( const { trackFormSubmit } = useFormTracking(
"checkout", "checkout",
subscribe, subscribe,
control, control,
@@ -93,8 +93,12 @@ export default function Details({ user }: DetailsProps) {
) )
useEffect(() => { useEffect(() => {
addPreSubmitCallback(`${idx}-details`, trigger) function callback() {
}, [addPreSubmitCallback, idx, trigger]) trigger()
trackFormSubmit()
}
addPreSubmitCallback(`${idx}-details`, callback)
}, [addPreSubmitCallback, idx, trigger, trackFormSubmit])
const onSubmit = useCallback( const onSubmit = useCallback(
(values: DetailsSchema) => { (values: DetailsSchema) => {

View File

@@ -1,10 +1,15 @@
"use client" "use client"
import isEqual from "fast-deep-equal"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { startTransition, useEffect, useRef, useState } from "react" import {
startTransition,
useCallback,
useEffect,
useRef,
useState,
} from "react"
import { import {
type Control, type Control,
type FieldErrors,
type FieldValues, type FieldValues,
useFormState, useFormState,
type UseFromSubscribe, type UseFromSubscribe,
@@ -21,8 +26,8 @@ import { createSDKPageObject, trackPageView } from "@/utils/tracking"
import { import {
type FormType, type FormType,
trackFormAbandonment, trackFormAbandonment,
trackFormCompletion,
trackFormInputStarted, trackFormInputStarted,
trackFormValidationError,
} from "@/utils/tracking/form" } from "@/utils/tracking/form"
import type { import type {
@@ -284,38 +289,10 @@ export function useFormTracking<T extends FieldValues>(
const [formStarted, setFormStarted] = useState(false) const [formStarted, setFormStarted] = useState(false)
const lastAccessedField = useRef<string | undefined>(undefined) const lastAccessedField = useRef<string | undefined>(undefined)
const formState = useFormState({ control }) const formState = useFormState({ control })
const previousErrors = useRef<FieldErrors<T>>({})
useEffect(() => {
const errors = formState.errors
const prevErrors = previousErrors.current
const isNewErrors = !isEqual(errors, prevErrors)
if (Object.keys(errors).length && isNewErrors) {
const errorString = Object.values(errors)
.map((err) => {
if (err && "message" in err) {
return err.message
}
const nested = Object.values(err ?? {}).find(
(val) => val && typeof val === "object" && "message" in val
)
if (nested) {
return nested.message
}
return undefined
})
.filter(Boolean)
.join("|")
trackFormValidationError(formType, errorString, nameSuffix)
previousErrors.current = { ...errors }
}
}, [formType, formState, nameSuffix])
useEffect(() => { useEffect(() => {
const unsubscribe = subscribe({ const unsubscribe = subscribe({
formState: { touchedFields: true }, formState: { dirtyFields: true },
callback: (data) => { callback: (data) => {
if ("name" in data) { if ("name" in data) {
lastAccessedField.current = data.name as string lastAccessedField.current = data.name as string
@@ -353,4 +330,14 @@ export function useFormTracking<T extends FieldValues>(
window.removeEventListener("visibilitychange", handleVisibilityChange) window.removeEventListener("visibilitychange", handleVisibilityChange)
} }
}, [formStarted, formType, nameSuffix, formState.isValid]) }, [formStarted, formType, nameSuffix, formState.isValid])
const trackFormSubmit = useCallback(() => {
if (formState.isValid) {
trackFormCompletion(formType, nameSuffix)
}
}, [formType, nameSuffix, formState.isValid])
return {
trackFormSubmit,
}
} }

View File

@@ -79,3 +79,25 @@ export function trackFormValidationError(
}) })
} }
} }
export function trackFormCompletion(type: FormType, nameSuffix?: string) {
if (type === "checkout") {
trackEvent({
event: "formCompletion",
form: {
action: "checkout form completion",
name: "checkout enter detail" + nameSuffix,
type: type,
},
})
} else if (type === "signup") {
trackEvent({
event: "formCompletion",
form: {
action: "signup form completion",
name: "member registration" + nameSuffix,
type: type,
},
})
}
}