@capillarytech/creatives-library 9.0.59-alpha.1 → 9.0.59-alpha.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "9.0.59-alpha.1",
4
+ "version": "9.0.59-alpha.2",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -20,12 +20,15 @@ import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
20
20
  import CapHeader from '@capillarytech/cap-ui-library/CapHeader';
21
21
  import CapSlideBox from '@capillarytech/cap-ui-library/CapSlideBox';
22
22
  import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
23
+ import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
23
24
  import CommunicationFlow from './index';
24
25
  import { CHANNELS, DEFAULT_COMMUNICATION_STRATEGY_OPTIONS, DYNAMIC_CONTROLS_CONFIG } from './constants';
25
26
  import {
26
27
  VIBER, SMS, EMAIL, WHATSAPP, RCS, ZALO,
27
28
  } from '../CreativesContainer/constants';
28
29
  import getContentBody from './utils/getContentBody';
30
+ import { getCommDefinition, getCommDefinitionVersion } from '../../services/api';
31
+ import { fetchAndMapCommDefinition } from './utils/mapCommDefinitionToStepData';
29
32
  import messages from './messages';
30
33
  import addCreativesIllustration from '../Assets/images/addCreativesIllustration.svg';
31
34
  import './CommunicationFlow.scss';
@@ -90,6 +93,40 @@ const CommunicationFlowCard = ({
90
93
  }
91
94
  }, [initialData]);
92
95
 
