@capillarytech/creatives-library 9.0.36-alpha.0 → 9.0.37-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/translations/en.json +2 -2
- package/utils/permissions.js +59 -0
- package/utils/tests/permissions.test.js +116 -0
- package/v2Components/AccessForbidden/_accessForbidden.scss +14 -0
- package/v2Components/AccessForbidden/index.js +5 -3
- package/v2Components/AccessForbidden/messages.js +3 -2
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +15 -108
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +1 -139
- package/v2Components/CommonTestAndPreview/index.js +26 -240
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +0 -364
- package/v2Components/NoPermissionWrapper/_noPermissionWrapper.scss +12 -0
- package/v2Components/NoPermissionWrapper/index.js +26 -0
- package/v2Containers/App/constants.js +5 -0
- package/v2Containers/Assets/Gallery/_gallery.scss +6 -0
- package/v2Containers/Assets/Gallery/index.js +34 -10
- package/v2Containers/Cap/index.js +5 -2
- package/v2Containers/Cap/messages.js +4 -0
- package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +2 -2
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +6 -6
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +4 -4
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +32 -32
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +36 -36
- package/v2Containers/Rcs/carouselUtils.js +73 -46
- package/v2Containers/Rcs/components/CarouselCard.js +9 -6
- package/v2Containers/Rcs/constants.js +7 -2
- package/v2Containers/Rcs/index.js +126 -72
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +43 -5
- package/v2Containers/Rcs/tests/CarouselCard.test.js +2 -1
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +118 -118
- package/v2Containers/Rcs/tests/carouselUtils.test.js +15 -15
- package/v2Containers/Rcs/tests/index.test.js +232 -10
- package/v2Containers/Rcs/tests/utils.test.js +17 -0
- package/v2Containers/Rcs/utils.js +51 -12
- package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +8 -8
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +18 -18
- package/v2Containers/Templates/ChannelTypeIllustration.js +10 -1
- package/v2Containers/Templates/_templates.scss +6 -114
- package/v2Containers/Templates/index.js +151 -190
- package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +7 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +173 -120
- package/v2Containers/TemplatesV2/index.js +16 -5
- package/v2Containers/Viber/constants.js +0 -21
- package/v2Containers/Viber/index.js +49 -719
- package/v2Containers/Viber/index.scss +0 -175
- package/v2Containers/Viber/messages.js +0 -121
- package/v2Containers/Viber/tests/index.test.js +0 -80
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +268 -268
- package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberCarouselPreviewCards.js +0 -132
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_viberCarouselPreviewCards.scss +0 -131
|
@@ -585,8 +585,8 @@ describe('buildCarouselCardsForPreview', () => {
|
|
|
585
585
|
expect(out[0].bodyText).toBe('Bye {{b}}');
|
|
586
586
|
});
|
|
587
587
|
|
|
588
|
-
it('resolves title/description through resolveCarouselTemplateWithMap, scoped per card index', () => {
|
|
589
|
-
const resolveCarouselTemplateWithMap = jest.fn((str, cardIndex) => `R(${str}|${cardIndex})`);
|
|
588
|
+
it('resolves title/description through resolveCarouselTemplateWithMap, scoped per card index and field', () => {
|
|
589
|
+
const resolveCarouselTemplateWithMap = jest.fn((str, cardIndex, isDescField) => `R(${str}|${cardIndex}|${isDescField})`);
|
|
590
590
|
const out = buildCarouselCardsForPreview(
|
|
591
591
|
[
|
|
592
592
|
{ title: 'Hi {{a}}', description: 'Bye {{b}} {{c}}' },
|
|
@@ -594,14 +594,14 @@ describe('buildCarouselCardsForPreview', () => {
|
|
|
594
594
|
],
|
|
595
595
|
{ isFullMode: false, resolveCarouselTemplateWithMap },
|
|
596
596
|
);
|
|
597
|
-
expect(resolveCarouselTemplateWithMap).toHaveBeenNthCalledWith(1, 'Hi {{a}}', 0);
|
|
598
|
-
expect(resolveCarouselTemplateWithMap).toHaveBeenNthCalledWith(2, 'Bye {{b}} {{c}}', 0);
|
|
599
|
-
expect(resolveCarouselTemplateWithMap).toHaveBeenNthCalledWith(3, 'Solo {{d}}', 1);
|
|
600
|
-
expect(resolveCarouselTemplateWithMap).toHaveBeenNthCalledWith(4, 'none', 1);
|
|
601
|
-
expect(out[0].title).toBe('R(Hi {{a}}|0)');
|
|
602
|
-
expect(out[0].bodyText).toBe('R(Bye {{b}} {{c}}|0)');
|
|
603
|
-
expect(out[1].title).toBe('R(Solo {{d}}|1)');
|
|
604
|
-
expect(out[1].bodyText).toBe('R(none|1)');
|
|
597
|
+
expect(resolveCarouselTemplateWithMap).toHaveBeenNthCalledWith(1, 'Hi {{a}}', 0, false);
|
|
598
|
+
expect(resolveCarouselTemplateWithMap).toHaveBeenNthCalledWith(2, 'Bye {{b}} {{c}}', 0, true);
|
|
599
|
+
expect(resolveCarouselTemplateWithMap).toHaveBeenNthCalledWith(3, 'Solo {{d}}', 1, false);
|
|
600
|
+
expect(resolveCarouselTemplateWithMap).toHaveBeenNthCalledWith(4, 'none', 1, true);
|
|
601
|
+
expect(out[0].title).toBe('R(Hi {{a}}|0|false)');
|
|
602
|
+
expect(out[0].bodyText).toBe('R(Bye {{b}} {{c}}|0|true)');
|
|
603
|
+
expect(out[1].title).toBe('R(Solo {{d}}|1|false)');
|
|
604
|
+
expect(out[1].bodyText).toBe('R(none|1|true)');
|
|
605
605
|
});
|
|
606
606
|
|
|
607
607
|
it('defaults missing card fields, including a default {} for a missing array item', () => {
|
|
@@ -853,17 +853,17 @@ describe('buildCarouselCardContentForPayload', () => {
|
|
|
853
853
|
expect(out[0].cardVarMapped).toBeUndefined();
|
|
854
854
|
});
|
|
855
855
|
|
|
856
|
-
it('builds cardVarMapped from title and description tokens, sanitizing values', () => {
|
|
856
|
+
it('builds cardVarMapped from title and description tokens, keyed by global position, sanitizing values', () => {
|
|
857
857
|
const out = buildCarouselCardContentForPayload(
|
|
858
858
|
[{ title: 'Hi {{name}}', description: 'Pts {{points}}', mediaType: RCS_MEDIA_TYPES.NONE }],
|
|
859
859
|
{
|
|
860
860
|
isSlotMappingMode: true,
|
|
861
|
-
cardVarMapped: {
|
|
861
|
+
cardVarMapped: { 1: 'Bob', 2: '{{2}}' },
|
|
862
862
|
selectedCarouselHeight: 'SHORT',
|
|
863
863
|
rcsVarRegex,
|
|
864
864
|
},
|
|
865
865
|
);
|
|
866
|
-
expect(out[0].cardVarMapped).toEqual({
|
|
866
|
+
expect(out[0].cardVarMapped).toEqual({ 1: 'Bob', 2: '' });
|
|
867
867
|
});
|
|
868
868
|
|
|
869
869
|
it('defaults an unmapped token value to empty string via sanitizeCardVarMappedValue', () => {
|
|
@@ -871,7 +871,7 @@ describe('buildCarouselCardContentForPayload', () => {
|
|
|
871
871
|
[{ title: 'Hi {{missing}}', description: 'D', mediaType: RCS_MEDIA_TYPES.NONE }],
|
|
872
872
|
{ isSlotMappingMode: true, cardVarMapped: {}, selectedCarouselHeight: 'SHORT', rcsVarRegex },
|
|
873
873
|
);
|
|
874
|
-
expect(out[0].cardVarMapped).toEqual({
|
|
874
|
+
expect(out[0].cardVarMapped).toEqual({ 1: '' });
|
|
875
875
|
});
|
|
876
876
|
|
|
877
877
|
it('handles a missing cardVarMapped object entirely', () => {
|
|
@@ -879,7 +879,7 @@ describe('buildCarouselCardContentForPayload', () => {
|
|
|
879
879
|
[{ title: 'Hi {{name}}', description: 'D', mediaType: RCS_MEDIA_TYPES.NONE }],
|
|
880
880
|
{ isSlotMappingMode: true, cardVarMapped: undefined, selectedCarouselHeight: 'SHORT', rcsVarRegex },
|
|
881
881
|
);
|
|
882
|
-
expect(out[0].cardVarMapped).toEqual({
|
|
882
|
+
expect(out[0].cardVarMapped).toEqual({ 1: '' });
|
|
883
883
|
});
|
|
884
884
|
|
|
885
885
|
it('skips a matched token that strips down to an empty variable name', () => {
|
|
@@ -475,7 +475,7 @@ describe('Creatives rcs test/>', () => {
|
|
|
475
475
|
});
|
|
476
476
|
|
|
477
477
|
describe('RCS carousel label resolution in non-full mode', () => {
|
|
478
|
-
it('keeps
|
|
478
|
+
it('keeps every carousel variable slot independent, even when they share a literal tag name, in both inline preview and Test&Preview slidebox', async () => {
|
|
479
479
|
const templateData = {
|
|
480
480
|
name: 'CarouselVarsTemplate',
|
|
481
481
|
versions: {
|
|
@@ -564,8 +564,10 @@ describe('RCS carousel label resolution in non-full mode', () => {
|
|
|
564
564
|
return id.includes('{{name}}_') || key.includes('{{name}}_');
|
|
565
565
|
});
|
|
566
566
|
|
|
567
|
+
// Card 0 has two `{{name}}` occurrences — title's, then description's, in DOM order — and
|
|
568
|
+
// each is now its own independent slot despite sharing the literal name.
|
|
567
569
|
const card0NameAreas = nameVarAreaForCard(0);
|
|
568
|
-
expect(card0NameAreas.length).
|
|
570
|
+
expect(card0NameAreas.length).toBe(2);
|
|
569
571
|
|
|
570
572
|
act(() => {
|
|
571
573
|
const slotId = card0NameAreas.at(0).prop('id') || card0NameAreas.at(0).key();
|
|
@@ -573,44 +575,264 @@ describe('RCS carousel label resolution in non-full mode', () => {
|
|
|
573
575
|
});
|
|
574
576
|
wrapper.update();
|
|
575
577
|
|
|
576
|
-
//
|
|
578
|
+
// Only the title resolves — description's own `{{name}}` slot is untouched (independent slot).
|
|
577
579
|
const updatedInlinePreview = wrapper.find('UnifiedPreview').filterWhere((n) => n.prop('content')?.carouselData).at(0);
|
|
578
580
|
const inlineCards = updatedInlinePreview.props().content.carouselData;
|
|
579
581
|
expect(inlineCards[0].title).toBe('Hi madhavi');
|
|
580
|
-
expect(inlineCards[0].bodyText).toBe('Welcome
|
|
582
|
+
expect(inlineCards[0].bodyText).toBe('Welcome {{name}}');
|
|
581
583
|
expect(inlineCards[1].title).toBe('Offer for {{name}}');
|
|
582
584
|
expect(inlineCards[1].bodyText).toBe('Dear {{name}}');
|
|
583
585
|
|
|
584
|
-
//
|
|
586
|
+
// Filling card 0's description slot separately resolves it without disturbing the title.
|
|
587
|
+
act(() => {
|
|
588
|
+
const descSlotId = nameVarAreaForCard(0).at(1).prop('id') || nameVarAreaForCard(0).at(1).key();
|
|
589
|
+
nameVarAreaForCard(0).at(1).props().onChange({ target: { id: descSlotId, value: 'lakshmi' } });
|
|
590
|
+
});
|
|
591
|
+
wrapper.update();
|
|
592
|
+
|
|
593
|
+
const afterDescInlinePreview = wrapper.find('UnifiedPreview').filterWhere((n) => n.prop('content')?.carouselData).at(0);
|
|
594
|
+
const afterDescInlineCards = afterDescInlinePreview.props().content.carouselData;
|
|
595
|
+
expect(afterDescInlineCards[0].title).toBe('Hi madhavi');
|
|
596
|
+
expect(afterDescInlineCards[0].bodyText).toBe('Welcome lakshmi');
|
|
597
|
+
|
|
598
|
+
// Slidebox content should mirror the same independent-slot resolution.
|
|
585
599
|
const slidebox = wrapper.find('TestAndPreviewSlidebox').at(0);
|
|
586
600
|
expect(slidebox.exists()).toBe(true);
|
|
587
601
|
const slideboxContent = slidebox.props().content;
|
|
588
602
|
expect(slideboxContent?.carouselData?.length).toBeGreaterThanOrEqual(2);
|
|
589
603
|
expect(slideboxContent.carouselData[0].title).toBe('Hi madhavi');
|
|
590
|
-
expect(slideboxContent.carouselData[0].bodyText).toBe('Welcome
|
|
604
|
+
expect(slideboxContent.carouselData[0].bodyText).toBe('Welcome lakshmi');
|
|
591
605
|
expect(slideboxContent.carouselData[1].title).toBe('Offer for {{name}}');
|
|
592
606
|
expect(slideboxContent.carouselData[1].bodyText).toBe('Dear {{name}}');
|
|
593
607
|
|
|
594
|
-
// Setting card 1's own `{{name}}`
|
|
608
|
+
// Setting card 1's own `{{name}}` slots independently resolves card 1 without disturbing card 0.
|
|
595
609
|
// Inactive tab panes aren't mounted until switched to, so activate card 1's tab first.
|
|
596
610
|
act(() => {
|
|
597
611
|
wrapper.find('CapTab').prop('onChange')('1');
|
|
598
612
|
});
|
|
599
613
|
wrapper.update();
|
|
600
614
|
const card1NameAreas = nameVarAreaForCard(1);
|
|
601
|
-
expect(card1NameAreas.length).
|
|
615
|
+
expect(card1NameAreas.length).toBe(2);
|
|
602
616
|
act(() => {
|
|
603
617
|
const slotId = card1NameAreas.at(0).prop('id') || card1NameAreas.at(0).key();
|
|
604
618
|
card1NameAreas.at(0).props().onChange({ target: { id: slotId, value: 'raghav' } });
|
|
605
619
|
});
|
|
606
620
|
wrapper.update();
|
|
621
|
+
act(() => {
|
|
622
|
+
const descSlotId = nameVarAreaForCard(1).at(1).prop('id') || nameVarAreaForCard(1).at(1).key();
|
|
623
|
+
nameVarAreaForCard(1).at(1).props().onChange({ target: { id: descSlotId, value: 'suresh' } });
|
|
624
|
+
});
|
|
625
|
+
wrapper.update();
|
|
607
626
|
|
|
608
627
|
const finalInlinePreview = wrapper.find('UnifiedPreview').filterWhere((n) => n.prop('content')?.carouselData).at(0);
|
|
609
628
|
const finalInlineCards = finalInlinePreview.props().content.carouselData;
|
|
610
629
|
expect(finalInlineCards[0].title).toBe('Hi madhavi');
|
|
611
|
-
expect(finalInlineCards[0].bodyText).toBe('Welcome
|
|
630
|
+
expect(finalInlineCards[0].bodyText).toBe('Welcome lakshmi');
|
|
612
631
|
expect(finalInlineCards[1].title).toBe('Offer for raghav');
|
|
613
|
-
expect(finalInlineCards[1].bodyText).toBe('Dear
|
|
632
|
+
expect(finalInlineCards[1].bodyText).toBe('Dear suresh');
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('picking a tag from "Add Labels" for card 2\'s description (a bare semantic token with no numeric backing) shows the value live, without touching card 2\'s title slot', async () => {
|
|
636
|
+
const templateData = {
|
|
637
|
+
name: 'CarouselTagPickTemplate',
|
|
638
|
+
versions: {
|
|
639
|
+
base: {
|
|
640
|
+
content: {
|
|
641
|
+
RCS: {
|
|
642
|
+
rcsContent: {
|
|
643
|
+
cardType: 'CAROUSEL',
|
|
644
|
+
cardSettings: { cardOrientation: 'VERTICAL', cardWidth: 'SMALL' },
|
|
645
|
+
cardContent: [
|
|
646
|
+
{
|
|
647
|
+
title: 'Hi {{name}}',
|
|
648
|
+
description: 'Welcome {{name}}',
|
|
649
|
+
mediaType: 'IMAGE',
|
|
650
|
+
media: { mediaUrl: 'https://cdn.example.com/c1.png', height: 'MEDIUM' },
|
|
651
|
+
cardVarMapped: {},
|
|
652
|
+
suggestions: [],
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
title: 'sdvsdvs {{first_name_capital}}',
|
|
656
|
+
description: 'dssdfv {{adv}}',
|
|
657
|
+
mediaType: 'IMAGE',
|
|
658
|
+
media: { mediaUrl: 'https://cdn.example.com/c2.png', height: 'MEDIUM' },
|
|
659
|
+
cardVarMapped: {},
|
|
660
|
+
suggestions: [],
|
|
661
|
+
},
|
|
662
|
+
],
|
|
663
|
+
contentType: 'RICHCARD',
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
type: 'RCS',
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
const wrapper = mountWithIntl(
|
|
673
|
+
<Provider store={store}>
|
|
674
|
+
<Rcs
|
|
675
|
+
actions={{
|
|
676
|
+
uploadRcsAsset,
|
|
677
|
+
clearRcsMediaAsset,
|
|
678
|
+
createTemplate,
|
|
679
|
+
clearCreateResponse,
|
|
680
|
+
getTemplateDetails,
|
|
681
|
+
editTemplate,
|
|
682
|
+
clearEditResponse,
|
|
683
|
+
}}
|
|
684
|
+
globalActions={{ fetchSchemaForEntity }}
|
|
685
|
+
onCreateComplete={onCreateComplete}
|
|
686
|
+
handleClose={handleClose}
|
|
687
|
+
intl={{ formatMessage }}
|
|
688
|
+
location={{ pathname: '/rcs/edit', query: { type: false, module: 'default' }, search: '' }}
|
|
689
|
+
params={params}
|
|
690
|
+
templateData={templateData}
|
|
691
|
+
rcsData={{}}
|
|
692
|
+
isFullMode={false}
|
|
693
|
+
isEditFlow={false}
|
|
694
|
+
loadingTags={false}
|
|
695
|
+
metaEntities={[]}
|
|
696
|
+
isDltEnabled={false}
|
|
697
|
+
smsRegister={'DLT'}
|
|
698
|
+
/>
|
|
699
|
+
</Provider>,
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
// Flush hydration useEffects
|
|
703
|
+
await act(async () => {
|
|
704
|
+
await Promise.resolve();
|
|
705
|
+
});
|
|
706
|
+
wrapper.update();
|
|
707
|
+
|
|
708
|
+
// Card 2 is inactive until switched to.
|
|
709
|
+
act(() => {
|
|
710
|
+
wrapper.find('CapTab').prop('onChange')('1');
|
|
711
|
+
});
|
|
712
|
+
wrapper.update();
|
|
713
|
+
|
|
714
|
+
const card2 = wrapper.find('CarouselCard').filterWhere((n) => n.prop('cardIndex') === 1);
|
|
715
|
+
|
|
716
|
+
const descVarArea = card2
|
|
717
|
+
.find('CapInputTextArea')
|
|
718
|
+
.filterWhere((n) => String(n.prop('id') || '').includes('{{adv}}_'));
|
|
719
|
+
expect(descVarArea.length).toBeGreaterThanOrEqual(1);
|
|
720
|
+
|
|
721
|
+
// Focus card 2's description var slot, exactly like a user clicking into that box.
|
|
722
|
+
act(() => {
|
|
723
|
+
const id = descVarArea.at(0).prop('id');
|
|
724
|
+
descVarArea.at(0).props().onFocus({ target: { id } });
|
|
725
|
+
});
|
|
726
|
+
wrapper.update();
|
|
727
|
+
|
|
728
|
+
// Card 2's description TagList (the second .tag-mock under this card) picks "adv".
|
|
729
|
+
const card2AfterFocus = wrapper.find('CarouselCard').filterWhere((n) => n.prop('cardIndex') === 1);
|
|
730
|
+
const descTagList = card2AfterFocus.find('.tag-mock').at(1);
|
|
731
|
+
act(() => {
|
|
732
|
+
descTagList.props().onTagSelect('adv');
|
|
733
|
+
});
|
|
734
|
+
wrapper.update();
|
|
735
|
+
|
|
736
|
+
// Live (no save/reload) — the description's own box should now show the picked tag as its value.
|
|
737
|
+
const card2Final = wrapper.find('CarouselCard').filterWhere((n) => n.prop('cardIndex') === 1);
|
|
738
|
+
const descVarAreaAfterPick = card2Final
|
|
739
|
+
.find('CapInputTextArea')
|
|
740
|
+
.filterWhere((n) => String(n.prop('id') || '').includes('{{adv}}_'));
|
|
741
|
+
expect(descVarAreaAfterPick.at(0).prop('value')).toBe('{{adv}}');
|
|
742
|
+
|
|
743
|
+
// Title's own slot must be untouched by the description's tag pick.
|
|
744
|
+
const titleVarAreaAfterPick = card2Final
|
|
745
|
+
.find('CapInputTextArea')
|
|
746
|
+
.filterWhere((n) => String(n.prop('id') || '').includes('{{first_name_capital}}_'));
|
|
747
|
+
expect(titleVarAreaAfterPick.at(0).prop('value')).toBe('');
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('reopening a carousel after a prior save shows card 2\'s description value (persisted under its global positional key), not empty', async () => {
|
|
751
|
+
// Shape of a template that was already saved once: card 2 (index 1) picked "adv" for its
|
|
752
|
+
// description slot in an earlier session. Card 0 has 2 var tokens (title + desc), so card 2's
|
|
753
|
+
// title is global position 3 and its description is global position 4 — key "4".
|
|
754
|
+
const templateData = {
|
|
755
|
+
name: 'CarouselReopenTemplate',
|
|
756
|
+
versions: {
|
|
757
|
+
base: {
|
|
758
|
+
content: {
|
|
759
|
+
RCS: {
|
|
760
|
+
rcsContent: {
|
|
761
|
+
cardType: 'CAROUSEL',
|
|
762
|
+
cardSettings: { cardOrientation: 'VERTICAL', cardWidth: 'SMALL' },
|
|
763
|
+
cardContent: [
|
|
764
|
+
{
|
|
765
|
+
title: 'Hi {{name}}',
|
|
766
|
+
description: 'Welcome {{name}}',
|
|
767
|
+
mediaType: 'IMAGE',
|
|
768
|
+
media: { mediaUrl: 'https://cdn.example.com/c1.png', height: 'MEDIUM' },
|
|
769
|
+
cardVarMapped: {},
|
|
770
|
+
suggestions: [],
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
title: 'sdvsdvs {{first_name_capital}}',
|
|
774
|
+
description: 'dssdfv {{adv}}',
|
|
775
|
+
mediaType: 'IMAGE',
|
|
776
|
+
media: { mediaUrl: 'https://cdn.example.com/c2.png', height: 'MEDIUM' },
|
|
777
|
+
cardVarMapped: { '4': '{{adv}}' },
|
|
778
|
+
suggestions: [],
|
|
779
|
+
},
|
|
780
|
+
],
|
|
781
|
+
contentType: 'RICHCARD',
|
|
782
|
+
},
|
|
783
|
+
},
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
},
|
|
787
|
+
type: 'RCS',
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
const wrapper = mountWithIntl(
|
|
791
|
+
<Provider store={store}>
|
|
792
|
+
<Rcs
|
|
793
|
+
actions={{
|
|
794
|
+
uploadRcsAsset,
|
|
795
|
+
clearRcsMediaAsset,
|
|
796
|
+
createTemplate,
|
|
797
|
+
clearCreateResponse,
|
|
798
|
+
getTemplateDetails,
|
|
799
|
+
editTemplate,
|
|
800
|
+
clearEditResponse,
|
|
801
|
+
}}
|
|
802
|
+
globalActions={{ fetchSchemaForEntity }}
|
|
803
|
+
onCreateComplete={onCreateComplete}
|
|
804
|
+
handleClose={handleClose}
|
|
805
|
+
intl={{ formatMessage }}
|
|
806
|
+
location={{ pathname: '/rcs/edit', query: { type: false, module: 'default' }, search: '' }}
|
|
807
|
+
params={params}
|
|
808
|
+
templateData={templateData}
|
|
809
|
+
rcsData={{}}
|
|
810
|
+
isFullMode={false}
|
|
811
|
+
isEditFlow={false}
|
|
812
|
+
loadingTags={false}
|
|
813
|
+
metaEntities={[]}
|
|
814
|
+
isDltEnabled={false}
|
|
815
|
+
smsRegister={'DLT'}
|
|
816
|
+
/>
|
|
817
|
+
</Provider>,
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
await act(async () => {
|
|
821
|
+
await Promise.resolve();
|
|
822
|
+
});
|
|
823
|
+
wrapper.update();
|
|
824
|
+
|
|
825
|
+
act(() => {
|
|
826
|
+
wrapper.find('CapTab').prop('onChange')('1');
|
|
827
|
+
});
|
|
828
|
+
wrapper.update();
|
|
829
|
+
|
|
830
|
+
const card2 = wrapper.find('CarouselCard').filterWhere((n) => n.prop('cardIndex') === 1);
|
|
831
|
+
const descVarArea = card2
|
|
832
|
+
.find('CapInputTextArea')
|
|
833
|
+
.filterWhere((n) => String(n.prop('id') || '').includes('{{adv}}_'));
|
|
834
|
+
expect(descVarArea.length).toBeGreaterThanOrEqual(1);
|
|
835
|
+
expect(descVarArea.at(0).prop('value')).toBe('{{adv}}');
|
|
614
836
|
});
|
|
615
837
|
});
|
|
616
838
|
|
|
@@ -669,6 +669,23 @@ describe('RCS utils', () => {
|
|
|
669
669
|
expect(out[0].description).toBe('This is Bird with colorful feathers {{voucher(1190738)}}');
|
|
670
670
|
});
|
|
671
671
|
|
|
672
|
+
it('substitutes plain text typed into a numeric personalization slot (not a tag pick)', () => {
|
|
673
|
+
const out = mapRcsCardContentForConsumerWithResolvedTags(
|
|
674
|
+
[
|
|
675
|
+
{
|
|
676
|
+
title: 'Hello {{1}}',
|
|
677
|
+
description: 'Visit us at {{2}} today',
|
|
678
|
+
mediaType: 'IMAGE',
|
|
679
|
+
cardVarMapped: { 1: 'World', 2: 'Downtown Store' },
|
|
680
|
+
},
|
|
681
|
+
],
|
|
682
|
+
{},
|
|
683
|
+
false,
|
|
684
|
+
);
|
|
685
|
+
expect(out[0].title).toBe('Hello World');
|
|
686
|
+
expect(out[0].description).toBe('Visit us at Downtown Store today');
|
|
687
|
+
});
|
|
688
|
+
|
|
672
689
|
it('BUG REPRO: 2-card carousel — card 1 description resolves to card 0\'s value instead of its own tag pick', () => {
|
|
673
690
|
// Mirrors CreativesContainer/index.js ~1448-1461: rootRcsCardVarMapped is derived ONLY from
|
|
674
691
|
// cardContentFromSubmit[0] (the FIRST card's own cardVarMapped) and reused for every card.
|
|
@@ -197,6 +197,50 @@ export function coalesceCardVarMappedToTemplate(
|
|
|
197
197
|
templateVarTokens.forEach((token, slotIndexZeroBased) => {
|
|
198
198
|
const semanticVarName = getVarNameFromToken(token);
|
|
199
199
|
if (!semanticVarName) return;
|
|
200
|
+
|
|
201
|
+
if (RCS_NUMERIC_VAR_NAME_REGEX.test(semanticVarName)) {
|
|
202
|
+
// Carousel numeric placeholder ({{3}}, {{4}}, …): the slot key IS the token digit itself —
|
|
203
|
+
// numbers are allocated globally across the whole carousel (getNextCarouselVarToken), not
|
|
204
|
+
// re-numbered per card, so the position-based `slotIndexZeroBased + 1` key below would target
|
|
205
|
+
// the wrong slot for every card after the first (e.g. card 2's first token is digit "3", not "1").
|
|
206
|
+
const numericSlotKey = semanticVarName;
|
|
207
|
+
const trimmedSlotValue = String(lookupSourceMap[numericSlotKey] ?? '').trim();
|
|
208
|
+
coalescedMap[numericSlotKey] = trimmedSlotValue;
|
|
209
|
+
// The slot's value may itself be a mustache-wrapped tag reference (e.g. "3" -> "{{last_name}}")
|
|
210
|
+
// — mirror it onto the bare semantic key too so VarSegment editors keyed by tag name also
|
|
211
|
+
// prepopulate (same rule as syncCardVarMappedSemanticsFromSlots).
|
|
212
|
+
const mustacheInnerMatch = trimmedSlotValue.match(/^\{\{([^}]+)\}\}$/);
|
|
213
|
+
const innerSemanticName = mustacheInnerMatch?.[1]?.trim();
|
|
214
|
+
if (innerSemanticName && innerSemanticName !== numericSlotKey) {
|
|
215
|
+
const existingInnerValue = String(coalescedMap[innerSemanticName] ?? '').trim();
|
|
216
|
+
if (!existingInnerValue) {
|
|
217
|
+
coalescedMap[innerSemanticName] = trimmedSlotValue;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// A numeric slot may already self-reference this exact tag (e.g. "4" -> "{{last_name}}") when
|
|
224
|
+
// the template's own numeric placeholder has since been resolved to this tag in the text (see
|
|
225
|
+
// mapRcsCardContentForConsumerWithResolvedTags). That numeric key is the source of truth — don't
|
|
226
|
+
// fall through to the position-based legacy lookup below, which targets an unrelated (or
|
|
227
|
+
// nonexistent) slot and would clobber the real value with an empty string.
|
|
228
|
+
const mustacheSelfReference = `{{${semanticVarName}}}`;
|
|
229
|
+
const existingNumericKeyForSemantic = Object.keys(lookupSourceMap).find((key) => (
|
|
230
|
+
RCS_NUMERIC_VAR_NAME_REGEX.test(key) && lookupSourceMap[key] === mustacheSelfReference
|
|
231
|
+
));
|
|
232
|
+
if (existingNumericKeyForSemantic) {
|
|
233
|
+
const trimmedSlotValue = String(lookupSourceMap[existingNumericKeyForSemantic] ?? '').trim();
|
|
234
|
+
coalescedMap[existingNumericKeyForSemantic] = trimmedSlotValue;
|
|
235
|
+
if (!seenSemanticVarNames.has(semanticVarName)) {
|
|
236
|
+
seenSemanticVarNames.add(semanticVarName);
|
|
237
|
+
coalescedMap[semanticVarName] = trimmedSlotValue;
|
|
238
|
+
}
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Legacy: template embeds the semantic token directly ({{user_name}}); older payloads stored
|
|
243
|
+
// the resolved value under a position-based numeric key ("1", "2", …) by order of appearance.
|
|
200
244
|
const numericSlotKey = String(slotIndexZeroBased + 1);
|
|
201
245
|
const isRepeatOfSemanticName = seenSemanticVarNames.has(semanticVarName);
|
|
202
246
|
const skipSharedSemanticLookup =
|
|
@@ -207,12 +251,6 @@ export function coalesceCardVarMappedToTemplate(
|
|
|
207
251
|
valueFromSource = lookupSourceMap[semanticVarName];
|
|
208
252
|
}
|
|
209
253
|
}
|
|
210
|
-
if (valueFromSource === undefined || valueFromSource === null) {
|
|
211
|
-
valueFromSource = lookupSourceMap[String(slotIndexZeroBased + 1)];
|
|
212
|
-
}
|
|
213
|
-
if (valueFromSource === undefined || valueFromSource === null) {
|
|
214
|
-
valueFromSource = lookupSourceMap[slotIndexZeroBased + 1];
|
|
215
|
-
}
|
|
216
254
|
const trimmedSlotValue = valueFromSource == null ? '' : String(valueFromSource).trim();
|
|
217
255
|
coalescedMap[numericSlotKey] = trimmedSlotValue;
|
|
218
256
|
if (!seenSemanticVarNames.has(semanticVarName)) {
|
|
@@ -291,11 +329,6 @@ export function isRcsTextOnlyCardMediaType(mediaType) {
|
|
|
291
329
|
*
|
|
292
330
|
* @param {boolean} [preserveLiteralValueTokens=false] When true, a slot value that is NOT itself a
|
|
293
331
|
* `{{...}}`-wrapped tag (e.g. a literal preview string like "SAVE20" typed for a voucher/offer tag)
|
|
294
|
-
* is left as the raw token instead of being baked into the returned string. Personalization-style
|
|
295
|
-
* values (themselves another mustache tag, e.g. `{{loyalty_points}}`) still resolve either way — those
|
|
296
|
-
* are internal-alias renames, not literal content. Pass true for payloads that get persisted/replayed
|
|
297
|
-
* (see `mapRcsCardContentForConsumerWithResolvedTags`) so a voucher's saved token survives for the
|
|
298
|
-
* editor to re-render an editable slot from, and for the backend to resolve per-recipient at send time.
|
|
299
332
|
*
|
|
300
333
|
* @param {number} [cardSlotOffset=0] Starting global slot index for this card's title. Carousel cards
|
|
301
334
|
* beyond index 0 must pass the running total of every prior card's var count — numeric `{{N}}` tokens
|
|
@@ -339,7 +372,12 @@ export function resolveRcsCardPreviewStrings(
|
|
|
339
372
|
);
|
|
340
373
|
if (slotValue == null || String(slotValue).trim() === '') return elem;
|
|
341
374
|
const trimmedSlotValue = String(slotValue).trim();
|
|
342
|
-
|
|
375
|
+
const isPlainNumericSlot = RCS_NUMERIC_VAR_NAME_REGEX.test(key);
|
|
376
|
+
if (
|
|
377
|
+
preserveLiteralValueTokens
|
|
378
|
+
&& !isPlainNumericSlot
|
|
379
|
+
&& !RCS_MUSTACHE_WRAPPED_REGEX.test(trimmedSlotValue)
|
|
380
|
+
) {
|
|
343
381
|
return elem;
|
|
344
382
|
}
|
|
345
383
|
return String(slotValue);
|
|
@@ -403,6 +441,7 @@ export function mapRcsCardContentForConsumerWithResolvedTags(
|
|
|
403
441
|
const descVarCount = (String(cardDescription).match(rcsVarRegex) || []).length;
|
|
404
442
|
carouselSlotOffset += titleVarCount + descVarCount;
|
|
405
443
|
const { cardVarMapped: _drop, ...cardRest } = card;
|
|
444
|
+
|
|
406
445
|
return {
|
|
407
446
|
...cardRest,
|
|
408
447
|
title: rcsTitle,
|
|
@@ -112,8 +112,8 @@ exports[`Creatives SmsTraiCreate test/> done button should be disabled if blank
|
|
|
112
112
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
113
113
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
114
114
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
115
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
116
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
115
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
116
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
117
117
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
118
118
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
119
119
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -3273,8 +3273,8 @@ exports[`Creatives SmsTraiCreate test/> invalid file drop 1`] = `
|
|
|
3273
3273
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
3274
3274
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
3275
3275
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
3276
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
3277
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
3276
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
3277
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
3278
3278
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
3279
3279
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
3280
3280
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -6417,8 +6417,8 @@ exports[`Creatives SmsTraiCreate test/> renders UI 1`] = `
|
|
|
6417
6417
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
6418
6418
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
6419
6419
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
6420
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
6421
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
6420
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
6421
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
6422
6422
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
6423
6423
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
6424
6424
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -9578,8 +9578,8 @@ exports[`Creatives SmsTraiCreate test/> valid file drop 1`] = `
|
|
|
9578
9578
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
9579
9579
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
9580
9580
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
9581
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
9582
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
9581
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
9582
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
9583
9583
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
9584
9584
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
9585
9585
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -87,8 +87,8 @@ exports[`Creatives SmsTraiEdit test/> should render 1`] = `
|
|
|
87
87
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
88
88
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
89
89
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
90
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
91
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
90
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
91
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
92
92
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
93
93
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
94
94
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -7411,8 +7411,8 @@ FREE GIFTS-
|
|
|
7411
7411
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
7412
7412
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
7413
7413
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
7414
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
7415
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
7414
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
7415
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
7416
7416
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
7417
7417
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
7418
7418
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -9749,8 +9749,8 @@ FREE GIFTS-
|
|
|
9749
9749
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
9750
9750
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
9751
9751
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
9752
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
9753
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
9752
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
9753
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
9754
9754
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
9755
9755
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
9756
9756
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -12238,8 +12238,8 @@ exports[`Creatives SmsTraiEdit test/> should render 2`] = `
|
|
|
12238
12238
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
12239
12239
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
12240
12240
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
12241
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
12242
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
12241
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
12242
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
12243
12243
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
12244
12244
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
12245
12245
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -20936,8 +20936,8 @@ FREE GIFTS-
|
|
|
20936
20936
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
20937
20937
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
20938
20938
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
20939
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
20940
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
20939
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
20940
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
20941
20941
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
20942
20942
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
20943
20943
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -23274,8 +23274,8 @@ FREE GIFTS-
|
|
|
23274
23274
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
23275
23275
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
23276
23276
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
23277
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
23278
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
23277
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
23278
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
23279
23279
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
23280
23280
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
23281
23281
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -25763,8 +25763,8 @@ exports[`Creatives SmsTraiEdit test/> should save 1`] = `
|
|
|
25763
25763
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
25764
25764
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
25765
25765
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
25766
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
25767
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
25766
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
25767
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
25768
25768
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
25769
25769
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
25770
25770
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -33087,8 +33087,8 @@ FREE GIFTS-
|
|
|
33087
33087
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
33088
33088
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
33089
33089
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
33090
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
33091
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
33090
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
33091
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
33092
33092
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
33093
33093
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
33094
33094
|
"creatives.components.CapTagList.Ok": "Ok",
|
|
@@ -35425,8 +35425,8 @@ FREE GIFTS-
|
|
|
35425
35425
|
"app.v2containers.SmsTrai.Edit.traiEditTitle": "Message",
|
|
35426
35426
|
"app.v2containers.SmsTrai.Edit.unicodeLabel": "Allow unicode characters",
|
|
35427
35427
|
"app.v2containers.SmsTrai.Edit.unsupportedTagsValidationError": "Unsupported tags: {unsupportedTags}. Please remove them from this message.",
|
|
35428
|
-
"creatives.components.AccessForbidden.forbiddenDesc": "
|
|
35429
|
-
"creatives.components.AccessForbidden.forbiddenHeader": "
|
|
35428
|
+
"creatives.components.AccessForbidden.forbiddenDesc": "Please contact your organisation administrator to request access.",
|
|
35429
|
+
"creatives.components.AccessForbidden.forbiddenHeader": "You don't have permission to view this page.",
|
|
35430
35430
|
"creatives.components.BreadCrumbs.header": "This is the BreadCrumbs component !",
|
|
35431
35431
|
"creatives.components.CapTagList.Cancel": "Cancel",
|
|
35432
35432
|
"creatives.components.CapTagList.Ok": "Ok",
|