@tagadapay/plugin-sdk 2.4.39 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +2 -0
  3. package/dist/react/hooks/useCheckout.js +19 -2
  4. package/dist/react/hooks/useCheckoutSession.d.ts +19 -0
  5. package/dist/react/hooks/useCheckoutSession.js +108 -0
  6. package/dist/react/hooks/useCheckoutToken.d.ts +17 -0
  7. package/dist/react/hooks/useCheckoutToken.js +80 -0
  8. package/dist/react/hooks/useOrderBump.js +92 -13
  9. package/dist/react/hooks/useOrderBumpV2.d.ts +17 -0
  10. package/dist/react/hooks/useOrderBumpV2.js +95 -0
  11. package/dist/react/hooks/useOrderBumpV3.d.ts +23 -0
  12. package/dist/react/hooks/useOrderBumpV3.js +109 -0
  13. package/dist/react/hooks/usePostPurchases.js +11 -5
  14. package/dist/react/index.d.ts +8 -0
  15. package/dist/react/index.js +4 -0
  16. package/dist/react/services/apiService.d.ts +1 -0
  17. package/dist/react/services/apiService.js +3 -0
  18. package/dist/v2/core/googleAutocomplete.d.ts +65 -0
  19. package/dist/v2/core/googleAutocomplete.js +94 -0
  20. package/dist/v2/core/index.d.ts +8 -0
  21. package/dist/v2/core/index.js +11 -0
  22. package/dist/v2/core/isoData.d.ts +50 -0
  23. package/dist/v2/core/isoData.js +103 -0
  24. package/dist/v2/core/resources/apiClient.d.ts +25 -0
  25. package/dist/v2/core/resources/apiClient.js +95 -0
  26. package/dist/v2/core/resources/checkout.d.ts +189 -0
  27. package/dist/v2/core/resources/checkout.js +119 -0
  28. package/dist/v2/core/resources/index.d.ts +13 -0
  29. package/dist/v2/core/resources/index.js +13 -0
  30. package/dist/v2/core/resources/offers.d.ts +98 -0
  31. package/dist/v2/core/resources/offers.js +115 -0
  32. package/dist/v2/core/resources/orders.d.ts +40 -0
  33. package/dist/v2/core/resources/orders.js +59 -0
  34. package/dist/v2/core/resources/payments.d.ts +140 -0
  35. package/dist/v2/core/resources/payments.js +126 -0
  36. package/dist/v2/core/resources/postPurchases.d.ts +182 -0
  37. package/dist/v2/core/resources/postPurchases.js +116 -0
  38. package/dist/v2/core/resources/products.d.ts +29 -0
  39. package/dist/v2/core/resources/products.js +49 -0
  40. package/dist/v2/core/resources/promotions.d.ts +45 -0
  41. package/dist/v2/core/resources/promotions.js +87 -0
  42. package/dist/v2/core/resources/threeds.d.ts +23 -0
  43. package/dist/v2/core/resources/threeds.js +15 -0
  44. package/dist/v2/core/utils/checkout.d.ts +24 -0
  45. package/dist/v2/core/utils/checkout.js +30 -0
  46. package/dist/v2/core/utils/currency.d.ts +28 -0
  47. package/dist/v2/core/utils/currency.js +272 -0
  48. package/dist/v2/core/utils/index.d.ts +12 -0
  49. package/dist/v2/core/utils/index.js +12 -0
  50. package/dist/v2/core/utils/order.d.ts +159 -0
  51. package/dist/v2/core/utils/order.js +42 -0
  52. package/dist/v2/core/utils/orderBump.d.ts +40 -0
  53. package/dist/v2/core/utils/orderBump.js +47 -0
  54. package/dist/v2/core/utils/pluginConfig.d.ts +43 -0
  55. package/dist/v2/core/utils/pluginConfig.js +155 -0
  56. package/dist/v2/core/utils/postPurchases.d.ts +32 -0
  57. package/dist/v2/core/utils/postPurchases.js +42 -0
  58. package/dist/v2/core/utils/products.d.ts +58 -0
  59. package/dist/v2/core/utils/products.js +64 -0
  60. package/dist/v2/core/utils/promotions.d.ts +24 -0
  61. package/dist/v2/core/utils/promotions.js +30 -0
  62. package/dist/v2/index.d.ts +19 -0
  63. package/dist/v2/index.js +15 -0
  64. package/dist/v2/react/components/DebugDrawer.d.ts +7 -0
  65. package/dist/v2/react/components/DebugDrawer.js +383 -0
  66. package/dist/v2/react/hooks/useApiQuery.d.ts +28 -0
  67. package/dist/v2/react/hooks/useApiQuery.js +84 -0
  68. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +39 -0
  69. package/dist/v2/react/hooks/useCheckoutQuery.js +208 -0
  70. package/dist/v2/react/hooks/useCheckoutToken.d.ts +17 -0
  71. package/dist/v2/react/hooks/useCheckoutToken.js +80 -0
  72. package/dist/v2/react/hooks/useCurrency.d.ts +9 -0
  73. package/dist/v2/react/hooks/useCurrency.js +21 -0
  74. package/dist/v2/react/hooks/useGeoLocation.d.ts +138 -0
  75. package/dist/v2/react/hooks/useGeoLocation.js +126 -0
  76. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +74 -0
  77. package/dist/v2/react/hooks/useGoogleAutocomplete.js +207 -0
  78. package/dist/v2/react/hooks/useISOData.d.ts +61 -0
  79. package/dist/v2/react/hooks/useISOData.js +176 -0
  80. package/dist/v2/react/hooks/useOffersQuery.d.ts +65 -0
  81. package/dist/v2/react/hooks/useOffersQuery.js +353 -0
  82. package/dist/v2/react/hooks/useOrderBumpQuery.d.ts +20 -0
  83. package/dist/v2/react/hooks/useOrderBumpQuery.js +88 -0
  84. package/dist/v2/react/hooks/useOrderQuery.d.ts +29 -0
  85. package/dist/v2/react/hooks/useOrderQuery.js +98 -0
  86. package/dist/v2/react/hooks/usePaymentPolling.d.ts +45 -0
  87. package/dist/v2/react/hooks/usePaymentPolling.js +153 -0
  88. package/dist/v2/react/hooks/usePaymentQuery.d.ts +19 -0
  89. package/dist/v2/react/hooks/usePaymentQuery.js +283 -0
  90. package/dist/v2/react/hooks/usePluginConfig.d.ts +16 -0
  91. package/dist/v2/react/hooks/usePluginConfig.js +36 -0
  92. package/dist/v2/react/hooks/usePostPurchasesQuery.d.ts +63 -0
  93. package/dist/v2/react/hooks/usePostPurchasesQuery.js +365 -0
  94. package/dist/v2/react/hooks/useProductsQuery.d.ts +31 -0
  95. package/dist/v2/react/hooks/useProductsQuery.js +102 -0
  96. package/dist/v2/react/hooks/usePromotionsQuery.d.ts +28 -0
  97. package/dist/v2/react/hooks/usePromotionsQuery.js +97 -0
  98. package/dist/v2/react/hooks/useThreeds.d.ts +36 -0
  99. package/dist/v2/react/hooks/useThreeds.js +166 -0
  100. package/dist/v2/react/hooks/useThreedsModal.d.ts +13 -0
  101. package/dist/v2/react/hooks/useThreedsModal.js +343 -0
  102. package/dist/v2/react/index.d.ts +38 -0
  103. package/dist/v2/react/index.js +27 -0
  104. package/dist/v2/react/providers/TagadaProvider.d.ts +63 -0
  105. package/dist/v2/react/providers/TagadaProvider.js +680 -0
  106. package/package.json +10 -3
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Checkout Hook using TanStack Query
3
+ * Replaces the coordinator pattern with automatic cache invalidation
4
+ */
5
+ import { useCallback, useState, useEffect, useMemo } from 'react';
6
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
7
+ import { CheckoutResource } from '../../core/resources/checkout';
8
+ import { useTagadaContext } from '../providers/TagadaProvider';
9
+ import { usePluginConfig } from './usePluginConfig';
10
+ import { useCurrency } from './useCurrency';
11
+ import { getGlobalApiClient } from './useApiQuery';
12
+ export function useCheckoutQuery(options = {}) {
13
+ const { checkoutToken: providedToken, enabled = true } = options;
14
+ const { storeId } = usePluginConfig();
15
+ const currency = useCurrency();
16
+ const queryClient = useQueryClient();
17
+ const { isSessionInitialized } = useTagadaContext();
18
+ // Create checkout resource client
19
+ const checkoutResource = useMemo(() => {
20
+ try {
21
+ return new CheckoutResource(getGlobalApiClient());
22
+ }
23
+ catch (error) {
24
+ throw new Error('Failed to initialize checkout resource: ' + (error instanceof Error ? error.message : 'Unknown error'));
25
+ }
26
+ }, []);
27
+ // Internal token state that can be updated after init
28
+ const [internalToken, setInternalToken] = useState(providedToken);
29
+ // Update internal token when provided token changes
30
+ useEffect(() => {
31
+ if (providedToken && providedToken !== internalToken) {
32
+ setInternalToken(providedToken);
33
+ }
34
+ }, [providedToken, internalToken]);
35
+ // Use provided token or internal token
36
+ const checkoutToken = providedToken || internalToken;
37
+ console.log('🔍 [useCheckoutQuery] Query setup:', {
38
+ providedToken: providedToken ? providedToken.substring(0, 8) + '...' : 'none',
39
+ internalToken: internalToken ? internalToken.substring(0, 8) + '...' : 'none',
40
+ finalToken: checkoutToken ? checkoutToken.substring(0, 8) + '...' : 'none',
41
+ enabled: enabled && !!checkoutToken && isSessionInitialized,
42
+ currentCurrency: currency.code,
43
+ isSessionInitialized
44
+ });
45
+ // Main checkout query
46
+ const { data: checkout, isLoading, error, isSuccess, refetch, } = useQuery({
47
+ queryKey: ['checkout', checkoutToken, currency.code],
48
+ queryFn: () => checkoutResource.getCheckout(checkoutToken, currency.code),
49
+ enabled: enabled && !!checkoutToken && isSessionInitialized,
50
+ staleTime: 30000, // 30 seconds
51
+ refetchOnWindowFocus: false,
52
+ });
53
+ console.log('🔍 [useCheckoutQuery] Query result:', {
54
+ hasCheckout: !!checkout,
55
+ isLoading,
56
+ error: error?.message,
57
+ isSuccess
58
+ });
59
+ // Refresh function
60
+ const refresh = useCallback(async () => {
61
+ if (checkoutToken) {
62
+ await refetch();
63
+ }
64
+ }, [refetch, checkoutToken]);
65
+ // Initialize checkout mutation
66
+ const initMutation = useMutation({
67
+ mutationFn: (params) => {
68
+ const requestBody = {
69
+ ...params,
70
+ storeId: params.storeId || storeId,
71
+ returnUrl: params.returnUrl || window.location.origin,
72
+ customer: {
73
+ ...params.customer,
74
+ currency: params.customer?.currency ?? currency.code,
75
+ },
76
+ };
77
+ return checkoutResource.initCheckout(requestBody);
78
+ },
79
+ onSuccess: (response) => {
80
+ // Update URL with checkout token
81
+ if (typeof window !== 'undefined' && response.checkoutToken) {
82
+ const currentUrl = new URL(window.location.href);
83
+ if (!currentUrl.searchParams.has('checkoutToken')) {
84
+ const newUrl = new URL(window.location.href);
85
+ newUrl.searchParams.set('checkoutToken', response.checkoutToken);
86
+ window.history.replaceState(null, '', newUrl.toString());
87
+ }
88
+ }
89
+ },
90
+ });
91
+ // Order bump functionality removed - use useOrderBumpQuery instead
92
+ // Line items mutation
93
+ const lineItemsMutation = useMutation({
94
+ mutationFn: ({ lineItems }) => {
95
+ if (!checkout?.checkoutSession?.id) {
96
+ throw new Error('No checkout session available');
97
+ }
98
+ return checkoutResource.updateLineItems(checkout.checkoutSession.id, lineItems);
99
+ },
100
+ onSuccess: () => {
101
+ if (checkoutToken) {
102
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
103
+ }
104
+ },
105
+ });
106
+ // Item quantity mutation
107
+ const quantityMutation = useMutation({
108
+ mutationFn: ({ variantId, quantity, priceId }) => {
109
+ if (!checkout?.checkoutSession?.id) {
110
+ throw new Error('No checkout session available');
111
+ }
112
+ return checkoutResource.setItemQuantity(checkout.checkoutSession.id, variantId, quantity, priceId);
113
+ },
114
+ onSuccess: () => {
115
+ if (checkoutToken) {
116
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
117
+ }
118
+ },
119
+ });
120
+ // Customer update mutation
121
+ const customerMutation = useMutation({
122
+ mutationFn: (data) => {
123
+ if (!checkout?.checkoutSession?.id) {
124
+ throw new Error('No checkout session available');
125
+ }
126
+ return checkoutResource.updateCustomer(checkout.checkoutSession.id, data);
127
+ },
128
+ onSuccess: () => {
129
+ if (checkoutToken) {
130
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
131
+ }
132
+ },
133
+ });
134
+ // Customer and session info update mutation
135
+ const customerAndSessionMutation = useMutation({
136
+ mutationFn: (data) => {
137
+ if (!checkout?.checkoutSession?.id) {
138
+ throw new Error('No checkout session available');
139
+ }
140
+ return checkoutResource.updateCustomerAndSessionInfo(checkout.checkoutSession.id, data);
141
+ },
142
+ onSuccess: () => {
143
+ if (checkoutToken) {
144
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
145
+ }
146
+ },
147
+ });
148
+ // Promotion code mutation
149
+ const promotionMutation = useMutation({
150
+ mutationFn: ({ code }) => {
151
+ if (!checkout?.checkoutSession?.id) {
152
+ throw new Error('No checkout session available');
153
+ }
154
+ return checkoutResource.applyPromotionCode(checkout.checkoutSession.id, code);
155
+ },
156
+ onSuccess: () => {
157
+ if (checkoutToken) {
158
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
159
+ }
160
+ if (checkout?.checkoutSession?.id) {
161
+ void queryClient.invalidateQueries({ queryKey: ['promotions', checkout.checkoutSession.id] });
162
+ }
163
+ },
164
+ });
165
+ // Remove promotion mutation
166
+ const removePromotionMutation = useMutation({
167
+ mutationFn: ({ promotionId }) => {
168
+ if (!checkout?.checkoutSession?.id) {
169
+ throw new Error('No checkout session available');
170
+ }
171
+ return checkoutResource.removePromotion(checkout.checkoutSession.id, promotionId);
172
+ },
173
+ onSuccess: () => {
174
+ if (checkoutToken) {
175
+ void queryClient.invalidateQueries({ queryKey: ['checkout', checkoutToken] });
176
+ }
177
+ if (checkout?.checkoutSession?.id) {
178
+ void queryClient.invalidateQueries({ queryKey: ['promotions', checkout.checkoutSession.id] });
179
+ }
180
+ },
181
+ });
182
+ return {
183
+ // Query data
184
+ checkout,
185
+ isLoading,
186
+ error,
187
+ isSuccess,
188
+ // Actions
189
+ init: async (params) => {
190
+ const result = await initMutation.mutateAsync(params);
191
+ // Update internal token state so the query can fetch the checkout data
192
+ setInternalToken(result.checkoutToken);
193
+ return {
194
+ checkoutUrl: result.checkoutUrl,
195
+ checkoutSession: checkout?.checkoutSession ?? {},
196
+ checkoutToken: result.checkoutToken,
197
+ };
198
+ },
199
+ refresh,
200
+ // Checkout operations
201
+ updateLineItems: (lineItems) => lineItemsMutation.mutateAsync({ lineItems }),
202
+ setItemQuantity: (variantId, quantity, priceId) => quantityMutation.mutateAsync({ variantId, quantity, priceId }),
203
+ updateCustomer: (data) => customerMutation.mutateAsync(data),
204
+ updateCustomerAndSessionInfo: (data) => customerAndSessionMutation.mutateAsync(data),
205
+ applyPromotionCode: (code) => promotionMutation.mutateAsync({ code }),
206
+ removePromotion: (promotionId) => removePromotionMutation.mutateAsync({ promotionId }),
207
+ };
208
+ }
@@ -0,0 +1,17 @@
1
+ export interface UseCheckoutTokenOptions {
2
+ checkoutToken?: string;
3
+ autoLoadFromToken?: boolean;
4
+ }
5
+ export interface UseCheckoutTokenResult {
6
+ checkoutToken: string | null;
7
+ isLoading: boolean;
8
+ error: Error | null;
9
+ isInitialized: boolean;
10
+ setToken: (token: string | null) => void;
11
+ clearToken: () => void;
12
+ }
13
+ /**
14
+ * React hook for managing checkout token state
15
+ * Handles token management without fetching checkout data
16
+ */
17
+ export declare function useCheckoutToken(options?: UseCheckoutTokenOptions): UseCheckoutTokenResult;
@@ -0,0 +1,80 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { useTagadaContext } from '../providers/TagadaProvider';
3
+ /**
4
+ * React hook for managing checkout token state
5
+ * Handles token management without fetching checkout data
6
+ */
7
+ export function useCheckoutToken(options = {}) {
8
+ const { isSessionInitialized } = useTagadaContext();
9
+ const { checkoutToken: providedToken, autoLoadFromToken = true } = options;
10
+ const [checkoutToken, setCheckoutToken] = useState(providedToken || null);
11
+ const [isLoading, setIsLoading] = useState(false);
12
+ const [error, setError] = useState(null);
13
+ const [isInitialized, setIsInitialized] = useState(false);
14
+ const hasAutoLoadedRef = useRef(false);
15
+ const isSessionInitializedRef = useRef(isSessionInitialized);
16
+ // Keep ref in sync with state
17
+ useEffect(() => {
18
+ isSessionInitializedRef.current = isSessionInitialized;
19
+ }, [isSessionInitialized]);
20
+ // Update token when provided token changes
21
+ useEffect(() => {
22
+ if (providedToken && providedToken !== checkoutToken) {
23
+ console.log('🔧 useCheckoutToken: Token updated from props', {
24
+ oldToken: checkoutToken ? checkoutToken.substring(0, 8) + '...' : null,
25
+ newToken: providedToken.substring(0, 8) + '...'
26
+ });
27
+ setCheckoutToken(providedToken);
28
+ setIsInitialized(false);
29
+ hasAutoLoadedRef.current = false;
30
+ }
31
+ }, [providedToken, checkoutToken]);
32
+ // Auto-load token from URL if no token provided
33
+ useEffect(() => {
34
+ if (!providedToken && autoLoadFromToken && !checkoutToken && !hasAutoLoadedRef.current) {
35
+ const urlParams = new URLSearchParams(window.location.search);
36
+ const urlToken = urlParams.get('checkoutToken') || urlParams.get('token');
37
+ if (urlToken) {
38
+ console.log('🔧 useCheckoutToken: Auto-loading token from URL', {
39
+ tokenPreview: urlToken.substring(0, 8) + '...'
40
+ });
41
+ setCheckoutToken(urlToken);
42
+ hasAutoLoadedRef.current = true;
43
+ }
44
+ }
45
+ }, [providedToken, autoLoadFromToken, checkoutToken]);
46
+ // Wait for session initialization
47
+ useEffect(() => {
48
+ if (!isSessionInitialized || !checkoutToken || isInitialized) {
49
+ return;
50
+ }
51
+ console.log('🔧 useCheckoutToken: Session initialized, token ready', {
52
+ tokenPreview: checkoutToken.substring(0, 8) + '...',
53
+ isSessionInitialized
54
+ });
55
+ setIsInitialized(true);
56
+ }, [isSessionInitialized, checkoutToken, isInitialized]);
57
+ const setToken = useCallback((token) => {
58
+ console.log('🔧 useCheckoutToken: Setting token', {
59
+ tokenPreview: token ? token.substring(0, 8) + '...' : null
60
+ });
61
+ setCheckoutToken(token);
62
+ setIsInitialized(!!token);
63
+ setError(null);
64
+ }, []);
65
+ const clearToken = useCallback(() => {
66
+ console.log('🔧 useCheckoutToken: Clearing token');
67
+ setCheckoutToken(null);
68
+ setIsInitialized(false);
69
+ setError(null);
70
+ hasAutoLoadedRef.current = false;
71
+ }, []);
72
+ return {
73
+ checkoutToken,
74
+ isLoading,
75
+ error,
76
+ isInitialized,
77
+ setToken,
78
+ clearToken,
79
+ };
80
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Currency Hook
3
+ * Uses CurrencyCore for business logic
4
+ */
5
+ import { Currency } from '../../core/utils/currency';
6
+ export interface UseCurrencyResult extends Currency {
7
+ format: (amount: number) => string;
8
+ }
9
+ export declare function useCurrency(defaultCurrency?: string): UseCurrencyResult;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Currency Hook
3
+ * Uses CurrencyCore for business logic
4
+ */
5
+ import { useMemo } from 'react';
6
+ import { useTagadaContext } from '../providers/TagadaProvider';
7
+ import { CurrencyUtils } from '../../core/utils/currency';
8
+ export function useCurrency(defaultCurrency = 'USD') {
9
+ const context = useTagadaContext();
10
+ const currency = useMemo(() => {
11
+ return CurrencyUtils.getCurrency(context, defaultCurrency);
12
+ }, [context, defaultCurrency]);
13
+ const format = (amount) => {
14
+ const symbol = currency.symbol || currency.code;
15
+ return `${symbol}${amount.toFixed(currency.decimalPlaces)}`;
16
+ };
17
+ return {
18
+ ...currency,
19
+ format,
20
+ };
21
+ }
@@ -0,0 +1,138 @@
1
+ export interface GeoLocationData {
2
+ ip_address?: string | null;
3
+ city?: string | null;
4
+ city_geoname_id?: number | null;
5
+ region?: string | null;
6
+ region_iso_code?: string | null;
7
+ region_geoname_id?: number | null;
8
+ postal_code?: string | null;
9
+ country?: string | null;
10
+ country_code?: string | null;
11
+ country_geoname_id?: number | null;
12
+ country_is_eu?: boolean | null;
13
+ continent?: string | null;
14
+ continent_code?: string | null;
15
+ continent_geoname_id?: number | null;
16
+ latitude?: number | null;
17
+ longitude?: number | null;
18
+ security?: {
19
+ is_vpn?: boolean | null;
20
+ } | null;
21
+ timezone?: {
22
+ name?: string | null;
23
+ abbreviation?: string | null;
24
+ gmt_offset?: number | null;
25
+ current_time?: string | null;
26
+ is_dst?: boolean | null;
27
+ } | null;
28
+ currency?: {
29
+ currency_name?: string | null;
30
+ currency_code?: string | null;
31
+ } | null;
32
+ connection?: {
33
+ autonomous_system_number?: number | null;
34
+ autonomous_system_organization?: string | null;
35
+ connection_type?: string | null;
36
+ isp_name?: string | null;
37
+ organization_name?: string | null;
38
+ } | null;
39
+ flag?: {
40
+ emoji?: string | null;
41
+ unicode?: string | null;
42
+ png?: string | null;
43
+ svg?: string | null;
44
+ } | null;
45
+ as?: string | null;
46
+ isp?: string | null;
47
+ lat?: number | null;
48
+ lon?: number | null;
49
+ org?: string | null;
50
+ query?: string | null;
51
+ regionName?: string | null;
52
+ status?: string | null;
53
+ zip?: string | null;
54
+ error?: string;
55
+ }
56
+ export interface UseGeoLocationOptions {
57
+ /**
58
+ * Whether to automatically fetch geolocation data on mount
59
+ * @default true
60
+ */
61
+ autoFetch?: boolean;
62
+ /**
63
+ * Custom IP address to fetch geolocation for
64
+ * If not provided, will use the client's IP
65
+ */
66
+ ip?: string;
67
+ /**
68
+ * Whether to refetch data when the hook mounts
69
+ * @default false
70
+ */
71
+ refetchOnMount?: boolean;
72
+ }
73
+ export interface UseGeoLocationReturn {
74
+ /**
75
+ * The geolocation data
76
+ */
77
+ data: GeoLocationData | null;
78
+ /**
79
+ * Whether the request is currently loading
80
+ */
81
+ isLoading: boolean;
82
+ /**
83
+ * Any error that occurred during the request
84
+ */
85
+ error: string | null;
86
+ /**
87
+ * Function to manually fetch geolocation data
88
+ */
89
+ fetchGeoData: (ip?: string) => Promise<void>;
90
+ /**
91
+ * Function to clear the current data and error state
92
+ */
93
+ clearData: () => void;
94
+ /**
95
+ * Whether the data is from localhost/development
96
+ */
97
+ isLocalhost: boolean;
98
+ /**
99
+ * Whether the current IP is valid
100
+ */
101
+ isValidIP: boolean;
102
+ }
103
+ /**
104
+ * Hook to fetch and manage geolocation data - V2 Implementation
105
+ * Compatible with V1 interface while using V2 provider architecture
106
+ *
107
+ * @example
108
+ * ```tsx
109
+ * function MyComponent() {
110
+ * const { data, isLoading, error, fetchGeoData } = useGeoLocation();
111
+ *
112
+ * if (isLoading) return <div>Loading location...</div>;
113
+ * if (error) return <div>Error: {error}</div>;
114
+ *
115
+ * return (
116
+ * <div>
117
+ * <p>Country: {data?.country}</p>
118
+ * <p>City: {data?.city}</p>
119
+ * <p>IP: {data?.ip_address}</p>
120
+ * </div>
121
+ * );
122
+ * }
123
+ * ```
124
+ *
125
+ * @example
126
+ * ```tsx
127
+ * function MyComponent() {
128
+ * const { fetchGeoData } = useGeoLocation({ autoFetch: false });
129
+ *
130
+ * const handleClick = () => {
131
+ * fetchGeoData('8.8.8.8'); // Fetch for specific IP
132
+ * };
133
+ *
134
+ * return <button onClick={handleClick}>Get Location for Google DNS</button>;
135
+ * }
136
+ * ```
137
+ */
138
+ export declare function useGeoLocation(options?: UseGeoLocationOptions): UseGeoLocationReturn;
@@ -0,0 +1,126 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { useTagadaContext } from '../providers/TagadaProvider';
4
+ /**
5
+ * Hook to fetch and manage geolocation data - V2 Implementation
6
+ * Compatible with V1 interface while using V2 provider architecture
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * function MyComponent() {
11
+ * const { data, isLoading, error, fetchGeoData } = useGeoLocation();
12
+ *
13
+ * if (isLoading) return <div>Loading location...</div>;
14
+ * if (error) return <div>Error: {error}</div>;
15
+ *
16
+ * return (
17
+ * <div>
18
+ * <p>Country: {data?.country}</p>
19
+ * <p>City: {data?.city}</p>
20
+ * <p>IP: {data?.ip_address}</p>
21
+ * </div>
22
+ * );
23
+ * }
24
+ * ```
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * function MyComponent() {
29
+ * const { fetchGeoData } = useGeoLocation({ autoFetch: false });
30
+ *
31
+ * const handleClick = () => {
32
+ * fetchGeoData('8.8.8.8'); // Fetch for specific IP
33
+ * };
34
+ *
35
+ * return <button onClick={handleClick}>Get Location for Google DNS</button>;
36
+ * }
37
+ * ```
38
+ */
39
+ export function useGeoLocation(options = {}) {
40
+ const { autoFetch = true, ip, refetchOnMount = false } = options;
41
+ const { apiService } = useTagadaContext();
42
+ const [data, setData] = useState(null);
43
+ const [isLoading, setIsLoading] = useState(false);
44
+ const [error, setError] = useState(null);
45
+ // Helper function to check if IP is localhost
46
+ const isLocalhost = useCallback((ipAddress) => {
47
+ if (!ipAddress)
48
+ return false;
49
+ return ipAddress === '127.0.0.1' || ipAddress === '::1' || ipAddress === 'localhost';
50
+ }, []);
51
+ // Helper function to validate IP address format
52
+ const isValidIP = useCallback((ipAddress) => {
53
+ if (!ipAddress)
54
+ return false;
55
+ // IPv4 regex pattern
56
+ const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}$/;
57
+ // IPv6 regex pattern (simplified)
58
+ const ipv6Pattern = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
59
+ if (!ipv4Pattern.test(ipAddress) && !ipv6Pattern.test(ipAddress)) {
60
+ return false;
61
+ }
62
+ // Additional validation for IPv4
63
+ if (ipv4Pattern.test(ipAddress)) {
64
+ const parts = ipAddress.split('.');
65
+ return parts.every((part) => {
66
+ const num = parseInt(part, 10);
67
+ return num >= 0 && num <= 255;
68
+ });
69
+ }
70
+ return true;
71
+ }, []);
72
+ // Function to fetch geolocation data
73
+ const fetchGeoData = useCallback(async (customIp) => {
74
+ if (!apiService) {
75
+ setError('API service not available');
76
+ return;
77
+ }
78
+ setIsLoading(true);
79
+ setError(null);
80
+ try {
81
+ const targetIp = customIp || ip;
82
+ const endpoint = targetIp ? `/api/v1/geo?ip=${encodeURIComponent(targetIp)}` : '/api/v1/geo';
83
+ console.log('[SDK] Fetching geolocation data for IP:', targetIp || 'client IP');
84
+ const geoData = await apiService.fetch(endpoint, {
85
+ method: 'GET',
86
+ skipAuth: true, // Geolocation endpoint doesn't require authentication
87
+ });
88
+ setData(geoData);
89
+ console.log('[SDK] Geolocation data fetched successfully:', geoData);
90
+ }
91
+ catch (err) {
92
+ const errorMessage = err instanceof Error ? err.message : 'Failed to fetch geolocation data';
93
+ setError(errorMessage);
94
+ console.error('[SDK] Error fetching geolocation data:', err);
95
+ }
96
+ finally {
97
+ setIsLoading(false);
98
+ }
99
+ }, [apiService, ip]);
100
+ // Function to clear data and error state
101
+ const clearData = useCallback(() => {
102
+ setData(null);
103
+ setError(null);
104
+ }, []);
105
+ // Auto-fetch on mount if enabled
106
+ useEffect(() => {
107
+ if (autoFetch && apiService) {
108
+ void fetchGeoData();
109
+ }
110
+ }, [autoFetch, apiService, fetchGeoData]);
111
+ // Refetch on mount if enabled
112
+ useEffect(() => {
113
+ if (refetchOnMount && apiService) {
114
+ void fetchGeoData();
115
+ }
116
+ }, [refetchOnMount, apiService, fetchGeoData]);
117
+ return {
118
+ data,
119
+ isLoading,
120
+ error,
121
+ fetchGeoData,
122
+ clearData,
123
+ isLocalhost: isLocalhost(data?.ip_address),
124
+ isValidIP: isValidIP(data?.ip_address),
125
+ };
126
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Google Autocomplete Hook - V2 Implementation
3
+ * Compatible with V1 interface while using V2 core architecture
4
+ */
5
+ declare global {
6
+ interface Window {
7
+ google?: {
8
+ maps?: {
9
+ places?: {
10
+ AutocompleteService: new () => any;
11
+ PlacesService: new (map: any) => any;
12
+ PlacesServiceStatus: {
13
+ OK: string;
14
+ ZERO_RESULTS: string;
15
+ };
16
+ };
17
+ Map: new (element: HTMLElement) => any;
18
+ };
19
+ };
20
+ }
21
+ }
22
+ export interface GooglePrediction {
23
+ place_id: string;
24
+ description: string;
25
+ structured_formatting?: {
26
+ main_text: string;
27
+ secondary_text: string;
28
+ };
29
+ }
30
+ export interface GooglePlaceDetails {
31
+ place_id: string;
32
+ formatted_address: string;
33
+ address_components: {
34
+ long_name: string;
35
+ short_name: string;
36
+ types: string[];
37
+ }[];
38
+ geometry?: {
39
+ location: {
40
+ lat: number;
41
+ lng: number;
42
+ };
43
+ };
44
+ }
45
+ export interface ExtractedAddress {
46
+ streetNumber: string;
47
+ route: string;
48
+ locality: string;
49
+ administrativeAreaLevel1: string;
50
+ administrativeAreaLevel1Long: string;
51
+ country: string;
52
+ postalCode: string;
53
+ }
54
+ export interface UseGoogleAutocompleteOptions {
55
+ apiKey: string;
56
+ libraries?: string[];
57
+ version?: string;
58
+ language?: string;
59
+ region?: string;
60
+ }
61
+ export interface UseGoogleAutocompleteResult {
62
+ predictions: GooglePrediction[];
63
+ isLoading: boolean;
64
+ isScriptLoaded: boolean;
65
+ searchPlaces: (input: string, countryRestriction?: string) => void;
66
+ getPlaceDetails: (placeId: string) => Promise<GooglePlaceDetails | null>;
67
+ extractAddressComponents: (place: GooglePlaceDetails) => ExtractedAddress;
68
+ clearPredictions: () => void;
69
+ }
70
+ /**
71
+ * React hook for Google Places Autocomplete with automatic script injection
72
+ * Automatically loads the Google Maps JavaScript API with Places library
73
+ */
74
+ export declare function useGoogleAutocomplete(options: UseGoogleAutocompleteOptions): UseGoogleAutocompleteResult;