@explorins/pers-sdk-react-native 2.1.2 → 2.1.5

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 (38) hide show
  1. package/README.md +7 -7
  2. package/dist/hooks/index.d.ts +6 -0
  3. package/dist/hooks/index.d.ts.map +1 -1
  4. package/dist/hooks/index.js +1 -0
  5. package/dist/hooks/useAnalytics.d.ts +37 -14
  6. package/dist/hooks/useAnalytics.d.ts.map +1 -1
  7. package/dist/hooks/useAnalytics.js +239 -19
  8. package/dist/hooks/useCampaigns.d.ts +14 -6
  9. package/dist/hooks/useCampaigns.d.ts.map +1 -1
  10. package/dist/hooks/useCampaigns.js +144 -10
  11. package/dist/hooks/useRedemptions.d.ts +5 -2
  12. package/dist/hooks/useRedemptions.d.ts.map +1 -1
  13. package/dist/hooks/useRedemptions.js +53 -2
  14. package/dist/hooks/useTokenBalances.d.ts.map +1 -1
  15. package/dist/hooks/useTokenBalances.js +21 -8
  16. package/dist/hooks/useTransactions.d.ts +8 -5
  17. package/dist/hooks/useTransactions.d.ts.map +1 -1
  18. package/dist/hooks/useTransactions.js +70 -27
  19. package/dist/hooks/useTriggerSources.d.ts +76 -0
  20. package/dist/hooks/useTriggerSources.d.ts.map +1 -0
  21. package/dist/hooks/useTriggerSources.js +272 -0
  22. package/dist/index.d.ts +12 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +2742 -495
  25. package/dist/index.js.map +1 -1
  26. package/dist/providers/PersSDKProvider.d.ts +1 -12
  27. package/dist/providers/PersSDKProvider.d.ts.map +1 -1
  28. package/dist/providers/PersSDKProvider.js +50 -25
  29. package/package.json +2 -2
  30. package/src/hooks/index.ts +17 -1
  31. package/src/hooks/useAnalytics.ts +268 -21
  32. package/src/hooks/useCampaigns.ts +176 -14
  33. package/src/hooks/useRedemptions.ts +66 -3
  34. package/src/hooks/useTokenBalances.ts +23 -9
  35. package/src/hooks/useTransactions.ts +84 -29
  36. package/src/hooks/useTriggerSources.ts +301 -0
  37. package/src/index.ts +33 -3
  38. package/src/providers/PersSDKProvider.tsx +58 -39
@@ -1,21 +1,9 @@
1
1
  import React, { ReactNode } from 'react';
2
2
  import { PersSDK, PersConfig, DefaultAuthProvider } from '@explorins/pers-sdk/core';
3
3
  import { UserDTO, AdminDTO } from '@explorins/pers-shared';
4
- import type { AuthManager, UserManager, TokenManager, BusinessManager, CampaignManager, RedemptionManager, TransactionManager, PurchaseManager, TenantManager, AnalyticsManager, DonationManager } from '@explorins/pers-sdk/core';
5
4
  export type { PersConfig } from '@explorins/pers-sdk/core';
