@tagadapay/plugin-sdk 2.8.8 → 2.8.10
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/react/config/environment.d.ts +1 -22
- package/dist/react/config/environment.js +1 -132
- package/dist/react/utils/deviceInfo.d.ts +1 -39
- package/dist/react/utils/deviceInfo.js +1 -163
- package/dist/react/utils/jwtDecoder.d.ts +1 -14
- package/dist/react/utils/jwtDecoder.js +1 -86
- package/dist/react/utils/tokenStorage.d.ts +1 -16
- package/dist/react/utils/tokenStorage.js +1 -53
- package/dist/v2/core/client.d.ts +96 -0
- package/dist/v2/core/client.js +430 -0
- package/dist/v2/core/config/environment.d.ts +36 -0
- package/dist/v2/core/config/environment.js +155 -0
- package/dist/v2/core/pathRemapping.js +61 -3
- package/dist/v2/core/resources/apiClient.d.ts +13 -0
- package/dist/v2/core/resources/apiClient.js +77 -9
- package/dist/v2/core/resources/funnel.d.ts +21 -0
- package/dist/v2/core/resources/payments.d.ts +23 -0
- package/dist/v2/core/types.d.ts +271 -0
- package/dist/v2/core/types.js +4 -0
- package/dist/v2/core/utils/deviceInfo.d.ts +39 -0
- package/dist/v2/core/utils/deviceInfo.js +162 -0
- package/dist/v2/core/utils/eventDispatcher.d.ts +10 -0
- package/dist/v2/core/utils/eventDispatcher.js +24 -0
- package/dist/v2/core/utils/jwtDecoder.d.ts +14 -0
- package/dist/v2/core/utils/jwtDecoder.js +85 -0
- package/dist/v2/core/utils/pluginConfig.d.ts +1 -0
- package/dist/v2/core/utils/pluginConfig.js +64 -8
- package/dist/v2/core/utils/tokenStorage.d.ts +19 -0
- package/dist/v2/core/utils/tokenStorage.js +52 -0
- package/dist/v2/react/components/ApplePayButton.js +1 -1
- package/dist/v2/react/components/DebugDrawer.js +90 -1
- package/dist/v2/react/hooks/__examples__/FunnelContextExample.d.ts +12 -0
- package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +54 -0
- package/dist/v2/react/hooks/useFunnel.d.ts +2 -1
- package/dist/v2/react/hooks/useFunnel.js +245 -69
- package/dist/v2/react/hooks/useGoogleAutocomplete.js +26 -18
- package/dist/v2/react/hooks/useISOData.js +4 -2
- package/dist/v2/react/hooks/useOffersQuery.d.ts +42 -29
- package/dist/v2/react/hooks/useOffersQuery.js +266 -204
- package/dist/v2/react/hooks/usePaymentQuery.js +99 -6
- package/dist/v2/react/providers/TagadaProvider.d.ts +13 -21
- package/dist/v2/react/providers/TagadaProvider.js +79 -673
- package/package.json +1 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get basic browser information from user agent
|
|
3
|
+
*/
|
|
4
|
+
function getBrowserInfo() {
|
|
5
|
+
const userAgent = navigator.userAgent;
|
|
6
|
+
// Chrome
|
|
7
|
+
if (userAgent.includes('Chrome')) {
|
|
8
|
+
const match = /Chrome\/(\d+)/.exec(userAgent);
|
|
9
|
+
return { name: 'Chrome', version: match ? match[1] : 'unknown' };
|
|
10
|
+
}
|
|
11
|
+
// Firefox
|
|
12
|
+
if (userAgent.includes('Firefox')) {
|
|
13
|
+
const match = /Firefox\/(\d+)/.exec(userAgent);
|
|
14
|
+
return { name: 'Firefox', version: match ? match[1] : 'unknown' };
|
|
15
|
+
}
|
|
16
|
+
// Safari
|
|
17
|
+
if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
|
|
18
|
+
const match = /Version\/(\d+)/.exec(userAgent);
|
|
19
|
+
return { name: 'Safari', version: match ? match[1] : 'unknown' };
|
|
20
|
+
}
|
|
21
|
+
// Edge
|
|
22
|
+
if (userAgent.includes('Edge')) {
|
|
23
|
+
const match = /Edge\/(\d+)/.exec(userAgent);
|
|
24
|
+
return { name: 'Edge', version: match ? match[1] : 'unknown' };
|
|
25
|
+
}
|
|
26
|
+
return { name: 'unknown', version: 'unknown' };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get basic OS information from user agent
|
|
30
|
+
*/
|
|
31
|
+
function getOSInfo() {
|
|
32
|
+
const userAgent = navigator.userAgent;
|
|
33
|
+
// Windows
|
|
34
|
+
if (userAgent.includes('Windows')) {
|
|
35
|
+
if (userAgent.includes('Windows NT 10.0'))
|
|
36
|
+
return { name: 'Windows', version: '10' };
|
|
37
|
+
if (userAgent.includes('Windows NT 6.3'))
|
|
38
|
+
return { name: 'Windows', version: '8.1' };
|
|
39
|
+
if (userAgent.includes('Windows NT 6.2'))
|
|
40
|
+
return { name: 'Windows', version: '8' };
|
|
41
|
+
if (userAgent.includes('Windows NT 6.1'))
|
|
42
|
+
return { name: 'Windows', version: '7' };
|
|
43
|
+
return { name: 'Windows', version: 'unknown' };
|
|
44
|
+
}
|
|
45
|
+
// macOS
|
|
46
|
+
if (userAgent.includes('Mac OS X')) {
|
|
47
|
+
const match = /Mac OS X (\d+[._]\d+)/.exec(userAgent);
|
|
48
|
+
return { name: 'macOS', version: match ? match[1].replace('_', '.') : 'unknown' };
|
|
49
|
+
}
|
|
50
|
+
// iOS
|
|
51
|
+
if (userAgent.includes('iPhone') || userAgent.includes('iPad')) {
|
|
52
|
+
const match = /OS (\d+[._]\d+)/.exec(userAgent);
|
|
53
|
+
return { name: 'iOS', version: match ? match[1].replace('_', '.') : 'unknown' };
|
|
54
|
+
}
|
|
55
|
+
// Android
|
|
56
|
+
if (userAgent.includes('Android')) {
|
|
57
|
+
const match = /Android (\d+[.\d]*)/.exec(userAgent);
|
|
58
|
+
return { name: 'Android', version: match ? match[1] : 'unknown' };
|
|
59
|
+
}
|
|
60
|
+
// Linux
|
|
61
|
+
if (userAgent.includes('Linux')) {
|
|
62
|
+
return { name: 'Linux', version: 'unknown' };
|
|
63
|
+
}
|
|
64
|
+
return { name: 'unknown', version: 'unknown' };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get device information
|
|
68
|
+
*/
|
|
69
|
+
function getDeviceInfo() {
|
|
70
|
+
const userAgent = navigator.userAgent;
|
|
71
|
+
// Mobile devices
|
|
72
|
+
if (userAgent.includes('iPhone')) {
|
|
73
|
+
return { type: 'mobile', model: 'iPhone' };
|
|
74
|
+
}
|
|
75
|
+
if (userAgent.includes('iPad')) {
|
|
76
|
+
return { type: 'tablet', model: 'iPad' };
|
|
77
|
+
}
|
|
78
|
+
if (userAgent.includes('Android')) {
|
|
79
|
+
if (userAgent.includes('Mobile')) {
|
|
80
|
+
return { type: 'mobile', model: 'Android' };
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
return { type: 'tablet', model: 'Android' };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Desktop (no specific device info)
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get screen resolution
|
|
91
|
+
*/
|
|
92
|
+
function getScreenResolution() {
|
|
93
|
+
return {
|
|
94
|
+
width: window.screen.width,
|
|
95
|
+
height: window.screen.height,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get timezone
|
|
100
|
+
*/
|
|
101
|
+
function getTimeZone() {
|
|
102
|
+
try {
|
|
103
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error('Failed to get timezone:', error);
|
|
107
|
+
return 'UTC';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get browser locale
|
|
112
|
+
*/
|
|
113
|
+
export function getBrowserLocale() {
|
|
114
|
+
try {
|
|
115
|
+
return navigator.language || 'en-US';
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error('Failed to get browser locale:', error);
|
|
119
|
+
return 'en-US';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Collect all device information
|
|
124
|
+
*/
|
|
125
|
+
export function collectDeviceInfo() {
|
|
126
|
+
if (typeof window === 'undefined') {
|
|
127
|
+
// Server-side fallback
|
|
128
|
+
return {
|
|
129
|
+
userAgent: {
|
|
130
|
+
browser: { name: 'unknown', version: 'unknown' },
|
|
131
|
+
os: { name: 'unknown', version: 'unknown' },
|
|
132
|
+
},
|
|
133
|
+
screenResolution: { width: 0, height: 0 },
|
|
134
|
+
timeZone: 'UTC',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
userAgent: {
|
|
139
|
+
browser: getBrowserInfo(),
|
|
140
|
+
os: getOSInfo(),
|
|
141
|
+
device: getDeviceInfo(),
|
|
142
|
+
},
|
|
143
|
+
screenResolution: getScreenResolution(),
|
|
144
|
+
timeZone: getTimeZone(),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get URL parameters for session initialization
|
|
149
|
+
*/
|
|
150
|
+
export function getUrlParams() {
|
|
151
|
+
if (typeof window === 'undefined') {
|
|
152
|
+
return {};
|
|
153
|
+
}
|
|
154
|
+
const params = new URLSearchParams(window.location.search);
|
|
155
|
+
return {
|
|
156
|
+
locale: params.get('locale') || undefined,
|
|
157
|
+
currency: params.get('currency') || undefined,
|
|
158
|
+
utmSource: params.get('utm_source') || undefined,
|
|
159
|
+
utmMedium: params.get('utm_medium') || undefined,
|
|
160
|
+
utmCampaign: params.get('utm_campaign') || undefined,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple event dispatcher for handling state changes in the SDK
|
|
3
|
+
*/
|
|
4
|
+
export type Listener<T> = (data: T) => void;
|
|
5
|
+
export declare class EventDispatcher<T> {
|
|
6
|
+
private listeners;
|
|
7
|
+
subscribe(listener: Listener<T>): () => void;
|
|
8
|
+
notify(data: T): void;
|
|
9
|
+
clear(): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class EventDispatcher {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.listeners = new Set();
|
|
4
|
+
}
|
|
5
|
+
subscribe(listener) {
|
|
6
|
+
this.listeners.add(listener);
|
|
7
|
+
return () => {
|
|
8
|
+
this.listeners.delete(listener);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
notify(data) {
|
|
12
|
+
this.listeners.forEach((listener) => {
|
|
13
|
+
try {
|
|
14
|
+
listener(data);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
console.error('Error in event listener:', error);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
clear() {
|
|
22
|
+
this.listeners.clear();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Session } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Decode a JWT token to extract session information
|
|
4
|
+
* This is a simple client-side decoder - do not use for security validation
|
|
5
|
+
*/
|
|
6
|
+
export declare function decodeJWTClient(token: string): Session | null;
|
|
7
|
+
/**
|
|
8
|
+
* Check if a JWT token is expired
|
|
9
|
+
*/
|
|
10
|
+
export declare function isTokenExpired(token: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Get token expiration time
|
|
13
|
+
*/
|
|
14
|
+
export declare function getTokenExpiration(token: string): Date | null;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decode a JWT token to extract session information
|
|
3
|
+
* This is a simple client-side decoder - do not use for security validation
|
|
4
|
+
*/
|
|
5
|
+
export function decodeJWTClient(token) {
|
|
6
|
+
try {
|
|
7
|
+
// Split the token into parts
|
|
8
|
+
const parts = token.split('.');
|
|
9
|
+
if (parts.length !== 3) {
|
|
10
|
+
console.error('Invalid JWT token format');
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
// Decode the payload (second part)
|
|
14
|
+
const payload = parts[1];
|
|
15
|
+
// Add padding if needed
|
|
16
|
+
const paddedPayload = payload + '='.repeat((4 - (payload.length % 4)) % 4);
|
|
17
|
+
// Decode base64
|
|
18
|
+
const decoded = atob(paddedPayload);
|
|
19
|
+
// Parse JSON
|
|
20
|
+
const parsedPayload = JSON.parse(decoded);
|
|
21
|
+
// Check if token is expired
|
|
22
|
+
if (parsedPayload.exp && Date.now() >= parsedPayload.exp * 1000) {
|
|
23
|
+
console.warn('JWT token is expired');
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
// Return session data
|
|
27
|
+
return {
|
|
28
|
+
sessionId: parsedPayload.sessionId,
|
|
29
|
+
storeId: parsedPayload.storeId,
|
|
30
|
+
accountId: parsedPayload.accountId,
|
|
31
|
+
customerId: parsedPayload.customerId,
|
|
32
|
+
role: parsedPayload.role,
|
|
33
|
+
isValid: true,
|
|
34
|
+
isLoading: false,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error('Failed to decode JWT token:', error);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if a JWT token is expired
|
|
44
|
+
*/
|
|
45
|
+
export function isTokenExpired(token) {
|
|
46
|
+
try {
|
|
47
|
+
const parts = token.split('.');
|
|
48
|
+
if (parts.length !== 3)
|
|
49
|
+
return true;
|
|
50
|
+
const payload = parts[1];
|
|
51
|
+
const paddedPayload = payload + '='.repeat((4 - (payload.length % 4)) % 4);
|
|
52
|
+
const decoded = atob(paddedPayload);
|
|
53
|
+
const parsedPayload = JSON.parse(decoded);
|
|
54
|
+
if (parsedPayload.exp) {
|
|
55
|
+
return Date.now() >= parsedPayload.exp * 1000;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error('Failed to check token expiration:', error);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get token expiration time
|
|
66
|
+
*/
|
|
67
|
+
export function getTokenExpiration(token) {
|
|
68
|
+
try {
|
|
69
|
+
const parts = token.split('.');
|
|
70
|
+
if (parts.length !== 3)
|
|
71
|
+
return null;
|
|
72
|
+
const payload = parts[1];
|
|
73
|
+
const paddedPayload = payload + '='.repeat((4 - (payload.length % 4)) % 4);
|
|
74
|
+
const decoded = atob(paddedPayload);
|
|
75
|
+
const parsedPayload = JSON.parse(decoded);
|
|
76
|
+
if (parsedPayload.exp) {
|
|
77
|
+
return new Date(parsedPayload.exp * 1000);
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error('Failed to get token expiration:', error);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -8,13 +8,19 @@ import { resolveEnvValue } from './env';
|
|
|
8
8
|
*/
|
|
9
9
|
const loadLocalDevConfig = async (configVariant = 'default') => {
|
|
10
10
|
try {
|
|
11
|
-
//
|
|
12
|
-
|
|
11
|
+
// Check for explicit environment override
|
|
12
|
+
const env = resolveEnvValue('TAGADA_ENV') || resolveEnvValue('TAGADA_ENVIRONMENT');
|
|
13
|
+
if (env === 'production') {
|
|
14
|
+
// Skip local config loading if explicitly in production mode
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
// Only try to load local config in TRUE local development (not deployed CDN instances)
|
|
18
|
+
// Exclude CDN subdomains (e.g., instance-id.cdn.localhost) by checking for 'cdn.' prefix
|
|
13
19
|
const isLocalDev = typeof window !== 'undefined' &&
|
|
14
20
|
(window.location.hostname === 'localhost' ||
|
|
15
|
-
window.location.hostname
|
|
16
|
-
window.location.hostname.includes('.
|
|
17
|
-
|
|
21
|
+
window.location.hostname === '127.0.0.1' ||
|
|
22
|
+
window.location.hostname.includes('ngrok-free.app')) &&
|
|
23
|
+
!window.location.hostname.includes('.cdn.');
|
|
18
24
|
if (!isLocalDev) {
|
|
19
25
|
return null;
|
|
20
26
|
}
|
|
@@ -81,6 +87,43 @@ const loadLocalDevConfig = async (configVariant = 'default') => {
|
|
|
81
87
|
return null;
|
|
82
88
|
}
|
|
83
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Load static resources for local development
|
|
92
|
+
* Loads /config/resources.static.json
|
|
93
|
+
*/
|
|
94
|
+
const loadStaticResources = async () => {
|
|
95
|
+
try {
|
|
96
|
+
// Check for explicit environment override
|
|
97
|
+
const env = resolveEnvValue('TAGADA_ENV') || resolveEnvValue('TAGADA_ENVIRONMENT');
|
|
98
|
+
if (env === 'production') {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
// Only try to load in TRUE local development (not deployed CDN instances)
|
|
102
|
+
// Exclude CDN subdomains (e.g., instance-id.cdn.localhost) by checking for 'cdn.' prefix
|
|
103
|
+
const isLocalDev = typeof window !== 'undefined' &&
|
|
104
|
+
(window.location.hostname === 'localhost' ||
|
|
105
|
+
window.location.hostname === '127.0.0.1' ||
|
|
106
|
+
window.location.hostname.includes('ngrok-free.app')) &&
|
|
107
|
+
!window.location.hostname.includes('.cdn.');
|
|
108
|
+
if (!isLocalDev) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
// Load static resources file
|
|
112
|
+
console.log('🛠️ Attempting to load /config/resources.static.json...');
|
|
113
|
+
const response = await fetch('/config/resources.static.json');
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
console.log('🛠️ resources.static.json not found or failed to load');
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const staticResources = await response.json();
|
|
119
|
+
console.log('🛠️ ✅ Loaded local static resources:', staticResources);
|
|
120
|
+
return staticResources;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error('🛠️ ❌ Error loading static resources:', error);
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
84
127
|
/**
|
|
85
128
|
* Helper to get content from meta tag
|
|
86
129
|
*/
|
|
@@ -137,6 +180,8 @@ const loadProductionConfig = async () => {
|
|
|
137
180
|
* Handles local dev, production, and raw config
|
|
138
181
|
*/
|
|
139
182
|
export const loadPluginConfig = async (configVariant = 'default', rawConfig) => {
|
|
183
|
+
// Load static resources first (only in local dev)
|
|
184
|
+
const staticResources = await loadStaticResources();
|
|
140
185
|
// If raw config is provided, use it directly
|
|
141
186
|
if (rawConfig) {
|
|
142
187
|
return {
|
|
@@ -144,21 +189,32 @@ export const loadPluginConfig = async (configVariant = 'default', rawConfig) =>
|
|
|
144
189
|
accountId: rawConfig.accountId,
|
|
145
190
|
basePath: rawConfig.basePath ?? '/',
|
|
146
191
|
config: rawConfig.config ?? {},
|
|
192
|
+
staticResources: staticResources ?? undefined,
|
|
147
193
|
};
|
|
148
194
|
}
|
|
149
195
|
else {
|
|
150
196
|
const rawConfig = await createRawPluginConfig();
|
|
151
197
|
if (rawConfig) {
|
|
152
|
-
return
|
|
198
|
+
return {
|
|
199
|
+
...rawConfig,
|
|
200
|
+
staticResources: staticResources ?? undefined,
|
|
201
|
+
};
|
|
153
202
|
}
|
|
154
203
|
}
|
|
155
204
|
// Try local development config
|
|
156
205
|
const localConfig = await loadLocalDevConfig(configVariant);
|
|
157
206
|
if (localConfig) {
|
|
158
|
-
return
|
|
207
|
+
return {
|
|
208
|
+
...localConfig,
|
|
209
|
+
staticResources: staticResources ?? undefined,
|
|
210
|
+
};
|
|
159
211
|
}
|
|
160
212
|
// Fall back to production config
|
|
161
|
-
|
|
213
|
+
const productionConfig = await loadProductionConfig();
|
|
214
|
+
return {
|
|
215
|
+
...productionConfig,
|
|
216
|
+
staticResources: staticResources ?? undefined,
|
|
217
|
+
};
|
|
162
218
|
};
|
|
163
219
|
/**
|
|
164
220
|
* Helper to load local config file for development (from /config directory)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side token storage utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Set the CMS token in localStorage
|
|
6
|
+
*/
|
|
7
|
+
export declare function setClientToken(token: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* Get the CMS token from localStorage
|
|
10
|
+
*/
|
|
11
|
+
export declare function getClientToken(): string | null;
|
|
12
|
+
/**
|
|
13
|
+
* Clear the CMS token from localStorage
|
|
14
|
+
*/
|
|
15
|
+
export declare function clearClientToken(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Check if a token exists in localStorage
|
|
18
|
+
*/
|
|
19
|
+
export declare function hasClientToken(): boolean;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side token storage utilities
|
|
3
|
+
*/
|
|
4
|
+
const TOKEN_KEY = 'cms_token';
|
|
5
|
+
/**
|
|
6
|
+
* Set the CMS token in localStorage
|
|
7
|
+
*/
|
|
8
|
+
export function setClientToken(token) {
|
|
9
|
+
if (typeof window !== 'undefined') {
|
|
10
|
+
try {
|
|
11
|
+
localStorage.setItem(TOKEN_KEY, token);
|
|
12
|
+
window.dispatchEvent(new Event('storage'));
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
console.error('Failed to save token to localStorage:', error);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get the CMS token from localStorage
|
|
21
|
+
*/
|
|
22
|
+
export function getClientToken() {
|
|
23
|
+
if (typeof window !== 'undefined') {
|
|
24
|
+
try {
|
|
25
|
+
return localStorage.getItem(TOKEN_KEY);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error('Failed to get token from localStorage:', error);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Clear the CMS token from localStorage
|
|
36
|
+
*/
|
|
37
|
+
export function clearClientToken() {
|
|
38
|
+
if (typeof window !== 'undefined') {
|
|
39
|
+
try {
|
|
40
|
+
localStorage.removeItem(TOKEN_KEY);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error('Failed to clear token from localStorage:', error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check if a token exists in localStorage
|
|
49
|
+
*/
|
|
50
|
+
export function hasClientToken() {
|
|
51
|
+
return !!getClientToken();
|
|
52
|
+
}
|
|
@@ -5,7 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
*/
|
|
6
6
|
import { useCallback, useEffect, useState } from 'react';
|
|
7
7
|
import { useExpressPaymentMethods } from '../hooks/useExpressPaymentMethods';
|
|
8
|
-
export const ApplePayButton = ({ className = '', disabled = false, onSuccess, onError, onCancel, storeName, currencyCode = 'USD', variant = '
|
|
8
|
+
export const ApplePayButton = ({ className = '', disabled = false, onSuccess, onError, onCancel, storeName, currencyCode = 'USD', variant = 'default', size = 'lg', checkout, }) => {
|
|
9
9
|
const { applePayPaymentMethod, shippingMethods, lineItems, handleAddExpressId, updateCheckoutSessionValues, updateCustomerEmail, reComputeOrderSummary, setError: setContextError, } = useExpressPaymentMethods();
|
|
10
10
|
const [processingPayment, setProcessingPayment] = useState(false);
|
|
11
11
|
const [isApplePayAvailable, setIsApplePayAvailable] = useState(false);
|
|
@@ -582,7 +582,96 @@ export const DebugDrawer = ({ isOpen, onClose }) => {
|
|
|
582
582
|
color: isCurrent ? '#d1fae5' : '#9ca3af',
|
|
583
583
|
marginLeft: '8px',
|
|
584
584
|
}, children: ["\u2022 ", cond.type, " \u2192 ", cond.nextStepId] }, condIdx)))] }))] }, step.id));
|
|
585
|
-
}) })] })),
|
|
585
|
+
}) })] })), context.debugFunnel.data?.session && (_jsxs("div", { style: { marginBottom: '16px' }, children: [_jsx("h4", { style: { margin: '0 0 8px 0', color: '#60a5fa', fontSize: '13px' }, children: "\uD83D\uDCE6 Resources & Context" }), context.debugFunnel.data.session.resources &&
|
|
586
|
+
Object.keys(context.debugFunnel.data.session.resources).length > 0 && (_jsxs("div", { style: { marginBottom: '12px' }, children: [_jsxs("div", { style: {
|
|
587
|
+
fontSize: '11px',
|
|
588
|
+
color: '#10b981',
|
|
589
|
+
fontWeight: 'bold',
|
|
590
|
+
marginBottom: '6px',
|
|
591
|
+
}, children: ["\uD83C\uDFAF Available Resources (", Object.keys(context.debugFunnel.data.session.resources).length, ")"] }), _jsx("div", { style: { display: 'grid', gap: '8px' }, children: Object.entries(context.debugFunnel.data.session.resources).map(([key, value]) => {
|
|
592
|
+
// Skip undefined/null resources
|
|
593
|
+
if (value === undefined || value === null)
|
|
594
|
+
return null;
|
|
595
|
+
// Check if it's an array or single resource
|
|
596
|
+
const isArray = Array.isArray(value);
|
|
597
|
+
const resourceCount = isArray ? value.length : 1;
|
|
598
|
+
return (_jsxs("div", { style: {
|
|
599
|
+
border: '1px solid #374151',
|
|
600
|
+
borderRadius: '6px',
|
|
601
|
+
padding: '10px',
|
|
602
|
+
backgroundColor: '#111827',
|
|
603
|
+
}, children: [_jsxs("div", { style: {
|
|
604
|
+
display: 'flex',
|
|
605
|
+
justifyContent: 'space-between',
|
|
606
|
+
alignItems: 'center',
|
|
607
|
+
marginBottom: '8px',
|
|
608
|
+
}, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx("span", { style: { color: '#10b981', fontSize: '14px' }, children: key === 'order' || key === 'mainOrder'
|
|
609
|
+
? '🛒'
|
|
610
|
+
: key === 'customer'
|
|
611
|
+
? '👤'
|
|
612
|
+
: key === 'checkout'
|
|
613
|
+
? '💳'
|
|
614
|
+
: key === 'offer'
|
|
615
|
+
? '🎁'
|
|
616
|
+
: key === 'subscription'
|
|
617
|
+
? '🔄'
|
|
618
|
+
: '📄' }), _jsx("span", { style: {
|
|
619
|
+
color: '#f9fafb',
|
|
620
|
+
fontWeight: 'bold',
|
|
621
|
+
fontSize: '12px',
|
|
622
|
+
}, children: key }), isArray && (_jsxs("span", { style: {
|
|
623
|
+
fontSize: '10px',
|
|
624
|
+
backgroundColor: '#3b82f6',
|
|
625
|
+
color: '#fff',
|
|
626
|
+
padding: '2px 6px',
|
|
627
|
+
borderRadius: '3px',
|
|
628
|
+
}, children: [resourceCount, " items"] }))] }), !isArray && value && typeof value === 'object' && (_jsx("div", { style: { fontSize: '10px', color: '#9ca3af' }, children: value.id && (_jsx("span", { style: { fontFamily: 'monospace' }, children: value.id })) }))] }), !isArray && value && typeof value === 'object' && (_jsxs("div", { style: {
|
|
629
|
+
display: 'grid',
|
|
630
|
+
gridTemplateColumns: '1fr 1fr',
|
|
631
|
+
gap: '6px',
|
|
632
|
+
fontSize: '11px',
|
|
633
|
+
marginBottom: '8px',
|
|
634
|
+
}, children: [value.amount !== undefined && (_jsxs("div", { children: [_jsx("span", { style: { color: '#6b7280' }, children: "Amount: " }), _jsx("span", { style: { color: '#10b981', fontWeight: 'bold' }, children: (() => {
|
|
635
|
+
try {
|
|
636
|
+
return context.money.formatMoney(Number(value.amount) || 0, String(value.currency) || 'USD', context.locale.locale);
|
|
637
|
+
}
|
|
638
|
+
catch {
|
|
639
|
+
return `${value.amount} ${value.currency || ''}`;
|
|
640
|
+
}
|
|
641
|
+
})() })] })), value.email !== undefined && (_jsxs("div", { children: [_jsx("span", { style: { color: '#6b7280' }, children: "Email: " }), _jsx("span", { style: { color: '#60a5fa' }, children: value.email })] })), value.status !== undefined && (_jsxs("div", { children: [_jsx("span", { style: { color: '#6b7280' }, children: "Status: " }), _jsx("span", { style: { color: '#f59e0b' }, children: value.status })] })), value.currency !== undefined && (_jsxs("div", { children: [_jsx("span", { style: { color: '#6b7280' }, children: "Currency: " }), _jsx("span", { style: { color: '#8b5cf6' }, children: value.currency })] }))] })), _jsxs("details", { style: { marginTop: '6px' }, children: [_jsx("summary", { style: {
|
|
642
|
+
cursor: 'pointer',
|
|
643
|
+
fontSize: '10px',
|
|
644
|
+
color: '#60a5fa',
|
|
645
|
+
fontWeight: 'bold',
|
|
646
|
+
}, children: "Show Full Data" }), _jsx("div", { style: { marginTop: '6px', fontSize: '10px' }, children: _jsx(TreeView, { data: value, name: key, maxLevel: 3 }) })] })] }, key));
|
|
647
|
+
}) })] })), context.debugFunnel.data.session.metadata &&
|
|
648
|
+
Object.keys(context.debugFunnel.data.session.metadata).length > 0 && (_jsxs("div", { style: { marginBottom: '12px' }, children: [_jsxs("div", { style: {
|
|
649
|
+
fontSize: '11px',
|
|
650
|
+
color: '#8b5cf6',
|
|
651
|
+
fontWeight: 'bold',
|
|
652
|
+
marginBottom: '6px',
|
|
653
|
+
}, children: ["\uD83D\uDCDD Session Metadata (", Object.keys(context.debugFunnel.data.session.metadata).length, ")"] }), _jsx("div", { style: {
|
|
654
|
+
border: '1px solid #374151',
|
|
655
|
+
borderRadius: '6px',
|
|
656
|
+
padding: '10px',
|
|
657
|
+
backgroundColor: '#111827',
|
|
658
|
+
}, children: _jsx(TreeView, { data: context.debugFunnel.data.session.metadata, name: "metadata", maxLevel: 2 }) })] })), _jsxs("div", { style: { marginBottom: '12px' }, children: [_jsx("div", { style: {
|
|
659
|
+
fontSize: '11px',
|
|
660
|
+
color: '#f59e0b',
|
|
661
|
+
fontWeight: 'bold',
|
|
662
|
+
marginBottom: '6px',
|
|
663
|
+
}, children: "\u2139\uFE0F Session Context" }), _jsx("div", { style: {
|
|
664
|
+
border: '1px solid #374151',
|
|
665
|
+
borderRadius: '6px',
|
|
666
|
+
padding: '10px',
|
|
667
|
+
backgroundColor: '#111827',
|
|
668
|
+
}, children: _jsxs("div", { style: { display: 'grid', gap: '6px', fontSize: '11px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { style: { color: '#6b7280' }, children: "Customer ID:" }), _jsx("span", { style: { color: '#60a5fa', fontFamily: 'monospace', fontSize: '10px' }, children: context.debugFunnel.data.session.customerId })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { style: { color: '#6b7280' }, children: "Store ID:" }), _jsx("span", { style: { color: '#60a5fa', fontFamily: 'monospace', fontSize: '10px' }, children: context.debugFunnel.data.session.storeId })] }), context.debugFunnel.data.session.environment && (_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { style: { color: '#6b7280' }, children: "Environment:" }), _jsx("span", { style: { color: '#10b981' }, children: context.debugFunnel.data.session.environment })] })), context.debugFunnel.data.session.startedAt && (_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { style: { color: '#6b7280' }, children: "Started At:" }), _jsx("span", { style: { color: '#9ca3af', fontSize: '10px' }, children: new Date(context.debugFunnel.data.session.startedAt).toLocaleString() })] })), context.debugFunnel.data.session.lastActivityAt && (_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { style: { color: '#6b7280' }, children: "Last Activity:" }), _jsx("span", { style: { color: '#9ca3af', fontSize: '10px' }, children: new Date(context.debugFunnel.data.session.lastActivityAt).toLocaleString() })] }))] }) })] })] })), _jsxs("details", { style: { marginTop: '12px' }, children: [_jsx("summary", { style: {
|
|
669
|
+
cursor: 'pointer',
|
|
670
|
+
fontWeight: 'bold',
|
|
671
|
+
fontSize: '13px',
|
|
672
|
+
color: '#60a5fa',
|
|
673
|
+
marginBottom: '8px',
|
|
674
|
+
}, children: "\uD83D\uDD0D Raw Funnel Data (Debug)" }), _jsx("div", { style: { fontSize: '11px', marginTop: '8px' }, children: _jsx(TreeView, { data: context.debugFunnel.data, name: "funnelData", maxLevel: 4 }) })] })] })) : (_jsx("p", { style: { color: '#6b7280' }, children: "No funnel hook active" }))] })), activeTab === 'checkout' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "Checkout Debug" }), context.debugCheckout.isActive ? (_jsxs("div", { children: [_jsxs("div", { style: { marginBottom: '20px' }, children: [_jsx("h4", { style: { margin: '0 0 12px 0', color: '#60a5fa' }, children: "Status" }), _jsxs("div", { style: { display: 'grid', gap: '8px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Hook Active:" }), _jsx("span", { style: { color: '#10b981' }, children: "\u2705 Yes" })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Loading:" }), _jsx("span", { style: { color: context.debugCheckout.isLoading ? '#f59e0b' : '#10b981' }, children: context.debugCheckout.isLoading ? '⏳ Yes' : '✅ No' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Has Error:" }), _jsx("span", { style: { color: context.debugCheckout.error ? '#ef4444' : '#10b981' }, children: context.debugCheckout.error ? '❌ Yes' : '✅ No' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Last Updated:" }), _jsx("span", { style: { color: '#9ca3af', fontSize: '12px' }, children: context.debugCheckout.lastUpdated?.toLocaleTimeString() || 'Never' })] })] })] }), context.debugCheckout.error && (_jsxs("div", { style: { marginBottom: '20px' }, children: [_jsx("h4", { style: { margin: '0 0 12px 0', color: '#ef4444' }, children: "Error Details" }), _jsxs("div", { style: {
|
|
586
675
|
backgroundColor: '#372c2c',
|
|
587
676
|
padding: '12px',
|
|
588
677
|
borderRadius: '4px',
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Accessing Funnel Context & Resources
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates how to access data from previous steps
|
|
5
|
+
* using the funnel context, including order, customer, and other resources.
|
|
6
|
+
*/
|
|
7
|
+
export declare function FunnelContextExample(): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
/**
|
|
9
|
+
* Simple example for documentation
|
|
10
|
+
* This can now be called with or without options
|
|
11
|
+
*/
|
|
12
|
+
export declare function SimpleContextExample(): import("react/jsx-runtime").JSX.Element;
|