@explorins/pers-sdk-react-native 2.1.13 → 2.1.17

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 (68) hide show
  1. package/README.md +108 -14
  2. package/dist/hooks/index.d.ts +3 -2
  3. package/dist/hooks/index.d.ts.map +1 -1
  4. package/dist/hooks/useAnalytics.d.ts +1 -1
  5. package/dist/hooks/useAnalytics.d.ts.map +1 -1
  6. package/dist/hooks/useAuth.d.ts +3 -3
  7. package/dist/hooks/useAuth.d.ts.map +1 -1
  8. package/dist/hooks/useAuth.js +5 -17
  9. package/dist/hooks/useBusiness.d.ts +3 -2
  10. package/dist/hooks/useBusiness.d.ts.map +1 -1
  11. package/dist/hooks/useBusiness.js +28 -2
  12. package/dist/hooks/useCampaigns.d.ts +12 -4
  13. package/dist/hooks/useCampaigns.d.ts.map +1 -1
  14. package/dist/hooks/useCampaigns.js +36 -8
  15. package/dist/hooks/useDonations.d.ts +1 -1
  16. package/dist/hooks/useDonations.d.ts.map +1 -1
  17. package/dist/hooks/usePurchases.d.ts +2 -2
  18. package/dist/hooks/usePurchases.d.ts.map +1 -1
  19. package/dist/hooks/useRedemptions.d.ts +28 -5
  20. package/dist/hooks/useRedemptions.d.ts.map +1 -1
  21. package/dist/hooks/useRedemptions.js +58 -1
  22. package/dist/hooks/useTenants.d.ts +1 -1
  23. package/dist/hooks/useTenants.d.ts.map +1 -1
  24. package/dist/hooks/useTokenBalances.d.ts +1 -1
  25. package/dist/hooks/useTokenBalances.d.ts.map +1 -1
  26. package/dist/hooks/useTokenBalances.js +1 -1
  27. package/dist/hooks/useTokens.d.ts +1 -1
  28. package/dist/hooks/useTokens.d.ts.map +1 -1
  29. package/dist/hooks/useTransactionSigner.d.ts.map +1 -1
  30. package/dist/hooks/useTransactionSigner.js +38 -11
  31. package/dist/hooks/useTransactions.d.ts +1 -1
  32. package/dist/hooks/useTransactions.d.ts.map +1 -1
  33. package/dist/hooks/useTriggerSources.d.ts +1 -1
  34. package/dist/hooks/useTriggerSources.d.ts.map +1 -1
  35. package/dist/hooks/useUserStatus.d.ts +1 -1
  36. package/dist/hooks/useUserStatus.d.ts.map +1 -1
  37. package/dist/hooks/useUsers.d.ts +1 -1
  38. package/dist/hooks/useUsers.d.ts.map +1 -1
  39. package/dist/index-CzLxqePZ.js +7717 -0
  40. package/dist/index-CzLxqePZ.js.map +1 -0
  41. package/dist/index.d.ts +4 -3
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1896 -317
  44. package/dist/index.js.map +1 -1
  45. package/dist/providers/PersSDKProvider.d.ts.map +1 -1
  46. package/dist/providers/PersSDKProvider.js +7 -17
  47. package/dist/providers/rn-dpop-provider.d.ts.map +1 -1
  48. package/dist/providers/rn-dpop-provider.js +48 -12
  49. package/package.json +3 -3
  50. package/src/hooks/index.ts +5 -2
  51. package/src/hooks/useAnalytics.ts +1 -1
  52. package/src/hooks/useAuth.ts +6 -20
  53. package/src/hooks/useBusiness.ts +32 -3
  54. package/src/hooks/useCampaigns.ts +49 -12
  55. package/src/hooks/useDonations.ts +1 -1
  56. package/src/hooks/usePurchases.ts +1 -1
  57. package/src/hooks/useRedemptions.ts +91 -6
  58. package/src/hooks/useTenants.ts +1 -1
  59. package/src/hooks/useTokenBalances.ts +1 -1
  60. package/src/hooks/useTokens.ts +1 -1
  61. package/src/hooks/useTransactionSigner.ts +41 -10
  62. package/src/hooks/useTransactions.ts +1 -1
  63. package/src/hooks/useTriggerSources.ts +1 -1
  64. package/src/hooks/useUserStatus.ts +1 -1
  65. package/src/hooks/useUsers.ts +1 -1
  66. package/src/index.ts +6 -3
  67. package/src/providers/PersSDKProvider.tsx +8 -17
  68. package/src/providers/rn-dpop-provider.ts +57 -11
