@capillarytech/creatives-library 9.0.11-beta.1 → 9.0.12

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.11-beta.1",
4
+ "version": "9.0.12",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -16,6 +16,7 @@ import { isTraiDLTEnable } from '../../utils/common';
16
16
  import { formatString } from '../../utils/Formatter';
17
17
  import {
18
18
  CAP_SPACE_12,
19
+ ICON_SIZE_M,
19
20
  } from '@capillarytech/cap-ui-library/styled/variables';
20
21
  import { WHATSAPP_HELP_DOC_LINK, JOURNEY } from './constants';
21
22
 
@@ -23,8 +24,14 @@ const { CapLabelInline } = CapLabel;
23
24
  const StyledLabel = styled(CapLabelInline)`
24
25
  margin-right: ${CAP_SPACE_12};
25
26
  `;
27
+ // CapIcons.BackIcon renders an <svg width="1em" height="1em">, so its size follows
28
+ // the inherited font-size. Pin it to the medium icon size (24px) and center it so
29
+ // the back arrow matches the header title instead of collapsing to a tiny glyph.
26
30
  const PrefixWrapper = styled.div`
27
31
  margin-right: 16px;
32
+ display: inline-flex;
33
+ align-items: center;
34
+ font-size: ${ICON_SIZE_M};
28
35
  `;
29
36
  const renderData = (type, value, channel) => (
30
37
  <StyledLabel className={channel?.toLowerCase() === ZALO ? 'zalo-template-name-spacing' : ''} type={type}>
@@ -39,6 +39,7 @@ export class Ebill extends React.Component { // eslint-disable-line react/prefer
39
39
  currentTab: 1,
40
40
  isEdit: false,
41
41
  loading: false,
42
+ isSaving: false,
42
43
  isFormValid: true,
43
44
  injectedTags: {},
44
45
  checkValidation: false,
@@ -570,26 +571,33 @@ export class Ebill extends React.Component { // eslint-disable-line react/prefer
570
571
  },
571
572
  };
572
573
  this.isTagLoaded = false;
574
+ this.isSubmitting = false;
573
575
  }
574
576
 
575
577
  componentWillMount() {
578
+ // NOTE: API dispatches must NOT live here. componentWillMount runs in React's
579
+ // render phase, which React 18 (createRoot/concurrent) may execute more than
580
+ // once per commit — causing every dispatched request to fire twice. All
581
+ // side-effects are in componentDidMount (commit phase, runs exactly once).
576
582
  if (this.props.params.id) {
577
- this.props.actions.getTemplateDetails(this.props.params.id, 'ebill');
578
583
  this.setState({isEdit: true});
579
584
  }
585
+ }
586
+
587
+ componentDidMount() {
588
+ if (this.props.params.id) {
589
+ this.props.actions.getTemplateDetails(this.props.params.id, 'ebill');
590
+ }
580
591
 
581
592
  if (!_.isEmpty(this.props.Templates.edmTemplate) && this.props.Templates.edmTemplate.versions.base.is_drag_drop) {
582
593
  this.props.actions.getCmsData('edm', this.props.Templates.edmTemplate.versions.base.drag_drop_id);
583
594
  }
584
- }
585
595
 
586
- componentDidMount() {
587
596
  const query = {
588
597
  layout: 'EBILL',
589
598
  type: 'LAYOUT',
590
599
  };
591
600
  this.props.globalActions.fetchSchemaForEntity(query);
592
-
593
601
  }
594
602
 
