@openlettermarketing/olc-react-sdk 2.1.7 → 2.1.8

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.
Files changed (230) hide show
  1. package/build/index.js +1 -1
  2. package/build/index.js.map +1 -1
  3. package/build/types/version.d.ts +1 -1
  4. package/package.json +7 -6
  5. package/.eslintignore +0 -1
  6. package/.eslintrc.cjs +0 -18
  7. package/.eslintrc.yml +0 -47
  8. package/.github/workflows/publish-beta.yml +0 -154
  9. package/.github/workflows/publish-production.yml +0 -143
  10. package/.prettierignore +0 -3
  11. package/.prettierrc.yml +0 -5
  12. package/CHANGELOG.md +0 -4
  13. package/babel.config.json +0 -10
  14. package/examples/.eslintrc.yml +0 -4
  15. package/index.html +0 -18
  16. package/public/vite.svg +0 -1
  17. package/src/App.tsx +0 -209
  18. package/src/assets/Fonts/Lexi-Regular.ttf +0 -0
  19. package/src/assets/images/create-template/prebuilt.svg +0 -13
  20. package/src/assets/images/create-template/scratch.svg +0 -4
  21. package/src/assets/images/input/cancel.tsx +0 -20
  22. package/src/assets/images/input/search.tsx +0 -20
  23. package/src/assets/images/input/select-cancel.tsx +0 -17
  24. package/src/assets/images/modal-icons/add.tsx +0 -36
  25. package/src/assets/images/modal-icons/cancel-file.tsx +0 -12
  26. package/src/assets/images/modal-icons/cancel-input.tsx +0 -13
  27. package/src/assets/images/modal-icons/cancel.tsx +0 -35
  28. package/src/assets/images/modal-icons/close-new.svg +0 -3
  29. package/src/assets/images/modal-icons/confirm-close-icon.tsx +0 -14
  30. package/src/assets/images/modal-icons/confirm-new.tsx +0 -22
  31. package/src/assets/images/modal-icons/confirm.svg +0 -12
  32. package/src/assets/images/modal-icons/cross.tsx +0 -23
  33. package/src/assets/images/modal-icons/del.tsx +0 -19
  34. package/src/assets/images/modal-icons/design-icon.tsx +0 -22
  35. package/src/assets/images/modal-icons/doc.tsx +0 -43
  36. package/src/assets/images/modal-icons/docx.tsx +0 -43
  37. package/src/assets/images/modal-icons/envelope-icon.tsx +0 -26
  38. package/src/assets/images/modal-icons/info.tsx +0 -19
  39. package/src/assets/images/modal-icons/jpeg.tsx +0 -43
  40. package/src/assets/images/modal-icons/jpg.tsx +0 -43
  41. package/src/assets/images/modal-icons/modal-cros.svg +0 -4
  42. package/src/assets/images/modal-icons/modal-cross.tsx +0 -37
  43. package/src/assets/images/modal-icons/new-cancel.tsx +0 -11
  44. package/src/assets/images/modal-icons/order-download.tsx +0 -42
  45. package/src/assets/images/modal-icons/pdf.tsx +0 -51
  46. package/src/assets/images/modal-icons/png.tsx +0 -43
  47. package/src/assets/images/modal-icons/save.tsx +0 -23
  48. package/src/assets/images/modal-icons/template-copy.tsx +0 -25
  49. package/src/assets/images/modal-icons/tool-cancel.tsx +0 -25
  50. package/src/assets/images/products/bi-new.svg +0 -23
  51. package/src/assets/images/products/left-arrow.svg +0 -17
  52. package/src/assets/images/products/personal-new.tsx +0 -31
  53. package/src/assets/images/products/postcard-new.tsx +0 -27
  54. package/src/assets/images/products/professional-new.tsx +0 -24
  55. package/src/assets/images/products/real-new.tsx +0 -30
  56. package/src/assets/images/products/right-arrow.svg +0 -17
  57. package/src/assets/images/products/snap-new.svg +0 -31
  58. package/src/assets/images/templates/actions.svg +0 -3
  59. package/src/assets/images/templates/address-block-icon.tsx +0 -62
  60. package/src/assets/images/templates/archive.svg +0 -3
  61. package/src/assets/images/templates/arrow-down.tsx +0 -27
  62. package/src/assets/images/templates/back-arrow.tsx +0 -19
  63. package/src/assets/images/templates/bi-fold-self-mailers.tsx +0 -28
  64. package/src/assets/images/templates/check.svg +0 -3
  65. package/src/assets/images/templates/code.svg +0 -10
  66. package/src/assets/images/templates/content-copy-icon.tsx +0 -24
  67. package/src/assets/images/templates/custom-add-on-icon.tsx +0 -18
  68. package/src/assets/images/templates/custom-qr-section-icon.tsx +0 -9
  69. package/src/assets/images/templates/custom-template.tsx +0 -23
  70. package/src/assets/images/templates/designer.tsx +0 -43
  71. package/src/assets/images/templates/dot.tsx +0 -22
  72. package/src/assets/images/templates/download-v2.svg +0 -4
  73. package/src/assets/images/templates/download.svg +0 -4
  74. package/src/assets/images/templates/dummy-template.tsx +0 -76
  75. package/src/assets/images/templates/dynamic-field.tsx +0 -119
  76. package/src/assets/images/templates/edit-pencil-icon.tsx +0 -21
  77. package/src/assets/images/templates/edit.svg +0 -3
  78. package/src/assets/images/templates/epo-icon.tsx +0 -16
  79. package/src/assets/images/templates/field.tsx +0 -29
  80. package/src/assets/images/templates/gsv-icon.tsx +0 -31
  81. package/src/assets/images/templates/info-icon.tsx +0 -37
  82. package/src/assets/images/templates/left-arrow.svg +0 -17
  83. package/src/assets/images/templates/pencil.svg +0 -3
  84. package/src/assets/images/templates/personal-letter.tsx +0 -53
  85. package/src/assets/images/templates/postcard.tsx +0 -32
  86. package/src/assets/images/templates/professional-letter.tsx +0 -53
  87. package/src/assets/images/templates/qr-code.tsx +0 -13
  88. package/src/assets/images/templates/real-penned-letters.tsx +0 -57
  89. package/src/assets/images/templates/right-arrow.svg +0 -17
  90. package/src/assets/images/templates/size-image-lg.tsx +0 -20
  91. package/src/assets/images/templates/size-image-mid.tsx +0 -20
  92. package/src/assets/images/templates/size-image-xl.tsx +0 -20
  93. package/src/assets/images/templates/size-image.tsx +0 -20
  94. package/src/assets/images/templates/snap-pack.tsx +0 -67
  95. package/src/assets/images/templates/template-default-design.tsx +0 -21
  96. package/src/assets/images/templates/trash-upload.svg +0 -3
  97. package/src/assets/images/templates/trash.svg +0 -3
  98. package/src/assets/images/templates/tri-fold-self-mailers.tsx +0 -93
  99. package/src/assets/images/templates/upload-image.svg +0 -10
  100. package/src/assets/images/templates/x.svg +0 -3
  101. package/src/assets/images/thumbnails/one.svg +0 -9
  102. package/src/assets/images/tooltip/tool-arrow.tsx +0 -25
  103. package/src/components/CreateTemplate/V2/index.tsx +0 -527
  104. package/src/components/CreateTemplate/V2/styles.scss +0 -372
  105. package/src/components/CreateTemplate/index.tsx +0 -508
  106. package/src/components/CreateTemplate/styles.scss +0 -404
  107. package/src/components/GenericUIBlocks/Button/index.tsx +0 -54
  108. package/src/components/GenericUIBlocks/Button/styles.scss +0 -43
  109. package/src/components/GenericUIBlocks/CircularProgress/index.tsx +0 -18
  110. package/src/components/GenericUIBlocks/CircularProgress/styles.scss +0 -93
  111. package/src/components/GenericUIBlocks/CustomTooltip/index.tsx +0 -88
  112. package/src/components/GenericUIBlocks/CustomTooltip/styles.scss +0 -19
  113. package/src/components/GenericUIBlocks/Dialog/V2/index.tsx +0 -227
  114. package/src/components/GenericUIBlocks/Dialog/V2/styles.scss +0 -289
  115. package/src/components/GenericUIBlocks/Dialog/index.tsx +0 -185
  116. package/src/components/GenericUIBlocks/Dialog/styles.scss +0 -227
  117. package/src/components/GenericUIBlocks/Divider/index.tsx +0 -12
  118. package/src/components/GenericUIBlocks/Divider/styles.scss +0 -7
  119. package/src/components/GenericUIBlocks/GeneralSelect/index.tsx +0 -114
  120. package/src/components/GenericUIBlocks/GeneralSelect/styles.scss +0 -406
  121. package/src/components/GenericUIBlocks/GeneralTooltip/index.tsx +0 -25
  122. package/src/components/GenericUIBlocks/GeneralTooltip/styles.scss +0 -20
  123. package/src/components/GenericUIBlocks/GenericSnackbar/Toast/index.tsx +0 -91
  124. package/src/components/GenericUIBlocks/GenericSnackbar/Toast/styles.scss +0 -92
  125. package/src/components/GenericUIBlocks/Grid/index.tsx +0 -82
  126. package/src/components/GenericUIBlocks/Input/index.tsx +0 -269
  127. package/src/components/GenericUIBlocks/Input/styles.scss +0 -332
  128. package/src/components/GenericUIBlocks/Tabs/index.tsx +0 -71
  129. package/src/components/GenericUIBlocks/Tabs/styles.scss +0 -42
  130. package/src/components/GenericUIBlocks/Typography/index.tsx +0 -18
  131. package/src/components/GenericUIBlocks/Typography/styles.scss +0 -27
  132. package/src/components/SidePanel/CustomAddOns/index.tsx +0 -342
  133. package/src/components/SidePanel/CustomAddOns/styles.scss +0 -86
  134. package/src/components/SidePanel/CustomBlockColors/index.tsx +0 -211
  135. package/src/components/SidePanel/CustomBlockColors/styles.scss +0 -80
  136. package/src/components/SidePanel/CustomFields/customFieldSection.tsx +0 -547
  137. package/src/components/SidePanel/CustomFields/styles.scss +0 -64
  138. package/src/components/SidePanel/CustomQRCode/V2/QRCodeModal/index.tsx +0 -172
  139. package/src/components/SidePanel/CustomQRCode/V2/QRCodeModal/styles.scss +0 -46
  140. package/src/components/SidePanel/CustomQRCode/index.tsx +0 -1070
  141. package/src/components/SidePanel/CustomQRCode/styles.scss +0 -149
  142. package/src/components/SidePanel/CustomUploads/V2/index.tsx +0 -542
  143. package/src/components/SidePanel/CustomUploads/V2/styles.scss +0 -267
  144. package/src/components/SidePanel/CustomUploads/index.tsx +0 -301
  145. package/src/components/SidePanel/Templates/ModalGallery/HireDesigner/index.tsx +0 -424
  146. package/src/components/SidePanel/Templates/ModalGallery/HireDesigner/styles.scss +0 -180
  147. package/src/components/SidePanel/Templates/ModalGallery/V2/index.tsx +0 -235
  148. package/src/components/SidePanel/Templates/ModalGallery/V2/styles.scss +0 -244
  149. package/src/components/SidePanel/Templates/ModalGallery/index.tsx +0 -231
  150. package/src/components/SidePanel/Templates/SideBarGallery/index.tsx +0 -233
  151. package/src/components/SidePanel/Templates/SideBarGallery/styles.scss +0 -152
  152. package/src/components/SidePanel/Templates/TemplatesCard/V2/index.tsx +0 -149
  153. package/src/components/SidePanel/Templates/TemplatesCard/V2/styles.scss +0 -156
  154. package/src/components/SidePanel/Templates/TemplatesCard/index.tsx +0 -160
  155. package/src/components/SidePanel/Templates/TemplatesCard/styles.scss +0 -98
  156. package/src/components/SidePanel/Templates/customTemplateSection.tsx +0 -793
  157. package/src/components/SidePanel/Templates/styles.scss +0 -244
  158. package/src/components/SidePanel/index.tsx +0 -160
  159. package/src/components/TemplateBuilder/index.tsx +0 -585
  160. package/src/components/TemplateBuilder/styles.scss +0 -100
  161. package/src/components/TemplateTypes/index.tsx +0 -96
  162. package/src/components/TemplateTypes/styles.scss +0 -91
  163. package/src/components/TopNavigation/ConfirmNavigateDialog/index.tsx +0 -81
  164. package/src/components/TopNavigation/ConfirmNavigateDialog/styles.scss +0 -123
  165. package/src/components/TopNavigation/DuplicateTemplateModal.tsx +0 -103
  166. package/src/components/TopNavigation/EditTemplateNameModel/index.tsx +0 -71
  167. package/src/components/TopNavigation/EditTemplateNameModel/styles.scss +0 -88
  168. package/src/components/TopNavigation/SaveTemplateModel/index.tsx +0 -201
  169. package/src/components/TopNavigation/SaveTemplateModel/styles.scss +0 -128
  170. package/src/components/TopNavigation/index.tsx +0 -938
  171. package/src/components/TopNavigation/styles.scss +0 -303
  172. package/src/importMeta.d.ts +0 -31
  173. package/src/index.scss +0 -131
  174. package/src/index.tsx +0 -238
  175. package/src/libs/test.ts +0 -7
  176. package/src/redux/actions/action-types.ts +0 -52
  177. package/src/redux/actions/customQRCodeActions.ts +0 -54
  178. package/src/redux/actions/snackbarActions.ts +0 -16
  179. package/src/redux/actions/templateActions.ts +0 -236
  180. package/src/redux/reducers/customFieldReducer.ts +0 -99
  181. package/src/redux/reducers/customQRCodeReducer.ts +0 -58
  182. package/src/redux/reducers/index.ts +0 -15
  183. package/src/redux/reducers/snackbarReducer.ts +0 -40
  184. package/src/redux/reducers/templateReducer.ts +0 -485
  185. package/src/redux/store.ts +0 -18
  186. package/src/styles/colors.scss +0 -61
  187. package/src/test/mocks.js +0 -89
  188. package/src/test/setupJest.js +0 -1
  189. package/src/utils/api.ts +0 -36
  190. package/src/utils/constants.ts +0 -182
  191. package/src/utils/customStyles.ts +0 -45
  192. package/src/utils/fetchWrapper.ts +0 -73
  193. package/src/utils/fonts.json +0 -1597
  194. package/src/utils/helper.ts +0 -205
  195. package/src/utils/local-storage.ts +0 -15
  196. package/src/utils/message.ts +0 -162
  197. package/src/utils/products.ts +0 -186
  198. package/src/utils/template-builder.ts +0 -328
  199. package/src/utils/templateIdentifierArea/biFold.ts +0 -107
  200. package/src/utils/templateIdentifierArea/index.ts +0 -35
  201. package/src/utils/templateIdentifierArea/personal.ts +0 -107
  202. package/src/utils/templateIdentifierArea/postCards.ts +0 -163
  203. package/src/utils/templateIdentifierArea/professional.ts +0 -125
  204. package/src/utils/templateIdentifierArea/snapPack.ts +0 -107
  205. package/src/utils/templateIdentifierArea/triFold.ts +0 -107
  206. package/src/utils/templateRestrictedArea/biFold.ts +0 -329
  207. package/src/utils/templateRestrictedArea/nonWindowProfessional.ts +0 -90
  208. package/src/utils/templateRestrictedArea/personal.ts +0 -90
  209. package/src/utils/templateRestrictedArea/postCard.ts +0 -334
  210. package/src/utils/templateRestrictedArea/postCardJumbo.tsx +0 -408
  211. package/src/utils/templateRestrictedArea/professional.ts +0 -318
  212. package/src/utils/templateRestrictedArea/realPenned.ts +0 -233
  213. package/src/utils/templateRestrictedArea/snapPack.ts +0 -1009
  214. package/src/utils/templateRestrictedArea/triFold.ts +0 -330
  215. package/src/utils/templateSafetyBorders/biFold.ts +0 -91
  216. package/src/utils/templateSafetyBorders/index.ts +0 -43
  217. package/src/utils/templateSafetyBorders/personal.ts +0 -41
  218. package/src/utils/templateSafetyBorders/postCards.ts +0 -259
  219. package/src/utils/templateSafetyBorders/professional.ts +0 -78
  220. package/src/utils/templateSafetyBorders/snapPack.ts +0 -165
  221. package/src/utils/templateSafetyBorders/triFold.ts +0 -114
  222. package/src/utils/templateSafetyBorders/types.d.ts +0 -68
  223. package/src/utils/types.ts +0 -12
  224. package/src/v2Theme.scss +0 -142
  225. package/tsconfig.json +0 -29
  226. package/tsconfig.node.json +0 -12
  227. package/update-version.js +0 -23
  228. package/version.js +0 -1
  229. package/vite.config.ts +0 -8
  230. package/webpack.config.js +0 -80
