avada-crossapp-banner 0.0.1

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/dist/index.js ADDED
@@ -0,0 +1,552 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var react = require('react');
7
+ var polaris = require('@shopify/polaris');
8
+ var polarisIcons = require('@shopify/polaris-icons');
9
+ var reactI18n = require('@shopify/react-i18n');
10
+
11
+ /**
12
+ * App handle constants
13
+ */
14
+ const APP_HANDLES = {
15
+ ORDER_LIMIT: 'orderLimit',
16
+ ACCESSIBILITY: 'accessibility',
17
+ COOKIE_BAR: 'cookieBar',
18
+ AGE_VERIFICATION: 'ageVerification',
19
+ };
20
+ /**
21
+ * App domains
22
+ */
23
+ const APP_DOMAINS = {
24
+ COOKIE_BAR: 'cookie.avada.io',
25
+ AGE_VERIFICATION: 'age-verification-b0fa4.firebaseapp.com',
26
+ ACCESSIBILITY: 'accessibility.avada.io',
27
+ };
28
+ /**
29
+ * Shopify app handles
30
+ */
31
+ const SHOPIFY_APP_HANDLES = {
32
+ COOKIE_BAR: 'avada-cookie-bar',
33
+ AGE_VERIFICATION: 'sun-age-verification-popup',
34
+ ACCESSIBILITY: 'avada-accessibility',
35
+ };
36
+ /**
37
+ * Shopify plans to exclude from cross-app promotion
38
+ */
39
+ const EXCLUDED_SHOPIFY_PLANS = [
40
+ 'partner_test',
41
+ 'affiliate',
42
+ 'staff',
43
+ 'frozen',
44
+ 'fraudulent',
45
+ 'cancelled',
46
+ 'paused',
47
+ ];
48
+ /**
49
+ * Countries to exclude from cross-app promotion
50
+ */
51
+ const EXCLUDED_COUNTRIES = ['IN', 'VN'];
52
+ /**
53
+ * Email suffixes to exclude
54
+ */
55
+ const EXCLUDED_EMAIL_SUFFIXES = ['shopify.com'];
56
+ /**
57
+ * Target storage types for display banner
58
+ */
59
+ const TARGET_STORAGE_TYPES = {
60
+ NEXT_DAY: 'next_day',
61
+ SEVEN_DAYS: '7_days_after',
62
+ THIRTY_DAYS: '30_days_after',
63
+ };
64
+ /**
65
+ * Get Shopify store name from domain
66
+ */
67
+ function getShopifyName(domain) {
68
+ return domain.replace('.myshopify.com', '');
69
+ }
70
+ /**
71
+ * Check if shop should be excluded from cross-app promotion
72
+ */
73
+ function shouldExcludeShop(shop) {
74
+ const { shopifyPlan, country, email } = shop;
75
+ if (shopifyPlan && EXCLUDED_SHOPIFY_PLANS.includes(shopifyPlan)) {
76
+ return true;
77
+ }
78
+ if (country && EXCLUDED_COUNTRIES.includes(country)) {
79
+ return true;
80
+ }
81
+ if (email) {
82
+ const emailLower = email.toLowerCase();
83
+ if (EXCLUDED_EMAIL_SUFFIXES.some(suffix => emailLower.endsWith(suffix))) {
84
+ return true;
85
+ }
86
+ }
87
+ return false;
88
+ }
89
+ /**
90
+ * Get Cookie Bar app data for cross-app promotion
91
+ */
92
+ function getCookieBarAppData(shop, options) {
93
+ var _a, _b;
94
+ const { shopifyDomain, cookieBarInstalled } = shop;
95
+ const shopName = getShopifyName(shopifyDomain);
96
+ return {
97
+ imageUrl: 'https://cdnapps.avada.io/crossApp/cookieBarGift.png',
98
+ imageAlt: 'Cookie Bar Gift',
99
+ translationKey: 'CrossApp.cookieApp',
100
+ planUrl: '/embed/subscription',
101
+ targetUrl: !cookieBarInstalled
102
+ ? `https://${APP_DOMAINS.COOKIE_BAR}/auth/shopify?shop=${shopifyDomain}`
103
+ : `https://admin.shopify.com/store/${shopName}/apps/${SHOPIFY_APP_HANDLES.COOKIE_BAR}/embed`,
104
+ showPlanBtn: (_a = options === null || options === void 0 ? void 0 : options.showPlanBtn) !== null && _a !== void 0 ? _a : false,
105
+ appHandle: APP_HANDLES.COOKIE_BAR,
106
+ bgColor: 'linear-gradient(90deg, #000926 0%, #0a1f3d 100%)',
107
+ isHideBanner: false,
108
+ eventPrefix: (_b = options === null || options === void 0 ? void 0 : options.eventPrefix) !== null && _b !== void 0 ? _b : 'cb',
109
+ bannerCloseKey: 'bannerCookieBarCloseCount',
110
+ };
111
+ }
112
+ /**
113
+ * Get Age Verification app data for cross-app promotion
114
+ */
115
+ function getAgeVerificationAppData(shop, options) {
116
+ var _a, _b;
117
+ const { shopifyDomain, ageVerificationInstalled, hideVerificationBanner } = shop;
118
+ const shopName = getShopifyName(shopifyDomain);
119
+ const utmSource = (_a = options === null || options === void 0 ? void 0 : options.utmSource) !== null && _a !== void 0 ? _a : 'crossapp';
120
+ return {
121
+ imageUrl: 'https://cdnapps.avada.io/crossApp/cookieBarGift.png',
122
+ imageAlt: 'Age Verification Person',
123
+ translationKey: 'CrossApp.ageVerification',
124
+ planUrl: '/embed/subscription',
125
+ targetUrl: !ageVerificationInstalled
126
+ ? `https://apps.shopify.com/sun-age-verification-popup?utm_source=${utmSource}&utm_medium=banner&utm_campaign=dashboard`
127
+ : `https://admin.shopify.com/store/${shopName}/apps/${SHOPIFY_APP_HANDLES.AGE_VERIFICATION}/embed?from=${utmSource}`,
128
+ showPlanBtn: false,
129
+ appHandle: APP_HANDLES.AGE_VERIFICATION,
130
+ bgColor: 'linear-gradient(135deg, #060101, #0e0101, #170101, #1e0101, #290101)',
131
+ isHideBanner: hideVerificationBanner !== null && hideVerificationBanner !== void 0 ? hideVerificationBanner : false,
132
+ eventPrefix: (_b = options === null || options === void 0 ? void 0 : options.eventPrefix) !== null && _b !== void 0 ? _b : 'av',
133
+ bannerCloseKey: 'bannerAgeVerificationCloseCount',
134
+ };
135
+ }
136
+ /**
137
+ * Get Accessibility app data for cross-app promotion
138
+ */
139
+ function getAccessibilityAppData(shop, options) {
140
+ var _a;
141
+ const { shopifyDomain, accessibilityInstalled } = shop;
142
+ const shopName = getShopifyName(shopifyDomain);
143
+ return {
144
+ imageUrl: 'https://cdnapps.avada.io/crossApp/accessibilityGift.png',
145
+ imageAlt: 'Accessibility App',
146
+ translationKey: 'CrossApp.accessibilityApp',
147
+ planUrl: '/embed/subscription',
148
+ targetUrl: !accessibilityInstalled
149
+ ? `https://${APP_DOMAINS.ACCESSIBILITY}/auth/shopify?shop=${shopifyDomain}`
150
+ : `https://admin.shopify.com/store/${shopName}/apps/${SHOPIFY_APP_HANDLES.ACCESSIBILITY}/embed`,
151
+ showPlanBtn: false,
152
+ appHandle: APP_HANDLES.ACCESSIBILITY,
153
+ bgColor: 'linear-gradient(90deg, #1a1a2e 0%, #16213e 100%)',
154
+ isHideBanner: false,
155
+ eventPrefix: (_a = options === null || options === void 0 ? void 0 : options.eventPrefix) !== null && _a !== void 0 ? _a : 'ac',
156
+ bannerCloseKey: 'bannerAccessibilityCloseCount',
157
+ };
158
+ }
159
+ /**
160
+ * Create cross-app config helper
161
+ */
162
+ function createCrossAppConfig(config) {
163
+ var _a, _b;
164
+ return {
165
+ appPrefix: config.appPrefix,
166
+ apiClient: config.apiClient,
167
+ storeDispatch: config.storeDispatch,
168
+ closeIcon: config.closeIcon,
169
+ bannerEventEndpoint: (_a = config.bannerEventEndpoint) !== null && _a !== void 0 ? _a : '/banner-event',
170
+ shopUpdateEndpoint: (_b = config.shopUpdateEndpoint) !== null && _b !== void 0 ? _b : '/shop',
171
+ };
172
+ }
173
+
174
+ /**
175
+ * Cookie Bar app icon
176
+ */
177
+ function CookieBarIcon({ size = 80 }) {
178
+ return (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 80 80", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("circle", { cx: "40", cy: "40", r: "36", fill: "#FFA726" }), jsxRuntime.jsx("circle", { cx: "28", cy: "32", r: "6", fill: "#8D6E63" }), jsxRuntime.jsx("circle", { cx: "48", cy: "28", r: "4", fill: "#8D6E63" }), jsxRuntime.jsx("circle", { cx: "52", cy: "48", r: "5", fill: "#8D6E63" }), jsxRuntime.jsx("circle", { cx: "32", cy: "52", r: "4", fill: "#8D6E63" }), jsxRuntime.jsx("circle", { cx: "44", cy: "44", r: "3", fill: "#8D6E63" })] }));
179
+ }
180
+ /**
181
+ * Age Verification app icon
182
+ */
183
+ function AgeVerificationIcon({ size = 80 }) {
184
+ return (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 80 80", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("circle", { cx: "40", cy: "40", r: "36", fill: "#EF5350" }), jsxRuntime.jsx("text", { x: "40", y: "48", textAnchor: "middle", fill: "white", fontSize: "24", fontWeight: "bold", fontFamily: "Arial, sans-serif", children: "18+" })] }));
185
+ }
186
+ /**
187
+ * Accessibility app icon
188
+ */
189
+ function AccessibilityIcon({ size = 80 }) {
190
+ return (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 80 80", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("circle", { cx: "40", cy: "40", r: "36", fill: "#2196F3" }), jsxRuntime.jsx("circle", { cx: "40", cy: "24", r: "6", fill: "white" }), jsxRuntime.jsx("path", { d: "M40 32V48M40 48L32 64M40 48L48 64M24 40H56", stroke: "white", strokeWidth: "4", strokeLinecap: "round", strokeLinejoin: "round" })] }));
191
+ }
192
+ /**
193
+ * Order Limit app icon
194
+ */
195
+ function OrderLimitIcon({ size = 80 }) {
196
+ return (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 80 80", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("circle", { cx: "40", cy: "40", r: "36", fill: "#9C27B0" }), jsxRuntime.jsx("path", { d: "M24 32H56L52 52H28L24 32Z", stroke: "white", strokeWidth: "3", fill: "none" }), jsxRuntime.jsx("circle", { cx: "32", cy: "58", r: "4", fill: "white" }), jsxRuntime.jsx("circle", { cx: "48", cy: "58", r: "4", fill: "white" })] }));
197
+ }
198
+ /**
199
+ * Default/fallback icon
200
+ */
201
+ function DefaultIcon({ size = 80 }) {
202
+ return (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 80 80", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("circle", { cx: "40", cy: "40", r: "36", fill: "#607D8B" }), jsxRuntime.jsx("text", { x: "40", y: "48", textAnchor: "middle", fill: "white", fontSize: "20", fontWeight: "bold", fontFamily: "Arial, sans-serif", children: "APP" })] }));
203
+ }
204
+ /**
205
+ * CrossApp icon component that renders the appropriate icon based on app handle
206
+ */
207
+ function CrossAppIcon({ app, size = 80 }) {
208
+ switch (app) {
209
+ case APP_HANDLES.COOKIE_BAR:
210
+ return jsxRuntime.jsx(CookieBarIcon, { size: size });
211
+ case APP_HANDLES.AGE_VERIFICATION:
212
+ return jsxRuntime.jsx(AgeVerificationIcon, { size: size });
213
+ case APP_HANDLES.ACCESSIBILITY:
214
+ return jsxRuntime.jsx(AccessibilityIcon, { size: size });
215
+ case APP_HANDLES.ORDER_LIMIT:
216
+ return jsxRuntime.jsx(OrderLimitIcon, { size: size });
217
+ default:
218
+ return jsxRuntime.jsx(DefaultIcon, { size: size });
219
+ }
220
+ }
221
+
222
+ function DownloadIcon({ width = 18, height = 20, fill = 'black', }) {
223
+ return (jsxRuntime.jsxs("svg", { width: width, height: height, viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("path", { d: "M10.8333 3.61111C10.8333 3.15087 10.4602 2.77778 9.99999 2.77778C9.53976 2.77778 9.16666 3.15087 9.16666 3.61111V11.0437L7.25592 9.13297C6.93048 8.80753 6.40284 8.80753 6.0774 9.13297C5.75197 9.4584 5.75197 9.98604 6.0774 10.3115L9.41074 13.6448C9.73618 13.9702 10.2638 13.9702 10.5893 13.6448L13.9226 10.3115C14.248 9.98604 14.248 9.4584 13.9226 9.13297C13.5971 8.80753 13.0695 8.80753 12.7441 9.13297L10.8333 11.0437V3.61111Z", fill: fill }), jsxRuntime.jsx("path", { d: "M17.2222 14.1667C17.2222 13.7064 16.8491 13.3333 16.3889 13.3333C15.9286 13.3333 15.5555 13.7064 15.5555 14.1667V15.0556C15.5555 15.5158 15.1825 15.8889 14.7222 15.8889L5.27777 15.8889C4.81753 15.8889 4.44444 15.5158 4.44444 15.0556L4.44444 14.1667C4.44444 13.7064 4.07134 13.3333 3.6111 13.3333C3.15087 13.3333 2.77777 13.7064 2.77777 14.1667V15.0556C2.77777 16.4363 3.89706 17.5556 5.27777 17.5556L14.7222 17.5556C16.1029 17.5556 17.2222 16.4363 17.2222 15.0556V14.1667Z", fill: fill })] }));
224
+ }
225
+
226
+ /**
227
+ * CrossApp Banner Component
228
+ *
229
+ * A reusable banner component for cross-promoting Avada apps.
230
+ * Supports configuration-based customization for different apps.
231
+ */
232
+ function CrossAppBanner$1({ config, appData, sourcePage = "dashboard", shop = {}, onClose, onShopUpdate, }) {
233
+ var _a;
234
+ const { appHandle, translationKey, targetUrl, showPlanBtn = false, planUrl, bgColor = "linear-gradient(90deg, #000926 0%, #0a1f3d 100%)", isHideBanner = false, eventPrefix, bannerCloseKey = "bannerCloseCount", } = react.useMemo(() => appData, [appData]);
235
+ const isVerificationApp = react.useMemo(() => appHandle === APP_HANDLES.AGE_VERIFICATION, [appHandle]);
236
+ const [showBanner, setShowBanner] = react.useState(!isHideBanner);
237
+ const getEventName = react.useCallback((action) => {
238
+ const prefix = eventPrefix !== null && eventPrefix !== void 0 ? eventPrefix : config.appPrefix;
239
+ return `${prefix}_banner_${action}`;
240
+ }, [eventPrefix, config.appPrefix]);
241
+ const logBannerEvent = react.useCallback(async (action, isClosePermanently = false) => {
242
+ var _a;
243
+ if (!isVerificationApp)
244
+ return;
245
+ const eventName = getEventName(action);
246
+ const endpoint = (_a = config.bannerEventEndpoint) !== null && _a !== void 0 ? _a : "/banner-event";
247
+ try {
248
+ await config.apiClient(endpoint, {
249
+ method: "POST",
250
+ body: {
251
+ event: eventName,
252
+ properties: {
253
+ [`${config.appPrefix}_banner_channel`]: sourcePage,
254
+ },
255
+ isClosePermanently,
256
+ },
257
+ });
258
+ }
259
+ catch (error) {
260
+ console.error(`[Tracking Error] ${eventName}:`, error);
261
+ }
262
+ }, [isVerificationApp, getEventName, config, sourcePage]);
263
+ react.useEffect(() => {
264
+ if (showBanner && isVerificationApp) {
265
+ logBannerEvent("display");
266
+ }
267
+ }, [showBanner, isVerificationApp, logBannerEvent]);
268
+ const handleClose = react.useCallback(() => {
269
+ setShowBanner(false);
270
+ if (isVerificationApp && bannerCloseKey) {
271
+ const currentCount = Number(localStorage.getItem(bannerCloseKey)) || 0;
272
+ const nextCount = currentCount + 1;
273
+ localStorage.setItem(bannerCloseKey, String(nextCount));
274
+ if (nextCount >= 2) {
275
+ // Update shop to hide banner permanently
276
+ if (onShopUpdate) {
277
+ onShopUpdate({ ...shop, hideVerificationBanner: true });
278
+ }
279
+ if (config.storeDispatch) {
280
+ config.storeDispatch({
281
+ type: "SET_SHOP",
282
+ payload: { ...shop, hideVerificationBanner: true },
283
+ });
284
+ }
285
+ logBannerEvent("close", true);
286
+ }
287
+ else {
288
+ logBannerEvent("close");
289
+ }
290
+ }
291
+ onClose === null || onClose === void 0 ? void 0 : onClose();
292
+ }, [
293
+ isVerificationApp,
294
+ bannerCloseKey,
295
+ shop,
296
+ onShopUpdate,
297
+ config.storeDispatch,
298
+ logBannerEvent,
299
+ onClose,
300
+ ]);
301
+ const handleCtaClick = react.useCallback(() => {
302
+ if (isVerificationApp) {
303
+ logBannerEvent("access");
304
+ }
305
+ }, [isVerificationApp, logBannerEvent]);
306
+ if (!showBanner || !appData)
307
+ return null;
308
+ const closeIcon = (_a = config.closeIcon) !== null && _a !== void 0 ? _a : jsxRuntime.jsx(polaris.Icon, { source: polarisIcons.XIcon });
309
+ return (jsxRuntime.jsx(polaris.Layout.Section, { children: jsxRuntime.jsx(polaris.Box, { borderRadius: "200", overflowX: "hidden", overflowY: "hidden", children: jsxRuntime.jsxs("div", { className: "Avada-CrossApp", style: { background: bgColor }, children: [jsxRuntime.jsxs(polaris.InlineStack, { gap: isVerificationApp ? "400" : "0", children: [jsxRuntime.jsx("div", { className: "Avada-CrossApp__Icon", children: jsxRuntime.jsx(CrossAppIcon, { app: appHandle, size: 80 }) }), jsxRuntime.jsxs("div", { className: "Avada-CrossApp__Content", children: [jsxRuntime.jsx("div", { className: "Avada-CrossApp__Content--Text", children: jsxRuntime.jsxs(polaris.BlockStack, { gap: "200", children: [jsxRuntime.jsxs(polaris.Text, { as: "h3", variant: "headingLg", fontWeight: "bold", children: [translationKey, ".title"] }), jsxRuntime.jsxs(polaris.BlockStack, { gap: isVerificationApp ? "200" : "0", children: [isVerificationApp ? (jsxRuntime.jsx("i", { children: jsxRuntime.jsxs(polaris.Text, { as: "p", variant: "bodyLg", fontWeight: "medium", children: [translationKey, ".description1"] }) })) : (jsxRuntime.jsxs(polaris.Text, { as: "p", variant: "bodyMd", children: [translationKey, ".description1"] })), jsxRuntime.jsxs(polaris.Text, { as: "p", variant: "bodyMd", children: [translationKey, ".description2"] })] })] }) }), jsxRuntime.jsx("div", { className: "Avada-CrossApp__Content--Btn", children: jsxRuntime.jsxs(polaris.BlockStack, { gap: "200", inlineAlign: "center", children: [jsxRuntime.jsx(polaris.Button, { url: targetUrl, target: "_blank", onClick: handleCtaClick, fullWidth: true, children: jsxRuntime.jsxs(polaris.InlineStack, { blockAlign: "center", gap: "100", children: [jsxRuntime.jsx(DownloadIcon, {}), jsxRuntime.jsxs(polaris.Text, { as: "span", children: [translationKey, ".btnClaim"] })] }) }), showPlanBtn && planUrl && (jsxRuntime.jsx("div", { className: "Avada-CrossApp__ViewPlan", children: jsxRuntime.jsx(polaris.Link, { url: planUrl, target: "_blank", children: jsxRuntime.jsxs("span", { children: [translationKey, ".btnViewPlan"] }) }) }))] }) })] })] }), jsxRuntime.jsx("button", { className: "Avada-CrossApp__CloseBtn", onClick: handleClose, "aria-label": "Close banner", type: "button", children: closeIcon })] }) }) }));
310
+ }
311
+
312
+ /**
313
+ * CrossApp Banner Component with i18n support
314
+ *
315
+ * A reusable banner component for cross-promoting Avada apps.
316
+ * Uses @shopify/react-i18n for translations.
317
+ */
318
+ function CrossAppBannerWithI18n({ config, appData, sourcePage = 'dashboard', shop = {}, onClose, onShopUpdate, }) {
319
+ var _a;
320
+ const [i18n] = reactI18n.useI18n();
321
+ const { appHandle, translationKey, targetUrl, showPlanBtn = false, planUrl, bgColor = 'linear-gradient(90deg, #000926 0%, #0a1f3d 100%)', isHideBanner = false, eventPrefix, bannerCloseKey = 'bannerCloseCount', } = react.useMemo(() => appData, [appData]);
322
+ const isVerificationApp = react.useMemo(() => appHandle === APP_HANDLES.AGE_VERIFICATION, [appHandle]);
323
+ const [showBanner, setShowBanner] = react.useState(!isHideBanner);
324
+ const getEventName = react.useCallback((action) => {
325
+ const prefix = eventPrefix !== null && eventPrefix !== void 0 ? eventPrefix : config.appPrefix;
326
+ return `${prefix}_banner_${action}`;
327
+ }, [eventPrefix, config.appPrefix]);
328
+ const logBannerEvent = react.useCallback(async (action, isClosePermanently = false) => {
329
+ var _a;
330
+ if (!isVerificationApp)
331
+ return;
332
+ const eventName = getEventName(action);
333
+ const endpoint = (_a = config.bannerEventEndpoint) !== null && _a !== void 0 ? _a : '/banner-event';
334
+ try {
335
+ await config.apiClient(endpoint, {
336
+ method: 'POST',
337
+ body: {
338
+ event: eventName,
339
+ properties: {
340
+ [`${config.appPrefix}_banner_channel`]: sourcePage,
341
+ },
342
+ isClosePermanently,
343
+ },
344
+ });
345
+ }
346
+ catch (error) {
347
+ console.error(`[Tracking Error] ${eventName}:`, error);
348
+ }
349
+ }, [isVerificationApp, getEventName, config, sourcePage]);
350
+ react.useEffect(() => {
351
+ if (showBanner && isVerificationApp) {
352
+ logBannerEvent('display');
353
+ }
354
+ }, [showBanner, isVerificationApp, logBannerEvent]);
355
+ const handleClose = react.useCallback(() => {
356
+ setShowBanner(false);
357
+ if (isVerificationApp && bannerCloseKey) {
358
+ const currentCount = Number(localStorage.getItem(bannerCloseKey)) || 0;
359
+ const nextCount = currentCount + 1;
360
+ localStorage.setItem(bannerCloseKey, String(nextCount));
361
+ if (nextCount >= 2) {
362
+ if (onShopUpdate) {
363
+ onShopUpdate({ ...shop, hideVerificationBanner: true });
364
+ }
365
+ if (config.storeDispatch) {
366
+ config.storeDispatch({
367
+ type: 'SET_SHOP',
368
+ payload: { ...shop, hideVerificationBanner: true },
369
+ });
370
+ }
371
+ logBannerEvent('close', true);
372
+ }
373
+ else {
374
+ logBannerEvent('close');
375
+ }
376
+ }
377
+ onClose === null || onClose === void 0 ? void 0 : onClose();
378
+ }, [
379
+ isVerificationApp,
380
+ bannerCloseKey,
381
+ shop,
382
+ onShopUpdate,
383
+ config.storeDispatch,
384
+ logBannerEvent,
385
+ onClose,
386
+ ]);
387
+ const handleCtaClick = react.useCallback(() => {
388
+ if (isVerificationApp) {
389
+ logBannerEvent('access');
390
+ }
391
+ }, [isVerificationApp, logBannerEvent]);
392
+ if (!showBanner || !appData)
393
+ return null;
394
+ const closeIcon = (_a = config.closeIcon) !== null && _a !== void 0 ? _a : jsxRuntime.jsx(polaris.Icon, { source: polarisIcons.XIcon });
395
+ return (jsxRuntime.jsx(polaris.Layout.Section, { children: jsxRuntime.jsx(polaris.Box, { borderRadius: "200", overflowX: "hidden", overflowY: "hidden", children: jsxRuntime.jsxs("div", { className: "Avada-CrossApp", style: { background: bgColor }, children: [jsxRuntime.jsxs(polaris.InlineStack, { gap: isVerificationApp ? '400' : '0', children: [jsxRuntime.jsx("div", { className: "Avada-CrossApp__Icon", children: jsxRuntime.jsx(CrossAppIcon, { app: appHandle, size: 80 }) }), jsxRuntime.jsxs("div", { className: "Avada-CrossApp__Content", children: [jsxRuntime.jsx("div", { className: "Avada-CrossApp__Content--Text", children: jsxRuntime.jsxs(polaris.BlockStack, { gap: "200", children: [jsxRuntime.jsx(polaris.Text, { as: "h3", variant: "headingLg", fontWeight: "bold", children: i18n.translate(`${translationKey}.title`) }), jsxRuntime.jsxs(polaris.BlockStack, { gap: isVerificationApp ? '200' : '0', children: [isVerificationApp ? (jsxRuntime.jsx("i", { children: jsxRuntime.jsx(polaris.Text, { as: "p", variant: "bodyLg", fontWeight: "medium", children: i18n.translate(`${translationKey}.description1`) }) })) : (jsxRuntime.jsx(polaris.Text, { as: "p", variant: "bodyMd", children: i18n.translate(`${translationKey}.description1`) })), jsxRuntime.jsx(polaris.Text, { as: "p", variant: "bodyMd", children: i18n.translate(`${translationKey}.description2`) })] })] }) }), jsxRuntime.jsx("div", { className: "Avada-CrossApp__Content--Btn", children: jsxRuntime.jsxs(polaris.BlockStack, { gap: "200", inlineAlign: "center", children: [jsxRuntime.jsx(polaris.Button, { url: targetUrl, target: "_blank", onClick: handleCtaClick, fullWidth: true, children: jsxRuntime.jsxs(polaris.InlineStack, { blockAlign: "center", gap: "100", children: [jsxRuntime.jsx(DownloadIcon, {}), jsxRuntime.jsx(polaris.Text, { as: "span", children: i18n.translate(`${translationKey}.btnClaim`) })] }) }), showPlanBtn && planUrl && (jsxRuntime.jsx("div", { className: "Avada-CrossApp__ViewPlan", children: jsxRuntime.jsx(polaris.Link, { url: planUrl, target: "_blank", children: jsxRuntime.jsx("span", { children: i18n.translate(`${translationKey}.btnViewPlan`) }) }) }))] }) })] })] }), jsxRuntime.jsx("button", { className: "Avada-CrossApp__CloseBtn", onClick: handleClose, "aria-label": "Close banner", type: "button", children: closeIcon })] }) }) }));
396
+ }
397
+
398
+ CrossAppBanner;
399
+
400
+ /**
401
+ * Get data from localStorage
402
+ */
403
+ function getStorageData(key) {
404
+ try {
405
+ const data = localStorage.getItem(key);
406
+ return data ? JSON.parse(data) : {};
407
+ }
408
+ catch (_a) {
409
+ return {};
410
+ }
411
+ }
412
+ /**
413
+ * Set data to localStorage
414
+ */
415
+ function setStorageData(key, data) {
416
+ try {
417
+ localStorage.setItem(key, JSON.stringify(data));
418
+ }
419
+ catch (e) {
420
+ console.error('Failed to save to localStorage:', e);
421
+ }
422
+ }
423
+ /**
424
+ * Check if object is empty
425
+ */
426
+ function isEmpty(obj) {
427
+ return Object.keys(obj).length === 0;
428
+ }
429
+ /**
430
+ * Calculate next display date based on target type
431
+ */
432
+ function getNextDisplayDate(targetDateType) {
433
+ const currentDate = new Date();
434
+ switch (targetDateType) {
435
+ case 'next_day':
436
+ currentDate.setDate(currentDate.getDate() + 1);
437
+ break;
438
+ case '7_days_after':
439
+ currentDate.setDate(currentDate.getDate() + 7);
440
+ break;
441
+ case '30_days_after':
442
+ currentDate.setDate(currentDate.getDate() + 30);
443
+ break;
444
+ default:
445
+ currentDate.setDate(currentDate.getDate() + 1);
446
+ }
447
+ return currentDate;
448
+ }
449
+ /**
450
+ * Hook to manage banner display state with localStorage persistence
451
+ */
452
+ function useDisplayBanner({ storageKey, shopKey = '', shop = {}, checkShop = false, disableCount = 3, targetDateType = 'next_day', apiClient, shopUpdateEndpoint = '/shops?type=banner', onShopUpdate, onClose, checkStorage = true, }) {
453
+ var _a;
454
+ const [isCheckShop, setIsCheckShop] = react.useState(checkShop);
455
+ const dismissedBanners = (_a = shop === null || shop === void 0 ? void 0 : shop.dismissedBanners) !== null && _a !== void 0 ? _a : [];
456
+ const isDismissed = react.useMemo(() => shopKey ? dismissedBanners.includes(shopKey) : false, [dismissedBanners, shopKey]);
457
+ const handleShopApi = react.useCallback(async (data) => {
458
+ if (!apiClient)
459
+ return;
460
+ try {
461
+ const resp = await apiClient(shopUpdateEndpoint, {
462
+ body: data,
463
+ method: 'PUT',
464
+ });
465
+ if (resp.success && onShopUpdate) {
466
+ onShopUpdate({
467
+ ...shop,
468
+ ...resp.data,
469
+ });
470
+ }
471
+ }
472
+ catch (e) {
473
+ console.error('Failed to update shop:', e);
474
+ }
475
+ }, [apiClient, shopUpdateEndpoint, shop, onShopUpdate]);
476
+ const checkIsShowBanner = react.useCallback(() => {
477
+ var _a;
478
+ if (!checkStorage) {
479
+ return true;
480
+ }
481
+ const storageData = getStorageData(storageKey);
482
+ const isValidStorage = isEmpty(storageData) ||
483
+ new Date((_a = storageData.currentDate) !== null && _a !== void 0 ? _a : '').getTime() < new Date().getTime();
484
+ if (!isCheckShop) {
485
+ return isValidStorage;
486
+ }
487
+ return isValidStorage && !isDismissed;
488
+ }, [storageKey, isCheckShop, isDismissed, checkStorage]);
489
+ const [shouldDisplay, setShouldDisplay] = react.useState(checkIsShowBanner);
490
+ const hideBanner = react.useCallback(() => {
491
+ setShouldDisplay(false);
492
+ }, []);
493
+ const showBanner = react.useCallback(() => {
494
+ setShouldDisplay(true);
495
+ }, []);
496
+ const handleCloseBanner = react.useCallback(async (data) => {
497
+ var _a;
498
+ const isDismissShop = (data === null || data === void 0 ? void 0 : data.isDismissShop) === true;
499
+ setShouldDisplay(false);
500
+ setIsCheckShop(isDismissShop);
501
+ const storageData = getStorageData(storageKey);
502
+ const count = (_a = storageData.disableCount) !== null && _a !== void 0 ? _a : 1;
503
+ const nextDate = getNextDisplayDate(targetDateType);
504
+ setStorageData(storageKey, {
505
+ currentDate: nextDate.toISOString(),
506
+ disableCount: count + 1,
507
+ });
508
+ if ((isCheckShop && count >= disableCount) || isDismissShop) {
509
+ const newDismissedBanners = [...new Set([...dismissedBanners, shopKey])];
510
+ await handleShopApi({
511
+ dismissedBanners: newDismissedBanners,
512
+ ...data,
513
+ });
514
+ }
515
+ onClose === null || onClose === void 0 ? void 0 : onClose();
516
+ }, [
517
+ storageKey,
518
+ targetDateType,
519
+ isCheckShop,
520
+ disableCount,
521
+ dismissedBanners,
522
+ shopKey,
523
+ handleShopApi,
524
+ onClose,
525
+ ]);
526
+ return {
527
+ shouldDisplay,
528
+ hideBanner,
529
+ showBanner,
530
+ handleCloseBanner,
531
+ };
532
+ }
533
+
534
+ exports.APP_DOMAINS = APP_DOMAINS;
535
+ exports.APP_HANDLES = APP_HANDLES;
536
+ exports.CrossAppBanner = CrossAppBanner$1;
537
+ exports.CrossAppBannerWithI18n = CrossAppBannerWithI18n;
538
+ exports.CrossAppIcon = CrossAppIcon;
539
+ exports.DownloadIcon = DownloadIcon;
540
+ exports.EXCLUDED_COUNTRIES = EXCLUDED_COUNTRIES;
541
+ exports.EXCLUDED_SHOPIFY_PLANS = EXCLUDED_SHOPIFY_PLANS;
542
+ exports.SHOPIFY_APP_HANDLES = SHOPIFY_APP_HANDLES;
543
+ exports.TARGET_STORAGE_TYPES = TARGET_STORAGE_TYPES;
544
+ exports.createCrossAppConfig = createCrossAppConfig;
545
+ exports.default = CrossAppBanner$1;
546
+ exports.getAccessibilityAppData = getAccessibilityAppData;
547
+ exports.getAgeVerificationAppData = getAgeVerificationAppData;
548
+ exports.getCookieBarAppData = getCookieBarAppData;
549
+ exports.getShopifyName = getShopifyName;
550
+ exports.shouldExcludeShop = shouldExcludeShop;
551
+ exports.useDisplayBanner = useDisplayBanner;
552
+ //# sourceMappingURL=index.js.map