@embedreach/components 0.3.16 → 0.3.18

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.
@@ -3233,6 +3233,7 @@ const useGetBusinessAutomationSentCommunications = (args) => {
3233
3233
  return {
3234
3234
  // Get automation sent communications query
3235
3235
  sentCommunications: getSentCommunicationsQuery.data,
3236
+ totalCount: getSentCommunicationsQuery.data?.pagination.total ?? null,
3236
3237
  isFetching: getSentCommunicationsQuery.isFetching,
3237
3238
  isLoading: getSentCommunicationsQuery.isLoading,
3238
3239
  isPlaceholderData: getSentCommunicationsQuery.isPlaceholderData,
@@ -3314,6 +3315,8 @@ const useListBusinessAutomations = (args) => {
3314
3315
  // The cursor to be used to fetch the *next* page of results.
3315
3316
  // Null or undefined if there are no more pages.
3316
3317
  nextCursor: query.data?.pagination.cursor ?? null,
3318
+ // Total count of items for pagination display
3319
+ totalCount: query.data?.pagination.total ?? null,
3317
3320
  isLoading: query.isLoading,
3318
3321
  error: query.error,
3319
3322
  isFetching: query.isFetching,
@@ -49151,6 +49154,8 @@ const useListSegments = (params) => {
49151
49154
  // The cursor to be used to fetch the *next* page of results.
49152
49155
  // Null or undefined if there are no more pages.
49153
49156
  nextCursor: query.data?.pagination.cursor ?? null,
49157
+ // Total count of items for pagination display
49158
+ totalCount: query.data?.pagination.total ?? null,
49154
49159
  isLoading: query.isLoading,
49155
49160
  error: query.error,
49156
49161
  isFetching: query.isFetching,
@@ -52901,7 +52906,7 @@ function useTanstackTable({
52901
52906
  columns,
52902
52907
  enableDebug = false,
52903
52908
  paginationMode = "none",
52904
- initialPageSize = 10,
52909
+ initialPageSize = 20,
52905
52910
  initialPageIndex = 0,
52906
52911
  cursorPaginationQueryResult,
52907
52912
  onQueryParametersChange,
@@ -53180,6 +53185,25 @@ function SortingHeader({
53180
53185
  )
53181
53186
  ] });
53182
53187
  }
53188
+ const formatValue = (value, options = {}) => {
53189
+ const { decimals = 0, suffix = "", useLocale = false } = options;
53190
+ const num = Number(value);
53191
+ if (!value || Number.isNaN(num)) {
53192
+ return `0${decimals > 0 ? "." + "0".repeat(decimals) : ""}${suffix}`;
53193
+ }
53194
+ let formatted;
53195
+ if (useLocale) {
53196
+ formatted = num.toLocaleString(void 0, {
53197
+ minimumFractionDigits: decimals,
53198
+ maximumFractionDigits: decimals
53199
+ });
53200
+ } else if (decimals > 0) {
53201
+ formatted = num.toFixed(decimals);
53202
+ } else {
53203
+ formatted = Math.round(num).toString();
53204
+ }
53205
+ return `${formatted}${suffix}`;
53206
+ };
53183
53207
  function DebouncedInput({
53184
53208
  value: initialValue,
53185
53209
  onChange,
@@ -53207,7 +53231,8 @@ function TanstackTable({
53207
53231
  columns,
53208
53232
  enableDebug = false,
53209
53233
  paginationMode = "none",
53210
- initialPageSize = 10,
53234
+ initialPageSize = 20,
53235
+ totalCount,
53211
53236
  initialPageIndex = 0,
53212
53237
  headerActions,
53213
53238
  isLoading = false,
@@ -53260,7 +53285,8 @@ function TanstackTable({
53260
53285
  manualSorting,
53261
53286
  enableSortingRemoval,
53262
53287
  onSortingChange: handleSortingChangeInternal,
53263
- manualFiltering
53288
+ manualFiltering,
53289
+ manualPageCount: totalCount ? Math.ceil(totalCount / initialPageSize) : void 0
53264
53290
  });
53265
53291
  const scrollContainerRef = useRef(null);
53266
53292
  const [isScrolled, setIsScrolled] = useState(false);
@@ -53338,7 +53364,20 @@ function TanstackTable({
53338
53364
  ))
53339
53365
  },
53340
53366
  row.id
53341
- )) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: columns.length, className: "h-24 text-center", children: "No results found" }) }) })
53367
+ )) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: columns.length, className: "h-24 text-center", children: "No results found" }) }) }),
53368
+ enableFooter && !!data?.length && /* @__PURE__ */ jsx(TableFooter, { className: "sticky bottom-0 z-10 bg-card border-t", children: table.getFooterGroups().map((footerGroup) => /* @__PURE__ */ jsx(TableRow, { children: footerGroup.headers.map((header2) => /* @__PURE__ */ jsx(
53369
+ TableHead,
53370
+ {
53371
+ colSpan: header2.colSpan,
53372
+ style: { width: header2.getSize() },
53373
+ className: "bg-card text-card-foreground font-medium",
53374
+ children: header2.isPlaceholder ? null : flexRender(
53375
+ header2.column.columnDef.footer,
53376
+ header2.getContext()
53377
+ )
53378
+ },
53379
+ header2.id
53380
+ )) }, footerGroup.id)) })
53342
53381
  ] });