@@ -1,1070 +0,0 @@
1
- import React, { useEffect, useState } from 'react';
2
-
3
- // Import Polotno and third-party libraries
4
- import QRCode from 'qrcode';
5
- import { observer } from 'mobx-react-lite';
6
- import { SectionTab } from 'polotno/side-panel';
7
- import type { StoreType } from 'polotno/model/store';
8
-
9
- import { useDispatch, useSelector } from 'react-redux';
10
- import { AppDispatch, RootState } from '../../../redux/store';
11
- import { failure, success } from '../../../redux/actions/snackbarActions';
12
- import {
13
- setQrUrl,
14
- setUtmSource,
15
- setUtmMedium,
16
- setUtmCampaignName,
17
- setCustomUtms,
18
- clearQrFields,
19
- setIsQR,
20
- setQrDialog,
21
- closeQrDialog,
22
- } from '../../../redux/actions/customQRCodeActions';
23
- import { uploadFile } from '../../../redux/actions/templateActions';
24
-
25
- // Utils
26
- import { MESSAGES } from '../../../utils/message';
27
- import { validURL } from '../../../utils/helper';
28
- import {
29
- DISALLOWED_DOMAINS,
30
- MERGE_UTM_PARAMS,
31
- emojiRegex,
32
- } from '../../../utils/constants';
33
-
34
- //Components
35
- import GeneralSelect from '../../../components/GenericUIBlocks/GeneralSelect';
36
- import QRCodeModal from './V2/QRCodeModal';
37
- import DialogV2 from '../../../components/GenericUIBlocks/Dialog/V2';
38
-
39
- // UI Components
40
- import Input from '../../../components/GenericUIBlocks/Input';
41
- import Typography from '../../GenericUIBlocks/Typography';
42
-
43
- // Icons
44
- import CustomQRIcon from '../../../assets/images/templates/custom-qr-section-icon';
45
- import QRCodeIcon from '../../../assets/images/templates/qr-code';
46
-
47
- // @ts-ignore
48
- import Edit from '../../../assets/images/templates/edit.svg';
49
- // @ts-ignore
50
- import Archive from '../../../assets/images/templates/archive.svg';
51
-
52
- // @ts-ignore
53
- import ConfirmCloseIcon from '../../../assets/images/modal-icons/confirm-close-icon';
54
- // @ts-ignore
55
- import Actions from '../../../assets/images/templates/actions.svg'
56
-
57
- // styles
58
- import './styles.scss';
59
-
60
- interface CustomQRProps {
61
- store: StoreType;
62
- allowSenderFields: any;
63
- allowPropertyFields: any;
64
- excludedFields: any;
65
- currentTheme?: string | null | undefined;
66
- onGetQRCodes?: (payload: any) => Promise<any>;
67
- onDeleteQRCodes?: (id: string | number) => Promise<void>;
68
- onUploadQRCode?: (payload: any) => Promise<any>;
69
- onEditQRCode?: (payload: any) => Promise<any>;
70
- }
71
-
72
- const cancelDialogStylesV2 = {
73
- maxWidth: '567px',
74
- minHeight: 'auto',
75
- padding: '40px',
76
- };
77
-
78
- // define the new custom section
79
- const CustomQRCode = {
80
- name: 'QR-Section',
81
- Tab: (props: any) => (
82
- <SectionTab name="QR" {...props} iconSize={20}>
83
- <CustomQRIcon />
84
- </SectionTab>
85
- ),
86
-
87
- // we need observer to update component automatically on any store changes
88
- Panel: observer(
89
- ({
90
- store,
91
- allowSenderFields,
92
- allowPropertyFields,
93
- excludedFields,
94
- currentTheme,
95
- onGetQRCodes,
96
- onDeleteQRCodes,
97
- onUploadQRCode,
98
- onEditQRCode,
99
- }: CustomQRProps) => {
100
- // V2 State for API-driven functionality
101
- const [qrCodes, setQrCodes] = useState<any[]>([]);
102
- const [isLoading, setIsLoading] = useState(false);
103
- const [isLoadingMore, setIsLoadingMore] = useState(false);
104
- const [currentPage, setCurrentPage] = useState(1);
105
- const [hasMore, setHasMore] = useState(true);
106
- const [activeDropdown, setActiveDropdown] = useState<
107
- string | number | null
108
- >(null);
109
- const [isEditing, setIsEditing] = useState(false);
110
- const [selectedQR, setSelectedQR] = useState<any>(null);
111
- const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
112
- const [isLoadingDelete, setIsLoadingDelete] = useState(false);
113
- const [saveQrLoading, setSaveQrLoading] = useState(false);
114
-
115
- const [isActionsOpen, setIsActionsOpen] = useState<
116
- string | number | null
117
- >(null);
118
- const [actionsDropdownUpward, setActionsDropdownUpward] = useState(false);
119
-
120
- const qrCodesGridRef = React.useRef<HTMLDivElement>(null);
121
- const dropdownRefs = React.useRef<Map<string | number, HTMLDivElement>>(
122
- new Map()
123
- );
124
- const actionsRefs = React.useRef<Map<string | number, HTMLDivElement>>(
125
- new Map()
126
- );
127
- const loadingTimeoutRef = React.useRef<NodeJS.Timeout | null>(null);
128
- const isLoadingRef = React.useRef(false);
129
-
130
- const noteStyles = {
131
- fontWeight: '400',
132
- fontSize: '12px',
133
- color: '#000',
134
- width: '100%',
135
- maxWidth: '245px',
136
- margin: '16px 0 20px',
137
- };
138
-
139
- const itemStyle = {
140
- margin: 0,
141
- fontSize: '12px',
142
- fontWeight: '300',
143
- };
144
-
145
- const handleDialogChange = (model = '') => {
146
- if (dialog.open) {
147
- dispatch(closeQrDialog());
148
- } else {
149
- dispatch(setQrDialog(true, model));
150
- }
151
- };
152
-
153
- const handleCloseDeleteDialog = () => {
154
- setOpenDeleteDialog(false);
155
- };
156
-
157
- const handleOpenDeleteDialog = () => {
158
- setOpenDeleteDialog(true);
159
- };
160
-
161
- const handleDialogClose = (model = '') => {
162
- dispatch(closeQrDialog());
163
- setIsEditing(false);
164
- dispatch(setQrUrl(''));
165
- dispatch(setUtmSource('direct mail'));
166
- dispatch(setUtmMedium('QR Code'));
167
- dispatch(setUtmCampaignName(''));
168
- dispatch(setCustomUtms({}));
169
- };
170
-
171
-
172
- const dialog = useSelector((state: RootState) => state.customQRCode.dialog);
173
- const url = useSelector((state: RootState) => state.customQRCode.url);
174
- const utmSource = useSelector(
175
- (state: RootState) => state.customQRCode.utmSource
176
- );
177
- const utmMedium = useSelector(
178
- (state: RootState) => state.customQRCode.utmMedium
179
- );
180
- const utmCampaignName = useSelector(
181
- (state: RootState) => state.customQRCode.utmCampaignName
182
- );
183
- const customUtms = useSelector(
184
- (state: RootState) => state.customQRCode.customUtms
185
- );
186
- const isQR = useSelector((state: RootState) => state.customQRCode.isQR);
187
-
188
- const dispatch: AppDispatch = useDispatch();
189
-
190
- const defaultFields = useSelector(
191
- (state: RootState) => state.templates.defaultDynamicFields
192
- );
193
-
194
- const defaultSenderFields = useSelector(
195
- (state: RootState) => state.templates.defaultSenderFields
196
-
197
- );
198
-
199
- const customFields = useSelector(
200
- (state: RootState) => state.customFields.customFields
201
- );
202
-
203
- const customFieldsV2 = useSelector(
204
- (state: RootState) => state.customFields.customFieldsV2
205
- ) as Record<string, any>;
206
-
207
- const defaultPropertyFields = useSelector(
208
- (state: RootState) => state.templates.defaultPropertyFields
209
- );
210
-
211
- const excludedLabels = ['utm_c_first_name c_last_name'];
212
-
213
- let flattenedFieldsV2 = [];
214
- if (customFieldsV2.length > 0) {
215
- flattenedFieldsV2 = customFieldsV2?.flatMap(
216
- (section: { fields: any }) => section.fields
217
- );
218
- }
219
-
220
- const allFields = [
221
- ...defaultFields,
222
- ...customFields,
223
- ...flattenedFieldsV2,
224
- ...(allowSenderFields ? defaultSenderFields : []),
225
- ...(allowPropertyFields ? defaultPropertyFields : []),
226
- ...(allowPropertyFields
227
- ? [
228
- {
229
- value: 'ROS.PROPERTY_OFFER',
230
- key: '{{ROS.PROPERTY_OFFER}}',
231
- defaultValue: '$123,456.00',
232
- },
233
- ]
234
- : []),
235
- ].filter(({ key }) => !excludedFields?.includes(key));
236
-
237
- const utmFields = allFields
238
- .map(({ key }) => ({
239
- label: `utm_${key?.toLowerCase().replaceAll('.', '_').replaceAll(/[{}]/g, '')}`,
240
- }))
241
- .filter((utmField) => !excludedLabels.includes(utmField.label));
242
-
243
- const utms = ['custom_utm_1', 'custom_utm_2', 'custom_utm_3'];
244
-
245
- const el = store.selectedElements[0];
246
-
247
- const clearQRFields = () => {
248
- store.selectElements([]);
249
- dispatch(clearQrFields());
250
- };
251
-
252
- const appendUtmParameters = (utmField: string, defaultValue: string) => {
253
- let result = '';
254
- const field = MERGE_UTM_PARAMS.find((field) => field.key === utmField);
255
- if (field) {
256
- result = `${field.value}=${defaultValue}`;
257
- }
258
- return result;
259
- };
260
-
261
- const validateQRCode = () => {
262
- const validations = [
263
- { value: utmSource, label: 'UTM Source' },
264
- { value: utmMedium, label: 'UTM Medium' },
265
- { value: utmCampaignName, label: 'UTM Campaign' },
266
- ];
267
-
268
- for (const { value, label } of validations) {
269
- if (value.length >= 150) {
270
- dispatch(failure(`${label} must be less than 150 characters`));
271
- return false;
272
- }
273
- if (emojiRegex.test(value)) {
274
- dispatch(failure(`Emoji are not allowed in ${label}`));
275
- return false;
276
- }
277
- }
278
-
279
- return true;
280
- };
281
-
282
- const containsDisallowedDomains = (str: string) => {
283
- return DISALLOWED_DOMAINS.some((substring) => str.includes(substring));
284
- };
285
-
286
- // create svg image for QR code for input text
287
- const getQR = (text: string) => {
288
- return new Promise<string>((resolve, reject) => {
289
- QRCode.toDataURL(
290
- text || 'no-data',
291
- {
292
- type: 'image/png',
293
- margin: 0,
294
- color: {
295
- dark: '#000000',
296
- light: '#0000', // transparent background
297
- },
298
- scale: 10, // increase for higher resolution
299
- },
300
- (err: any, url: string) => {
301
- if (err) return reject(err);
302
- resolve(url); // returns base64 PNG image string
303
- }
304
- );
305
- });
306
- };
307
-
308
- // Converts base64 data URL to File
309
- const base64ToFile = (base64: string, filename: string): File => {
310
- const arr = base64.split(',');
311
- const mime = arr[0].match(/:(.*?);/)?.[1] || 'image/png';
312
- const bstr = atob(arr[1]);
313
- let n = bstr.length;
314
- const u8arr = new Uint8Array(n);
315
- while (n--) {
316
- u8arr[n] = bstr.charCodeAt(n);
317
- }
318
- return new File([u8arr], filename, { type: mime });
319
- };
320
-
321
- const createCustomizeURL = (url: string) => {
322
- let customURL = url;
323
- let params = [];
324
- if (utmSource) {
325
- params.push(`utm_source=${utmSource.replace(/ /g, '_')}`);
326
- }
327
- if (utmMedium) {
328
- params.push(`utm_medium=${utmMedium.replace(/ /g, '_')}`);
329
- }
330
- if (utmCampaignName) {
331
- params.push(`utm_campaign=${utmCampaignName.replace(/ /g, '_')}`);
332
- }
333
-
334
- if (customUtms[utms[0]]?.label) {
335
- const orignalField = `{{${customUtms[utms[0]]?.label.replace('utm_', '').replace('_', '.').toUpperCase()}}}`;
336
- const defaultValue = allFields.find(
337
- (field) => field.key === orignalField
338
- )?.defaultValue;
339
- const attachUtmKeys = appendUtmParameters(orignalField, defaultValue);
340
- const mergeUtmParams =
341
- attachUtmKeys && orignalField == '{{C.PHONE_NUMBER}}'
342
- ? encodeURIComponent(
343
- `${customUtms[utms[0]]?.label}=${defaultValue}&${attachUtmKeys}`
344
- ).replace(/[\s\(\)]/g, '')
345
- : attachUtmKeys
346
- ? `${customUtms[utms[0]]?.label}=${defaultValue}&${attachUtmKeys}`
347
- : `${customUtms[utms[0]]?.label}=${defaultValue}`;
348
- params.push(mergeUtmParams);
349
- }
350
-
351
- if (customUtms[utms[1]]?.label) {
352
- const orignalField = `{{${customUtms[utms[1]]?.label.replace('utm_', '').replace('_', '.').toUpperCase()}}}`;
353
- const defaultValue = allFields.find(
354
- (field) => field.key === orignalField
355
- )?.defaultValue;
356
- const attachUtmKeys = appendUtmParameters(orignalField, defaultValue);
357
- const mergeUtmParams =
358
- attachUtmKeys && orignalField == '{{C.PHONE_NUMBER}}'
359
- ? encodeURIComponent(
360
- `${customUtms[utms[1]]?.label}=${defaultValue}&${attachUtmKeys}`
361
- ).replace(/[\s\(\)]/g, '')
362
- : attachUtmKeys
363
- ? `${customUtms[utms[1]]?.label}=${defaultValue}&${attachUtmKeys}`
364
- : `${customUtms[utms[1]]?.label}=${defaultValue}`;
365
- params.push(mergeUtmParams);
366
- }
367
-
368
- if (customUtms[utms[2]]?.label) {
369
- const orignalField = `{{${customUtms[utms[2]]?.label.replace('utm_', '').replace('_', '.').toUpperCase()}}}`;
370
- const defaultValue = allFields.find(
371
- (field) => field.key === orignalField
372
- )?.defaultValue;
373
- const attachUtmKeys = appendUtmParameters(orignalField, defaultValue);
374
- const mergeUtmParams =
375
- attachUtmKeys && orignalField == '{{C.PHONE_NUMBER}}'
376
- ? encodeURIComponent(
377
- `${customUtms[utms[2]]?.label}=${defaultValue}&${attachUtmKeys}`
378
- ).replace(/[\s\(\)]/g, '')
379
- : attachUtmKeys
380
- ? `${customUtms[utms[2]]?.label}=${defaultValue}&${attachUtmKeys}`
381
- : `${customUtms[utms[2]]?.label}=${defaultValue}`;
382
- params.push(mergeUtmParams);
383
- }
384
-
385
- if (params.length > 0) {
386
- customURL += `/?${params.join('&')}`;
387
- }
388
-
389
- return encodeURI(customURL);
390
- };
391
-
392
- const addNewQRCode = async () => {
393
- if (utmCampaignName) {
394
- if (url) {
395
- if (validURL(url) && !containsDisallowedDomains(url)) {
396
- const isValidQR = validateQRCode();
397
- if (!isValidQR) return false;
398
-
399
- setSaveQrLoading(true);
400
- const randomizedId = Math.random().toString(36).substring(2, 7);
401
- const customQRUrl = createCustomizeURL(url);
402
-
403
- const src = await getQR(customQRUrl);
404
- if (currentTheme === 'v2' && onUploadQRCode && onGetQRCodes) {
405
- const file = base64ToFile(src, `${utmCampaignName}-qr.png`);
406
- const uploadedFile = await uploadFile(file);
407
- if (!uploadedFile) {
408
- setSaveQrLoading(false);
409
- return dispatch(failure(MESSAGES.TEMPLATE.QR_SECTION.FAILED_QR));
410
- }
411
- const payload = {
412
- name: utmCampaignName,
413
- qrCodeJson: {
414
- name: utmCampaignName,
415
- url: url,
416
- qrImagePath: uploadedFile,
417
- utm_source: utmSource,
418
- utm_medium: utmMedium,
419
- utm_campaign_name: utmCampaignName,
420
- custom_utms: customUtms,
421
- },
422
- };
423
-
424
- try {
425
- await onUploadQRCode(payload);
426
- // Reset pagination and reload from first page
427
- setCurrentPage(1);
428
- setHasMore(true);
429
- await loadQRCodes(1, false, false);
430
- handleDialogClose(); // Close modal
431
- clearQRFields();
432
- setSaveQrLoading(false);
433
- } catch (error) {
434
- setSaveQrLoading(false);
435
- console.error('Failed to create QR code:', error);
436
- }
437
- } else {
438
- store.activePage.addElement({
439
- id: `qr-${randomizedId}`,
440
- type: 'image',
441
- name: 'qr',
442
- x: 50,
443
- y: 50,
444
- width: 100,
445
- height: 100,
446
- blurRadius: 0,
447
- keepRatio: true,
448
- src,
449
- custom: {
450
- url,
451
- utm_source: utmSource,
452
- utm_medium: utmMedium,
453
- utm_campaign_name: utmCampaignName,
454
- custom_utms: customUtms,
455
- },
456
- });
457
- clearQRFields();
458
- }
459
- setSaveQrLoading(false);
460
- } else {
461
- dispatch(failure(MESSAGES.TEMPLATE.QR_SECTION.INVALID_URL));
462
- }
463
- } else {
464
- dispatch(failure(MESSAGES.TEMPLATE.QR_SECTION.EMPTY_QR));
465
- }
466
- } else {
467
- dispatch(failure(MESSAGES.TEMPLATE.QR_SECTION.EMPTY_CAMPAIGN));
468
- }
469
- };
470
-
471
- // V2 API Functions
472
- const loadQRCodes = React.useCallback(
473
- async (page: number = 1, append: boolean = false, loading = true) => {
474
- if (!onGetQRCodes) return;
475
-
476
- if (isLoadingRef.current) return;
477
- isLoadingRef.current = true;
478
-
479
- if (page === 1 && loading) {
480
- setIsLoading(true);
481
- } else {
482
- setIsLoadingMore(true);
483
- }
484
-
485
- try {
486
- const payload = {
487
- page,
488
- pageSize: 15,
489
- };
490
-
491
- const qrCodesData = await onGetQRCodes(payload);
492
-
493
- if (qrCodesData && Array.isArray(qrCodesData.rows)) {
494
- const normalized = qrCodesData.rows.map((qr: any) => ({
495
- id: qr.id,
496
- name: qr?.qrCodeJson?.name || qr?.qrCodeJson?.utm_campaign_name || '',
497
- url: qr.qrCodeJson.url,
498
- qrImagePath: qr.qrCodeJson.qrImagePath,
499
- status: qr.status,
500
- utm_source: qr.qrCodeJson.utm_source,
501
- utm_medium: qr.qrCodeJson.utm_medium,
502
- utm_campaign_name: qr.qrCodeJson.utm_campaign_name,
503
- custom_utms: qr.qrCodeJson.custom_utms,
504
- }));
505
-
506
- if (append && page > 1) {
507
- setQrCodes((prevQrCodes) => {
508
- const existingIds = new Set(
509
- prevQrCodes.map((qr: any) => qr.id)
510
- );
511
- const newQrCodes = normalized.filter(
512
- (qr: any) => !existingIds.has(qr.id)
513
- );
514
- return [...prevQrCodes, ...newQrCodes];
515
- });
516
- } else {
517
- setQrCodes(normalized);
518
- }
519
-
520
- const hasMorePages =
521
- qrCodesData.currentPage < qrCodesData.lastPage;
522
- setHasMore(hasMorePages);
523
- setCurrentPage(qrCodesData.currentPage);
524
- } else {
525
- if (!append) {
526
- setQrCodes([]);
527
- }
528
- setHasMore(false);
529
- }
530
- } catch (error) {
531
- console.error('Failed to load QR codes:', error);
532
- } finally {
533
- setIsLoading(false);
534
- setIsLoadingMore(false);
535
- isLoadingRef.current = false;
536
- }
537
- },
538
- [onGetQRCodes]
539
- );
540
-
541
- const loadMoreQRCodes = React.useCallback(async () => {
542
- if (!hasMore || isLoadingMore || isLoading) return;
543
- await loadQRCodes(currentPage + 1, true);
544
- }, [hasMore, isLoadingMore, isLoading, currentPage, loadQRCodes]);
545
-
546
- const handleScroll = React.useCallback(() => {
547
- if (!qrCodesGridRef.current || !hasMore || isLoadingMore || isLoading)
548
- return;
549
-
550
- const { scrollTop, scrollHeight, clientHeight } = qrCodesGridRef.current;
551
- const scrollPercentage = (scrollTop + clientHeight) / scrollHeight;
552
-
553
- if (scrollPercentage > 0.75) {
554
- if (loadingTimeoutRef.current) {
555
- clearTimeout(loadingTimeoutRef.current);
556
- }
557
-
558
- loadingTimeoutRef.current = setTimeout(() => {
559
- loadMoreQRCodes();
560
- }, 300);
561
- }
562
- }, [hasMore, isLoadingMore, isLoading, loadMoreQRCodes]);
563
-
564
- const handleQRCodeSelect = async (qrCode: any) => {
565
- let customQRUrl = '';
566
- let src: any = '';
567
- let customData = {};
568
-
569
- if (currentTheme === 'v2') {
570
- customQRUrl = createCustomizeURL(qrCode.url);
571
- src = qrCode.qrImagePath;
572
- customData = {
573
- url: qrCode.url,
574
- qrCodeInstanceId: qrCode.id,
575
- qrImagePath: qrCode.qrImagePath,
576
- utm_source: qrCode.utm_source,
577
- utm_medium: qrCode.utm_medium,
578
- utm_campaign_name: qrCode.utm_campaign_name,
579
- custom_utms: qrCode.custom_utms,
580
- }
581
- } else {
582
- customQRUrl = createCustomizeURL(qrCode.url);
583
- src = await getQR(customQRUrl);
584
- customData = {
585
- url: qrCode.url,
586
- utm_source: qrCode.utmSource,
587
- utm_medium: qrCode.utmMedium,
588
- utm_campaign_name: qrCode.utmCampaign,
589
- custom_utms: qrCode.customUtms,
590
- }
591
- }
592
-
593
- const randomizedId = Math.random().toString(36).substring(2, 7);
594
- store.activePage.addElement({
595
- id: `qr-${randomizedId}`,
596
- type: 'image',
597
- name: 'qr',
598
- x: 50,
599
- y: 50,
600
- width: 100,
601
- height: 100,
602
- blurRadius: 0,
603
- keepRatio: true,
604
- src,
605
- custom: customData,
606
- });
607
- };
608
-
609
- const handleDeleteQRCode = async (qrCodeId: string | number) => {
610
- if (!onDeleteQRCodes) return;
611
- setIsLoadingDelete(true);
612
- try {
613
- await onDeleteQRCodes(qrCodeId);
614
- setQrCodes((prev) => prev.filter((qr) => qr.id !== qrCodeId));
615
- setActiveDropdown(null);
616
- setIsActionsOpen(null);
617
- } catch (error) {
618
- console.error('Failed to delete QR code:', error);
619
- }
620
- finally {
621
- setIsLoadingDelete(false);
622
- }
623
- };
624
-
625
- const handleEditQRCode = async (qrCode: any) => {
626
- setIsEditing(true);
627
- setSelectedQR(qrCode);
628
- dispatch(setQrUrl(qrCode.url ?? ''));
629
- dispatch(setUtmSource(qrCode.utm_source ?? ''));
630
- dispatch(setUtmMedium(qrCode.utm_medium ?? ''));
631
- dispatch(setUtmCampaignName(qrCode.utm_campaign_name ?? ''));
632
- dispatch(setCustomUtms(qrCode.custom_utms ?? {}));
633
-
634
- handleDialogChange('qr-modal');
635
- setIsActionsOpen(null);
636
- };
637
-
638
- const toggleActions = (
639
- qrCodeId: string | number,
640
- event: React.MouseEvent
641
- ) => {
642
- event.stopPropagation();
643
- const isOpening = isActionsOpen !== qrCodeId;
644
- if (isOpening) {
645
- const triggerEl = actionsRefs.current.get(qrCodeId);
646
- const scrollContainer = qrCodesGridRef.current;
647
- const dropdownHeight = 80;
648
- if (triggerEl && scrollContainer) {
649
- const triggerRect = triggerEl.getBoundingClientRect();
650
- const containerRect = scrollContainer.getBoundingClientRect();
651
- const spaceBelow = containerRect.bottom - triggerRect.bottom;
652
- setActionsDropdownUpward(spaceBelow < dropdownHeight);
653
- } else {
654
- setActionsDropdownUpward(false);
655
- }
656
- }
657
- setIsActionsOpen(isActionsOpen === qrCodeId ? null : qrCodeId);
658
- };
659
-
660
- // if selection is changed we need to update input value
661
- const updateQRCode = async () => {
662
- if (url) {
663
- if (validURL(url) && !containsDisallowedDomains(url)) {
664
- const isValidQR = validateQRCode();
665
- if (!isValidQR) return false;
666
-
667
- setSaveQrLoading(true);
668
- const customQRUrl = createCustomizeURL(url);
669
- const src = await getQR(customQRUrl);
670
-
671
- if (currentTheme === 'v2' && onEditQRCode && onGetQRCodes) {
672
- const file = base64ToFile(src, `${utmCampaignName}-qr.png`);
673
- const uploadedFile = await uploadFile(file);
674
- const payload = {
675
- name: utmCampaignName,
676
- id: selectedQR?.id,
677
- qrCodeJson: {
678
- name: utmCampaignName,
679
- url: url,
680
- qrImagePath: uploadedFile,
681
- utm_source: utmSource || null,
682
- utm_medium: utmMedium || null,
683
- utm_campaign_name: utmCampaignName,
684
- custom_utms: customUtms,
685
- },
686
- };
687
-
688
- try {
689
- await onEditQRCode(payload);
690
- // Reset pagination and reload from first page
691
- setCurrentPage(1);
692
- setHasMore(true);
693
- await loadQRCodes(1, false, false);
694
- handleDialogClose(); // Close modal
695
- clearQRFields();
696
- setSaveQrLoading(false);
697
- } catch (error) {
698
- setSaveQrLoading(false);
699
- console.error('Failed to create QR code:', error);
700
- }
701
- } else if (el?.name === 'qr' && url) {
702
- el.set({
703
- src,
704
- custom: {
705
- url,
706
- utm_source: utmSource,
707
- utm_medium: utmMedium,
708
- utm_campaign_name: utmCampaignName,
709
- custom_utms: customUtms,
710
- },
711
- });
712
- clearQRFields();
713
- setSaveQrLoading(false);
714
- }
715
- } else {
716
- dispatch(failure(MESSAGES.TEMPLATE.QR_SECTION.INVALID_URL));
717
- }
718
- } else {
719
- dispatch(failure(MESSAGES.TEMPLATE.QR_SECTION.EMPTY_QR));
720
- }
721
- };
722
-
723
- // Handler to update dropdown values
724
- const handleSelect = (utmKey: string, value: any) => {
725
- const updatedUtms = { ...customUtms };
726
- if (value === null) {
727
- delete updatedUtms[utmKey];
728
- } else {
729
- updatedUtms[utmKey] = value;
730
- }
731
- dispatch(setCustomUtms(updatedUtms));
732
- };
733
-
734
- // if selection is changed we need to update input value
735
- useEffect(() => {
736
- if (el?.name === 'qr' && currentTheme !== 'v2') {
737
- dispatch(setIsQR(el?.name === 'qr'));
738
- dispatch(setQrUrl(el?.custom?.url || el?.custom?.value || ''));
739
- dispatch(setUtmSource(el?.custom?.utm_source || 'direct mail'));
740
- dispatch(setUtmMedium(el?.custom?.utm_medium || 'QR Code'));
741
- dispatch(setUtmCampaignName(el?.custom?.utm_campaign_name || ''));
742
- if (Object.values(el?.custom?.custom_utms || {}).length) {
743
- dispatch(setCustomUtms(el?.custom?.custom_utms));
744
- } else {
745
- dispatch(setCustomUtms({}));
746
- }
747
- } else if (isQR && el?.name !== 'qr') {
748
- dispatch(clearQrFields());
749
- }
750
- }, [isQR, el]);
751
-
752
- // Handle click outside to close actions menu
753
- useEffect(() => {
754
- const handleClickOutside = (event: MouseEvent) => {
755
- // Close actions if clicking outside the currently open actions container
756
- if (isActionsOpen !== null) {
757
- const actionsElement = actionsRefs.current.get(isActionsOpen);
758
- if (
759
- actionsElement &&
760
- !actionsElement.contains(event.target as Node)
761
- ) {
762
- setIsActionsOpen(null);
763
- }
764
- }
765
-
766
- // V2: Handle dropdowns
767
- if (activeDropdown) {
768
- const dropdownElement = dropdownRefs.current.get(activeDropdown);
769
- if (
770
- dropdownElement &&
771
- !dropdownElement.contains(event.target as Node)
772
- ) {
773
- setActiveDropdown(null);
774
- }
775
- }
776
- };
777
-
778
- if (isActionsOpen !== null || activeDropdown !== null) {
779
- document.addEventListener('mousedown', handleClickOutside);
780
- }
781
-
782
- return () => {
783
- document.removeEventListener('mousedown', handleClickOutside);
784
- };
785
- }, [isActionsOpen, activeDropdown]);
786
-
787
- // V2: Load QR codes on mount if callbacks are provided
788
- useEffect(() => {
789
- if (onGetQRCodes) {
790
- loadQRCodes(1, false);
791
- }
792
- }, [onGetQRCodes, loadQRCodes]);
793
-
794
- // V2: Setup scroll event listener on the list container
795
- useEffect(() => {
796
- const listElement = qrCodesGridRef.current;
797
- if (listElement && onGetQRCodes) {
798
- listElement.addEventListener('scroll', handleScroll);
799
- return () => {
800
- listElement.removeEventListener('scroll', handleScroll);
801
- if (loadingTimeoutRef.current) {
802
- clearTimeout(loadingTimeoutRef.current);
803
- }
804
- };
805
- }
806
- }, [
807
- hasMore,
808
- isLoadingMore,
809
- isLoading,
810
- currentPage,
811
- handleScroll,
812
- onGetQRCodes,
813
- ]);
814
-
815
- return (
816
- <>
817
- {currentTheme === 'v2' ? (
818
- <>
819
- <div
820
- className="qr-section-v2-container"
821
- style={{
822
- padding: '14px',
823
- }}
824
- >
825
- <div
826
- className="qr-code-wrapper qr-code-wrapper-sticky"
827
- onClick={() => handleDialogChange('qr-modal')}
828
- >
829
- <QRCodeIcon />
830
- <button className="qr-submit-btn-new">Create QR-Code</button>
831
- </div>
832
- <Typography variant="h6" style={noteStyles}>
833
- {MESSAGES.TEMPLATE.QR_SECTION.QR_NOTE}
834
- </Typography>
835
-
836
- {/* V2 QR Codes List when callbacks are provided */}
837
- {onGetQRCodes && (
838
- <div
839
- className="qr-codes-list-scroll"
840
- ref={qrCodesGridRef}
841
- >
842
- <div className="qr-codes-grid">
843
- {isLoading ? (
844
- <div className="loading-state">Loading QR codes...</div>
845
- ) : qrCodes.length === 0 ? (
846
- <div className="empty-state">No QR codes created yet</div>
847
- ) : (
848
- <>
849
- {qrCodes.map((qrCode) => (
850
- <div key={qrCode.id} className="qr-card" onClick={() => handleQRCodeSelect(qrCode)}>
851
- <img
852
- src={qrCode.qrImagePath}
853
- alt="code"
854
- style={{ width: '30px', height: '30px' }}
855
- />
856
- <div
857
- className="qr-card-content"
858
- >
859
- <Typography
860
- style={{
861
- fontWeight: '600',
862
- fontSize: '12px',
863
- color: '#545454',
864
- margin: 0,
865
- }}
866
- >
867
- {qrCode.name}
868
- </Typography>
869
- <Typography
870
- style={{
871
- fontWeight: '400',
872
- fontSize: '12px',
873
- color: '#54545499',
874
- margin: 0,
875
- }}
876
- >
877
- {qrCode.status}
878
- </Typography>
879
- </div>
880
- {(onEditQRCode || onDeleteQRCodes) && <div
881
- className="actions-container"
882
- ref={(el) => {
883
- if (el) actionsRefs.current.set(qrCode.id, el);
884
- }}
885
- >
886
- <div
887
- className="actions"
888
- onClick={(e) => { toggleActions(qrCode.id, e); e.stopPropagation(); }}
889
- >
890
- <img src={Actions} alt='actions' />
891
- </div>
892
- {/* Actions */}
893
- {isActionsOpen === qrCode.id && (
894
- <div
895
- className={`actions-wrapper${actionsDropdownUpward ? ' actions-wrapper-up' : ''}`}
896
- >
897
- {onEditQRCode && (
898
- <div
899
- className="action-item"
900
- onClick={(e) => {
901
- e.stopPropagation();
902
- handleEditQRCode(qrCode);
903
- }}
904
- >
905
- <img src={Edit} alt="edit" />
906
- <Typography style={itemStyle}>
907
- Edit
908
- </Typography>
909
- </div>
910
- )}
911
- {onDeleteQRCodes && (
912
- <div
913
- className="action-item"
914
- onClick={(e) => {
915
- e.stopPropagation();
916
- handleOpenDeleteDialog();
917
- setSelectedQR(qrCode.id);
918
- }}
919
- >
920
- <img src={Archive} alt="Archive" />
921
- <Typography style={itemStyle}>
922
- Archive
923
- </Typography>
924
- </div>
925
- )}
926
- </div>
927
- )}
928
- </div>}
929
- </div>
930
- ))}
931
- {/* Loading indicator for pagination */}
932
- {isLoadingMore && (
933
- <div className="loading-more">
934
- <div className="loading-state">
935
- Loading more QR codes...
936
- </div>
937
- </div>
938
- )}
939
- </>
940
- )}
941
- </div>
942
- </div>
943
- )}
944
- </div>
945
- {dialog.open && (
946
- <QRCodeModal
947
- show={dialog.open}
948
- utms={utms}
949
- utmFields={utmFields}
950
- isEditing={isEditing}
951
- handleSelect={handleSelect}
952
- handleDialogChange={handleDialogChange}
953
- handleClose={handleDialogClose}
954
- setUtmCampaignName={setUtmCampaignName}
955
- setQrUrl={setQrUrl}
956
- setUtmSource={setUtmSource}
957
- setUtmMedium={setUtmMedium}
958
- setCustomUtms={setCustomUtms}
959
- addNewQRCode={isEditing ? updateQRCode : addNewQRCode}
960
- loading={saveQrLoading}
961
- />
962
- )}
963
- {openDeleteDialog && (
964
- <DialogV2
965
- icon={<ConfirmCloseIcon fill='var(--primary-color)' />}
966
- customStyles={cancelDialogStylesV2}
967
- open={openDeleteDialog}
968
- handleClose={handleCloseDeleteDialog}
969
- title='Delete QR Code'
970
- subHeading=''
971
- description='Are you sure you want to delete this QR code?'
972
- onSubmit={() => {
973
- handleDeleteQRCode(selectedQR);
974
- handleCloseDeleteDialog();
975
- }}
976
- onCancel={handleCloseDeleteDialog}
977
- cancelText='No'
978
- submitText='Yes'
979
- isGallery={false}
980
- loading={isLoadingDelete}
981
- />)}
982
- </>
983
- ) : (
984
- <>
985
- <button
986
- className="qr-submit-btn"
987
- onClick={isQR ? updateQRCode : addNewQRCode}
988
- >
989
- {isQR
990
- ? MESSAGES.TEMPLATE.QR_SECTION.UPDATE_BUTTON
991
- : MESSAGES.TEMPLATE.QR_SECTION.SUBMIT_BUTTON}
992
- </button>
993
- <div className="qr-input-wrapper">
994
- <label>QR URL*:</label>
995
- <Input
996
- type="text"
997
- onChange={(e) => {
998
- dispatch(setQrUrl(e.target.value));
999
- }}
1000
- placeholder={MESSAGES.TEMPLATE.QR_SECTION.QR_PLACEHOLDER}
1001
- value={url}
1002
- qrField={true}
1003
- />
1004
- </div>
1005
- <div className="qr-input-wrapper">
1006
- <label>UTM Source:</label>
1007
- <Input
1008
- type="text"
1009
- onChange={(e) => {
1010
- dispatch(setUtmSource(e.target.value));
1011
- }}
1012
- placeholder={'Enter UTM Source'}
1013
- value={utmSource}
1014
- qrField={true}
1015
- />
1016
- </div>
1017
- <div className="qr-input-wrapper">
1018
- <label>UTM Medium:</label>
1019
- <Input
1020
- type="text"
1021
- onChange={(e) => {
1022
- dispatch(setUtmMedium(e.target.value));
1023
- }}
1024
- placeholder={'Enter UTM Medium'}
1025
- value={utmMedium}
1026
- qrField={true}
1027
- />
1028
- </div>
1029
- <div className="qr-input-wrapper">
1030
- <label>UTM Campaign Name*:</label>
1031
- <Input
1032
- type="text"
1033
- onChange={(e) => {
1034
- dispatch(setUtmCampaignName(e.target.value));
1035
- }}
1036
- placeholder={'Enter UTM Campaign Name'}
1037
- value={utmCampaignName}
1038
- qrField={true}
1039
- />
1040
- </div>
1041
- {utms?.map((utm, idx) => {
1042
- return (
1043
- <div className="qr-input-wrapper" key={idx}>
1044
- <label>{utm.toUpperCase().replace(/\_/g, ' ')}:</label>
1045
- <GeneralSelect
1046
- placeholder={`Search / Select Custom UTM ${idx + 1}`}
1047
- options={utmFields as any}
1048
- setSelectedValue={(value: any) =>
1049
- handleSelect(utm, value)
1050
- }
1051
- selectedValue={customUtms[utm] || (null as any)}
1052
- builderSelect={true}
1053
- clearField={true}
1054
- search={true}
1055
- qrField={true}
1056
- isError={false}
1057
- gallerySelect={false}
1058
- />
1059
- </div>
1060
- );
1061
- })}
1062
- </>
1063
- )}
1064
- </>
1065
- );
1066
- }
1067
- ),
1068
- };
1069
-
1070
- export default CustomQRCode;