@heycater/qualification-funnel 1.19.37 → 1.20.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
@@ -2226,6 +2226,7 @@ const CITY_LABELS = {
2226
2226
  koeln: "Köln",
2227
2227
  stuttgart: "Stuttgart",
2228
2228
  wien: "Wien",
2229
+ london: "London",
2229
2230
  other: "Andere Stadt"
2230
2231
  };
2231
2232
  const EVENT_LABELS_DE = {
@@ -7342,6 +7343,60 @@ const LeadTimeNote = styled.p`
7342
7343
  margin: 12px 0 0;
7343
7344
  text-align: center;
7344
7345
  `;
7346
+ const useIsGb = () => {
7347
+ const { query } = useRouter();
7348
+ return getIsGb(query);
7349
+ };
7350
+ const getIsGb = (query) => {
7351
+ if (typeof window !== "undefined") {
7352
+ const pathname = window.location.pathname.toLowerCase();
7353
+ if (pathname.includes("en-uk") || pathname.includes("london")) {
7354
+ return true;
7355
+ }
7356
+ }
7357
+ if (query == null ? void 0 : query.slug) {
7358
+ return query.slug === "catering-london";
7359
+ }
7360
+ if (typeof window !== "undefined") {
7361
+ const params = new URLSearchParams(window.location.search);
7362
+ return params.get("slug") === "catering-london";
7363
+ }
7364
+ return false;
7365
+ };
7366
+ const getCurrencySymbol = (isGb) => {
7367
+ return isGb ? "£" : "€";
7368
+ };
7369
+ function getRegionalFormat({
7370
+ isGb,
7371
+ isGerman
7372
+ }) {
7373
+ const currencySymbol = getCurrencySymbol(isGb);
7374
+ const numberLocale = isGerman ? "de-DE" : "en-GB";
7375
+ return {
7376
+ currencySymbol,
7377
+ dialCode: isGb ? "+44" : "+49",
7378
+ currencyPosition: isGb ? "prefix" : "suffix",
7379
+ formatMoney: (amount) => {
7380
+ const value = amount.toLocaleString(numberLocale);
7381
+ return isGb ? `${currencySymbol}${value}` : `${value} ${currencySymbol}`;
7382
+ },
7383
+ formatDate: (date2) => format(date2, isGb ? "dd/MM/yyyy" : "dd.MM.yy"),
7384
+ netLabel: isGerman ? "netto" : "excl. VAT",
7385
+ emailPlaceholder: isGb ? "name@company.co.uk" : "name@firma.de",
7386
+ phonePlaceholder: isGb ? "+44 20 7123 4567" : "+49 30 56837200",
7387
+ addressPlaceholder: isGerman ? "Straße, PLZ, Ort" : isGb ? "Street, Postcode, City" : "Street, ZIP, City"
7388
+ };
7389
+ }
7390
+ const MIN_PHONE_DIGITS = 8;
7391
+ function normalizePhoneInput(value, dialCode) {
7392
+ const cleaned = value.replace(/[^\d+\s-]/g, "");
7393
+ if (!cleaned) return cleaned;
7394
+ if (cleaned.startsWith("+")) return cleaned;
7395
+ return `${dialCode}${cleaned.replace(/^0+/, "")}`;
7396
+ }
7397
+ function hasEnoughPhoneDigits(value) {
7398
+ return (value || "").replace(/\D/g, "").length >= MIN_PHONE_DIGITS;
7399
+ }
7345
7400
  const PRESETS = [10, 25, 50, 100];
7346
7401
  const BudgetPeopleStep = ({
7347
7402
  peopleCount,
@@ -7352,12 +7407,14 @@ const BudgetPeopleStep = ({
7352
7407
  var _a2;
7353
7408
  const { i18n } = useTranslation();
7354
7409
  const isGerman = (_a2 = i18n.language) == null ? void 0 : _a2.startsWith("de");
7410
+ const isGb = useIsGb();
7411
+ const regional = getRegionalFormat({ isGb, isGerman });
7355
7412
  const [peopleInputValue, setPeopleInputValue] = useState(String(peopleCount));
7356
7413
  const [budgetInputValue, setBudgetInputValue] = useState(String(budgetPerPerson2));
7357
7414
  const peopleInputRef = useRef(null);
7358
7415
  const budgetInputRef = useRef(null);
7359
7416
  const totalBudget = peopleCount * budgetPerPerson2;
7360
- const formattedTotal = totalBudget.toLocaleString("de-DE");
7417
+ const formattedTotal = regional.formatMoney(totalBudget);
7361
7418
  const handlePeopleIncrement = useCallback(() => {
7362
7419
  const newValue = peopleCount + 5;
7363
7420
  onPeopleCountChange(newValue);
@@ -7463,7 +7520,7 @@ const BudgetPeopleStep = ({
7463
7520
  onBlur: handleBudgetInputBlur
7464
7521
  }
7465
7522
  ),
7466
- /* @__PURE__ */ jsx(CurrencyLabel, { children: "€" })
7523
+ /* @__PURE__ */ jsx(CurrencyLabel, { $position: regional.currencyPosition, children: regional.currencySymbol })
7467
7524
  ] }),
7468
7525
  /* @__PURE__ */ jsx(StepButton, { onClick: handleBudgetIncrement, type: "button", children: "+" })
7469
7526
  ] })
@@ -7472,12 +7529,9 @@ const BudgetPeopleStep = ({
7472
7529
  /* @__PURE__ */ jsxs(TotalLabel, { children: [
7473
7530
  isGerman ? "Gesamtbudget" : "Total budget",
7474
7531
  " ",
7475
- /* @__PURE__ */ jsx(NetLabel, { children: "netto" })
7532
+ /* @__PURE__ */ jsx(NetLabel, { children: regional.netLabel })
7476
7533
  ] }),
7477
- /* @__PURE__ */ jsxs(TotalValue, { children: [
7478
- formattedTotal,
7479
- " €"
7480
- ] })
7534
+ /* @__PURE__ */ jsx(TotalValue, { children: formattedTotal })
7481
7535
  ] })
