@tagadapay/plugin-sdk 2.1.3 → 2.2.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.
@@ -1,3 +1,5 @@
1
+ export declare const AVAILABLE_LANGUAGES: readonly ["en", "ru", "de", "fr", "es", "zh", "hi", "pt", "ja", "ar", "it", "he"];
2
+ export type SupportedLanguage = typeof AVAILABLE_LANGUAGES[number];
1
3
  export interface Country {
2
4
  code: string;
3
5
  name: string;
@@ -11,14 +13,14 @@ export interface State {
11
13
  countryCode: string;
12
14
  uniqueKey?: string;
13
15
  }
14
- export declare const getCountries: () => Country[];
15
- export declare const getAllStates: () => State[];
16
- export declare const getStatesForCountry: (countryCode: string) => State[];
17
- export declare const findCountryByName: (countryName: string) => Country | null;
18
- export declare const findRegionByCode: (regionCode: string) => State | null;
19
- export declare const isValidCountryCode: (countryCode: string) => boolean;
20
- export declare const isValidStateCode: (countryCode: string, stateCode: string) => boolean;
21
- export declare const getCountryWithRegions: (countryCode: string) => {
16
+ export declare const getCountries: (language?: SupportedLanguage) => Country[];
17
+ export declare const getAllStates: (language?: SupportedLanguage) => State[];
18
+ export declare const getStatesForCountry: (countryCode: string, language?: SupportedLanguage) => State[];
19
+ export declare const findCountryByName: (countryName: string, language?: SupportedLanguage) => Country | null;
20
+ export declare const findRegionByCode: (regionCode: string, language?: SupportedLanguage) => State | null;
21
+ export declare const isValidCountryCode: (countryCode: string, language?: SupportedLanguage) => boolean;
22
+ export declare const isValidStateCode: (countryCode: string, stateCode: string, language?: SupportedLanguage) => boolean;
23
+ export declare const getCountryWithRegions: (countryCode: string, language?: SupportedLanguage) => {
22
24
  country: {
23
25
  code: any;
24
26
  name: any;
@@ -1,11 +1,70 @@
1
- // Import the ISO3166 library API functions
1
+ // Import static language databases for better browser compatibility
2
+ // All supported languages from iso3166-2-db package
2
3
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3
4
  // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
4
- import { getDataSet, reduce } from 'iso3166-2-db';
5
- // Get the full dataset with default English language
6
- const worldDatabase = reduce(getDataSet(), 'en');
5
+ import enDatabase from 'iso3166-2-db/i18n/dispute/UN/en';
6
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
7
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
8
+ import ruDatabase from 'iso3166-2-db/i18n/dispute/UN/ru';
9
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
10
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
11
+ import deDatabase from 'iso3166-2-db/i18n/dispute/UN/de';
12
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
13
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
14
+ import frDatabase from 'iso3166-2-db/i18n/dispute/UN/fr';
15
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
16
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
17
+ import esDatabase from 'iso3166-2-db/i18n/dispute/UN/es';
18
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
19
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
20
+ import zhDatabase from 'iso3166-2-db/i18n/dispute/UN/zh';
21
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
22
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
23
+ import hiDatabase from 'iso3166-2-db/i18n/dispute/UN/hi';
24
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
25
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
26
+ import ptDatabase from 'iso3166-2-db/i18n/dispute/UN/pt';
27
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
28
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
29
+ import jaDatabase from 'iso3166-2-db/i18n/dispute/UN/ja';
30
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
31
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
32
+ import arDatabase from 'iso3166-2-db/i18n/dispute/UN/ar';
33
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
34
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
35
+ import itDatabase from 'iso3166-2-db/i18n/dispute/UN/it';
36
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
37
+ // @ts-ignore - iso3166-2-db doesn't have TypeScript definitions
38
+ import heDatabase from 'iso3166-2-db/i18n/dispute/UN/he';
39
+ // All available languages from iso3166-2-db package
40
+ export const AVAILABLE_LANGUAGES = ['en', 'ru', 'de', 'fr', 'es', 'zh', 'hi', 'pt', 'ja', 'ar', 'it', 'he'];
41
+ // Static language database mapping for all supported languages
42
+ const languageDatabases = {
43
+ en: enDatabase, // English
44
+ ru: ruDatabase, // Russian
45
+ de: deDatabase, // German
46
+ fr: frDatabase, // French
47
+ es: esDatabase, // Spanish
48
+ zh: zhDatabase, // Chinese
49
+ hi: hiDatabase, // Hindi
50
+ pt: ptDatabase, // Portuguese
51
+ ja: jaDatabase, // Japanese
52
+ ar: arDatabase, // Arabic
53
+ it: itDatabase, // Italian
54
+ he: heDatabase, // Hebrew
55
+ };
56
+ // Function to get language-specific database
57
+ function getWorldDatabase(language = 'en') {
58
+ const database = languageDatabases[language];
59
+ if (!database) {
60
+ console.warn(`Language ${language} not available, falling back to English`);
61
+ return languageDatabases.en;
62
+ }
63
+ return database;
64
+ }
7
65
  // Transform the ISO3166 data into our expected format
8
- export const getCountries = () => {
66
+ export const getCountries = (language = 'en') => {
67
+ const worldDatabase = getWorldDatabase(language);
9
68
  const countries = [];
10
69
  Object.keys(worldDatabase).forEach((countryCode) => {
11
70
  const countryData = worldDatabase[countryCode];
@@ -21,7 +80,8 @@ export const getCountries = () => {
21
80
  return countries.sort((a, b) => a.name.localeCompare(b.name));
22
81
  };
23
82
  // Get all states/regions for all countries
24
- export const getAllStates = () => {
83
+ export const getAllStates = (language = 'en') => {
84
+ const worldDatabase = getWorldDatabase(language);
25
85
  const states = [];
26
86
  Object.keys(worldDatabase).forEach((countryCode) => {
27
87
  const countryData = worldDatabase[countryCode];
@@ -40,7 +100,8 @@ export const getAllStates = () => {
40
100
  return states.sort((a, b) => a.name.localeCompare(b.name));
41
101
  };
42
102
  // Get states for a specific country
43
- export const getStatesForCountry = (countryCode) => {
103
+ export const getStatesForCountry = (countryCode, language = 'en') => {
104
+ const worldDatabase = getWorldDatabase(language);
44
105
  const countryData = worldDatabase[countryCode];
45
106
  if (!countryData?.regions || !Array.isArray(countryData.regions)) {
46
107
  return [];
@@ -55,8 +116,8 @@ export const getStatesForCountry = (countryCode) => {
55
116
  .sort((a, b) => a.name.localeCompare(b.name));
56
117
  };
57
118
  // Find country by name (fuzzy search)
58
- export const findCountryByName = (countryName) => {
59
- const countries = getCountries();
119
+ export const findCountryByName = (countryName, language = 'en') => {
120
+ const countries = getCountries(language);
60
121
  const normalizedSearchName = countryName.toLowerCase().trim();
61
122
  // Exact match first
62
123
  const exactMatch = countries.find((country) => country.name.toLowerCase() === normalizedSearchName);
@@ -68,24 +129,26 @@ export const findCountryByName = (countryName) => {
68
129
  return partialMatch ?? null;
69
130
  };
70
131
  // Find region by ISO code (e.g., "US-CA" for California)
71
- export const findRegionByCode = (regionCode) => {
132
+ export const findRegionByCode = (regionCode, language = 'en') => {
72
133
  const [countryCode, stateCode] = regionCode.split('-');
73
134
  if (!countryCode || !stateCode)
74
135
  return null;
75
- const states = getStatesForCountry(countryCode);
136
+ const states = getStatesForCountry(countryCode, language);
76
137
  return states.find((state) => state.code === stateCode) ?? null;
77
138
  };
78
139
  // Validate if a country code exists
79
- export const isValidCountryCode = (countryCode) => {
140
+ export const isValidCountryCode = (countryCode, language = 'en') => {
141
+ const worldDatabase = getWorldDatabase(language);
80
142
  return Object.prototype.hasOwnProperty.call(worldDatabase, countryCode);
81
143
  };
82
144
  // Validate if a state code exists for a given country
83
- export const isValidStateCode = (countryCode, stateCode) => {
84
- const states = getStatesForCountry(countryCode);
145
+ export const isValidStateCode = (countryCode, stateCode, language = 'en') => {
146
+ const states = getStatesForCountry(countryCode, language);
85
147
  return states.some((state) => state.code === stateCode);
86
148
  };
87
149
  // Get country info including regions
88
- export const getCountryWithRegions = (countryCode) => {
150
+ export const getCountryWithRegions = (countryCode, language = 'en') => {
151
+ const worldDatabase = getWorldDatabase(language);
89
152
  const countryData = worldDatabase[countryCode];
90
153
  if (!countryData)
91
154
  return null;
@@ -97,6 +160,6 @@ export const getCountryWithRegions = (countryCode) => {
97
160
  numeric: countryData.numeric,
98
161
  uniqueKey: `country-${countryData.iso}`,
99
162
  },
100
- regions: getStatesForCountry(countryCode),
163
+ regions: getStatesForCountry(countryCode, language),
101
164
  };
102
165
  };
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState, useEffect } from 'react';
4
4
  import { useTagadaContext } from '../providers/TagadaProvider';
5
+ import { usePluginConfig } from '../hooks/usePluginConfig';
5
6
  const safeStringify = (value) => {
6
7
  if (value === null)
7
8
  return 'null';
@@ -74,6 +75,7 @@ const TreeView = ({ data, name, level = 0, maxLevel = 3 }) => {
74
75
  };
75
76
  export const DebugDrawer = ({ isOpen, onClose }) => {
76
77
  const context = useTagadaContext();
78
+ const pluginConfig = usePluginConfig();
77
79
  const [activeTab, setActiveTab] = useState('overview');
78
80
  useEffect(() => {
79
81
  const handleEscape = (e) => {
@@ -88,6 +90,7 @@ export const DebugDrawer = ({ isOpen, onClose }) => {
88
90
  return null;
89
91
  const baseTabs = [
90
92
  { id: 'overview', label: 'Overview' },
93
+ { id: 'config', label: 'Config' },
91
94
  { id: 'store', label: 'Store' },
92
95
  { id: 'session', label: 'Session' },
93
96
  { id: 'auth', label: 'Auth' },
@@ -154,7 +157,19 @@ export const DebugDrawer = ({ isOpen, onClose }) => {
154
157
  padding: '16px',
155
158
  overflow: 'auto',
156
159
  backgroundColor: '#1f2937',
157
- }, children: [activeTab === 'overview' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "SDK Overview" }), _jsxs("div", { style: { display: 'grid', gap: '12px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Initialized:" }), _jsx("span", { style: { color: context.isInitialized ? '#10b981' : '#ef4444' }, children: context.isInitialized ? '✅ Yes' : '❌ No' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Loading:" }), _jsx("span", { style: { color: context.isLoading ? '#f59e0b' : '#10b981' }, children: context.isLoading ? '⏳ Yes' : '✅ No' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Environment:" }), _jsx("span", { style: { color: '#60a5fa' }, children: context.environment.apiConfig.baseUrl.includes('dev') ? 'Development' : 'Production' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "API Base URL:" }), _jsx("span", { style: { color: '#10b981' }, children: context.environment.apiConfig.baseUrl })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Store ID:" }), _jsx("span", { style: { color: '#60a5fa' }, children: context.store?.id || 'Not set' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Customer ID:" }), _jsx("span", { style: { color: '#60a5fa' }, children: context.customer?.id || 'Not set' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Authenticated:" }), _jsx("span", { style: { color: context.auth.isAuthenticated ? '#10b981' : '#ef4444' }, children: context.auth.isAuthenticated ? '✅ Yes' : '❌ No' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Currency:" }), _jsxs("span", { style: { color: '#f59e0b' }, children: [context.currency.code, " (", context.currency.symbol, ")"] })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Locale:" }), _jsx("span", { style: { color: '#8b5cf6' }, children: context.locale.locale })] })] })] })), activeTab === 'store' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "Store Configuration" }), context.store ? (_jsx(TreeView, { data: context.store, name: "store" })) : (_jsx("p", { style: { color: '#6b7280' }, children: "No store data available" }))] })), activeTab === 'session' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "Session Data" }), context.session ? (_jsx(TreeView, { data: context.session, name: "session" })) : (_jsx("p", { style: { color: '#6b7280' }, children: "No session data available" }))] })), activeTab === 'auth' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "Authentication State" }), _jsx(TreeView, { data: context.auth, name: "auth" }), context.customer && (_jsxs(_Fragment, { children: [_jsx("h4", { style: { margin: '24px 0 12px 0', color: '#60a5fa' }, children: "Customer Data" }), _jsx(TreeView, { data: context.customer, name: "customer" })] }))] })), activeTab === 'items' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "Items & Discounts" }), context.debugCheckout.data?.checkout?.summary ? (_jsxs("div", { children: [_jsxs("div", { style: { marginBottom: '24px' }, children: [_jsx("h4", { style: { margin: '0 0 12px 0', color: '#60a5fa' }, children: "Summary" }), _jsxs("div", { style: { display: 'grid', gap: '8px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Currency:" }), _jsx("span", { style: { color: '#f59e0b' }, children: context.debugCheckout.data.checkout.summary.currency })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Subtotal:" }), _jsx("span", { style: { color: '#9ca3af' }, children: (() => {
160
+ }, children: [activeTab === 'overview' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "SDK Overview" }), _jsxs("div", { style: { display: 'grid', gap: '12px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Initialized:" }), _jsx("span", { style: { color: context.isInitialized ? '#10b981' : '#ef4444' }, children: context.isInitialized ? '✅ Yes' : '❌ No' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Loading:" }), _jsx("span", { style: { color: context.isLoading ? '#f59e0b' : '#10b981' }, children: context.isLoading ? '⏳ Yes' : '✅ No' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Environment:" }), _jsx("span", { style: { color: '#60a5fa' }, children: context.environment.apiConfig.baseUrl.includes('dev') ? 'Development' : 'Production' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "API Base URL:" }), _jsx("span", { style: { color: '#10b981' }, children: context.environment.apiConfig.baseUrl })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Store ID:" }), _jsx("span", { style: { color: '#60a5fa' }, children: context.store?.id || 'Not set' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Customer ID:" }), _jsx("span", { style: { color: '#60a5fa' }, children: context.customer?.id || 'Not set' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Authenticated:" }), _jsx("span", { style: { color: context.auth.isAuthenticated ? '#10b981' : '#ef4444' }, children: context.auth.isAuthenticated ? '✅ Yes' : '❌ No' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Currency:" }), _jsxs("span", { style: { color: '#f59e0b' }, children: [context.currency.code, " (", context.currency.symbol, ")"] })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Locale:" }), _jsx("span", { style: { color: '#8b5cf6' }, children: context.locale.locale })] })] })] })), activeTab === 'config' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "Plugin Configuration" }), _jsxs("div", { style: { marginBottom: '24px' }, children: [_jsx("h4", { style: { margin: '0 0 12px 0', color: '#9ca3af', fontSize: '14px' }, children: "Configuration Summary" }), _jsxs("div", { style: { display: 'grid', gap: '8px', fontSize: '13px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Store ID:" }), _jsx("span", { style: { color: '#60a5fa' }, children: pluginConfig.storeId || 'Not set' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Account ID:" }), _jsx("span", { style: { color: '#60a5fa' }, children: pluginConfig.accountId || 'Not set' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Base Path:" }), _jsx("span", { style: { color: '#10b981' }, children: pluginConfig.basePath || '/' })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Config Name:" }), _jsx("span", { style: { color: '#f59e0b' }, children: pluginConfig.config?.configName || 'default' })] }), pluginConfig.config?.branding?.primaryColor && (_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Primary Color:" }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' }, children: [_jsx("div", { style: {
161
+ width: '16px',
162
+ height: '16px',
163
+ backgroundColor: pluginConfig.config.branding.primaryColor,
164
+ border: '1px solid #374151',
165
+ borderRadius: '3px',
166
+ } }), _jsx("span", { style: { color: '#10b981' }, children: pluginConfig.config.branding.primaryColor })] })] }))] })] }), _jsxs("div", { style: { marginBottom: '16px' }, children: [_jsx("h4", { style: { margin: '0 0 12px 0', color: '#9ca3af', fontSize: '14px' }, children: "Full Configuration" }), _jsx(TreeView, { data: pluginConfig, name: "pluginConfig" })] }), process.env.NODE_ENV === 'development' && (_jsxs("div", { style: {
167
+ padding: '12px',
168
+ backgroundColor: '#1f2937',
169
+ border: '1px solid #374151',
170
+ borderRadius: '6px',
171
+ marginTop: '16px',
172
+ }, children: [_jsx("h4", { style: { margin: '0 0 8px 0', color: '#f59e0b', fontSize: '14px' }, children: "Development Mode" }), _jsxs("p", { style: { margin: '0', fontSize: '12px', color: '#9ca3af', lineHeight: '1.4' }, children: ["Configuration is loaded from ", _jsx("code", { style: { color: '#60a5fa' }, children: "/.local.json" }), " and", ' ', _jsxs("code", { style: { color: '#60a5fa' }, children: ["/config/", pluginConfig.config?.configName || 'default', ".tgd.json"] })] }), _jsxs("p", { style: { margin: '8px 0 0 0', fontSize: '12px', color: '#9ca3af', lineHeight: '1.4' }, children: ["Local config variant:", ' ', _jsx("code", { style: { color: '#f59e0b' }, children: pluginConfig.config?.configName || 'default' })] })] }))] })), activeTab === 'store' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "Store Configuration" }), context.store ? (_jsx(TreeView, { data: context.store, name: "store" })) : (_jsx("p", { style: { color: '#6b7280' }, children: "No store data available" }))] })), activeTab === 'session' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "Session Data" }), context.session ? (_jsx(TreeView, { data: context.session, name: "session" })) : (_jsx("p", { style: { color: '#6b7280' }, children: "No session data available" }))] })), activeTab === 'auth' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "Authentication State" }), _jsx(TreeView, { data: context.auth, name: "auth" }), context.customer && (_jsxs(_Fragment, { children: [_jsx("h4", { style: { margin: '24px 0 12px 0', color: '#60a5fa' }, children: "Customer Data" }), _jsx(TreeView, { data: context.customer, name: "customer" })] }))] })), activeTab === 'items' && (_jsxs("div", { children: [_jsx("h3", { style: { margin: '0 0 16px 0', color: '#60a5fa' }, children: "Items & Discounts" }), context.debugCheckout.data?.checkout?.summary ? (_jsxs("div", { children: [_jsxs("div", { style: { marginBottom: '24px' }, children: [_jsx("h4", { style: { margin: '0 0 12px 0', color: '#60a5fa' }, children: "Summary" }), _jsxs("div", { style: { display: 'grid', gap: '8px' }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Currency:" }), _jsx("span", { style: { color: '#f59e0b' }, children: context.debugCheckout.data.checkout.summary.currency })] }), _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [_jsx("span", { children: "Subtotal:" }), _jsx("span", { style: { color: '#9ca3af' }, children: (() => {
158
173
  try {
159
174
  return context.money.formatMoney(Number(context.debugCheckout.data.checkout.summary.subtotalAmount) || 0, String(context.debugCheckout.data.checkout.summary.currency) || 'USD', context.locale.locale);
160
175
  }
@@ -1,3 +1,4 @@
1
+ import { type SupportedLanguage } from '../../data/iso3166';
1
2
  export interface ISOCountry {
2
3
  iso: string;
3
4
  iso3: string;
@@ -16,26 +17,26 @@ export interface UseISODataResult {
16
17
  }
17
18
  /**
18
19
  * React hook for accessing ISO3166 countries and regions data
19
- * @param language - Language code (en, fr, de, es, etc.)
20
- * @param disputeSetting - Territorial dispute perspective (UN, RU, UA, TR)
20
+ * @param language - Language code (supports: en, ru, de, fr, es, zh, hi, pt, ja, ar, it, he)
21
+ * @param disputeSetting - Territorial dispute perspective (currently only UN is supported)
21
22
  * @returns Object with countries data and helper functions
22
23
  */
23
- export declare function useISOData(language?: string, disputeSetting?: string): UseISODataResult;
24
+ export declare function useISOData(language?: SupportedLanguage, disputeSetting?: string): UseISODataResult;
24
25
  /**
25
26
  * Get available languages for ISO data
26
27
  */
27
- export declare function getAvailableLanguages(): string[];
28
+ export declare function getAvailableLanguages(): SupportedLanguage[];
28
29
  /**
29
30
  * Get list of countries as options for select components
30
31
  */
31
- export declare function useCountryOptions(language?: string): {
32
+ export declare function useCountryOptions(language?: SupportedLanguage): {
32
33
  value: string;
33
34
  label: string;
34
35
  }[];
35
36
  /**
36
37
  * Get list of regions/states for a country as options for select components
37
38
  */
38
- export declare function useRegionOptions(countryCode: string, language?: string): {
39
+ export declare function useRegionOptions(countryCode: string, language?: SupportedLanguage): {
39
40
  value: string;
40
41
  label: string;
41
42
  }[];
@@ -1,75 +1,35 @@
1
- import { useMemo, useState, useEffect } from 'react';
1
+ import { useMemo } from 'react';
2
+ // Import the pre-built ISO data functions
3
+ import { getCountries, getStatesForCountry } from '../../data/iso3166';
2
4
  /**
3
5
  * React hook for accessing ISO3166 countries and regions data
4
- * @param language - Language code (en, fr, de, es, etc.)
5
- * @param disputeSetting - Territorial dispute perspective (UN, RU, UA, TR)
6
+ * @param language - Language code (supports: en, ru, de, fr, es, zh, hi, pt, ja, ar, it, he)
7
+ * @param disputeSetting - Territorial dispute perspective (currently only UN is supported)
6
8
  * @returns Object with countries data and helper functions
7
9
  */
8
10
  export function useISOData(language = 'en', disputeSetting = 'UN') {
9
- const [isoModule, setIsoModule] = useState(null);
10
- const [isLoading, setIsLoading] = useState(true);
11
- useEffect(() => {
12
- let isMounted = true;
13
- const loadModule = async () => {
14
- try {
15
- // Try dynamic import first (ES modules)
16
- const isoModule = await import('iso3166-2-db');
17
- if (isMounted) {
18
- setIsoModule(isoModule);
19
- setIsLoading(false);
20
- }
21
- }
22
- catch (importError) {
23
- console.error(`Failed to load ISO data module:`, importError);
24
- if (isMounted) {
25
- setIsoModule(null);
26
- setIsLoading(false);
27
- }
28
- }
29
- };
30
- void loadModule();
31
- return () => {
32
- isMounted = false;
33
- };
34
- }, []);
35
11
  const data = useMemo(() => {
36
- if (isLoading || !isoModule) {
37
- return {
38
- countries: {},
39
- getRegions: () => [],
40
- findRegion: () => null,
41
- mapGoogleToISO: () => null,
42
- };
43
- }
44
12
  try {
45
- const { getDataSet, reduce } = isoModule;
46
- // Get the dataset using the iso3166-2-db library
47
- const worldDatabase = reduce(getDataSet(), language);
48
- // Transform to our expected format
13
+ // Get countries from pre-built data with language support (now synchronous)
14
+ const countriesArray = getCountries(language);
15
+ // Transform to our expected format (Record<string, ISOCountry>)
49
16
  const countries = {};
50
- Object.keys(worldDatabase).forEach((countryCode) => {
51
- const countryData = worldDatabase[countryCode];
52
- countries[countryData.iso] = {
53
- iso: countryData.iso,
54
- iso3: countryData.iso3,
55
- numeric: countryData.numeric,
56
- name: countryData.name,
17
+ countriesArray.forEach((country) => {
18
+ countries[country.code] = {
19
+ iso: country.code,
20
+ iso3: country.iso3 || '',
21
+ numeric: country.numeric || 0,
22
+ name: country.name,
57
23
  };
58
24
  });
59
25
  // Helper to load regions for a specific country
60
26
  const getRegions = (countryCode) => {
61
27
  try {
62
- const countryData = worldDatabase[countryCode];
63
- if (!countryData?.regions) {
64
- return [];
65
- }
66
- return Object.keys(countryData.regions).map((regionCode) => {
67
- const regionData = countryData.regions[regionCode];
68
- return {
69
- iso: regionData.iso,
70
- name: regionData.name,
71
- };
72
- });
28
+ const states = getStatesForCountry(countryCode, language);
29
+ return states.map((state) => ({
30
+ iso: state.code,
31
+ name: state.name,
32
+ }));
73
33
  }
74
34
  catch {
75
35
  return []; // Return empty array if no regions
@@ -117,14 +77,15 @@ export function useISOData(language = 'en', disputeSetting = 'UN') {
117
77
  mapGoogleToISO: () => null,
118
78
  };
119
79
  }
120
- }, [isoModule, isLoading, language, disputeSetting]);
80
+ }, [language, disputeSetting]);
121
81
  return data;
122
82
  }
123
83
  /**
124
84
  * Get available languages for ISO data
125
85
  */
126
86
  export function getAvailableLanguages() {
127
- return ['en', 'fr', 'de', 'es', 'ru', 'zh', 'ar', 'it', 'pt', 'ja', 'hi'];
87
+ // Return all statically imported languages for browser compatibility
88
+ return ['en', 'ru', 'de', 'fr', 'es', 'zh', 'hi', 'pt', 'ja', 'ar', 'it', 'he'];
128
89
  }
129
90
  /**
130
91
  * Get list of countries as options for select components
@@ -46,6 +46,59 @@ export interface PostPurchaseOffer {
46
46
  summaries: PostPurchaseOfferSummary[];
47
47
  offerLineItems: PostPurchaseOfferLineItem[];
48
48
  }
49
+ export interface CurrencyOptions {
50
+ rate: number;
51
+ date: string;
52
+ amount: number;
53
+ lock: boolean;
54
+ }
55
+ export interface VariantOption {
56
+ id: string;
57
+ name: string;
58
+ sku: string | null;
59
+ default: boolean | null;
60
+ externalVariantId: string | null;
61
+ prices: {
62
+ id: string;
63
+ currencyOptions: CurrencyOptions;
64
+ }[];
65
+ }
66
+ export interface OrderSummaryItem {
67
+ id: string;
68
+ productId: string;
69
+ productName: string;
70
+ productDescription: string | null;
71
+ variantId: string;
72
+ variantName: string;
73
+ variantSku: string | null;
74
+ variantDefault: boolean | null;
75
+ variantExternalId: string | null;
76
+ priceId: string;
77
+ currencyOptions: CurrencyOptions;
78
+ quantity: number;
79
+ unitAmount: number;
80
+ amount: number;
81
+ adjustedAmount: number;
82
+ imageUrl?: string;
83
+ product: {
84
+ name: string;
85
+ description: string;
86
+ };
87
+ variant: {
88
+ name: string;
89
+ description: string;
90
+ imageUrl: string;
91
+ grams: number | null;
92
+ };
93
+ }
94
+ export interface OrderSummary {
95
+ items: OrderSummaryItem[];
96
+ totalAmount: number;
97
+ totalAdjustedAmount: number;
98
+ totalPromotionAmount: number;
99
+ currency: string;
100
+ options: Record<string, VariantOption[]>;
101
+ }
49
102
  export interface UsePostPurchasesOptions {
50
103
  /**
51
104
  * OrderID to fetch post-purchase offers for
@@ -56,6 +109,18 @@ export interface UsePostPurchasesOptions {
56
109
  * @default true
57
110
  */
58
111
  enabled?: boolean;
112
+ /**
113
+ * Whether to automatically initialize checkout sessions for offers
114
+ * @default false
115
+ */
116
+ autoInitializeCheckout?: boolean;
117
+ }
118
+ export interface CheckoutSessionState {
119
+ checkoutSessionId: string | null;
120
+ orderSummary: OrderSummary | null;
121
+ selectedVariants: Record<string, string>;
122
+ loadingVariants: Record<string, boolean>;
123
+ isUpdatingSummary: boolean;
59
124
  }
60
125
  export interface UsePostPurchasesResult {
61
126
  /**
@@ -95,15 +160,58 @@ export interface UsePostPurchasesResult {
95
160
  /**
96
161
  * Initialize a checkout session for a post-purchase offer with specific variants
97
162
  */
98
- initCheckoutSessionWithVariants: (offerId: string, orderId: string, lineItems: Array<{
163
+ initCheckoutSessionWithVariants: (offerId: string, orderId: string, lineItems: {
99
164
  variantId: string;
100
165
  quantity: number;
101
- }>) => Promise<{
166
+ }[]) => Promise<{
102
167
  checkoutSessionId: string;
103
168
  }>;
104
169
  /**
105
170
  * Pay with a checkout session for a post-purchase offer
106
171
  */
107
172
  payWithCheckoutSession: (checkoutSessionId: string, orderId?: string) => Promise<void>;
173
+ /**
174
+ * Get checkout session state for an offer
175
+ */
176
+ getCheckoutSessionState: (offerId: string) => CheckoutSessionState | null;
177
+ /**
178
+ * Initialize checkout session with variant options for an offer
179
+ */
180
+ initializeOfferCheckout: (offerId: string) => Promise<void>;
181
+ /**
182
+ * Get available variants for a product in an offer's checkout session
183
+ */
184
+ getAvailableVariants: (offerId: string, productId: string) => {
185
+ variantId: string;
186
+ variantName: string;
187
+ variantSku: string | null;
188
+ variantDefault: boolean | null;
189
+ variantExternalId: string | null;
190
+ priceId: string;
191
+ currencyOptions: CurrencyOptions;
192
+ }[];
193
+ /**
194
+ * Select a variant for a product in an offer's checkout session
195
+ */
196
+ selectVariant: (offerId: string, productId: string, variantId: string) => Promise<void>;
197
+ /**
198
+ * Get the order summary for an offer's checkout session
199
+ */
200
+ getOrderSummary: (offerId: string) => OrderSummary | null;
201
+ /**
202
+ * Check if variants are being loaded for a specific product in an offer
203
+ */
204
+ isLoadingVariants: (offerId: string, productId: string) => boolean;
205
+ /**
206
+ * Check if order summary is being updated for an offer
207
+ */
208
+ isUpdatingOrderSummary: (offerId: string) => boolean;
209
+ /**
210
+ * Confirm purchase for an offer with current variant selections
211
+ */
212
+ confirmPurchase: (offerId: string, options?: {
213
+ draft?: boolean;
214
+ returnUrl?: string;
215
+ }) => Promise<void>;
108
216
  }
109
217
  export declare function usePostPurchases(options: UsePostPurchasesOptions): UsePostPurchasesResult;
@@ -2,10 +2,12 @@ import { useCallback, useEffect, useState } from 'react';
2
2
  import { useTagadaContext } from '../providers/TagadaProvider';
3
3
  export function usePostPurchases(options) {
4
4
  const { apiService, session } = useTagadaContext();
5
- const { orderId, enabled = true } = options;
5
+ const { orderId, enabled = true, autoInitializeCheckout = false } = options;
6
6
  const [offers, setOffers] = useState([]);
7
7
  const [isLoading, setIsLoading] = useState(false);
8
8
  const [error, setError] = useState(null);
9
+ // Enhanced state for checkout sessions per offer
10
+ const [checkoutSessions, setCheckoutSessions] = useState({});
9
11
  const fetchOffers = useCallback(async () => {
10
12
  if (!orderId) {
11
13
  setOffers([]);
@@ -17,7 +19,14 @@ export function usePostPurchases(options) {
17
19
  const response = await apiService.fetch(`/api/v1/post-purchase/${orderId}/offers`, {
18
20
  method: 'GET',
19
21
  });
20
- setOffers(response || []);
22
+ const fetchedOffers = response || [];
23
+ setOffers(fetchedOffers);
24
+ // Auto-initialize checkout sessions if enabled
25
+ if (autoInitializeCheckout && fetchedOffers.length > 0) {
26
+ for (const offer of fetchedOffers) {
27
+ await initializeOfferCheckout(offer.id);
28
+ }
29
+ }
21
30
  }
22
31
  catch (err) {
23
32
  const error = err instanceof Error ? err : new Error('Failed to fetch post-purchase offers');
@@ -27,7 +36,7 @@ export function usePostPurchases(options) {
27
36
  finally {
28
37
  setIsLoading(false);
29
38
  }
30
- }, [orderId]);
39
+ }, [orderId, autoInitializeCheckout]);
31
40
  const initCheckoutSession = useCallback(async (offerId, orderId) => {
32
41
  if (!session?.customerId) {
33
42
  throw new Error('Customer ID is required');
@@ -35,26 +44,26 @@ export function usePostPurchases(options) {
35
44
  const response = await apiService.fetch(`/api/v1/checkout/offer/init`, {
36
45
  method: 'POST',
37
46
  body: JSON.stringify({
38
- offerId: offerId,
47
+ offerId,
39
48
  returnUrl: window.location.href,
40
49
  customerId: session?.customerId,
41
50
  orderId,
42
51
  }),
43
52
  });
44
53
  return response;
45
- }, []);
54
+ }, [apiService, session?.customerId]);
46
55
  const initCheckoutSessionWithVariants = useCallback(async (offerId, orderId, lineItems) => {
47
56
  const response = await apiService.fetch(`/api/v1/offers/${offerId}/transform-to-checkout`, {
48
57
  method: 'POST',
49
58
  body: JSON.stringify({
50
- offerId: offerId,
51
- lineItems: lineItems,
59
+ offerId,
60
+ lineItems,
52
61
  returnUrl: window.location.href,
53
62
  mainOrderId: orderId,
54
63
  }),
55
64
  });
56
65
  return response;
57
- }, []);
66
+ }, [apiService]);
58
67
  const payWithCheckoutSession = useCallback(async (checkoutSessionId, orderId) => {
59
68
  const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkoutSessionId}/pay`, {
60
69
  method: 'POST',
@@ -67,7 +76,206 @@ export function usePostPurchases(options) {
67
76
  }),
68
77
  });
69
78
  return response;
70
- }, []);
79
+ }, [apiService]);
80
+ // Enhanced checkout session management
81
+ const initializeOfferCheckout = useCallback(async (offerId) => {
82
+ if (!session?.customerId) {
83
+ throw new Error('Customer ID is required');
84
+ }
85
+ try {
86
+ // Initialize checkout session
87
+ const initResult = await initCheckoutSession(offerId, orderId);
88
+ if (!initResult.checkoutSessionId) {
89
+ throw new Error('Failed to initialize checkout session');
90
+ }
91
+ const sessionId = initResult.checkoutSessionId;
92
+ // Initialize session state
93
+ setCheckoutSessions(prev => ({
94
+ ...prev,
95
+ [offerId]: {
96
+ checkoutSessionId: sessionId,
97
+ orderSummary: null,
98
+ selectedVariants: {},
99
+ loadingVariants: {},
100
+ isUpdatingSummary: false,
101
+ }
102
+ }));
103
+ // Fetch order summary with variant options
104
+ await fetchOrderSummary(offerId, sessionId);
105
+ }
106
+ catch (error) {
107
+ console.error(`[SDK] Failed to initialize checkout for offer ${offerId}:`, error);
108
+ throw error;
109
+ }
110
+ }, [initCheckoutSession, orderId, session?.customerId]);
111
+ const fetchOrderSummary = useCallback(async (offerId, sessionId) => {
112
+ try {
113
+ // Set updating state
114
+ setCheckoutSessions(prev => ({
115
+ ...prev,
116
+ [offerId]: {
117
+ ...prev[offerId],
118
+ isUpdatingSummary: true,
119
+ }
120
+ }));
121
+ const summaryResult = await apiService.fetch(`/api/v1/checkout-sessions/${sessionId}/order-summary`, {
122
+ method: 'POST',
123
+ body: JSON.stringify({ includeVariantOptions: true }),
124
+ });
125
+ if (summaryResult) {
126
+ // Sort items by productId to ensure consistent order
127
+ const sortedItems = [...summaryResult.items].sort((a, b) => a.productId.localeCompare(b.productId));
128
+ const orderSummary = {
129
+ ...summaryResult,
130
+ items: sortedItems,
131
+ };
132
+ // Initialize selected variants based on the summary
133
+ const initialVariants = {};
134
+ sortedItems.forEach((item) => {
135
+ if (item.productId && item.variantId) {
136
+ initialVariants[item.productId] = item.variantId;
137
+ }
138
+ });
139
+ setCheckoutSessions(prev => ({
140
+ ...prev,
141
+ [offerId]: {
142
+ ...prev[offerId],
143
+ orderSummary,
144
+ selectedVariants: initialVariants,
145
+ isUpdatingSummary: false,
146
+ }
147
+ }));
148
+ }
149
+ }
150
+ catch (error) {
151
+ console.error(`[SDK] Failed to fetch order summary for offer ${offerId}:`, error);
152
+ setCheckoutSessions(prev => ({
153
+ ...prev,
154
+ [offerId]: {
155
+ ...prev[offerId],
156
+ isUpdatingSummary: false,
157
+ }
158
+ }));
159
+ throw error;
160
+ }
161
+ }, [apiService]);
162
+ const selectVariant = useCallback(async (offerId, productId, variantId) => {
163
+ const sessionState = checkoutSessions[offerId];
164
+ if (!sessionState?.checkoutSessionId || !sessionState.orderSummary) {
165
+ throw new Error('Checkout session not initialized for this offer');
166
+ }
167
+ // Set loading state for this specific variant
168
+ setCheckoutSessions(prev => ({
169
+ ...prev,
170
+ [offerId]: {
171
+ ...prev[offerId],
172
+ loadingVariants: {
173
+ ...prev[offerId].loadingVariants,
174
+ [productId]: true,
175
+ }
176
+ }
177
+ }));
178
+ try {
179
+ const availableVariants = getAvailableVariants(offerId, productId);
180
+ const selectedVariant = availableVariants.find(v => v.variantId === variantId);
181
+ if (!selectedVariant) {
182
+ throw new Error('Selected variant not found');
183
+ }
184
+ // Find the current item to get its quantity
185
+ const currentItem = sessionState.orderSummary.items.find(item => item.productId === productId);
186
+ if (!currentItem) {
187
+ throw new Error('Current item not found');
188
+ }
189
+ // Update selected variants state
190
+ setCheckoutSessions(prev => ({
191
+ ...prev,
192
+ [offerId]: {
193
+ ...prev[offerId],
194
+ selectedVariants: {
195
+ ...prev[offerId].selectedVariants,
196
+ [productId]: variantId,
197
+ }
198
+ }
199
+ }));
200
+ // Update line items on the server
201
+ await apiService.fetch(`/api/v1/checkout-sessions/${sessionState.checkoutSessionId}/line-items`, {
202
+ method: 'POST',
203
+ body: JSON.stringify({
204
+ lineItems: [
205
+ {
206
+ variantId: selectedVariant.variantId,
207
+ quantity: currentItem.quantity,
208
+ },
209
+ ],
210
+ }),
211
+ });
212
+ // Refetch order summary after successful line item update
213
+ await fetchOrderSummary(offerId, sessionState.checkoutSessionId);
214
+ }
215
+ catch (error) {
216
+ console.error(`[SDK] Failed to update variant for offer ${offerId}:`, error);
217
+ throw error;
218
+ }
219
+ finally {
220
+ // Clear loading state for this specific variant
221
+ setCheckoutSessions(prev => ({
222
+ ...prev,
223
+ [offerId]: {
224
+ ...prev[offerId],
225
+ loadingVariants: {
226
+ ...prev[offerId].loadingVariants,
227
+ [productId]: false,
228
+ }
229
+ }
230
+ }));
231
+ }
232
+ }, [checkoutSessions, apiService, fetchOrderSummary]);
233
+ const confirmPurchase = useCallback(async (offerId, options) => {
234
+ const sessionState = checkoutSessions[offerId];
235
+ if (!sessionState?.checkoutSessionId) {
236
+ throw new Error('Checkout session not initialized for this offer');
237
+ }
238
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${sessionState.checkoutSessionId}/pay`, {
239
+ method: 'POST',
240
+ body: JSON.stringify({
241
+ checkoutSessionId: sessionState.checkoutSessionId,
242
+ draft: options?.draft || false,
243
+ returnUrl: options?.returnUrl || window.location.href,
244
+ metadata: {
245
+ comingFromPostPurchase: true,
246
+ postOrder: orderId,
247
+ },
248
+ }),
249
+ });
250
+ return response;
251
+ }, [checkoutSessions, apiService, orderId]);
252
+ // Helper functions
253
+ const getCheckoutSessionState = useCallback((offerId) => {
254
+ return checkoutSessions[offerId] || null;
255
+ }, [checkoutSessions]);
256
+ const getAvailableVariants = useCallback((offerId, productId) => {
257
+ const sessionState = checkoutSessions[offerId];
258
+ if (!sessionState?.orderSummary?.options?.[productId])
259
+ return [];
260
+ return sessionState.orderSummary.options[productId].map((variant) => ({
261
+ variantId: variant.id,
262
+ variantName: variant.name,
263
+ variantSku: variant.sku,
264
+ variantDefault: variant.default,
265
+ variantExternalId: variant.externalVariantId,
266
+ priceId: variant.prices[0]?.id,
267
+ currencyOptions: variant.prices[0]?.currencyOptions,
268
+ }));
269
+ }, [checkoutSessions]);
270
+ const getOrderSummary = useCallback((offerId) => {
271
+ return checkoutSessions[offerId]?.orderSummary || null;
272
+ }, [checkoutSessions]);
273
+ const isLoadingVariants = useCallback((offerId, productId) => {
274
+ return checkoutSessions[offerId]?.loadingVariants?.[productId] || false;
275
+ }, [checkoutSessions]);
276
+ const isUpdatingOrderSummary = useCallback((offerId) => {
277
+ return checkoutSessions[offerId]?.isUpdatingSummary || false;
278
+ }, [checkoutSessions]);
71
279
  useEffect(() => {
72
280
  if (enabled && orderId) {
73
281
  fetchOffers();
@@ -103,5 +311,14 @@ export function usePostPurchases(options) {
103
311
  initCheckoutSession,
104
312
  initCheckoutSessionWithVariants,
105
313
  payWithCheckoutSession,
314
+ // Enhanced functionality
315
+ getCheckoutSessionState,
316
+ initializeOfferCheckout,
317
+ getAvailableVariants,
318
+ selectVariant,
319
+ getOrderSummary,
320
+ isLoadingVariants,
321
+ isUpdatingOrderSummary,
322
+ confirmPurchase,
106
323
  };
107
324
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tagadapay/plugin-sdk",
3
- "version": "2.1.3",
3
+ "version": "2.2.1",
4
4
  "description": "Modern React SDK for building Tagada Pay plugins",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",