@capillarytech/creatives-library 9.0.13-alpha.1 → 9.0.13-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/constants/unified.js +3 -0
  2. package/package.json +1 -1
  3. package/utils/common.js +8 -0
  4. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +15 -108
  5. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +1 -141
  6. package/v2Components/CommonTestAndPreview/index.js +26 -244
  7. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +0 -364
  8. package/v2Components/FormBuilder/Classic.js +4487 -0
  9. package/v2Components/FormBuilder/Functional/FormBuilderShell.js +369 -0
  10. package/v2Components/FormBuilder/Functional/channels/registry.js +17 -0
  11. package/v2Components/FormBuilder/Functional/channels/sms/buildSubmitPayload.js +9 -0
  12. package/v2Components/FormBuilder/Functional/channels/sms/config.js +30 -0
  13. package/v2Components/FormBuilder/Functional/channels/sms/getEditorErrorDescriptor.js +46 -0
  14. package/v2Components/FormBuilder/Functional/channels/sms/getLiquidContent.js +13 -0
  15. package/v2Components/FormBuilder/Functional/channels/sms/index.js +22 -0
  16. package/v2Components/FormBuilder/Functional/channels/sms/tests/getEditorErrorDescriptor.test.js +52 -0
  17. package/v2Components/FormBuilder/Functional/channels/sms/tests/getLiquidContent.test.js +25 -0
  18. package/v2Components/FormBuilder/Functional/channels/sms/tests/validate.test.js +87 -0
  19. package/v2Components/FormBuilder/Functional/channels/sms/validate.js +89 -0
  20. package/v2Components/FormBuilder/Functional/constants.js +42 -0
  21. package/v2Components/FormBuilder/Functional/core/schema/fieldRegistry.js +38 -0
  22. package/v2Components/FormBuilder/Functional/core/schema/initializeFormState.js +85 -0
  23. package/v2Components/FormBuilder/Functional/core/store/formReducer.js +81 -0
  24. package/v2Components/FormBuilder/Functional/core/store/selectors.js +30 -0
  25. package/v2Components/FormBuilder/Functional/core/store/toLegacyFormData.js +91 -0
  26. package/v2Components/FormBuilder/Functional/index.js +26 -0
  27. package/v2Components/FormBuilder/Functional/layout/FieldSlot.js +59 -0
  28. package/v2Components/FormBuilder/Functional/layout/SchemaForm.js +32 -0
  29. package/v2Components/FormBuilder/Functional/layout/Section.js +116 -0
  30. package/v2Components/FormBuilder/Functional/renderers/smsRenderers.js +258 -0
  31. package/v2Components/FormBuilder/Functional/tests/channelRegistry.test.js +21 -0
  32. package/v2Components/FormBuilder/Functional/tests/fieldRegistry.test.js +65 -0
  33. package/v2Components/FormBuilder/Functional/tests/fieldSlot.test.js +97 -0
  34. package/v2Components/FormBuilder/Functional/tests/fixtures/smsParityCases.js +192 -0
  35. package/v2Components/FormBuilder/Functional/tests/formReducer.test.js +129 -0
  36. package/v2Components/FormBuilder/Functional/tests/initializeFormState.test.js +132 -0
  37. package/v2Components/FormBuilder/Functional/tests/schemaForm.test.js +40 -0
  38. package/v2Components/FormBuilder/Functional/tests/section.test.js +99 -0
  39. package/v2Components/FormBuilder/Functional/tests/selectors.test.js +67 -0
  40. package/v2Components/FormBuilder/Functional/tests/sms.crossFlowParity.test.js +155 -0
  41. package/v2Components/FormBuilder/Functional/tests/sms.liquid.test.js +172 -0
  42. package/v2Components/FormBuilder/Functional/tests/sms.rollout.test.js +122 -0
  43. package/v2Components/FormBuilder/Functional/tests/sms.shell.parity.test.js +329 -0
  44. package/v2Components/FormBuilder/Functional/tests/smsRenderers.test.js +160 -0
  45. package/v2Components/FormBuilder/Functional/tests/toLegacyFormData.test.js +95 -0
  46. package/v2Components/FormBuilder/_formBuilder.scss +5 -0
  47. package/v2Components/FormBuilder/index.js +41 -4479
  48. package/v2Components/FormBuilder/tests/__snapshots__/sms.characterization.test.js.snap +114 -0
  49. package/v2Components/FormBuilder/tests/entryGate.test.js +106 -0
  50. package/v2Components/FormBuilder/tests/sms.characterization.test.js +336 -0
  51. package/v2Containers/Templates/_templates.scss +0 -83
  52. package/v2Containers/Templates/index.js +10 -90
  53. package/v2Containers/Viber/constants.js +0 -21
  54. package/v2Containers/Viber/index.js +49 -719
  55. package/v2Containers/Viber/index.scss +0 -175
  56. package/v2Containers/Viber/messages.js +0 -121
  57. package/v2Containers/Viber/tests/index.test.js +0 -80
  58. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberCarouselPreviewCards.js +0 -132
  59. package/v2Components/CommonTestAndPreview/UnifiedPreview/_viberCarouselPreviewCards.scss +0 -132
