@heycater/qualification-funnel 1.4.10 → 1.4.12

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
@@ -1,7 +1,7 @@
1
1
  var _a;
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import React__default, { createContext, useContext, useMemo, useReducer, useCallback, useEffect, useRef, useState, isValidElement, cloneElement, Children, forwardRef } from "react";
4
+ import React__default, { createContext, useContext, useMemo, useReducer, useCallback, useEffect, useRef, useState, isValidElement, cloneElement, Children, forwardRef, useLayoutEffect } from "react";
5
5
  import { darken as darken$1, lighten as lighten$1, ButtonBase as ButtonBase$2, Typography as Typography$1, useMediaQuery, Input as Input$1, IconButton as IconButton$2, useTheme as useTheme$1, FormControlLabel, Tooltip, Menu, Hidden, SnackbarContent, Snackbar, TextField, CircularProgress, Modal, ThemeProvider as ThemeProvider$1, StylesProvider as StylesProvider$1, CssBaseline } from "@material-ui/core";
6
6
  import { useTranslation, initReactI18next, I18nextProvider } from "react-i18next";
7
7
  import styled, { css as css$1, createGlobalStyle, ThemeProvider } from "styled-components";
@@ -11047,9 +11047,12 @@ function trackAnswerSelected(context, answer) {
11047
11047
  if (context.city) {
11048
11048
  properties.city = context.city;
11049
11049
  }
11050
- captureEvent("funnel_answer_selected", properties);
11050
+ captureEvent("qf_v2_answer_selected", properties);
11051
11051
  }
