@tagadapay/plugin-sdk 2.4.10 → 2.4.14

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.
@@ -0,0 +1,93 @@
1
+ export interface ClubOfferItem {
2
+ id: string;
3
+ productId: string;
4
+ variantId: string;
5
+ product: {
6
+ name: string;
7
+ description: string;
8
+ };
9
+ variant: {
10
+ name: string;
11
+ description: string;
12
+ imageUrl: string;
13
+ grams: number;
14
+ };
15
+ unitAmount: number;
16
+ quantity: number;
17
+ amount: number;
18
+ adjustedAmount: number;
19
+ }
20
+ export interface ClubOfferSummary {
21
+ currency: string;
22
+ totalAmount: number;
23
+ totalAdjustedAmount: number;
24
+ items: ClubOfferItem[];
25
+ }
26
+ export interface ClubOfferLineItem {
27
+ id: string;
28
+ quantity: number;
29
+ price: {
30
+ variant: {
31
+ id: string;
32
+ name: string;
33
+ description: string | null;
34
+ imageUrl: string;
35
+ grams: number;
36
+ product: {
37
+ id: string;
38
+ name: string;
39
+ description: string;
40
+ };
41
+ };
42
+ };
43
+ }
44
+ export interface ClubOffer {
45
+ id: string;
46
+ titleTrans: {
47
+ en: string;
48
+ };
49
+ summaries: ClubOfferSummary[];
50
+ offerLineItems: ClubOfferLineItem[];
51
+ }
52
+ export interface UseClubOffersOptions {
53
+ /**
54
+ * Whether to fetch club offers automatically on mount
55
+ * @default true
56
+ */
57
+ enabled?: boolean;
58
+ }
59
+ export interface UseClubOffersResult {
60
+ /**
61
+ * Array of fetched club offers
62
+ */
63
+ offers: ClubOffer[];
64
+ /**
65
+ * Loading state
66
+ */
67
+ isLoading: boolean;
68
+ /**
69
+ * Error state
70
+ */
71
+ error: Error | null;
72
+ /**
73
+ * Refetch club offers
74
+ */
75
+ refetch: () => Promise<void>;
76
+ /**
77
+ * Get club offer by ID from the loaded offers
78
+ */
79
+ getOffer: (offerId: string) => ClubOffer | undefined;
80
+ /**
81
+ * Get total value of all club offers
82
+ */
83
+ getTotalValue: () => number;
84
+ /**
85
+ * Get total savings across all club offers
86
+ */
87
+ getTotalSavings: () => number;
88
+ /**
89
+ * Clear error state
90
+ */
91
+ clearError: () => void;
92
+ }
93
+ export declare function useClubOffers(options?: UseClubOffersOptions): UseClubOffersResult;
@@ -0,0 +1,71 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { useTagadaContext } from '../providers/TagadaProvider';
3
+ import { usePluginConfig } from './usePluginConfig';
4
+ export function useClubOffers(options = {}) {
5
+ const { apiService } = useTagadaContext();
6
+ const { storeId } = usePluginConfig();
7
+ const [offers, setOffers] = useState([]);
8
+ const [isLoading, setIsLoading] = useState(false);
9
+ const [error, setError] = useState(null);
10
+ const { enabled = true } = options;
11
+ const clearError = useCallback(() => {
12
+ setError(null);
13
+ }, []);
14
+ const fetchClubOffers = useCallback(async () => {
15
+ if (!enabled)
16
+ return;
17
+ setIsLoading(true);
18
+ setError(null);
19
+ try {
20
+ if (!storeId) {
21
+ throw new Error('Store ID not found. Make sure the TagadaProvider is properly configured.');
22
+ }
23
+ const responseData = await apiService.fetch(`/api/v1/stores/${storeId}/offers`, {
24
+ method: 'GET',
25
+ headers: {
26
+ 'Content-Type': 'application/json',
27
+ },
28
+ params: {
29
+ type: 'club',
30
+ },
31
+ });
32
+ setOffers(responseData.offers || []);
33
+ }
34
+ catch (err) {
35
+ const error = err instanceof Error ? err : new Error('Failed to fetch club offers');
36
+ setError(error);
37
+ console.error('Error fetching club offers:', error);
38
+ }
39
+ finally {
40
+ setIsLoading(false);
41
+ }
42
+ }, [apiService, storeId, enabled]);
43
+ const getOffer = useCallback((offerId) => {
44
+ return offers.find((offer) => offer.id === offerId);
45
+ }, [offers]);
46
+ const getTotalValue = useCallback(() => {
47
+ return offers.reduce((total, offer) => {
48
+ const firstSummary = offer.summaries[0];
49
+ return total + (firstSummary?.totalAdjustedAmount || 0);
50
+ }, 0);
51
+ }, [offers]);
52
+ const getTotalSavings = useCallback(() => {
53
+ return offers.reduce((total, offer) => {
54
+ const firstSummary = offer.summaries[0];
55
+ return total + (firstSummary?.totalAmount - firstSummary?.totalAdjustedAmount || 0);
56
+ }, 0);
57
+ }, [offers]);
58
+ useEffect(() => {
59
+ void fetchClubOffers();
60
+ }, [fetchClubOffers]);
61
+ return {
62
+ offers,
63
+ isLoading,
64
+ error,
65
+ refetch: fetchClubOffers,
66
+ getOffer,
67
+ getTotalValue,
68
+ getTotalSavings,
69
+ clearError,
70
+ };
71
+ }
@@ -0,0 +1,137 @@
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
105
+ *
106
+ * @example
107
+ * ```tsx
108
+ * function MyComponent() {
109
+ * const { data, isLoading, error, fetchGeoData } = useGeoLocation();
110
+ *
111
+ * if (isLoading) return <div>Loading location...</div>;
112
+ * if (error) return <div>Error: {error}</div>;
113
+ *
114
+ * return (
115
+ * <div>
116
+ * <p>Country: {data?.country}</p>
117
+ * <p>City: {data?.city}</p>
118
+ * <p>IP: {data?.ip_address}</p>
119
+ * </div>
120
+ * );
121
+ * }
122
+ * ```
123
+ *
124
+ * @example
125
+ * ```tsx
126
+ * function MyComponent() {
127
+ * const { fetchGeoData } = useGeoLocation({ autoFetch: false });
128
+ *
129
+ * const handleClick = () => {
130
+ * fetchGeoData('8.8.8.8'); // Fetch for specific IP
131
+ * };
132
+ *
133
+ * return <button onClick={handleClick}>Get Location for Google DNS</button>;
134
+ * }
135
+ * ```
136
+ */
137
+ export declare function useGeoLocation(options?: UseGeoLocationOptions): UseGeoLocationReturn;
@@ -0,0 +1,125 @@
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
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * function MyComponent() {
10
+ * const { data, isLoading, error, fetchGeoData } = useGeoLocation();
11
+ *
12
+ * if (isLoading) return <div>Loading location...</div>;
13
+ * if (error) return <div>Error: {error}</div>;
14
+ *
15
+ * return (
16
+ * <div>
17
+ * <p>Country: {data?.country}</p>
18
+ * <p>City: {data?.city}</p>
19
+ * <p>IP: {data?.ip_address}</p>
20
+ * </div>
21
+ * );
22
+ * }
23
+ * ```
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * function MyComponent() {
28
+ * const { fetchGeoData } = useGeoLocation({ autoFetch: false });
29
+ *
30
+ * const handleClick = () => {
31
+ * fetchGeoData('8.8.8.8'); // Fetch for specific IP
32
+ * };
33
+ *
34
+ * return <button onClick={handleClick}>Get Location for Google DNS</button>;
35
+ * }
36
+ * ```
37
+ */
38
+ export function useGeoLocation(options = {}) {
39
+ const { autoFetch = true, ip, refetchOnMount = false } = options;
40
+ const { apiService } = useTagadaContext();
41
+ const [data, setData] = useState(null);
42
+ const [isLoading, setIsLoading] = useState(false);
43
+ const [error, setError] = useState(null);
44
+ // Helper function to check if IP is localhost
45
+ const isLocalhost = useCallback((ipAddress) => {
46
+ if (!ipAddress)
47
+ return false;
48
+ return ipAddress === '127.0.0.1' || ipAddress === '::1' || ipAddress === 'localhost';
49
+ }, []);
50
+ // Helper function to validate IP address format
51
+ const isValidIP = useCallback((ipAddress) => {
52
+ if (!ipAddress)
53
+ return false;
54
+ // IPv4 regex pattern
55
+ const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}$/;
56
+ // IPv6 regex pattern (simplified)
57
+ const ipv6Pattern = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
58
+ if (!ipv4Pattern.test(ipAddress) && !ipv6Pattern.test(ipAddress)) {
59
+ return false;
60
+ }
61
+ // Additional validation for IPv4
62
+ if (ipv4Pattern.test(ipAddress)) {
63
+ const parts = ipAddress.split('.');
64
+ return parts.every((part) => {
65
+ const num = parseInt(part, 10);
66
+ return num >= 0 && num <= 255;
67
+ });
68
+ }
69
+ return true;
70
+ }, []);
71
+ // Function to fetch geolocation data
72
+ const fetchGeoData = useCallback(async (customIp) => {
73
+ if (!apiService) {
74
+ setError('API service not available');
75
+ return;
76
+ }
77
+ setIsLoading(true);
78
+ setError(null);
79
+ try {
80
+ const targetIp = customIp || ip;
81
+ const endpoint = targetIp ? `/api/v1/geo?ip=${encodeURIComponent(targetIp)}` : '/api/v1/geo';
82
+ console.log('[SDK] Fetching geolocation data for IP:', targetIp || 'client IP');
83
+ const geoData = await apiService.fetch(endpoint, {
84
+ method: 'GET',
85
+ skipAuth: true, // Geolocation endpoint doesn't require authentication
86
+ });
87
+ setData(geoData);
88
+ console.log('[SDK] Geolocation data fetched successfully:', geoData);
89
+ }
90
+ catch (err) {
91
+ const errorMessage = err instanceof Error ? err.message : 'Failed to fetch geolocation data';
92
+ setError(errorMessage);
93
+ console.error('[SDK] Error fetching geolocation data:', err);
94
+ }
95
+ finally {
96
+ setIsLoading(false);
97
+ }
98
+ }, [apiService, ip]);
99
+ // Function to clear data and error state
100
+ const clearData = useCallback(() => {
101
+ setData(null);
102
+ setError(null);
103
+ }, []);
104
+ // Auto-fetch on mount if enabled
105
+ useEffect(() => {
106
+ if (autoFetch && apiService) {
107
+ fetchGeoData();
108
+ }
109
+ }, [autoFetch, apiService, fetchGeoData]);
110
+ // Refetch on mount if enabled
111
+ useEffect(() => {
112
+ if (refetchOnMount && apiService) {
113
+ fetchGeoData();
114
+ }
115
+ }, [refetchOnMount, apiService, fetchGeoData]);
116
+ return {
117
+ data,
118
+ isLoading,
119
+ error,
120
+ fetchGeoData,
121
+ clearData,
122
+ isLocalhost: isLocalhost(data?.ip_address),
123
+ isValidIP: isValidIP(data?.ip_address),
124
+ };
125
+ }
@@ -4,10 +4,12 @@
4
4
  export { TagadaProvider } from './providers/TagadaProvider';