53343
53382
  const TableSkeleton = () => /* @__PURE__ */ jsx("div", { className: "h-full w-full flex items-center justify-center min-h-[250px]", children: /* @__PURE__ */ jsx(BasicLoader, {}) });
53344
53383
  const isTableLoading = hideLoadingState ? isLoading && (!data || data.length === 0) : isLoading;
@@ -53358,8 +53397,8 @@ function TanstackTable({
53358
53397
  /* @__PURE__ */ jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-primary" }),
53359
53398
  /* @__PURE__ */ jsx("span", { className: "text-sm text-foreground", children: "Loading..." })
53360
53399
  ] }) }),
53361
- (onSearchChange || isStandardPagination || isCursorPagination) && !isTableLoading && /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-20 bg-card border-b px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
53362
- /* @__PURE__ */ jsx("div", { children: onSearchChange && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
53400
+ (onSearchChange || isStandardPagination || isCursorPagination) && !isTableLoading && /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-20 bg-card border-b px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4", children: [
53401
+ onSearchChange && /* @__PURE__ */ jsxs("div", { className: "relative w-full sm:w-auto", children: [
53363
53402
  /* @__PURE__ */ jsx(
53364
53403
  DebouncedInput,
53365
53404
  {
@@ -53385,7 +53424,7 @@ function TanstackTable({
53385
53424
  }
53386
53425
  },
53387
53426
  className: cn$1(
53388
- "w-80 h-9 text-sm border border-border rounded-md px-3 bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring",
53427
+ "w-full sm:w-80 h-9 text-sm border border-border rounded-md px-3 bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring",
53389
53428
  isFetching && "pr-10"
53390
53429
  // Add padding for spinner
53391
53430
  ),
@@ -53394,31 +53433,32 @@ function TanstackTable({
53394
53433
  }
53395
53434
  ),
53396
53435
  !hideLoadingState && isFetching && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-1/2 transform -translate-y-1/2", children: /* @__PURE__ */ jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-muted-foreground" }) })
53397
- ] }) }),
53436
+ ] }),
53398
53437
  /* @__PURE__ */ jsx("div", { children: (isStandardPagination || isCursorPagination) && /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-4", children: [
53399
53438
  /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: (() => {
53400
- if (paginationMode === "cursor") {
53401
- return null;
53402
- }
53403
- const totalRows = table.getPrePaginationRowModel().rows.length;
53404
53439
  const currentPageRows = table.getRowModel().rows.length;
53405
- if (totalRows === 0) {
53406
- return /* @__PURE__ */ jsx("span", { children: "No items" });
53440
+ const total = totalCount && totalCount > 0 ? formatValue(totalCount, {
53441
+ decimals: 0,
53442
+ useLocale: true
53443
+ }) : table.getPrePaginationRowModel().rows.length;
53444
+ if (currentPageRows === 0) {
53445
+ return null;
53407
53446
  }
53408
- const startItem = pageIndex * pageSize + 1;
53409
- const endItem = pageIndex * pageSize + currentPageRows;
53447
+ const logicalPageNumber = isCursorPagination ? table.logicalPage ?? 0 : pageIndex;
53448
+ const startItem = logicalPageNumber * pageSize + 1;
53449
+ const endItem = logicalPageNumber * pageSize + currentPageRows;
53410
53450
  return /* @__PURE__ */ jsxs("span", { children: [
53411
53451
  "Showing ",
53412
53452
  startItem,
53413
53453
  "–",
53414
53454
  endItem,
53415
53455
  " of ",
53416
- totalRows,
53456
+ total,
53417
53457
  " items"
53418
53458
  ] });
53419
53459
  })() }),
53420
53460
  /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
53421
- /* @__PURE__ */ jsxs(
53461
+ /* @__PURE__ */ jsx(
53422
53462
  Button$1,
53423
53463
  {
53424
53464
  "data-testid": "previous-button",
@@ -53432,13 +53472,10 @@ function TanstackTable({
53432
53472
  }
53433
53473
  },
53434
53474
  disabled: !actualCanPreviousPage || (hideLoadingState ? false : isLoading || isFetching),
53435
- children: [
53436
- /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4 mr-1" }),
53437
- "Previous"
53438
- ]
53475
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4 mr-1" })
53439
53476
  }
53440
53477
  ),
53441
- /* @__PURE__ */ jsxs(
53478
+ /* @__PURE__ */ jsx(
53442
53479
  Button$1,
53443
53480
  {
53444
53481
  "data-testid": "next-button",
@@ -53452,10 +53489,7 @@ function TanstackTable({
53452
53489
  }
53453
53490
  },
53454
53491
  disabled: !actualCanNextPage || (hideLoadingState ? false : isLoading || isFetching),
53455
- children: [
53456
- "Next",
53457
- /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 ml-1" })
53458
- ]
53492
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 ml-1" })
53459
53493
  }
53460
53494
  )
53461
53495
  ] })
