@capillarytech/creatives-library 8.0.343 → 8.0.345-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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.343",
4
+ "version": "8.0.345-0",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -214,11 +214,19 @@ const WhatsAppPreviewContent = ({
214
214
 
215
215
  {/* Image Preview */}
216
216
  {content?.whatsappImageSrc && (
217
- <CapImage
218
- src={content.whatsappImageSrc}
219
- className="whatsapp-image"
220
- alt={formatMessage(messages.previewGenerated)}
221
- />
217
+ /\{\{.*?\}\}/.test(content.whatsappImageSrc) ? (
218
+ <CapRow className="whatsapp-image-url-placeholder">
219
+ <CapIcon type="picture" size="s" className="whatsapp-image-url-placeholder-icon" />
220
+ <span className="whatsapp-image-url-placeholder-text">{content.whatsappImageSrc}</span>
221
+ </CapRow>
222
+ ) : (
223
+ <img
224
+ src={content.whatsappImageSrc}
225
+ className="whatsapp-image"
226
+ alt={formatMessage(messages.previewGenerated)}
227
+ onError={(e) => { e.target.src = whatsappImageEmptyPreview; }}
228
+ />
229
+ )
222
230
  )}
223
231
 
224
232
  {/* Video Preview */}
@@ -940,6 +940,33 @@
940
940
  }
941
941
  }
942
942
 
943
+ .whatsapp-image-url-placeholder {
944
+ display: flex;
945
+ flex-direction: column;
946
+ align-items: center;
947
+ justify-content: center;
948
+ gap: $CAP_SPACE_08;
949
+ width: 100%;
950
+ min-height: 5rem;
951
+ max-height: 8.786rem;
952
+ margin-bottom: $CAP_SPACE_04;
953
+ background-color: #f5f5f5;
954
+ border-radius: 4px;
955
+ padding: $CAP_SPACE_08;
956
+
957
+ .whatsapp-image-url-placeholder-icon {
958
+ color: #8c8c8c;
959
+ font-size: 1.5rem;
960
+ }
961
+
962
+ .whatsapp-image-url-placeholder-text {
963
+ font-size: $FONT_SIZE_S;
964
+ color: #595959;
965
+ text-align: center;
966
+ word-break: break-all;
967
+ }
968
+ }
969
+
943
970
  .whatsapp-image {
944
971
  width: 100%;
945
972
  max-height: 8.786rem;
@@ -816,6 +816,27 @@
816
816
  }
817
817
  }
818
818
 