@@ -189,8 +189,6 @@ const CommonTestAndPreview = (props) => {
189
189
  const [previewDevice, setPreviewDevice] = useState(initialDevice);
190
190
  // Track if a preview call has been made (to know when to use previewDataHtml vs raw content)
191
191
  const [hasPreviewCallBeenMade, setHasPreviewCallBeenMade] = useState(false);
192
- // Viber: tag values applied to preview only after explicit Update preview / Discard (not while typing)
193
- const [viberPreviewTagValues, setViberPreviewTagValues] = useState(null);
194
192
  const [previewDataHtml, setPreviewDataHtml] = useState(() => {
195
193
  // Initialize preview data based on channel
196
194
  if (channel === CHANNELS.EMAIL && formData) {
@@ -458,159 +456,6 @@ const CommonTestAndPreview = (props) => {
458
456
  return resolvedText;
459
457
  };
460
458
 
461
- const getViberMergedFormData = useCallback((formDataOverride) => {
462
- const formDataObj = formDataOverride && typeof formDataOverride === 'object'
463
- ? formDataOverride
464
- : (formData && typeof formData === 'object' ? formData : {});
465
- let contentObj = {};
466
- if (content && typeof content === 'object') {
467
- contentObj = content;
468
- } else if (typeof content === 'string' && content.trim()) {
469
- try {
470
- contentObj = JSON.parse(content);
471
- } catch (e) {
472
- contentObj = {};
473
- }
474
- }
475
- const previewCards = formDataObj.viberPreviewContent?.cards
476
- ?? formDataObj.cards
477
- ?? contentObj.viberPreviewContent?.cards
478
- ?? contentObj.cards;
479
- const isCarousel = Boolean(
480
- formDataObj.type === MEDIA_TYPE_CAROUSEL
481
- || contentObj.type === MEDIA_TYPE_CAROUSEL
482
- || (Array.isArray(previewCards) && previewCards.length > 0),
483
- );
484
- const mergedPreview = {
485
- ...(contentObj.viberPreviewContent || {}),
486
- ...(formDataObj.viberPreviewContent || {}),
487
- ...(Array.isArray(previewCards) && previewCards.length ? { cards: previewCards } : {}),
488
- ...(isCarousel ? {
489
- type: MEDIA_TYPE_CAROUSEL,
490
- showCarouselEditorPreview: true,
491
- } : {}),
492
- };
493
- return {
494
- ...contentObj,
495
- ...formDataObj,
496
- ...(Array.isArray(previewCards) && previewCards.length ? { cards: previewCards } : {}),
497
- ...(isCarousel ? { type: MEDIA_TYPE_CAROUSEL } : {}),
498
- ...(Object.keys(mergedPreview).length ? { viberPreviewContent: mergedPreview } : {}),
499
- };
500
- }, [formData, content]);
501
-
502
- const getViberCarouselCardsFromFormData = (formDataObj) => {
503
- const previewCards = formDataObj?.viberPreviewContent?.cards;
504
- if (Array.isArray(previewCards) && previewCards.length) {
505
- return previewCards;
506
- }
507
- const rootCards = formDataObj?.cards;
508
- if (Array.isArray(rootCards) && rootCards.length) {
509
- return rootCards;
510
- }
511
- return [];
512
- };
513
-
514
- const getViberTagExtractionContent = (formDataObj, contentStr = '') => {
515
- const messageText = formDataObj?.messageContent
516
- || formDataObj?.viberPreviewContent?.messageContent
517
- || contentStr
518
- || '';
519
- const cardFieldTexts = getViberCarouselCardsFromFormData(formDataObj).flatMap((card) => {
520
- const fields = [card?.text || ''];
521
- (card?.buttons || []).forEach((button) => {
522
- fields.push(button?.title || '', button?.action || '');
523
- });
524
- return fields;
525
- });
526
- return [messageText, ...cardFieldTexts]
527
- .filter((value) => typeof value === 'string' && value.trim())
528
- .join(' ');
529
- };
530
-
531
- const resolveViberCarouselCards = (cards, tagValues) => {
532
- if (!Array.isArray(cards) || !tagValues || !Object.keys(tagValues).length) {
533
- return cards;
534
- }
535
- return cards.map((card) => ({
536
- ...card,
537
- text: resolveTagsInText(card?.text || '', tagValues),
538
- buttons: (card?.buttons || []).map((button) => ({
539
- ...button,
540
- title: resolveTagsInText(button?.title || '', tagValues),
541
- action: resolveTagsInText(button?.action || '', tagValues),
542
- })),
543
- }));
544
- };
545
-
546
- const applyViberCustomValuesToContent = (contentObj, tagValues) => {
547
- if (!contentObj || typeof contentObj !== 'object' || !tagValues || !Object.keys(tagValues).length) {
548
- return contentObj;
549
- }
550
- const result = { ...contentObj };
551
- if (typeof result.messageContent === 'string') {
552
- result.messageContent = resolveTagsInText(result.messageContent, tagValues);
553
- }
554
- if (Array.isArray(result.cards)) {
555
- result.cards = resolveViberCarouselCards(result.cards, tagValues);
556
- }
557
- if (result.viberPreviewContent && typeof result.viberPreviewContent === 'object') {
558
- const preview = result.viberPreviewContent;
559
- result.viberPreviewContent = {
560
- ...preview,
561
- messageContent: resolveTagsInText(preview.messageContent || '', tagValues),
562
- cards: resolveViberCarouselCards(preview.cards, tagValues),
563
- };
564
- }
565
- return result;
566
- };
567
-
568
- const mergeViberPreviewWithResolved = (base, resolved) => {
569
- if (!resolved || typeof resolved !== 'object') {
570
- return base && typeof base === 'object' ? base : {};
571
- }
572
- const baseObj = base && typeof base === 'object' ? base : {};
573
- const basePreview = baseObj.viberPreviewContent || {};
574
- const resolvedPreview = resolved.viberPreviewContent || {};
575
- const baseCards = basePreview.cards || baseObj.cards || [];
576
- const resolvedCards = resolvedPreview.cards || resolved.cards || [];
577
- const mergedCards = baseCards.length
578
- ? baseCards.map((card, index) => {
579
- const resolvedCard = resolvedCards[index];
580
- if (!resolvedCard) {
581
- return card;
582
- }
583
- return {
584
- ...card,
585
- ...(resolvedCard.text != null ? { text: resolvedCard.text } : {}),
586
- ...(resolvedCard.mediaUrl != null ? { mediaUrl: resolvedCard.mediaUrl } : {}),
587
- buttons: (card.buttons || []).map((button, buttonIndex) => ({
588
- ...button,
589
- ...(resolvedCard.buttons?.[buttonIndex] || {}),
590
- })),
591
- };
592
- })
593
- : resolvedCards;
594
- const resolvedMessage = resolved.messageContent ?? resolvedPreview.messageContent;
595
-
596
- return {
597
- ...baseObj,
598
- ...resolved,
599
- viberPreviewContent: {
600
- ...basePreview,
601
- ...resolvedPreview,
602
- type: resolvedPreview.type || basePreview.type || baseObj.type || (mergedCards.length ? MEDIA_TYPE_CAROUSEL : ''),
603
- cards: mergedCards.length ? mergedCards : (resolvedPreview.cards || basePreview.cards),
604
- showCarouselEditorPreview: Boolean(
605
- basePreview.showCarouselEditorPreview
606
- || resolvedPreview.showCarouselEditorPreview
607
- || mergedCards.length > 0,
608
- ),
609
- ...(resolvedMessage != null ? { messageContent: resolvedMessage } : {}),
610
- },
611
- };
612
- };
613
-
614
459
  /**
615
460
  * Common handler for saving test customers (both new and existing)
616
461
  */
@@ -818,13 +663,11 @@ const CommonTestAndPreview = (props) => {
818
663
  templateContent: formDataObj?.bodyText || contentStr,
819
664
  };
820
665
 
821
- case CHANNELS.VIBER: {
822
- const viberFormData = getViberMergedFormData(formDataObj);
666
+ case CHANNELS.VIBER:
823
667
  return {
824
- templateSubject: viberFormData?.messageTitle || '',
825
- templateContent: getViberTagExtractionContent(viberFormData, contentStr),
668
+ templateSubject: formDataObj?.messageTitle || '',
669
+ templateContent: contentStr,
826
670
  };
827
- }
828
671
 
829
672
  case CHANNELS.WEBPUSH:
830
673
  return {
@@ -1719,16 +1562,11 @@ const CommonTestAndPreview = (props) => {
1719
1562
  // 1. formDataObj from getTemplateContent (contains both viberPreviewContent and payload fields)
1720
1563
  // 2. formDataObj[0] (array format - legacy)
1721
1564
  // 3. contentStr (direct string - legacy)
1722
- // Merge content prop so listing / preview-only flows include carousel card tags
1723
- const viberFormData = getViberMergedFormData(formDataObj);
1724
- formDataObj = viberFormData;
1725
1565
 
1726
1566
  let messageText = '';
1727
1567
  let imageData = null;
1728
1568
  let videoData = null;
1729
1569
  let buttonData = null;
1730
- let cardsData = [];
1731
- let messageType = '';
1732
1570
  let accountId = null;
1733
1571
  let accountDetails = null;
1734
1572
  let scenarioKey = '';
@@ -1743,8 +1581,6 @@ const CommonTestAndPreview = (props) => {
1743
1581
  imageData = formDataObj.image || null;
1744
1582
  videoData = formDataObj.video || null;
1745
1583
  buttonData = formDataObj.button || null;
1746
- cardsData = formDataObj.cards || [];
1747
- messageType = formDataObj.type || '';
1748
1584
  accountId = formDataObj.accountId || null;
1749
1585
  accountDetails = formDataObj.accountDetails || null;
1750
1586
  scenarioKey = formDataObj.scenarioKey || VIBER_API_SCENARIO_KEY;
@@ -1771,8 +1607,6 @@ const CommonTestAndPreview = (props) => {
1771
1607
  url: formDataObj?.buttonURL || '',
1772
1608
  };
1773
1609
  }
1774
- cardsData = viberPreview.cards || formDataObj?.cards || [];
1775
- messageType = viberPreview.type || formDataObj?.type || '';
1776
1610
  // Extract account info from parent formDataObj if available
1777
1611
  accountId = formDataObj.accountId || null;
1778
1612
  accountDetails = formDataObj.accountDetails || null;
@@ -1815,10 +1649,6 @@ const CommonTestAndPreview = (props) => {
1815
1649
  text: messageText,
1816
1650
  };
1817
1651
 
1818
- if (messageType === CHANNELS.VIBER) {
1819
- messageType = '';
1820
- }
1821
-
1822
1652
  // Add image if present
1823
1653
  if (imageData && imageData.url) {
1824
1654
  viberContent.image = {
@@ -1847,34 +1677,9 @@ const CommonTestAndPreview = (props) => {
1847
1677
  }
1848
1678
  }
1849
1679
 
1850
- if (messageType === MEDIA_TYPE_CAROUSEL || (Array.isArray(cardsData) && cardsData.length)) {
1851
- viberContent.type = MEDIA_TYPE_CAROUSEL;
1852
- viberContent.cards = (cardsData || []).map((card) => ({
1853
- text: card?.text || '',
1854
- mediaUrl: card?.mediaUrl || '',
1855
- buttons: (card?.buttons || []).map((button) => ({
1856
- title: button?.title || '',
1857
- action: button?.action || '',
1858
- })),
1859
- }));
1860
- delete viberContent.button;
1861
- }
1862
-
1863
- // Resolve tags in text/cards if custom values are provided
1864
- if (customValuesObj && Object.keys(customValuesObj).length > 0) {
1865
- if (viberContent.text) {
1866
- viberContent.text = resolveTagsInText(viberContent.text, customValuesObj);
1867
- }
1868
- if (Array.isArray(viberContent.cards)) {
1869
- viberContent.cards = resolveViberCarouselCards(viberContent.cards, customValuesObj);
1870
- }
1871
- if (viberContent.button) {
1872
- viberContent.button = {
1873
- ...viberContent.button,
1874
- text: resolveTagsInText(viberContent.button.text || '', customValuesObj),
1875
- url: resolveTagsInText(viberContent.button.url || '', customValuesObj),
1876
- };
1877
- }
1680
+ // Resolve tags in text if custom values are provided
1681
+ if (customValuesObj && Object.keys(customValuesObj).length > 0 && viberContent.text) {
1682
+ viberContent.text = resolveTagsInText(viberContent.text, customValuesObj);
1878
1683
  }
1879
1684
 
1880
1685
  // Build messageBody JSON string
@@ -2254,39 +2059,33 @@ const CommonTestAndPreview = (props) => {
2254
2059
  // Only use previewDataHtml if preview call was made
2255
2060
  if (hasPreviewCallBeenMade && previewDataHtml?.resolvedBody) {
2256
2061
  resolvedContent = previewDataHtml.resolvedBody;
2257
- if (typeof resolvedContent === 'string') {
2258
- try {
2259
- resolvedContent = JSON.parse(resolvedContent);
2260
- } catch (e) {
2261
- resolvedContent = null;
2262
- }
2263
- }
2264
2062
  }
2265
2063
 
2266
- // Parse content if it's a string, then merge with formData for carousel + tags in all entry flows
2267
- let parsedViberContent = getViberMergedFormData();
2268
- if (!parsedViberContent || typeof parsedViberContent !== 'object') {
2269
- parsedViberContent = {};
2064
+ // Parse content if it's a string
2065
+ let parsedViberContent = content;
2066
+ if (typeof content === 'string') {
2067
+ try {
2068
+ parsedViberContent = JSON.parse(content);
2069
+ } catch (e) {
2070
+ parsedViberContent = {};
2071
+ }
2270
2072
  }
2271
- // Merge template preview state with API resolved body so carousel type/cards survive slim responses
2073
+ // Use resolvedContent if available (from preview call), otherwise use raw content
2272
2074
  if (resolvedContent && typeof resolvedContent === 'object') {
2273
- const base = parsedViberContent && typeof parsedViberContent === 'object' ? parsedViberContent : {};
2274
- contentObj = mergeViberPreviewWithResolved(base, resolvedContent);
2275
- if (base.accountName && !contentObj.accountName) {
2276
- contentObj.accountName = base.accountName;
2075
+ contentObj = { ...resolvedContent };
2076
+ // Merge in accountName and brandName from raw content if not in resolvedContent
2077
+ if (parsedViberContent?.accountName && !contentObj.accountName) {
2078
+ contentObj.accountName = parsedViberContent.accountName;
2277
2079
  }
2278
- if (base.brandName && !contentObj.brandName) {
2279
- contentObj.brandName = base.brandName;
2080
+ if (parsedViberContent?.brandName && !contentObj.brandName) {
2081
+ contentObj.brandName = parsedViberContent.brandName;
2280
2082
  }
2281
2083
  } else {
2282
- contentObj = parsedViberContent && typeof parsedViberContent === 'object' ? parsedViberContent : {};
2283
- }
2284
- if (
2285
- hasPreviewCallBeenMade
2286
- && viberPreviewTagValues
2287
- && Object.keys(viberPreviewTagValues).length > 0
2288
- ) {
2289
- contentObj = applyViberCustomValuesToContent(contentObj, viberPreviewTagValues);
2084
+ // Use raw content if no preview call was made or resolvedContent is not available
2085
+ contentObj = {...parsedViberContent, messageContent: resolvedContent};
2086
+ if (resolvedContent) {
2087
+ contentObj.viberPreviewContent = {...parsedViberContent?.viberPreviewContent, messageContent: resolvedContent};
2088
+ }
2290
2089
  }
2291
2090
  } else if (channel === CHANNELS.ZALO) {
2292
2091
  // For Zalo, content is an object with templatePreviewUrl, templateStatus, etc.
@@ -2542,8 +2341,6 @@ const CommonTestAndPreview = (props) => {
2542
2341
  setShowJSON(false);
2543
2342
  setTagsExtracted(false);
2544
2343
  setPreviewDevice(DESKTOP);
2545
- setHasPreviewCallBeenMade(false);
2546
- setViberPreviewTagValues(null);
2547
2344
  setSelectedTestEntities([]);
2548
2345
  actions.clearPrefilledValues();
2549
2346
  } else {
@@ -2673,11 +2470,6 @@ const CommonTestAndPreview = (props) => {
2673
2470
 
2674
2471
  setCustomValues(updatedValues);
2675
2472
 
2676
- // Viber: do not auto-resolve tags in preview; user must click Update preview
2677
- if (channel === CHANNELS.VIBER) {
2678
- return;
2679
- }
2680
-
2681
2473
  // Update preview with prefilled values (this is a valid preview call trigger)
2682
2474
  const payload = preparePreviewPayload(
2683
2475
  channel,
@@ -2755,7 +2547,6 @@ const CommonTestAndPreview = (props) => {
2755
2547
  setPreviewDevice(DESKTOP);
2756
2548
  setPreviewDataHtml('');
2757
2549
  setHasPreviewCallBeenMade(false); // Reset preview call flag
2758
- setViberPreviewTagValues(null);
2759
2550
  setSelectedTestEntities([]);
2760
2551
  setBeeContent('');
2761
2552
  previousBeeContentRef.current = '';
@@ -2797,7 +2588,6 @@ const CommonTestAndPreview = (props) => {
2797
2588
  const handleClearSelection = () => {
2798
2589
  setSelectedCustomer(null);
2799
2590
  setHasPreviewCallBeenMade(false); // Reset flag when customer is cleared
2800
- setViberPreviewTagValues(null);
2801
2591
 
2802
2592
  // Clear all preview errors when customer is cleared
2803
2593
  actions.clearPreviewErrors();
@@ -2857,9 +2647,6 @@ const CommonTestAndPreview = (props) => {
2857
2647
  emptyValues,
2858
2648
  selectedCustomer
2859
2649
  );
2860
- if (channel === CHANNELS.VIBER) {
2861
- setViberPreviewTagValues({ ...emptyValues });
2862
- }
2863
2650
  actions.updatePreviewRequested(payload);
2864
2651
  setHasPreviewCallBeenMade(true); // Mark that preview call was made
2865
2652
  };
@@ -2877,9 +2664,6 @@ const CommonTestAndPreview = (props) => {
2877
2664
  customValues,
2878
2665
  selectedCustomer
2879
2666
  );
2880
- if (channel === CHANNELS.VIBER) {
2881
- setViberPreviewTagValues({ ...customValues });
2882
- }
2883
2667
  await actions.updatePreviewRequested(payload);
2884
2668
  setHasPreviewCallBeenMade(true); // Mark that preview call was made
2885
2669
  } catch (error) {
@@ -2901,8 +2685,6 @@ const CommonTestAndPreview = (props) => {
2901
2685
  const activeTab = currentTabData?.activeTab;
2902
2686
  const templateContent = currentTabData?.[activeTab]?.['template-content'];
2903
2687
  contentToExtract = templateContent || contentToExtract;
2904
- } else if (channel === CHANNELS.VIBER) {
2905
- contentToExtract = getViberTagExtractionContent(getViberMergedFormData(), contentToExtract);
2906
2688
  }
2907
2689
 
2908
2690
  // Check for personalization tags (excluding unsubscribe)