@@ -53470,18 +53504,6 @@ function TanstackTable({
53470
53504
  children: isTableLoading ? /* @__PURE__ */ jsx(TableSkeleton, {}) : /* @__PURE__ */ jsx(TableContent, {})
53471
53505
  }
53472
53506
  ),
53473
- enableFooter && !isTableLoading && !!data?.length && /* @__PURE__ */ jsx("div", { className: "sticky bottom-0 z-10 bg-card border-t", children: /* @__PURE__ */ jsx("div", { className: "w-full overflow-x-auto", children: /* @__PURE__ */ jsx(Table$1, { className: "table-fixed", children: /* @__PURE__ */ jsx(TableFooter, { children: table.getFooterGroups().map((footerGroup) => /* @__PURE__ */ jsx(TableRow, { children: footerGroup.headers.map((header2) => /* @__PURE__ */ jsx(
53474
- TableHead,
53475
- {
53476
- colSpan: header2.colSpan,
53477
- style: { width: header2.getSize() },
53478
- children: header2.isPlaceholder ? null : flexRender(
53479
- header2.column.columnDef.footer,
53480
- header2.getContext()
53481
- )
53482
- },
53483
- header2.id
53484
- )) }, footerGroup.id)) }) }) }) }),
53485
53507
  enableDebug && /* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx("pre", { className: "p-2 bg-gray-50 text-xs overflow-x-auto", children: /* @__PURE__ */ jsx("code", { children: JSON.stringify(table.getState(), null, 2) }) }) })
53486
53508
  ] });
53487
53509
  }
@@ -97651,6 +97673,11 @@ const buildFinalSMSBody = (args) => {
97651
97673
  return companyName && smsBody ? `${companyName}:
97652
97674
  ${smsBody}` : smsBody ?? "";
97653
97675
  };
97676
+ var ReachMergeFieldTypeEnum = /* @__PURE__ */ ((ReachMergeFieldTypeEnum2) => {
97677
+ ReachMergeFieldTypeEnum2["STATIC"] = "static";
97678
+ ReachMergeFieldTypeEnum2["DYNAMIC"] = "dynamic";
97679
+ return ReachMergeFieldTypeEnum2;
97680
+ })(ReachMergeFieldTypeEnum || {});
97654
97681
  const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
97655
97682
  async function convertToPng(base64Data) {
97656
97683
  if (!isBrowser) {
@@ -97702,6 +97729,12 @@ function extractBase64Data(dataUrl) {
97702
97729
  }
97703
97730
  return parts[1];
97704
97731
  }
97732
+ const extractMergeFieldNamesUtil = (text2) => {
97733
+ const mergeFieldRegex = /\{\{([^}]+)\}\}/g;
97734
+ const matches2 = text2.match(mergeFieldRegex);
97735
+ if (!matches2) return [];
97736
+ return matches2.map((match2) => match2.slice(2, -2));
97737
+ };
97705
97738
  const capitalize = (str) => {
97706
97739
  if (!str) return str;
97707
97740
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -100342,7 +100375,7 @@ const SMSEditor = ({
100342
100375
  ]
100343
100376
  }
100344
100377
  ),
