@openlettermarketing/olc-react-sdk 2.1.4 → 2.1.5-beta.2

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/.eslintignore +1 -0
  2. package/.eslintrc.cjs +18 -0
  3. package/.eslintrc.yml +47 -0
  4. package/.github/workflows/publish-beta.yml +154 -0
  5. package/.github/workflows/publish-production.yml +143 -0
  6. package/.prettierignore +3 -0
  7. package/.prettierrc.yml +5 -0
  8. package/CHANGELOG.md +4 -0
  9. package/babel.config.json +10 -0
  10. package/build/index.js +82 -82
  11. package/build/index.js.map +1 -1
  12. package/build/types/version.d.ts +1 -1
  13. package/examples/.eslintrc.yml +4 -0
  14. package/index.html +18 -0
  15. package/package.json +1 -1
  16. package/public/vite.svg +1 -0
  17. package/src/App.tsx +209 -0
  18. package/src/assets/Fonts/Lexi-Regular.ttf +0 -0
  19. package/src/assets/images/create-template/prebuilt.svg +13 -0
  20. package/src/assets/images/create-template/scratch.svg +4 -0
  21. package/src/assets/images/input/cancel.tsx +20 -0
  22. package/src/assets/images/input/search.tsx +20 -0
  23. package/src/assets/images/input/select-cancel.tsx +17 -0
  24. package/src/assets/images/modal-icons/add.tsx +36 -0
  25. package/src/assets/images/modal-icons/cancel-file.tsx +12 -0
  26. package/src/assets/images/modal-icons/cancel-input.tsx +13 -0
  27. package/src/assets/images/modal-icons/cancel.tsx +35 -0
  28. package/src/assets/images/modal-icons/close-new.svg +3 -0
  29. package/src/assets/images/modal-icons/confirm-close-icon.tsx +14 -0
  30. package/src/assets/images/modal-icons/confirm-new.tsx +22 -0
  31. package/src/assets/images/modal-icons/confirm.svg +12 -0
  32. package/src/assets/images/modal-icons/cross.tsx +23 -0
  33. package/src/assets/images/modal-icons/del.tsx +19 -0
  34. package/src/assets/images/modal-icons/design-icon.tsx +22 -0
  35. package/src/assets/images/modal-icons/doc.tsx +43 -0
  36. package/src/assets/images/modal-icons/docx.tsx +43 -0
  37. package/src/assets/images/modal-icons/envelope-icon.tsx +26 -0
  38. package/src/assets/images/modal-icons/info.tsx +19 -0
  39. package/src/assets/images/modal-icons/jpeg.tsx +43 -0
  40. package/src/assets/images/modal-icons/jpg.tsx +43 -0
  41. package/src/assets/images/modal-icons/modal-cros.svg +4 -0
  42. package/src/assets/images/modal-icons/modal-cross.tsx +37 -0
  43. package/src/assets/images/modal-icons/new-cancel.tsx +11 -0
  44. package/src/assets/images/modal-icons/order-download.tsx +42 -0
  45. package/src/assets/images/modal-icons/pdf.tsx +51 -0
  46. package/src/assets/images/modal-icons/png.tsx +43 -0
  47. package/src/assets/images/modal-icons/save.tsx +23 -0
  48. package/src/assets/images/modal-icons/template-copy.tsx +25 -0
  49. package/src/assets/images/modal-icons/tool-cancel.tsx +25 -0
  50. package/src/assets/images/products/bi-new.svg +23 -0
  51. package/src/assets/images/products/left-arrow.svg +17 -0
  52. package/src/assets/images/products/personal-new.tsx +31 -0
  53. package/src/assets/images/products/postcard-new.tsx +27 -0
  54. package/src/assets/images/products/professional-new.tsx +24 -0
  55. package/src/assets/images/products/real-new.tsx +30 -0
  56. package/src/assets/images/products/right-arrow.svg +17 -0
  57. package/src/assets/images/products/snap-new.svg +31 -0
  58. package/src/assets/images/templates/actions.svg +3 -0
  59. package/src/assets/images/templates/address-block-icon.tsx +62 -0
  60. package/src/assets/images/templates/archive.svg +3 -0
  61. package/src/assets/images/templates/arrow-down.tsx +27 -0
  62. package/src/assets/images/templates/back-arrow.tsx +19 -0
  63. package/src/assets/images/templates/bi-fold-self-mailers.tsx +28 -0
  64. package/src/assets/images/templates/check.svg +3 -0
  65. package/src/assets/images/templates/code.svg +10 -0
  66. package/src/assets/images/templates/content-copy-icon.tsx +24 -0
  67. package/src/assets/images/templates/custom-add-on-icon.tsx +18 -0
  68. package/src/assets/images/templates/custom-qr-section-icon.tsx +9 -0
  69. package/src/assets/images/templates/custom-template.tsx +23 -0
  70. package/src/assets/images/templates/designer.tsx +43 -0
  71. package/src/assets/images/templates/dot.tsx +22 -0
  72. package/src/assets/images/templates/download-v2.svg +4 -0
  73. package/src/assets/images/templates/download.svg +4 -0
  74. package/src/assets/images/templates/dummy-template.tsx +76 -0
  75. package/src/assets/images/templates/dynamic-field.tsx +119 -0
  76. package/src/assets/images/templates/edit-pencil-icon.tsx +21 -0
  77. package/src/assets/images/templates/edit.svg +3 -0
  78. package/src/assets/images/templates/epo-icon.tsx +16 -0
  79. package/src/assets/images/templates/field.tsx +29 -0
  80. package/src/assets/images/templates/gsv-icon.tsx +31 -0
  81. package/src/assets/images/templates/info-icon.tsx +37 -0
  82. package/src/assets/images/templates/left-arrow.svg +17 -0
  83. package/src/assets/images/templates/pencil.svg +3 -0
  84. package/src/assets/images/templates/personal-letter.tsx +53 -0
  85. package/src/assets/images/templates/postcard.tsx +32 -0
  86. package/src/assets/images/templates/professional-letter.tsx +53 -0
  87. package/src/assets/images/templates/qr-code.tsx +13 -0
  88. package/src/assets/images/templates/real-penned-letters.tsx +57 -0
  89. package/src/assets/images/templates/right-arrow.svg +17 -0
  90. package/src/assets/images/templates/size-image-lg.tsx +20 -0
  91. package/src/assets/images/templates/size-image-mid.tsx +20 -0
  92. package/src/assets/images/templates/size-image-xl.tsx +20 -0
  93. package/src/assets/images/templates/size-image.tsx +20 -0
  94. package/src/assets/images/templates/snap-pack.tsx +67 -0
  95. package/src/assets/images/templates/template-default-design.tsx +21 -0
  96. package/src/assets/images/templates/trash-upload.svg +3 -0
  97. package/src/assets/images/templates/trash.svg +3 -0
  98. package/src/assets/images/templates/tri-fold-self-mailers.tsx +93 -0
  99. package/src/assets/images/templates/upload-image.svg +10 -0
  100. package/src/assets/images/templates/x.svg +3 -0
  101. package/src/assets/images/thumbnails/one.svg +9 -0
  102. package/src/assets/images/tooltip/tool-arrow.tsx +25 -0
  103. package/src/components/CreateTemplate/V2/index.tsx +525 -0
  104. package/src/components/CreateTemplate/V2/styles.scss +372 -0
  105. package/src/components/CreateTemplate/index.tsx +508 -0
  106. package/src/components/CreateTemplate/styles.scss +404 -0
  107. package/src/components/GenericUIBlocks/Button/index.tsx +54 -0
  108. package/src/components/GenericUIBlocks/Button/styles.scss +43 -0
  109. package/src/components/GenericUIBlocks/CircularProgress/index.tsx +18 -0
  110. package/src/components/GenericUIBlocks/CircularProgress/styles.scss +93 -0
  111. package/src/components/GenericUIBlocks/CustomTooltip/index.tsx +88 -0
  112. package/src/components/GenericUIBlocks/CustomTooltip/styles.scss +19 -0
  113. package/src/components/GenericUIBlocks/Dialog/V2/index.tsx +227 -0
  114. package/src/components/GenericUIBlocks/Dialog/V2/styles.scss +289 -0
  115. package/src/components/GenericUIBlocks/Dialog/index.tsx +185 -0
  116. package/src/components/GenericUIBlocks/Dialog/styles.scss +227 -0
  117. package/src/components/GenericUIBlocks/Divider/index.tsx +12 -0
  118. package/src/components/GenericUIBlocks/Divider/styles.scss +7 -0
  119. package/src/components/GenericUIBlocks/GeneralSelect/index.tsx +114 -0
  120. package/src/components/GenericUIBlocks/GeneralSelect/styles.scss +406 -0
  121. package/src/components/GenericUIBlocks/GeneralTooltip/index.tsx +25 -0
  122. package/src/components/GenericUIBlocks/GeneralTooltip/styles.scss +20 -0
  123. package/src/components/GenericUIBlocks/GenericSnackbar/Toast/index.tsx +91 -0
  124. package/src/components/GenericUIBlocks/GenericSnackbar/Toast/styles.scss +92 -0
  125. package/src/components/GenericUIBlocks/Grid/index.tsx +82 -0
  126. package/src/components/GenericUIBlocks/Input/index.tsx +269 -0
  127. package/src/components/GenericUIBlocks/Input/styles.scss +332 -0
  128. package/src/components/GenericUIBlocks/Tabs/index.tsx +71 -0
  129. package/src/components/GenericUIBlocks/Tabs/styles.scss +42 -0
  130. package/src/components/GenericUIBlocks/Typography/index.tsx +18 -0
  131. package/src/components/GenericUIBlocks/Typography/styles.scss +27 -0
  132. package/src/components/SidePanel/CustomAddOns/index.tsx +342 -0
  133. package/src/components/SidePanel/CustomAddOns/styles.scss +86 -0
  134. package/src/components/SidePanel/CustomBlockColors/index.tsx +211 -0
  135. package/src/components/SidePanel/CustomBlockColors/styles.scss +80 -0
  136. package/src/components/SidePanel/CustomFields/customFieldSection.tsx +547 -0
  137. package/src/components/SidePanel/CustomFields/styles.scss +64 -0
  138. package/src/components/SidePanel/CustomQRCode/V2/QRCodeModal/index.tsx +172 -0
  139. package/src/components/SidePanel/CustomQRCode/V2/QRCodeModal/styles.scss +46 -0
  140. package/src/components/SidePanel/CustomQRCode/index.tsx +1070 -0
  141. package/src/components/SidePanel/CustomQRCode/styles.scss +149 -0
  142. package/src/components/SidePanel/CustomUploads/V2/index.tsx +542 -0
  143. package/src/components/SidePanel/CustomUploads/V2/styles.scss +267 -0
  144. package/src/components/SidePanel/CustomUploads/index.tsx +301 -0
  145. package/src/components/SidePanel/Templates/ModalGallery/HireDesigner/index.tsx +424 -0
  146. package/src/components/SidePanel/Templates/ModalGallery/HireDesigner/styles.scss +180 -0
  147. package/src/components/SidePanel/Templates/ModalGallery/V2/index.tsx +235 -0
  148. package/src/components/SidePanel/Templates/ModalGallery/V2/styles.scss +244 -0
  149. package/src/components/SidePanel/Templates/ModalGallery/index.tsx +231 -0
  150. package/src/components/SidePanel/Templates/SideBarGallery/index.tsx +233 -0
  151. package/src/components/SidePanel/Templates/SideBarGallery/styles.scss +152 -0
  152. package/src/components/SidePanel/Templates/TemplatesCard/V2/index.tsx +149 -0
  153. package/src/components/SidePanel/Templates/TemplatesCard/V2/styles.scss +156 -0
  154. package/src/components/SidePanel/Templates/TemplatesCard/index.tsx +160 -0
  155. package/src/components/SidePanel/Templates/TemplatesCard/styles.scss +98 -0
  156. package/src/components/SidePanel/Templates/customTemplateSection.tsx +793 -0
  157. package/src/components/SidePanel/Templates/styles.scss +244 -0
  158. package/src/components/SidePanel/index.tsx +160 -0
  159. package/src/components/TemplateBuilder/index.tsx +585 -0
  160. package/src/components/TemplateBuilder/styles.scss +100 -0
  161. package/src/components/TemplateTypes/index.tsx +96 -0
  162. package/src/components/TemplateTypes/styles.scss +91 -0
  163. package/src/components/TopNavigation/ConfirmNavigateDialog/index.tsx +81 -0
  164. package/src/components/TopNavigation/ConfirmNavigateDialog/styles.scss +123 -0
  165. package/src/components/TopNavigation/DuplicateTemplateModal.tsx +103 -0
  166. package/src/components/TopNavigation/EditTemplateNameModel/index.tsx +71 -0
  167. package/src/components/TopNavigation/EditTemplateNameModel/styles.scss +88 -0
  168. package/src/components/TopNavigation/SaveTemplateModel/index.tsx +201 -0
  169. package/src/components/TopNavigation/SaveTemplateModel/styles.scss +128 -0
  170. package/src/components/TopNavigation/index.tsx +938 -0
  171. package/src/components/TopNavigation/styles.scss +303 -0
  172. package/src/importMeta.d.ts +31 -0
  173. package/src/index.scss +131 -0
  174. package/src/index.tsx +238 -0
  175. package/src/libs/test.ts +7 -0
  176. package/src/redux/actions/action-types.ts +52 -0
  177. package/src/redux/actions/customQRCodeActions.ts +54 -0
  178. package/src/redux/actions/snackbarActions.ts +16 -0
  179. package/src/redux/actions/templateActions.ts +236 -0
  180. package/src/redux/reducers/customFieldReducer.ts +99 -0
  181. package/src/redux/reducers/customQRCodeReducer.ts +58 -0
  182. package/src/redux/reducers/index.ts +15 -0
  183. package/src/redux/reducers/snackbarReducer.ts +40 -0
  184. package/src/redux/reducers/templateReducer.ts +485 -0
  185. package/src/redux/store.ts +18 -0
  186. package/src/styles/colors.scss +61 -0
  187. package/src/test/mocks.js +89 -0
  188. package/src/test/setupJest.js +1 -0
  189. package/src/utils/api.ts +36 -0
  190. package/src/utils/constants.ts +182 -0
  191. package/src/utils/customStyles.ts +45 -0
  192. package/src/utils/fetchWrapper.ts +73 -0
  193. package/src/utils/fonts.json +1597 -0
  194. package/src/utils/helper.ts +205 -0
  195. package/src/utils/local-storage.ts +15 -0
  196. package/src/utils/message.ts +162 -0
  197. package/src/utils/products.ts +186 -0
  198. package/src/utils/template-builder.ts +328 -0
  199. package/src/utils/templateIdentifierArea/biFold.ts +107 -0
  200. package/src/utils/templateIdentifierArea/index.ts +35 -0
  201. package/src/utils/templateIdentifierArea/personal.ts +107 -0
  202. package/src/utils/templateIdentifierArea/postCards.ts +163 -0
  203. package/src/utils/templateIdentifierArea/professional.ts +125 -0
  204. package/src/utils/templateIdentifierArea/snapPack.ts +107 -0
  205. package/src/utils/templateIdentifierArea/triFold.ts +107 -0
  206. package/src/utils/templateRestrictedArea/biFold.ts +329 -0
  207. package/src/utils/templateRestrictedArea/nonWindowProfessional.ts +90 -0
  208. package/src/utils/templateRestrictedArea/personal.ts +90 -0
  209. package/src/utils/templateRestrictedArea/postCard.ts +334 -0
  210. package/src/utils/templateRestrictedArea/postCardJumbo.tsx +408 -0
  211. package/src/utils/templateRestrictedArea/professional.ts +318 -0
  212. package/src/utils/templateRestrictedArea/realPenned.ts +233 -0
  213. package/src/utils/templateRestrictedArea/snapPack.ts +1009 -0
  214. package/src/utils/templateRestrictedArea/triFold.ts +330 -0
  215. package/src/utils/templateSafetyBorders/biFold.ts +91 -0
  216. package/src/utils/templateSafetyBorders/index.ts +43 -0
  217. package/src/utils/templateSafetyBorders/personal.ts +41 -0
  218. package/src/utils/templateSafetyBorders/postCards.ts +259 -0
  219. package/src/utils/templateSafetyBorders/professional.ts +78 -0
  220. package/src/utils/templateSafetyBorders/snapPack.ts +165 -0
  221. package/src/utils/templateSafetyBorders/triFold.ts +114 -0
  222. package/src/utils/templateSafetyBorders/types.d.ts +68 -0
  223. package/src/utils/types.ts +12 -0
  224. package/src/v2Theme.scss +142 -0
  225. package/tsconfig.json +29 -0
  226. package/tsconfig.node.json +12 -0
  227. package/update-version.js +23 -0
  228. package/version.js +1 -0
  229. package/vite.config.ts +8 -0
  230. package/webpack.config.js +80 -0
