@capillarytech/creatives-library 8.0.239 → 8.0.240

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
@@ -21,6 +21,7 @@ const config = {
21
21
  },
22
22
  development: {
23
23
  api_endpoint: 'https://crm-nightly-new.cc.capillarytech.com/arya/api/v1/creatives',
24
+ // api_endpoint: 'http://localhost:2022/arya/api/v1/creatives',
24
25
  campaigns_api_endpoint: 'https://crm-nightly-new.cc.capillarytech.com/iris/v2/campaigns',
25
26
  campaigns_api_org_endpoint: 'https://crm-nightly-new.cc.capillarytech.com/iris/v2/org/campaign',
26
27
  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.239",
4
+ "version": "8.0.240",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
package/services/api.js CHANGED
@@ -264,6 +264,7 @@ export const getUserData = () => {
264
264
 
265
265
  export const createTemplate = ({template}) => {
266
266
  const url = `${API_ENDPOINT}/templates/SMS`;
267
+ console.log("creating template",template);
267
268
  return request(url, getAPICallObject('POST', template));
268
269
  };
269
270
 
@@ -346,6 +347,7 @@ export const getAllTemplates = async ({channel, queryParams = {}}) => {
346
347
 
347
348
  export const deleteTemplate = ({channel, id}) => {
348
349
  const url = `${API_ENDPOINT}/templates/${id}/${channel}`;
350
+ console.log("deleting template", url);
349
351
  return request(url, getAPICallObject('DELETE'));
350
352
  //return API.deleteResource(url);
351
353
  };
@@ -675,7 +675,7 @@ export class Creatives extends React.Component {
675
675
  } = templateData || {};
676
676
  const cardContent = (rcsContent.cardContent && rcsContent.cardContent[0]) || {};
677
677
  const Status = RCS_STATUSES.approved || '';
678
-
678
+ console.log("onedit data--------->",templateData);
679
679
  creativesTemplateData = {
680
680
  type: channel,
681
681
  edit: true,
@@ -3,7 +3,7 @@ import React, { useState, useEffect, useCallback } from 'react';
3
3
  import { bindActionCreators } from 'redux';
4
4
  import { createStructuredSelector } from 'reselect';
5
5
  import { injectIntl, FormattedMessage } from 'react-intl';
6
- import { get, isEmpty, cloneDeep } from 'lodash';
6
+ import { get, isEmpty, cloneDeep, isNil } from 'lodash';
7
7
  import styled from 'styled-components';
8
8
  import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
9
9
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
@@ -282,9 +282,22 @@ export const Rcs = (props) => {
282
282
  if (type === MESSAGE_TEXT) setTemplateDescError(false);
283
283
  return;
284
284
  }
285
+
286
+ let contentForValidation = resolved;
287
+ const placeholderTokens = templateStr.match(rcsVarRegex) || [];
288
+ placeholderTokens.forEach((t) => {
289
+ const escaped = t.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
290
+ contentForValidation = contentForValidation.replace(new RegExp(escaped, 'g'), '');
291
+ });
292
+ if (!contentForValidation.trim()) {
293
+ if (type === TITLE_TEXT) setTemplateTitleError(false);
294
+ if (type === MESSAGE_TEXT) setTemplateDescError(false);
295
+ return;
296
+ }
297
+
285
298
  const validationResponse =
286
299
  validateTags({
287
- content: resolved,
300
+ content: contentForValidation,
288
301
  tagsParam: tags,
289
302
  injectedTagsParams: injectedTags,
290
303
  location,
@@ -322,7 +335,8 @@ export const Rcs = (props) => {
322
335
  if (rcsVarTestRegex.test(elem)) {
323
336
  const key = getVarNameFromToken(elem);
324
337
  const v = cardVarMapped?.[key];
325
- return (v ?? '').toString();
338
+ if (isNil(v) || String(v)?.trim?.() === '') return elem;
339
+ return String(v);
326
340
  }
327
341
  return elem;
328
342
  }).join('');
@@ -330,7 +344,7 @@ export const Rcs = (props) => {
330
344
 
331
345
 
332
346
  useEffect(() => {
333
- if (isFullMode) return;
347
+ if (isFullMode || isEditFlow) return;
334
348
  const tokens = [
335
349
  ...(templateTitle ? (templateTitle.match(rcsVarRegex) || []) : []),
336
350
  ...(templateDesc ? (templateDesc.match(rcsVarRegex) || []) : []),
@@ -1729,8 +1743,7 @@ const splitTemplateVarString = (str) => {
1729
1743
  const currentDimension =selectedDimension || Object.keys(RCS_VIDEO_THUMBNAIL_DIMENSIONS)[0];
1730
1744
  return (
1731
1745
  <>
1732
- {renderLabel('cardOrientationLabel')}
1733
- {/* Match image behavior: allow changing dimensions only in full-mode create flow */}
1746
+ {isFullMode && renderLabel('cardOrientationLabel')}
1734
1747
  {!isEditFlow && isFullMode && (
1735
1748
  <CapSelect
1736
1749
  id="rcs-dimension-select"
@@ -1829,11 +1842,27 @@ const splitTemplateVarString = (str) => {
1829
1842
  if (!str) return '';
1830
1843
  if (!mapping || Object.keys(mapping).length === 0) return str;
1831
1844
  let result = str;
1832
- Object.keys(mapping).forEach((key) => {
1833
- const value = mapping[key];
1834
- if (!value) return;
1835
- const bracedValue = /^\{\{[\s\S]*\}\}$/.test(value) ? value : `{{${value}}}`;
1836
- const escaped = bracedValue.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
1845
+ const replacements = [];
1846
+ Object.entries(mapping).forEach(([key, value]) => {
1847
+ const raw = (value ?? '').toString();
1848
+ if (!raw || raw?.trim?.() === '') return;
1849
+ const braced = /^\{\{[\s\S]*\}\}$/.test(raw) ? raw : `{{${raw}}}`;
1850
+ replacements.push({ key, needle: raw });
1851
+ if (braced !== raw) replacements.push({ key, needle: braced });
1852
+ });
1853
+ const seen = new Set();
1854
+ const uniq = replacements
1855
+ .filter(({ key, needle }) => {
1856
+ const id = `${key}::${needle}`;
1857
+ if (seen.has(id)) return false;
1858
+ seen.add(id);
1859
+ return true;
1860
+ })
1861
+ .sort((a, b) => (b.needle.length - a.needle.length));
1862
+
1863
+ uniq.forEach(({ key, needle }) => {
1864
+ if (!needle) return;
1865
+ const escaped = needle.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
1837
1866
  const regex = new RegExp(escaped, 'g');
1838
1867
  result = result.replace(regex, `{{${key}}}`);
1839
1868
  });
@@ -1879,8 +1908,24 @@ const splitTemplateVarString = (str) => {
1879
1908
  ? RCS_IMAGE_DIMENSIONS[selectedDimension]?.heightType || MEDIUM
1880
1909
  : RCS_VIDEO_THUMBNAIL_DIMENSIONS[selectedDimension]?.heightType || MEDIUM,
1881
1910
  }}),
1882
- // ...(!isFullMode && {Status:RCS_STATUSES.approved}), //since, we anyways allow only approved templates to be sent, sending approved by default for edit functionality
1883
- ...(!isFullMode && { cardVarMapped }),
1911
+ ...(!isFullMode && (() => {
1912
+ const tokens = [
1913
+ ...(templateTitle ? (templateTitle.match(rcsVarRegex) || []) : []),
1914
+ ...(templateDesc ? (templateDesc.match(rcsVarRegex) || []) : []),
1915
+ ];
1916
+ const allowedKeys = tokens
1917
+ .map((t) => getVarNameFromToken(t))
1918
+ .filter(Boolean);
1919
+ const nextMap = {};
1920
+ allowedKeys.forEach((k) => {
1921
+ if (Object.prototype.hasOwnProperty.call(cardVarMapped || {}, k)) {
1922
+ nextMap[k] = cardVarMapped[k];
1923
+ } else {
1924
+ nextMap[k] = '';
1925
+ }
1926
+ });
1927
+ return { cardVarMapped: nextMap };
1928
+ })()),
1884
1929
  ...(suggestions.length > 0 && { suggestions }),
1885
1930
  }
1886
1931
  ],
@@ -968,6 +968,87 @@ describe('RCS createPayload', () => {
968
968
  expect(updatedTitleVarAreas.at(0).prop('value')).toBe('{{first_name}}');
969
969
  expect(updatedTitleVarAreas.at(1).prop('value')).toBe('{{first_name}}');
970
970
  });
971
+
972
+ it('should keep two tags + freetext inside the variable textarea in non-full mode edit (not merged into static text)', () => {
973
+ const templateData = {
974
+ name: 'TwoTagsAndFreeText',
975
+ versions: {
976
+ base: {
977
+ content: {
978
+ RCS: {
979
+ rcsContent: {
980
+ cardType: 'STANDALONE',
981
+ cardSettings: { cardOrientation: 'VERTICAL', cardWidth: 'SMALL' },
982
+ cardContent: [
983
+ {
984
+ // NOTE: In campaigns/edit flows, title/description can be stored "resolved" (values applied).
985
+ // We expect RCS to "unmap" this back to placeholders and show values in variable textareas.
986
+ title: 'Update for {{user_id_b64}} regarding {{first_name}}{{adv}}freeText',
987
+ description: 'Hi {{user_id_b64}}, your {{first_name}}{{adv}}freeText is ready.',
988
+ mediaType: 'NONE',
989
+ cardVarMapped: {
990
+ user_name: '{{user_id_b64}}',
991
+ service_type: '{{first_name}}{{adv}}freeText',
992
+ },
993
+ suggestions: [],
994
+ },
995
+ ],
996
+ contentType: 'RICHCARD',
997
+ },
998
+ },
999
+ },
1000
+ },
1001
+ },
1002
+ type: 'RCS',
1003
+ };
1004
+
1005
+ const wrapper = mountWithIntl(
1006
+ <Provider store={store}>
1007
+ <Rcs
1008
+ actions={{
1009
+ clearCreateResponse: jest.fn(),
1010
+ getTemplateDetails: jest.fn(),
1011
+ uploadRcsAsset: jest.fn(),
1012
+ clearRcsMediaAsset: jest.fn(),
1013
+ editTemplate: jest.fn(),
1014
+ clearEditResponse: jest.fn(),
1015
+ }}
1016
+ globalActions={{ fetchSchemaForEntity }}
1017
+ onCreateComplete={onCreateComplete}
1018
+ handleClose={handleClose}
1019
+ intl={{ formatMessage }}
1020
+ location={{ pathname: '/rcs/edit', query: { type: false, module: 'default' }, search: '' }}
1021
+ params={params}
1022
+ templateData={templateData}
1023
+ rcsData={{}}
1024
+ isFullMode={false}
1025
+ isEditFlow={false}
1026
+ loadingTags={false}
1027
+ metaEntities={[]}
1028
+ isDltEnabled={false}
1029
+ smsRegister={'DLT'}
1030
+ getFormData={jest.fn()}
1031
+ />
1032
+ </Provider>,
1033
+ );
1034
+
1035
+ wrapper.update();
1036
+
1037
+ // The placeholder {{service_type}} should exist as a variable textarea id, and its value should be the mixed string.
1038
+ const serviceTypeAreas = wrapper.find('TextArea').filterWhere((n) => {
1039
+ const id = n.prop('id') || '';
1040
+ return id.includes('{{service_type}}_');
1041
+ });
1042
+ expect(serviceTypeAreas.length).toBeGreaterThanOrEqual(1);
1043
+ expect(serviceTypeAreas.at(0).prop('value')).toBe('{{first_name}}{{adv}}freeText');
1044
+
1045
+ // Ensure freetext does NOT leak into static text blocks.
1046
+ const staticAreas = wrapper.find('TextArea').filterWhere((n) => !!n.prop('disabled'));
1047
+ const staticValues = staticAreas.map((n) => String(n.prop('value') || ''));
1048
+ staticValues.forEach((v) => {
1049
+ expect(v.includes('freeText')).toBe(false);
1050
+ });
1051
+ });
971
1052
  });
972
1053
 
973
1054
  describe('Character Counting Functions', () => {
@@ -1146,6 +1146,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1146
1146
  }
1147
1147
 
1148
1148
  filterRcsTemplates = (templates) => {
1149
+ console.log('templates', templates);
1149
1150
  let { selectedRcsStatus } = this.state;
1150
1151
  selectedRcsStatus = !this.props.isFullMode ? RCS_STATUSES.approved : '';
1151
1152
  if (selectedRcsStatus) {
@@ -290,6 +290,7 @@ export const Whatsapp = (props) => {
290
290
  //gets account details
291
291
  useEffect(() => {
292
292
  const accountObj = accountData.selectedWhatsappAccount || {};
293
+ console.log('accountObj', accountObj);
293
294
  if (!isEmpty(accountObj)) {
294
295
  const {
295
296
  sourceAccountIdentifier = '',