100345
- /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground pt-1 border-t", children: "Maximum file size: 5MB. We will try to compress the image if possible." })
100378
+ /* @__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." })
100346
100379
  ] }) })
100347
100380
  ]
100348
100381
  }
@@ -100354,7 +100387,7 @@ const SMSEditor = ({
100354
100387
  "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).",
100355
100388
  /* @__PURE__ */ jsx("br", {}),
100356
100389
  /* @__PURE__ */ jsx("br", {}),
100357
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Maximum file size: 5MB. We will try to compress the image if possible." })
100390
+ /* @__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." })
100358
100391
  ] })
100359
100392
  ] }),
100360
100393
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
@@ -100681,6 +100714,9 @@ const SMSEditorContent = ({
100681
100714
  const hasMergeFieldsInUrl = useCallback((url) => {
100682
100715
  return /\{\{[^}]+\}\}/.test(url);
100683
100716
  }, []);
100717
+ const extractMergeFieldNames = useCallback((content2) => {
100718
+ return extractMergeFieldNamesUtil(content2);
100719
+ }, []);
100684
100720
  const [editingMessage, setEditingMessage] = useState(initialMessage);
100685
100721
  const [imagePreview, setImagePreview] = useState(
100686
100722
  initialImageUrls.length > 0 ? initialImageUrls[0] : null
@@ -100690,6 +100726,7 @@ const SMSEditorContent = ({
100690
100726
  const [isUploadingImage, setIsUploadingImage] = useState(false);
100691
100727
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
100692
100728
  const [hasInitialized, setHasInitialized] = useState(false);
100729
+ const [dynamicMergeFieldImages, setDynamicMergeFieldImages] = useState([]);
100693
100730
  const fileInputRef = useRef(null);
100694
100731
  const autoSaveRef = useRef();
100695
100732
  const isInitialMountRef = useRef(true);
@@ -100697,6 +100734,29 @@ const SMSEditorContent = ({
100697
100734
  message: initialMessage
100698
100735
  });
100699
100736
  const { communicationGroup } = useGetCommunicationGroup(communicationGroupId);
100737
+ const getDynamicMergeFieldImages = useCallback(
100738
+ (content2) => {
100739
+ if (!communicationGroup?.extraMergeFields) return [];
100740
+ const mergeFieldNames = extractMergeFieldNames(content2);
100741
+ const dynamicImages = [];
100742
+ for (const mergeField of communicationGroup.extraMergeFields) {
100743
+ if (mergeField.type === ReachMergeFieldTypeEnum.DYNAMIC) {
100744
+ for (const field of mergeField.mergeFields) {
100745
+ if (mergeFieldNames.includes(field.templateName) && field.image) {
100746
+ dynamicImages.push({
100747
+ templateName: field.templateName,
100748
+ displayName: field.displayName,
100749
+ placeholderUrl: field.image.placeholderUrl,
100750
+ maxSize: field.image.maxSize
100751
+ });
100752
+ }
100753
+ }
100754
+ }
100755
+ }
100756
+ return dynamicImages;
100757
+ },
100758
+ [communicationGroup?.extraMergeFields, extractMergeFieldNames]
100759
+ );
100700
100760
  const finalSMSBody = useMemo(() => {
100701
100761
  return buildFinalSMSBody({
100702
100762
  companyName: stableCompanyName || void 0,
@@ -100704,6 +100764,12 @@ const SMSEditorContent = ({
100704
100764
  });
100705
100765
  }, [stableCompanyName, editingMessage]);
100706
100766
  const debouncedMessage = useDebounce(editingMessage, 1e3);
100767
+ useEffect(() => {
100768
+ if (hasInitialized) {
100769
+ const dynamicImages = getDynamicMergeFieldImages(editingMessage);
100770
+ setDynamicMergeFieldImages(dynamicImages);
100771
+ }
100772
+ }, [editingMessage, getDynamicMergeFieldImages, hasInitialized]);
100707
100773
  const autoSave = useCallback(async () => {
100708
100774
  if (isInitialMountRef.current) {
100709
100775
  return;
@@ -100825,7 +100891,7 @@ const SMSEditorContent = ({
100825
100891
  console.log("File too large", file.size, MAX_FILE_SIZE);
100826
100892
  toast2({
100827
100893
  title: "File too large",
100828
- description: `Image must be 5MB or smaller. Your file is ${(file.size / (1024 * 1024)).toFixed(1)}MB.`,
100894
+ description: `Sum of all images attached must be 5MB or smaller. The file you tried to upload is ${(file.size / (1024 * 1024)).toFixed(1)}MB.`,
100829
100895
  variant: "destructive"
100830
100896
  });
100831
100897
  return;
@@ -100941,30 +101007,52 @@ const SMSEditorContent = ({
100941
101007
  ) }),
100942
101008
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row gap-6 h-full", children: [
100943
101009
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
100944
- imagePreview && /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsx("div", { className: "relative group w-fit", children: hasMergeFieldsInUrl(imagePreview) ? /* @__PURE__ */ jsx("div", { className: "max-h-48 max-w-xs rounded-xl shadow-lg border border-gray-200 bg-gray-50 p-4 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
100945
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-700 mb-2", children: "Image will be populated at send time based on the following merge fields:" }),
100946
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 justify-center", children: getMergeFieldsFromUrl(
100947
- imagePreview,
100948
- getMergeFields2
100949
- ).map((fieldName, index2) => /* @__PURE__ */ jsx(
100950
- "span",
100951
- {
100952
- className: "inline-flex items-center px-2 py-1 rounded text-xs bg-blue-100 text-blue-800 font-medium border border-blue-200",
100953
- children: fieldName
100954
- },
100955
- index2
100956
- )) })
100957
- ] }) }) : /* @__PURE__ */ jsx(
100958
- "img",
100959
- {
100960
- src: imagePreview,
100961
- alt: "SMS attachment preview",
100962
- className: "max-h-48 max-w-xs rounded-xl shadow-lg border border-gray-200 object-contain bg-white transition-transform duration-200",
100963
- style: {
100964
- boxShadow: "0 4px 24px 0 rgba(0,0,0,0.08)"
100965
- }
100966
- }
100967
- ) }) }),
101010
+ (imagePreview || dynamicMergeFieldImages.length > 0) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-4 justify-center mb-6", children: [
101011
+ imagePreview && /* @__PURE__ */ jsx("div", { className: "relative group w-fit", children: hasMergeFieldsInUrl(imagePreview) ? /* @__PURE__ */ jsx("div", { className: "max-h-48 max-w-xs rounded-xl shadow-lg border border-gray-200 bg-gray-50 p-4 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
101012
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-700 mb-2", children: "Image will be populated at send time based on the following merge fields:" }),
101013
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 justify-center", children: getMergeFieldsFromUrl(
101014
+ imagePreview,
101015
+ getMergeFields2
101016
+ ).map((fieldName, index2) => /* @__PURE__ */ jsx(
101017
+ "span",
101018
+ {
101019
+ className: "inline-flex items-center px-2 py-1 rounded text-xs bg-blue-100 text-blue-800 font-medium border border-blue-200",
101020
+ children: fieldName
101021
+ },
101022
+ index2
101023
+ )) })
101024
+ ] }) }) : /* @__PURE__ */ jsxs("div", { className: "max-h-48 max-w-xs rounded-xl shadow-lg border border-green-200 bg-green-50 p-4 flex flex-col items-center justify-center min-h-[12rem]", children: [
101025
+ /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsx(
101026
+ "img",
101027
+ {
101028
+ src: imagePreview,
101029
+ alt: "SMS attachment preview",
101030
+ className: "max-h-32 max-w-48 rounded-lg border border-green-300 object-contain bg-white",
101031
+ style: {
101032
+ boxShadow: "0 2px 12px 0 rgba(0,0,0,0.06)"
101033
+ }
101034
+ }
101035
+ ) }),
101036
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-green-600 mt-2", children: "Uploaded Image" })
101037
+ ] }) }),
101038
+ dynamicMergeFieldImages.map((dynamicImage, index2) => /* @__PURE__ */ jsxs("div", { className: "relative group w-fit", children: [
101039
+ /* @__PURE__ */ jsx("div", { className: "max-h-48 max-w-xs rounded-xl shadow-lg border border-blue-200 bg-blue-50 p-4 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
101040
+ /* @__PURE__ */ jsx(
101041
+ "img",
101042
+ {
101043
+ src: dynamicImage.placeholderUrl,
101044
+ alt: `Dynamic merge field ${dynamicImage.displayName} placeholder`,
101045
+ className: "max-h-32 max-w-48 rounded-lg border border-blue-300 object-contain bg-white",
101046
+ style: {
101047
+ boxShadow: "0 2px 12px 0 rgba(0,0,0,0.06)"
101048
+ }
101049
+ }
101050
+ ),
101051
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-blue-600 mt-3", children: dynamicImage.displayName })
101052
+ ] }) }),
101053
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground mt-2 text-center", children: "Will be dynamically fetched at send time" })
101054
+ ] }, index2))
101055
+ ] }),
100968
101056
  /* @__PURE__ */ jsx(
100969
101057
  "input",
100970
101058
  {
@@ -100999,7 +101087,10 @@ const SMSEditorContent = ({
100999
101087
  MemoizedSMSPreview,
101000
101088
  {
101001
101089
  body: finalSMSBody,
101002
- imageUrls: imagePreview ? [imagePreview] : []
101090
+ imageUrls: [
101091
+ ...dynamicMergeFieldImages.map((img) => img.placeholderUrl),
101092
+ ...imagePreview ? [imagePreview] : []
101093
+ ]
101003
101094
  }
101004
101095
  ) }) }) })