96
+ // A consumer other than CapNotify (which always pre-fetches and passes full initialData
97
+ // itself) may hand this card only a commDefinitionId — no local content copy at all — and
98
+ // rely on CommunicationFlowCard to fetch + show the configured summary itself, instead of
99
+ // the empty "Add creatives" state. Mirrors CommunicationFlow.js's own fetch-by-id path
100
+ // (config.context.existingCommDefinitionId); doing it here too means the SUMMARY CARD
101
+ // reflects fetched content immediately, without the user having to open the slidebox and
102
+ // click Save first — CommunicationFlow's own fetch would only ever reach this card's
103
+ // savedData via that save round-trip, since nothing here listens for its internal onChange.
104
+ const [isFetchingCommDefinition, setIsFetchingCommDefinition] = useState(
105
+ () => !!config?.context?.existingCommDefinitionId && !initialData?.contentItems?.length,
106
+ );
107
+
108
+ useEffect(() => {
109
+ const existingCommDefinitionId = config?.context?.existingCommDefinitionId;
110
+ if (!existingCommDefinitionId || initialData?.contentItems?.length) return undefined;
111
+ let cancelled = false;
112
+ (async () => {
113
+ try {
114
+ const mapped = await fetchAndMapCommDefinition(existingCommDefinitionId, {
115
+ getCommDefinition,
116
+ getCommDefinitionVersion,
117
+ });
118
+ if (!cancelled && mapped) {
119
+ setSavedData((prevSavedData) => ({ ...prevSavedData, ...mapped }));
120
+ }
121
+ } catch (error) {
122
+ console.error('[CommunicationFlowCard] fetchAndMapCommDefinition error:', error);
123
+ } finally {
124
+ if (!cancelled) setIsFetchingCommDefinition(false);
125
+ }
126
+ })();
127
+ return () => { cancelled = true; };
128
+ }, []);
129
+
93
130
  const handleSave = useCallback((data) => {
94
131
  setSavedData(data);
95
132
  setShowSlideBox(false);
@@ -199,7 +236,16 @@ const CommunicationFlowCard = ({
199
236
 
200
237
  return (
201
238
  <>
202
- {!firstItem ? (
239
+ {isFetchingCommDefinition ? (
240
+ <CapCard
241
+ className="communication-flow-card-loading"
242
+ bodyStyle={{ padding: 0, height: 152 }}
243
+ >
244
+ <CapRow type="flex" justify="center" align="middle" style={{ height: '100%' }}>
245
+ <CapSpin spinning />
246
+ </CapRow>
247
+ </CapCard>
248
+ ) : !firstItem ? (
203
249
  <CapCard
204
250
  className="communication-flow-card-empty"
205
251
  bodyStyle={{ padding: 0, height: 152 }}
@@ -38,14 +38,25 @@ jest.mock('@capillarytech/cap-ui-library/CapIcon', () => function MockCapIcon({
38
38
 
39
39
  jest.mock('../utils/getContentBody', () => jest.fn());
40
40
 
41
- import { render, screen, fireEvent } from '@testing-library/react';
41
+ jest.mock('../../../services/api', () => ({
42
+ getCommDefinition: jest.fn(),
43
+ getCommDefinitionVersion: jest.fn(),
44
+ }));
45
+
46
+ jest.mock('../utils/mapCommDefinitionToStepData', () => ({
47
+ fetchAndMapCommDefinition: jest.fn(),
48
+ }));
49
+
50
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
42
51
  import '@testing-library/jest-dom';
43
52
  import { IntlProvider } from 'react-intl';
44
53
  import CommunicationFlowCard from '../CommunicationFlowCard';
45
54
  import getContentBody from '../utils/getContentBody';
55
+ import { fetchAndMapCommDefinition } from '../utils/mapCommDefinitionToStepData';
46
56
 
47
57
  beforeEach(() => {
48
58
  getContentBody.mockReturnValue(<span data-testid="content-body">Content</span>);
59
+ fetchAndMapCommDefinition.mockReset();
49
60
  });
50
61
 
51
62
  const baseConfig = {
@@ -711,3 +722,52 @@ describe('CommunicationFlowCard', () => {
711
722
  });
712
723
  });
713
724
  });
725
+
726
+ describe('CommunicationFlowCard — fetch-by-id (config.context.existingCommDefinitionId)', () => {
727
+ const configWithId = {
728
+ ...baseConfig,
729
+ context: { existingCommDefinitionId: 'cd_1' },
730
+ };
731
+
732
+ it('shows a loading state (not the empty "Add creatives" state) while fetching', () => {
733
+ fetchAndMapCommDefinition.mockReturnValue(new Promise(() => {})); // never resolves
734
+ renderCard({ config: configWithId, initialData: null });
735
+ expect(screen.queryByText('Add creatives')).not.toBeInTheDocument();
736
+ expect(screen.queryByText('Message:')).not.toBeInTheDocument();
737
+ });
738
+
739
+ it('shows the configured summary card once the fetch resolves, without opening the slidebox', async () => {
740
+ fetchAndMapCommDefinition.mockResolvedValue(makeSavedData());
741
+ renderCard({ config: configWithId, initialData: null });
742
+
743
+ await waitFor(() => expect(screen.getByText('Message:')).toBeInTheDocument());
744
+ expect(screen.queryByText('Add creatives')).not.toBeInTheDocument();
745
+ expect(screen.queryByTestId('slide-box')).not.toBeInTheDocument();
746
+ });
747
+
748
+ it('falls back to the empty "Add creatives" state when the fetch resolves with nothing mappable', async () => {
749
+ fetchAndMapCommDefinition.mockResolvedValue(null);
750
+ renderCard({ config: configWithId, initialData: null });
751
+
752
+ await waitFor(() => expect(screen.getByText('Add creatives')).toBeInTheDocument());
753
+ });
754
+
755
+ it('falls back to the empty "Add creatives" state when the fetch rejects', async () => {
756
+ fetchAndMapCommDefinition.mockRejectedValue(new Error('network error'));
757
+ renderCard({ config: configWithId, initialData: null });
758
+
759
+ await waitFor(() => expect(screen.getByText('Add creatives')).toBeInTheDocument());
760
+ });
761
+
762
+ it('does not fetch when initialData already has content (e.g. CapNotify, which always pre-fetches itself)', () => {
763
+ renderCard({ config: configWithId, initialData: makeSavedData() });
764
+ expect(fetchAndMapCommDefinition).not.toHaveBeenCalled();
765
+ expect(screen.getByText('Message:')).toBeInTheDocument();
766
+ });
767
+
768
+ it('does not fetch when config.context.existingCommDefinitionId is absent', () => {
769
+ renderCard({ config: baseConfig, initialData: null });
770
+ expect(fetchAndMapCommDefinition).not.toHaveBeenCalled();
771
+ expect(screen.getByText('Add creatives')).toBeInTheDocument();
772
+ });
773
+ });