6
5
  export interface PersSDKContext {
7
6
  sdk: PersSDK | null;
8
- auth: AuthManager | null;
9
- users: UserManager | null;
10
- tokens: TokenManager | null;
11
- businesses: BusinessManager | null;
12
- campaigns: CampaignManager | null;
13
- redemptions: RedemptionManager | null;
14
- transactions: TransactionManager | null;
15
- purchases: PurchaseManager | null;
16
- tenants: TenantManager | null;
17
- analytics: AnalyticsManager | null;
18
- donations: DonationManager | null;
19
7
  authProvider: DefaultAuthProvider | null;
20
8
  isInitialized: boolean;
21
9
  isAuthenticated: boolean;
@@ -23,6 +11,7 @@ export interface PersSDKContext {
23
11
  initialize: (config: PersConfig) => Promise<void>;
24
12
  setAuthenticationState: (user: UserDTO | AdminDTO | null, isAuthenticated: boolean) => void;
25
13
  refreshUserData: () => Promise<void>;
14
+ restoreSession: () => Promise<UserDTO | null>;
26
15
  }
27
16
  export declare const PersSDKProvider: React.FC<{
28
17
  children: ReactNode;
@@ -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;AAG3D,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,MAAM,WAAW,cAAc;IAE7B,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAGpB,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAClC,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACtC,YAAY,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACxC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAGlC,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;CACtC;AAMD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IACrC,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CAyMA,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,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;AAMD,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"}
@@ -10,11 +10,19 @@ const SDKContext = createContext(null);
10
10
  // Provider component
11
11
  export const PersSDKProvider = ({ children, config }) => {
12
12
  const initializingRef = useRef(false);
13
+ // State refs for stable functions to read current values
14
+ const sdkRef = useRef(null);
15
+ const isInitializedRef = useRef(false);
16
+ const isAuthenticatedRef = useRef(false);
13
17
  const [sdk, setSdk] = useState(null);
14
18
  const [authProvider, setAuthProvider] = useState(null);
15
19
  const [isInitialized, setIsInitialized] = useState(false);
16
20
  const [isAuthenticated, setIsAuthenticated] = useState(false);
17
21
  const [user, setUser] = useState(null);
22
+ // Keep state refs in sync immediately (not in useEffect to avoid race conditions)
23
+ sdkRef.current = sdk;
24
+ isInitializedRef.current = isInitialized;
25
+ isAuthenticatedRef.current = isAuthenticated;
18
26
  const initialize = useCallback(async (config) => {
19
27
  // Prevent multiple initializations
20
28
  if (isInitialized || initializingRef.current) {
@@ -81,18 +89,40 @@ export const PersSDKProvider = ({ children, config }) => {
81
89
  }
82
90
  }, [config, isInitialized, initialize]);
83
91
  const refreshUserData = useCallback(async () => {
84
- if (!sdk || !isAuthenticated || !isInitialized) {
85
- throw new Error('SDK not initialized or not authenticated. Cannot refresh user data.');
92
+ // Read from refs to get current values
93
+ const currentSdk = sdkRef.current;
94
+ const currentIsInitialized = isInitializedRef.current;
95
+ if (!currentSdk || !currentIsInitialized) {
96
+ throw new Error('SDK not initialized. Cannot refresh user data.');
86
97
  }
87
98
  try {
88
- const freshUserData = await sdk.users.getCurrentUser();
99
+ const freshUserData = await currentSdk.users.getCurrentUser();
89
100
  setUser(freshUserData);
90
101
  }
91
102
  catch (error) {
92
103
  console.error('Failed to refresh user data:', error);
93
104
  throw error;
94
105
  }
95
- }, [sdk, isAuthenticated, isInitialized]);
106
+ }, []); // No dependencies - reads from refs
107
+ const restoreSession = useCallback(async () => {
108
+ // Read from refs to get current values
109
+ const currentSdk = sdkRef.current;
110
+ const currentIsInitialized = isInitializedRef.current;
111
+ if (!currentSdk || !currentIsInitialized) {
112
+ throw new Error('SDK not initialized. Call initialize() first.');
113
+ }
114
+ try {
115
+ const userData = await currentSdk.restoreSession();
116
+ if (userData) {
117
+ setAuthenticationState(userData, true);
118
+ }
119
+ return userData;
120
+ }
121
+ catch (error) {
122
+ console.error('[PersSDK] Failed to restore session:', error);
123
+ throw error;
124
+ }
125
+ }, [setAuthenticationState]); // Depends on setAuthenticationState
96
126
  // Listen for authentication events from core SDK
97
127
  // Set up immediately when SDK is created (don't wait for isInitialized)
98
128
  // to catch session_restored events that fire during SDK initialization
@@ -105,13 +135,18 @@ export const PersSDKProvider = ({ children, config }) => {
105
135
  // Session restored successfully - sync React state
106
136
  if (event.type === 'session_restored') {
107
137
  console.log('[PersSDK] Session restoration event received, syncing state...');
108
- sdk.users.getCurrentUser()
109
- .then(userData => {
110
- setAuthenticationState(userData, true);
111
- })
112
- .catch(error => {
113
- console.error('[PersSDK] Failed to sync restored session:', error);
114
- });
138
+ // Read user from event details if available, otherwise fetch
139
+ const userId = event.details?.userId;
140
+ if (userId) {
141
+ // User ID available, fetch user data
142
+ sdk.users.getCurrentUser()
143
+ .then(userData => {
144
+ setAuthenticationState(userData, true);
145
+ })
146
+ .catch(error => {
147
+ console.error('[PersSDK] Failed to sync restored session:', error);
148
+ });
149
+ }
115
150
  }
116
151
  // Session restoration failed or auth error - clear React state
117
152
  if (event.type === 'session_restoration_failed' || event.code === 'AUTH_FAILED') {
@@ -149,28 +184,17 @@ export const PersSDKProvider = ({ children, config }) => {
149
184
  const contextValue = useMemo(() => ({
150
185
  // Main SDK instance
151
186
  sdk,
152
- // Manager shortcuts for convenience
153
- auth: sdk?.auth || null,
154
- users: sdk?.users || null,
155
- tokens: sdk?.tokens || null,
156
- businesses: sdk?.businesses || null,
157
- campaigns: sdk?.campaigns || null,
158
- redemptions: sdk?.redemptions || null,
159
- transactions: sdk?.transactions || null,
160
- purchases: sdk?.purchases || null,
161
- tenants: sdk?.tenants || null,
162
- analytics: sdk?.analytics || null,
163
- donations: sdk?.donations || null,
164
187
  // Platform-specific providers
165
188
  authProvider,
166
189
  // State
167
190
  isInitialized,
168
191
  isAuthenticated,
169
192
  user,
170
- // Methods
193
+ // Methods - expose functions directly, not through refs
171
194
  initialize,
172
195
  setAuthenticationState,
173
196
  refreshUserData,
197
+ restoreSession,
174
198
  }), [
175
199
  sdk,
176
200
  authProvider,
@@ -179,7 +203,8 @@ export const PersSDKProvider = ({ children, config }) => {
179
203
  user,
180
204
  initialize,
181
205
  setAuthenticationState,
182
- refreshUserData
206
+ refreshUserData,
207
+ restoreSession
183
208
  ]);
184
209
  return (_jsx(SDKContext.Provider, { value: contextValue, children: children }));
185
210
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@explorins/pers-sdk-react-native",
3
- "version": "2.1.2",
3
+ "version": "2.1.5",
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.2",
40
+ "@explorins/pers-sdk": "^2.1.6",
41
41
  "@explorins/pers-signer": "^1.0.33",
42
42
  "buffer": "^6.0.3",
43
43
  "ethers": "^6.15.0",
@@ -16,6 +16,7 @@ export { useFiles } from './useFiles';
16
16
  export { useAnalytics } from './useAnalytics';
17
17
  export { useDonations } from './useDonations';
18
18
  export { useEvents } from './useEvents';
19
+ export { useTriggerSources } from './useTriggerSources';
19
20
 
20
21
  // Re-export auth-related types for convenience
21
22
  export type { RawUserData } from './useAuth';
@@ -34,4 +35,19 @@ export type {
34
35
  TokenBalanceWithToken,
35
36
  UseTokenBalancesOptions,
36
37
  UseTokenBalancesResult
37
- } from './useTokenBalances';
38
+ } from './useTokenBalances';
39
+
40
+ // Export campaign-related types
41
+ export type { CampaignClaimFilters, CampaignHook } from './useCampaigns';
42
+
43
+ // Export redemption-related types
44
+ export type { RedemptionRedeemFilters, RedemptionHook } from './useRedemptions';
45
+
46
+ // Export transaction-related types
47
+ export type { TransactionQueryOptions, TransactionHook } from './useTransactions';
48
+
49
+ // Export trigger source-related types
50
+ export type { TriggerSourceQueryOptions, TriggerSourceHook } from './useTriggerSources';
51
+
52
+ // Export analytics-related types
53
+ export type { AnalyticsHook } from './useAnalytics';
@@ -2,35 +2,63 @@ import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
3
  import {
4
4
  TransactionAnalyticsRequestDTO,
5
- TransactionAnalyticsResponseDTO
5
+ TransactionAnalyticsResponseDTO,
6
+ CampaignClaimAnalyticsRequestDTO,
7
+ CampaignClaimAnalyticsResponseDTO,
8
+ UserAnalyticsRequestDTO,
9
+ UserAnalyticsResponseDTO,
10
+ UserRankingAnalyticsRequestDTO,
11
+ UserRankingAnalyticsResponseDTO,
12
+ BusinessRankingAnalyticsRequestDTO,
13
+ BusinessRankingAnalyticsResponseDTO,
14
+ RetentionAnalyticsRequestDTO,
15
+ RetentionAnalyticsResponseDTO
6
16
  } from '@explorins/pers-shared';
7
17
 
8
18
  /**
9
19
  * React hook for analytics operations in the PERS SDK
10
20
  *
11
- * Provides methods for retrieving transaction analytics and insights.
12
- * Supports various analytics queries for business intelligence and reporting.
21
+ * Provides comprehensive analytics and business intelligence capabilities:
22
+ * - **Transaction Analytics**: Volume, trends, business performance
23
+ * - **Campaign Claim Analytics**: Campaign performance and claim patterns
24
+ * - **User Analytics**: Engagement metrics with per-active-user averages
25
+ * - **User Ranking**: Leaderboards with full user details
26
+ * - **Business Ranking**: Business performance rankings
27
+ * - **Retention Analytics**: Monthly retention metrics
13
28
  *
14
29
  * @returns Analytics hook with methods for data analysis
15
30
  *
16
- * @example
31
+ * @example Basic Transaction Analytics
17
32
  * ```typescript
18
33
  * function AnalyticsComponent() {
19
34
  * const { getTransactionAnalytics } = useAnalytics();
20
35
  *
21
36
  * const loadAnalytics = async () => {
22
- * try {
23
- * const analytics = await getTransactionAnalytics({
24
- * timeRange: 'last_30_days',
25
- * groupBy: 'day'
26
- * });
27
- * console.log('Transaction analytics:', analytics);
28
- * } catch (error) {
29
- * console.error('Failed to load analytics:', error);
30
- * }
37
+ * const analytics = await getTransactionAnalytics({
38
+ * startDate: '2024-01-01',
39
+ * endDate: '2024-01-31',
40
+ * groupBy: 'day'
41
+ * });
42
+ * console.log('Transaction analytics:', analytics);
31
43
  * };
44
+ * }
45
+ * ```
32
46
  *
33
- * return <button onClick={loadAnalytics}>Load Analytics</button>;
47
+ * @example User Leaderboard
48
+ * ```typescript
49
+ * function LeaderboardScreen() {
50
+ * const { getUserRanking } = useAnalytics();
51
+ *
52
+ * const loadLeaderboard = async () => {
53
+ * const ranking = await getUserRanking({
54
+ * sortBy: 'totalTransactions',
55
+ * sortOrder: 'DESC',
56
+ * limit: 50
57
+ * });
58
+ * ranking.results.forEach((user, i) => {
59
+ * console.log(`#${i + 1}: ${user.email} - ${user.totalTransactions} txns`);
60
+ * });
61
+ * };
34
62
  * }
35
63
  * ```
36
64
  */
@@ -46,19 +74,17 @@ export const useAnalytics = () => {
46
74
  *
47
75
  * @example
48
76
  * ```typescript
49
- * const { getTransactionAnalytics } = useAnalytics();
50
77
  * const analytics = await getTransactionAnalytics({
51
- * groupBy: ['day'],
52
- * metrics: ['count', 'sum'],
53
78
  * startDate: '2024-01-01',
54
79
  * endDate: '2024-01-31',
55
- * filters: { status: 'completed' }
80
+ * groupBy: 'day',
81
+ * metrics: ['count', 'sum']
56
82
  * });
57
- * console.log('Daily transaction analytics:', analytics.results);
58
- * console.log('Execution time:', analytics.metadata.executionTime);
59
83
  * ```
60
84
  */
61
- const getTransactionAnalytics = useCallback(async (request: TransactionAnalyticsRequestDTO): Promise<TransactionAnalyticsResponseDTO> => {
85
+ const getTransactionAnalytics = useCallback(async (
86
+ request: TransactionAnalyticsRequestDTO
87
+ ): Promise<TransactionAnalyticsResponseDTO> => {
62
88
  if (!isInitialized || !sdk) {
63
89
  throw new Error('SDK not initialized. Call initialize() first.');
64
90
  }
@@ -72,8 +98,229 @@ export const useAnalytics = () => {
72
98
  }
73
99
  }, [sdk, isInitialized]);
74
100
 
101
+ /**
102
+ * Retrieves campaign claim analytics with aggregation
103
+ *
104
+ * Provides insights into campaign performance, claim patterns, and user engagement.
105
+ *
106
+ * @param request - Analytics request with filters, groupBy, and metrics
107
+ * @returns Promise resolving to campaign claim analytics data
108
+ *
109
+ * @example Claims per campaign
110
+ * ```typescript
111
+ * const analytics = await getCampaignClaimAnalytics({
112
+ * filters: { status: 'COMPLETED' },
113
+ * groupBy: ['campaignId'],
114
+ * metrics: ['count'],
115
+ * sortBy: 'count',
116
+ * sortOrder: 'DESC',
117
+ * limit: 10
118
+ * });
119
+ * console.log('Top campaigns:', analytics.results);
120
+ * ```
121
+ */
122
+ const getCampaignClaimAnalytics = useCallback(async (
123
+ request: CampaignClaimAnalyticsRequestDTO
124
+ ): Promise<CampaignClaimAnalyticsResponseDTO> => {
125
+ if (!isInitialized || !sdk) {
126
+ throw new Error('SDK not initialized. Call initialize() first.');
127
+ }
128
+
129
+ try {
130
+ const result = await sdk.analytics.getCampaignClaimAnalytics(request);
131
+ return result;
132
+ } catch (error) {
133
+ console.error('Failed to fetch campaign claim analytics:', error);
134
+ throw error;
135
+ }
136
+ }, [sdk, isInitialized]);
137
+
138
+ /**
139
+ * Retrieves user analytics with engagement metrics
140
+ *
141
+ * Includes both per-user and per-active-user metrics for accurate engagement insights.
142
+ * Per-active-user metrics show concentrated engagement among active users.
143
+ *
144
+ * @param request - Analytics request with optional filters and date range
145
+ * @returns Promise resolving to user analytics data
146
+ *
147
+ * @example Compare per-user vs per-active-user metrics
148
+ * ```typescript
149
+ * const analytics = await getUserAnalytics({
150
+ * startDate: new Date('2026-02-01'),
151
+ * endDate: new Date('2026-02-28')
152
+ * });
153
+ *
154
+ * console.log(`Active users: ${analytics.activeUsers} / ${analytics.totalUsers}`);
155
+ * console.log(`Engagement rate: ${analytics.engagementRate.toFixed(1)}%`);
156
+ *
157
+ * // Per-active-user metrics (more meaningful)
158
+ * console.log(`Avg transactions per active user: ${analytics.averageTransactionsPerActiveUser}`);
159
+ * ```
160
+ *
161
+ * @example Business-specific analytics
162
+ * ```typescript
163
+ * const analytics = await getUserAnalytics({
164
+ * filters: { businessId: 'business-123' },
165
+ * startDate: new Date('2026-01-01'),
166
+ * endDate: new Date('2026-12-31')
167
+ * });
168
+ * ```
169
+ */
170
+ const getUserAnalytics = useCallback(async (
171
+ request: UserAnalyticsRequestDTO = {}
172
+ ): Promise<UserAnalyticsResponseDTO> => {
173
+ if (!isInitialized || !sdk) {
174
+ throw new Error('SDK not initialized. Call initialize() first.');
175
+ }
176
+
177
+ try {
178
+ const result = await sdk.analytics.getUserAnalytics(request);
179
+ return result;
180
+ } catch (error) {
181
+ console.error('Failed to fetch user analytics:', error);
182
+ throw error;
183
+ }
184
+ }, [sdk, isInitialized]);
185
+
186
+ /**
187
+ * Retrieves user transaction ranking with enriched user data
188
+ *
189
+ * Returns ranked list of users with full user details (email, externalUserId)
190
+ * and transaction metrics. Ideal for leaderboards, engagement analysis,
191
+ * and identifying power users.
192
+ *
193
+ * @param request - Ranking request with filters, sorting, and limit
194
+ * @returns Promise resolving to ranked user list with transaction metrics
195
+ *
196
+ * @example Top 50 users by transaction count
197
+ * ```typescript
198
+ * const ranking = await getUserRanking({
199
+ * sortBy: 'totalTransactions',
200
+ * sortOrder: 'DESC',
201
+ * limit: 50
202
+ * });
203
+ *
204
+ * ranking.results.forEach((user, index) => {
205
+ * console.log(`#${index + 1}: ${user.email || user.externalUserId}`);
206
+ * console.log(` Transactions: ${user.totalTransactions}`);
207
+ * console.log(` Token spent: ${user.tokenSpent}`);
208
+ * });
209
+ * ```
210
+ *
211
+ * @example Top STAMP spenders
212
+ * ```typescript
213
+ * const ranking = await getUserRanking({
214
+ * filters: { tokenType: 'STAMP' },
215
+ * sortBy: 'tokenSpent',
216
+ * sortOrder: 'DESC',
217
+ * limit: 20
218
+ * });
219
+ * ```
220
+ */
221
+ const getUserRanking = useCallback(async (
222
+ request: UserRankingAnalyticsRequestDTO = {}
223
+ ): Promise<UserRankingAnalyticsResponseDTO> => {
224
+ if (!isInitialized || !sdk) {
225
+ throw new Error('SDK not initialized. Call initialize() first.');
226
+ }
227
+
228
+ try {
229
+ const result = await sdk.analytics.getUserRanking(request);
230
+ return result;
231
+ } catch (error) {
232
+ console.error('Failed to fetch user ranking:', error);
233
+ throw error;
234
+ }
235
+ }, [sdk, isInitialized]);
236
+
237
+ /**
238
+ * Retrieves business transaction ranking with enriched business data
239
+ *
240
+ * Returns ranked list of businesses with transaction metrics for
241
+ * partner analytics and performance dashboards.
242
+ *
243
+ * @param request - Ranking request with filters, sorting, and limit
244
+ * @returns Promise resolving to ranked business list
245
+ *
246
+ * @example Top businesses by transaction count
247
+ * ```typescript
248
+ * const ranking = await getBusinessRanking({
249
+ * sortBy: 'totalTransactions',
250
+ * sortOrder: 'DESC',
251
+ * limit: 20
252
+ * });
253
+ *
254
+ * ranking.results.forEach((business, index) => {
255
+ * console.log(`#${index + 1}: ${business.businessId}`);
256
+ * console.log(` Transactions: ${business.totalTransactions}`);
257
+ * console.log(` Token spent: ${business.tokenSpent}`);
258
+ * });
259
+ * ```
260
+ */
261
+ const getBusinessRanking = useCallback(async (
262
+ request: BusinessRankingAnalyticsRequestDTO = {}
263
+ ): Promise<BusinessRankingAnalyticsResponseDTO> => {
264
+ if (!isInitialized || !sdk) {
265
+ throw new Error('SDK not initialized. Call initialize() first.');
266
+ }
267
+
268
+ try {
269
+ const result = await sdk.analytics.getBusinessRanking(request);
270
+ return result;
271
+ } catch (error) {
272
+ console.error('Failed to fetch business ranking:', error);
273
+ throw error;
274
+ }
275
+ }, [sdk, isInitialized]);
276
+
277
+ /**
278
+ * Retrieves monthly user retention analytics
279
+ *
280
+ * Returns monthly retention data with active, new, and returning users
281
+ * along with retention rates. Useful for churn analysis and engagement trends.
282
+ *
283
+ * @param request - Retention request with monthsBack and filters
284
+ * @returns Promise resolving to monthly retention data
285
+ *
286
+ * @example Get 12 months of retention data
287
+ * ```typescript
288
+ * const retention = await getRetentionAnalytics({
289
+ * monthsBack: 12
290
+ * });
291
+ *
292
+ * retention.results.forEach(month => {
293
+ * console.log(`${month.month}:`);
294
+ * console.log(` Active: ${month.activeUsers}`);
295
+ * console.log(` New: ${month.newUsers}`);
296
+ * console.log(` Returning: ${month.returningUsers}`);
297
+ * console.log(` Retention Rate: ${month.retentionRate.toFixed(1)}%`);
298
+ * });
299
+ * ```
300
+ */
301
+ const getRetentionAnalytics = useCallback(async (
302
+ request: RetentionAnalyticsRequestDTO = {}
303
+ ): Promise<RetentionAnalyticsResponseDTO> => {
304
+ if (!isInitialized || !sdk) {
305
+ throw new Error('SDK not initialized. Call initialize() first.');
306
+ }
307
+
308
+ try {
309
+ const result = await sdk.analytics.getRetentionAnalytics(request);
310
+ return result;
311
+ } catch (error) {
312
+ console.error('Failed to fetch retention analytics:', error);
313
+ throw error;
314
+ }
315
+ }, [sdk, isInitialized]);
316
+
75
317
  return {
76
318
  getTransactionAnalytics,
319
+ getCampaignClaimAnalytics,
320
+ getUserAnalytics,
321
+ getUserRanking,
322
+ getBusinessRanking,
323
+ getRetentionAnalytics,
77
324
  isAvailable: isInitialized && !!sdk?.analytics,
78
325
  };
79
326
  };