101005
101096
  ] })
@@ -102467,6 +102558,8 @@ const ChannelToggleButton = ({
102467
102558
  isUpdating,
102468
102559
  onToggle
102469
102560
  }) => {
102561
+ const automation2 = useAutomation();
102562
+ const disableToggle = !automation2 || automation2.status !== AutomationStatus.DRAFT;
102470
102563
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
102471
102564
  /* @__PURE__ */ jsx(
102472
102565
  Switch,
@@ -102477,7 +102570,7 @@ const ChannelToggleButton = ({
102477
102570
  onToggle();
102478
102571
  }
102479
102572
  },
102480
- disabled: isUpdating,
102573
+ disabled: isUpdating || disableToggle,
102481
102574
  className: "data-[state=checked]:bg-green-500 data-[state=unchecked]:bg-gray-300"
102482
102575
  }
102483
102576
  ),
@@ -103624,12 +103717,45 @@ const AutomationEditorSMSPreview = ({
103624
103717
  const currentlySelectedSender = smsChannelSenders.find(
103625
103718
  (sender) => sender.channelSender.id === communicationGroup?.smsChannelSenderId
103626
103719
  );
103720
+ const extractMergeFieldNames = useCallback((content2) => {
103721
+ return extractMergeFieldNamesUtil(content2);
103722
+ }, []);
103723
+ const smsBody = communicationGroup?.smsMessageBody || "";
103724
+ const getDynamicMergeFieldImages = useCallback(
103725
+ (content2) => {
103726
+ if (!communicationGroup?.extraMergeFields) return [];
103727
+ const mergeFieldNames = extractMergeFieldNames(content2);
103728
+ const dynamicImages = [];
103729
+ for (const mergeField of communicationGroup.extraMergeFields) {
103730
+ if (mergeField.type === ReachMergeFieldTypeEnum.DYNAMIC) {
103731
+ for (const field of mergeField.mergeFields) {
103732
+ if (mergeFieldNames.includes(field.templateName) && field.image) {
103733
+ dynamicImages.push({
103734
+ templateName: field.templateName,
103735
+ displayName: field.displayName,
103736
+ placeholderUrl: field.image.placeholderUrl,
103737
+ maxSize: field.image.maxSize
103738
+ });
103739
+ }
103740
+ }
103741
+ }
103742
+ }
103743
+ return dynamicImages;
103744
+ },
103745
+ [communicationGroup?.extraMergeFields, extractMergeFieldNames]
103746
+ );
103627
103747
  const imageUrls = useMemo(
103628
103748
  () => communicationGroup?.textMessageMediaUrls ?? [],
103629
103749
  [communicationGroup?.textMessageMediaUrls]
103630
103750
  );
103631
- const smsBody = communicationGroup?.smsMessageBody || "";
103632
103751
  const isLoading = !communicationGroup || !channelSenders || !channelAccounts;
103752
+ const dynamicMergeFieldImages = useMemo(() => {
103753
+ return getDynamicMergeFieldImages(smsBody);
103754
+ }, [getDynamicMergeFieldImages, smsBody]);
103755
+ const allImageUrls = useMemo(() => {
103756
+ const dynamicUrls = dynamicMergeFieldImages.map((img) => img.placeholderUrl);
103757
+ return [...dynamicUrls, ...imageUrls];
103758
+ }, [imageUrls, dynamicMergeFieldImages]);
103633
103759
  const lastValidCompanyName = useRef(null);
103634
103760
  const lastChangeTime = useRef(0);
103635
103761
  const companyName = useMemo(() => {
@@ -103764,7 +103890,17 @@ const AutomationEditorSMSPreview = ({
103764
103890
  if (prevWithoutCompany === nextWithoutCompany) {
103765
103891
  return true;
103766
103892
  }
103767
- return false;
103893
+ const prevImageUrls = prevProps.imageUrls || [];
103894
+ const nextImageUrls = nextProps.imageUrls || [];
103895
+ if (prevImageUrls.length !== nextImageUrls.length) {
103896
+ return false;
103897
+ }
103898
+ for (let i3 = 0; i3 < prevImageUrls.length; i3++) {
103899
+ if (prevImageUrls[i3] !== nextImageUrls[i3]) {
103900
+ return false;
103901
+ }
103902
+ }
103903
+ return true;
103768
103904
  });
103769
103905
  return /* @__PURE__ */ jsx("div", { className: "h-full", children: hasAnySMSsenders === false ? /* @__PURE__ */ jsx("div", { className: "w-full pt-8 mx-auto", children: /* @__PURE__ */ jsx(SMSSetup, {}) }) : /* @__PURE__ */ jsxs(
103770
103906
  motion.div,
@@ -103857,18 +103993,18 @@ const AutomationEditorSMSPreview = ({
103857
103993
  SMSPreviewExpanded,
103858
103994
  {
103859
103995
  smsBody: finalSMSBody,
103860
- imageUrls
103996
+ imageUrls: allImageUrls
103861
103997
  }
103862
103998
  )
103863
103999
  }
103864
104000
  )
103865
104001
  ] })
103866
104002
  ] }),