5
5
  export { useAuth } from './hooks/useAuth';
6
6
  export { useCheckout } from './hooks/useCheckout';
7
+ export { useClubOffers } from './hooks/useClubOffers';
7
8
  export { useCurrency } from './hooks/useCurrency';
8
9
  export { useCustomer } from './hooks/useCustomer';
9
10
  export { useDiscounts } from './hooks/useDiscounts';
10
11
  export { useEnvironment } from './hooks/useEnvironment';
12
+ export { useGeoLocation } from './hooks/useGeoLocation';
11
13
  export { useGoogleAutocomplete } from './hooks/useGoogleAutocomplete';
12
14
  export { useLocale } from './hooks/useLocale';
13
15
  export { useLogin } from './hooks/useLogin';
@@ -25,6 +27,7 @@ export { clearPluginConfigCache, debugPluginConfig, getPluginConfig, useBasePath
25
27
  export type { PluginConfig } from './hooks/usePluginConfig';
26
28
  export { getAvailableLanguages, useCountryOptions, useISOData, useRegionOptions } from './hooks/useISOData';
27
29
  export type { ISOCountry, ISORegion, UseISODataResult } from './hooks/useISOData';
30
+ export type { GeoLocationData, UseGeoLocationOptions, UseGeoLocationReturn } from './hooks/useGeoLocation';
28
31
  export type { ExtractedAddress, GoogleAddressComponent, GooglePlaceDetails, GooglePrediction, UseGoogleAutocompleteOptions, UseGoogleAutocompleteResult } from './hooks/useGoogleAutocomplete';
29
32
  export { useOrder } from './hooks/useOrder';
30
33
  export type { UseOrderOptions, UseOrderResult } from './hooks/useOrder';
@@ -39,6 +42,7 @@ export type { CheckoutData, CheckoutInitParams, CheckoutLineItem, CheckoutSessio
39
42
  export type { Discount, DiscountCodeValidation, UseDiscountsOptions, UseDiscountsResult } from './hooks/useDiscounts';
40
43
  export type { OrderBumpPreview, UseOrderBumpOptions, UseOrderBumpResult } from './hooks/useOrderBump';
41
44
  export type { UseVipOffersOptions, UseVipOffersResult, VipOffer, VipPreviewResponse } from './hooks/useVipOffers';
45
+ export type { ClubOffer, ClubOfferItem, ClubOfferLineItem, ClubOfferSummary, UseClubOffersOptions, UseClubOffersResult } from './hooks/useClubOffers';
42
46
  export type { PostPurchaseOffer, PostPurchaseOfferItem, PostPurchaseOfferLineItem, PostPurchaseOfferSummary, UsePostPurchasesOptions, UsePostPurchasesResult } from './hooks/usePostPurchases';
43
47
  export type { Payment, PaymentPollingHook, PollingOptions } from './hooks/usePaymentPolling';
44
48
  export type { PaymentInstrument, ThreedsChallenge, ThreedsHook, ThreedsOptions, ThreedsProvider, ThreedsSession } from './hooks/useThreeds';
@@ -7,10 +7,12 @@ export { TagadaProvider } from './providers/TagadaProvider';
7
7
  // Hook exports
8
8
  export { useAuth } from './hooks/useAuth';
9
9
  export { useCheckout } from './hooks/useCheckout';
10
+ export { useClubOffers } from './hooks/useClubOffers';
10
11
  export { useCurrency } from './hooks/useCurrency';
11
12
  export { useCustomer } from './hooks/useCustomer';
12
13
  export { useDiscounts } from './hooks/useDiscounts';
13
14
  export { useEnvironment } from './hooks/useEnvironment';
15
+ export { useGeoLocation } from './hooks/useGeoLocation';
14
16
  export { useGoogleAutocomplete } from './hooks/useGoogleAutocomplete';
15
17
  export { useLocale } from './hooks/useLocale';
16
18
  export { useLogin } from './hooks/useLogin';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tagadapay/plugin-sdk",
3
- "version": "2.4.10",
3
+ "version": "2.4.14",
4
4
  "description": "Modern React SDK for building Tagada Pay plugins",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",