@capillarytech/creatives-library 8.0.158 → 8.0.159-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/config/app.js CHANGED
@@ -20,7 +20,7 @@ const config = {
20
20
  accountConfig: (strs, accountId) => `${window.location.origin}/org/config/AccountAdd?q=a&channelId=2&accountId=${accountId}&edit=1`,
21
21
  },
22
22
  development: {
23
- api_endpoint: 'http://localhost:2022/arya/api/v1/creatives',
23
+ api_endpoint: 'https://crm-nightly-new.cc.capillarytech.com/arya/api/v1/creatives',
24
24
  campaigns_api_endpoint: 'https://crm-nightly-new.cc.capillarytech.com/iris/v2/campaigns',
25
25
  campaigns_api_org_endpoint: 'https://crm-nightly-new.cc.capillarytech.com/iris/v2/org/campaign',
26
26
  auth_endpoint: 'https://crm-nightly-new.cc.capillarytech.com/arya/api/v1/auth',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.158",
4
+ "version": "8.0.159-alpha.0",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -69,7 +69,9 @@ const CustomerSearchSection = ({
69
69
  if (value?.trim()) {
70
70
  debouncedSearch(value.trim());
71
71
  } else {
72
- onClearSearch();
72
+ if (onClearSearch && typeof onClearSearch === 'function') {
73
+ onClearSearch();
74
+ }
73
75
  setShowDropdown(false);
74
76
  }
75
77
  },
@@ -182,86 +184,88 @@ const CustomerSearchSection = ({
182
184
 
183
185
  return (
184
186
  <CapRow className="customer-search-section">
185
- <CapRow className="search-container" ref={searchContainerRef}>
186
- {selectedCustomer ? (
187
- <SelectedCustomerView
188
- customer={selectedCustomer}
189
- onClear={onClear}
190
- showIdentifiers={showIdentifiers}
191
- />
192
- ) : (
193
- <SearchInput
194
- value={customerSearchValue}
195
- onChange={handleSearchChange}
196
- isLoading={isSearchingCustomer}
197
- error={searchError}
198
- disabled={disabled}
199
- placeholder={formatMessage(messages.searchPlaceholder)}
200
- />
201
- )}
202
- {showDropdown && !selectedCustomer && (
203
- <CapRow className="search-dropdown-container">
204
- <CapRow type="flex" justify="center" align="middle">
205
- {(processedSearchData?.length === 0 && customerSearchValue != null && !isSearchingCustomer) && (
206
- <CapCard className="validation-card">
207
- <CapHeading type="h6">
208
- {formatMessage(messages.noCustomersFound)}
209
- </CapHeading>
210
- </CapCard>
211
- )}
212
- {(isSearchingCustomer) && (
213
- <CapCard className="validation-card">
214
- <CapRow className="spin-card-align">
215
- <CapSpin />
216
- </CapRow>
217
- </CapCard>
218
- )}
219
- </CapRow>
220
- <CapRow type="flex" justify="center" align="middle">
221
- {processedSearchData?.length > 0 && (
222
- <CapCard className="search-result-card scroll-bar">
223
- {
224
- <CapRow className="identifier-row">
225
- {processedSearchData?.map((d) => (
226
- <>
227
- <CapLink
228
- key={d?.customerId}
229
- className="search-results-height link-result"
230
- title={(
231
- <>
232
- <CapRow type="flex" align="middle" gutter={16}>
233
- <CapColumn data-testid="namingIcon">
234
- <CapRow className="customer-common-profile">{getNamingIcon(d?.name)}</CapRow>
235
- </CapColumn>
236
- <CapColumn>
237
- <CapRow type="flex" gutter={12}>
238
- <CapColumn data-testid="searchResultName">
239
- <CapHeading className="search-text" type="h4">
240
- {d?.name}
241
- </CapHeading>
242
- </CapColumn>
243
- </CapRow>
244
- {(
245
- <CapRow type="flex">
246
- {showIdentifiers(d?.identifiers)}
247
- </CapRow>
248
- )}
249
- </CapColumn>
250
- </CapRow>
251
- </>
252
- )}
253
- onClick={() => handleCustomerSelectItem(d)}
254
- />
255
- </>
256
- ))}
187
+ <div ref={searchContainerRef}>
188
+ <CapRow className="search-container">
189
+ {selectedCustomer ? (
190
+ <SelectedCustomerView
191
+ customer={selectedCustomer}
192
+ onClear={onClear}
193
+ showIdentifiers={showIdentifiers}
194
+ />
195
+ ) : (
196
+ <SearchInput
197
+ value={customerSearchValue}
198
+ onChange={handleSearchChange}
199
+ isLoading={isSearchingCustomer}
200
+ error={searchError}
201
+ disabled={disabled}
202
+ placeholder={formatMessage(messages.searchPlaceholder)}
203
+ />
204
+ )}
205
+ {showDropdown && !selectedCustomer && (
206
+ <CapRow className="search-dropdown-container">
207
+ <CapRow type="flex" justify="center" align="middle">
208
+ {(processedSearchData?.length === 0 && customerSearchValue != null && !isSearchingCustomer) && (
209
+ <CapCard className="validation-card">
210
+ <CapHeading type="h6">
211
+ {formatMessage(messages.noCustomersFound)}
212
+ </CapHeading>
213
+ </CapCard>
214
+ )}
215
+ {(isSearchingCustomer) && (
216
+ <CapCard className="validation-card">
217
+ <CapRow className="spin-card-align">
218
+ <CapSpin />
257
219
  </CapRow>
258
- }
259
- </CapCard>
260
- )}
220
+ </CapCard>
221
+ )}
222
+ </CapRow>
223
+ <CapRow type="flex" justify="center" align="middle">
224
+ {processedSearchData?.length > 0 && (
225
+ <CapCard className="search-result-card scroll-bar">
226
+ {
227
+ <CapRow className="identifier-row">
228
+ {processedSearchData?.map((d) => (
229
+ <>
230
+ <CapLink
231
+ key={d?.customerId}
232
+ className="search-results-height link-result"
233
+ title={(
234
+ <>
235
+ <CapRow type="flex" align="middle" gutter={16}>
236
+ <CapColumn data-testid="namingIcon">
237
+ <CapRow className="customer-common-profile">{getNamingIcon(d?.name)}</CapRow>
238
+ </CapColumn>
239
+ <CapColumn>
240
+ <CapRow type="flex" gutter={12}>
241
+ <CapColumn data-testid="searchResultName">
242
+ <CapHeading className="search-text" type="h4">
243
+ {d?.name}
244
+ </CapHeading>
245
+ </CapColumn>
246
+ </CapRow>
247
+ {(
248
+ <CapRow type="flex">
249
+ {showIdentifiers(d?.identifiers)}
250
+ </CapRow>
251
+ )}
252
+ </CapColumn>
253
+ </CapRow>
254
+ </>
255
+ )}
256
+ onClick={() => handleCustomerSelectItem(d)}
257
+ />
258
+ </>
259
+ ))}
260
+ </CapRow>
261
+ }
262
+ </CapCard>
263
+ )}
264
+ </CapRow>
261
265
  </CapRow>
262
- </CapRow>
263
- )}
264
- </CapRow>
266
+ )}
267
+ </CapRow>
268
+ </div>
265
269
  </CapRow>
266
270
  );
267
271
  };
@@ -15,7 +15,7 @@ const PreviewSection = ({
15
15
  formatMessage,
16
16
  PreviewChrome,
17
17
  }) => (
18
- <CapRow className="preview-section panel-section">
18
+ <CapRow className="test-and-preview-section panel-section">
19
19
  <PreviewChrome
20
20
  device={previewDevice}
21
21
  onDeviceChange={setPreviewDevice}
@@ -28,6 +28,7 @@
28
28
  }
29
29
 
30
30
  .test-preview-container {
31
+ margin-left: 2%;
31
32
  height: 100%;
32
33
  display: flex;
33
34
  flex-direction: column;
@@ -329,7 +330,7 @@
329
330
  }
330
331
  }
331
332
 
332
- .preview-section {
333
+ .test-and-preview-section {
333
334
  .section-title {
334
335
  margin-bottom: $CAP_SPACE_16;
335
336
  color: #333;
@@ -93,10 +93,15 @@ const TestAndPreviewSlidebox = (props) => {
93
93
  ), [requiredTags, customValues]);
94
94
 
95
95
  useEffect(() => {
96
- if (show && beeInstance) {
96
+ // Only save BEE content if we're actually using BEE editor
97
+ const currentTabData = formData[formData.currentTab - 1];
98
+ const activeTab = currentTabData?.activeTab;
99
+ const isDragDrop = currentTabData?.[activeTab]?.is_drag_drop;
100
+
101
+ if (show && beeInstance && isDragDrop) {
97
102
  beeInstance.save();
98
103
  }
99
- }, [show, beeInstance]);
104
+ }, [show, beeInstance, formData]);
100
105
 
101
106
  useEffect(() => {
102
107
  if (show) {
@@ -281,7 +286,35 @@ const TestAndPreviewSlidebox = (props) => {
281
286
  };
282
287
 
283
288
  // Handle update preview
284
- const handleUpdatePreview = () => {
289
+ const handleUpdatePreview = async () => {
290
+ // For BEE editor, ensure content is saved first
291
+ const currentTabData = formData[formData.currentTab - 1];
292
+ const activeTab = currentTabData?.activeTab;
293
+
294
+ // Get latest content from BEE editor if needed
295
+ if (beeInstance && currentTabData?.[activeTab]?.is_drag_drop) {
296
+ // Trigger save to ensure latest content
297
+ beeInstance.save();
298
+
299
+ // Wait a bit for save to complete
300
+ await new Promise((resolve) => setTimeout(resolve, 500));
301
+
302
+ // Get latest content from BEE editor
303
+ beeInstance.getContent((jsonFile, htmlFile) => {
304
+ const payload = {
305
+ channel: EMAIL,
306
+ messageTitle: formData['template-subject'],
307
+ messageBody: htmlFile || content,
308
+ resolvedTags: customValues,
309
+ userId: selectedCustomer?.customerId,
310
+ };
311
+
312
+ actions.updatePreviewRequested(payload);
313
+ });
314
+ return;
315
+ }
316
+
317
+ // Use content from formData for non-BEE editor
285
318
  const payload = {
286
319
  channel: EMAIL,
287
320
  messageTitle: formData['template-subject'],
@@ -289,6 +322,7 @@ const TestAndPreviewSlidebox = (props) => {
289
322
  resolvedTags: customValues,
290
323
  userId: selectedCustomer?.customerId,
291
324
  };
325
+
292
326
  actions.updatePreviewRequested(payload);
293
327
  };
294
328
 
@@ -91,7 +91,7 @@ describe('PreviewSection', () => {
91
91
  </TestWrapper>
92
92
  );
93
93
 
94
- expect(container.querySelector('.preview-section')).toBeTruthy();
94
+ expect(container.querySelector('.test-and-preview-section')).toBeTruthy();
95
95
  expect(container.querySelector('.panel-section')).toBeTruthy();
96
96
  });
97
97
  });
@@ -592,27 +592,24 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
592
592
  onChange = (evt) => {
593
593
  const {isFullMode, showTemplateName} = this.props;
594
594
  const formData = _.cloneDeep(this.state.formData);
595
+ const currentTabData = formData[this.state.currentTab - 1];
596
+ const activeTab = currentTabData?.activeTab;
595
597
 
596
- //if (evt.editor.name.indexOf('editor') === -1) {
597
- // _.forEach(window.CKEDITOR.instances, (winInstance) => {
598
- // const temp = winInstance;
599
- // if (winInstance.name === evt.editor.name) {
600
- //
601
- // setTimeout(() => {
602
- // //temp.name = id;
603
- // }, 1000);
604
- // //temp.name = id;
605
- //
606
- // }
607
- // });
608
- //}
609
- if (!formData[this.state.currentTab - 1][formData[`${this.state.currentTab - 1}`].activeTab].is_drag_drop) {
610
- formData[`${this.state.currentTab - 1}`][formData[`${this.state.currentTab - 1}`].activeTab][`template-content`] = _.cloneDeep(evt.editor.getData());
611
- if (formData[`${this.state.currentTab - 1}`].tabKey === formData.base.tabKey) {
612
- formData.base[formData[`${this.state.currentTab - 1}`].activeTab][`template-content`] = _.cloneDeep(evt.editor.getData());
598
+ // Only handle CKEditor changes
599
+ if (currentTabData && activeTab && !currentTabData[activeTab]?.is_drag_drop) {
600
+ const newContent = evt.editor.getData();
601
+
602
+ // Update formData with new content
603
+ currentTabData[activeTab]['template-content'] = _.cloneDeep(newContent);
604
+ if (currentTabData.tabKey === formData.base.tabKey) {
605
+ formData.base[activeTab]['template-content'] = _.cloneDeep(newContent);
613
606
  }
614
607
 
615
- this.setState({formData, content: evt.editor.getData()}, () => {
608
+ // Update state with new content
609
+ this.setState({
610
+ formData,
611
+ content: newContent // Keep latest content in state
612
+ }, () => {
616
613
  if (isFullMode && showTemplateName) {
617
614
  showTemplateName({formData, onFormDataChange: this.onFormDataChange});
618
615
  }
@@ -827,8 +824,11 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
827
824
  const currentTabData = formData[currentTab - 1];
828
825
  const activeTab = currentTabData?.activeTab;
829
826
 
827
+ // Get content from formData
830
828
  if (currentTabData && activeTab && currentTabData[activeTab]) {
831
- return currentTabData[activeTab]['template-content'] || '';
829
+ const isDragDrop = currentTabData[activeTab].is_drag_drop;
830
+ const templateContent = currentTabData[activeTab]['template-content'];
831
+ return templateContent || '';
832
832
  }
833
833
 
834
834
  return '';
@@ -2471,9 +2471,39 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2471
2471
  const formData = _.cloneDeep(this.state.formData);
2472
2472
  const activeLangTab = formData[`${this.state.currentTab - 1}`].activeTab;
2473
2473
  const langIndex = formData[`${this.state.currentTab - 1}`].selectedLanguages.indexOf(activeLangTab);
2474
+
2475
+ // Store current content before switching
2476
+ const currentContent = formData[`${this.state.currentTab - 1}`][activeLangTab]['template-content'];
2477
+
2478
+ // Update formData to mark as not drag-drop
2479
+ formData[`${this.state.currentTab - 1}`][activeLangTab].is_drag_drop = false;
2480
+ if (formData[`${this.state.currentTab - 1}`].base) {
2481
+ formData.base[activeLangTab].is_drag_drop = false;
2482
+ }
2483
+
2484
+ this.setState({
2485
+ mode: "switchEditor",
2486
+ formData
2487
+ }, () => {
2488
+ // No need to call handleEdmSave as we're switching to CKEditor
2489
+ let schema = _.cloneDeep(this.state.schema);
2490
+ _.forEach(schema.containers, (container, index) => {
2491
+ if (parseInt(index, 10) === (this.state.currentTab - 1)) {
2492
+ const temp = container;
2493
+ const langTab = formData[`${this.state.currentTab - 1}`].selectedLanguages.indexOf(activeLangTab);
2494
+
2495
+ temp.panes[langTab].sections[0].inputFields[0].cols[0].colStyle = {display: ""};
2496
+ temp.panes[langTab].sections[0].inputFields[0].cols[1].colStyle = {display: "none"};
2497
+ temp.tabBarExtraContent.sections[0].inputFields[0].cols[4].colStyle.display = "none";
2498
+ }
2499
+ });
2500
+ schema = this.showInsertImageButton(schema);
2474
2501
 
2475
- this.setState({mode: "switchEditor"}, () => {
2476
- this.handleEdmSave();
2502
+ this.setState({
2503
+ schema,
2504
+ showConfirmationModal: false,
2505
+ isSchemaChanged: true
2506
+ });
2477
2507
  });
2478
2508
  }
2479
2509
 
@@ -2668,22 +2698,26 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2668
2698
  this.beeInstance = instance;
2669
2699
  }
2670
2700
  saveBeeData = (json, html) => {
2701
+ // Update state with new content
2671
2702
  this.setState((prevState) => {
2672
2703
  const { currentTab, formData } = _.cloneDeep(prevState);
2673
2704
  const activeTab = formData[currentTab - 1].activeTab;
2705
+
2706
+ // Update content in both formData and base
2674
2707
  formData[currentTab - 1][activeTab]['template-content'] = html;
2675
2708
  formData[currentTab - 1][activeTab]['json-content'] = json;
2676
-
2677
2709
  formData.base[activeTab]['template-content'] = html;
2678
2710
  formData.base[activeTab]['json-content'] = json;
2679
2711
 
2680
2712
  return {
2681
2713
  ...prevState,
2682
2714
  formData,
2715
+ content: html // Keep latest content in state
2683
2716
  };
2684
2717
  }, () => {
2685
- !this.props.showTestAndPreviewSlidebox && this.setState({
2686
- startValidation: true,
2718
+ // Always trigger validation to ensure content is saved
2719
+ this.setState({
2720
+ startValidation: true
2687
2721
  });
2688
2722
  });
2689
2723
  }
@@ -2697,6 +2731,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2697
2731
  const imagePreviewHeader = (<h3>{this.props.intl.formatMessage(messages.h3imageSelection)}</h3>);
2698
2732
  const imagePreviewContent = this.getImagePreviewContent();
2699
2733
  const { selectedOfferDetails, getDefaultTags, Email: { CmsSettings = {} } = {}, moduleType = '', showTestAndPreviewSlidebox, handleTestAndPreview, handleCloseTestAndPreview } = this.props;
2734
+ const testAndPreviewContent = this.getTemplateContent();
2700
2735
  if (!this.props.currentOrgDetails || !this.props.currentOrgDetails.basic_details) {
2701
2736
  return (<div>Loading...</div>);
2702
2737
  }
@@ -2796,7 +2831,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2796
2831
  onClose={handleCloseTestAndPreview}
2797
2832
  formData={this.state.formData}
2798
2833
  beeInstance={this.beeInstance}
2799
- content={this.getTemplateContent()}
2834
+ content={testAndPreviewContent}
2800
2835
  currentChannel={EMAIL}
2801
2836
  />
2802
2837
  </div>
Binary file
@@ -1,44 +0,0 @@
1
- /**
2
- * Utility functions for handling WhatsApp media URL transformations
3
- */
4
-
5
- /**
6
- * Extracts creative ID from media URL pattern
7
- * @param {string} mediaUrl - URL with pattern {{media_content(<creativeId>_<mediaDetailId>)}}
8
- * @returns {string|null} - Extracted creative ID or null if not found
9
- */
10
- export const extractCreativeIdFromMediaUrl = (mediaUrl) => {
11
- if (!mediaUrl || typeof mediaUrl !== 'string') {
12
- return null;
13
- }
14
-
15
- // Extract creativeId from pattern: {{media_content(<creativeId>_<mediaDetailId>)}}
16
- const match = mediaUrl.match(/\{\{media_content\(([^_]+)_[^)]+\)\}\}/);
17
- return match ? match[1] : null;
18
- };
19
-
20
- /**
21
- * Creates URL mapping from API response format to template pattern format
22
- * @param {Object} mediaDetails - API response with format {url: "mediaDetailId_creativeId"}
23
- * @returns {Object} - Mapping object {creativeId_mediaDetailId: url}
24
- */
25
- export const createMediaUrlMapping = (mediaDetails) => {
26
- const mediaUrlMap = {};
27
-
28
- if (!mediaDetails || typeof mediaDetails !== 'object') {
29
- return mediaUrlMap;
30
- }
31
-
32
- Object.keys(mediaDetails).forEach(url => {
33
- const value = mediaDetails[url];
34
- // API response format: "mediaDetailId_creativeId"
35
- // We need to reverse it to match our pattern: "creativeId_mediaDetailId"
36
- if (typeof value === 'string' && value.includes('_')) {
37
- const [mediaDetailId, creativeId] = value.split('_');
38
- const templatePattern = `${mediaDetailId}_${creativeId}`;
39
- mediaUrlMap[templatePattern] = url;
40
- }
41
- });
42
-
43
- return mediaUrlMap;
44
- };
@@ -1,35 +0,0 @@
1
- import React from "react";
2
- import { injectIntl } from 'react-intl';
3
- import '@testing-library/jest-dom';
4
- import {
5
- render,
6
- } from '../../../utils/test-utils';
7
- import { EMAILPreviewMockData } from '../mockdata/mockdata';
8
- import { Email } from "../index";
9
-
10
- const initializeComponent = () => {
11
- const Component = injectIntl(Email);
12
- const resetUploadData = jest.fn();
13
- const clearStoreValues = jest.fn();
14
- const clearCRUDResponse = jest.fn();
15
- const fetchSchemaForEntity = jest.fn();
16
-
17
- return render( <Component
18
- {...EMAILPreviewMockData}
19
- templatesActions={{resetUploadData}}
20
- actions={{
21
- clearStoreValues,
22
- clearCRUDResponse,
23
- }}
24
- globalActions={{
25
- fetchSchemaForEntity,
26
- }}
27
- />);
28
- };
29
-
30
- describe('renders a message', () => {
31
- it("Test if Email component renders", () => {
32
- initializeComponent();
33
- });
34
- });
35
-