11052
11052
  function trackFormSubmitted(context, metadata) {
11053
+ if (!context.session_id) {
11054
+ console.warn("[Tracking] trackFormSubmitted called with empty session_id", context);
11055
+ }
11053
11056
  const properties = {
11054
11057
  funnel_mode: context.funnel_mode,
11055
11058
  session_id: context.session_id,
@@ -11062,9 +11065,12 @@ function trackFormSubmitted(context, metadata) {
11062
11065
  if (context.city) {
11063
11066
  properties.city = context.city;
11064
11067
  }
11065
- captureEvent("funnel_form_submitted", properties);
11068
+ captureEvent("qf_v2_form_submitted", properties);
11066
11069
  }
11067
11070
  function trackCompleted(context) {
11071
+ if (!context.session_id) {
11072
+ console.warn("[Tracking] trackCompleted called with empty session_id", context);
11073
+ }
11068
11074
  const properties = {
11069
11075
  funnel_mode: context.funnel_mode,
11070
11076
  session_id: context.session_id,
@@ -11076,9 +11082,12 @@ function trackCompleted(context) {
11076
11082
  if (context.city) {
11077
11083
  properties.city = context.city;
11078
11084
  }
11079
- captureEvent("funnel_completed", properties);
11085
+ captureEvent("qf_v2_completed", properties);
11080
11086
  }
11081
11087
  function trackAbandoned(context, lastStep) {
11088
+ if (!context.session_id) {
11089
+ console.warn("[Tracking] trackAbandoned called with empty session_id", context);
11090
+ }
11082
11091
  const properties = {
11083
11092
  funnel_mode: context.funnel_mode,
11084
11093
  session_id: context.session_id,
@@ -11092,7 +11101,7 @@ function trackAbandoned(context, lastStep) {
11092
11101
  if (context.city) {
11093
11102
  properties.city = context.city;
11094
11103
  }
11095
- captureEvent("funnel_abandoned", properties);
11104
+ captureEvent("qf_v2_abandoned", properties);
11096
11105
  }
11097
11106
  function trackStarted(context, initialStepId) {
11098
11107
  const properties = {
@@ -11100,11 +11109,11 @@ function trackStarted(context, initialStepId) {
11100
11109
  session_id: context.session_id,
11101
11110
  initial_step: initialStepId
11102
11111
  };
11103
- captureEvent("funnel_started", properties);
11112
+ captureEvent("qf_v2_started", properties);
11104
11113
  }
11105
11114
  const TrackingContext = createContext(null);
11106
11115
  function TrackingProvider({
11107
- funnelContext,
11116
+ funnelContextRef,
11108
11117
  updateContext,
11109
11118
  currentStepId,
11110
11119
  children
@@ -11121,12 +11130,12 @@ function TrackingProvider({
11121
11130
  const questions2 = (schemaStep == null ? void 0 : schemaStep.questions) || [];
11122
11131
  const questionIndex = questions2.findIndex((q2) => q2.id === questionId);
11123
11132
  const isLastQuestion = questionIndex === questions2.length - 1;
11124
- const sessionKey = `funnel_started_${funnelContext.session_id}`;
11133
+ const sessionKey = `qf_v2_started_${funnelContextRef.current.session_id}`;
11125
11134
  if (typeof sessionStorage !== "undefined") {
11126
11135
  const hasStarted = sessionStorage.getItem(sessionKey);
11127
11136
  if (!hasStarted) {
11128
11137
  sessionStorage.setItem(sessionKey, "true");
11129
- trackStarted(funnelContext, currentStepId);
11138
+ trackStarted(funnelContextRef.current, currentStepId);
11130
11139
  }
11131
11140
  }
11132
11141
  if (questionId === "service_type") {
@@ -11141,7 +11150,7 @@ function TrackingProvider({
11141
11150
  } else if (questionId === "city") {
11142
11151
  updateContext({ city: value });
11143
11152
  }
11144
- trackAnswerSelected(funnelContext, {
11153
+ trackAnswerSelected(funnelContextRef.current, {
11145
11154
  step_id: step2.id,
11146
11155
  step_index: stepIndex,
11147
11156
  question_id: questionId,
@@ -11149,20 +11158,23 @@ function TrackingProvider({
11149
11158
  is_last_question: isLastQuestion
11150
11159
  });
11151
11160
  },
11152
- [funnelContext, currentStepId, state, updateContext]
11161
+ [funnelContextRef, currentStepId, state, updateContext]
11153
11162
  );
11154
- return /* @__PURE__ */ jsx(TrackingContext.Provider, { value: { funnelContext, currentStepId, trackAnswer }, children });
11163
+ return /* @__PURE__ */ jsx(TrackingContext.Provider, { value: { funnelContextRef, currentStepId, trackAnswer }, children });
11155
11164
  }
11156
11165
  function useTracking() {
11157
11166
  const context = useContext(TrackingContext);
11158
11167
  if (!context) {
11159
- return {
11160
- funnelContext: {
11168
+ const defaultRef = {
11169
+ current: {
11161
11170
  session_id: "",
11162
11171
  funnel_mode: "embedded",
11163
11172
  service_type: "",
11164
11173
  total_steps: 0
11165
- },
11174
+ }
11175
+ };
11176
+ return {
11177
+ funnelContextRef: defaultRef,
11166
11178
  currentStepId: "",
11167
11179
  trackAnswer: () => {
11168
11180
  }
@@ -26660,7 +26672,7 @@ function RequestForm({ header = null }) {
26660
26672
  }
26661
26673
  handleDeliveryAddressChange();
26662
26674
  }, [deliveryAddress]);
26663
- const { funnelContext } = useTracking();
26675
+ const { funnelContextRef } = useTracking();
26664
26676
  const handleSubmit = async (values2) => {
26665
26677
  var _a3;
26666
26678
  handleDisableCTA(true);
@@ -26676,10 +26688,10 @@ function RequestForm({ header = null }) {
26676
26688
  if (success) {
26677
26689
  setIsActualUkLead(isUkLead);
26678
26690
  setShowLeadSuccess(true);
26679
- trackFormSubmitted(funnelContext, {
26691
+ trackFormSubmitted(funnelContextRef.current, {
26680
26692
  is_uk_lead: isUkLead
26681
26693
  });
26682
- trackCompleted(funnelContext);
26694
+ trackCompleted(funnelContextRef.current);
26683
26695
  return;
26684
26696
  } else {
26685
26697
  console.error("[HeyCater Funnel] Expected success screen but got success=false. Result:", response);
@@ -26706,10 +26718,10 @@ function RequestForm({ header = null }) {
26706
26718
  redirectRoute = `/${router.locale}/account/external/requests/${opportunity.id}`;
26707
26719
  }
26708
26720
  }
26709
- trackFormSubmitted(funnelContext, {
26721
+ trackFormSubmitted(funnelContextRef.current, {
26710
26722
  is_logged_in: !!currentUserAccount
26711
26723
  });
26712
- trackCompleted(funnelContext);
26724
+ trackCompleted(funnelContextRef.current);
26713
26725
  actions.setRequest(values2);
26714
26726
  redirectToCustomerAccountRequest(redirectRoute);
26715
26727
  };
@@ -29791,7 +29803,7 @@ const LoadingIndicator = ({ variant }) => {
29791
29803
  const WrapperBox = styled(Box)`
29792
29804
  color: var(--embedded-text-color, inherit);
29793
29805
  `;
29794
- function useAbandonmentTracking(funnelContext, state) {
29806
+ function useAbandonmentTracking(funnelContextRef, state) {
29795
29807
  const hasTrackedAbandonmentRef = useRef(false);
29796
29808
  const hasCompletedRef = useRef(false);
29797
29809
  useEffect(() => {
@@ -29811,7 +29823,7 @@ function useAbandonmentTracking(funnelContext, state) {
29811
29823
  const handleAbandonment = () => {
29812
29824
  if (hasTrackedAbandonmentRef.current) return;
29813
29825
  if (hasCompletedRef.current) return;
29814
- trackAbandoned(funnelContext, {
29826
+ trackAbandoned(funnelContextRef.current, {
29815
29827
  step_id: stateSnapshot.stepId,
29816
29828
  step_index: stateSnapshot.stepIndex
29817
29829
  });
@@ -29832,8 +29844,7 @@ function useAbandonmentTracking(funnelContext, state) {
29832
29844
  };
29833
29845
  }, [
29834
29846
  state.status,
29835
- "qualification" in state && "step" in state.qualification ? state.qualification.step.id : null,
29836
- funnelContext
29847
+ "qualification" in state && "step" in state.qualification ? state.qualification.step.id : null
29837
29848
  ]);
29838
29849
  }
29839
29850
  const EmbeddedFunnel = React__default.forwardRef(
@@ -29859,22 +29870,41 @@ const EmbeddedFunnel = React__default.forwardRef(
29859
29870
  });
29860
29871
  const updateContext = useCallback((patch) => {
29861
29872
  funnelContext.current = { ...funnelContext.current, ...patch };
29873
+ if (typeof sessionStorage !== "undefined" && funnelContext.current.session_id) {
29874
+ try {
29875
+ sessionStorage.setItem(
29876
+ `funnel_context_${funnelContext.current.session_id}`,
29877
+ JSON.stringify(funnelContext.current)
29878
+ );
29879
+ } catch (e2) {
29880
+ }
29881
+ }
29862
29882
  }, []);
29863
29883
  useEffect(() => {
29864
29884
  cleanupOldTrackingSessions();
29865
29885
  }, []);
29866
- useEffect(() => {
29886
+ useLayoutEffect(() => {
29867
29887
  var _a3, _b2, _c2;
29868
29888
  if (!("qualification" in state)) return;
29869
29889
  const sessionId = ((_a3 = state.qualification.sessionInfo) == null ? void 0 : _a3.sessionId) || "";
29870
29890
  const totalSteps = state.qualification.steps.filter((s3) => !s3.disabled).length;
29871
29891
  const serviceType = ((_b2 = state.qualification.answers) == null ? void 0 : _b2.service_type) || "";
29872
29892
  const city = (_c2 = state.qualification.answers) == null ? void 0 : _c2.city;
29893
+ let restoredContext = null;
29894
+ if (typeof sessionStorage !== "undefined" && sessionId) {
29895
+ try {
29896
+ const stored = sessionStorage.getItem(`funnel_context_${sessionId}`);
29897
+ if (stored) {
29898
+ restoredContext = JSON.parse(stored);
29899
+ }
29900
+ } catch (e2) {
29901
+ }
29902
+ }
29873
29903
  updateContext({
29874
29904
  session_id: sessionId,
29875
29905
  total_steps: totalSteps,
29876
- service_type: serviceType,
29877
- ...city && { city }
29906
+ service_type: (restoredContext == null ? void 0 : restoredContext.service_type) || serviceType,
29907
+ ...(restoredContext == null ? void 0 : restoredContext.city) || city ? { city: (restoredContext == null ? void 0 : restoredContext.city) || city } : {}
29878
29908
  });
29879
29909
  }, [
29880
29910
  "qualification" in state ? (_a2 = state.qualification.sessionInfo) == null ? void 0 : _a2.sessionId : null,
@@ -29892,7 +29922,7 @@ const EmbeddedFunnel = React__default.forwardRef(
29892
29922
  });
29893
29923
  }
29894
29924
  }, [state.status]);
29895
- useAbandonmentTracking(funnelContext.current, state);
29925
+ useAbandonmentTracking(funnelContext, state);
29896
29926
  const toNextStep2 = useCallback(() => {
29897
29927
  actions.nextStep();
29898
29928
  }, [actions]);
@@ -29944,7 +29974,7 @@ const EmbeddedFunnel = React__default.forwardRef(
29944
29974
  return /* @__PURE__ */ jsx(
29945
29975
  TrackingProvider,
29946
29976
  {
29947
- funnelContext: funnelContext.current,
29977
+ funnelContextRef: funnelContext,
29948
29978
  updateContext,
29949
29979
  currentStepId: stepId,
29950
29980
  children: /* @__PURE__ */ jsxs(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heycater/qualification-funnel",
3
- "version": "1.4.10",
3
+ "version": "1.4.12",
4
4
  "type": "module",
5
5
  "description": "Heycater embedded qualification funnel widget",
6
6
  "main": "dist/index.cjs.js",
@@ -32,7 +32,10 @@
32
32
  "preview": "vite preview",
33
33
  "prepublishOnly": "npm run clean && npm run build",
34
34
  "lint": "npx eslint ./src",
35
- "type-check": "tsc --noEmit"
35
+ "type-check": "tsc --noEmit",
36
+ "test": "vitest",
37
+ "test:ui": "vitest --ui",
38
+ "test:coverage": "vitest --coverage"
36
39
  },
37
40
  "peerDependencies": {
38
41
  "@bugsnag/js": "^7.5.4",
@@ -66,6 +69,9 @@
66
69
  },
67
70
  "devDependencies": {
68
71
  "@svgr/rollup": "^8.1.0",
72
+ "@testing-library/react": "^12.1.5",
73
+ "@testing-library/react-hooks": "^8.0.1",
74
+ "@testing-library/user-event": "^14.5.0",
69
75
  "@types/google.maps": "^3.54.1",
70
76
  "@types/lodash": "^4.17.0",
71
77
  "@types/node": "20.3.2",
@@ -74,6 +80,8 @@
74
80
  "@types/react-dom": "^17.0.2",
75
81
  "@types/styled-components": "^5.1.26",
76
82
  "@vitejs/plugin-react": "^4.2.1",
83
+ "@vitest/ui": "^1.0.0",
84
+ "happy-dom": "^12.10.3",
77
85
  "react": "^17.0.2",
78
86
  "react-dom": "^17.0.2",
79
87
  "rollup-plugin-visualizer": "^6.0.5",
@@ -81,6 +89,7 @@
81
89
  "typescript": "5.1.3",
82
90
  "vite": "^5.0.10",
83
91
  "vite-plugin-dts": "^3.7.0",
84
- "vite-plugin-static-copy": "^3.1.4"
92
+ "vite-plugin-static-copy": "^3.1.4",
93
+ "vitest": "^1.0.0"
85
94
  }
86
95
  }