@@ -0,0 +1,18 @@
1
+ import React, { CSSProperties, ReactNode } from 'react';
2
+
3
+ // styles
4
+ import './styles.scss';
5
+
6
+ interface TypographyProps {
7
+ children?: ReactNode;
8
+ style?: CSSProperties;
9
+ variant?: keyof JSX.IntrinsicElements;
10
+ className?: string;
11
+ }
12
+
13
+ const Typography: React.FC<TypographyProps> = ({ children = '', style = {}, variant = 'p', className = '' }) => {
14
+ const Tag = variant || 'p';
15
+ return <Tag className={className} style={style}>{children}</Tag>;
16
+ };
17
+
18
+ export default Typography;
@@ -0,0 +1,27 @@
1
+ .basic-typo{
2
+ font-weight: 500;
3
+ margin: 0;
4
+ width: 100%;
5
+ margin: 0;
6
+ h1{
7
+ font-size: 50px;
8
+ }
9
+ h2{
10
+ font-size: 45px;
11
+ }
12
+ h3{
13
+ font-size: 40px;
14
+ }
15
+ h4{
16
+ font-size: 35px;
17
+ }
18
+ h5{
19
+ font-size: 30px;
20
+ }
21
+ h6{
22
+ font-size: 25px;
23
+ }
24
+ p{
25
+ font-size: 18px;
26
+ }
27
+ }
@@ -0,0 +1,342 @@
1
+ import React, { CSSProperties, useEffect, useState } from 'react';
2
+
3
+ // Plotno Library Imports
4
+ import { observer } from 'mobx-react-lite';
5
+ import { SectionTab } from 'polotno/side-panel';
6
+ import type { StoreType } from 'polotno/model/store';
7
+ import type { TemplatesSection } from 'polotno/side-panel';
8
+
9
+ // Hooks
10
+ import { useDispatch, useSelector } from 'react-redux';
11
+ import { AppDispatch, RootState } from '../../../redux/store';
12
+
13
+ // Actions
14
+ import { failure, success } from '../../../redux/actions/snackbarActions';
15
+ import { SET_ROS_OFFER_PERCENTAGE } from '../../../redux/actions/action-types';
16
+
17
+ // Utils
18
+ import { MESSAGES } from '../../../utils/message';
19
+ import { copyToClipboard, getEnv, getIsSandbox, getPublicApiKey } from '../../../utils/helper';
20
+
21
+ // Components
22
+ import Input from '../../GenericUIBlocks/Input';
23
+
24
+ // MUI Components
25
+ import GeneralTootip from '../../GenericUIBlocks/GeneralTooltip';
26
+ import Typography from '../../GenericUIBlocks/Typography';
27
+ import Button from '../../../components/GenericUIBlocks/Button';
28
+
29
+ // Dummy Image for GSV
30
+ import {
31
+ DEMO_PO_GENERATOR_URL,
32
+ DEMO_S3_URL,
33
+ GOOGLE_STREET_VIEW_IMAGE_URL,
34
+ PROD_PO_GENERATOR_URL,
35
+ PROD_S3_URL,
36
+ STAGE_PO_GENERATOR_URL,
37
+ STAGE_S3_URL,
38
+ } from '../../../utils/constants';
39
+
40
+ // Icons
41
+ //@ts-ignore
42
+ import CustomAddOnIcon from '../../../assets/images/templates/custom-add-on-icon';
43
+ import ContentCopyIcon from '../../../assets/images/templates/content-copy-icon';
44
+ import InfoIcon from '../../../assets/images/templates/info-icon';
45
+ import GsvIcon from '../../../assets/images/templates/gsv-icon';
46
+ import EpoIcon from '../../../assets/images/templates/epo-icon';
47
+
48
+ import './styles.scss';
49
+
50
+ type SideSection = typeof TemplatesSection;
51
+
52
+ type CustomAddOnsSectionProps = {
53
+ store: StoreType;
54
+ onClick: () => void;
55
+ allowedAddOns?: any;
56
+ propertyOfferCost?: number;
57
+ customPropertyOfferCost?: number;
58
+ gsvCost?: number;
59
+ };
60
+
61
+ const iconButtonStyles: CSSProperties = {
62
+ backgroundColor: 'transparent',
63
+ position: 'absolute',
64
+ top: "10px",
65
+ right: "10px",
66
+ height: "auto"
67
+ };
68
+
69
+ // define the new custom section
70
+ const CustomAddOns: SideSection = {
71
+ name: 'CustomAddOns',
72
+ Tab: observer(
73
+ (props: { store: StoreType; active: boolean; onClick: () => void }) => (
74
+ <SectionTab name="Add On's" {...props} iconSize={20}>
75
+ <CustomAddOnIcon />
76
+ </SectionTab>
77
+ )
78
+ ) as SideSection['Tab'],
79
+
80
+ // we need observer to update component automatically on any store changes
81
+ Panel: observer(({ store, allowedAddOns, propertyOfferCost, gsvCost, customPropertyOfferCost }: CustomAddOnsSectionProps) => {
82
+ const [customRosValue, setCustomRosValue] = useState('');
83
+
84
+ const currentTemplate = useSelector(
85
+ (state: RootState) => state.templates.template
86
+ ) as Record<string, any>;
87
+
88
+ const currentOfferPercentage = useSelector(
89
+ (state: RootState) => state.templates.offerPercentage
90
+ ) as string;
91
+
92
+ const dispatch: AppDispatch = useDispatch();
93
+
94
+ const googleStreetViewSrc: string =
95
+ (getEnv() === 'local' || getEnv() === 'staging'
96
+ ? STAGE_S3_URL
97
+ : getIsSandbox()
98
+ ? DEMO_S3_URL
99
+ : PROD_S3_URL) + GOOGLE_STREET_VIEW_IMAGE_URL;
100
+
101
+ const propertyOfferGeneratorURL: string =
102
+ (getEnv() === 'local' || getEnv() === 'staging'
103
+ ? STAGE_PO_GENERATOR_URL
104
+ : getIsSandbox()
105
+ ? DEMO_PO_GENERATOR_URL
106
+ : PROD_PO_GENERATOR_URL) + `?apiKey=${getPublicApiKey()}`;
107
+
108
+ const PropertyOfferfieldValue = '{{ROS.PROPERTY_OFFER}}';
109
+
110
+ const handleAddElementOnScreen = (
111
+ event: Event,
112
+ value: string,
113
+ type: string
114
+ ) => {
115
+ event.preventDefault();
116
+ const currentPage = store.activePage.toJSON();
117
+ if (type === 'gsv') {
118
+ if (
119
+ currentPage?.children?.length &&
120
+ currentPage.children.find(
121
+ (item: any) => item.id === `gsv-image_${store.activePage.id}`
122
+ )
123
+ ) {
124
+ dispatch(failure(MESSAGES.TEMPLATE.GSV_RESTRICT_ONE_PER_PAGE));
125
+ return;
126
+ }
127
+ store.activePage?.addElement({
128
+ id: `gsv-image_${store.activePage.id}`,
129
+ type: 'image',
130
+ src: googleStreetViewSrc,
131
+ width: 287,
132
+ height: 188,
133
+ contentEditable: false,
134
+ keepRatio: true,
135
+ opacity: 1,
136
+ custom: {
137
+ elementType: value,
138
+ },
139
+ });
140
+ } else if (type === 'rpo') {
141
+ const randomizedId = Math.random().toString(36).substring(2, 7);
142
+ store.activePage.addElement({
143
+ id: `ros_${randomizedId}`,
144
+ type: 'text',
145
+ x: 100,
146
+ y: 100,
147
+ text: value,
148
+ width: 175,
149
+ contentEditable: false,
150
+ });
151
+ } else if (type === 'custom_rpo') {
152
+ if (!customRosValue) {
153
+ percentageRequired();
154
+ return;
155
+ }
156
+ if (!validCustomRosRange()) return false;
157
+ dispatch({ type: SET_ROS_OFFER_PERCENTAGE, payload: customRosValue });
158
+ const randomizedId = Math.random().toString(36).substring(2, 7);
159
+ store.activePage.addElement({
160
+ id: `ros_${randomizedId}`,
161
+ type: 'text',
162
+ x: 100,
163
+ y: 100,
164
+ text: value,
165
+ width: 175,
166
+ contentEditable: false,
167
+ custom: {
168
+ ros_custom_value: customRosValue
169
+ }
170
+ });
171
+ }
172
+ };
173
+
174
+ const percentageRequired = () => dispatch(failure(`Please enter the offer percentage`));
175
+
176
+ const validCustomRosRange = () => {
177
+ if(+customRosValue < 1 || +customRosValue > 100) {
178
+ dispatch(failure(`Please enter an offer percentage between 1 and 100`));
179
+ return false;
180
+ }
181
+ return true;
182
+ }
183
+
184
+ const copyPropertyOfferField = (e: React.MouseEvent<HTMLDivElement>) => {
185
+ e.stopPropagation();
186
+ copyToClipboard(PropertyOfferfieldValue);
187
+ dispatch({ type: SET_ROS_OFFER_PERCENTAGE, payload: customRosValue });
188
+ dispatch(success(`ROS Property Offer Copied`));
189
+ };
190
+
191
+ useEffect(() => {
192
+ if (!allowedAddOns?.includes('custom_property_offer')) return;
193
+ if (currentOfferPercentage || currentOfferPercentage === "") {
194
+ setCustomRosValue(currentOfferPercentage);
195
+ } else if (currentTemplate?.meta?.rosOfferPercentage) {
196
+ setCustomRosValue(currentTemplate.meta.rosOfferPercentage);
197
+ dispatch({ type: SET_ROS_OFFER_PERCENTAGE, payload: currentTemplate.meta.rosOfferPercentage });
198
+ }
199
+ }, [allowedAddOns, currentOfferPercentage, currentTemplate?.meta?.rosOfferPercentage]);
200
+
201
+ return (
202
+ <div className="dynamic-content">
203
+ <div className="dynamic-content__top">
204
+ <div>
205
+ <span className="title">{MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.TITLE}</span>{' '}
206
+ <InfoIcon fill="var(--primary-color)" className="infoIcon" />
207
+ <GeneralTootip
208
+ anchorSelect=".infoIcon"
209
+ place="bottom"
210
+ className='custom-add-on-tooltip'
211
+ title={MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.DESCRIPTION}
212
+ />
213
+ </div>
214
+ </div>
215
+ {(!allowedAddOns || allowedAddOns?.includes('gsv')) && (
216
+ <div
217
+ className="addonBox"
218
+ onClick={(event: any) =>
219
+ handleAddElementOnScreen(event, 'GOOGLE_STREET_VIEW', 'gsv')
220
+ }
221
+ >
222
+ <GsvIcon />
223
+ <Typography>{MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.GSV.TITLE}</Typography>
224
+ <Typography>
225
+ {gsvCost && typeof gsvCost === 'number'
226
+ ? '$' + gsvCost + MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.GSV.CUSTOM_PRICE
227
+ : MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.GSV.DESCRIPTION}
228
+ </Typography>
229
+ </div>
230
+ )}
231
+ {allowedAddOns?.includes('property_offer') && (
232
+ <div
233
+ className="addonBox"
234
+ style={{position: 'relative'}}
235
+ onClick={(event: any) =>
236
+ handleAddElementOnScreen(event, PropertyOfferfieldValue, 'rpo')
237
+ }
238
+ >
239
+ <Button
240
+ style={iconButtonStyles}
241
+ onClick={copyPropertyOfferField}
242
+ backdrop={false}
243
+ >
244
+ <ContentCopyIcon className="copy" />
245
+ </Button>
246
+ <EpoIcon />
247
+ <Typography>{MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.PROPERTY_OFFER.TITLE}</Typography>
248
+ <Typography className="no-margin">
249
+ {MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.PROPERTY_OFFER.DESCRIPTION}
250
+ </Typography>
251
+ <Typography>
252
+ {propertyOfferCost && typeof propertyOfferCost === 'number'
253
+ ? '$' + propertyOfferCost + MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.PROPERTY_OFFER.CUSTOM_PRICE
254
+ : MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.PROPERTY_OFFER.PRICE}
255
+ </Typography>
256
+ </div>
257
+ )}
258
+ {allowedAddOns?.includes('custom_property_offer') && (
259
+ <div
260
+ className="addonBox"
261
+ style={{position: 'relative'}}
262
+ onClick={(event: any) =>
263
+ handleAddElementOnScreen(
264
+ event,
265
+ PropertyOfferfieldValue,
266
+ 'custom_rpo'
267
+ )
268
+ }
269
+ >
270
+ <Button
271
+ style={iconButtonStyles}
272
+ onClick={customRosValue ? copyPropertyOfferField : percentageRequired}
273
+ backdrop={false}
274
+ >
275
+ <ContentCopyIcon className="copy" />
276
+ </Button>
277
+ <EpoIcon />
278
+ <Typography> {customPropertyOfferCost && typeof customPropertyOfferCost === 'number'
279
+ ? `Add an offer (+$${customPropertyOfferCost} per mail piece)`
280
+ : MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.PROPERTY_OFFER.CUSTOM.TITLE}</Typography>
281
+ <Typography variant="h3" className="no-margin s-font">
282
+ {MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.PROPERTY_OFFER.CUSTOM.DESCRIPTION}
283
+ </Typography>
284
+ <div style={{ margin: '12px' }} className='plain-text'>
285
+ <a href={propertyOfferGeneratorURL} target='_blank'
286
+ onClick={(e) => {
287
+ e.stopPropagation()
288
+ }}
289
+ style={{
290
+ color: 'var(--primary-color)',
291
+ }}
292
+ >
293
+ {MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.PROPERTY_OFFER.CUSTOM.CLICK}
294
+ </a>{' '}
295
+ {MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.PROPERTY_OFFER.CUSTOM.CSV_IMPORT}
296
+ </div>
297
+ <Typography className="no-margin s-font">
298
+ {customPropertyOfferCost && typeof customPropertyOfferCost === 'number'
299
+ ? `$${customPropertyOfferCost} per mail piece`
300
+ : MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.PROPERTY_OFFER.CUSTOM.CUSTOM_PRICE}
301
+ </Typography>
302
+ <div
303
+ onClick={(e) => e.stopPropagation()}
304
+ style={{ width: '18rem', marginBottom: '15px' }}
305
+ >
306
+ {allowedAddOns?.includes('custom_property_offer') && (
307
+ <Input
308
+ type="number"
309
+ onlyNumber={true}
310
+ placeholder={'Type in a Number between 1-100'}
311
+ qrField={true}
312
+ value={customRosValue}
313
+ onChange={(e) => {
314
+ const val = e.target.value;
315
+ if (val === "") {
316
+ setCustomRosValue("");
317
+ dispatch({ type: SET_ROS_OFFER_PERCENTAGE, payload: "" });
318
+ return;
319
+ }
320
+ const num = Number(val);
321
+ if (!isNaN(num) && num >= 1 && num <= 100) {
322
+ setCustomRosValue(val);
323
+ dispatch({ type: SET_ROS_OFFER_PERCENTAGE, payload: val });
324
+ } else {
325
+ dispatch(failure("Please enter a number between 1 and 100"));
326
+ }
327
+ }}
328
+ />
329
+ )}
330
+ </div>
331
+ </div>
332
+ )}
333
+ <div className='no-result-addOns'>
334
+ {!allowedAddOns || allowedAddOns?.length === 0 && <Typography>{MESSAGES.TEMPLATE.CUSTOM_ADD_ONS.NO_RESULT}</Typography>}
335
+ </div>
336
+ <GeneralTootip anchorSelect=".copy" place="bottom" title="Copy" />
337
+ </div>
338
+ );
339
+ }) as unknown as SideSection['Panel'],
340
+ };
341
+
342
+ export default CustomAddOns;
@@ -0,0 +1,86 @@
1
+ .gsv-contact-element {
2
+ width: 100%;
3
+ border-radius: 3px;
4
+ border: 0.5px solid #303030;
5
+ text-align: center;
6
+ height: 100px;
7
+ line-height: 35px;
8
+ margin: 5px auto;
9
+ display: flex;
10
+ justify-content: center;
11
+
12
+ span {
13
+ transform: translateY(30%);
14
+ }
15
+ }
16
+
17
+ .addonBox {
18
+ border-radius: 10px;
19
+ border: 1px solid #303030;
20
+ display: flex !important;
21
+ justify-content: center;
22
+ align-items: center;
23
+ flex-direction: column;
24
+ margin-bottom: 10px;
25
+ cursor: pointer;
26
+
27
+ .no-margin {
28
+ margin: 0 !important;
29
+ }
30
+
31
+ p {
32
+ font-size: 16px;
33
+ font-style: normal;
34
+ color: #303030;
35
+ font-weight: 800;
36
+
37
+ &:last-child {
38
+ font-weight: 500;
39
+ }
40
+ }
41
+
42
+ .s-font {
43
+ font-weight: 600;
44
+ font-size: 14px;
45
+ }
46
+
47
+ .plain-text {
48
+ font-weight: 300;
49
+ font-size: 14px;
50
+ text-align: center;
51
+ }
52
+
53
+ svg {
54
+ width: 100px;
55
+ height: 120px;
56
+ opacity: 0.5;
57
+ }
58
+
59
+ .copy {
60
+ width: 30px !important;
61
+ height: 30px !important;
62
+ }
63
+ }
64
+
65
+ .hidden-important {
66
+ display: block !important;
67
+ }
68
+
69
+ .custom-po {
70
+ margin-top: -70% !important;
71
+ }
72
+
73
+ input[type="number"]::-webkit-outer-spin-button,
74
+ input[type="number"]::-webkit-inner-spin-button {
75
+ -webkit-appearance: none;
76
+ margin: 0;
77
+ }
78
+ input[type="number"] {
79
+ -moz-appearance: textfield;
80
+ }
81
+
82
+ .no-result-addOns {
83
+ display: flex;
84
+ justify-content: center;
85
+ margin: 3px;
86
+ }
@@ -0,0 +1,211 @@
1
+ import React, { useState, useEffect } from 'react';
2
+
3
+ // Polotno and third party libraries
4
+ import { observer } from 'mobx-react-lite';
5
+ import { SectionTab } from 'polotno/side-panel';
6
+ import type { StoreType } from 'polotno/model/store';
7
+ import type { TemplatesSection } from 'polotno/side-panel';
8
+
9
+ // Hooks
10
+ import { useDispatch, useSelector } from 'react-redux';
11
+ import { AppDispatch, RootState } from '../../../redux/store';
12
+
13
+ // Utils
14
+ import { DEFAULT_RESTRICTED_AREA_BG, RESTRICTED_AREA_COLOR_PALETTE } from '../../../utils/constants';
15
+
16
+ // Actions
17
+ import { SET_BLOCK_COLOR } from '../../../redux/actions/action-types';
18
+ import { success } from '../../../redux/actions/snackbarActions';
19
+
20
+ // Components
21
+ import Typography from '../../GenericUIBlocks/Typography';
22
+
23
+ // Icons
24
+ // @ts-ignore
25
+ import AddressBlockIcon from '../../../assets/images/templates/address-block-icon';
26
+
27
+ // styles
28
+ import './styles.scss';
29
+
30
+ type SideSection = typeof TemplatesSection;
31
+
32
+ interface BlockColorsSectionProps {
33
+ store: StoreType;
34
+ }
35
+
36
+ const BlockColors: SideSection = {
37
+ name: 'address-block',
38
+ Tab: observer(
39
+ (props: { store: StoreType; active: boolean; onClick: () => void }) => (
40
+ <SectionTab name="Address Block" {...props} iconSize={20}>
41
+ <AddressBlockIcon />
42
+ </SectionTab>
43
+ )
44
+ ) as SideSection['Tab'],
45
+ Panel: observer(({ store }: BlockColorsSectionProps) => {
46
+ const dispatch: AppDispatch = useDispatch();
47
+
48
+ const blockColor = useSelector(
49
+ (state: RootState) => state.templates.blockColor
50
+ );
51
+
52
+ const product = useSelector(
53
+ (state: RootState) => state.templates.product
54
+ ) as Record<string, any>;
55
+
56
+ const currentProductId = +product?.id;
57
+
58
+ const [selectedColor, setSelectedColor] = useState(
59
+ blockColor || DEFAULT_RESTRICTED_AREA_BG
60
+ );
61
+
62
+ useEffect(() => {
63
+ if (blockColor && blockColor !== selectedColor) {
64
+ setSelectedColor(blockColor);
65
+ }
66
+ }, [blockColor]);
67
+
68
+ const handleColorChange = (color: string) => {
69
+
70
+ setSelectedColor(color);
71
+ const pages = store.pages;
72
+ let updated = false;
73
+ let isSnapPack = false;
74
+
75
+ const basicRestrictedIds = [
76
+ 'figure-1',
77
+ 'figure-2',
78
+ 'figure-3',
79
+ ]
80
+
81
+ const snapPackRestrictedIds = [
82
+ 'recipient-address-area',
83
+ 'recipient-address-box',
84
+ 'return-address-box',
85
+ 'postage-indicia-box',
86
+ 'barcode-box',
87
+ ]
88
+
89
+ const restrictedIds = [
90
+ ...basicRestrictedIds,
91
+ ...snapPackRestrictedIds
92
+ ];
93
+ const updatedPages: any = [];
94
+
95
+ let lastCustomFigures: any = [];
96
+ let lastPageIndex = 0;
97
+
98
+ pages.forEach((page, index) => {
99
+ const pageData = page.toJSON();
100
+ const children = pageData.children;
101
+ lastPageIndex = index;
102
+
103
+ // Remove previously added custom-figure-* elements
104
+ const filteredChildren = children.filter(
105
+ (el: any) => !restrictedIds.some(id => el.id === `custom-${id}`)
106
+ );
107
+
108
+ // Find restricted elements
109
+ const restrictedFigures = restrictedIds
110
+ .map((id) => children.find((el: any) => el.type === 'figure' && el.id === id))
111
+ .filter(Boolean); // Remove undefined
112
+
113
+ if (restrictedFigures.length) {
114
+ restrictedFigures.forEach(el => {
115
+ if (snapPackRestrictedIds.includes(el.id)) {
116
+ isSnapPack = true;
117
+ el.cornerRadius = 5;
118
+ el.preFill = color;
119
+ if (el.id === 'recipient-address-box') {
120
+ el.opacity = 0;
121
+ }
122
+ } else if (basicRestrictedIds.includes(el.id)) {
123
+ el.cornerRadius = 5;
124
+ }
125
+ });
126
+ }
127
+
128
+ let customFigures: any[] = [];
129
+
130
+ if (restrictedFigures?.length > 0) {
131
+ customFigures = restrictedFigures.map((el: any) => {
132
+ const json = el.toJSON?.() || el;
133
+ return {
134
+ ...json,
135
+ id: `custom-${json.id}`,
136
+ type: json.type,
137
+ fill: color,
138
+ cornerRadius: 5,
139
+ ...(isSnapPack && {
140
+ custom: {
141
+ preFill: color,
142
+ replaceBg: true
143
+ }
144
+ })
145
+ };
146
+ });
147
+
148
+ // Save for adding after store reload
149
+ lastCustomFigures = customFigures;
150
+
151
+ // Append to children
152
+ customFigures.forEach(fig => {
153
+ const matchId = fig.id.replace("custom-", ""); // remove prefix
154
+ const targetIndex = filteredChildren.findIndex((item: any) => item.id === matchId);
155
+
156
+ if (targetIndex !== -1) {
157
+ filteredChildren.splice(targetIndex + 1, 0, fig);
158
+ }
159
+ });
160
+ }
161
+
162
+ updatedPages.push({
163
+ ...pageData,
164
+ children: filteredChildren,
165
+ });
166
+ });
167
+
168
+ // Load updated pages into store
169
+ store.loadJSON({ pages: updatedPages });
170
+
171
+ // Add custom elements back to the last modified page
172
+ if (lastCustomFigures.length) {
173
+ store.pages[lastPageIndex].addElement(lastCustomFigures);
174
+ updated = true;
175
+ }
176
+
177
+ if (updated) {
178
+ dispatch({ type: SET_BLOCK_COLOR, payload: color });
179
+ dispatch(success('Address block color updated successfully'));
180
+ }
181
+ };
182
+
183
+ return (
184
+ <div className="block-colors-section">
185
+ <div className="block-colors-header">
186
+ <Typography className="title">
187
+ Optional: Choose a background color for the mailing address block.
188
+ </Typography>
189
+ <Typography className="description">
190
+ These approved options enhance your mailer's look while maintaining
191
+ USPS compliance.
192
+ </Typography>
193
+ </div>
194
+
195
+ <div className="color-palette">
196
+ {RESTRICTED_AREA_COLOR_PALETTE.map((color, index) => (
197
+ <div
198
+ key={index}
199
+ className={`color-swatch ${selectedColor === color.value ? 'selected' : ''}`}
200
+ style={{ backgroundColor: color.value }}
201
+ onClick={() => handleColorChange(color.value)}
202
+ title={color.name}
203
+ />
204
+ ))}
205
+ </div>
206
+ </div>
207
+ );
208
+ }),
209
+ };
210
+
211
+ export default BlockColors;