7482
7536
  ] }) });
7483
7537
  };
@@ -7566,7 +7620,7 @@ const ValueInputWrapper = styled.div`
7566
7620
  `;
7567
7621
  const CurrencyLabel = styled.span`
7568
7622
  position: absolute;
7569
- right: 12px;
7623
+ ${({ $position }) => $position === "prefix" ? "left: 12px;" : "right: 12px;"}
7570
7624
  font-family: ${REDESIGN_FONTS.family};
7571
7625
  font-weight: ${REDESIGN_FONTS.weight.semibold};
7572
7626
  font-size: 16px;
@@ -7697,22 +7751,6 @@ const DEFAULT_QUALIFICATION_STATE = Object.freeze(
7697
7751
  currentUserAccount: null
7698
7752
  }
7699
7753
  );
7700
- const getIsGb = (query) => {
7701
- if (typeof window !== "undefined") {
7702
- const pathname = window.location.pathname.toLowerCase();
7703
- if (pathname.includes("en-uk") || pathname.includes("london")) {
7704
- return true;
7705
- }
7706
- }
7707
- if (typeof window !== "undefined") {
7708
- const params = new URLSearchParams(window.location.search);
7709
- return params.get("slug") === "catering-london";
7710
- }
7711
- return false;
7712
- };
7713
- const getCurrencySymbol = (isGb) => {
7714
- return isGb ? "£" : "€";
7715
- };
7716
7754
  const CATERING_CATEGORIES = [
7717
7755
  "breakfast",
7718
7756
  "fingerfood",
@@ -21698,6 +21736,8 @@ const RedesignContactForm = ({
21698
21736
  var _a2;
21699
21737
  const { t: t2, i18n } = useTranslation("qualification");
21700
21738
  const isGerman = (_a2 = i18n.language) == null ? void 0 : _a2.startsWith("de");
21739
+ const isGb = useIsGb();
21740
+ const regional = getRegionalFormat({ isGb, isGerman });
21701
21741
  const { answerOf } = useQualification();
21702
21742
  const [commentOpen, setCommentOpen] = useState(false);
21703
21743
  const [hasSubmitted, setHasSubmitted] = useState(false);
@@ -21730,7 +21770,11 @@ const RedesignContactForm = ({
21730
21770
  last_name: create$5().required(isGerman ? "Bitte Nachname eingeben" : "Please enter last name"),
21731
21771
  company: create$5().required(isGerman ? "Bitte Firma eingeben" : "Please enter company"),
21732
21772
  email: create$5().email(isGerman ? "Bitte gültige E-Mail eingeben" : "Please enter valid email").required(isGerman ? "Bitte E-Mail eingeben" : "Please enter email"),
21733
- phone: create$5().required(isGerman ? "Bitte Telefon eingeben" : "Please enter phone"),
21773
+ phone: create$5().required(isGerman ? "Bitte Telefon eingeben" : "Please enter phone").test(
21774
+ "phone-complete",
21775
+ isGerman ? "Bitte vollständige Telefonnummer eingeben" : "Please enter a complete phone number",
21776
+ hasEnoughPhoneDigits
21777
+ ),
21734
21778
  deliveryAddressStreet: create$5().min(1).required(isGerman ? "Bitte Lieferadresse eingeben" : "Please enter delivery address"),
21735
21779
  deliveryAddressCity: create$5().min(1).required(isGerman ? "Bitte Stadt eingeben" : "Please enter city")
21736
21780
  });
@@ -21755,7 +21799,7 @@ const RedesignContactForm = ({
21755
21799
  } else if (eventDate) {
21756
21800
  try {
21757
21801
  const dateObj = eventDate instanceof Date ? eventDate : new Date(eventDate);
21758
- chips.push({ label: isGerman ? "Datum" : "Date", value: format(dateObj, "dd.MM.yy") });
21802
+ chips.push({ label: isGerman ? "Datum" : "Date", value: regional.formatDate(dateObj) });
21759
21803
  } catch {
21760
21804
  }
21761
21805
  }
@@ -21764,7 +21808,7 @@ const RedesignContactForm = ({
21764
21808
  }
21765
21809
  if (typeof pricePerPerson === "number" && pricePerPerson > 0) {
21766
21810
  const total2 = (peopleCount || 0) * pricePerPerson;
21767
- chips.push({ label: "Budget", value: `${total2.toLocaleString("de-DE")} €` });
21811
+ chips.push({ label: "Budget", value: regional.formatMoney(total2) });
21768
21812
  }
21769
21813
  } catch (e2) {
21770
21814
  console.error("Error building summary chips:", e2);
@@ -21843,7 +21887,7 @@ const RedesignContactForm = ({
21843
21887
  validateOnMount: false,
21844
21888
  validateOnBlur: true,
21845
21889
  validateOnChange: true,
21846
- children: ({ errors, touched, handleChange, handleBlur, values: values2, validateForm }) => /* @__PURE__ */ jsxs(FormWrapper, { children: [
21890
+ children: ({ errors, touched, handleChange, handleBlur, values: values2, validateForm, setFieldValue }) => /* @__PURE__ */ jsxs(FormWrapper, { children: [
21847
21891
  /* @__PURE__ */ jsx(ScrollableContent, { children: /* @__PURE__ */ jsxs(Container, { children: [
21848
21892
  /* @__PURE__ */ jsxs(SummaryRow, { children: [
21849
21893
  /* @__PURE__ */ jsx(SummaryLabel, { children: isGerman ? "Deine Anfrage:" : "Your request:" }),
@@ -21959,7 +22003,7 @@ const RedesignContactForm = ({
21959
22003
  markUserTouched("email");
21960
22004
  handleBlur(e2);
21961
22005
  },
21962
- placeholder: "name@firma.de",
22006
+ placeholder: regional.emailPlaceholder,
21963
22007
  $hasError: shouldShowError("email", !!touched.email, errors.email)
21964
22008
  }
21965
22009
  ),
@@ -21976,12 +22020,15 @@ const RedesignContactForm = ({
21976
22020
  type: "tel",
21977
22021
  name: "phone",
21978
22022
  value: values2.phone,
21979
- onChange: handleChange,
22023
+ onChange: (e2) => setFieldValue(
22024
+ "phone",
22025
+ normalizePhoneInput(e2.target.value, regional.dialCode)
22026
+ ),
21980
22027
  onBlur: (e2) => {
21981
22028
  markUserTouched("phone");
21982
22029
  handleBlur(e2);
21983
22030
  },
21984
- placeholder: "+49 30 56837200",
22031
+ placeholder: regional.phonePlaceholder,
21985
22032
  $hasError: shouldShowError("phone", !!touched.phone, errors.phone)
21986
22033
  }
21987
22034
  ),
@@ -21992,7 +22039,7 @@ const RedesignContactForm = ({
21992
22039
  {
21993
22040
  name: "delivery_address",
21994
22041
  label: isGerman ? "Lieferadresse" : "Delivery address",
21995
- placeholder: isGerman ? "Straße, PLZ, Ort" : "Street, ZIP, City",
22042
+ placeholder: regional.addressPlaceholder,
21996
22043
  helperText: isGerman ? "Kannst du später jederzeit anpassen." : "You can adjust this later anytime.",
21997
22044
  component: AutocompleteAddress,
21998
22045
  required: true
@@ -22536,6 +22583,11 @@ const SubmitButton = styled.button`
22536
22583
  transform: translateY(-1px);
