@embedreach/components 0.3.44 → 0.3.45

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.
@@ -4578,15 +4578,15 @@ function arrayRemove$2(array, item) {
4578
4578
  function removeLinks$2(items) {
4579
4579
  return items.filter((item) => item.tagName !== "A");
4580
4580
  }
4581
- var PORTAL_NAME$7 = "Portal";
4582
- var Portal$6 = React.forwardRef((props2, forwardedRef) => {
4581
+ var PORTAL_NAME$8 = "Portal";
4582
+ var Portal$8 = React.forwardRef((props2, forwardedRef) => {
4583
4583
  const { container: containerProp, ...portalProps } = props2;
4584
4584
  const [mounted, setMounted] = React.useState(false);
4585
4585
  useLayoutEffect2$7(() => setMounted(true), []);
4586
4586
  const container2 = containerProp || mounted && globalThis?.document?.body;
4587
4587
  return container2 ? ReactDOM__default.createPortal(/* @__PURE__ */ jsx(Primitive$9.div, { ...portalProps, ref: forwardedRef }), container2) : null;
4588
4588
  });
4589
- Portal$6.displayName = PORTAL_NAME$7;
4589
+ Portal$8.displayName = PORTAL_NAME$8;
4590
4590
  function useStateMachine$7(initialState2, machine) {
4591
4591
  return React.useReducer((state, event) => {
4592
4592
  const nextState = machine[state][event];
@@ -5566,16 +5566,16 @@ var DialogTrigger$1 = React.forwardRef(
5566
5566
  }
5567
5567
  );
5568
5568
  DialogTrigger$1.displayName = TRIGGER_NAME$5;
5569
- var PORTAL_NAME$6 = "DialogPortal";
5570
- var [PortalProvider$2, usePortalContext$2] = createDialogContext(PORTAL_NAME$6, {
5569
+ var PORTAL_NAME$7 = "DialogPortal";
5570
+ var [PortalProvider$2, usePortalContext$2] = createDialogContext(PORTAL_NAME$7, {
5571
5571
  forceMount: void 0
5572
5572
  });
5573
5573
  var DialogPortal$1 = (props2) => {
5574
5574
  const { __scopeDialog, forceMount, children, container: container2 } = props2;
5575
- const context2 = useDialogContext(PORTAL_NAME$6, __scopeDialog);
5576
- return /* @__PURE__ */ jsx(PortalProvider$2, { scope: __scopeDialog, forceMount, children: React.Children.map(children, (child) => /* @__PURE__ */ jsx(Presence$6, { present: forceMount || context2.open, children: /* @__PURE__ */ jsx(Portal$6, { asChild: true, container: container2, children: child }) })) });
5575
+ const context2 = useDialogContext(PORTAL_NAME$7, __scopeDialog);
5576
+ return /* @__PURE__ */ jsx(PortalProvider$2, { scope: __scopeDialog, forceMount, children: React.Children.map(children, (child) => /* @__PURE__ */ jsx(Presence$6, { present: forceMount || context2.open, children: /* @__PURE__ */ jsx(Portal$8, { asChild: true, container: container2, children: child }) })) });
5577
5577
  };
5578
- DialogPortal$1.displayName = PORTAL_NAME$6;
5578
+ DialogPortal$1.displayName = PORTAL_NAME$7;
5579
5579
  var OVERLAY_NAME = "DialogOverlay";
5580
5580
  var DialogOverlay$1 = React.forwardRef(
5581
5581
  (props2, forwardedRef) => {
@@ -5802,7 +5802,7 @@ var DescriptionWarning = ({ contentRef, descriptionId }) => {
5802
5802
  };
5803
5803
  var Root$d = Dialog$1;
5804
5804
  var Trigger$4 = DialogTrigger$1;
5805
- var Portal$5 = DialogPortal$1;
5805
+ var Portal$7 = DialogPortal$1;
5806
5806
  var Overlay = DialogOverlay$1;
5807
5807
  var Content$4 = DialogContent$1;
5808
5808
  var Title$1 = DialogTitle$1;
@@ -5813,7 +5813,7 @@ function cn$1(...inputs) {
5813
5813
  }
5814
5814
  const Dialog = Root$d;
5815
5815
  const DialogTrigger = Trigger$4;
5816
- const DialogPortal = ({ ...props2 }) => /* @__PURE__ */ jsx(Portal$5, { children: /* @__PURE__ */ jsx("div", { "data-reach-root": true, children: props2.children }) });
5816
+ const DialogPortal = ({ ...props2 }) => /* @__PURE__ */ jsx(Portal$7, { children: /* @__PURE__ */ jsx("div", { "data-reach-root": true, children: props2.children }) });
5817
5817
  const DialogOverlay = React.forwardRef(({ className, ...props2 }, ref) => /* @__PURE__ */ jsx(
5818
5818
  Overlay,
5819
5819
  {
@@ -5912,7 +5912,7 @@ const DialogVisuallyHidden = React.forwardRef(({ className, children, ...props2
5912
5912
  DialogVisuallyHidden.displayName = "DialogVisuallyHidden";
5913
5913
  const Sheet = Root$d;
5914
5914
  const SheetTrigger = Trigger$4;
5915
- const SheetPortal = Portal$5;
5915
+ const SheetPortal = Portal$7;
5916
5916
  const SheetOverlay = React.forwardRef(({ className, ...props2 }, ref) => /* @__PURE__ */ jsx(
5917
5917
  Overlay,
5918
5918
  {
@@ -16850,7 +16850,7 @@ var me$2 = React.forwardRef((r2, o2) => {
16850
16850
  }, []), React.createElement(Primitive$7.div, { ref: G$2([d4, o2]), ...c3, "cmdk-list": "", role: "listbox", "aria-label": u3, id: p2.listId }, j$3(r2, (v2) => React.createElement("div", { ref: G$2([f2, p2.listInnerRef]), "cmdk-list-sizer": "" }, v2)));
16851
16851
  }), Pe$2 = React.forwardRef((r2, o2) => {
16852
16852
  let { open: t3, onOpenChange: u3, overlayClassName: c3, contentClassName: d4, container: f2, ...p2 } = r2;
16853
- return React.createElement(Root$d, { open: t3, onOpenChange: u3 }, React.createElement(Portal$5, { container: f2 }, React.createElement(Overlay, { "cmdk-overlay": "", className: c3 }), React.createElement(Content$4, { "aria-label": r2.label, "cmdk-dialog": "", className: d4 }, React.createElement(me$2, { ref: o2, ...p2 }))));
16853
+ return React.createElement(Root$d, { open: t3, onOpenChange: u3 }, React.createElement(Portal$7, { container: f2 }, React.createElement(Overlay, { "cmdk-overlay": "", className: c3 }), React.createElement(Content$4, { "aria-label": r2.label, "cmdk-dialog": "", className: d4 }, React.createElement(me$2, { ref: o2, ...p2 }))));
16854
16854
  }), we$2 = React.forwardRef((r2, o2) => T$2((u3) => u3.filtered.count === 0) ? React.createElement(Primitive$7.div, { ref: o2, ...r2, "cmdk-empty": "", role: "presentation" }) : null), De$2 = React.forwardRef((r2, o2) => {
16855
16855
  let { progress: t3, children: u3, label: c3 = "Loading...", ...d4 } = r2;
16856
16856
  return React.createElement(Primitive$7.div, { ref: o2, ...d4, "cmdk-loading": "", role: "progressbar", "aria-valuenow": t3, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": c3 }, j$3(r2, (f2) => React.createElement("div", { "aria-hidden": true }, f2)));
@@ -19662,6 +19662,15 @@ var Root2$6 = Popper$2;
19662
19662
  var Anchor$2 = PopperAnchor$2;
19663
19663
  var Content$3 = PopperContent$2;
19664
19664
  var Arrow$4 = PopperArrow$2;
19665
+ var PORTAL_NAME$6 = "Portal";
19666
+ var Portal$6 = React.forwardRef((props2, forwardedRef) => {
19667
+ const { container: containerProp, ...portalProps } = props2;
19668
+ const [mounted, setMounted] = React.useState(false);
19669
+ useLayoutEffect2$6(() => setMounted(true), []);
19670
+ const container2 = containerProp || mounted && globalThis?.document?.body;
19671
+ return container2 ? ReactDOM__default.createPortal(/* @__PURE__ */ jsx(Primitive$7.div, { ...portalProps, ref: forwardedRef }), container2) : null;
19672
+ });
19673
+ Portal$6.displayName = PORTAL_NAME$6;
19665
19674
  function useStateMachine$6(initialState2, machine) {
19666
19675
  return React.useReducer((state, event) => {
19667
19676
  const nextState = machine[state][event];
@@ -19908,6 +19917,12 @@ var PORTAL_NAME$5 = "PopoverPortal";
19908
19917
  var [PortalProvider$1, usePortalContext$1] = createPopoverContext(PORTAL_NAME$5, {
19909
19918
  forceMount: void 0
19910
19919
  });
19920
+ var PopoverPortal = (props2) => {
19921
+ const { __scopePopover, forceMount, children, container: container2 } = props2;
19922
+ const context2 = usePopoverContext(PORTAL_NAME$5, __scopePopover);
19923
+ return /* @__PURE__ */ jsx(PortalProvider$1, { scope: __scopePopover, forceMount, children: /* @__PURE__ */ jsx(Presence$5, { present: forceMount || context2.open, children: /* @__PURE__ */ jsx(Portal$6, { asChild: true, container: container2, children }) }) });
19924
+ };
19925
+ PopoverPortal.displayName = PORTAL_NAME$5;
19911
19926
  var CONTENT_NAME$6 = "PopoverContent";
19912
19927
  var PopoverContent$1 = React.forwardRef(
19913
19928
  (props2, forwardedRef) => {
@@ -20092,6 +20107,7 @@ function getState$3(open) {
20092
20107
  }
20093
20108
  var Root2$5 = Popover$1;
20094
20109
  var Trigger$3 = PopoverTrigger$1;
20110
+ var Portal$5 = PopoverPortal;
20095
20111
  var Content2$2 = PopoverContent$1;
20096
20112
  const Popover = Root2$5;
20097
20113
  const PopoverTrigger = Trigger$3;
@@ -34463,6 +34479,24 @@ const updateBusinessUiDefaultProperty = async (key, value) => {
34463
34479
  const toggleEmailHelpDialog = async (show) => {
34464
34480
  return updateBusinessUiDefaultProperty("emailHelpDialogShow", show);
34465
34481
  };
34482
+ const updateIntegrationWebhookSecret = async (integrationType, secret) => {
34483
+ const response = await baseRequest(BUSINESS_PATH, {
34484
+ method: "PATCH",
34485
+ body: JSON.stringify({
34486
+ integrationWebhookSecret: { integrationType, secret }
34487
+ })
34488
+ });
34489
+ return response.data;
34490
+ };
34491
+ const removeIntegrationWebhookSecret = async (integrationType) => {
34492
+ const response = await baseRequest(
34493
+ `${BUSINESS_PATH}/integration-webhook-secret/${integrationType}`,
34494
+ {
34495
+ method: "DELETE"
34496
+ }
34497
+ );
34498
+ return response.data;
34499
+ };
34466
34500
  const reputationCommunicationGroupsQueryKey = [
34467
34501
  "reputation",
34468
34502
  "communication-groups"
@@ -34607,6 +34641,76 @@ const useBusiness$1 = () => {
34607
34641
  });
34608
34642
  }
34609
34643
  });
34644
+ const updateCallRailWebhookSecretMutation = useMutation({
34645
+ mutationFn: (secret) => updateIntegrationWebhookSecret("callrail", secret),
34646
+ onSuccess: (updatedBusiness) => {
34647
+ queryClient.setQueryData(meQueryKey, updatedBusiness);
34648
+ queryClient.invalidateQueries({ queryKey: meQueryKey });
34649
+ toast2({
34650
+ title: "CallRail secret saved successfully",
34651
+ description: "Your CallRail webhook is now connected."
34652
+ });
34653
+ },
34654
+ onError: () => {
34655
+ toast2({
34656
+ title: "Failed to save CallRail secret",
34657
+ description: "Please try again",
34658
+ variant: "destructive"
34659
+ });
34660
+ }
34661
+ });
34662
+ const removeCallRailWebhookSecretMutation = useMutation({
34663
+ mutationFn: () => removeIntegrationWebhookSecret("callrail"),
34664
+ onSuccess: async () => {
34665
+ await queryClient.invalidateQueries({ queryKey: meQueryKey });
34666
+ toast2({
34667
+ title: "CallRail secret removed successfully",
34668
+ description: "Your CallRail webhook connection has been removed."
34669
+ });
34670
+ },
34671
+ onError: () => {
34672
+ toast2({
34673
+ title: "Failed to remove CallRail secret",
34674
+ description: "Please try again",
34675
+ variant: "destructive"
34676
+ });
34677
+ }
34678
+ });
34679
+ const updateCtmWebhookSecretMutation = useMutation({
34680
+ mutationFn: (secret) => updateIntegrationWebhookSecret("ctm", secret),
34681
+ onSuccess: (updatedBusiness) => {
34682
+ queryClient.setQueryData(meQueryKey, updatedBusiness);
34683
+ queryClient.invalidateQueries({ queryKey: meQueryKey });
34684
+ toast2({
34685
+ title: "CTM secret saved successfully",
34686
+ description: "Copy the secret below - it will not be shown again."
34687
+ });
34688
+ },
34689
+ onError: () => {
34690
+ toast2({
34691
+ title: "Failed to save CTM secret",
34692
+ description: "Please try again",
34693
+ variant: "destructive"
34694
+ });
34695
+ }
34696
+ });
34697
+ const removeCtmWebhookSecretMutation = useMutation({
34698
+ mutationFn: () => removeIntegrationWebhookSecret("ctm"),
34699
+ onSuccess: async () => {
34700
+ await queryClient.invalidateQueries({ queryKey: meQueryKey });
34701
+ toast2({
34702
+ title: "CTM secret removed successfully",
34703
+ description: "Your CTM webhook connection has been removed."
34704
+ });
34705
+ },
34706
+ onError: () => {
34707
+ toast2({
34708
+ title: "Failed to remove CTM secret",
34709
+ description: "Please try again",
34710
+ variant: "destructive"
34711
+ });
34712
+ }
34713
+ });
34610
34714
  return {
34611
34715
  data: meQuery.data,
34612
34716
  isLoading: meQuery.isLoading,
@@ -34633,7 +34737,19 @@ const useBusiness$1 = () => {
34633
34737
  isUpdatingBusinessUiDefaults: updateBusinessUiDefaultsMutation.isPending,
34634
34738
  isUpdatingBusinessUiDefaultProperty: updateBusinessUiDefaultPropertyMutation.isPending,
34635
34739
  isTogglingEmailHelpDialog: toggleEmailHelpDialogMutation.isPending,
34636
- isTogglingEngageOnboarding: toggleEngageOnboardingMutation.isPending
34740
+ isTogglingEngageOnboarding: toggleEngageOnboardingMutation.isPending,
34741
+ updateCallRailWebhookSecret: updateCallRailWebhookSecretMutation.mutate,
34742
+ updateCallRailWebhookSecretAsync: updateCallRailWebhookSecretMutation.mutateAsync,
34743
+ isUpdatingCallRailSecret: updateCallRailWebhookSecretMutation.isPending,
34744
+ removeCallRailWebhookSecret: removeCallRailWebhookSecretMutation.mutate,
34745
+ removeCallRailWebhookSecretAsync: removeCallRailWebhookSecretMutation.mutateAsync,
34746
+ isRemovingCallRailSecret: removeCallRailWebhookSecretMutation.isPending,
34747
+ updateCtmWebhookSecret: updateCtmWebhookSecretMutation.mutate,
34748
+ updateCtmWebhookSecretAsync: updateCtmWebhookSecretMutation.mutateAsync,
34749
+ isUpdatingCtmSecret: updateCtmWebhookSecretMutation.isPending,
34750
+ removeCtmWebhookSecret: removeCtmWebhookSecretMutation.mutate,
34751
+ removeCtmWebhookSecretAsync: removeCtmWebhookSecretMutation.mutateAsync,
34752
+ isRemovingCtmSecret: removeCtmWebhookSecretMutation.isPending
34637
34753
  };
34638
34754
  };
34639
34755
  const websiteContentKeys = {
@@ -45989,6 +46105,12 @@ const dashboardFilterReducer = (state, action) => {
45989
46105
  ...state,
45990
46106
  tabCanUseTransactionAttribution: action.payload
45991
46107
  };
46108
+ case "SET_ACTIVE_TAB":
46109
+ return { ...state, activeTab: action.payload };
46110
+ case "SET_LEAD_STATUSES":
46111
+ return { ...state, leadStatuses: action.payload };
46112
+ case "SET_IDENTIFICATION_SOURCES":
46113
+ return { ...state, identificationSources: action.payload };
45992
46114
  default:
45993
46115
  return state;
45994
46116
  }
@@ -46011,11 +46133,21 @@ const updateTabCanUseTransactionAttribution = (dispatch2) => (tabCanUseTransacti
46011
46133
  payload: tabCanUseTransactionAttribution
46012
46134
  });
46013
46135
  };
46136
+ const updateActiveTab = (dispatch2) => (activeTab) => {
46137
+ dispatch2({ type: "SET_ACTIVE_TAB", payload: activeTab });
46138
+ };
46139
+ const updateLeadStatuses = (dispatch2) => (leadStatuses) => {
46140
+ dispatch2({ type: "SET_LEAD_STATUSES", payload: leadStatuses });
46141
+ };
46142
+ const updateIdentificationSources = (dispatch2) => (sources) => {
46143
+ dispatch2({ type: "SET_IDENTIFICATION_SOURCES", payload: sources });
46144
+ };
46014
46145
  const defaultState$1 = {
46015
46146
  dateRange: void 0,
46016
46147
  channel: void 0,
46017
46148
  campaignId: void 0,
46018
- attributionStyle: DateAttributionStyle.CLICK
46149
+ attributionStyle: DateAttributionStyle.CLICK,
46150
+ activeTab: "overview"
46019
46151
  };
46020
46152
  createDataContext(
46021
46153
  dashboardFilterReducer,
@@ -46024,7 +46156,10 @@ createDataContext(
46024
46156
  updateChannel,
46025
46157
  updateCampaignId,
46026
46158
  updateAttributionStyle,
46027
- updateTabCanUseTransactionAttribution
46159
+ updateTabCanUseTransactionAttribution,
46160
+ updateActiveTab,
46161
+ updateLeadStatuses,
46162
+ updateIdentificationSources
46028
46163
  },
46029
46164
  defaultState$1
46030
46165
  );
@@ -48719,6 +48854,24 @@ const measure$1 = {
48719
48854
  new_customers_description: "The number of first-time customers acquired through your advertising campaigns during the selected period. \n\nThis metric specifically tracks customers who have never purchased from your business before and made their first purchase after interacting with your ads.",
48720
48855
  new_leads: "New Leads",
48721
48856
  new_leads_description: "The number of new potential customers who provided their contact information through your advertising campaigns during the selected period. \n\nLeads are typically generated through form submissions, newsletter signups, download requests, or other lead magnets promoted in your ads.",
48857
+ open_leads: "Open Leads",
48858
+ open_leads_description: "The number of leads who have not yet made a purchase and have had activity within the last 90 days. These are active leads that are still in your pipeline.",
48859
+ converted_leads: "Converted Leads",
48860
+ converted_leads_description: "The number of leads who have made at least one purchase. These leads have successfully converted into paying customers.",
48861
+ stale_leads: "Stale Leads",
48862
+ stale_leads_description: "Leads with no activity (no new identifications, visits, or purchases) for more than 90 days. Consider re-engagement campaigns for these leads.",
48863
+ leads_from_ads: "Leads From Ads",
48864
+ leads_from_ads_description: "The number of new leads attributed to paid advertising during the selected period. \n\nA lead is counted when a visitor provides contact information after interacting with your ads.",
48865
+ cost_per_lead: "Cost Per Lead",
48866
+ cost_per_lead_description: "The average amount spent to acquire each new lead from ads, calculated by dividing ad spend by leads from ads.",
48867
+ lead_to_purchase_rate: "Lead to Purchase Rate",
48868
+ lead_to_purchase_rate_description: "The percentage of leads who went on to make a purchase during the selected period.",
48869
+ total_value_of_open_leads: "Est. Value of Open Leads",
48870
+ total_value_of_open_leads_description: "Estimated potential revenue from leads who have not yet converted. Calculated by multiplying the number of open (unconverted) leads by the average revenue per converted lead. This is a projection, not actual revenue.",
48871
+ total_value_of_converted_leads: "Total Value of Converted Leads",
48872
+ total_value_of_converted_leads_description: "The total revenue from all purchases made by leads who converted during the selected period.",
48873
+ average_lead_value: "Average Lead Value",
48874
+ average_lead_value_description: "The average revenue per converted lead, calculated by dividing converted lead revenue by the number of converted leads.",
48722
48875
  reengaged_customers: "Reengaged Customers",
48723
48876
  reengaged_customers_description: "The number of previously inactive customers who made a purchase after being targeted by your advertising campaigns during the selected period. \n\nThese are customers who had purchased from your business before but had been dormant for a significant period.",
48724
48877
  revenue_per_click: "Revenue Per Click",
@@ -48749,6 +48902,9 @@ const measure$1 = {
48749
48902
  not_running_paid_ads: "Not running paid ads? Use AI to create a Google Ads campaign",
48750
48903
  privacy_policy: "Privacy Policy",
48751
48904
  quick_links: "Quick Links",
48905
+ quick_links_and_integrations: "Quick Links + Integrations",
48906
+ connect_callrail: "Connect CallRail",
48907
+ connect_ctm: "Connect CTM",
48752
48908
  setup_tracking_pixel: "Setup tracking pixel",
48753
48909
  understand_attribution_model: "Understand our attribution model",
48754
48910
  update_ad_campaign_settings: "Update ad campaign settings",
@@ -48853,6 +49009,11 @@ For example: If someone clicks your Google ad, we'll show "Google Ads" even if t
48853
49009
  utm_medium_description: "Revenue grouped by the marketing medium from your tracking URLs. If you add UTM parameters to your links (like ?utm_medium=email or ?utm_medium=cpc), this shows which types of marketing efforts drive the most sales. Helps you understand if email campaigns convert better than paid ads.",
48854
49010
  referrer_description: "Revenue grouped by the website that sent visitors to you before they made a purchase. This shows the actual domains like google.com, facebook.com, or partner websites that are driving sales. Useful for discovering unexpected traffic sources or measuring the value of partnerships."
48855
49011
  }
49012
+ },
49013
+ leads: {
49014
+ title: "Leads",
49015
+ prototype_banner_title: "New feature — we're still optimizing",
49016
+ prototype_banner_description: "This experience is actively being improved. We'd love your feedback to help us get it right."
48856
49017
  }
48857
49018
  }
48858
49019
  },
@@ -48864,6 +49025,75 @@ For example: If someone clicks your Google ad, we'll show "Google Ads" even if t
48864
49025
  platform_settings: "{{provider}} Settings",
48865
49026
  disconnect_account_description: "Remove connection to your {{provider}} account",
48866
49027
  finalizing_connection: "Finalizing connection..."
49028
+ },
49029
+ callrail: {
49030
+ connect: "Connect Account",
49031
+ connected: "Connected",
49032
+ connected_description: "CallRail is connected and receiving webhook events.",
49033
+ setup: {
49034
+ title: "Connect CallRail",
49035
+ description: "Follow these steps to connect your CallRail account and start tracking phone calls.",
49036
+ step_1_title: "Step 1: Copy the webhook URL",
49037
+ step_1_description: "Copy the webhook URL below. You'll need to paste this into CallRail in the next step.",
49038
+ step_2_title: "Step 2: Add webhook in CallRail",
49039
+ step_2_1: "Log in to your CallRail account",
49040
+ step_2_2: "Go to Settings → Integrations → Webhooks",
49041
+ step_2_3: "Scroll down to the Post-Call section.",
49042
+ step_2_4: 'Paste the webhook URL from Step 1 into the "Webhook URL" field',
49043
+ step_2_5: "Scroll to the bottom of the page and click Update.",
49044
+ step_3_title: "Step 3: Get your Webhook Signature Secret Token",
49045
+ step_3_description: 'Above the Update button you will see "Advanced Settings >". Click on this to expand the section. Here you will see Webhook Signature Secret Token. Copy the secret you are shown here.',
49046
+ step_4_title: "Step 4: Enter your Webhook Signature Secret Token",
49047
+ step_4_description: "Paste your CallRail Webhook Signature Secret Token below to complete the connection.",
49048
+ secret_label: "Webhook Signature Secret Token",
49049
+ secret_placeholder: "Paste your Webhook Signature Secret Token here",
49050
+ change_token: "Change",
49051
+ close: "Close",
49052
+ remove: "Remove",
49053
+ confirm_remove: "Are you sure you want to remove the CallRail connection? This will stop receiving webhook events.",
49054
+ copy: "Copy",
49055
+ copied: "Copied",
49056
+ cancel: "Cancel",
49057
+ save: "Save & Connect",
49058
+ saving: "Saving..."
49059
+ }
49060
+ },
49061
+ ctm: {
49062
+ connected_status: "CTM is connected and receiving webhook events.",
49063
+ setup: {
49064
+ title: "Connect CTM",
49065
+ description: "Follow these steps to connect your CTM account and start tracking phone calls.",
49066
+ step_1_title: "Step 1: Get your Secret Key from CTM",
49067
+ step_1_description: 'Log in to your CTM account and navigate to Account Settings → Api Integration. Copy your "Secret Key" from that page.',
49068
+ step_2_title: "Step 2: Paste your Secret Key",
49069
+ step_2_description: "Paste the Secret Key from CTM below. This allows us to verify that webhook requests are genuinely from CTM.",
49070
+ step_3_title: "Step 3: Copy the webhook URL",
49071
+ step_3_description: "Copy the webhook URL below. You'll paste this into CTM in Step 4.",
49072
+ step_4_title: "Step 4: Configure webhook in CTM",
49073
+ step_4_description: "Follow these steps to configure the webhook in your CTM account:",
49074
+ step_4_1: "Log in to your CTM account",
49075
+ step_4_2: "Click on Settings (in side navigation)",
49076
+ step_4_3: "Under Configuration, click on Webhooks",
49077
+ step_4_4: "Click New Webhook button",
49078
+ step_4_5: "Enter a name for your webhook (e.g., 'Ads Call Tracking')",
49079
+ step_4_6: "Under Trigger, select 'When a call ends immediately without delay'",
49080
+ step_4_7: "Paste the webhook URL from Step 3 into the Callback URL field",
49081
+ step_4_8: "Select 'Log Data' for Request Body Type",
49082
+ step_4_9: "Click Save Changes",
49083
+ ready_message: "You're all set! CTM will now start sending webhook events, and your phone calls will be tracked automatically.",
49084
+ secret_saved: "Your CTM Secret Key is saved. To update it, enter a new key below:",
49085
+ secret_label: "CTM Secret Key",
49086
+ secret_placeholder: "Paste your CTM Secret Key here...",
49087
+ save: "Save",
49088
+ saving: "Saving...",
49089
+ update: "Update",
49090
+ remove: "Remove",
49091
+ cancel: "Cancel",
49092
+ close: "Close",
49093
+ copy: "Copy",
49094
+ copied: "Copied",
49095
+ confirm_remove: "Are you sure you want to remove the CTM connection? This will stop receiving webhook events."
49096
+ }
48867
49097
  }
48868
49098
  };
48869
49099
  const authentication$1 = {
@@ -48931,7 +49161,7 @@ const analytics$1 = {
48931
49161
  copied_to_clipboard: "Copied to clipboard",
48932
49162
  copied_to_clipboard_description: "The tracking pixel code has been copied to your clipboard.",
48933
49163
  title: "Install our Tracking Pixel",
48934
- description: "In order to get a complete picture of how users are interacting with your website, it’s important for the Reach analytics code to be on every page of your website and within as many third-party tools that you use as possible.",
49164
+ description: "In order to get a complete picture of how users are interacting with your website, it’s important for the analytics code to be on every page of your website and within as many third-party tools that you use as possible.",
48935
49165
  card_title: "Install the Snippet inside the <HEAD> of every page",
48936
49166
  third_party_pages_your_tracking_pixel: "Your Tracking Pixel:",
48937
49167
  card_description: "Copy this following text and insert it inside of the <HEAD> tags on all pages.",
@@ -48950,7 +49180,7 @@ const analytics$1 = {
48950
49180
  },
48951
49181
  third_party_installation_guides: {
48952
49182
  title: "Installation Guides",
48953
- description: "These guides will help you add the Reach analytics snippet to your website. The process works with any website builder or platform and most third-party tools.",
49183
+ description: "These guides will help you add the analytics snippet to your website. The process works with any website builder or platform and most third-party tools.",
48954
49184
  installation_guide: "{{name}} Installation Guide",
48955
49185
  official_guide: "Official Guide",
48956
49186
  shopify: {
@@ -48961,18 +49191,20 @@ const analytics$1 = {
48961
49191
  step_5: "Paste the code snippet from the section above just before it"
48962
49192
  },
48963
49193
  google_tag_manager: {
48964
- step_1: "Go to https://tagmanager.google.com",
48965
- step_2: "Sign in to your Google Account",
48966
- step_3: 'In the Tags tab, click the "New" button to create a new tag',
48967
- step_4: 'Choose "Custom HTML" as the tag type',
48968
- step_5: "Paste the tracking code above into the HTML field",
48969
- step_6: "Click anywhere in the triggering section to edit this setting",
48970
- step_7: 'Select "Initialization - All Pages" as the trigger',
48971
- step_8: "Click Save to save the tag",
48972
- step_9: "Click Submit in the top right",
48973
- step_10: "Add a version name and description",
48974
- step_11: "Click Publish to deploy your changes",
48975
- special_note: "Make sure you have the Google Tag Manager container code already installed on your site before adding this tag."
49194
+ step_1: "Go to tagmanager.google.com and sign in with your Google account",
49195
+ step_2: "Select the container for your website (or create one if you haven't already)",
49196
+ step_3: "Important: Make sure your GTM container code is already installed on your website. If not, follow Google's installation instructions first, then return here.",
49197
+ step_3_link: "https://support.google.com/tagmanager/answer/6103696",
49198
+ step_4: 'In the left sidebar, click "Tags", then click the "New" button in the top right',
49199
+ step_5: 'Give your tag a name at the top (e.g., "Source Attribution Pixel")',
49200
+ step_6: 'Click anywhere in the "Tag Configuration" box, then select "Custom HTML" from the tag type menu',
49201
+ step_7: "Paste the tracking code from above into the HTML field (the code already includes the required <script> tags)",
49202
+ step_8: 'Click anywhere in the "Triggering" box below the tag configuration',
49203
+ step_9: 'Select "Initialization - All Pages" to ensure the pixel loads before other tags (recommended), or "All Pages" for standard page load timing',
49204
+ step_10: 'Click "Save" in the top right corner to save your tag',
49205
+ step_11: 'Click "Submit" in the top right corner of the workspace',
49206
+ step_12: 'Add a version name (e.g., "Added source attribution pixel") and click "Publish" to deploy your changes',
49207
+ special_note: "Google Tag Manager works with any website platform. If you already have GTM installed on your site, you can use this method instead of adding code directly to your website's HTML."
48976
49208
  },
48977
49209
  hubspot: {
48978
49210
  step_1: "Go to Settings → Content → Pages",
@@ -48981,18 +49213,30 @@ const analytics$1 = {
48981
49213
  step_4: 'Click "Save"'
48982
49214
  },
48983
49215
  wordpress: {
48984
- step_1: "We recommend using the popular WPCode Plugin",
48985
- step_2: 'In the plugin, select "+ Add Snippet"',
48986
- step_3: 'When presented with a menu of options, navigate to "Add Your Custom Code (New Snippet)" and select "Use snippet"',
48987
- step_4: "Paste the code snippet from the section above",
48988
- step_5: 'For Insert Method, choose "Auto Insert"',
48989
- step_6: 'For Location, select "Site Wide Header"',
48990
- step_7: "Be sure to save your snippet"
49216
+ intro: "WordPress sites can add tracking code to the header using various plugins or theme settings. Find your method below and paste the tracking code into the Header section.",
49217
+ methods: [
49218
+ {
49219
+ id: "wpcode",
49220
+ name: "WPCode",
49221
+ description: 'Code Snippets Header & Footer → paste in "Header" section → Save Changes',
49222
+ link: "https://wpcode.com/docs/using-the-global-header-footer-settings/"
49223
+ },
49224
+ {
49225
+ id: "elementor",
49226
+ name: "Elementor",
49227
+ description: 'Elementor → Custom Code → Add New → set location to "<head>" → set conditions to "Entire Site" → Publish',
49228
+ link: "https://elementor.com/help/custom-code-pro/"
49229
+ }
49230
+ ],
49231
+ special_note: "Important: After adding tracking code, clear your WordPress cache if you use a caching plugin (WP Rocket, LiteSpeed Cache, W3 Total Cache, Cloudflare, etc.). The code won't appear on your site until the cache is cleared.\n\nIf you're using a different plugin for custom JavaScript or code injection, check your WordPress Plugins section to identify the plugin and follow that plugin's specific instructions for adding code to the header."
48991
49232
  },
48992
49233
  squarespace: {
48993
- step_1: "Go to Settings Advanced Code Injection",
48994
- step_2: 'Paste the code from the section above into the "Header" field',
48995
- step_3: 'Click "Save"'
49234
+ step_1: "Sign in to your Squarespace account at account.squarespace.com",
49235
+ step_2: 'On the Dashboard, click the "Website" button for the site you want to modify',
49236
+ step_3: 'From the main menu, click "Pages" under the "Website" section',
49237
+ step_4: 'Scroll to the bottom and click "Custom Code", then click "Code Injection". Note: on older versions of Squarespace, "Custom Code" may be labeled "Website Tools"',
49238
+ step_5: 'Under "Header", paste the tracking code from above and click "Save"',
49239
+ special_note: "Code Injection requires a Squarespace Business or Commerce plan. If you don't see the Code Injection option, you may need to upgrade your plan."
48996
49240
  },
48997
49241
  wix: {
48998
49242
  step_1: "Go to Settings → Custom Code",
@@ -49314,7 +49558,7 @@ const oauth$1 = {
49314
49558
  fetchAccounts: "Fetching accounts...",
49315
49559
  noAccountSelected: "No account selected",
49316
49560
  enableTrackingParams: "Enable Tracking Parameters",
49317
- enableTrackingParamsDescription: "Append the reach campaign id parameter to your ad platform tracking settings. We will not overwrite any existing tracking parameters. Required for accurate tracking.",
49561
+ enableTrackingParamsDescription: "Append the 'ER' campaign id parameter to your ad platform tracking settings. We will not overwrite any existing tracking parameters. Required for accurate tracking.",
49318
49562
  google: {
49319
49563
  selectAccountDescription: "Select the Google Ads account you want to connect to view your data. These will be linked to our MCC for optimal tracking.",
49320
49564
  mccAccounts: "MCC Accounts",
@@ -49612,6 +49856,22 @@ const measure = {
49612
49856
  new_customers_description: "El número de nuevos clientes adquiridos a través de tus campañas de anuncios durante el período seleccionado. \n\nEsta métrica rastrea clientes que nunca han comprado de tu negocio antes y han realizado su primera compra después de interactuar con tus anuncios.",
49613
49857
  new_leads: "Nuevos leads",
49614
49858
  new_leads_description: "El número de nuevos clientes potenciales que proporcionaron su información de contacto a través de tus campañas de anuncios durante el período seleccionado. \n\nLos leads son típicamente generados a través de envíos de formularios, suscripciones a newsletters, solicitudes de descarga, o otros magnetos de leads promovidos en tus anuncios.",
49859
+ open_leads: "Leads abiertos",
49860
+ open_leads_description: "El número de leads que aún no han realizado una compra y han tenido actividad en los últimos 90 días. Estos son leads activos que aún están en tu pipeline.",
49861
+ converted_leads: "Leads convertidos",
49862
+ converted_leads_description: "El número de leads que han realizado al menos una compra. Estos leads se han convertido exitosamente en clientes de pago.",
49863
+ stale_leads: "Leads inactivos",
49864
+ stale_leads_description: "Leads sin actividad (sin nuevas identificaciones, visitas o compras) por más de 90 días. Considera campañas de reactivación para estos leads.",
49865
+ leads_from_ads: "Leads de anuncios",
49866
+ leads_from_ads_description: "El número de nuevos leads atribuidos a anuncios pagos durante el período seleccionado. \n\nUn lead se cuenta cuando un visitante proporciona su información de contacto después de interactuar con tus anuncios.",
49867
+ cost_per_lead: "Costo por lead",
49868
+ cost_per_lead_description: "El costo promedio para adquirir cada nuevo lead desde anuncios, calculado dividiendo el gasto en anuncios entre los leads de anuncios.",
49869
+ lead_to_purchase_rate: "Tasa de lead a compra",
49870
+ lead_to_purchase_rate_description: "El porcentaje de leads que realizaron una compra durante el período seleccionado.",
49871
+ total_value_of_converted_leads: "Valor total de leads convertidos",
49872
+ total_value_of_converted_leads_description: "El total de ingresos de todas las compras realizadas por leads que se convirtieron durante el período seleccionado.",
49873
+ average_lead_value: "Valor promedio por lead",
49874
+ average_lead_value_description: "El ingreso promedio por lead convertido, calculado dividiendo el ingreso de leads convertidos entre la cantidad de leads convertidos.",
49615
49875
  reengaged_customers: "Clientes reactivados",
49616
49876
  reengaged_customers_description: "El número de clientes anteriores que hicieron una compra después de ser targetizados por tus campañas de anuncios durante el período seleccionado. \n\nEstos son clientes que habían comprado de tu negocio antes pero habían estado inactivos durante un período significativo.",
49617
49877
  revenue_per_click: "Ingresos por clic",
@@ -53967,14 +54227,18 @@ const automationKeys = {
53967
54227
  args.automationId,
53968
54228
  "orders",
53969
54229
  ...args.cursor ? [args.cursor] : [],
53970
- ...args.limit ? [args.limit] : []
54230
+ ...args.limit ? [args.limit] : [],
54231
+ args.startDate ? formatDateToString(args.startDate) : "no-start",
54232
+ args.endDate ? formatDateToString(args.endDate) : "no-end"
53971
54233
  ],
53972
54234
  businessOrders: (args) => [
53973
54235
  ...automationKeys.all,
53974
54236
  "business",
53975
54237
  "orders",
53976
54238
  ...args.cursor ? [args.cursor] : [],
53977
- ...args.limit ? [args.limit] : []
54239
+ ...args.limit ? [args.limit] : [],
54240
+ args.startDate ? formatDateToString(args.startDate) : "no-start",
54241
+ args.endDate ? formatDateToString(args.endDate) : "no-end"
53978
54242
  ],
53979
54243
  businessStatistics: (startDate, endDate) => [
53980
54244
  ...automationKeys.all,
@@ -53999,7 +54263,9 @@ const automationKeys = {
53999
54263
  ...args?.limit ? [args.limit] : [],
54000
54264
  ...args?.status ? [args.status] : [],
54001
54265
  ...args?.search ? [args.search] : [],
54002
- ...args?.triggerTypes ? [...args.triggerTypes] : []
54266
+ ...args?.triggerTypes ? [...args.triggerTypes] : [],
54267
+ args?.startDate ? formatDateToString(args.startDate) : "no-start",
54268
+ args?.endDate ? formatDateToString(args.endDate) : "no-end"
54003
54269
  ],
54004
54270
  aiContent: () => [...automationKeys.all, "ai-content"],
54005
54271
  reputationTemplate: () => [...automationKeys.all, "template", "reputation"]
@@ -54090,6 +54356,12 @@ const listBusinessAutomations = async (args) => {
54090
54356
  if (args.triggerTypes) {
54091
54357
  params.append("triggerTypes", args.triggerTypes.join(","));
54092
54358
  }
54359
+ if (args.startDate) {
54360
+ params.append("startDate", formatDateToString(args.startDate));
54361
+ }
54362
+ if (args.endDate) {
54363
+ params.append("endDate", formatDateToString(args.endDate));
54364
+ }
54093
54365
  const response = await baseRequest(
54094
54366
  `${BUSINESS_AUTOMATION_PATH}?${params.toString()}`,
54095
54367
  {
@@ -54142,14 +54414,23 @@ const getEstimatedRecipients = async (args) => {
54142
54414
  return response.data;
54143
54415
  };
54144
54416
  const getAutomationOrders = async (args) => {
54145
- const params = new URLSearchParams();
54417
+ const searchParams = new URLSearchParams();
54146
54418
  if (args.cursor) {
54147
- params.append("cursor", args.cursor);
54419
+ searchParams.append("cursor", args.cursor);
54148
54420
  }
54149
54421
  if (args.limit) {
54150
- params.append("limit", args.limit.toString());
54422
+ searchParams.append("limit", args.limit.toString());
54423
+ }
54424
+ if (args.startDate) {
54425
+ searchParams.append("startDate", formatDateToString(args.startDate));
54151
54426
  }
54152
- const response = await baseRequest(`/automations/internal/${args.automationId}/orders?${params.toString()}`, {
54427
+ if (args.endDate) {
54428
+ searchParams.append("endDate", formatDateToString(args.endDate));
54429
+ }
54430
+ const basePath = `/automations/internal/${args.automationId}/orders`;
54431
+ const queryString = searchParams.toString();
54432
+ const url = queryString ? `${basePath}?${queryString}` : basePath;
54433
+ const response = await baseRequest(url, {
54153
54434
  method: "GET"
54154
54435
  });
54155
54436
  return response.data;
@@ -54162,6 +54443,12 @@ const getBusinessOrders = async (params) => {
54162
54443
  if (params.limit) {
54163
54444
  queryParams.append("limit", params.limit.toString());
54164
54445
  }
54446
+ if (params.startDate) {
54447
+ queryParams.append("startDate", formatDateToString(params.startDate));
54448
+ }
54449
+ if (params.endDate) {
54450
+ queryParams.append("endDate", formatDateToString(params.endDate));
54451
+ }
54165
54452
  const response = await baseRequest(`/automations/internal/orders?${queryParams.toString()}`, {
54166
54453
  method: "GET"
54167
54454
  });
@@ -54378,6 +54665,12 @@ const useUpdateCommunicationGroup = () => {
54378
54665
  queryClient.invalidateQueries({
54379
54666
  queryKey: automationKeys.list()
54380
54667
  });
54668
+ queryClient.invalidateQueries({
54669
+ queryKey: ["automations"],
54670
+ predicate: (query) => {
54671
+ return Array.isArray(query.queryKey) && query.queryKey.length >= 3 && query.queryKey[0] === "automations" && query.queryKey[2] === "estimated-recipients";
54672
+ }
54673
+ });
54381
54674
  }
54382
54675
  });
54383
54676
  return {
@@ -55178,178 +55471,190 @@ const SMSSetupBusinessForm = forwardRef(({ isSubmitting, onSubmit, hideSubmitBut
55178
55471
  };
55179
55472
  await onSubmit(data);
55180
55473
  };
55181
- return /* @__PURE__ */ jsx("div", { className: "w-full max-w-4xl text-left", children: /* @__PURE__ */ jsx(Form$1, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit: form.handleSubmit(handleSubmit), className: "space-y-6", children: [
55182
- /* @__PURE__ */ jsxs(
55183
- FormSection,
55184
- {
55185
- title: "Legal Business Settings",
55186
- description: "Provide your legal business information required for SMS channel verification and future communications.",
55187
- children: [
55188
- /* @__PURE__ */ jsx(
55189
- CompanyInfoFields,
55190
- {
55191
- control: form.control,
55192
- disabled: isSubmitting,
55193
- context: "sms",
55194
- showInfoCard: true
55195
- }
55196
- ),
55197
- watchedCountry === "US" && /* @__PURE__ */ jsxs(Fragment$1, { children: [
55198
- /* @__PURE__ */ jsx(
55199
- TextField,
55200
- {
55201
- control: form.control,
55202
- name: "company.businessRegistrationNumber",
55203
- label: "Employer Identification Number (EIN)",
55204
- placeholder: "12-3456789",
55205
- toolTipComponent: /* @__PURE__ */ jsx(
55206
- InfoTooltip,
55207
- {
55208
- title: "Why do we need this?",
55209
- description: "Starting February 17th, 2026, all US businesses are required to provide their EIN (Employer Identification Number) when registering for SMS. Your EIN must match the legal business name you've provided above."
55210
- }
55211
- ),
55212
- disabled: isSoleProprietor || isSubmitting
55213
- }
55214
- ),
55215
- /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
55474
+ return /* @__PURE__ */ jsx("div", { className: "w-full max-w-4xl text-left", children: /* @__PURE__ */ jsx(Form$1, { ...form, children: /* @__PURE__ */ jsxs(
55475
+ "form",
55476
+ {
55477
+ onSubmit: form.handleSubmit(handleSubmit),
55478
+ onKeyDown: (e4) => {
55479
+ if (e4.key === "Enter" && e4.target instanceof HTMLInputElement) {
55480
+ e4.preventDefault();
55481
+ }
55482
+ },
55483
+ className: "space-y-6",
55484
+ children: [
55485
+ /* @__PURE__ */ jsxs(
55486
+ FormSection,
55487
+ {
55488
+ title: "Legal Business Settings",
55489
+ description: "Provide your legal business information required for SMS channel verification and future communications.",
55490
+ children: [
55216
55491
  /* @__PURE__ */ jsx(
55217
- Checkbox,
55492
+ CompanyInfoFields,
55218
55493
  {
55219
- checked: isSoleProprietor,
55220
- onCheckedChange: (checked) => {
55221
- const isChecked = !!checked;
55222
- if (isChecked) {
55223
- form.setValue(
55224
- "company.businessType",
55225
- "SOLE_PROPRIETOR"
55226
- );
55227
- form.setValue("company.businessRegistrationNumber", "");
55228
- } else {
55229
- form.setValue("company.businessType", void 0);
55230
- }
55231
- },
55232
- disabled: isSubmitting
55233
- }
55234
- ),
55235
- /* @__PURE__ */ jsx("label", { className: "text-sm text-muted-foreground cursor-pointer", children: "I am a sole proprietor without an EIN" })
55236
- ] }),
55237
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground text-pretty", children: "If no EIN is available (e.g., a self-run business or sole prop with no employees), please check the box above." })
55238
- ] })
55239
- ]
55240
- }
55241
- ),
55242
- /* @__PURE__ */ jsx(
55243
- FormSection,
55244
- {
55245
- title: "Contact Information",
55246
- description: "Provide the contact information of the person authorized to represent your business.",
55247
- children: /* @__PURE__ */ jsx(ContactInfoFields, { control: form.control, disabled: isSubmitting })
55248
- }
55249
- ),
55250
- /* @__PURE__ */ jsxs(
55251
- FormSection,
55252
- {
55253
- title: "Messaging Use Case",
55254
- description: "Tell us about how you plan to use SMS messaging.",
55255
- children: [
55256
- /* @__PURE__ */ jsx(
55257
- TextField,
55258
- {
55259
- control: form.control,
55260
- name: "messagingUseCase.monthlyVolume",
55261
- label: "Expected Monthly Volume",
55262
- placeholder: "1000",
55263
- type: "number",
55264
- disabled: isSubmitting
55265
- }
55266
- ),
55267
- /* @__PURE__ */ jsx(
55268
- TextField,
55269
- {
55270
- control: form.control,
55271
- name: "messagingUseCase.useCaseDetails",
55272
- label: "Use Case Details",
55273
- placeholder: "Describe your use case in detail",
55274
- toolTipComponent: /* @__PURE__ */ jsx(
55275
- InfoTooltip,
55276
- {
55277
- title: "What does this summary look like?",
55278
- description: `This is the summary of how the phone number will be used. For example: "This number is used to send out promotional offers and coupons to the customers of ${business?.name || "my company"}"`
55494
+ control: form.control,
55495
+ disabled: isSubmitting,
55496
+ context: "sms",
55497
+ showInfoCard: true
55279
55498
  }
55280
55499
  ),
55281
- disabled: isSubmitting
55282
- }
55283
- )
55284
- ]
55285
- }
55286
- ),
55287
- /* @__PURE__ */ jsxs(
55288
- FormSection,
55289
- {
55290
- title: "Message Samples",
55291
- description: "Provide examples of messages you plan to send (up to 3 samples).",
55292
- children: [
55293
- /* @__PURE__ */ jsx(AnimatePresence, { children: messageSamples.map((sample, index2) => /* @__PURE__ */ jsxs(
55294
- motion.div,
55295
- {
55296
- initial: { opacity: 0, y: 20 },
55297
- animate: { opacity: 1, y: 0 },
55298
- exit: { opacity: 0, y: -20 },
55299
- transition: { duration: 0.2 },
55300
- className: "relative",
55301
- children: [
55500
+ watchedCountry === "US" && /* @__PURE__ */ jsxs(Fragment$1, { children: [
55302
55501
  /* @__PURE__ */ jsx(
55303
55502
  TextField,
55304
55503
  {
55305
55504
  control: form.control,
55306
- name: `messageSamples.${index2}`,
55307
- label: `Sample Message ${index2 + 1}`,
55308
- placeholder: "Enter a sample message",
55505
+ name: "company.businessRegistrationNumber",
55506
+ label: "Employer Identification Number (EIN)",
55507
+ placeholder: "12-3456789",
55309
55508
  toolTipComponent: /* @__PURE__ */ jsx(
55310
55509
  InfoTooltip,
55311
55510
  {
55312
- title: "Sample Message",
55313
- description: `This is an example message that the shop would send. Don't forget to append the Unsubscribe message to the end. For example: "Stop in at ${business?.name || "my company"} tomorrow for a free Coffee! Reply UNSUBSCRIBE to opt out."`
55511
+ title: "Why do we need this?",
55512
+ description: "Starting February 17th, 2026, all US businesses are required to provide their EIN (Employer Identification Number) when registering for SMS. Your EIN must match the legal business name you've provided above."
55314
55513
  }
55315
55514
  ),
55316
- disabled: isSubmitting
55515
+ disabled: isSoleProprietor || isSubmitting
55317
55516
  }
55318
55517
  ),
55319
- messageSamples.length > 1 && /* @__PURE__ */ jsx(
55320
- motion.button,
55321
- {
55322
- type: "button",
55323
- onClick: () => removeMessageSample(index2),
55324
- className: "absolute -right-2 -top-2 rounded-full bg-destructive/10 p-1 text-destructive hover:bg-destructive/20",
55325
- whileHover: { scale: 1.1 },
55326
- whileTap: { scale: 0.9 },
55327
- children: /* @__PURE__ */ jsx(IconDefinitions.CloseButton, { className: "h-4 w-4" })
55328
- }
55329
- )
55330
- ]
55331
- },
55332
- index2
55333
- )) }),
55334
- messageSamples.length < 3 && /* @__PURE__ */ jsxs(
55335
- motion.button,
55336
- {
55337
- type: "button",
55338
- onClick: addMessageSample,
55339
- className: "mt-4 flex items-center gap-2 rounded-lg border border-dashed border-border px-4 py-2 text-sm text-muted-foreground hover:border-border/80 hover:text-foreground",
55340
- whileHover: { scale: 1.02 },
55341
- whileTap: { scale: 0.98 },
55342
- children: [
55343
- /* @__PURE__ */ jsx(IconDefinitions.PlusIcon, { className: "h-4 w-4" }),
55344
- "Add another sample"
55345
- ]
55346
- }
55347
- )
55348
- ]
55349
- }
55350
- ),
55351
- !hideSubmitButton && /* @__PURE__ */ jsx("div", { className: "flex gap-3 pt-4 justify-end", children: /* @__PURE__ */ jsx(Button$1, { type: "submit", disabled: isSubmitting, children: isSubmitting ? "Submitting..." : "Submit for approval" }) })
55352
- ] }) }) });
55518
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
55519
+ /* @__PURE__ */ jsx(
55520
+ Checkbox,
55521
+ {
55522
+ checked: isSoleProprietor,
55523
+ onCheckedChange: (checked) => {
55524
+ const isChecked = !!checked;
55525
+ if (isChecked) {
55526
+ form.setValue(
55527
+ "company.businessType",
55528
+ "SOLE_PROPRIETOR"
55529
+ );
55530
+ form.setValue("company.businessRegistrationNumber", "");
55531
+ } else {
55532
+ form.setValue("company.businessType", void 0);
55533
+ }
55534
+ },
55535
+ disabled: isSubmitting
55536
+ }
55537
+ ),
55538
+ /* @__PURE__ */ jsx("label", { className: "text-sm text-muted-foreground cursor-pointer", children: "I am a sole proprietor without an EIN" })
55539
+ ] }),
55540
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground text-pretty", children: "If no EIN is available (e.g., a self-run business or sole prop with no employees), please check the box above." })
55541
+ ] })
55542
+ ]
55543
+ }
55544
+ ),
55545
+ /* @__PURE__ */ jsx(
55546
+ FormSection,
55547
+ {
55548
+ title: "Contact Information",
55549
+ description: "Provide the contact information of the person authorized to represent your business.",
55550
+ children: /* @__PURE__ */ jsx(ContactInfoFields, { control: form.control, disabled: isSubmitting })
55551
+ }
55552
+ ),
55553
+ /* @__PURE__ */ jsxs(
55554
+ FormSection,
55555
+ {
55556
+ title: "Messaging Use Case",
55557
+ description: "Tell us about how you plan to use SMS messaging.",
55558
+ children: [
55559
+ /* @__PURE__ */ jsx(
55560
+ TextField,
55561
+ {
55562
+ control: form.control,
55563
+ name: "messagingUseCase.monthlyVolume",
55564
+ label: "Expected Monthly Volume",
55565
+ placeholder: "1000",
55566
+ type: "number",
55567
+ disabled: isSubmitting
55568
+ }
55569
+ ),
55570
+ /* @__PURE__ */ jsx(
55571
+ TextField,
55572
+ {
55573
+ control: form.control,
55574
+ name: "messagingUseCase.useCaseDetails",
55575
+ label: "Use Case Details",
55576
+ placeholder: "Describe your use case in detail",
55577
+ toolTipComponent: /* @__PURE__ */ jsx(
55578
+ InfoTooltip,
55579
+ {
55580
+ title: "What does this summary look like?",
55581
+ description: `This is the summary of how the phone number will be used. For example: "This number is used to send out promotional offers and coupons to the customers of ${business?.name || "my company"}"`
55582
+ }
55583
+ ),
55584
+ disabled: isSubmitting
55585
+ }
55586
+ )
55587
+ ]
55588
+ }
55589
+ ),
55590
+ /* @__PURE__ */ jsxs(
55591
+ FormSection,
55592
+ {
55593
+ title: "Message Samples",
55594
+ description: "Provide examples of messages you plan to send (up to 3 samples).",
55595
+ children: [
55596
+ /* @__PURE__ */ jsx(AnimatePresence, { children: messageSamples.map((sample, index2) => /* @__PURE__ */ jsxs(
55597
+ motion.div,
55598
+ {
55599
+ initial: { opacity: 0, y: 20 },
55600
+ animate: { opacity: 1, y: 0 },
55601
+ exit: { opacity: 0, y: -20 },
55602
+ transition: { duration: 0.2 },
55603
+ className: "relative",
55604
+ children: [
55605
+ /* @__PURE__ */ jsx(
55606
+ TextField,
55607
+ {
55608
+ control: form.control,
55609
+ name: `messageSamples.${index2}`,
55610
+ label: `Sample Message ${index2 + 1}`,
55611
+ placeholder: "Enter a sample message",
55612
+ toolTipComponent: /* @__PURE__ */ jsx(
55613
+ InfoTooltip,
55614
+ {
55615
+ title: "Sample Message",
55616
+ description: `This is an example message that the shop would send. Don't forget to append the Unsubscribe message to the end. For example: "Stop in at ${business?.name || "my company"} tomorrow for a free Coffee! Reply UNSUBSCRIBE to opt out."`
55617
+ }
55618
+ ),
55619
+ disabled: isSubmitting
55620
+ }
55621
+ ),
55622
+ messageSamples.length > 1 && /* @__PURE__ */ jsx(
55623
+ motion.button,
55624
+ {
55625
+ type: "button",
55626
+ onClick: () => removeMessageSample(index2),
55627
+ className: "absolute -right-2 -top-2 rounded-full bg-destructive/10 p-1 text-destructive hover:bg-destructive/20",
55628
+ whileHover: { scale: 1.1 },
55629
+ whileTap: { scale: 0.9 },
55630
+ children: /* @__PURE__ */ jsx(IconDefinitions.CloseButton, { className: "h-4 w-4" })
55631
+ }
55632
+ )
55633
+ ]
55634
+ },
55635
+ index2
55636
+ )) }),
55637
+ messageSamples.length < 3 && /* @__PURE__ */ jsxs(
55638
+ motion.button,
55639
+ {
55640
+ type: "button",
55641
+ onClick: addMessageSample,
55642
+ className: "mt-4 flex items-center gap-2 rounded-lg border border-dashed border-border px-4 py-2 text-sm text-muted-foreground hover:border-border/80 hover:text-foreground",
55643
+ whileHover: { scale: 1.02 },
55644
+ whileTap: { scale: 0.98 },
55645
+ children: [
55646
+ /* @__PURE__ */ jsx(IconDefinitions.PlusIcon, { className: "h-4 w-4" }),
55647
+ "Add another sample"
55648
+ ]
55649
+ }
55650
+ )
55651
+ ]
55652
+ }
55653
+ ),
55654
+ !hideSubmitButton && /* @__PURE__ */ jsx("div", { className: "flex gap-3 pt-4 justify-end", children: /* @__PURE__ */ jsx(Button$1, { type: "submit", disabled: isSubmitting, children: isSubmitting ? "Submitting..." : "Submit for approval" }) })
55655
+ ]
55656
+ }
55657
+ ) }) });
55353
55658
  });
55354
55659
  const SMSSetup = ({
55355
55660
  onSubmit,
@@ -55368,6 +55673,9 @@ const SMSSetup = ({
55368
55673
  onFormVisibilityChange?.(showForm);
55369
55674
  }, [showForm, onFormVisibilityChange]);
55370
55675
  const handleSubmit = async (data) => {
55676
+ if (isSubmitting) {
55677
+ return;
55678
+ }
55371
55679
  try {
55372
55680
  createSmsRegistrationApplication2({
55373
55681
  application: data,
@@ -56286,7 +56594,10 @@ const useUpdateBusinessAutomation = (automationId) => {
56286
56594
  queryClient.invalidateQueries({
56287
56595
  queryKey: communicationGroupQueryKeys.all
56288
56596
  });
56289
- queryClient.invalidateQueries({ queryKey: automationKeys.list() });
56597
+ queryClient.invalidateQueries({ queryKey: automationKeys.all });
56598
+ queryClient.invalidateQueries({
56599
+ queryKey: automationKeys.estimatedRecipients(automationId)
56600
+ });
56290
56601
  },
56291
56602
  onError: () => {
56292
56603
  }
@@ -56313,7 +56624,7 @@ const useArchiveBusinessAutomation = () => {
56313
56624
  queryClient.invalidateQueries({
56314
56625
  queryKey: communicationGroupQueryKeys.all
56315
56626
  });
56316
- queryClient.invalidateQueries({ queryKey: automationKeys.list() });
56627
+ queryClient.invalidateQueries({ queryKey: automationKeys.all });
56317
56628
  toast2({
56318
56629
  title: "Successfully archived"
56319
56630
  });
@@ -77729,6 +78040,15 @@ const Observability = ({
77729
78040
  flags,
77730
78041
  debug2
77731
78042
  ]);
78043
+ useEffect(() => {
78044
+ try {
78045
+ setReachClient(clientEntryPoint);
78046
+ } catch (e4) {
78047
+ if (debug2) {
78048
+ console.error("Failed to set reach client:", e4);
78049
+ }
78050
+ }
78051
+ }, [clientEntryPoint, debug2]);
77732
78052
  useEffect(() => {
77733
78053
  if (sdk || !isFeHoneycombLogsEnabled || !sessionId) {
77734
78054
  if (debug2) {
@@ -77778,13 +78098,6 @@ const Observability = ({
77778
78098
  ["posthog.sessionId"]: sessionId
77779
78099
  }
77780
78100
  });
77781
- try {
77782
- setReachClient(clientEntryPoint);
77783
- } catch (e4) {
77784
- if (debug2) {
77785
- console.error("Failed to set reach client:", e4);
77786
- }
77787
- }
77788
78101
  sdk.start();
77789
78102
  if (debug2) {
77790
78103
  console.log(
@@ -84487,64 +84800,6 @@ const defaultTranslations = {
84487
84800
  transaction_table_created_header: "Transaction Created"
84488
84801
  },
84489
84802
  common: {
84490
- day: "Day",
84491
- day_other: "Days",
84492
- view_as_table: "View as table",
84493
- view_as_chart: "View as chart",
84494
- total: "Total",
84495
- hour: "Hour",
84496
- hour_other: "Hours",
84497
- minute: "Minute",
84498
- minute_other: "Minutes",
84499
- second: "Second",
84500
- second_other: "Seconds",
84501
- account_id: "Account ID",
84502
- account_name: "Account Name",
84503
- ad_spend: "Ad Spend",
84504
- all: "All",
84505
- apply: "Apply",
84506
- date: "Date",
84507
- campaign: "Campaign",
84508
- close: "Close",
84509
- continue: "Continue",
84510
- connected: "Connected",
84511
- connecting: "Connecting...",
84512
- custom: "Custom",
84513
- disconnect: "Disconnect",
84514
- finish: "Finish",
84515
- reauthenticate: "Reauthenticate",
84516
- account_settings: "Account Settings",
84517
- manage_account_settings: "Manage your account settings",
84518
- copy: "Copy",
84519
- copied: "Copied",
84520
- error: "An error occurred",
84521
- filter: "Filter",
84522
- filter_other: "Filters",
84523
- finishingUp: "Finishing up...",
84524
- loading: "Loading...",
84525
- need_help: "Need help? let us know",
84526
- next: "Next",
84527
- next_steps: "Next steps:",
84528
- number: "{{number}}",
84529
- percentage: "Percentage",
84530
- previous: "Previous",
84531
- reset: "Reset",
84532
- revenue: "Revenue",
84533
- revenue_from_ads: "Revenue From Ads",
84534
- roas: "ROAS",
84535
- source: "Source",
84536
- channel: "Channel",
84537
- referrer: "Referrer",
84538
- group_by: "Group by",
84539
- transactions: "Transactions",
84540
- no_results_found: "No results found",
84541
- utm_campaign: "UTM Campaign",
84542
- utm_medium: "UTM Medium",
84543
- spend: "Spend",
84544
- return_on_ad_spend: "Return on Ad Spend",
84545
- select_dates: "Select dates",
84546
- total_ads_spend: "Total Ads Spend",
84547
- search: "Search...",
84548
84803
  measure: {
84549
84804
  dashboard: {
84550
84805
  chart_empty_state: "No data available for the selected period",
@@ -84615,6 +84870,24 @@ const defaultTranslations = {
84615
84870
  new_customers_description: "The number of first-time customers acquired through your advertising campaigns during the selected period. \n\nThis metric specifically tracks customers who have never purchased from your business before and made their first purchase after interacting with your ads.",
84616
84871
  new_leads: "New Leads",
84617
84872
  new_leads_description: "The number of new potential customers who provided their contact information through your advertising campaigns during the selected period. \n\nLeads are typically generated through form submissions, newsletter signups, download requests, or other lead magnets promoted in your ads.",
84873
+ open_leads: "Open Leads",
84874
+ open_leads_description: "The number of leads who have not yet made a purchase and have had activity within the last 90 days. These are active leads that are still in your pipeline.",
84875
+ converted_leads: "Converted Leads",
84876
+ converted_leads_description: "The number of leads who have made at least one purchase. These leads have successfully converted into paying customers.",
84877
+ stale_leads: "Stale Leads",
84878
+ stale_leads_description: "Leads with no activity (no new identifications, visits, or purchases) for more than 90 days. Consider re-engagement campaigns for these leads.",
84879
+ leads_from_ads: "Leads From Ads",
84880
+ leads_from_ads_description: "The number of new leads attributed to paid advertising during the selected period. \n\nA lead is counted when a visitor provides contact information after interacting with your ads.",
84881
+ cost_per_lead: "Cost Per Lead",
84882
+ cost_per_lead_description: "The average amount spent to acquire each new lead from ads, calculated by dividing ad spend by leads from ads.",
84883
+ lead_to_purchase_rate: "Lead to Purchase Rate",
84884
+ lead_to_purchase_rate_description: "The percentage of leads who went on to make a purchase during the selected period.",
84885
+ total_value_of_open_leads: "Est. Value of Open Leads",
84886
+ total_value_of_open_leads_description: "Estimated potential revenue from leads who have not yet converted. Calculated by multiplying the number of open (unconverted) leads by the average revenue per converted lead. This is a projection, not actual revenue.",
84887
+ total_value_of_converted_leads: "Total Value of Converted Leads",
84888
+ total_value_of_converted_leads_description: "The total revenue from all purchases made by leads who converted during the selected period.",
84889
+ average_lead_value: "Average Lead Value",
84890
+ average_lead_value_description: "The average revenue per converted lead, calculated by dividing converted lead revenue by the number of converted leads.",
84618
84891
  reengaged_customers: "Reengaged Customers",
84619
84892
  reengaged_customers_description: "The number of previously inactive customers who made a purchase after being targeted by your advertising campaigns during the selected period. \n\nThese are customers who had purchased from your business before but had been dormant for a significant period.",
84620
84893
  revenue_per_click: "Revenue Per Click",
@@ -84645,6 +84918,9 @@ const defaultTranslations = {
84645
84918
  not_running_paid_ads: "Not running paid ads? Use AI to create a Google Ads campaign",
84646
84919
  privacy_policy: "Privacy Policy",
84647
84920
  quick_links: "Quick Links",
84921
+ quick_links_and_integrations: "Quick Links + Integrations",
84922
+ connect_callrail: "Connect CallRail",
84923
+ connect_ctm: "Connect CTM",
84648
84924
  setup_tracking_pixel: "Setup tracking pixel",
84649
84925
  understand_attribution_model: "Understand our attribution model",
84650
84926
  update_ad_campaign_settings: "Update ad campaign settings",
@@ -84749,6 +85025,11 @@ For example: If someone clicks your Google ad, we'll show "Google Ads" even if t
84749
85025
  transaction_attributed_description: "Shows all transactions created in the selected date range.",
84750
85026
  transaction_attributed_why_it_matters: "Why it matters: \nUse this to understand actual sales performance during a specific period and where they came from. Transactions are shown based on when the transaction was created, regardless of when or how the customer first discovered your business. This helps you see all revenue that occurred during the selected timeframe, and which digital marketing efforts they are associated to. \n\nExample: \nA customer clicks your ad on Oct 15 but completes their transaction on Nov 3. With Nov 1-30 selected, this transaction appears in your data. With Oct 1-31 selected, it doesn't."
84751
85027
  }
85028
+ },
85029
+ leads: {
85030
+ title: "Leads",
85031
+ prototype_banner_title: "New feature — we're still optimizing",
85032
+ prototype_banner_description: "This experience is actively being improved. We'd love your feedback to help us get it right."
84752
85033
  }
84753
85034
  }
84754
85035
  },
@@ -84762,6 +85043,64 @@ For example: If someone clicks your Google ad, we'll show "Google Ads" even if t
84762
85043
  finalizing_connection: "Finalizing connection..."
84763
85044
  }
84764
85045
  },
85046
+ day: "Day",
85047
+ day_other: "Days",
85048
+ view_as_table: "View as table",
85049
+ view_as_chart: "View as chart",
85050
+ total: "Total",
85051
+ hour: "Hour",
85052
+ hour_other: "Hours",
85053
+ minute: "Minute",
85054
+ minute_other: "Minutes",
85055
+ second: "Second",
85056
+ second_other: "Seconds",
85057
+ account_id: "Account ID",
85058
+ account_name: "Account Name",
85059
+ ad_spend: "Ad Spend",
85060
+ all: "All",
85061
+ apply: "Apply",
85062
+ date: "Date",
85063
+ campaign: "Campaign",
85064
+ close: "Close",
85065
+ continue: "Continue",
85066
+ connected: "Connected",
85067
+ connecting: "Connecting...",
85068
+ custom: "Custom",
85069
+ disconnect: "Disconnect",
85070
+ finish: "Finish",
85071
+ reauthenticate: "Reauthenticate",
85072
+ account_settings: "Account Settings",
85073
+ manage_account_settings: "Manage your account settings",
85074
+ copy: "Copy",
85075
+ copied: "Copied",
85076
+ error: "An error occurred",
85077
+ filter: "Filter",
85078
+ filter_other: "Filters",
85079
+ finishingUp: "Finishing up...",
85080
+ loading: "Loading...",
85081
+ need_help: "Need help? let us know",
85082
+ next: "Next",
85083
+ next_steps: "Next steps:",
85084
+ number: "{{number}}",
85085
+ percentage: "Percentage",
85086
+ previous: "Previous",
85087
+ reset: "Reset",
85088
+ revenue: "Revenue",
85089
+ revenue_from_ads: "Revenue From Ads",
85090
+ roas: "ROAS",
85091
+ source: "Source",
85092
+ channel: "Channel",
85093
+ referrer: "Referrer",
85094
+ group_by: "Group by",
85095
+ transactions: "Transactions",
85096
+ no_results_found: "No results found",
85097
+ utm_campaign: "UTM Campaign",
85098
+ utm_medium: "UTM Medium",
85099
+ spend: "Spend",
85100
+ return_on_ad_spend: "Return on Ad Spend",
85101
+ select_dates: "Select dates",
85102
+ total_ads_spend: "Total Ads Spend",
85103
+ search: "Search...",
84765
85104
  // DELETE
84766
85105
  authentication: {
84767
85106
  required: "Authentication required",
@@ -84828,7 +85167,7 @@ For example: If someone clicks your Google ad, we'll show "Google Ads" even if t
84828
85167
  copied_to_clipboard: "Copied to clipboard",
84829
85168
  copied_to_clipboard_description: "The tracking pixel code has been copied to your clipboard.",
84830
85169
  title: "Install our Tracking Pixel",
84831
- description: "In order to get a complete picture of how users are interacting with your website, it’s important for the Reach analytics code to be on every page of your website and within as many third-party tools that you use as possible.",
85170
+ description: "In order to get a complete picture of how users are interacting with your website, it’s important for the analytics code to be on every page of your website and within as many third-party tools that you use as possible.",
84832
85171
  card_title: "Install the Snippet inside the <HEAD> of every page",
84833
85172
  third_party_pages_your_tracking_pixel: "Your Tracking Pixel:",
84834
85173
  card_description: "Copy this following text and insert it inside of the <HEAD> tags on all pages.",
@@ -84847,7 +85186,7 @@ For example: If someone clicks your Google ad, we'll show "Google Ads" even if t
84847
85186
  },
84848
85187
  third_party_installation_guides: {
84849
85188
  title: "Installation Guides",
84850
- description: "These guides will help you add the Reach analytics snippet to your website. The process works with any website builder or platform and most third-party tools.",
85189
+ description: "These guides will help you add the analytics snippet to your website. The process works with any website builder or platform and most third-party tools.",
84851
85190
  installation_guide: "{{name}} Installation Guide",
84852
85191
  official_guide: "Official Guide",
84853
85192
  shopify: {
@@ -84858,18 +85197,20 @@ For example: If someone clicks your Google ad, we'll show "Google Ads" even if t
84858
85197
  step_5: "Paste the code snippet from the section above just before it"
84859
85198
  },
84860
85199
  google_tag_manager: {
84861
- step_1: "Go to https://tagmanager.google.com",
84862
- step_2: "Sign in to your Google Account",
84863
- step_3: 'In the Tags tab, click the "New" button to create a new tag',
84864
- step_4: 'Choose "Custom HTML" as the tag type',
84865
- step_5: "Paste the tracking code above into the HTML field",
84866
- step_6: "Click anywhere in the triggering section to edit this setting",
84867
- step_7: 'Select "Initialization - All Pages" as the trigger',
84868
- step_8: "Click Save to save the tag",
84869
- step_9: "Click Submit in the top right",
84870
- step_10: "Add a version name and description",
84871
- step_11: "Click Publish to deploy your changes",
84872
- special_note: "Make sure you have the Google Tag Manager container code already installed on your site before adding this tag."
85200
+ step_1: "Go to tagmanager.google.com and sign in with your Google account",
85201
+ step_2: "Select the container for your website (or create one if you haven't already)",
85202
+ step_3: "Important: Make sure your GTM container code is already installed on your website. If not, follow Google's installation instructions first, then return here.",
85203
+ step_3_link: "https://support.google.com/tagmanager/answer/6103696",
85204
+ step_4: 'In the left sidebar, click "Tags", then click the "New" button in the top right',
85205
+ step_5: 'Give your tag a name at the top (e.g., "Source Attribution Pixel")',
85206
+ step_6: 'Click anywhere in the "Tag Configuration" box, then select "Custom HTML" from the tag type menu',
85207
+ step_7: "Paste the tracking code from above into the HTML field (the code already includes the required <script> tags)",
85208
+ step_8: 'Click anywhere in the "Triggering" box below the tag configuration',
85209
+ step_9: 'Select "Initialization - All Pages" to ensure the pixel loads before other tags (recommended), or "All Pages" for standard page load timing',
85210
+ step_10: 'Click "Save" in the top right corner to save your tag',
85211
+ step_11: 'Click "Submit" in the top right corner of the workspace',
85212
+ step_12: 'Add a version name (e.g., "Added source attribution pixel") and click "Publish" to deploy your changes',
85213
+ special_note: "Google Tag Manager works with any website platform. If you already have GTM installed on your site, you can use this method instead of adding code directly to your website's HTML."
84873
85214
  },
84874
85215
  hubspot: {
84875
85216
  step_1: "Go to Settings → Content → Pages",
@@ -84878,18 +85219,30 @@ For example: If someone clicks your Google ad, we'll show "Google Ads" even if t
84878
85219
  step_4: 'Click "Save"'
84879
85220
  },
84880
85221
  wordpress: {
84881
- step_1: "We recommend using the popular WPCode Plugin",
84882
- step_2: 'In the plugin, select "+ Add Snippet"',
84883
- step_3: 'When presented with a menu of options, navigate to "Add Your Custom Code (New Snippet)" and select "Use snippet"',
84884
- step_4: "Paste the code snippet from the section above",
84885
- step_5: 'For Insert Method, choose "Auto Insert"',
84886
- step_6: 'For Location, select "Site Wide Header"',
84887
- step_7: "Be sure to save your snippet"
85222
+ intro: "WordPress sites can add tracking code to the header using various plugins or theme settings. Find your method below and paste the tracking code into the Header section.",
85223
+ methods: [
85224
+ {
85225
+ id: "wpcode",
85226
+ name: "WPCode",
85227
+ description: 'Code Snippets Header & Footer → paste in "Header" section → Save Changes',
85228
+ link: "https://wpcode.com/docs/using-the-global-header-footer-settings/"
85229
+ },
85230
+ {
85231
+ id: "elementor",
85232
+ name: "Elementor",
85233
+ description: 'Elementor → Custom Code → Add New → set location to "<head>" → set conditions to "Entire Site" → Publish',
85234
+ link: "https://elementor.com/help/custom-code-pro/"
85235
+ }
85236
+ ],
85237
+ special_note: "Important: After adding tracking code, clear your WordPress cache if you use a caching plugin (WP Rocket, LiteSpeed Cache, W3 Total Cache, Cloudflare, etc.). The code won't appear on your site until the cache is cleared.\n\nIf you're using a different plugin for custom JavaScript or code injection, check your WordPress Plugins section to identify the plugin and follow that plugin's specific instructions for adding code to the header."
84888
85238
  },
84889
85239
  squarespace: {
84890
- step_1: "Go to Settings Advanced Code Injection",
84891
- step_2: 'Paste the code from the section above into the "Header" field',
84892
- step_3: 'Click "Save"'
85240
+ step_1: "Sign in to your Squarespace account at account.squarespace.com",
85241
+ step_2: 'On the Dashboard, click the "Website" button for the site you want to modify',
85242
+ step_3: 'From the main menu, click "Pages" under the "Website" section',
85243
+ step_4: 'Scroll to the bottom and click "Custom Code", then click "Code Injection". Note: on older versions of Squarespace, "Custom Code" may be labeled "Website Tools"',
85244
+ step_5: 'Under "Header", paste the tracking code from above and click "Save"',
85245
+ special_note: "Code Injection requires a Squarespace Business or Commerce plan. If you don't see the Code Injection option, you may need to upgrade your plan."
84893
85246
  },
84894
85247
  wix: {
84895
85248
  step_1: "Go to Settings → Custom Code",
@@ -85211,7 +85564,7 @@ For example: If someone clicks your Google ad, we'll show "Google Ads" even if t
85211
85564
  fetchAccounts: "Fetching accounts...",
85212
85565
  noAccountSelected: "No account selected",
85213
85566
  enableTrackingParams: "Enable Tracking Parameters",
85214
- enableTrackingParamsDescription: "Append the reach campaign id parameter to your ad platform tracking settings. We will not overwrite any existing tracking parameters. Required for accurate tracking.",
85567
+ enableTrackingParamsDescription: "Append the 'ER' campaign id parameter to your ad platform tracking settings. We will not overwrite any existing tracking parameters. Required for accurate tracking.",
85215
85568
  google: {
85216
85569
  selectAccountDescription: "Select the Google Ads account you want to connect to view your data. These will be linked to our MCC for optimal tracking.",
85217
85570
  mccAccounts: "MCC Accounts",
@@ -86876,6 +87229,9 @@ const useParseDate = () => {
86876
87229
  };
86877
87230
  const usePreviewSegmentRecipients = (type, conditions, businessId, options, existingSegmentConditions) => {
86878
87231
  const [debouncedConditions, setDebouncedConditions] = useState(conditions);
87232
+ const [debouncedConditionGroups, setDebouncedConditionGroups] = useState(
87233
+ options?.conditionGroups
87234
+ );
86879
87235
  const [debouncedSearch, setDebouncedSearch] = useState(options?.search || "");
86880
87236
  useEffect(() => {
86881
87237
  const timer = setTimeout(() => {
@@ -86883,6 +87239,12 @@ const usePreviewSegmentRecipients = (type, conditions, businessId, options, exis
86883
87239
  }, options?.debounceMs);
86884
87240
  return () => clearTimeout(timer);
86885
87241
  }, [conditions, options?.debounceMs]);
87242
+ useEffect(() => {
87243
+ const timer = setTimeout(() => {
87244
+ setDebouncedConditionGroups(options?.conditionGroups);
87245
+ }, options?.debounceMs);
87246
+ return () => clearTimeout(timer);
87247
+ }, [options?.conditionGroups, options?.debounceMs]);
86886
87248
  useEffect(() => {
86887
87249
  const timer = setTimeout(() => {
86888
87250
  setDebouncedSearch(options?.search || "");
@@ -86907,7 +87269,18 @@ const usePreviewSegmentRecipients = (type, conditions, businessId, options, exis
86907
87269
  });
86908
87270
  return isValid2;
86909
87271
  };
87272
+ const areConditionGroupsValid = (conditionGroups) => {
87273
+ if (!conditionGroups || conditionGroups.length === 0) {
87274
+ return false;
87275
+ }
87276
+ return conditionGroups.some(
87277
+ (group) => group.conditions && group.conditions.length > 0
87278
+ );
87279
+ };
87280
+ const hasConditionGroups = areConditionGroupsValid(debouncedConditionGroups);
87281
+ const hasConditions = areConditionsValid(debouncedConditions, type);
86910
87282
  const conditionsKey = JSON.stringify(debouncedConditions);
87283
+ const conditionGroupsKey = JSON.stringify(debouncedConditionGroups || []);
86911
87284
  const existingConditionsKey = JSON.stringify(existingSegmentConditions || []);
86912
87285
  const query = useQuery({
86913
87286
  queryKey: [
@@ -86915,11 +87288,20 @@ const usePreviewSegmentRecipients = (type, conditions, businessId, options, exis
86915
87288
  businessId,
86916
87289
  type,
86917
87290
  conditionsKey,
87291
+ conditionGroupsKey,
86918
87292
  existingConditionsKey,
86919
87293
  debouncedSearch || "",
86920
87294
  options?.limit ?? 100
86921
87295
  ],
86922
87296
  queryFn: () => {
87297
+ if (hasConditionGroups) {
87298
+ return previewSegmentRecipients({
87299
+ businessId,
87300
+ conditionGroups: debouncedConditionGroups,
87301
+ limit: options?.limit ?? 100,
87302
+ search: debouncedSearch || void 0
87303
+ });
87304
+ }
86923
87305
  return previewSegmentRecipients({
86924
87306
  businessId,
86925
87307
  // If the segment is a SQL segment, use the existing segment conditions
@@ -86928,7 +87310,7 @@ const usePreviewSegmentRecipients = (type, conditions, businessId, options, exis
86928
87310
  search: debouncedSearch || void 0
86929
87311
  });
86930
87312
  },
86931
- enabled: (options?.enabled ?? true) && areConditionsValid(debouncedConditions, type),
87313
+ enabled: (options?.enabled ?? true) && (hasConditionGroups || hasConditions),
86932
87314
  staleTime: 3e4,
86933
87315
  // 30 seconds
86934
87316
  gcTime: 5 * 60 * 1e3,
@@ -86980,6 +87362,9 @@ var ConditionOperatorEnumType;
86980
87362
  ConditionOperatorEnumType2["BETWEEN_MONTH_DAY"] = "between_month_day";
86981
87363
  ConditionOperatorEnumType2["BETWEEN_MONTH_DAY_YEAR"] = "between_month_day_year";
86982
87364
  ConditionOperatorEnumType2["ARRAY_CONTAINS"] = "array_contains";
87365
+ ConditionOperatorEnumType2["CONTAINS"] = "contains";
87366
+ ConditionOperatorEnumType2["NOT_CONTAINS"] = "not_contains";
87367
+ ConditionOperatorEnumType2["IN"] = "in";
86983
87368
  })(ConditionOperatorEnumType || (ConditionOperatorEnumType = {}));
86984
87369
  const ConditionOperatorEnum = z$2.nativeEnum(ConditionOperatorEnumType);
86985
87370
  const relativeDateSchema = z$2.object({
@@ -86993,6 +87378,7 @@ const betweenDateSchema = z$2.object({
86993
87378
  endDate: z$2.union([z$2.coerce.date(), relativeDateSchema])
86994
87379
  });
86995
87380
  const fieldConditionSchema = z$2.object({
87381
+ type: z$2.literal("property").optional(),
86996
87382
  field: z$2.union([ConditionFieldEnum, z$2.string()]),
86997
87383
  value: z$2.array(z$2.union([
86998
87384
  z$2.string(),
@@ -87004,14 +87390,100 @@ const fieldConditionSchema = z$2.object({
87004
87390
  ])).describe("The value to compare the field against (string, number, boolean, date)"),
87005
87391
  operator: ConditionOperatorEnum
87006
87392
  });
87393
+ var CountOperator;
87394
+ (function(CountOperator2) {
87395
+ CountOperator2["EQ"] = "eq";
87396
+ CountOperator2["GT"] = "gt";
87397
+ CountOperator2["GTE"] = "gte";
87398
+ CountOperator2["LT"] = "lt";
87399
+ CountOperator2["LTE"] = "lte";
87400
+ })(CountOperator || (CountOperator = {}));
87401
+ const relatedResourceSchema = z$2.object({
87402
+ /**
87403
+ * The schema definition ID of the related resource to query
87404
+ * (e.g., the PolicyLift policy schema ID)
87405
+ */
87406
+ schemaId: z$2.string(),
87407
+ /**
87408
+ * JSON path in the related resource's data that links to the contact's external_id
87409
+ * (e.g., "customerId" for policies that have a customerId field)
87410
+ */
87411
+ foreignKeyPath: z$2.string()
87412
+ });
87413
+ const relatedResourceConditionSchema = z$2.object({
87414
+ type: z$2.literal("related_resource"),
87415
+ /**
87416
+ * Which related resource to query and how it links to the contact
87417
+ */
87418
+ relatedResource: relatedResourceSchema,
87419
+ /**
87420
+ * How to compare the count of matching resources (eq, gt, gte, lt, lte)
87421
+ */
87422
+ countOperator: z$2.nativeEnum(CountOperator),
87423
+ /**
87424
+ * The count value to compare against (e.g., 0 for "none", 1 for "at least one")
87425
+ */
87426
+ countValue: z$2.number().int().min(0),
87427
+ /**
87428
+ * Conditions to filter the related resources (applied as AND logic)
87429
+ * These conditions are evaluated against the related resource's data
87430
+ */
87431
+ conditions: z$2.array(fieldConditionSchema)
87432
+ });
87007
87433
  function isFieldCondition(condition) {
87008
- return "field" in condition;
87009
- }
87434
+ if (typeof condition !== "object" || condition === null)
87435
+ return false;
87436
+ const c3 = condition;
87437
+ if (c3.type === "property")
87438
+ return true;
87439
+ if (c3.type === "related_resource" || c3.type === "group")
87440
+ return false;
87441
+ return "field" in c3;
87442
+ }
87443
+ var ConditionGroupLogic;
87444
+ (function(ConditionGroupLogic2) {
87445
+ ConditionGroupLogic2["AND"] = "and";
87446
+ ConditionGroupLogic2["OR"] = "or";
87447
+ })(ConditionGroupLogic || (ConditionGroupLogic = {}));
87448
+ const baseConditionItemSchema = z$2.union([
87449
+ fieldConditionSchema,
87450
+ relatedResourceConditionSchema
87451
+ ]);
87452
+ const level2GroupSchema = z$2.object({
87453
+ type: z$2.literal("group").optional().default("group"),
87454
+ logic: z$2.nativeEnum(ConditionGroupLogic),
87455
+ conditions: z$2.array(baseConditionItemSchema)
87456
+ }).transform((val) => ({ ...val, type: "group" }));
87457
+ const level1GroupSchema = z$2.object({
87458
+ type: z$2.literal("group").optional().default("group"),
87459
+ logic: z$2.nativeEnum(ConditionGroupLogic),
87460
+ conditions: z$2.array(z$2.union([baseConditionItemSchema, level2GroupSchema]))
87461
+ }).transform((val) => ({ ...val, type: "group" }));
87462
+ const conditionGroupSchemaFlat = z$2.object({
87463
+ type: z$2.literal("group").optional().default("group"),
87464
+ logic: z$2.nativeEnum(ConditionGroupLogic),
87465
+ conditions: z$2.array(z$2.union([baseConditionItemSchema, level1GroupSchema]))
87466
+ }).transform((val) => ({
87467
+ ...val,
87468
+ type: "group"
87469
+ }));
87470
+ const conditionGroupSchemaRecursive = z$2.lazy(() => z$2.object({
87471
+ type: z$2.literal("group").optional().default("group"),
87472
+ logic: z$2.nativeEnum(ConditionGroupLogic),
87473
+ conditions: z$2.array(z$2.union([
87474
+ fieldConditionSchema,
87475
+ relatedResourceConditionSchema,
87476
+ conditionGroupSchemaRecursive
87477
+ ]))
87478
+ }).transform((val) => ({ ...val, type: "group" })));
87479
+ const conditionGroupSchema = typeof process !== "undefined" && process.env?.GENERATE_DOCS === "true" ? conditionGroupSchemaFlat : conditionGroupSchemaRecursive;
87010
87480
  const ConditionSchema = z$2.union([
87011
87481
  fieldConditionSchema,
87012
- sqlSegmentConditionSchema
87482
+ sqlSegmentConditionSchema,
87483
+ relatedResourceConditionSchema
87013
87484
  ]);
87014
87485
  z$2.array(fieldConditionSchema);
87486
+ z$2.array(conditionGroupSchema);
87015
87487
  z$2.array(ConditionSchema);
87016
87488
  var CurrencyCode;
87017
87489
  (function(CurrencyCode2) {
@@ -87404,10 +87876,6 @@ const useCurrentCommunicationGroup = () => {
87404
87876
  isGetSuccess: false
87405
87877
  };
87406
87878
  };
87407
- const useValidationStats = () => {
87408
- const { state } = useViewAutomationContext();
87409
- return state.recipientStats;
87410
- };
87411
87879
  const useRecipientStatsTracking = (existingAutomation) => {
87412
87880
  const { setRecipientStats: setRecipientStats2, state } = useViewAutomationContext();
87413
87881
  const automation2 = state.automation || existingAutomation;
@@ -90994,6 +91462,7 @@ function useTanstackTable({
90994
91462
  initialPageIndex = 0,
90995
91463
  cursorPaginationQueryResult,
90996
91464
  onQueryParametersChange,
91465
+ resetPaginationKey,
90997
91466
  manualPageCount,
90998
91467
  enableSorting = true,
90999
91468
  initialSorting = [],
@@ -91109,6 +91578,16 @@ function useTanstackTable({
91109
91578
  pageIndex: initialPageIndex,
91110
91579
  pageSize: initialPageSize
91111
91580
  });
91581
+ useEffect(() => {
91582
+ if (resetPaginationKey !== void 0) {
91583
+ setLogicalPage(initialPageIndex);
91584
+ setPageCursors([void 0]);
91585
+ setPagination({
91586
+ pageIndex: initialPageIndex,
91587
+ pageSize: pagination.pageSize
91588
+ });
91589
+ }
91590
+ }, [resetPaginationKey]);
91112
91591
  const handleCursorNavigation = useCallback(
91113
91592
  (action) => {
91114
91593
  if (!isCursorPagination || !onQueryParametersChange) return;
@@ -91317,6 +91796,7 @@ function TanstackTable({
91317
91796
  hideLoadingState = false,
91318
91797
  cursorPaginationQueryResult,
91319
91798
  onQueryParametersChange,
91799
+ resetPaginationKey,
91320
91800
  searchValue,
91321
91801
  onSearchChange,
91322
91802
  onRowClick,
@@ -91370,6 +91850,7 @@ function TanstackTable({
91370
91850
  initialPageIndex,
91371
91851
  cursorPaginationQueryResult,
91372
91852
  onQueryParametersChange,
91853
+ resetPaginationKey,
91373
91854
  enableSorting,
91374
91855
  initialSorting,
91375
91856
  manualSorting,
@@ -94469,7 +94950,7 @@ function createNameColumn(nameAccessor = "name", headerText, showDescription = f
94469
94950
  return createTextColumn({
94470
94951
  accessorKey: nameAccessor,
94471
94952
  header: headerText,
94472
- enableSorting: false,
94953
+ enableSorting: true,
94473
94954
  meta: {
94474
94955
  align: "left",
94475
94956
  displayName: headerText,
@@ -94522,7 +95003,7 @@ function createUsersColumn() {
94522
95003
  return createTextColumn({
94523
95004
  accessorKey: "user_count",
94524
95005
  header: t$2("engage:user_other"),
94525
- enableSorting: false,
95006
+ enableSorting: true,
94526
95007
  size: 120,
94527
95008
  minSize: 100,
94528
95009
  headerClassName: "justify-end text-right w-full",
@@ -94543,7 +95024,7 @@ function createTypeColumn() {
94543
95024
  return createTextColumn({
94544
95025
  accessorKey: "type",
94545
95026
  header: "Type",
94546
- enableSorting: false,
95027
+ enableSorting: true,
94547
95028
  size: 110,
94548
95029
  minSize: 90,
94549
95030
  headerClassName: "justify-end text-right w-full",
@@ -94599,7 +95080,7 @@ function createModifiedColumn(dateAccessor = "updated_at") {
94599
95080
  formatType: "relative",
94600
95081
  size: 160,
94601
95082
  minSize: 120,
94602
- enableSorting: false,
95083
+ enableSorting: true,
94603
95084
  headerClassName: "justify-end text-right w-full",
94604
95085
  meta: {
94605
95086
  align: "right",
@@ -95800,164 +96281,6 @@ const EmailPreviewHtmlRenderer = ({
95800
96281
  }
95801
96282
  ) });
95802
96283
  };
95803
- const truncateTextForIcon = (text2, maxWidth = 200, iconWidth = 32) => {
95804
- if (text2.length <= 20) return text2;
95805
- const avgCharWidth = 8;
95806
- const availableWidth = maxWidth - iconWidth - 16;
95807
- const maxChars = Math.floor(availableWidth / avgCharWidth);
95808
- if (text2.length <= maxChars) return text2;
95809
- return text2.substring(0, maxChars - 3) + "...";
95810
- };
95811
- const ComboboxSelect = ({
95812
- options,
95813
- value,
95814
- onChange: onChange2,
95815
- placeholder,
95816
- className = "",
95817
- disableInput = false,
95818
- disabled = false,
95819
- onOpenChange,
95820
- limitWhenNotSearching,
95821
- onSearchChange,
95822
- isLoading = false,
95823
- shouldFilter = true,
95824
- showEmptyState = true
95825
- }) => {
95826
- const [open, setOpen2] = React.useState(false);
95827
- const [searchQuery, setSearchQuery] = React.useState("");
95828
- const triggerRef = React.useRef(null);
95829
- const [triggerWidth, setTriggerWidth] = React.useState();
95830
- React.useEffect(() => {
95831
- if (triggerRef.current && open) {
95832
- setTriggerWidth(triggerRef.current.offsetWidth);
95833
- }
95834
- }, [open]);
95835
- const handleOpenChange = (newOpen) => {
95836
- setOpen2(newOpen);
95837
- if (!newOpen && !onSearchChange) {
95838
- setSearchQuery("");
95839
- }
95840
- onOpenChange?.(newOpen);
95841
- };
95842
- const handleSearchChange = (search2) => {
95843
- setSearchQuery(search2);
95844
- onSearchChange?.(search2);
95845
- };
95846
- const displayOptions = React.useMemo(() => {
95847
- if (!shouldFilter) return options;
95848
- if (!limitWhenNotSearching) return options;
95849
- const query = searchQuery.trim().toLowerCase();
95850
- if (!query) {
95851
- return options.slice(0, limitWhenNotSearching);
95852
- }
95853
- const filtered = options.filter((option) => {
95854
- const searchableText = [
95855
- option.label,
95856
- option.description || "",
95857
- option.value
95858
- ].join(" ").toLowerCase();
95859
- return searchableText.includes(query);
95860
- });
95861
- return filtered.slice(0, 200);
95862
- }, [options, searchQuery, limitWhenNotSearching, shouldFilter]);
95863
- return /* @__PURE__ */ jsxs(
95864
- Popover,
95865
- {
95866
- modal: true,
95867
- open: disabled ? false : open,
95868
- onOpenChange: disabled ? void 0 : handleOpenChange,
95869
- children: [
95870
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
95871
- Button$1,
95872
- {
95873
- ref: triggerRef,
95874
- "aria-expanded": open,
95875
- disabled,
95876
- className: cn$2(
95877
- "w-full overflow-hidden justify-between",
95878
- disabled && "bg-gray-50 text-gray-700 cursor-not-allowed",
95879
- className
95880
- ),
95881
- variant: "outline",
95882
- children: [
95883
- /* @__PURE__ */ jsx(
95884
- "span",
95885
- {
95886
- className: "truncate",
95887
- title: value ? options.find((option) => option.value === value)?.label : placeholder,
95888
- children: value ? truncateTextForIcon(
95889
- options.find((option) => option.value === value)?.label || "",
95890
- 200,
95891
- 32
95892
- ) : placeholder
95893
- }
95894
- ),
95895
- /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 flex-shrink-0 opacity-50" })
95896
- ]
95897
- }
95898
- ) }),
95899
- /* @__PURE__ */ jsx(
95900
- PopoverContent,
95901
- {
95902
- align: "start",
95903
- style: {
95904
- width: triggerWidth ? `${triggerWidth}px` : "300px",
95905
- minWidth: triggerWidth ? `${triggerWidth}px` : "300px"
95906
- },
95907
- className: cn$2("p-0", className),
95908
- children: /* @__PURE__ */ jsxs(Command, { shouldFilter, children: [
95909
- !disableInput && /* @__PURE__ */ jsx(
95910
- CommandInput,
95911
- {
95912
- placeholder: `${placeholder}...`,
95913
- onValueChange: handleSearchChange
95914
- }
95915
- ),
95916
- /* @__PURE__ */ jsxs(CommandList, { children: [
95917
- !isLoading && showEmptyState && /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
95918
- /* @__PURE__ */ jsx(CommandGroup, { children: isLoading ? (
95919
- // Show skeleton loaders while loading
95920
- Array.from({ length: 5 }).map((_2, index2) => /* @__PURE__ */ jsx(CommandItem, { disabled: true, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 w-full", children: [
95921
- /* @__PURE__ */ jsx("div", { className: "h-4 w-4 bg-gray-200 rounded animate-pulse" }),
95922
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 flex-1", children: [
95923
- /* @__PURE__ */ jsx("div", { className: "h-4 bg-gray-200 rounded animate-pulse w-3/4" }),
95924
- /* @__PURE__ */ jsx("div", { className: "h-3 bg-gray-200 rounded animate-pulse w-1/2" })
95925
- ] })
95926
- ] }) }, `skeleton-${index2}`))
95927
- ) : displayOptions.map((option) => /* @__PURE__ */ jsxs(
95928
- CommandItem,
95929
- {
95930
- value: option.value,
95931
- onSelect: () => {
95932
- onChange2(option.value === value ? "" : option.value);
95933
- handleOpenChange(false);
95934
- },
95935
- children: [
95936
- /* @__PURE__ */ jsx(
95937
- Check,
95938
- {
95939
- className: cn$2(
95940
- "mr-2 h-4 w-4 flex-shrink-0",
95941
- value === option.value ? "opacity-100" : "opacity-0"
95942
- )
95943
- }
95944
- ),
95945
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col overflow-hidden flex-1 min-w-0", children: [
95946
- /* @__PURE__ */ jsx("span", { className: "font-medium break-words", children: option.label }),
95947
- option.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground break-words", children: option.description })
95948
- ] })
95949
- ]
95950
- },
95951
- option.id || option.value
95952
- )) })
95953
- ] })
95954
- ] })
95955
- }
95956
- )
95957
- ]
95958
- }
95959
- );
95960
- };
95961
96284
  const SendPreviewPopup = ({ type, setOpenSendPreviewPopup, open }) => {
95962
96285
  const [recipient, setRecipient] = useState("");
95963
96286
  const [error2, setError2] = useState("");
@@ -96104,9 +96427,6 @@ const SendPreviewPopup = ({ type, setOpenSendPreviewPopup, open }) => {
96104
96427
  setSearchResults(void 0);
96105
96428
  }
96106
96429
  }, [open]);
96107
- useEffect(() => {
96108
- setTimeout(() => document.body.style.pointerEvents = "", 0);
96109
- }, []);
96110
96430
  const humanReadableTypeLong = type === "email" ? "email address" : "phone number";
96111
96431
  const humanReadableTypeShort = type === "email" ? "email" : "text";
96112
96432
  const handleSendPreview = () => {
@@ -96212,52 +96532,86 @@ const SendPreviewPopup = ({ type, setOpenSendPreviewPopup, open }) => {
96212
96532
  /* @__PURE__ */ jsx("span", { className: "text-gray-500 font-normal", children: "(optional)" })
96213
96533
  ] })
96214
96534
  ] }),
96215
- /* @__PURE__ */ jsxs("div", { className: "flex items-stretch gap-2", children: [
96216
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(
96217
- ComboboxSelect,
96218
- {
96219
- options: recipientOptions,
96220
- value: selectedPreviewAsRecipientId,
96221
- onChange: (value) => {
96222
- setSelectedPreviewAsRecipientId(value);
96223
- },
96224
- placeholder: "Search for a customer...",
96225
- disableInput: false,
96226
- disabled: cantSendEmail,
96227
- onSearchChange: setSearchQuery,
96228
- isLoading: isLoadingUsers,
96229
- shouldFilter: false,
96230
- showEmptyState: debouncedSearchQuery.trim().length > 0
96231
- }
96232
- ) }),
96233
- selectedPreviewAsRecipientId && /* @__PURE__ */ jsx(
96535
+ selectedPreviewAsRecipientId && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-2 bg-blue-50 border border-blue-200 rounded-lg", children: [
96536
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-blue-900 truncate", children: recipientOptions.find(
96537
+ (o2) => o2.value === selectedPreviewAsRecipientId
96538
+ )?.label || selectedPreviewAsRecipientId }),
96539
+ /* @__PURE__ */ jsx(
96234
96540
  Button$1,
96235
96541
  {
96236
96542
  variant: "ghost",
96237
96543
  size: "icon",
96238
96544
  onClick: () => {
96239
96545
  setSelectedPreviewAsRecipientId("");
96546
+ setSearchQuery("");
96240
96547
  },
96241
- disabled: cantSendEmail,
96242
- className: `h-10 w-10 flex-shrink-0 rounded-lg hover:bg-gray-100 transition-colors ${cantSendEmail && "cursor-not-allowed"}`,
96548
+ className: "h-7 w-7 flex-shrink-0 rounded hover:bg-blue-100",
96243
96549
  title: "Clear selection",
96244
- children: /* @__PURE__ */ jsx(IconDefinitions.CloseButton, { className: "h-4 w-4 text-gray-500" })
96550
+ children: /* @__PURE__ */ jsx(IconDefinitions.CloseButton, { className: "h-3.5 w-3.5 text-blue-600" })
96245
96551
  }
96246
96552
  )
96247
96553
  ] }),
96248
- /* @__PURE__ */ jsxs("div", { className: "space-y-2 pl-6", children: [
96249
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-600 leading-relaxed", children: [
96250
- "Preview what the ",
96251
- humanReadableTypeShort,
96252
- " would look like with real customer data (merge tags like",
96253
- " ",
96254
- /* @__PURE__ */ jsx("code", { className: "px-1.5 py-0.5 bg-gray-100 text-gray-800 rounded text-xs font-mono", children: "{{First Name}}" }),
96255
- "). The ",
96256
- humanReadableTypeShort,
96257
- " will still be sent to the address you entered above."
96258
- ] }),
96259
- cantSendEmail && selectedPreviewAsRecipientId && /* @__PURE__ */ jsx("p", { className: "text-xs text-amber-600 font-medium", children: "⚠️ Please select a sender and subject first" })
96260
- ] })
96554
+ /* @__PURE__ */ jsxs(
96555
+ Command,
96556
+ {
96557
+ shouldFilter: false,
96558
+ className: "rounded-lg border overflow-visible",
96559
+ children: [
96560
+ /* @__PURE__ */ jsx(
96561
+ CommandInput,
96562
+ {
96563
+ placeholder: "Search for a customer...",
96564
+ onValueChange: setSearchQuery,
96565
+ disabled: cantSendEmail
96566
+ }
96567
+ ),
96568
+ (searchQuery.trim().length > 0 || isLoadingUsers) && /* @__PURE__ */ jsxs(CommandList, { className: "max-h-[150px] border-t", children: [
96569
+ !isLoadingUsers && debouncedSearchQuery.trim().length > 0 && recipientOptions.length === 0 && /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
96570
+ /* @__PURE__ */ jsx(CommandGroup, { children: isLoadingUsers ? Array.from({ length: 3 }).map((_2, index2) => /* @__PURE__ */ jsx(CommandItem, { disabled: true, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 w-full", children: [
96571
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-4 bg-gray-200 rounded animate-pulse" }),
96572
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 flex-1", children: [
96573
+ /* @__PURE__ */ jsx("div", { className: "h-4 bg-gray-200 rounded animate-pulse w-3/4" }),
96574
+ /* @__PURE__ */ jsx("div", { className: "h-3 bg-gray-200 rounded animate-pulse w-1/2" })
96575
+ ] })
96576
+ ] }) }, `skeleton-${index2}`)) : recipientOptions.map((option) => /* @__PURE__ */ jsxs(
96577
+ CommandItem,
96578
+ {
96579
+ value: option.value,
96580
+ onSelect: () => {
96581
+ setSelectedPreviewAsRecipientId(
96582
+ option.value === selectedPreviewAsRecipientId ? "" : option.value
96583
+ );
96584
+ setSearchQuery("");
96585
+ },
96586
+ children: [
96587
+ /* @__PURE__ */ jsx(
96588
+ Check,
96589
+ {
96590
+ className: cn$2(
96591
+ "mr-2 h-4 w-4 flex-shrink-0",
96592
+ selectedPreviewAsRecipientId === option.value ? "opacity-100" : "opacity-0"
96593
+ )
96594
+ }
96595
+ ),
96596
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col overflow-hidden flex-1 min-w-0", children: [
96597
+ /* @__PURE__ */ jsx("span", { className: "font-medium break-words text-sm", children: option.label }),
96598
+ option.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground break-words", children: option.description })
96599
+ ] })
96600
+ ]
96601
+ },
96602
+ option.id || option.value
96603
+ )) })
96604
+ ] })
96605
+ ]
96606
+ }
96607
+ ),
96608
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500 leading-relaxed", children: [
96609
+ "Optionally preview with real customer data (merge tags). The",
96610
+ " ",
96611
+ humanReadableTypeShort,
96612
+ " still goes to the address above."
96613
+ ] }),
96614
+ cantSendEmail && selectedPreviewAsRecipientId && /* @__PURE__ */ jsx("p", { className: "text-xs text-amber-600 font-medium", children: "⚠️ Please select a sender and subject first" })
96261
96615
  ] }),
96262
96616
  error2 && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 p-3 bg-red-50 border border-red-200 rounded-lg", children: [
96263
96617
  /* @__PURE__ */ jsx(IconDefinitions.AlertCircleIcon, { className: "h-4 w-4 text-red-600 mt-0.5 flex-shrink-0" }),
@@ -117636,15 +117990,22 @@ function MultiSelectDialog({
117636
117990
  variant: "outline",
117637
117991
  disabled: !canOpen,
117638
117992
  className: cn$2(
117639
- "w-full justify-between text-left font-normal h-9 p-3",
117993
+ "w-full justify-between text-left font-normal min-h-9 h-auto p-3",
117640
117994
  !canOpen && "opacity-50 cursor-not-allowed"
117641
117995
  ),
117642
117996
  children: [
117643
- /* @__PURE__ */ jsx("div", { className: "flex flex-nowrap items-center gap-1 max-w-[70%]", children: selectedDisplay.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder }) : singleSelect ? /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs whitespace-nowrap", children: selectedDisplay[0].label }) : selectedDisplay.length <= 2 ? selectedDisplay.map((item) => /* @__PURE__ */ jsx(
117997
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1 min-w-0 overflow-hidden", children: selectedDisplay.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder }) : singleSelect ? /* @__PURE__ */ jsx(
117644
117998
  Badge,
117645
117999
  {
117646
118000
  variant: "secondary",
117647
- className: "text-xs whitespace-nowrap",
118001
+ className: "text-xs truncate max-w-[200px]",
118002
+ children: selectedDisplay[0].label
118003
+ }
118004
+ ) : selectedDisplay.length <= 2 ? selectedDisplay.map((item) => /* @__PURE__ */ jsx(
118005
+ Badge,
118006
+ {
118007
+ variant: "secondary",
118008
+ className: "text-xs truncate max-w-[200px]",
117648
118009
  children: item.label
117649
118010
  },
117650
118011
  item.value
@@ -117653,7 +118014,7 @@ function MultiSelectDialog({
117653
118014
  Badge,
117654
118015
  {
117655
118016
  variant: "secondary",
117656
- className: "text-xs whitespace-nowrap",
118017
+ className: "text-xs truncate max-w-[200px]",
117657
118018
  children: selectedDisplay[0].label
117658
118019
  },
117659
118020
  selectedDisplay[0].value
@@ -117662,7 +118023,7 @@ function MultiSelectDialog({
117662
118023
  Badge,
117663
118024
  {
117664
118025
  variant: "secondary",
117665
- className: "text-xs whitespace-nowrap",
118026
+ className: "text-xs truncate max-w-[200px]",
117666
118027
  children: [
117667
118028
  "+",
117668
118029
  selectedDisplay.length - 1,
@@ -118151,7 +118512,7 @@ const SMSEditor = ({
118151
118512
  ]
118152
118513
  }
118153
118514
  ),
118154
- /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground pt-1 border-t", children: "Maximum size of all images: 5MB. We will try to compress the image if possible." })
118515
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground pt-1 border-t", children: "Images are converted to PNG and compressed if needed. After processing, the total size must be under 5MB. Large or complex images may need to be reduced in dimensions or quality before uploading." })
118155
118516
  ] }) })
118156
118517
  ]
118157
118518
  }
@@ -118215,7 +118576,7 @@ const SMSEditor = ({
118215
118576
  "Enter the URL of the image you want to add to your SMS message. You can use merge fields to make the URL dynamic (e.g., https://example.com/images/{{customer_name}}.jpg).",
118216
118577
  /* @__PURE__ */ jsx("br", {}),
118217
118578
  /* @__PURE__ */ jsx("br", {}),
118218
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Maximum size of all images: 5MB. We will try to compress the image if possible." })
118579
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Images are converted to PNG and compressed if needed. After processing, the total size must be under 5MB. Large or complex images may need to be reduced in dimensions or quality before uploading." })
118219
118580
  ] })
118220
118581
  ] }),
118221
118582
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
@@ -118838,6 +119199,26 @@ const SMSEditorContent = React__default.memo(
118838
119199
  if (fileInputRef.current) {
118839
119200
  fileInputRef.current.value = "";
118840
119201
  }
119202
+ const supportedTypes = [
119203
+ "image/jpeg",
119204
+ "image/jpg",
119205
+ "image/png",
119206
+ "image/gif",
119207
+ "image/webp"
119208
+ ];
119209
+ const fileExtension = file.name.split(".").pop()?.toLowerCase();
119210
+ const isUnsupportedFormat = !supportedTypes.includes(file.type) && !["jpg", "jpeg", "png", "gif", "webp"].includes(
119211
+ fileExtension || ""
119212
+ );
119213
+ if (isUnsupportedFormat) {
119214
+ const formatName = fileExtension?.toUpperCase() || "this format";
119215
+ toast2({
119216
+ title: "Unsupported image format",
119217
+ description: `${formatName} files are not supported. Please convert your image to JPEG, PNG, GIF, or WebP format before uploading.`,
119218
+ variant: "destructive"
119219
+ });
119220
+ return;
119221
+ }
118841
119222
  const MAX_FILE_SIZE = 20 * 1024 * 1024;
118842
119223
  if (file.size > MAX_FILE_SIZE) {
118843
119224
  console.log("File too large", file.size, MAX_FILE_SIZE);
@@ -118873,10 +119254,12 @@ const SMSEditorContent = React__default.memo(
118873
119254
  });
118874
119255
  },
118875
119256
  onError: (data) => {
118876
- if (data.message.includes("Image is still too large")) {
119257
+ console.error("Failed to upload image:", data);
119258
+ const errorMessage = data.message || "";
119259
+ if (errorMessage.includes("too large") || errorMessage.includes("exceeds") || errorMessage.includes("Unable to compress") || errorMessage.includes("4.95MB") || errorMessage.includes("5MB")) {
118877
119260
  toast2({
118878
- title: "Image too large",
118879
- description: "The image is too large. Please try again with a smaller image.",
119261
+ title: "Image too large after processing",
119262
+ description: "Your image was converted to PNG format, which may have increased its size. The processed image must be under 5MB. Try reducing the image dimensions or quality before uploading.",
118880
119263
  variant: "destructive"
118881
119264
  });
118882
119265
  } else {
@@ -118890,12 +119273,24 @@ const SMSEditorContent = React__default.memo(
118890
119273
  }
118891
119274
  );
118892
119275
  } catch (error2) {
119276
+ console.error("Failed to upload image:", error2);
118893
119277
  console.error("Failed to convert or upload image:", error2);
118894
- toast2({
118895
- title: "Error uploading image",
118896
- description: "Failed to process or upload the image",
118897
- variant: "destructive"
118898
- });
119278
+ const errorMessage = error2 instanceof Error ? error2.message : String(error2);
119279
+ const isConversionError = errorMessage.includes("Failed to load image") || errorMessage.includes("convert") || errorMessage.includes("load");
119280
+ if (isConversionError) {
119281
+ const fileExtension2 = file.name.split(".").pop()?.toUpperCase();
119282
+ toast2({
119283
+ title: "Image format not supported",
119284
+ description: `Unable to process ${fileExtension2 || "this image"} format. Please convert your image to JPEG, PNG, GIF, or WebP format before uploading.`,
119285
+ variant: "destructive"
119286
+ });
119287
+ } else {
119288
+ toast2({
119289
+ title: "Error uploading image",
119290
+ description: "Failed to process or upload the image. Please try again with a different image.",
119291
+ variant: "destructive"
119292
+ });
119293
+ }
118899
119294
  } finally {
118900
119295
  setIsUploadingImage(false);
118901
119296
  }
@@ -120750,14 +121145,14 @@ const operatorToHumanReadable = (operator, type) => {
120750
121145
  case ConditionOperatorEnumType.NOT_EQUALS:
120751
121146
  return "Not Equals";
120752
121147
  case ConditionOperatorEnumType.GREATER_THAN: {
120753
- if (type === JSONSchemaType.Date) {
120754
- return "Is After Date";
121148
+ if (type === JSONSchemaType.Date || type === JSONSchemaType.DateTime) {
121149
+ return "After";
120755
121150
  }
120756
121151
  return "Greater than";
120757
121152
  }
120758
121153
  case ConditionOperatorEnumType.LESS_THAN: {
120759
- if (type === JSONSchemaType.Date) {
120760
- return "Is Before Date";
121154
+ if (type === JSONSchemaType.Date || type === JSONSchemaType.DateTime) {
121155
+ return "Before";
120761
121156
  }
120762
121157
  return "Less Than";
120763
121158
  }
@@ -120785,6 +121180,164 @@ const buildInFieldToHumanReadable = {
120785
121180
  firstName: "First Name",
120786
121181
  lastName: "Last Name"
120787
121182
  };
121183
+ const ComboboxSelect = ({
121184
+ options,
121185
+ value,
121186
+ onChange: onChange2,
121187
+ placeholder,
121188
+ className = "",
121189
+ disableInput = false,
121190
+ disabled = false,
121191
+ onOpenChange,
121192
+ limitWhenNotSearching,
121193
+ onSearchChange,
121194
+ isLoading = false,
121195
+ shouldFilter = true,
121196
+ showEmptyState = true
121197
+ }) => {
121198
+ const [open, setOpen2] = React.useState(false);
121199
+ const [searchQuery, setSearchQuery] = React.useState("");
121200
+ const triggerRef = React.useRef(null);
121201
+ const [triggerWidth, setTriggerWidth] = React.useState();
121202
+ React.useEffect(() => {
121203
+ if (triggerRef.current && open) {
121204
+ setTriggerWidth(triggerRef.current.offsetWidth);
121205
+ }
121206
+ }, [open]);
121207
+ const handleOpenChange = (newOpen) => {
121208
+ setOpen2(newOpen);
121209
+ if (!newOpen && !onSearchChange) {
121210
+ setSearchQuery("");
121211
+ }
121212
+ onOpenChange?.(newOpen);
121213
+ if (newOpen && value) {
121214
+ requestAnimationFrame(() => {
121215
+ const selected = document.querySelector(
121216
+ `[cmdk-item][data-value="${CSS.escape(value)}"]`
121217
+ );
121218
+ selected?.scrollIntoView({ block: "nearest" });
121219
+ });
121220
+ }
121221
+ };
121222
+ const handleSearchChange = (search2) => {
121223
+ setSearchQuery(search2);
121224
+ onSearchChange?.(search2);
121225
+ };
121226
+ const displayOptions = React.useMemo(() => {
121227
+ if (!shouldFilter) return options;
121228
+ if (!limitWhenNotSearching) return options;
121229
+ const query = searchQuery.trim().toLowerCase();
121230
+ if (!query) {
121231
+ return options.slice(0, limitWhenNotSearching);
121232
+ }
121233
+ const filtered = options.filter((option) => {
121234
+ const searchableText = [
121235
+ option.label,
121236
+ option.description || "",
121237
+ option.value
121238
+ ].join(" ").toLowerCase();
121239
+ return searchableText.includes(query);
121240
+ });
121241
+ return filtered.slice(0, 200);
121242
+ }, [options, searchQuery, limitWhenNotSearching, shouldFilter]);
121243
+ return /* @__PURE__ */ jsxs(
121244
+ Popover,
121245
+ {
121246
+ modal: true,
121247
+ open: disabled ? false : open,
121248
+ onOpenChange: disabled ? void 0 : handleOpenChange,
121249
+ children: [
121250
+ /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
121251
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
121252
+ Button$1,
121253
+ {
121254
+ ref: triggerRef,
121255
+ "aria-expanded": open,
121256
+ disabled,
121257
+ className: cn$2(
121258
+ "w-full overflow-hidden justify-between",
121259
+ disabled && "bg-gray-50 text-gray-700 cursor-not-allowed",
121260
+ className
121261
+ ),
121262
+ variant: "outline",
121263
+ children: [
121264
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: value ? options.find((option) => option.value === value)?.label || value : placeholder }),
121265
+ /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 flex-shrink-0 opacity-50" })
121266
+ ]
121267
+ }
121268
+ ) }) }),
121269
+ value && options.find((option) => option.value === value)?.label && /* @__PURE__ */ jsx(TooltipContent, { side: "top", children: options.find((option) => option.value === value)?.label })
121270
+ ] }) }),
121271
+ /* @__PURE__ */ jsx(Portal$5, { children: /* @__PURE__ */ jsx(
121272
+ PopoverContent,
121273
+ {
121274
+ align: "start",
121275
+ style: {
121276
+ width: triggerWidth ? `${Math.max(triggerWidth, 300)}px` : "300px",
121277
+ minWidth: "300px"
121278
+ },
121279
+ className: cn$2("p-0", className),
121280
+ children: /* @__PURE__ */ jsxs(Command, { shouldFilter, children: [
121281
+ !disableInput && /* @__PURE__ */ jsx(
121282
+ CommandInput,
121283
+ {
121284
+ placeholder: `${placeholder}...`,
121285
+ onValueChange: handleSearchChange
121286
+ }
121287
+ ),
121288
+ /* @__PURE__ */ jsxs(CommandList, { children: [
121289
+ !isLoading && showEmptyState && /* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }),
121290
+ /* @__PURE__ */ jsx(CommandGroup, { children: isLoading ? (
121291
+ // Show skeleton loaders while loading
121292
+ Array.from({ length: 5 }).map((_2, index2) => /* @__PURE__ */ jsx(CommandItem, { disabled: true, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 w-full", children: [
121293
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-4 bg-gray-200 rounded animate-pulse" }),
121294
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 flex-1", children: [
121295
+ /* @__PURE__ */ jsx("div", { className: "h-4 bg-gray-200 rounded animate-pulse w-3/4" }),
121296
+ /* @__PURE__ */ jsx("div", { className: "h-3 bg-gray-200 rounded animate-pulse w-1/2" })
121297
+ ] })
121298
+ ] }) }, `skeleton-${index2}`))
121299
+ ) : displayOptions.map((option) => /* @__PURE__ */ jsxs(
121300
+ CommandItem,
121301
+ {
121302
+ value: option.value,
121303
+ onSelect: () => {
121304
+ onChange2(option.value === value ? "" : option.value);
121305
+ handleOpenChange(false);
121306
+ },
121307
+ children: [
121308
+ /* @__PURE__ */ jsx(
121309
+ Check,
121310
+ {
121311
+ className: cn$2(
121312
+ "mr-2 h-4 w-4 flex-shrink-0",
121313
+ value === option.value ? "opacity-100" : "opacity-0"
121314
+ )
121315
+ }
121316
+ ),
121317
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col overflow-hidden flex-1 min-w-0", children: [
121318
+ /* @__PURE__ */ jsx("span", { className: "font-medium break-words", children: option.label }),
121319
+ option.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground break-words", children: option.description })
121320
+ ] })
121321
+ ]
121322
+ },
121323
+ option.id || option.value
121324
+ )) })
121325
+ ] })
121326
+ ] })
121327
+ }
121328
+ ) })
121329
+ ]
121330
+ }
121331
+ );
121332
+ };
121333
+ const truncateTextForIcon = (text2, maxWidth = 200, iconWidth = 32) => {
121334
+ if (text2.length <= 20) return text2;
121335
+ const avgCharWidth = 8;
121336
+ const availableWidth = maxWidth - iconWidth - 16;
121337
+ const maxChars = Math.floor(availableWidth / avgCharWidth);
121338
+ if (text2.length <= maxChars) return text2;
121339
+ return text2.substring(0, maxChars - 3) + "...";
121340
+ };
120788
121341
  function composeEventHandlers$1(originalEventHandler, ourEventHandler, { checkForDefaultPrevented = true } = {}) {
120789
121342
  return function handleEvent(event) {
120790
121343
  originalEventHandler?.(event);
@@ -123322,8 +123875,8 @@ const ConditionRow = ({
123322
123875
  className: `bg-white rounded-lg border border-gray-200 p-4 transition-all duration-200 ${viewOnly ? "bg-gray-50 border-gray-300" : "hover:border-gray-300 hover:shadow-sm"}`,
123323
123876
  onMouseEnter: !viewOnly ? handleMouseEnter : void 0,
123324
123877
  onMouseLeave: !viewOnly ? handleMouseLeave : void 0,
123325
- children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row items-stretch gap-3 min-w-0 w-full", children: [
123326
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex items-center gap-2", children: [
123878
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:grid sm:grid-cols-[minmax(120px,3fr)_minmax(100px,2fr)_minmax(100px,3fr)] items-stretch gap-3 min-w-0 w-full", children: [
123879
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex items-center gap-2", children: [
123327
123880
  (metadata2.description || condition.field) && /* @__PURE__ */ jsx(
123328
123881
  InfoTooltip,
123329
123882
  {
@@ -123399,7 +123952,7 @@ const ConditionRow = ({
123399
123952
  }
123400
123953
  )
123401
123954
  ] }),
123402
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 max-w-xs flex gap-2 items-center", children: [
123955
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex gap-2 items-center", children: [
123403
123956
  (condition.operator === ConditionOperatorEnumType.EQUALS_MONTH_DAY || condition.operator === ConditionOperatorEnumType.EQUALS_MONTH_DAY_YEAR || condition.operator === ConditionOperatorEnumType.BETWEEN_MONTH_DAY || condition.operator === ConditionOperatorEnumType.BETWEEN_MONTH_DAY_YEAR) && /* @__PURE__ */ jsx("div", { className: "flex items-center", children: getInfoTooltip(condition.operator) }),
123404
123957
  /* @__PURE__ */ jsx(
123405
123958
  ComboboxSelect,
@@ -123424,7 +123977,7 @@ const ConditionRow = ({
123424
123977
  }
123425
123978
  )
123426
123979
  ] }),
123427
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0 max-w-full overflow-x-hidden", children: /* @__PURE__ */ jsx(
123980
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 overflow-x-hidden", children: /* @__PURE__ */ jsx(
123428
123981
  ValueInput,
123429
123982
  {
123430
123983
  fieldType,
@@ -123475,7 +124028,8 @@ const SegmentBuilderContent = ({
123475
124028
  businessId,
123476
124029
  viewOnly = false,
123477
124030
  segmentType,
123478
- existingSegmentConditions
124031
+ existingSegmentConditions,
124032
+ onEstimatedTotalChange
123479
124033
  }) => {
123480
124034
  const [searchQuery, setSearchQuery] = React.useState("");
123481
124035
  const [limit, setLimit] = React.useState(100);
@@ -123483,9 +124037,13 @@ const SegmentBuilderContent = ({
123483
124037
  () => JSON.stringify(segment2.conditions),
123484
124038
  [segment2.conditions]
123485
124039
  );
124040
+ const conditionGroupsKey = React.useMemo(
124041
+ () => JSON.stringify(segment2.conditionGroups),
124042
+ [segment2.conditionGroups]
124043
+ );
123486
124044
  React.useEffect(() => {
123487
124045
  setLimit(100);
123488
- }, [searchQuery, conditionsKey]);
124046
+ }, [searchQuery, conditionsKey, conditionGroupsKey]);
123489
124047
  const {
123490
124048
  data: recipientsData,
123491
124049
  isLoading,
@@ -123498,7 +124056,8 @@ const SegmentBuilderContent = ({
123498
124056
  {
123499
124057
  debounceMs: 500,
123500
124058
  search: searchQuery,
123501
- limit
124059
+ limit,
124060
+ conditionGroups: segment2.conditionGroups
123502
124061
  },
123503
124062
  existingSegmentConditions
123504
124063
  );
@@ -123510,6 +124069,9 @@ const SegmentBuilderContent = ({
123510
124069
  })) ?? [];
123511
124070
  const estimatedTotal = recipientsData?.estimatedTotal ?? 0;
123512
124071
  const hasMore = recipientsData?.pagination?.hasNextPage ?? false;
124072
+ React.useEffect(() => {
124073
+ onEstimatedTotalChange?.(estimatedTotal);
124074
+ }, [estimatedTotal, onEstimatedTotalChange]);
123513
124075
  const isLoadingMoreRef = React.useRef(false);
123514
124076
  const handleLoadMore = React.useCallback(() => {
123515
124077
  if (isLoadingMoreRef.current || isLoading || !hasMore) return;
@@ -123519,12 +124081,12 @@ const SegmentBuilderContent = ({
123519
124081
  isLoadingMoreRef.current = false;
123520
124082
  }, 1e3);
123521
124083
  }, [isLoading, hasMore]);
123522
- return /* @__PURE__ */ jsx(BlurDiv, { className: "flex-1 flex flex-col overflow-visible", children: /* @__PURE__ */ jsxs("div", { className: "flex h-full gap-6 overflow-visible", children: [
123523
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col overflow-visible", children: [
123524
- /* @__PURE__ */ jsxs("div", { className: "mb-6 grid grid-cols-2 gap-2", children: [
123525
- /* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
123526
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0", children: [
123527
- /* @__PURE__ */ jsx(MinorText, { children: "Name" }),
124084
+ return /* @__PURE__ */ jsxs(BlurDiv, { className: "flex-1 flex flex-col xl:flex-row gap-8 overflow-visible min-h-0", children: [
124085
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col min-w-0 overflow-x-hidden px-1", children: [
124086
+ /* @__PURE__ */ jsxs("div", { className: "mb-8 space-y-5", children: [
124087
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
124088
+ /* @__PURE__ */ jsxs("label", { className: "text-sm font-medium text-neutral-900 flex items-center gap-0", children: [
124089
+ "Name",
123528
124090
  !viewOnly && /* @__PURE__ */ jsx("span", { className: "text-xs text-red-500 ml-1 align-super", children: "*" })
123529
124091
  ] }),
123530
124092
  /* @__PURE__ */ jsx(
@@ -123538,10 +124100,13 @@ const SegmentBuilderContent = ({
123538
124100
  }
123539
124101
  )
123540
124102
  ] }),
123541
- /* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
123542
- /* @__PURE__ */ jsx(MinorText, { children: "Description" }),
124103
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
124104
+ /* @__PURE__ */ jsxs("label", { className: "text-sm font-medium text-neutral-900 flex items-center gap-2", children: [
124105
+ "Description",
124106
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-normal text-neutral-400", children: "Optional" })
124107
+ ] }),
123543
124108
  /* @__PURE__ */ jsx(
123544
- Input$1,
124109
+ Textarea,
123545
124110
  {
123546
124111
  placeholder: `${t$2("engage:segment")} description`,
123547
124112
  value: segment2.description,
@@ -123550,11 +124115,13 @@ const SegmentBuilderContent = ({
123550
124115
  description: e4.target.value
123551
124116
  }),
123552
124117
  disabled: viewOnly,
123553
- className: viewOnly ? "bg-gray-50 text-gray-700" : ""
124118
+ className: viewOnly ? "bg-gray-50 text-gray-700 resize-none" : "resize-none",
124119
+ rows: 2
123554
124120
  }
123555
124121
  )
123556
124122
  ] })
123557
124123
  ] }),
124124
+ /* @__PURE__ */ jsx("div", { className: "h-px bg-neutral-200 mb-8" }),
123558
124125
  segmentType === "sql" && /* @__PURE__ */ jsxs("div", { className: "mb-6 p-4 bg-blue-50 border border-blue-200 rounded-lg", children: [
123559
124126
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
123560
124127
  /* @__PURE__ */ jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full" }),
@@ -123562,7 +124129,11 @@ const SegmentBuilderContent = ({
123562
124129
  ] }),
123563
124130
  /* @__PURE__ */ jsx("p", { className: "text-sm text-blue-600 mt-1", children: "This segment is managed by your admin and cannot be edited here." })
123564
124131
  ] }),
123565
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto overflow-x-hidden pb-8", children: /* @__PURE__ */ jsx("div", { className: "grid overflow-visible", children: /* @__PURE__ */ jsxs(AnimatePresence, { mode: "popLayout", children: [
124132
+ /* @__PURE__ */ jsxs("div", { className: "mb-5", children: [
124133
+ /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-neutral-900", children: "Audience Conditions" }),
124134
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-neutral-500 mt-1", children: "Users must match all conditions to be included" })
124135
+ ] }),
124136
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto overflow-x-hidden pb-8", children: /* @__PURE__ */ jsx("div", { className: "grid overflow-visible gap-1", children: /* @__PURE__ */ jsxs(AnimatePresence, { mode: "popLayout", children: [
123566
124137
  segmentType !== "sql" && segment2.conditions.map((condition, index2) => {
123567
124138
  return /* @__PURE__ */ jsxs(
123568
124139
  motion.div,
@@ -123581,7 +124152,7 @@ const SegmentBuilderContent = ({
123581
124152
  index2 > 0 && /* @__PURE__ */ jsxs(
123582
124153
  motion.div,
123583
124154
  {
123584
- className: "relative flex items-center py-2",
124155
+ className: "relative flex items-center py-3",
123585
124156
  initial: { opacity: 0 },
123586
124157
  animate: { opacity: 1 },
123587
124158
  transition: { delay: 0.1 },
@@ -123589,7 +124160,7 @@ const SegmentBuilderContent = ({
123589
124160
  /* @__PURE__ */ jsx(
123590
124161
  motion.div,
123591
124162
  {
123592
- className: "h-px flex-1 bg-border",
124163
+ className: "h-px flex-1 bg-neutral-200",
123593
124164
  initial: { scaleX: 0 },
123594
124165
  animate: { scaleX: 1 },
123595
124166
  transition: {
@@ -123603,7 +124174,7 @@ const SegmentBuilderContent = ({
123603
124174
  /* @__PURE__ */ jsx(
123604
124175
  motion.span,
123605
124176
  {
123606
- className: "text-muted-foreground text-xs",
124177
+ className: "bg-neutral-100 text-neutral-500 text-xs font-medium rounded-full px-3 py-1 mx-3 border border-neutral-200",
123607
124178
  initial: { opacity: 0 },
123608
124179
  animate: { opacity: 1 },
123609
124180
  transition: { delay: 0.2 },
@@ -123613,7 +124184,7 @@ const SegmentBuilderContent = ({
123613
124184
  /* @__PURE__ */ jsx(
123614
124185
  motion.div,
123615
124186
  {
123616
- className: "h-px flex-1 bg-border",
124187
+ className: "h-px flex-1 bg-neutral-200",
123617
124188
  initial: { scaleX: 0 },
123618
124189
  animate: { scaleX: 1 },
123619
124190
  transition: {
@@ -123656,21 +124227,36 @@ const SegmentBuilderContent = ({
123656
124227
  mass: 0.5
123657
124228
  },
123658
124229
  className: "pt-4",
123659
- children: /* @__PURE__ */ jsxs(Button$1, { variant: "secondary", onClick: addCondition, children: [
123660
- /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
123661
- "Add Condition"
123662
- ] })
124230
+ children: /* @__PURE__ */ jsxs(
124231
+ "button",
124232
+ {
124233
+ onClick: addCondition,
124234
+ className: "w-full flex items-center justify-center gap-2 border-2 border-dashed border-neutral-300 rounded-lg py-3 text-sm text-neutral-500 hover:border-neutral-400 hover:text-neutral-700 hover:bg-neutral-50 transition-colors",
124235
+ children: [
124236
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
124237
+ "Add Condition"
124238
+ ]
124239
+ }
124240
+ )
123663
124241
  },
123664
124242
  "add-condition-button"
123665
124243
  )
123666
124244
  ] }) }) })
123667
124245
  ] }),
123668
- /* @__PURE__ */ jsxs("div", { className: "w-96 flex flex-col h-full overflow-hidden", children: [
123669
- /* @__PURE__ */ jsxs("div", { className: "mb-4 flex-shrink-0", children: [
123670
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-foreground", children: "Recipients Preview" }),
123671
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: isLoading ? "Loading recipients..." : estimatedTotal > 0 ? `${estimatedTotal.toLocaleString()} users match your conditions` : "No users match your conditions" })
124246
+ /* @__PURE__ */ jsx("div", { className: "h-px bg-neutral-200 xl:hidden" }),
124247
+ /* @__PURE__ */ jsx("div", { className: "w-full xl:w-96 flex flex-col min-h-0 xl:h-full overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "border border-neutral-200 rounded-lg flex flex-col flex-1 min-h-0 overflow-hidden", children: [
124248
+ /* @__PURE__ */ jsxs("div", { className: "p-4 border-b border-neutral-100 flex-shrink-0", children: [
124249
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
124250
+ /* @__PURE__ */ jsx(Users, { className: "h-4 w-4 text-neutral-500" }),
124251
+ /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-neutral-900", children: "Audience Preview" })
124252
+ ] }),
124253
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-neutral-500", children: isLoading ? "Loading recipients..." : estimatedTotal > 0 ? `${estimatedTotal.toLocaleString()} users match your conditions` : "No users match your conditions" }),
124254
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 inline-flex items-center gap-1.5 bg-neutral-100 text-neutral-500 text-xs rounded-md px-2 py-1", children: [
124255
+ /* @__PURE__ */ jsx(Info, { className: "h-3 w-3" }),
124256
+ "Preview based on sample data"
124257
+ ] })
123672
124258
  ] }),
123673
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 flex flex-col", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsx("div", { className: "animate-spin h-6 w-6 border-2 border-blue-600 border-t-transparent rounded-full" }) }) : error2 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full text-red-600", children: /* @__PURE__ */ jsx("p", { children: "Error loading recipients" }) }) : /* @__PURE__ */ jsx(
124259
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 flex flex-col overflow-hidden", children: isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full p-8", children: /* @__PURE__ */ jsx("div", { className: "animate-spin h-6 w-6 border-2 border-blue-600 border-t-transparent rounded-full" }) }) : error2 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full text-red-600 p-8", children: /* @__PURE__ */ jsx("p", { children: "Error loading recipients" }) }) : /* @__PURE__ */ jsx(
123674
124260
  RecipientsTable,
123675
124261
  {
123676
124262
  recipients,
@@ -123686,8 +124272,8 @@ const SegmentBuilderContent = ({
123686
124272
  isLoading
123687
124273
  }
123688
124274
  ) })
123689
- ] })
123690
- ] }) });
124275
+ ] }) })
124276
+ ] });
123691
124277
  };
123692
124278
  const SegmentBuilderHeader = ({
123693
124279
  segmentId,
@@ -123698,16 +124284,22 @@ const SegmentBuilderHeader = ({
123698
124284
  viewOnly = false
123699
124285
  }) => {
123700
124286
  return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(DialogHeader$1, { className: "", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
123701
- /* @__PURE__ */ jsx(DialogTitle, { className: "text-3xl font-normal", children: viewOnly ? `View ${t$2("engage:segment")}` : segmentId ? `Edit ${t$2("engage:segment")}` : `Create ${t$2("engage:segment")}` }),
124287
+ /* @__PURE__ */ jsxs("div", { children: [
124288
+ /* @__PURE__ */ jsx(DialogTitle, { className: "text-3xl font-normal", children: viewOnly ? `View ${t$2("engage:segment")}` : segmentId ? `Edit ${t$2("engage:segment")}` : `Create ${t$2("engage:segment")}` }),
124289
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Define conditions to target specific user groups" })
124290
+ ] }),
123702
124291
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
123703
124292
  !viewOnly && promptBuilderStyle === false && textToSegment2 === false && /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
123704
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
124293
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
123705
124294
  Button$1,
123706
124295
  {
123707
124296
  variant: "outline",
123708
124297
  size: "sm",
123709
124298
  onClick: () => setTextToSegment(true),
123710
- children: /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-3" })
124299
+ children: [
124300
+ /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" }),
124301
+ "Build with AI"
124302
+ ]
123711
124303
  }
123712
124304
  ) }),
123713
124305
  /* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsxs("p", { children: [
@@ -123990,12 +124582,18 @@ function SegmentBuilder({
123990
124582
  viewOnly ? false : newSegment ? true : false
123991
124583
  );
123992
124584
  const [showDeleteDialog, setShowDeleteDialog] = React.useState(false);
124585
+ const [estimatedTotal, setEstimatedTotal] = React.useState(0);
124586
+ const handleEstimatedTotalChange = React.useCallback((total2) => {
124587
+ setEstimatedTotal(total2);
124588
+ }, []);
123993
124589
  React.useEffect(() => {
123994
124590
  if (!segmentsConditions) {
123995
124591
  return;
123996
124592
  }
123997
124593
  if (existingSegment) {
123998
- const mappedConditions = existingSegment.conditions.filter(isFieldCondition).map((condition) => ({
124594
+ const conditionsList = existingSegment.conditions;
124595
+ const mappedConditions = conditionsList.filter((c3) => isFieldCondition(c3)).map((condition) => ({
124596
+ type: "property",
123999
124597
  field: condition.field,
124000
124598
  operator: condition.operator,
124001
124599
  value: condition.value
@@ -124007,6 +124605,7 @@ function SegmentBuilder({
124007
124605
  description: existingSegment.description || "",
124008
124606
  logic: "AND",
124009
124607
  conditions: mappedConditions,
124608
+ conditionGroups: existingSegment.conditionGroups,
124010
124609
  type: existingSegment.type,
124011
124610
  createdAt: existingSegment.createdAt
124012
124611
  });
@@ -124023,6 +124622,7 @@ function SegmentBuilder({
124023
124622
  * Use the first fields of segment conditions to create a new segment
124024
124623
  */
124025
124624
  {
124625
+ type: "property",
124026
124626
  field: segmentsConditions.conditions[0].field,
124027
124627
  operator: segmentsConditions.conditions[0].operators[0],
124028
124628
  value: []
@@ -124327,6 +124927,7 @@ function SegmentBuilder({
124327
124927
  };
124328
124928
  const addCondition = () => {
124329
124929
  const newCondition = {
124930
+ type: "property",
124330
124931
  field: segmentsConditions.conditions[0].field,
124331
124932
  operator: segmentsConditions.conditions[0].operators[0],
124332
124933
  value: []
@@ -124366,117 +124967,135 @@ function SegmentBuilder({
124366
124967
  )
124367
124968
  },
124368
124969
  "prompt-builder-style"
124369
- ) : /* @__PURE__ */ jsx(BlurDiv, { className: "flex-1 flex flex-col", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col", children: [
124370
- /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col px-8 py-2", children: /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col gap-4", children: /* @__PURE__ */ jsx(AnimatePresence, { children: !viewOnly && textToSegment2 ? /* @__PURE__ */ jsx(BlurDiv, { className: "flex justify-center min-h-full overflow-auto", children: /* @__PURE__ */ jsx(
124371
- TextToSegment,
124372
- {
124373
- setTextToSegment,
124374
- setSegment: (conditions) => {
124375
- setSegment((prevSegment) => {
124376
- if (!prevSegment) {
124377
- return null;
124378
- }
124379
- return {
124380
- ...prevSegment,
124381
- conditions
124382
- };
124383
- });
124384
- },
124385
- setSegmentName: (name) => {
124386
- setSegment((prevSegment) => {
124387
- if (!prevSegment) {
124388
- return null;
124389
- }
124390
- const newSegment2 = { ...prevSegment, name };
124391
- return newSegment2;
124392
- });
124393
- },
124394
- setSegmentDescription: (description2) => {
124395
- setSegment((prevSegment) => {
124396
- if (!prevSegment) {
124397
- return null;
124398
- }
124399
- const newSegment2 = { ...prevSegment, description: description2 };
124400
- return newSegment2;
124401
- });
124402
- },
124403
- currentSegment: segment2
124404
- }
124405
- ) }) : /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(
124406
- SegmentBuilderContent,
124407
- {
124408
- segment: segment2,
124409
- setSegment,
124410
- segmentsConditions: {
124411
- conditions: segmentsConditions?.conditions.map((c3) => ({
124412
- ...c3,
124413
- operators: c3.operators
124414
- })) ?? []
124415
- },
124416
- updateCondition,
124417
- duplicateCondition,
124418
- deleteCondition,
124419
- addCondition,
124420
- businessId: business?.id ?? "",
124421
- viewOnly,
124422
- segmentType: existingSegment?.type,
124423
- existingSegmentConditions: existingSegment?.conditions
124424
- }
124425
- ) }) }) }) }),
124426
- /* @__PURE__ */ jsx("div", { className: "border-t px-8 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
124427
- !viewOnly && textToSegment2 && /* @__PURE__ */ jsxs(
124428
- Button$1,
124429
- {
124430
- variant: "secondary",
124431
- onClick: () => setTextToSegment(false),
124432
- children: [
124433
- /* @__PURE__ */ jsx(Settings2, { className: "h-4 w-4" }),
124434
- "Manual Builder"
124435
- ]
124436
- }
124437
- ),
124438
- /* @__PURE__ */ jsx("div", { className: "flex gap-2 ml-auto", children: viewOnly ? (
124439
- // VIEW-ONLY: Only Close button
124440
- /* @__PURE__ */ jsx(Button$1, { variant: "default", onClick: () => setOpen2(false), children: "Close" })
124441
- ) : (
124442
- // EDIT MODE: Delete (if editing), Cancel, and Save buttons
124443
- /* @__PURE__ */ jsxs(Fragment$1, { children: [
124444
- segmentId && !newSegment && /* @__PURE__ */ jsxs(
124445
- Button$1,
124446
- {
124447
- variant: "destructive",
124448
- onClick: () => setShowDeleteDialog(true),
124449
- disabled: isDeleting,
124450
- children: [
124451
- /* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
124452
- "Delete"
124453
- ]
124454
- }
124455
- ),
124456
- /* @__PURE__ */ jsx(
124457
- Button$1,
124458
- {
124459
- variant: "secondary",
124460
- onClick: () => setOpen2(false),
124461
- children: "Cancel"
124462
- }
124463
- ),
124464
- /* @__PURE__ */ jsx(
124465
- Button$1,
124466
- {
124467
- variant: "default",
124468
- onClick: handleSave,
124469
- disabled: isLoading || isCreating,
124470
- children: isLoading || isCreating ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
124471
- /* @__PURE__ */ jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }),
124472
- segmentId ? "Updating..." : "Creating..."
124473
- ] }) : segmentId ? "Update" : "Create"
124474
- }
124475
- )
124476
- ] })
124477
- ) })
124478
- ] }) })
124479
- ] }) }, "manual-builder-style"),
124970
+ ) : /* @__PURE__ */ jsx(
124971
+ BlurDiv,
124972
+ {
124973
+ className: "flex-1 flex flex-col min-h-0",
124974
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col min-h-0", children: [
124975
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col px-2 py-2 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col gap-4", children: /* @__PURE__ */ jsx(AnimatePresence, { children: !viewOnly && textToSegment2 ? /* @__PURE__ */ jsx(BlurDiv, { className: "flex justify-center min-h-full overflow-hidden", children: /* @__PURE__ */ jsx(
124976
+ TextToSegment,
124977
+ {
124978
+ setTextToSegment,
124979
+ setSegment: (conditions) => {
124980
+ setSegment((prevSegment) => {
124981
+ if (!prevSegment) {
124982
+ return null;
124983
+ }
124984
+ return {
124985
+ ...prevSegment,
124986
+ conditions
124987
+ };
124988
+ });
124989
+ },
124990
+ setSegmentName: (name) => {
124991
+ setSegment((prevSegment) => {
124992
+ if (!prevSegment) {
124993
+ return null;
124994
+ }
124995
+ const newSegment2 = { ...prevSegment, name };
124996
+ return newSegment2;
124997
+ });
124998
+ },
124999
+ setSegmentDescription: (description2) => {
125000
+ setSegment((prevSegment) => {
125001
+ if (!prevSegment) {
125002
+ return null;
125003
+ }
125004
+ const newSegment2 = { ...prevSegment, description: description2 };
125005
+ return newSegment2;
125006
+ });
125007
+ },
125008
+ currentSegment: segment2
125009
+ }
125010
+ ) }) : /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(
125011
+ SegmentBuilderContent,
125012
+ {
125013
+ segment: segment2,
125014
+ setSegment,
125015
+ segmentsConditions: {
125016
+ conditions: segmentsConditions?.conditions.map((c3) => ({
125017
+ ...c3,
125018
+ operators: c3.operators
125019
+ })) ?? []
125020
+ },
125021
+ updateCondition,
125022
+ duplicateCondition,
125023
+ deleteCondition,
125024
+ addCondition,
125025
+ businessId: business?.id ?? "",
125026
+ viewOnly,
125027
+ segmentType: existingSegment?.type,
125028
+ existingSegmentConditions: existingSegment?.conditions,
125029
+ onEstimatedTotalChange: handleEstimatedTotalChange
125030
+ }
125031
+ ) }) }) }) }),
125032
+ /* @__PURE__ */ jsx("div", { className: "border-t bg-neutral-50/50 px-2 py-4 flex-shrink-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
125033
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
125034
+ !viewOnly && textToSegment2 && /* @__PURE__ */ jsxs(
125035
+ Button$1,
125036
+ {
125037
+ variant: "secondary",
125038
+ onClick: () => setTextToSegment(false),
125039
+ children: [
125040
+ /* @__PURE__ */ jsx(Settings2, { className: "h-4 w-4" }),
125041
+ "Manual Builder"
125042
+ ]
125043
+ }
125044
+ ),
125045
+ !textToSegment2 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-neutral-500", children: [
125046
+ /* @__PURE__ */ jsx(Users, { className: "h-4 w-4" }),
125047
+ /* @__PURE__ */ jsxs("span", { children: [
125048
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-neutral-700", children: estimatedTotal.toLocaleString() }),
125049
+ " ",
125050
+ "users match this segment"
125051
+ ] })
125052
+ ] })
125053
+ ] }),
125054
+ /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: viewOnly ? (
125055
+ // VIEW-ONLY: Only Close button
125056
+ /* @__PURE__ */ jsx(Button$1, { variant: "default", onClick: () => setOpen2(false), children: "Close" })
125057
+ ) : (
125058
+ // EDIT MODE: Delete (if editing), Cancel, and Save buttons
125059
+ /* @__PURE__ */ jsxs(Fragment$1, { children: [
125060
+ segmentId && !newSegment && /* @__PURE__ */ jsxs(
125061
+ Button$1,
125062
+ {
125063
+ variant: "destructive",
125064
+ onClick: () => setShowDeleteDialog(true),
125065
+ disabled: isDeleting,
125066
+ children: [
125067
+ /* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
125068
+ "Delete"
125069
+ ]
125070
+ }
125071
+ ),
125072
+ /* @__PURE__ */ jsx(
125073
+ Button$1,
125074
+ {
125075
+ variant: "secondary",
125076
+ onClick: () => setOpen2(false),
125077
+ children: "Cancel"
125078
+ }
125079
+ ),
125080
+ /* @__PURE__ */ jsx(
125081
+ Button$1,
125082
+ {
125083
+ variant: "default",
125084
+ onClick: handleSave,
125085
+ disabled: isLoading || isCreating,
125086
+ children: isLoading || isCreating ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
125087
+ /* @__PURE__ */ jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }),
125088
+ segmentId ? "Saving..." : "Creating..."
125089
+ ] }) : segmentId ? "Save" : "Create"
125090
+ }
125091
+ )
125092
+ ] })
125093
+ ) })
125094
+ ] }) })
125095
+ ] })
125096
+ },
125097
+ "manual-builder-style"
125098
+ ),
124480
125099
  /* @__PURE__ */ jsx(Dialog, { open: showDeleteDialog, onOpenChange: setShowDeleteDialog, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[425px]", children: [
124481
125100
  /* @__PURE__ */ jsxs(DialogHeader$1, { children: [
124482
125101
  /* @__PURE__ */ jsx(DialogTitle, { children: "Delete Segment" }),
@@ -125524,15 +126143,17 @@ const AutomationAudienceSelectorMain = ({ title: title2 = "Preview Audience" })
125524
126143
  setSelectedAudience(AutomationAudienceSelectorType.INDIVIDUAL);
125525
126144
  if (singleSegment.conditions) {
125526
126145
  const userIds = [];
125527
- singleSegment.conditions.forEach((condition) => {
125528
- if (condition.field === "userId" && condition.operator === ConditionOperatorEnumType.EQUALS) {
125529
- condition.value.forEach((val) => {
125530
- if (typeof val === "string") {
125531
- userIds.push(val);
125532
- }
125533
- });
126146
+ singleSegment.conditions.forEach(
126147
+ (condition) => {
126148
+ if (isFieldCondition(condition) && condition.field === "userId" && condition.operator === ConditionOperatorEnumType.EQUALS) {
126149
+ condition.value.forEach((val) => {
126150
+ if (typeof val === "string") {
126151
+ userIds.push(val);
126152
+ }
126153
+ });
126154
+ }
125534
126155
  }
125535
- });
126156
+ );
125536
126157
  setSelectedIndividualUserIds(userIds);
125537
126158
  if (userIds.length > 0 && singleSegment.id) {
125538
126159
  try {
@@ -125607,15 +126228,17 @@ const AutomationAudienceSelectorMain = ({ title: title2 = "Preview Audience" })
125607
126228
  setExcludedSegments([]);
125608
126229
  if (segment2.conditions) {
125609
126230
  const userIds = [];
125610
- segment2.conditions.forEach((condition) => {
125611
- if (condition.field === "userId" && condition.operator === ConditionOperatorEnumType.EQUALS) {
125612
- condition.value.forEach((val) => {
125613
- if (typeof val === "string") {
125614
- userIds.push(val);
125615
- }
125616
- });
126231
+ segment2.conditions.forEach(
126232
+ (condition) => {
126233
+ if (isFieldCondition(condition) && condition.field === "userId" && condition.operator === ConditionOperatorEnumType.EQUALS) {
126234
+ condition.value.forEach((val) => {
126235
+ if (typeof val === "string") {
126236
+ userIds.push(val);
126237
+ }
126238
+ });
126239
+ }
125617
126240
  }
125618
- });
126241
+ );
125619
126242
  setSelectedIndividualUserIds(userIds);
125620
126243
  if (userIds.length > 0) {
125621
126244
  try {
@@ -125655,6 +126278,7 @@ const AutomationAudienceSelectorMain = ({ title: title2 = "Preview Audience" })
125655
126278
  type: SegmentDefinitionTypeEnum.ONE_OFF,
125656
126279
  conditions: [
125657
126280
  {
126281
+ type: "property",
125658
126282
  field: "userId",
125659
126283
  operator: ConditionOperatorEnumType.EQUALS,
125660
126284
  value: []
@@ -125708,12 +126332,14 @@ const AutomationAudienceSelectorMain = ({ title: title2 = "Preview Audience" })
125708
126332
  params: {
125709
126333
  conditions: userIds.length > 0 ? [
125710
126334
  {
126335
+ type: "property",
125711
126336
  field: "userId",
125712
126337
  operator: ConditionOperatorEnumType.EQUALS,
125713
126338
  value: userIds
125714
126339
  }
125715
126340
  ] : [
125716
126341
  {
126342
+ type: "property",
125717
126343
  field: "userId",
125718
126344
  operator: ConditionOperatorEnumType.EQUALS,
125719
126345
  value: []
@@ -125729,12 +126355,14 @@ const AutomationAudienceSelectorMain = ({ title: title2 = "Preview Audience" })
125729
126355
  type: SegmentDefinitionTypeEnum.ONE_OFF,
125730
126356
  conditions: userIds.length > 0 ? [
125731
126357
  {
126358
+ type: "property",
125732
126359
  field: "userId",
125733
126360
  operator: ConditionOperatorEnumType.EQUALS,
125734
126361
  value: userIds
125735
126362
  }
125736
126363
  ] : [
125737
126364
  {
126365
+ type: "property",
125738
126366
  field: "userId",
125739
126367
  operator: ConditionOperatorEnumType.EQUALS,
125740
126368
  value: []
@@ -128186,6 +128814,7 @@ const ConsolidatedStatsBar = ({
128186
128814
  statistics,
128187
128815
  automationId,
128188
128816
  onLinkClickStatsOpen,
128817
+ includeEmail = true,
128189
128818
  includeSms = false,
128190
128819
  showErrors = false
128191
128820
  }) => {
@@ -128258,7 +128887,7 @@ const ConsolidatedStatsBar = ({
128258
128887
  ] : []
128259
128888
  ];
128260
128889
  return /* @__PURE__ */ jsx("div", { className: "bg-white rounded-xl p-4 border border-border", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row lg:items-center gap-4 lg:gap-0", children: [
128261
- /* @__PURE__ */ jsxs("div", { className: "lg:flex-[3]", children: [
128890
+ includeEmail && /* @__PURE__ */ jsxs("div", { className: "lg:flex-[3]", children: [
128262
128891
  /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-4 lg:mb-0", children: /* @__PURE__ */ jsx(MinorText, { className: "text-[10px] font-semibold text-gray-500 uppercase tracking-wide", children: "Email" }) }),
128263
128892
  /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 sm:gap-3 flex-wrap justify-evenly w-full", children: emailStats.map((stat, index2) => /* @__PURE__ */ jsx(
128264
128893
  StatItem,
@@ -128431,6 +129060,7 @@ const LinkClickStatsDialog = ({
128431
129060
  const AutomationStatistics = ({
128432
129061
  automationId,
128433
129062
  includeSales = false,
129063
+ includeEmail = true,
128434
129064
  includeSms = false,
128435
129065
  showErrors = false,
128436
129066
  automation: automation2
@@ -128573,6 +129203,7 @@ const AutomationStatistics = ({
128573
129203
  statistics,
128574
129204
  automationId,
128575
129205
  onLinkClickStatsOpen: () => setIsLinkClickStatsOpen(true),
129206
+ includeEmail,
128576
129207
  includeSms: includeSms && isSmsEnabled,
128577
129208
  showErrors
128578
129209
  },
@@ -129856,7 +130487,9 @@ const AutomationsEditorHeader = ({ showBackButton, onDuplicationCreated, onBefor
129856
130487
  const navigate = useNavigate();
129857
130488
  const { updateAutomation: businessUpdateAutomation } = useUpdateBusinessAutomation(automation2?.id || "");
129858
130489
  const { toast: toast2 } = useToast();
129859
- const estimatedMatchesStats = useValidationStats();
130490
+ const { data: estimatedRecipientsData } = useGetEstimatedRecipients({
130491
+ automationId: automation2?.id || ""
130492
+ });
129860
130493
  const { communicationGroup } = useCurrentCommunicationGroup();
129861
130494
  const { data: smsApplication } = useGetLatestSmsRegistrationApplication();
129862
130495
  const { channelSenders } = useChannelSender();
@@ -129962,11 +130595,11 @@ const AutomationsEditorHeader = ({ showBackButton, onDuplicationCreated, onBefor
129962
130595
  } else {
129963
130596
  if (onBeforeSchedule && markActive === true) {
129964
130597
  let retries = 0;
129965
- while (!estimatedMatchesStats && retries < 20) {
130598
+ while (!estimatedRecipientsData && retries < 20) {
129966
130599
  await new Promise((resolve) => setTimeout(resolve, 100));
129967
130600
  retries++;
129968
130601
  }
129969
- if (!estimatedMatchesStats) {
130602
+ if (!estimatedRecipientsData) {
129970
130603
  toast2({
129971
130604
  title: "Error while updating the status",
129972
130605
  description: "Please contact your administrator",
@@ -129976,8 +130609,8 @@ const AutomationsEditorHeader = ({ showBackButton, onDuplicationCreated, onBefor
129976
130609
  }
129977
130610
  const scheduleSendAt = getScheduleSendAtFromAutomation(automation2);
129978
130611
  const result2 = await onBeforeSchedule({
129979
- estimatedEmailRecipients: estimatedMatchesStats?.emails ?? 0,
129980
- estimatedSmsRecipients: estimatedMatchesStats?.phones ?? 0,
130612
+ estimatedEmailRecipients: estimatedRecipientsData?.estimatedEmails ?? 0,
130613
+ estimatedSmsRecipients: estimatedRecipientsData?.estimatedSms ?? 0,
129981
130614
  scheduleSendAt
129982
130615
  });
129983
130616
  if (result2 !== true) {
@@ -130370,7 +131003,7 @@ const AutomationsEditorHeader = ({ showBackButton, onDuplicationCreated, onBefor
130370
131003
  automationLanguage,
130371
131004
  onBeforeSchedule,
130372
131005
  businessUpdateAutomation,
130373
- estimatedMatchesStats,
131006
+ estimatedRecipientsData,
130374
131007
  queryClient,
130375
131008
  handleUndo
130376
131009
  ]);
@@ -130624,8 +131257,13 @@ const AnimatedTabs = ({
130624
131257
  };
130625
131258
  const AutomationsEditorStatsTab = () => {
130626
131259
  const automation2 = useAutomation();
131260
+ const { state: engageFilterState } = useEngageFilterContext();
131261
+ const dateRange = engageFilterState.dateRange;
131262
+ const { communicationGroup } = useCurrentCommunicationGroup();
130627
131263
  const { isLoading: isStatisticsLoading } = useGetBusinessAutomationStatistics(
130628
- automation2?.id
131264
+ automation2?.id,
131265
+ dateRange?.from,
131266
+ dateRange?.to
130629
131267
  );
130630
131268
  const [cursorForQuery, setCursorForQuery] = useState(
130631
131269
  void 0
@@ -130642,11 +131280,15 @@ const AutomationsEditorStatsTab = () => {
130642
131280
  cursor: cursorForQuery,
130643
131281
  limit: 10,
130644
131282
  automationStatus: automation2?.status,
130645
- automationType: automation2?.triggerType
131283
+ automationType: automation2?.triggerType,
131284
+ startDate: dateRange?.from,
131285
+ endDate: dateRange?.to
130646
131286
  });
130647
131287
  const {
130648
131288
  state: { hideSales, hideSms }
130649
131289
  } = useContext(Context$3);
131290
+ const isEmailChannelEnabled = !!communicationGroup?.emailChannelSenderId;
131291
+ const isSmsChannelEnabled = !!communicationGroup?.smsChannelSenderId;
130650
131292
  const handleQueryParametersChange = useCallback(
130651
131293
  (params) => {
130652
131294
  setCursorForQuery(params.cursor);
@@ -130661,6 +131303,7 @@ const AutomationsEditorStatsTab = () => {
130661
131303
  }
130662
131304
  );
130663
131305
  }
131306
+ const isBroadcast = automation2.triggerType === AutomationTriggerType.ONE_TIME;
130664
131307
  const orderData = orders?.results || [];
130665
131308
  const columns = [
130666
131309
  {
@@ -130727,14 +131370,15 @@ const AutomationsEditorStatsTab = () => {
130727
131370
  }
130728
131371
  ];
130729
131372
  return /* @__PURE__ */ jsxs("div", { className: "h-full p-4 space-y-6", children: [
130730
- /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(EngageDateRangePicker, {}) }),
131373
+ !isBroadcast && /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(EngageDateRangePicker, {}) }),
130731
131374
  /* @__PURE__ */ jsx(
130732
131375
  AutomationStatistics,
130733
131376
  {
130734
131377
  automationId: automation2.id,
130735
131378
  automation: automation2,
130736
131379
  includeSales: !hideSales,
130737
- includeSms: !hideSms,
131380
+ includeEmail: isEmailChannelEnabled,
131381
+ includeSms: !hideSms && isSmsChannelEnabled,
130738
131382
  showErrors: true
130739
131383
  }
130740
131384
  ),
@@ -134008,7 +134652,7 @@ const actionTypeToHumanReadable = (actionType, customBlockText) => {
134008
134652
  case ActionType.SEND_COMMUNICATION:
134009
134653
  return customBlockText || "Send Communication";
134010
134654
  case ActionType.WAIT_UNTIL_AT_LEAST_NEXT_AVAILABLE_TIME:
134011
- return "Wait (Skip if Time Has Already Passed Today)";
134655
+ return "Wait (Proceed if Time Has Already Passed Today)";
134012
134656
  case ActionType.WAIT_UNTIL_NEXT_OCCURRENCE:
134013
134657
  return "Wait (Always Wait for Next Occurrence (even if it means waiting until tomorrow))";
134014
134658
  case ActionType.WAIT_FOR_DURATION:
@@ -136553,7 +137197,7 @@ const ViewAutomationMain = ({
136553
137197
  updateCommunicationGroup2
136554
137198
  ]);
136555
137199
  if (!automation2 || !communicationGroup || isLoadingSegments) {
136556
- return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx(
137200
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsx(
136557
137201
  BasicLoader,
136558
137202
  {
136559
137203
  text: [
@@ -136572,7 +137216,7 @@ const ViewAutomationMain = ({
136572
137216
  fromNameSettingsLink,
136573
137217
  hideSms,
136574
137218
  hideSales,
136575
- children: /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col gap-4 p-4", children: [
137219
+ children: /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-4 p-4", children: [
136576
137220
  /* @__PURE__ */ jsx(
136577
137221
  AutomationsEditorHeader,
136578
137222
  {
@@ -136592,7 +137236,7 @@ const ViewAutomationContent = ({ ...props2 }) => {
136592
137236
  const effectiveAutomationId = props2.automationId || params?.automationId || searchParams.get("automationId") || "";
136593
137237
  const shouldShowBackButton = searchParams.get("showBackButton") === "true" || props2.showBackButton;
136594
137238
  const shouldHideSms = searchParams.get("hideSms") === "true" || props2.hideSms;
136595
- return /* @__PURE__ */ jsx(Provider, { children: /* @__PURE__ */ jsx(ViewAutomationProvider, { automationId: effectiveAutomationId, children: /* @__PURE__ */ jsx("div", { className: "bg-background h-full min-h-[750px]", children: /* @__PURE__ */ jsx(
137239
+ return /* @__PURE__ */ jsx(Provider, { children: /* @__PURE__ */ jsx(ViewAutomationProvider, { automationId: effectiveAutomationId, children: /* @__PURE__ */ jsx("div", { className: "bg-background flex flex-col h-full min-h-[750px]", children: /* @__PURE__ */ jsx(
136596
137240
  ViewAutomationMain,
136597
137241
  {
136598
137242
  ...props2,