@@ -1 +1 @@
1
- {"version":3,"file":"PersSDKProvider.d.ts","sourceRoot":"","sources":["../../src/providers/PersSDKProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAuC,SAAS,EAA2C,MAAM,OAAO,CAAC;AAEvH,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIpF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAkB3D,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAE7B,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAGpB,YAAY,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAGzC,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;IAGhC,UAAU,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,sBAAsB,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,EAAE,eAAe,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5F,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,cAAc,EAAE,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CAC/C;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IACrC,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CAwOA,CAAC;AAGF,eAAO,MAAM,UAAU,QAAO,cAQ7B,CAAC"}
1
+ {"version":3,"file":"PersSDKProvider.d.ts","sourceRoot":"","sources":["../../src/providers/PersSDKProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAuC,SAAS,EAA2C,MAAM,OAAO,CAAC;AAEvH,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIpF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAkB3D,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAE7B,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAGpB,YAAY,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAGzC,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;IAGhC,UAAU,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,sBAAsB,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,EAAE,eAAe,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5F,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,cAAc,EAAE,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;CAC/C;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IACrC,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CA+NA,CAAC;AAGF,eAAO,MAAM,UAAU,QAAO,cAQ7B,CAAC"}
@@ -158,22 +158,12 @@ export const PersSDKProvider = ({ children, config }) => {
158
158
  if (!sdk)
159
159
  return;
160
160
  const unsubscribe = sdk.events.subscribe((event) => {
161
- if (event.domain !== 'authentication')
162
- return;
163
- // Session restored successfully - sync React state
164
- if (event.type === 'session_restored') {
165
- console.log('[PersSDK] Session restoration event received, syncing state...');
166
- // Read user from event details if available, otherwise fetch
167
- const userId = event.details?.userId;
168
- if (userId) {
169
- // User ID available, fetch user data
170
- sdk.users.getCurrentUser()
171
- .then(userData => {
172
- setAuthenticationState(userData, true);
173
- })
174
- .catch(error => {
175
- console.error('[PersSDK] Failed to sync restored session:', error);
176
- });
161
+ // Handle both session restoration and fresh login
162
+ if (event.type === 'session_restored' || event.type === 'login_success') {
163
+ const details = event.details;
164
+ if (details?.user) {
165
+ console.log(`[PersSDK] ${event.type}: syncing user state`);
166
+ setAuthenticationState(details.user, true);
177
167
  }
178
168
  }
179
169
  // Session restoration failed or auth error - clear React state
@@ -181,7 +171,7 @@ export const PersSDKProvider = ({ children, config }) => {
181
171
  console.log('[PersSDK] Authentication failed - clearing session');
182
172
  setAuthenticationState(null, false);
183
173
  }
184
- }, { domains: ['authentication'] });
174
+ }, { domains: ['authentication'], replay: true });
185
175
  return () => {
186
176
  unsubscribe();
187
177
  };
@@ -1 +1 @@
1
- {"version":3,"file":"rn-dpop-provider.d.ts","sourceRoot":"","sources":["../../src/providers/rn-dpop-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAiB3E,qBAAa,uBAAwB,YAAW,kBAAkB;IAChE;;;;;OAKG;IACG,eAAe,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAqC1E,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAqD9E,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgB3D,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,eAAe;CAOxB"}
1
+ {"version":3,"file":"rn-dpop-provider.d.ts","sourceRoot":"","sources":["../../src/providers/rn-dpop-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAgD3E,qBAAa,uBAAwB,YAAW,kBAAkB;IAChE;;;;;OAKG;IACG,eAAe,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IA0C1E,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IA0D9E,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqB3D,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,eAAe;CAOxB"}
@@ -1,20 +1,44 @@
1
1
  import { Platform } from 'react-native';
2
2
  import { Buffer } from 'buffer';
3
- // Conditionally require quick-crypto for Native platforms only
4
- let crypto;
5
- if (Platform.OS !== 'web') {
3
+ // Lazy-loaded crypto module - initialized on first use to avoid native module timing issues
4
+ let _crypto = null;
5
+ let _cryptoInitialized = false;
6
+ /**
7
+ * Lazily load the crypto module.
8
+ *
9
+ * Supports customer pre-initialization via global.__QuickCrypto for environments
10
+ * where native module timing is unpredictable (e.g., certain React Native setups).
11
+ *
12
+ * @returns The crypto module or null if unavailable
13
+ */
14
+ function getCrypto() {
15
+ if (_cryptoInitialized) {
16
+ return _crypto;
17
+ }
18
+ _cryptoInitialized = true;
19
+ if (Platform.OS === 'web') {
20
+ // On Web, we shouldn't be using this provider (Core SDK has WebDPoPProvider)
21
+ _crypto = {
22
+ generateKeyPair: () => { throw new Error('ReactNativeDPoPProvider not supported on Web'); },
23
+ randomUUID: () => globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`
24
+ };
25
+ return _crypto;
26
+ }
27
+ // Check for customer pre-initialized global (for native module timing issues)
28
+ if (typeof global !== 'undefined' && global.__QuickCrypto) {
29
+ _crypto = global.__QuickCrypto;
30
+ return _crypto;
31
+ }
32
+ // Standard lazy load
6
33
  try {
7
- crypto = require('react-native-quick-crypto').default || require('react-native-quick-crypto');
34
+ _crypto = require('react-native-quick-crypto').default || require('react-native-quick-crypto');
8
35
  }
9
36
  catch (e) {
10
- console.warn('ReactNativeDPoPProvider: Failed to load react-native-quick-crypto', e);
37
+ console.warn('ReactNativeDPoPProvider: Failed to load react-native-quick-crypto.', e);
38
+ console.warn('Tip: If you encounter timing issues, pre-initialize in index.js: global.__QuickCrypto = require("react-native-quick-crypto")');
39
+ _crypto = null;
11
40
  }
12
- }
13
- else {
14
- // on Web, we shouldn't be using this provider anyway (Core SDK has WebDPoPProvider)
15
- crypto = {
16
- generateKeyPair: () => { throw new Error('ReactNativeDPoPProvider not supported on Web'); }
17
- };
41
+ return _crypto;
18
42
  }
19
43
  export class ReactNativeDPoPProvider {
20
44
  /**
@@ -24,6 +48,10 @@ export class ReactNativeDPoPProvider {
24
48
  * Falls back to Node.js crypto API on iOS if needed.
25
49
  */
26
50
  async generateKeyPair(options) {
51
+ const crypto = getCrypto();
52
+ if (!crypto) {
53
+ throw new Error('Crypto module not available. Install react-native-quick-crypto or pre-initialize global.__QuickCrypto');
54
+ }
27
55
  // Try WebCrypto API first (works on both iOS and Android)
28
56
  if (crypto.subtle) {
29
57
  try {
@@ -57,6 +85,10 @@ export class ReactNativeDPoPProvider {
57
85
  });
58
86
  }
59
87
  async signProof(payload, keyPair) {
88
+ const crypto = getCrypto();
89
+ if (!crypto) {
90
+ throw new Error('Crypto module not available for signing');
91
+ }
60
92
  // 1. Construct Header
61
93
  const header = {
62
94
  typ: 'dpop+jwt',
@@ -67,7 +99,7 @@ export class ReactNativeDPoPProvider {
67
99
  const finalPayload = {
68
100
  ...payload,
69
101
  iat: payload.iat || Math.floor(Date.now() / 1000),
70
- jti: payload.jti || crypto.randomUUID()
102
+ jti: payload.jti || (crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`)
71
103
  };
72
104
  // 3. Encode
73
105
  const b64Header = this.base64Url(JSON.stringify(header));
@@ -93,6 +125,10 @@ export class ReactNativeDPoPProvider {
93
125
  return `${signingInput}.${this.base64UrlBuffer(signature)}`;
94
126
  }
95
127
  async hashAccessToken(accessToken) {
128
+ const crypto = getCrypto();
129
+ if (!crypto) {
130
+ throw new Error('Crypto module not available for hashing');
131
+ }
96
132
  // Try WebCrypto first
97
133
  if (crypto.subtle) {
98
134
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@explorins/pers-sdk-react-native",
3
- "version": "2.1.13",
3
+ "version": "2.1.17",
4
4
  "description": "React Native SDK for PERS Platform - Tourism Loyalty System with Blockchain Transaction Signing and WebAuthn Authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -37,7 +37,7 @@
37
37
  "author": "eXplorins",
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
- "@explorins/pers-sdk": "^2.1.20",
40
+ "@explorins/pers-sdk": "^2.1.24",
41
41
  "@explorins/pers-signer": "^1.0.33",
42
42
  "buffer": "^6.0.3",
43
43
  "ethers": "^6.15.0",
@@ -155,7 +155,7 @@
155
155
  "@ethersproject/scrypt": false
156
156
  },
157
157
  "devDependencies": {
158
- "@explorins/pers-shared": "^2.1.123",
158
+ "@explorins/pers-shared": "^2.1.128",
159
159
  "@react-native-async-storage/async-storage": "^3.0.2",
160
160
  "@rollup/plugin-commonjs": "^25.0.7",
161
161
  "@rollup/plugin-json": "^6.1.0",
@@ -38,10 +38,13 @@ export type {
38
38
  } from './useTokenBalances';
39
39
 
40
40
  // Export campaign-related types
41
- export type { CampaignClaimFilters, CampaignHook, GetCampaignsOptions } from './useCampaigns';
41
+ export type { CampaignClaimFilters, CampaignFilterOptions, CampaignHook, GetCampaignsOptions } from './useCampaigns';
42
42
 
43
43
  // Export redemption-related types
44
- export type { RedemptionRedeemFilters, RedemptionHook } from './useRedemptions';
44
+ export type { RedemptionRedeemFilters, RedemptionFilterOptions, RedemptionHook, GetRedemptionsOptions } from './useRedemptions';
45
+
46
+ // Export business-related types
47
+ export type { BusinessFilterOptions, BusinessHook } from './useBusiness';
45
48
 
46
49
  // Export transaction-related types
47
50
  export type { TransactionQueryOptions, TransactionHook } from './useTransactions';
@@ -13,7 +13,7 @@ import {
13
13
  BusinessRankingAnalyticsResponseDTO,
14
14
  RetentionAnalyticsRequestDTO,
15
15
  RetentionAnalyticsResponseDTO
16
- } from '@explorins/pers-shared';
16
+ } from '@explorins/pers-sdk';
17
17
 
18
18
  /**
19
19
  * React hook for analytics operations in the PERS SDK
@@ -2,7 +2,7 @@ import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
3
  import type { RawUserData } from '@explorins/pers-sdk/core';
4
4
  import { AccountOwnerType } from '@explorins/pers-sdk/core';
5
- import type { UserDTO, AdminDTO, SessionAuthContextResponseDTO } from '@explorins/pers-shared';
5
+ import type { UserDTO, SessionAuthContextResponseDTO } from '@explorins/pers-sdk';
6
6
 
7
7
  // Re-export RawUserData for convenience
8
8
  export type { RawUserData };
@@ -80,22 +80,15 @@ export const useAuth = () => {
80
80
  await authProvider.setAccessToken(jwtToken);
81
81
 
82
82
  // Perform login using the manager
83
+ // SDK emits login_success event with user data - PersSDKProvider handles state sync
83
84
  const result = await sdk.auth.loginWithToken(jwtToken, userType);
84
85
 
85
- const userData = result.user || result.admin;
86
-
87
- if (userData) {
88
- setAuthenticationState(userData, true);
89
-
90
- return result;
91
- } else {
92
- throw new Error('No user data returned from login');
93
- }
86
+ return result;
94
87
  } catch (error) {
95
88
  console.error('Login failed:', error);
96
89
  throw error;
97
90
  }
98
- }, [sdk, authProvider, setAuthenticationState]);
91
+ }, [sdk, authProvider]);
99
92
 
100
93
  /**
101
94
  * Logs in a user using raw user data (direct authentication bypass)
@@ -116,25 +109,18 @@ export const useAuth = () => {
116
109
 
117
110
  try {
118
111
  // Use the raw data login from the auth manager
112
+ // SDK emits login_success event with user data - PersSDKProvider handles state sync
119
113
  const result = await sdk.auth.loginWithRawData(rawUserData);
120
114
 
121
115
  // Set token from result
122
116
  if (result.accessToken) {
123
117
  await authProvider.setAccessToken(result.accessToken);
124
118
  }
125
-
126
- const userData = result.user;
127
-
128
- if (userData) {
129
- setAuthenticationState(userData, true);
130
- } else {
131
- throw new Error('No user data returned from raw data login');
132
- }
133
119
  } catch (error) {
134
120
  console.error('Raw data login failed:', error);
135
121
  throw error;
136
122
  }
137
- }, [sdk, authProvider, setAuthenticationState]);
123
+ }, [sdk, authProvider]);
138
124
 
139
125
  /**
140
126
  * Logs out the current user and clears authentication state
@@ -1,6 +1,9 @@
1
1
  import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type { BusinessDTO, BusinessTypeDTO, BusinessUpdateRequestDTO, BusinessToggleActiveRequestDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
3
+ import type { BusinessDTO, BusinessTypeDTO, BusinessUpdateRequestDTO, BusinessToggleActiveRequestDTO, PaginatedResponseDTO, BusinessFilterOptions } from '@explorins/pers-sdk';
4
+
5
+ // Re-export for consumers
6
+ export type { BusinessFilterOptions } from '@explorins/pers-sdk';
4
7
 
5
8
  /**
6
9
  * React hook for business operations in the PERS SDK
@@ -107,13 +110,39 @@ export const useBusiness = () => {
107
110
  }
108
111
  }, [sdk, isInitialized]);
109
112
 
110
- const getBusinesses = useCallback(async (): Promise<PaginatedResponseDTO<BusinessDTO>> => {
113
+ /**
114
+ * Admin: Get all businesses with optional filtering
115
+ *
116
+ * @param options - Filter and pagination options
117
+ * @returns Promise resolving to paginated businesses
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * // Get all businesses
122
+ * const { data } = await getBusinesses();
123
+ *
124
+ * // Filter by tag
125
+ * const tagged = await getBusinesses({ tag: 'restaurant' });
126
+ *
127
+ * // Multiple tags (OR match)
128
+ * const multiTag = await getBusinesses({ tag: ['restaurant', 'cafe'] });
129
+ *
130
+ * // With search and pagination
131
+ * const filtered = await getBusinesses({
132
+ * search: 'coffee',
133
+ * active: true,
134
+ * page: 1,
135
+ * limit: 20
136
+ * });
137
+ * ```
138
+ */
139
+ const getBusinesses = useCallback(async (options?: BusinessFilterOptions): Promise<PaginatedResponseDTO<BusinessDTO>> => {
111
140
  if (!isInitialized || !sdk) {
112
141
  throw new Error('SDK not initialized. Call initialize() first.');
113
142
  }
114
143
 
115
144
  try {
116
- const result = await sdk.businesses.getBusinesses();
145
+ const result = await sdk.businesses.getBusinesses(options);
117
146
  return result;
118
147
  } catch (error) {
119
148
  console.error('Failed to fetch all businesses:', error);
@@ -9,21 +9,30 @@ import type {
9
9
  CampaignClaimIncludeRelation,
10
10
  CampaignIncludeRelation,
11
11
  SortOrder,
12
+ CampaignFilterOptions,
12
13
  // DynamicContext
13
- } from '@explorins/pers-shared';
14
+ } from '@explorins/pers-sdk';
14
15
  import type { CampaignClaimFilters } from '@explorins/pers-sdk/campaign';
15
16
 
16
17
  // Re-export for consumers
17
18
  export type { CampaignClaimFilters } from '@explorins/pers-sdk/campaign';
19
+ export type { CampaignFilterOptions } from '@explorins/pers-sdk';
18
20
 
19
21
  /**
20
22
  * Options for fetching campaigns
23
+ * @deprecated Use CampaignFilterOptions from '@explorins/pers-sdk/campaign' instead
21
24
  */
22
25
  export interface GetCampaignsOptions {
23
- /** Filter by tag */
24
- tag?: string;
26
+ /** Filter by tag(s) - single tag or array for OR match */
27
+ tags?: string | string[];
28
+ /** Free text search across name and description (case-insensitive) */
29
+ search?: string;
25
30
  /** Filter by business ID */
26
31
  businessId?: string;
32
+ /** Filter campaigns starting on or after this date */
33
+ startDate?: Date | string;
34
+ /** Filter campaigns ending on or before this date */
35
+ endDate?: Date | string;
27
36
  /** Page number (1-indexed) */
28
37
  page?: number;
29
38
  /** Items per page */
@@ -32,7 +41,7 @@ export interface GetCampaignsOptions {
32
41
  sortBy?: 'name' | 'createdAt' | 'startDate';
33
42
  /** Sort order */
34
43
  sortOrder?: SortOrder;
35
- /** Relations to include: 'triggerSources', 'businesses' */
44
+ /** Relations to include: 'triggerSources', 'businesses', 'claimCount' */
36
45
  include?: CampaignIncludeRelation[];
37
46
  }
38
47
 
@@ -57,11 +66,15 @@ export const useCampaigns = () => {
57
66
  * include: ['triggerSources', 'businesses']
58
67
  * });
59
68
  *
60
- * // Filter by tag
61
- * const { data } = await getActiveCampaigns({ tag: 'summer-promo' });
69
+ * // Filter by date range
70
+ * const { data } = await getActiveCampaigns({
71
+ * startDate: '2026-05-01',
72
+ * endDate: '2026-12-31'
73
+ * });
62
74
  *
63
- * // Filter by business
64
- * const { data } = await getActiveCampaigns({ businessId: 'business-123' });
75
+ * // Get campaigns with claim count
76
+ * const { data } = await getActiveCampaigns({ include: ['claimCount'] });
77
+ * data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
65
78
  * ```
66
79
  */
67
80
  const getActiveCampaigns = useCallback(async (
@@ -87,7 +100,7 @@ export const useCampaigns = () => {
87
100
  * Get campaign by ID with optional include relations
88
101
  *
89
102
  * @param campaignId - The campaign ID
90
- * @param include - Relations to include: 'triggerSources', 'businesses'
103
+ * @param include - Relations to include: 'triggerSources', 'businesses', 'claimCount'
91
104
  * @returns Promise resolving to campaign data
92
105
  *
93
106
  * @example
@@ -95,6 +108,10 @@ export const useCampaigns = () => {
95
108
  * // Get campaign with all related data
96
109
  * const campaign = await getCampaignById('campaign-123', ['triggerSources', 'businesses']);
97
110
  * console.log('Trigger sources:', campaign.included?.triggerSources);
111
+ *
112
+ * // Get campaign with claim count
113
+ * const campaign = await getCampaignById('campaign-123', ['claimCount']);
114
+ * console.log('Total claims:', campaign.claimCount);
98
115
  * ```
99
116
  */
100
117
  const getCampaignById = useCallback(async (
@@ -257,8 +274,13 @@ export const useCampaigns = () => {
257
274
  * // Get only active campaigns with trigger sources
258
275
  * const { data } = await getAllCampaigns({ active: true, include: ['triggerSources'] });
259
276
  *
260
- * // Get inactive campaigns
261
- * const { data } = await getAllCampaigns({ active: false });
277
+ * // Filter by date range with claim count
278
+ * const { data } = await getAllCampaigns({
279
+ * startDate: '2026-05-01',
280
+ * endDate: '2026-12-31',
281
+ * include: ['claimCount']
282
+ * });
283
+ * data.forEach(c => console.log(`${c.name}: ${c.claimCount} claims`));
262
284
  *
263
285
  * // With pagination and sorting
264
286
  * const { data } = await getAllCampaigns({
@@ -288,7 +310,16 @@ export const useCampaigns = () => {
288
310
  /**
289
311
  * Admin: Get campaign claims with optional filters and include relations
290
312
  *
291
- * @param filters - Optional filters for campaign, user, or business
313
+ * @param filters - Optional filters for campaign, user, business, and date range
314
+ * @param filters.campaignId - Filter by specific campaign ID
315
+ * @param filters.userId - Filter by user ID
316
+ * @param filters.businessId - Filter by business ID
317
+ * @param filters.dateFrom - Filter claims created on or after this date (ISO 8601 format)
318
+ * @param filters.dateTo - Filter claims created on or before this date (ISO 8601 format)
319
+ * @param filters.page - Page number (1-indexed)
320
+ * @param filters.limit - Items per page
321
+ * @param filters.sortBy - Field to sort by
322
+ * @param filters.sortOrder - Sort direction
292
323
  * @param include - Relations to include: 'campaign', 'user', 'business'
293
324
  * @returns Promise resolving to paginated campaign claims
294
325
  *
@@ -299,6 +330,12 @@ export const useCampaigns = () => {
299
330
  *
300
331
  * // Get claims for a specific campaign
301
332
  * const { data } = await getCampaignClaims({ campaignId: 'campaign-123' }, ['user', 'business']);
333
+ *
334
+ * // Filter by date range
335
+ * const { data } = await getCampaignClaims({
336
+ * dateFrom: '2026-01-01',
337
+ * dateTo: '2026-03-31'
338
+ * }, ['campaign']);
302
339
  * ```
303
340
  */
304
341
  const getCampaignClaims = useCallback(async (
@@ -1,6 +1,6 @@
1
1
  import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type { DonationTypeDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
3
+ import type { DonationTypeDTO, PaginatedResponseDTO } from '@explorins/pers-sdk';
4
4
 
5
5
  /**
6
6
  * React hook for donation operations in the PERS SDK
@@ -1,6 +1,6 @@
1
1
  import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type { PurchaseTokenDTO, PurchaseDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
3
+ import type { PurchaseTokenDTO, PurchaseDTO, PaginatedResponseDTO } from '@explorins/pers-sdk';
4
4
 
5
5
  export const usePurchases = () => {
6
6
  const { sdk, isInitialized, isAuthenticated } = usePersSDK();
@@ -9,14 +9,42 @@ import type {
9
9
  RedemptionTypeDTO,
10
10
  PaginatedResponseDTO,
11
11
  RedemptionRedeemIncludeRelation,
12
+ RedemptionIncludeRelation,
12
13
  ProcessRecordStatus,
13
- DynamicContext
14
- } from '@explorins/pers-shared';
15
- import type { RedemptionRedeemFilters } from '@explorins/pers-sdk/redemption';
14
+ DynamicContext,
15
+ SortOrder
16
+ } from '@explorins/pers-sdk';
17
+ import type { RedemptionRedeemFilters, RedemptionFilterOptions } from '@explorins/pers-sdk/redemption';
16
18
  import type { PaginationOptions } from '@explorins/pers-sdk';
17
19
 
18
20
  // Re-export for consumers
19
- export type { RedemptionRedeemFilters } from '@explorins/pers-sdk/redemption';
21
+ export type { RedemptionRedeemFilters, RedemptionFilterOptions } from '@explorins/pers-sdk/redemption';
22
+
23
+ /**
24
+ * Options for fetching redemptions with filtering and pagination
25
+ */
26
+ export interface GetRedemptionsOptions {
27
+ /** Filter by active status (Admin only - regular users always see active offers) */
28
+ active?: boolean;
29
+ /** Free text search across name, description, tags (case-insensitive) */
30
+ search?: string;
31
+ /** Filter by tag(s) - single tag or array for OR match */
32
+ tags?: string | string[];
33
+ /** Filter redemptions starting on or after this date */
34
+ startDate?: Date | string;
35
+ /** Filter redemptions ending on or before this date */
36
+ endDate?: Date | string;
37
+ /** Relations to include: 'redeemCount' */
38
+ include?: RedemptionIncludeRelation[];
39
+ /** Page number (1-indexed) */
40
+ page?: number;
41
+ /** Results per page */
42
+ limit?: number;
43
+ /** Field to sort by */
44
+ sortBy?: 'name' | 'createdAt' | 'startDate';
45
+ /** Sort direction */
46
+ sortOrder?: SortOrder;
47
+ }
20
48
 
21
49
  export const useRedemptions = () => {
22
50
  const { sdk, isInitialized, isAuthenticated } = usePersSDK();
@@ -29,8 +57,49 @@ export const useRedemptions = () => {
29
57
  * **Administrators:** Can see all redemptions and filter by active/inactive status
30
58
  *
31
59
  * The backend enforces permission-based filtering automatically.
60
+ *
61
+ * @param options - Filter and pagination options
62
+ * @param options.active - Filter by active status (Admin only)
63
+ * @param options.search - Free text search across name, description, tags
64
+ * @param options.startDate - Filter redemptions starting on or after this date
65
+ * @param options.endDate - Filter redemptions ending on or before this date
66
+ * @param options.include - Relations to include: 'redeemCount'
67
+ * @param options.page - Page number (1-indexed)
68
+ * @param options.limit - Results per page
69
+ * @param options.sortBy - Field to sort by ('name' | 'createdAt')
70
+ * @param options.sortOrder - Sort direction ('ASC' | 'DESC')
71
+ *
72
+ * @example Basic usage
73
+ * ```typescript
74
+ * const { data: offers } = await getRedemptions();
75
+ * ```
76
+ *
77
+ * @example Filter by date range
78
+ * ```typescript
79
+ * const { data: dateFiltered } = await getRedemptions({
80
+ * startDate: '2026-05-01',
81
+ * endDate: '2026-06-30'
82
+ * });
83
+ * ```
84
+ *
85
+ * @example Get redemptions with redeem count
86
+ * ```typescript
87
+ * const { data: offers } = await getRedemptions({ include: ['redeemCount'] });
88
+ * offers.forEach(o => console.log(`${o.name}: ${o.redeemCount} redeems`));
89
+ * ```
90
+ *
91
+ * @example Search with pagination
92
+ * ```typescript
93
+ * const { data, pagination } = await getRedemptions({
94
+ * search: 'discount',
95
+ * page: 1,
96
+ * limit: 10,
97
+ * sortBy: 'name',
98
+ * sortOrder: 'ASC'
99
+ * });
100
+ * ```
32
101
  */
33
- const getRedemptions = useCallback(async (options?: { active?: boolean }): Promise<PaginatedResponseDTO<RedemptionDTO>> => {
102
+ const getRedemptions = useCallback(async (options?: GetRedemptionsOptions): Promise<PaginatedResponseDTO<RedemptionDTO>> => {
34
103
  if (!isInitialized || !sdk) {
35
104
  throw new Error('SDK not initialized. Call initialize() first.');
36
105
  }
@@ -194,7 +263,17 @@ export const useRedemptions = () => {
194
263
  *
195
264
  * Retrieves all redemption redeems across the platform with filtering.
196
265
  *
197
- * @param filters - Optional filters for user, redemption, and pagination
266
+ * @param filters - Optional filters for user, redemption, date range, and pagination
267
+ * @param filters.userId - Filter by user ID
268
+ * @param filters.redemptionId - Filter by redemption offer ID
269
+ * @param filters.businessId - Filter by business ID (admin only)
270
+ * @param filters.status - Filter by processing status (PENDING, PROCESSING, COMPLETED, FAILED)
271
+ * @param filters.dateFrom - Filter redeems created on or after this date (ISO 8601 format)
272
+ * @param filters.dateTo - Filter redeems created on or before this date (ISO 8601 format)
273
+ * @param filters.page - Page number (1-indexed)
274
+ * @param filters.limit - Items per page
275
+ * @param filters.sortBy - Field to sort by
276
+ * @param filters.sortOrder - Sort direction
198
277
  * @param include - Relations to include: 'redemption', 'user', 'business'
199
278
  * @returns Promise resolving to paginated redemption redeems
200
279
  *
@@ -208,6 +287,12 @@ export const useRedemptions = () => {
208
287
  * { userId: 'user-123' },
209
288
  * ['redemption']
210
289
  * );
290
+ *
291
+ * // Filter by date range
292
+ * const { data } = await getRedemptionRedeems({
293
+ * dateFrom: '2026-01-01',
294
+ * dateTo: '2026-03-31'
295
+ * }, ['user', 'redemption']);
211
296
  * ```
212
297
  */
213
298
  const getRedemptionRedeems = useCallback(async (
@@ -1,6 +1,6 @@
1
1
  import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type { TenantPublicDTO, TenantClientConfigDTO, AdminDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
3
+ import type { TenantPublicDTO, TenantClientConfigDTO, AdminDTO, PaginatedResponseDTO } from '@explorins/pers-sdk';
4
4
 
5
5
  export const useTenants = () => {
6
6
  const { sdk, isInitialized } = usePersSDK();
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useEffect, useState, useRef } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
3
  import { useWeb3 } from './useWeb3';
4
- import { NativeTokenTypes, type TokenDTO } from '@explorins/pers-shared';
4
+ import { NativeTokenTypes, type TokenDTO } from '@explorins/pers-sdk';
5
5
  import type { TokenBalance, TokenBalanceRequest } from '@explorins/pers-sdk/web3';
6
6
 
7
7
  /**
@@ -1,6 +1,6 @@
1
1
  import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type { TokenDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
3
+ import type { TokenDTO, PaginatedResponseDTO } from '@explorins/pers-sdk';
4
4
 
5
5
  /**
6
6
  * React hook for token operations in the PERS SDK