22537
22584
  }
22538
22585
  `;
22586
+ function pushToDataLayer(eventName, properties) {
22587
+ if (typeof window === "undefined") return;
22588
+ window.dataLayer = window.dataLayer || [];
22589
+ window.dataLayer.push({ event: eventName, ...properties });
22590
+ }
22539
22591
  const sessionSequence = /* @__PURE__ */ new Map();
22540
22592
  function getNextSequence(sessionId) {
22541
22593
  const current = sessionSequence.get(sessionId) || 0;
@@ -22556,12 +22608,7 @@ function captureEvent(eventName, properties) {
22556
22608
  console.error("[PostHog] Failed to send event:", error);
22557
22609
  }
22558
22610
  }
22559
- if (window.dataLayer) {
22560
- window.dataLayer.push({
22561
- event: eventName,
22562
- ...properties
22563
- });
22564
- }
22611
+ pushToDataLayer(eventName, properties);
22565
22612
  }
22566
22613
  function buildBaseProperties(context) {
22567
22614
  const properties = {
@@ -22674,18 +22721,7 @@ function trackOptimeleonConversion(eventName, serviceType) {
22674
22721
  }
22675
22722
  }
22676
22723
  function trackGtagConversion(eventName, params) {
22677
- if (typeof window === "undefined") return;
22678
- if (window.gtag) {
22679
- try {
22680
- window.gtag("event", eventName, {
22681
- ...params,
22682
- transport_type: "beacon",
22683
- send_to: getConfig().googleTagId || void 0
22684
- });
22685
- } catch (error) {
22686
- console.error("[gtag] Failed to send event:", error);
22687
- }
22688
- }
22724
+ pushToDataLayer(eventName, params);
22689
22725
  }
22690
22726
  const getCookie$1 = (name) => {
22691
22727
  const match2 = document.cookie.match(new RegExp("(^| )" + name + "=([^;]+)"));
@@ -22982,87 +23018,72 @@ function useFunnelCheckout() {
22982
23018
  funnelCheckout
22983
23019
  };
22984
23020
  }
23021
+ function countHeads(values2) {
23022
+ return (values2.number_of_vegans || 0) + (values2.number_of_vegetarians || 0) + (values2.number_of_carnivore || 0);
23023
+ }
23024
+ function buildAddress(values2) {
23025
+ return formatPlace(
23026
+ {
23027
+ country: values2.deliveryAddressCountry,
23028
+ city: values2.deliveryAddressCity,
23029
+ street: values2.deliveryAddressStreet,
23030
+ streetNumber: values2.deliveryAddressStreetNumber,
23031
+ postalCode: values2.deliveryAddressPostalCode
23032
+ },
23033
+ "inputString"
23034
+ );
23035
+ }
23036
+ function buildQfLeadAttributes(values2) {
23037
+ const numberOfPeople = countHeads(values2);
23038
+ return {
23039
+ firstName: values2.first_name,
23040
+ lastName: values2.last_name,
23041
+ email: values2.email,
23042
+ phone: values2.phone || "",
23043
+ company: values2.company,
23044
+ comment: values2.comment,
23045
+ address: buildAddress(values2),
23046
+ cateringCategories: values2.catering_categories,
23047
+ eventDate: values2.event_date ? new Date(values2.event_date).toDateString() : void 0,
23048
+ hasRecurringNeed: values2.has_recurring_need,
23049
+ isMultiDay: values2.is_multi_day,
23050
+ numberOfOmnivores: values2.number_of_carnivore,
23051
+ numberOfVegans: values2.number_of_vegans,
23052
+ numberOfVegetarians: values2.number_of_vegetarians,
23053
+ numberOfPeople,
23054
+ totalBudget: numberOfPeople * (values2.price_per_person || 0),
23055
+ leadType: values2.lead_type,
23056
+ serviceType: values2.service_type,
23057
+ teamSize: values2.team_size,
23058
+ deliveryFrequency: values2.delivery_frequency,
23059
+ budgetRange: values2.budget_range,
23060
+ leadSource: values2.leadSource,
23061
+ conversionUrl: values2.conversionUrl,
23062
+ entranceUrl: values2.entranceUrl,
23063
+ referrerUrl: values2.referrerUrl,
23064
+ landingPageSource: values2.landingPageSource
23065
+ };
23066
+ }
22985
23067
  function useQfLeadCreator() {
22986
23068
  const client = useClient();
22987
- const createQfLead = async (variables) => {
23069
+ const createQfLead = async (values2) => {
22988
23070
  try {
22989
- const {
22990
- company,
22991
- comment,
22992
- deliveryAddressCity,
22993
- deliveryAddressCountry,
22994
- deliveryAddressPostalCode,
22995
- deliveryAddressStreet,
22996
- deliveryAddressStreetNumber,
22997
- email,
22998
- first_name,
22999
- last_name,
23000
- phone,
23001
- catering_categories,
23002
- event_date,
23003
- has_recurring_need,
23004
- is_multi_day,
23005
- number_of_carnivore,
23006
- lead_type,
23007
- number_of_vegans,
23008
- number_of_vegetarians,
23009
- price_per_person,
23010
- service_type,
23011
- team_size,
23012
- delivery_frequency,
23013
- budget_range,
23014
- leadSource,
23015
- conversionUrl,
23016
- entranceUrl,
23017
- referrerUrl,
23018
- landingPageSource
23019
- } = variables;
23020
- const numberOfPeople = number_of_vegans + number_of_vegetarians + number_of_carnivore;
23021
- const {
23022
- data: { createQfLead: response }
23023
- } = await createQfLeadMutation(
23024
- {
23025
- email,
23026
- firstName: first_name,
23027
- lastName: last_name,
23028
- phone,
23029
- company,
23030
- comment,
23031
- address: `${deliveryAddressStreet} ${deliveryAddressStreetNumber}, ${deliveryAddressPostalCode} ${deliveryAddressCity}, ${deliveryAddressCountry}`,
23032
- cateringCategories: catering_categories,
23033
- eventDate: new Date(event_date).toDateString(),
23034
- hasRecurringNeed: has_recurring_need,
23035
- isMultiDay: is_multi_day,
23036
- numberOfOmnivores: number_of_carnivore,
23037
- numberOfVegans: number_of_vegans,
23038
- numberOfVegetarians: number_of_vegetarians,
23039
- numberOfPeople,
23040
- totalBudget: numberOfPeople * price_per_person,
23041
- leadType: lead_type,
23042
- serviceType: service_type,
23043
- teamSize: team_size,
23044
- deliveryFrequency: delivery_frequency,
23045
- budgetRange: budget_range,
23046
- leadSource,
23047
- conversionUrl,
23048
- entranceUrl,
23049
- referrerUrl,
23050
- landingPageSource
23051
- },
23071
+ const { data } = await createQfLeadMutation(
23072
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23073
+ buildQfLeadAttributes(values2),
23052
23074
  { client }
23053
23075
  );
23054
- return response;
23076
+ return data == null ? void 0 : data.createQfLead;
23055
23077
  } catch (error) {
23056
23078
  if (error) {
23057
23079
  BugsnagExport.notify(error, (event) => {
23058
- event.addMetadata("createQfLead", variables);
23080
+ event.addMetadata("createQfLead", values2);
23059
23081
  });
23060
23082
  }
23083
+ return void 0;
23061
23084
  }
23062
23085
  };
23063
- return {
23064
- createQfLead
23065
- };
23086
+ return { createQfLead };
23066
23087
  }
23067
23088
  const getCookie = (name) => {
23068
23089
  const match2 = document.cookie.match(new RegExp("(^| )" + name + "=([^;]+)"));
@@ -23390,7 +23411,12 @@ const RedesignedFunnel = ({
23390
23411
  trackFormSubmitted(funnelContextRef.current, metadata);
23391
23412
  trackCompleted(funnelContextRef.current);
23392
23413
  trackOptimeleonConversion("funnel_completed", serviceType);
23393
- trackGtagConversion("submit_qf_request");
23414
+ trackGtagConversion("submit_qf_request", {
23415
+ city: funnelContextRef.current.city,
23416
+ service_type: serviceType,
23417
+ funnel_mode: funnelContextRef.current.funnel_mode,
23418
+ funnel_variant: funnelContextRef.current.funnel_variant
23419
+ });
23394
23420
  }, [funnelContextRef]);
23395
23421
  const handleContactFormSubmit = useCallback(async (formData) => {
23396
23422
  if (isSubmittingRef.current) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heycater/qualification-funnel",
3
- "version": "1.19.37",
3
+ "version": "1.20.0",
4
4
  "type": "module",
5
5
  "description": "Heycater embedded qualification funnel widget",
6
6
  "main": "dist/index.cjs.js",