819
+ .whatsapp-image-url-placeholder {
820
+ display: flex;
821
+ flex-direction: column;
822
+ align-items: center;
823
+ justify-content: center;
824
+ background-color: #f5f5f5;
825
+ border-radius: 4px;
826
+ width: 100%;
827
+ min-height: 5rem;
828
+ max-height: 8.786rem;
829
+ margin-bottom: 4px;
830
+ .whatsapp-image-url-placeholder-text {
831
+ font-size: 11px;
832
+ color: #666;
833
+ margin-top: 4px;
834
+ text-align: center;
835
+ word-break: break-all;
836
+ padding: 0 4px;
837
+ }
838
+ }
839
+
819
840
  .msg-container-carousel {
820
841
  display: flex;
821
842
  flex-direction: column;
@@ -1194,11 +1194,19 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
1194
1194
  {content?.showUrlPreview
1195
1195
  && renderUrlPreview(content?.metaTagsDetails)}
1196
1196
  {content?.whatsappImageSrc && (
1197
- <CapImage
1198
- src={content.whatsappImageSrc}
1199
- className="whatsapp-image"
1200
- alt={formatMessage(messages.previewGenerated)}
1201
- />
1197
+ /\{\{.*?\}\}/.test(content.whatsappImageSrc) ? (
1198
+ <div className="whatsapp-image-url-placeholder">
1199
+ <CapIcon type="picture" size="s" className="whatsapp-image-url-placeholder-icon" />
1200
+ <span className="whatsapp-image-url-placeholder-text">{content.whatsappImageSrc}</span>
1201
+ </div>
1202
+ ) : (
1203
+ <img
1204
+ src={content.whatsappImageSrc}
1205
+ className="whatsapp-image"
1206
+ alt={formatMessage(messages.previewGenerated)}
1207
+ onError={(e) => { e.target.src = whatsappImageEmptyPreview; }}
1208
+ />
1209
+ )
1202
1210
  )}
1203
1211
  {content?.whatsappVideoPreviewImg && (
1204
1212
  <CapTooltip
@@ -684,6 +684,8 @@ export class Creatives extends React.Component {
684
684
  switch (mediaType) {
685
685
  case (WHATSAPP_MEDIA_TYPES.IMAGE):
686
686
  mediaParams.imageUrl = url;
687
+ // Detect isAddImageUrl from previewUrl marker (previewUrl is unused for IMAGE type)
688
+ mediaParams.isAddImageUrl = previewUrl === '__add_image_url__';
687
689
  break;
688
690
  case (WHATSAPP_MEDIA_TYPES.VIDEO):
689
691
  mediaParams.videoUrl = url;
@@ -1142,6 +1144,7 @@ export class Creatives extends React.Component {
1142
1144
  headerTemplate = '',
1143
1145
  } = {},
1144
1146
  isPreviewUrl = false,
1147
+ isAddImageUrl = false,
1145
1148
  carouselData = [],
1146
1149
  } = cloneDeep(versions.base.content.whatsapp);
1147
1150
 
@@ -1178,6 +1181,11 @@ export class Creatives extends React.Component {
1178
1181
  switch (mediaType) {
1179
1182
  case (WHATSAPP_MEDIA_TYPES.IMAGE):
1180
1183
  whatsappMedia.url = imageUrl;
1184
+ // previewUrl is unused for IMAGE type — reuse it to persist isAddImageUrl flag
1185
+ // without adding any new field that the backend would reject
1186
+ if (isAddImageUrl) {
1187
+ whatsappMedia.previewUrl = '__add_image_url__';
1188
+ }
1181
1189
  break;
1182
1190
  case (WHATSAPP_MEDIA_TYPES.VIDEO):
1183
1191
  whatsappMedia.url = videoUrl;
@@ -252,12 +252,15 @@ export const Whatsapp = (props) => {
252
252
  const [headerTextAreaId, setHeaderTextAreaId] = useState('');
253
253
  const [isHeaderTagValidationError, updateIsHeaderTagValidationError] =
254
254
  useState(false);
255
+ const [imageURLCursorPosition, setImageURLCursorPosition] = useState(0);
255
256
  const [showUrlPreview, setShowUrlPreview] = useState(false);
256
257
  const [previewUrl, setPreviewUrl] = useState('');
257
258
  const [carouselMediaType, setCarouselMediaType] = useState('image');
258
259
  const [carouselData, setCarouselData] = useState(CAROUSEL_INITIAL_DATA);
259
260
  const [activeIndex, setActiveIndex] = useState(defaultActiveIndex);
260
261
  const [carouselValidateTag, setCarouselValidateTag] = useState(false);
262
+ const [mediaTypeSelection, setMediaTypeSelection] = useState('Upload image');
263
+ const [imageURL, setImageURL] = useState('');
261
264
  // TestAndPreviewSlidebox state
262
265
  const [showTestAndPreviewSlidebox, setShowTestAndPreviewSlidebox] = useState(false);
263
266
  const [isTestAndPreviewMode, setIsTestAndPreviewMode] = useState(false);
@@ -364,6 +367,7 @@ export const Whatsapp = (props) => {
364
367
  whatsappMedia: { header = '', footer = '', headerVarMapped = [] } = {},
365
368
  isPreviewUrl = false,
366
369
  carouselData : editCarouselData = [],
370
+ isAddImageUrl = false,
367
371
  } = editContent;
368
372
  setTemplateCategory(category);
369
373
  setTemplateStatus(status);
@@ -378,6 +382,14 @@ export const Whatsapp = (props) => {
378
382
  });
379
383
  setWhatsappDocSource(documentUrl);
380
384
  setWhatsappDocParams(whatsappDocParams);
385
+ if (mediaType === WHATSAPP_MEDIA_TYPES.IMAGE) {
386
+ if (isAddImageUrl) {
387
+ setMediaTypeSelection('Add image URL');
388
+ setImageURL(imageUrl);
389
+ } else {
390
+ setMediaTypeSelection('Upload image');
391
+ }
392
+ }
381
393
  if (host === HOST_HAPTIC) {
382
394
  updateAssetList(transformToVendorFormat({
383
395
  ...assetList,
@@ -800,6 +812,14 @@ export const Whatsapp = (props) => {
800
812
  });
801
813
  }
802
814
 
815
+ const onImageURLTagSelect = (data) => {
816
+ const currentURL = imageURL || '';
817
+ const tag = `{{${data}}}`;
818
+ const newURL = currentURL.slice(0, imageURLCursorPosition) + tag + currentURL.slice(imageURLCursorPosition);
819
+ setImageURL(newURL);
820
+ setWhatsappImageSrc(newURL);
821
+ };
822
+
803
823
  //setting the id of currently selected text area, is used onTagSelect
804
824
  const setTextAreaId = ({ target: { id } }, type, carouselIndex) => {
805
825
  if (type === HEADER_TEXT) {
@@ -1151,6 +1171,9 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
1151
1171
  );
1152
1172
  } else {
1153
1173
  setWhatsappImageSrc(filePath);
1174
+ if (mediaTypeSelection !== 'Add image URL') {
1175
+ setMediaTypeSelection('Upload image');
1176
+ }
1154
1177
  if (isHaptic) {
1155
1178
  setHapticFileHandle(fileHandle);
1156
1179
  } else {
@@ -1159,7 +1182,7 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
1159
1182
  }
1160
1183
  actions.clearWhatsappAsset(0);
1161
1184
  },
1162
- [whatsappImageSrc, isMediaTypeCarousel, activeIndex, carouselData, isHaptic],
1185
+ [whatsappImageSrc, isMediaTypeCarousel, activeIndex, carouselData, isHaptic, mediaTypeSelection],
1163
1186
  );
1164
1187
 
1165
1188
  const updateOnWhatsappImageReUpload = useCallback(() => {
@@ -1626,8 +1649,10 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
1626
1649
  const { whatsappVideoSrc = '', whatsappVideoPreviewImg = '' } = whatsappVideoSrcAndPreview;
1627
1650
  switch (templateMediaType) {
1628
1651
  case WHATSAPP_MEDIA_TYPES.IMAGE:
1652
+ const isImageUrlMode = mediaTypeSelection === 'Add image URL';
1629
1653
  mediaParams = {
1630
1654
  imageUrl: getCdnUrl({ url: whatsappImageSrc, channelName: WHATSAPP }),
1655
+ ...(isImageUrlMode && { isAddImageUrl: true }),
1631
1656
  };
1632
1657
  break;
1633
1658
  case WHATSAPP_MEDIA_TYPES.VIDEO:
@@ -1646,7 +1671,6 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
1646
1671
  break;
1647
1672
  }
1648
1673
 
1649
- // Add the correct file handle based on host
1650
1674
  if (isHaptic && hapticFileHandle) {
1651
1675
  mediaParams.hapticFileHandle = hapticFileHandle;
1652
1676
  } else if (!isHaptic && karixFileHandle) {
@@ -1758,7 +1782,22 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
1758
1782
  type: WHATSAPP,
1759
1783
  });
1760
1784
  };
1761
- const isMediatypeValid =()=> ((isMediaTypeImage && whatsappImageSrc === "") || (isMediaTypeVideo && whatsappVideoSrcAndPreview?.whatsappVideoSrc === "") || (isMediaTypeDoc && whatsappDocSource === ""));
1785
+ const isMediatypeValid =()=> {
1786
+ if (isMediaTypeImage) {
1787
+ // In Add image URL mode: creation needs a demo upload; edit flow shows stored URL
1788
+ if (mediaTypeSelection === 'Add image URL') {
1789
+ return isEditFlow ? imageURL === "" : whatsappImageSrc === "";
1790
+ }
1791
+ return whatsappImageSrc === "";
1792
+ }
1793
+ if (isMediaTypeVideo) {
1794
+ return whatsappVideoSrcAndPreview?.whatsappVideoSrc === "";
1795
+ }
1796
+ if (isMediaTypeDoc) {
1797
+ return whatsappDocSource === "";
1798
+ }
1799
+ return false;
1800
+ };
1762
1801
 
1763
1802
  const isDisableDone = () => {
1764
1803
  // Snapshot removed
@@ -1835,9 +1874,106 @@ const isAuthenticationTemplate = isEqual(templateCategory, WHATSAPP_CATEGORIES.a
1835
1874
  return false;
1836
1875
  };
1837
1876
 
1877
+ const renderImageURLComponent = () => (
1878
+ <>
1879
+ {templateStatus === WHATSAPP_STATUSES.approved && !isAuthenticationTemplate && (
1880
+ <CapRow className="whatsapp-render-heading">
1881
+ <CapHeader
1882
+ title={
1883
+ <CapHeading type="h4">
1884
+ {formatMessage(messages.mediaImage)}
1885
+ </CapHeading>
1886
+ }
1887
+ suffix={
1888
+ <TagList
1889
+ label={formatMessage(globalMessages.addLabels)}
1890
+ onTagSelect={onImageURLTagSelect}
1891
+ location={location}
1892
+ tags={tags || []}
1893
+ onContextChange={handleOnTagsContextChange}
1894
+ injectedTags={injectedTags || {}}
1895
+ selectedOfferDetails={selectedOfferDetails}
1896
+ eventContextTags={eventContextTags}
1897
+ />
1898
+ }
1899
+ />
1900
+ </CapRow>
1901
+ )}
1902
+ <CapRow className="whatsapp-image-url-container">
1903
+ <CapInput
1904
+ id="whatsapp-image-url-input"
1905
+ placeholder="Enter image URL"
1906
+ value={imageURL}
1907
+ disabled={isEditFlow && isFullMode}
1908
+ onChange={e => {
1909
+ setImageURL(e.target.value);
1910
+ setWhatsappImageSrc(e.target.value);
1911
+ }}
1912
+ onFocus={(e) => {
1913
+ setImageURLCursorPosition(e.target.selectionStart || e.target.value.length);
1914
+ }}
1915
+ onSelect={(e) => {
1916
+ setImageURLCursorPosition(e.target.selectionStart || e.target.value.length);
1917
+ }}
1918
+ />
1919
+ </CapRow>
1920
+ </>
1921
+ );
1922
+
1923
+ const handleMediaChange = (e) => {
1924
+ if (templateStatus === WHATSAPP_STATUSES.approved || isEditFlow) {
1925
+ return;
1926
+ }
1927
+ setMediaTypeSelection(e.target.value);
1928
+ if (e.target.value === 'Upload image') {
1929
+ setImageURL('');
1930
+ } else if (e.target.value === 'Add image URL') {
1931
+ setWhatsappImageSrc('');
1932
+ if (isHaptic) {
1933
+ setHapticFileHandle('');
1934
+ } else {
1935
+ setKarixFileHandle('');
1936
+ }
1937
+ }
1938
+ };
1939
+
1838
1940
  const renderMediaComponent = () => (
1839
1941
  <>
1840
- {isMediaTypeImage && renderImageComponent()}
1942
+ {isMediaTypeImage && (
1943
+ <CapRow className="whatsapp-dynamic-url-selection-container">
1944
+ <CapHeading type="h4" className="whatsapp-dynamic-url-selection-heading">
1945
+ {formatMessage(messages.mediaImage)}
1946
+ </CapHeading>
1947
+ <CapRadioGroup
1948
+ options={[
1949
+ {
1950
+ value: 'Upload image',
1951
+ label: 'Upload image',
1952
+ },
1953
+ {
1954
+ value: 'Add image URL',
1955
+ label: (
1956
+ <span>
1957
+ Add image URL{' '}
1958
+ <CapTooltip
1959
+ placement="top"
1960
+ title="You can upload a placeholder image and change it after approval. The link can include a personalized image URL."
1961
+ >
1962
+ <CapIcon type="info-circle" size="s" style={{ cursor: 'pointer' }} />
1963
+ </CapTooltip>
1964
+ </span>
1965
+ ),
1966
+ },
1967
+ ]}
1968
+ value={mediaTypeSelection}
1969
+ onChange={handleMediaChange}
1970
+ className="whatsapp-media-radio"
1971
+ disabled={templateStatus === WHATSAPP_STATUSES.approved || isEditFlow}
1972
+ />
1973
+ </CapRow>
1974
+ )}
1975
+ {isMediaTypeImage && (mediaTypeSelection === 'Upload image' || !isEditFlow) && renderImageComponent()}
1976
+ {isMediaTypeImage && mediaTypeSelection === 'Add image URL' && isEditFlow && renderImageURLComponent()}
1841
1977
  {isMediaTypeVideo && renderVideoComonent()}
1842
1978
  {isMediaTypeDoc && renderDocumentComponent()}
1843
1979
  </>
@@ -9,6 +9,14 @@
9
9
  margin-top: $CAP_SPACE_16;
10
10
  }
11
11
 
12
+ .whatsapp-dynamic-url-selection-container {
13
+ margin-bottom: $CAP_SPACE_08;
14
+ margin-top: $CAP_SPACE_32;
15
+ .whatsapp-dynamic-url-selection-heading {
16
+ margin-bottom: $CAP_SPACE_08;
17
+ }
18
+ }
19
+
12
20
  .whatsapp-render-heading {
13
21
  margin-top: $CAP_SPACE_16;
14
22
  margin-bottom: $CAP_SPACE_06;