595
603
  componentWillReceiveProps(nextProps) {
@@ -609,7 +617,7 @@ export class Ebill extends React.Component { // eslint-disable-line react/prefer
609
617
  this.setEditState(nextProps.Ebill.templateDetails);
610
618
  }
611
619
 
612
- if (nextProps.Ebill.createResponse && nextProps.Ebill.createResponse.templateId) {
620
+ if (nextProps.Ebill.createResponse && nextProps.Ebill.createResponse.templateId && !_.isEqual(nextProps.Ebill.createResponse, this.props.Ebill.createResponse)) {
613
621
  let message;
614
622
  if (this.state.isEdit) {
615
623
  message = getMessageObject('success', this.props.intl.formatMessage(messages.ebillEditSuccess), true);
@@ -618,6 +626,8 @@ export class Ebill extends React.Component { // eslint-disable-line react/prefer
618
626
  }
619
627
  this.props.globalActions.addMessageToQueue(message);
620
628
  this.props.actions.clearCRUDResponse();
629
+ this.isSubmitting = false;
630
+ this.setState({ isSaving: false });
621
631
  const module = this.props.location.query.module ? this.props.location.query.module : 'default';
622
632
  const type = this.props.location.query.type;
623
633
  this.props.router.push({
@@ -629,6 +639,8 @@ export class Ebill extends React.Component { // eslint-disable-line react/prefer
629
639
  if (nextProps.Ebill.createTemplateError && !_.isEqual(nextProps.Ebill.createTemplateError, this.props.Ebill.createTemplateError)) {
630
640
  const message = getMessageObject('error', (nextProps.Ebill.createTemplateErrorMessage && nextProps.Ebill.createTemplateErrorMessage !== '') ? nextProps.Ebill.createTemplateErrorMessage : this.props.intl.formatMessage(messages.somethingWentWrong), true);
631
641
  this.props.globalActions.addMessageToQueue(message);
642
+ this.isSubmitting = false;
643
+ this.setState({ isSaving: false });
632
644
  }
633
645
 
634
646
 
@@ -773,11 +785,23 @@ export class Ebill extends React.Component { // eslint-disable-line react/prefer
773
785
  saveFormData = (formData) => {
774
786
  //Logic to save in db etc
775
787
  // const isValidated = this.validateFormData(formData);
776
- //
788
+ //
777
789
  // if (!isValidated) {
778
790
  // return;
779
791
  // }
780
-
792
+
793
+ // Guard against duplicate submissions. Uses a synchronous instance flag
794
+ // (NOT React state) because setState is async — two onSubmit invocations in
795
+ // the same tick would both read a stale `false` and each dispatch a
796
+ // createTemplate, firing POST /templates/EBILL twice. The instance flag
797
+ // flips synchronously, so the second call is dropped immediately.
798
+ // Reset once the create/update response or error is received in componentWillReceiveProps.
799
+ if (this.isSubmitting) {
800
+ return;
801
+ }
802
+ this.isSubmitting = true;
803
+ this.setState({ isSaving: true });
804
+
781
805
  const obj = {};
782
806
  obj.versions = {
783
807
  base: {},
@@ -378,8 +378,8 @@ const MediaUploaders = ({
378
378
  })));
379
379
  };
380
380
 
381
- const onTabChange = (index) => {
382
- setCarouselActiveTabIndex(index);
381
+ const onTabChange = (key) => {
382
+ setCarouselActiveTabIndex(Number(key));
383
383
  };
384
384
 
385
385
  const addCarouselCard = () => {
@@ -765,7 +765,7 @@ const MediaUploaders = ({
765
765
  const getTabPanes = () => {
766
766
  const currentData = getCurrentCarouselData();
767
767
  return currentData?.map((data, index) => ({
768
- key: index,
768
+ key: String(index),
769
769
  tab: index + 1,
770
770
  content: (
771
771
  <CapCard
@@ -2146,8 +2146,7 @@ describe('MediaUploaders', () => {
2146
2146
  const secondTabButton = screen.getByText('Tab 2');
2147
2147
  fireEvent.click(secondTabButton);
2148
2148
 
2149
- // Should call setCarouselActiveTabIndex with correct index
2150
- expect(setCarouselActiveTabIndex).toHaveBeenCalledWith('1');
2149
+ expect(setCarouselActiveTabIndex).toHaveBeenCalledWith(1);
2151
2150
  });
2152
2151
 
2153
2152
  it('should handle carousel card add operation', () => {
@@ -646,7 +646,7 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
646
646
  }
647
647
 
648
648
 
649
- if (selectedChannel === "ebill" && nextProps.CreateEbill && nextProps.CreateEbill.createResponse && nextProps.CreateEbill.createResponse.templateId && nextProps.CreateEbill.createResponse.templateId !== '') {
649
+ if (selectedChannel === "ebill" && nextProps.CreateEbill && nextProps.CreateEbill.createResponse && nextProps.CreateEbill.createResponse.templateId && nextProps.CreateEbill.createResponse.templateId !== '' && !isEqual(nextProps.CreateEbill.createResponse, this.props.CreateEbill && this.props.CreateEbill.createResponse)) {
650
650
  const message = `${this.state.channel} ${this.props.intl.formatMessage(messages.templateDuplicateSuccess)}`;
651
651
  CapNotification.success({key: 'duplicateSucess', message});
652
652
  this.getAllTemplates({params, resetPage: true});