@heycater/qualification-funnel 1.8.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -10085,7 +10085,7 @@ function trackAnswerSelected(context, answer) {
10085
10085
  if (context.city) {
10086
10086
  properties.city = context.city;
10087
10087
  }
10088
- captureEvent("qf_v6_answer_selected", properties);
10088
+ captureEvent("qf_v8_answer_selected", properties);
10089
10089
  }
10090
10090
  function trackFormSubmitted(context, metadata) {
10091
10091
  if (!context.session_id) {
@@ -10103,7 +10103,7 @@ function trackFormSubmitted(context, metadata) {
10103
10103
  if (context.city) {
10104
10104
  properties.city = context.city;
10105
10105
  }
10106
- captureEvent("qf_v6_form_submitted", properties);
10106
+ captureEvent("qf_v8_form_submitted", properties);
10107
10107
  }
10108
10108
  function trackCompleted(context) {
10109
10109
  if (!context.session_id) {
@@ -10120,7 +10120,7 @@ function trackCompleted(context) {
10120
10120
  if (context.city) {
10121
10121
  properties.city = context.city;
10122
10122
  }
10123
- captureEvent("qf_v6_completed", properties);
10123
+ captureEvent("qf_v8_completed", properties);
10124
10124
  }
10125
10125
  function trackAbandoned(context, lastStep) {
10126
10126
  if (!context.session_id) {
@@ -10139,7 +10139,7 @@ function trackAbandoned(context, lastStep) {
10139
10139
  if (context.city) {
10140
10140
  properties.city = context.city;
10141
10141
  }
10142
- captureEvent("qf_v6_abandoned", properties);
10142
+ captureEvent("qf_v8_abandoned", properties);
10143
10143
  }
10144
10144
  function trackStarted(context, initialStepId) {
10145
10145
  const properties = {
@@ -10147,7 +10147,7 @@ function trackStarted(context, initialStepId) {
10147
10147
  session_id: context.session_id,
10148
10148
  initial_step: initialStepId
10149
10149
  };
10150
- captureEvent("qf_v6_started", properties);
10150
+ captureEvent("qf_v8_started", properties);
10151
10151
  }
10152
10152
  const TrackingContext = createContext(null);
10153
10153
  function TrackingProvider({
@@ -10156,7 +10156,44 @@ function TrackingProvider({
10156
10156
  currentStepId,
10157
10157
  children
10158
10158
  }) {
10159
- const { state } = useQualification();
10159
+ const { state, answerOf } = useQualification();
10160
+ const trackStepCompletion = useCallback(() => {
10161
+ if (!("qualification" in state)) return;
10162
+ const qual = state.qualification;
10163
+ if (!("step" in qual)) return;
10164
+ const step2 = qual.step;
10165
+ const stepIndex = qual.stepIndex;
10166
+ const schemaStep = SCHEMA_STEPS[step2.id];
10167
+ const questions2 = (schemaStep == null ? void 0 : schemaStep.questions) || [];
10168
+ const sessionKey = `qf_v8_started_${funnelContextRef.current.session_id}`;
10169
+ if (typeof sessionStorage !== "undefined") {
10170
+ const hasStarted = sessionStorage.getItem(sessionKey);
10171
+ if (!hasStarted) {
10172
+ sessionStorage.setItem(sessionKey, "true");
10173
+ trackStarted(funnelContextRef.current, currentStepId);
10174
+ }
10175
+ }
10176
+ questions2.forEach((question, questionIndex) => {
10177
+ const currentValue = answerOf(question.id);
10178
+ const isLastQuestion = questionIndex === questions2.length - 1;
10179
+ if (question.id === "service_type" && isValidServiceType(currentValue)) {
10180
+ const stepOrder = getStepOrderForServiceType(currentValue);
10181
+ updateContext({
10182
+ service_type: currentValue,
10183
+ total_steps: stepOrder.length
10184
+ });
10185
+ } else if (question.id === "city" && currentValue) {
10186
+ updateContext({ city: currentValue });
10187
+ }
10188
+ trackAnswerSelected(funnelContextRef.current, {
10189
+ step_id: step2.id,
10190
+ step_index: stepIndex,
10191
+ question_id: question.id,
10192
+ answer_value: currentValue ?? null,
10193
+ is_last_question: isLastQuestion
10194
+ });
10195
+ });
10196
+ }, [funnelContextRef, currentStepId, state, updateContext, answerOf]);
10160
10197
  const trackAnswer = useCallback(
10161
10198
  (questionId, value) => {
10162
10199
  if (!("qualification" in state)) return;
@@ -10168,7 +10205,7 @@ function TrackingProvider({
10168
10205
  const questions2 = (schemaStep == null ? void 0 : schemaStep.questions) || [];
10169
10206
  const questionIndex = questions2.findIndex((q2) => q2.id === questionId);
10170
10207
  const isLastQuestion = questionIndex === questions2.length - 1;
10171
- const sessionKey = `qf_v6_started_${funnelContextRef.current.session_id}`;
10208
+ const sessionKey = `qf_v8_started_${funnelContextRef.current.session_id}`;
10172
10209
  if (typeof sessionStorage !== "undefined") {
10173
10210
  const hasStarted = sessionStorage.getItem(sessionKey);
10174
10211
  if (!hasStarted) {
@@ -10176,16 +10213,13 @@ function TrackingProvider({
10176
10213
  trackStarted(funnelContextRef.current, currentStepId);
10177
10214
  }
10178
10215
  }
10179
- if (questionId === "service_type") {
10180
- const serviceType = value;
10181
- if (serviceType) {
10182
- const stepOrder = getStepOrderForServiceType(serviceType);
10183
- delete funnelContextRef.current.city;
10184
- updateContext({
10185
- service_type: serviceType,
10186
- total_steps: stepOrder.length
10187
- });
10188
- }
10216
+ if (questionId === "service_type" && isValidServiceType(value)) {
10217
+ const stepOrder = getStepOrderForServiceType(value);
10218
+ delete funnelContextRef.current.city;
10219
+ updateContext({
10220
+ service_type: value,
10221
+ total_steps: stepOrder.length
10222
+ });
10189
10223
  } else if (questionId === "city") {
10190
10224
  updateContext({ city: value });
10191
10225
  }
@@ -10199,7 +10233,7 @@ function TrackingProvider({
10199
10233
  },
10200
10234
  [funnelContextRef, currentStepId, state, updateContext]
10201
10235
  );
10202
- return /* @__PURE__ */ jsx(TrackingContext.Provider, { value: { funnelContextRef, currentStepId, trackAnswer }, children });
10236
+ return /* @__PURE__ */ jsx(TrackingContext.Provider, { value: { funnelContextRef, currentStepId, trackAnswer, trackStepCompletion }, children });
10203
10237
  }
10204
10238
  function useTracking() {
10205
10239
  const context = useContext(TrackingContext);
@@ -10216,6 +10250,8 @@ function useTracking() {
10216
10250
  funnelContextRef: defaultRef,
10217
10251
  currentStepId: "",
10218
10252
  trackAnswer: () => {
10253
+ },
10254
+ trackStepCompletion: () => {
10219
10255
  }
10220
10256
  };
10221
10257
  }
@@ -29968,8 +30004,13 @@ const EmbeddedFunnel = React__default.forwardRef(
29968
30004
  }
29969
30005
  }, [state.status]);
29970
30006
  useAbandonmentTracking(funnelContext, state);
30007
+ const toNextStepRef = useRef(null);
29971
30008
  const toNextStep2 = useCallback(() => {
29972
- actions.nextStep();
30009
+ if (toNextStepRef.current) {
30010
+ toNextStepRef.current();
30011
+ } else {
30012
+ actions.nextStep();
30013
+ }
29973
30014
  }, [actions]);
29974
30015
  useEffect(() => {
29975
30016
  if (state.status === QUALIFICATION_STATUS.qualifiedForMarketplace) {
@@ -30024,69 +30065,85 @@ const EmbeddedFunnel = React__default.forwardRef(
30024
30065
  const isFullscreen = mode === "fullscreen";
30025
30066
  const enabledSteps = state.qualification.steps.filter((q2) => !q2.disabled);
30026
30067
  const handleCancel = onCancel || (() => window.history.back());
30027
- return /* @__PURE__ */ jsx(
30068
+ return /* @__PURE__ */ jsxs(
30028
30069
  TrackingProvider,
30029
30070
  {
30030
30071
  funnelContextRef: funnelContext,
30031
30072
  updateContext,
30032
30073
  currentStepId: stepId,
30033
- children: /* @__PURE__ */ jsxs(
30034
- FormWrapper,
30035
- {
30036
- $textColor: textColor,
30037
- $mode: mode,
30038
- tabIndex: 0,
30039
- ref: ref2,
30040
- "data-embedded-funnel": true,
30041
- "data-ph-capture-attribute-funnel-step": stepId || void 0,
30042
- "data-ph-capture-attribute-funnel-mode": mode,
30043
- name: "step" in state.qualification ? state.qualification.step.id : state.status,
30044
- onSubmit: (e2) => {
30045
- e2.preventDefault();
30046
- toNextStep2();
30047
- },
30048
- onKeyPressCapture: onKeyPress,
30049
- children: [
30050
- isFullscreen && state.status === QUALIFICATION_STATUS.answering && /* @__PURE__ */ jsx(
30051
- FullscreenHeader,
30052
- {
30053
- currentStep: state.qualification.stepIndex + 1,
30054
- stepCount: enabledSteps.length,
30055
- onCancel: handleCancel
30056
- }
30057
- ),
30058
- /* @__PURE__ */ jsx(Box, { position: "absolute", top: "-120px", ref: scrollHelperRef }),
30059
- /* @__PURE__ */ jsxs(Box, { width: "100%", pb: isFullscreen ? 0 : 6, flexGrow: 1, display: "flex", flexDirection: "column", children: [
30060
- state.status === QUALIFICATION_STATUS.answering && /* @__PURE__ */ jsx(
30061
- EmbeddedQuestion,
30074
+ children: [
30075
+ /* @__PURE__ */ jsx(StepTransitionHandler, { toNextStepRef, nextStep: actions.nextStep }),
30076
+ /* @__PURE__ */ jsxs(
30077
+ FormWrapper,
30078
+ {
30079
+ $textColor: textColor,
30080
+ $mode: mode,
30081
+ tabIndex: 0,
30082
+ ref: ref2,
30083
+ "data-embedded-funnel": true,
30084
+ "data-ph-capture-attribute-funnel-step": stepId || void 0,
30085
+ "data-ph-capture-attribute-funnel-mode": mode,
30086
+ name: "step" in state.qualification ? state.qualification.step.id : state.status,
30087
+ onSubmit: (e2) => {
30088
+ e2.preventDefault();
30089
+ toNextStep2();
30090
+ },
30091
+ onKeyPressCapture: onKeyPress,
30092
+ children: [
30093
+ isFullscreen && state.status === QUALIFICATION_STATUS.answering && /* @__PURE__ */ jsx(
30094
+ FullscreenHeader,
30062
30095
  {
30063
- slides,
30064
- step: state.qualification.step,
30065
- highlightColor,
30066
- ratings,
30067
- renderTitle,
30068
- LayoutComponent,
30069
- titleTranslationKeys,
30070
- mode
30096
+ currentStep: state.qualification.stepIndex + 1,
30097
+ stepCount: enabledSteps.length,
30098
+ onCancel: handleCancel
30071
30099
  }
30072
30100
  ),
30073
- state.status === QUALIFICATION_STATUS.answered && /* @__PURE__ */ jsxs(Fragment, { children: [
30074
- /* @__PURE__ */ jsx(
30075
- LoadingIndicator,
30101
+ /* @__PURE__ */ jsx(Box, { position: "absolute", top: "-120px", ref: scrollHelperRef }),
30102
+ /* @__PURE__ */ jsxs(Box, { width: "100%", pb: isFullscreen ? 0 : 6, flexGrow: 1, display: "flex", flexDirection: "column", children: [
30103
+ state.status === QUALIFICATION_STATUS.answering && /* @__PURE__ */ jsx(
30104
+ EmbeddedQuestion,
30076
30105
  {
30077
- variant: onBackground === "dark" ? "light" : "dark"
30106
+ slides,
30107
+ step: state.qualification.step,
30108
+ highlightColor,
30109
+ ratings,
30110
+ renderTitle,
30111
+ LayoutComponent,
30112
+ titleTranslationKeys,
30113
+ mode
30078
30114
  }
30079
30115
  ),
30080
- /* @__PURE__ */ jsx(Box, { display: "none", children: /* @__PURE__ */ jsx(EmbeddedRequestPage, { ratings }) })
30116
+ state.status === QUALIFICATION_STATUS.answered && /* @__PURE__ */ jsxs(Fragment, { children: [
30117
+ /* @__PURE__ */ jsx(
30118
+ LoadingIndicator,
30119
+ {
30120
+ variant: onBackground === "dark" ? "light" : "dark"
30121
+ }
30122
+ ),
30123
+ /* @__PURE__ */ jsx(Box, { display: "none", children: /* @__PURE__ */ jsx(EmbeddedRequestPage, { ratings }) })
30124
+ ] })
30081
30125
  ] })
30082
- ] })
30083
- ]
30084
- }
30085
- )
30126
+ ]
30127
+ }
30128
+ )
30129
+ ]
30086
30130
  }
30087
30131
  );
30088
30132
  }
30089
30133
  );
30134
+ function StepTransitionHandler({ toNextStepRef, nextStep }) {
30135
+ const { trackStepCompletion } = useTracking();
30136
+ useLayoutEffect(() => {
30137
+ toNextStepRef.current = () => {
30138
+ trackStepCompletion();
30139
+ nextStep();
30140
+ };
30141
+ return () => {
30142
+ toNextStepRef.current = null;
30143
+ };
30144
+ }, [toNextStepRef, trackStepCompletion, nextStep]);
30145
+ return null;
30146
+ }
30090
30147
  const FormWrapper = styled.form`
30091
30148
  --embedded-text-color: ${({ $textColor }) => $textColor === "light" ? "white" : "rgb(60, 67, 64)"};
30092
30149
  width: 100%;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heycater/qualification-funnel",
3
- "version": "1.8.1",
3
+ "version": "1.9.0",
4
4
  "type": "module",
5
5
  "description": "Heycater embedded qualification funnel widget",
6
6
  "main": "dist/index.cjs.js",