103867
- /* @__PURE__ */ jsx(
104003
+ isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-32", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "Loading preview..." }) }) : /* @__PURE__ */ jsx(
103868
104004
  MemoizedSMSPreview,
103869
104005
  {
103870
104006
  body: finalSMSBody,
103871
- imageUrls
104007
+ imageUrls: allImageUrls
103872
104008
  }
103873
104009
  )
103874
104010
  ] }),
@@ -111011,6 +111147,7 @@ function AutomationList() {
111011
111147
  const {
111012
111148
  automations,
111013
111149
  nextCursor,
111150
+ totalCount,
111014
111151
  isLoading,
111015
111152
  error: error2,
111016
111153
  isFetching,
@@ -111105,6 +111242,7 @@ function AutomationList() {
111105
111242
  data: mappedItems,
111106
111243
  columns,
111107
111244
  paginationMode: "cursor",
111245
+ totalCount: totalCount ?? void 0,
111108
111246
  initialPageSize: pageSizeForQuery,
111109
111247
  isLoading: isLoading && !isPlaceholderData,
111110
111248
  onRowClick: (row) => {
@@ -111147,6 +111285,7 @@ function BroadcastList() {
111147
111285
  const [searchQuery, setSearchQuery] = useState("");
111148
111286
  const {
111149
111287
  automations,
111288
+ totalCount,
111150
111289
  nextCursor,
111151
111290
  isLoading,
111152
111291
  error: error2,
@@ -111305,6 +111444,7 @@ function BroadcastList() {
111305
111444
  paginationMode: "cursor",
111306
111445
  initialPageSize: pageSizeForQuery,
111307
111446
  isLoading: isLoading && !isPlaceholderData,
111447
+ totalCount: totalCount ?? void 0,
111308
111448
  cursorPaginationQueryResult: {
111309
111449
  nextCursor,
111310
111450
  isLoading,
@@ -111340,7 +111480,14 @@ function SegmentList() {
111340
111480
  void 0
111341
111481
  );
111342
111482
  const [searchQuery, setSearchQuery] = useState("");
111343
- const { segments, isLoading, nextCursor, isFetching, isPlaceholderData } = useListSegments({
111483
+ const {
111484
+ segments,
111485
+ isLoading,
111486
+ nextCursor,
111487
+ totalCount,
111488
+ isFetching,
111489
+ isPlaceholderData
111490
+ } = useListSegments({
111344
111491
  search: searchQuery || void 0,
111345
111492
  cursor: cursorForQuery,
111346
111493
  limit: pageSizeForQuery
@@ -111396,6 +111543,7 @@ function SegmentList() {
111396
111543
  paginationMode: "cursor",
111397
111544
  initialPageSize: pageSizeForQuery,
111398
111545
  isLoading: isLoading && !isPlaceholderData,
111546
+ totalCount: totalCount ?? void 0,
111399
111547
  cursorPaginationQueryResult: {
111400
111548
  nextCursor,
111401
111549
  isLoading,
@@ -112852,6 +113000,7 @@ const AutomationRecipientsTable = () => {
112852
113000
  const [searchQuery, setSearchQuery] = useState("");
112853
113001
  const {
112854
113002
  sentCommunications,
113003
+ totalCount,
112855
113004
  isLoading: isLoadingRecipients,
112856
113005
  isFetching,
112857
113006
  isPlaceholderData,
@@ -113005,6 +113154,7 @@ const AutomationRecipientsTable = () => {
113005
113154
  {
113006
113155
  data: sentCommunications?.results || [],
113007
113156
  columns,
113157
+ totalCount: totalCount ?? void 0,
113008
113158
  paginationMode: "cursor",
113009
113159
  initialPageSize: pageSizeForQuery,
113010
113160
  isLoading: isLoadingRecipients && !isPlaceholderData,
@@ -115468,6 +115618,40 @@ const AutomationEditorTrigger = () => {
115468
115618
  };
115469
115619
  return /* @__PURE__ */ jsx(Fragment$1, { children: renderTriggerData() });
115470
115620
  };
115621
+ const safelyTruncateWithMergeFields = (text2, maxLength) => {
115622
+ if (text2.length <= maxLength) {
115623
+ return text2;
115624
+ }
115625
+ const mergeFieldRegex = /\{\{[^}]+\}\}/g;
115626
+ const mergeFields = [];
115627
+ let match2;
115628
+ while ((match2 = mergeFieldRegex.exec(text2)) !== null) {
115629
+ mergeFields.push({
115630
+ start: match2.index,
115631
+ end: match2.index + match2[0].length,
115632
+ field: match2[0]
115633
+ });
115634
+ }
115635
+ let truncationPoint = maxLength;
115636
+ for (const mergeField of mergeFields) {
115637
+ if (truncationPoint > mergeField.start && truncationPoint < mergeField.end) {
115638
+ truncationPoint = mergeField.start;
115639
+ break;
115640
+ }
115641
+ if (truncationPoint === mergeField.start) {
115642
+ const lastSpace = text2.lastIndexOf(" ", mergeField.start - 1);
115643
+ truncationPoint = lastSpace > 0 ? lastSpace : mergeField.start;
115644
+ break;
115645
+ }
115646
+ }
115647
+ if (truncationPoint === 0) {
115648
+ if (mergeFields.length > 0) {
115649
+ return mergeFields[0].field;
115650
+ }
115651
+ return text2.substring(0, Math.min(maxLength, text2.length));
115652
+ }
115653
+ return text2.substring(0, truncationPoint) + "...";
115654
+ };
115471
115655
  const AutomationEditorSubStep = ({
115472
115656
  type,
115473
115657
  actionId
@@ -115598,7 +115782,10 @@ const AutomationEditorSubStep = ({
115598
115782
  ) : isGettingMergeFields ? /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-muted-foreground", children: "..." }) : /* @__PURE__ */ jsx(
115599
115783
  MergeFieldRenderer,
115600
115784
  {
115601
- content: communicationGroup?.smsMessageBody ? `${communicationGroup.smsMessageBody.substring(0, 40)}${communicationGroup.smsMessageBody.length > 40 ? "..." : ""}` : "No message set",
115785
+ content: communicationGroup?.smsMessageBody ? safelyTruncateWithMergeFields(
115786
+ communicationGroup.smsMessageBody,
115787
+ 40
115788
+ ) : "No message set",
115602
115789
  mergeFieldsResponse: getMergeFields2,
115603
115790
  variant: "minimal",
115604
115791
  className: "text-[10px] font-medium text-muted-foreground"
package/dist/index.d.ts CHANGED
@@ -87,6 +87,7 @@ declare type DynamicMergeField = ReachMergeFieldBase & {
87
87
  mergeFields: {
88
88
  displayName: string;
89
89
  templateName: string;
90
+ image?: ImageMergeFieldType;
90
91
  }[];
91
92
  /**
92
93
  * The url of the dynamic merge field (e.g. "https://acme.co/id/")
@@ -120,6 +121,11 @@ declare interface EngageTypedOverrides {
120
121
  */
121
122
  declare type FeatureKey = 'measure' | 'measure-setup' | 'acquire-setup' | 'reputation' | 'engage-segment-builder' | 'engage-automations-create-modal' | 'engage-automations-view-modal' | 'engage';
122
123
 
124
+ declare type ImageMergeFieldType = {
125
+ placeholderUrl: string;
126
+ maxSize: number;
127
+ };
128
+
123
129
  declare interface LanguageConfig {
124
130
  /** Initial language code (e.g., 'en', 'es', 'fr') */
125
131
  default: string;