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/README.md ADDED
@@ -0,0 +1,217 @@
1
+ # @avada/crossapp-banner
2
+
3
+ Shared CrossApp Banner components and hooks for Avada Shopify apps.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @avada/crossapp-banner
9
+ # or
10
+ yarn add @avada/crossapp-banner
11
+ ```
12
+
13
+ ## Peer Dependencies
14
+
15
+ This library requires the following peer dependencies:
16
+
17
+ ```json
18
+ {
19
+ "react": ">=16.8.0",
20
+ "react-dom": ">=16.8.0",
21
+ "@shopify/polaris": ">=12.0.0",
22
+ "@shopify/polaris-icons": ">=7.0.0",
23
+ "@shopify/react-i18n": ">=7.0.0"
24
+ }
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Basic Usage
30
+
31
+ ```tsx
32
+ import {
33
+ CrossAppBanner,
34
+ createCrossAppConfig,
35
+ getAgeVerificationAppData,
36
+ } from '@avada/crossapp-banner';
37
+ import '@avada/crossapp-banner/dist/styles.css';
38
+
39
+ // Create config for your app
40
+ const config = createCrossAppConfig({
41
+ appPrefix: 'cb', // 'cb' | 'ol' | 'ac' | 'av'
42
+ apiClient: fetchAuthenticatedApi, // Your API client
43
+ storeDispatch: (action) => store.dispatch(action), // Optional
44
+ });
45
+
46
+ // Get app data
47
+ const appData = getAgeVerificationAppData(shop, {
48
+ eventPrefix: 'cb',
49
+ utmSource: 'cookiebar',
50
+ });
51
+
52
+ function MyComponent() {
53
+ return (
54
+ <CrossAppBanner
55
+ config={config}
56
+ appData={appData}
57
+ sourcePage="dashboard"
58
+ shop={shop}
59
+ onClose={() => console.log('Banner closed')}
60
+ onShopUpdate={(data) => dispatch(setShop(data))}
61
+ />
62
+ );
63
+ }
64
+ ```
65
+
66
+ ### With i18n Support
67
+
68
+ ```tsx
69
+ import {CrossAppBannerWithI18n} from '@avada/crossapp-banner';
70
+
71
+ // Requires @shopify/react-i18n context
72
+ function MyComponent() {
73
+ return (
74
+ <CrossAppBannerWithI18n
75
+ config={config}
76
+ appData={appData}
77
+ sourcePage="settings"
78
+ />
79
+ );
80
+ }
81
+ ```
82
+
83
+ ### Using the Display Banner Hook
84
+
85
+ ```tsx
86
+ import {useDisplayBanner} from '@avada/crossapp-banner';
87
+
88
+ function MyBanner() {
89
+ const {shouldDisplay, hideBanner, handleCloseBanner} = useDisplayBanner({
90
+ storageKey: 'my-banner-key',
91
+ shopKey: 'banner_promotion',
92
+ shop: activeShop,
93
+ checkShop: true,
94
+ disableCount: 3,
95
+ targetDateType: '7_days_after',
96
+ apiClient: fetchAuthenticatedApi,
97
+ onShopUpdate: (data) => dispatch(setShop(data)),
98
+ onClose: () => console.log('Closed'),
99
+ });
100
+
101
+ if (!shouldDisplay) return null;
102
+
103
+ return (
104
+ <div>
105
+ <button onClick={() => handleCloseBanner()}>Close</button>
106
+ </div>
107
+ );
108
+ }
109
+ ```
110
+
111
+ ## API Reference
112
+
113
+ ### Components
114
+
115
+ #### `CrossAppBanner`
116
+ Main banner component without i18n.
117
+
118
+ #### `CrossAppBannerWithI18n`
119
+ Banner component with `@shopify/react-i18n` support.
120
+
121
+ #### `CrossAppIcon`
122
+ Icon component for different apps.
123
+
124
+ #### `DownloadIcon`
125
+ Download icon SVG component.
126
+
127
+ ### Hooks
128
+
129
+ #### `useDisplayBanner`
130
+ Hook to manage banner display state with localStorage persistence.
131
+
132
+ ### Utilities
133
+
134
+ #### `createCrossAppConfig(config)`
135
+ Create a configuration object for the banner.
136
+
137
+ #### `getCookieBarAppData(shop, options?)`
138
+ Get app data for Cookie Bar promotion.
139
+
140
+ #### `getAgeVerificationAppData(shop, options?)`
141
+ Get app data for Age Verification promotion.
142
+
143
+ #### `getAccessibilityAppData(shop, options?)`
144
+ Get app data for Accessibility promotion.
145
+
146
+ #### `shouldExcludeShop(shop)`
147
+ Check if a shop should be excluded from cross-app promotions.
148
+
149
+ ### Constants
150
+
151
+ - `APP_HANDLES` - App handle identifiers
152
+ - `APP_DOMAINS` - App domains
153
+ - `SHOPIFY_APP_HANDLES` - Shopify app handles
154
+ - `EXCLUDED_SHOPIFY_PLANS` - Plans to exclude
155
+ - `EXCLUDED_COUNTRIES` - Countries to exclude
156
+ - `TARGET_STORAGE_TYPES` - Storage type options
157
+
158
+ ## Types
159
+
160
+ All types are exported and can be imported:
161
+
162
+ ```tsx
163
+ import type {
164
+ AppHandle,
165
+ AppPrefix,
166
+ CrossAppConfig,
167
+ CrossAppData,
168
+ CrossAppBannerProps,
169
+ UseDisplayBannerConfig,
170
+ } from '@avada/crossapp-banner';
171
+ ```
172
+
173
+ ## Migration from Existing Code
174
+
175
+ ### Before (duplicated code)
176
+
177
+ ```tsx
178
+ // order-limit/CrossApp.js
179
+ import {useStore} from '@assets/reducers/storeReducer';
180
+ import {setShop} from '@assets/actions/storeActions';
181
+
182
+ const CrossApp = ({appData}) => {
183
+ const {dispatch, state: {shop}} = useStore();
184
+ // ... 170+ lines of duplicated code
185
+ };
186
+ ```
187
+
188
+ ### After (using library)
189
+
190
+ ```tsx
191
+ // order-limit/CrossApp.js
192
+ import {CrossAppBannerWithI18n, createCrossAppConfig} from '@avada/crossapp-banner';
193
+ import '@avada/crossapp-banner/dist/styles.css';
194
+
195
+ const config = createCrossAppConfig({
196
+ appPrefix: 'ol',
197
+ apiClient: fetchAuthenticatedApi,
198
+ storeDispatch: (action) => setShop(dispatch, action.payload),
199
+ });
200
+
201
+ const CrossApp = ({appData}) => {
202
+ const {dispatch, state: {shop}} = useStore();
203
+
204
+ return (
205
+ <CrossAppBannerWithI18n
206
+ config={config}
207
+ appData={appData}
208
+ shop={shop}
209
+ onShopUpdate={(data) => setShop(dispatch, data)}
210
+ />
211
+ );
212
+ };
213
+ ```
214
+
215
+ ## License
216
+
217
+ MIT
@@ -0,0 +1,10 @@
1
+ import { CrossAppBannerProps } from "../../types";
2
+ import "./CrossAppBanner.scss";
3
+ /**
4
+ * CrossApp Banner Component
5
+ *
6
+ * A reusable banner component for cross-promoting Avada apps.
7
+ * Supports configuration-based customization for different apps.
8
+ */
9
+ export declare function CrossAppBanner({ config, appData, sourcePage, shop, onClose, onShopUpdate, }: CrossAppBannerProps): import("react/jsx-runtime").JSX.Element | null;
10
+ export default CrossAppBanner;
@@ -0,0 +1,10 @@
1
+ import { CrossAppBannerProps } from '../../types';
2
+ import './CrossAppBanner.scss';
3
+ /**
4
+ * CrossApp Banner Component with i18n support
5
+ *
6
+ * A reusable banner component for cross-promoting Avada apps.
7
+ * Uses @shopify/react-i18n for translations.
8
+ */
9
+ export declare function CrossAppBannerWithI18n({ config, appData, sourcePage, shop, onClose, onShopUpdate, }: CrossAppBannerProps): import("react/jsx-runtime").JSX.Element | null;
10
+ export default CrossAppBannerWithI18n;
@@ -0,0 +1,3 @@
1
+ export { CrossAppBanner } from './CrossAppBanner';
2
+ export { CrossAppBannerWithI18n } from './CrossAppBannerWithI18n';
3
+ export default CrossAppBanner;
@@ -0,0 +1,10 @@
1
+ import { AppHandle } from '../../types';
2
+ export interface CrossAppIconProps {
3
+ app: AppHandle;
4
+ size?: number;
5
+ }
6
+ /**
7
+ * CrossApp icon component that renders the appropriate icon based on app handle
8
+ */
9
+ export declare function CrossAppIcon({ app, size }: CrossAppIconProps): import("react/jsx-runtime").JSX.Element;
10
+ export default CrossAppIcon;
@@ -0,0 +1,7 @@
1
+ export interface DownloadIconProps {
2
+ width?: number;
3
+ height?: number;
4
+ fill?: string;
5
+ }
6
+ export declare function DownloadIcon({ width, height, fill, }: DownloadIconProps): import("react/jsx-runtime").JSX.Element;
7
+ export default DownloadIcon;
@@ -0,0 +1,4 @@
1
+ export { DownloadIcon } from './DownloadIcon';
2
+ export type { DownloadIconProps } from './DownloadIcon';
3
+ export { CrossAppIcon } from './CrossAppIcon';
4
+ export type { CrossAppIconProps } from './CrossAppIcon';
@@ -0,0 +1,116 @@
1
+ import { AppHandle, CrossAppData, TargetStorageType } from '../types';
2
+ /**
3
+ * App handle constants
4
+ */
5
+ export declare const APP_HANDLES: {
6
+ readonly ORDER_LIMIT: AppHandle;
7
+ readonly ACCESSIBILITY: AppHandle;
8
+ readonly COOKIE_BAR: AppHandle;
9
+ readonly AGE_VERIFICATION: AppHandle;
10
+ };
11
+ /**
12
+ * App domains
13
+ */
14
+ export declare const APP_DOMAINS: {
15
+ readonly COOKIE_BAR: "cookie.avada.io";
16
+ readonly AGE_VERIFICATION: "age-verification-b0fa4.firebaseapp.com";
17
+ readonly ACCESSIBILITY: "accessibility.avada.io";
18
+ };
19
+ /**
20
+ * Shopify app handles
21
+ */
22
+ export declare const SHOPIFY_APP_HANDLES: {
23
+ readonly COOKIE_BAR: "avada-cookie-bar";
24
+ readonly AGE_VERIFICATION: "sun-age-verification-popup";
25
+ readonly ACCESSIBILITY: "avada-accessibility";
26
+ };
27
+ /**
28
+ * Shopify plans to exclude from cross-app promotion
29
+ */
30
+ export declare const EXCLUDED_SHOPIFY_PLANS: readonly ["partner_test", "affiliate", "staff", "frozen", "fraudulent", "cancelled", "paused"];
31
+ /**
32
+ * Countries to exclude from cross-app promotion
33
+ */
34
+ export declare const EXCLUDED_COUNTRIES: readonly ["IN", "VN"];
35
+ /**
36
+ * Email suffixes to exclude
37
+ */
38
+ export declare const EXCLUDED_EMAIL_SUFFIXES: readonly ["shopify.com"];
39
+ /**
40
+ * Avada email suffixes
41
+ */
42
+ export declare const AVADA_EMAIL_SUFFIXES: readonly ["avadagroup.com", "avada.io"];
43
+ /**
44
+ * Target storage types for display banner
45
+ */
46
+ export declare const TARGET_STORAGE_TYPES: Record<string, TargetStorageType>;
47
+ /**
48
+ * Default banner close key prefix
49
+ */
50
+ export declare const BANNER_CLOSE_KEY_PREFIX = "bannerCloseCount_";
51
+ /**
52
+ * Get Shopify store name from domain
53
+ */
54
+ export declare function getShopifyName(domain: string): string;
55
+ /**
56
+ * Check if shop should be excluded from cross-app promotion
57
+ */
58
+ export declare function shouldExcludeShop(shop: {
59
+ shopifyPlan?: string;
60
+ country?: string;
61
+ email?: string;
62
+ }): boolean;
63
+ /**
64
+ * Get Cookie Bar app data for cross-app promotion
65
+ */
66
+ export declare function getCookieBarAppData(shop: {
67
+ shopifyDomain: string;
68
+ cookieBarInstalled?: boolean;
69
+ }, options?: {
70
+ showPlanBtn?: boolean;
71
+ eventPrefix?: string;
72
+ }): CrossAppData;
73
+ /**
74
+ * Get Age Verification app data for cross-app promotion
75
+ */
76
+ export declare function getAgeVerificationAppData(shop: {
77
+ shopifyDomain: string;
78
+ ageVerificationInstalled?: boolean;
79
+ hideVerificationBanner?: boolean;
80
+ }, options?: {
81
+ eventPrefix?: string;
82
+ utmSource?: string;
83
+ }): CrossAppData;
84
+ /**
85
+ * Get Accessibility app data for cross-app promotion
86
+ */
87
+ export declare function getAccessibilityAppData(shop: {
88
+ shopifyDomain: string;
89
+ accessibilityInstalled?: boolean;
90
+ }, options?: {
91
+ eventPrefix?: string;
92
+ }): CrossAppData;
93
+ /**
94
+ * Create cross-app config helper
95
+ */
96
+ export declare function createCrossAppConfig(config: {
97
+ appPrefix: 'cb' | 'av' | 'ac' | 'ol';
98
+ apiClient: (url: string, options?: {
99
+ method?: string;
100
+ body?: Record<string, unknown>;
101
+ }) => Promise<unknown>;
102
+ storeDispatch?: (action: unknown) => void;
103
+ closeIcon?: React.ReactNode;
104
+ bannerEventEndpoint?: string;
105
+ shopUpdateEndpoint?: string;
106
+ }): {
107
+ appPrefix: "cb" | "av" | "ac" | "ol";
108
+ apiClient: (url: string, options?: {
109
+ method?: string;
110
+ body?: Record<string, unknown>;
111
+ }) => Promise<unknown>;
112
+ storeDispatch: ((action: unknown) => void) | undefined;
113
+ closeIcon: import("react").ReactNode;
114
+ bannerEventEndpoint: string;
115
+ shopUpdateEndpoint: string;
116
+ };
@@ -0,0 +1,2 @@
1
+ export { useDisplayBanner } from './useDisplayBanner';
2
+ export type { UseDisplayBannerConfig } from './useDisplayBanner';
@@ -0,0 +1,66 @@
1
+ import { UseDisplayBannerReturn, TargetStorageType } from '../types';
2
+ export interface UseDisplayBannerConfig {
3
+ /**
4
+ * Storage key for tracking display state
5
+ */
6
+ storageKey: string;
7
+ /**
8
+ * Shop key for tracking dismissed banners per shop
9
+ */
10
+ shopKey?: string;
11
+ /**
12
+ * Current shop data
13
+ */
14
+ shop?: {
15
+ dismissedBanners?: string[];
16
+ [key: string]: unknown;
17
+ };
18
+ /**
19
+ * Whether to check shop dismissed banners
20
+ * @default false
21
+ */
22
+ checkShop?: boolean;
23
+ /**
24
+ * Number of closes before permanently dismissing
25
+ * @default 3
26
+ */
27
+ disableCount?: number;
28
+ /**
29
+ * Target storage type for next display
30
+ * @default 'next_day'
31
+ */
32
+ targetDateType?: TargetStorageType;
33
+ /**
34
+ * API client function for making requests
35
+ */
36
+ apiClient?: (url: string, options?: {
37
+ method?: string;
38
+ body?: Record<string, unknown>;
39
+ }) => Promise<{
40
+ success?: boolean;
41
+ data?: Record<string, unknown>;
42
+ }>;
43
+ /**
44
+ * Endpoint for updating shop data
45
+ * @default '/shops?type=banner'
46
+ */
47
+ shopUpdateEndpoint?: string;
48
+ /**
49
+ * Callback when shop data is updated
50
+ */
51
+ onShopUpdate?: (shopData: Record<string, unknown>) => void;
52
+ /**
53
+ * Callback when banner is closed
54
+ */
55
+ onClose?: () => void;
56
+ /**
57
+ * Whether to check storage for display state
58
+ * @default true
59
+ */
60
+ checkStorage?: boolean;
61
+ }
62
+ /**
63
+ * Hook to manage banner display state with localStorage persistence
64
+ */
65
+ export declare function useDisplayBanner({ storageKey, shopKey, shop, checkShop, disableCount, targetDateType, apiClient, shopUpdateEndpoint, onShopUpdate, onClose, checkStorage, }: UseDisplayBannerConfig): UseDisplayBannerReturn;
66
+ export default useDisplayBanner;