@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.
- package/README.md +7 -7
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useAnalytics.d.ts +37 -14
- package/dist/hooks/useAnalytics.d.ts.map +1 -1
- package/dist/hooks/useAnalytics.js +239 -19
- package/dist/hooks/useCampaigns.d.ts +14 -6
- package/dist/hooks/useCampaigns.d.ts.map +1 -1
- package/dist/hooks/useCampaigns.js +144 -10
- package/dist/hooks/useRedemptions.d.ts +5 -2
- package/dist/hooks/useRedemptions.d.ts.map +1 -1
- package/dist/hooks/useRedemptions.js +53 -2
- package/dist/hooks/useTokenBalances.d.ts.map +1 -1
- package/dist/hooks/useTokenBalances.js +21 -8
- package/dist/hooks/useTransactions.d.ts +8 -5
- package/dist/hooks/useTransactions.d.ts.map +1 -1
- package/dist/hooks/useTransactions.js +70 -27
- package/dist/hooks/useTriggerSources.d.ts +76 -0
- package/dist/hooks/useTriggerSources.d.ts.map +1 -0
- package/dist/hooks/useTriggerSources.js +272 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2742 -495
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts +1 -12
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +50 -25
- package/package.json +2 -2
- package/src/hooks/index.ts +17 -1
- package/src/hooks/useAnalytics.ts +268 -21
- package/src/hooks/useCampaigns.ts +176 -14
- package/src/hooks/useRedemptions.ts +66 -3
- package/src/hooks/useTokenBalances.ts +23 -9
- package/src/hooks/useTransactions.ts +84 -29
- package/src/hooks/useTriggerSources.ts +301 -0
- package/src/index.ts +33 -3
- package/src/providers/PersSDKProvider.tsx +58 -39
package/dist/index.js
CHANGED
|
@@ -2828,6 +2828,28 @@ exports.SortOrder = void 0;
|
|
|
2828
2828
|
SortOrder["DESC"] = "DESC";
|
|
2829
2829
|
})(exports.SortOrder || (exports.SortOrder = {}));
|
|
2830
2830
|
|
|
2831
|
+
/**
|
|
2832
|
+
* ProcessRecordStatus - Status for business process records that create blockchain transactions
|
|
2833
|
+
*
|
|
2834
|
+
* Used for tracking the lifecycle of business operations (campaigns, redemptions) that
|
|
2835
|
+
* trigger blockchain transactions. This is distinct from TransactionStatus which tracks
|
|
2836
|
+
* the blockchain transaction itself.
|
|
2837
|
+
*
|
|
2838
|
+
* Usage:
|
|
2839
|
+
* - Campaign Claims (user claims rewards via blockchain transactions)
|
|
2840
|
+
* - Redemption Redeems (user redeems items via blockchain transactions)
|
|
2841
|
+
* - Any business process creating blockchain transactions that need status tracking
|
|
2842
|
+
*
|
|
2843
|
+
* NOT for blockchain transactions themselves - use TransactionStatus instead.
|
|
2844
|
+
*/
|
|
2845
|
+
exports.ProcessRecordStatus = void 0;
|
|
2846
|
+
(function (ProcessRecordStatus) {
|
|
2847
|
+
ProcessRecordStatus["PENDING"] = "PENDING";
|
|
2848
|
+
ProcessRecordStatus["PROCESSING"] = "PROCESSING";
|
|
2849
|
+
ProcessRecordStatus["COMPLETED"] = "COMPLETED";
|
|
2850
|
+
ProcessRecordStatus["FAILED"] = "FAILED"; // Transaction processing failed (blockchain or validation error)
|
|
2851
|
+
})(exports.ProcessRecordStatus || (exports.ProcessRecordStatus = {}));
|
|
2852
|
+
|
|
2831
2853
|
exports.PurchaseStatus = void 0;
|
|
2832
2854
|
(function (PurchaseStatus) {
|
|
2833
2855
|
// after creation of payment and before payment is done by user
|
|
@@ -2847,35 +2869,30 @@ exports.PurchaseCurrency = void 0;
|
|
|
2847
2869
|
PurchaseCurrency["EUR"] = "eur";
|
|
2848
2870
|
})(exports.PurchaseCurrency || (exports.PurchaseCurrency = {}));
|
|
2849
2871
|
|
|
2850
|
-
exports.RedemptionRedeemStatus = void 0;
|
|
2851
|
-
(function (RedemptionRedeemStatus) {
|
|
2852
|
-
RedemptionRedeemStatus["PENDING"] = "PENDING";
|
|
2853
|
-
RedemptionRedeemStatus["PROCESSING"] = "PROCESSING";
|
|
2854
|
-
RedemptionRedeemStatus["COMPLETED"] = "COMPLETED";
|
|
2855
|
-
RedemptionRedeemStatus["FAILED"] = "FAILED"; // Processing failed
|
|
2856
|
-
})(exports.RedemptionRedeemStatus || (exports.RedemptionRedeemStatus = {}));
|
|
2857
|
-
|
|
2858
2872
|
/**
|
|
2859
2873
|
* Trigger Source Types
|
|
2860
2874
|
* Defines the different types of triggers that can activate a campaign flow
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2875
|
+
* Using const object pattern for better extensibility without requiring deploys
|
|
2876
|
+
*/
|
|
2877
|
+
const TRIGGER_SOURCE_TYPES = {
|
|
2878
|
+
QR_CODE: 'QR_CODE',
|
|
2879
|
+
NFC_TAG: 'NFC_TAG',
|
|
2880
|
+
API_WEBHOOK: 'API_WEBHOOK',
|
|
2881
|
+
GPS_GEOFENCE: 'GPS_GEOFENCE',
|
|
2882
|
+
TRANSACTION: 'TRANSACTION',
|
|
2883
|
+
};
|
|
2884
|
+
// Export values array for validation
|
|
2885
|
+
const TRIGGER_SOURCE_TYPE_VALUES = Object.values(TRIGGER_SOURCE_TYPES);
|
|
2869
2886
|
/**
|
|
2870
2887
|
* Source Logic Types
|
|
2871
2888
|
* Defines how multiple trigger sources combine to activate a flow
|
|
2872
2889
|
* Currently only ANY (OR logic) is implemented
|
|
2873
2890
|
*/
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2891
|
+
const SOURCE_LOGIC_TYPES = {
|
|
2892
|
+
ANY: 'any'
|
|
2893
|
+
// Future: ALL: 'all', SEQUENCE: 'sequence', WEIGHTED: 'weighted'
|
|
2894
|
+
};
|
|
2895
|
+
const SOURCE_LOGIC_TYPE_VALUES = Object.values(SOURCE_LOGIC_TYPES);
|
|
2879
2896
|
|
|
2880
2897
|
exports.CampaignConditionType = void 0;
|
|
2881
2898
|
(function (CampaignConditionType) {
|
|
@@ -3104,6 +3121,103 @@ const Domains = {
|
|
|
3104
3121
|
AUDIT: 'audit',
|
|
3105
3122
|
};
|
|
3106
3123
|
|
|
3124
|
+
/**
|
|
3125
|
+
* SQL operator mapping for query operators
|
|
3126
|
+
* Maps abstract query operators to SQL equivalents
|
|
3127
|
+
*/
|
|
3128
|
+
const QUERY_OPERATOR_SQL_MAP = {
|
|
3129
|
+
eq: '=',
|
|
3130
|
+
ne: '!=',
|
|
3131
|
+
gt: '>',
|
|
3132
|
+
gte: '>=',
|
|
3133
|
+
lt: '<',
|
|
3134
|
+
lte: '<=',
|
|
3135
|
+
in: 'IN',
|
|
3136
|
+
nin: 'NOT IN'
|
|
3137
|
+
};
|
|
3138
|
+
|
|
3139
|
+
/**
|
|
3140
|
+
* Helper to validate includes array at runtime
|
|
3141
|
+
*/
|
|
3142
|
+
const VALID_RELATIONS = ['user', 'campaign', 'business', 'triggerSource'];
|
|
3143
|
+
function isValidRelation(value) {
|
|
3144
|
+
return VALID_RELATIONS.includes(value);
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
/**
|
|
3148
|
+
* Valid transaction relations for runtime validation
|
|
3149
|
+
*/
|
|
3150
|
+
const VALID_TRANSACTION_RELATIONS = [
|
|
3151
|
+
'sender',
|
|
3152
|
+
'recipient',
|
|
3153
|
+
'business'
|
|
3154
|
+
];
|
|
3155
|
+
/**
|
|
3156
|
+
* Type guard to validate relation strings at runtime
|
|
3157
|
+
*/
|
|
3158
|
+
function isValidTransactionRelation(value) {
|
|
3159
|
+
return VALID_TRANSACTION_RELATIONS.includes(value);
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
/**
|
|
3163
|
+
* Array of all valid campaign include relations
|
|
3164
|
+
* Used for runtime validation
|
|
3165
|
+
*/
|
|
3166
|
+
const VALID_CAMPAIGN_RELATIONS = [
|
|
3167
|
+
'triggerSources',
|
|
3168
|
+
'businesses'
|
|
3169
|
+
];
|
|
3170
|
+
/**
|
|
3171
|
+
* Type guard to validate campaign include relation at runtime
|
|
3172
|
+
*
|
|
3173
|
+
* @param value - String to validate
|
|
3174
|
+
* @returns True if value is a valid CampaignIncludeRelation
|
|
3175
|
+
*/
|
|
3176
|
+
function isValidCampaignRelation(value) {
|
|
3177
|
+
return VALID_CAMPAIGN_RELATIONS.includes(value);
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
/**
|
|
3181
|
+
* Array of all valid campaign claim include relations
|
|
3182
|
+
* Used for runtime validation
|
|
3183
|
+
*/
|
|
3184
|
+
const VALID_CAMPAIGN_CLAIM_RELATIONS = [
|
|
3185
|
+
'campaign',
|
|
3186
|
+
'user',
|
|
3187
|
+
'business',
|
|
3188
|
+
'triggerSource',
|
|
3189
|
+
'transactions'
|
|
3190
|
+
];
|
|
3191
|
+
/**
|
|
3192
|
+
* Type guard to validate campaign claim include relation at runtime
|
|
3193
|
+
*
|
|
3194
|
+
* @param value - String to validate
|
|
3195
|
+
* @returns True if value is a valid CampaignClaimIncludeRelation
|
|
3196
|
+
*/
|
|
3197
|
+
function isValidCampaignClaimRelation(value) {
|
|
3198
|
+
return VALID_CAMPAIGN_CLAIM_RELATIONS.includes(value);
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
/**
|
|
3202
|
+
* Array of all valid redemption redeem include relations
|
|
3203
|
+
* Used for runtime validation
|
|
3204
|
+
*/
|
|
3205
|
+
const VALID_REDEMPTION_REDEEM_RELATIONS = [
|
|
3206
|
+
'redemption',
|
|
3207
|
+
'user',
|
|
3208
|
+
'business',
|
|
3209
|
+
'transactions'
|
|
3210
|
+
];
|
|
3211
|
+
/**
|
|
3212
|
+
* Type guard to validate redemption redeem include relation at runtime
|
|
3213
|
+
*
|
|
3214
|
+
* @param value - String to validate
|
|
3215
|
+
* @returns True if value is a valid RedemptionRedeemIncludeRelation
|
|
3216
|
+
*/
|
|
3217
|
+
function isValidRedemptionRedeemRelation(value) {
|
|
3218
|
+
return VALID_REDEMPTION_REDEEM_RELATIONS.includes(value);
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3107
3221
|
/**
|
|
3108
3222
|
* Transaction format constants for Ethereum and EVM-compatible chains
|
|
3109
3223
|
* Using const assertions for zero runtime overhead
|
|
@@ -3642,10 +3756,30 @@ class UserApi {
|
|
|
3642
3756
|
* ADMIN: Get all remote users with query parameters
|
|
3643
3757
|
* Uses new RESTful /users endpoint with role-based access
|
|
3644
3758
|
* Note: Admin users get full data, non-admin users get public profiles only
|
|
3759
|
+
*
|
|
3760
|
+
* @param options - Pagination options (page, limit, sortBy, sortOrder)
|
|
3761
|
+
* @param search - Optional search query to filter users
|
|
3762
|
+
*
|
|
3763
|
+
* @example
|
|
3764
|
+
* ```typescript
|
|
3765
|
+
* // Get users sorted by email
|
|
3766
|
+
* const users = await sdk.users.getAllUsers({
|
|
3767
|
+
* page: 1,
|
|
3768
|
+
* limit: 10,
|
|
3769
|
+
* sortBy: 'email',
|
|
3770
|
+
* sortOrder: SortOrder.ASC
|
|
3771
|
+
* });
|
|
3772
|
+
*
|
|
3773
|
+
* // Search for users
|
|
3774
|
+
* const searchResults = await sdk.users.getAllUsers({ page: 1, limit: 10 }, 'john');
|
|
3775
|
+
* ```
|
|
3645
3776
|
*/
|
|
3646
|
-
async getAllRemoteUsers(options) {
|
|
3777
|
+
async getAllRemoteUsers(options, search) {
|
|
3647
3778
|
const params = buildPaginationParams(options);
|
|
3648
|
-
|
|
3779
|
+
if (search) {
|
|
3780
|
+
params.set('search', search);
|
|
3781
|
+
}
|
|
3782
|
+
const url = `${this.basePath}?merge=soft&${params.toString()}`;
|
|
3649
3783
|
const response = await this.apiClient.get(url);
|
|
3650
3784
|
return normalizeToPaginated(response);
|
|
3651
3785
|
}
|
|
@@ -3740,9 +3874,11 @@ class UserService {
|
|
|
3740
3874
|
/**
|
|
3741
3875
|
* ADMIN: Get all remote users
|
|
3742
3876
|
* ✅ FIXED: Matches API method signature (no parameters needed)
|
|
3877
|
+
* @param options - Pagination options
|
|
3878
|
+
* @param search - Optional search query
|
|
3743
3879
|
*/
|
|
3744
|
-
async getAllRemoteUsers(options) {
|
|
3745
|
-
return this.userApi.getAllRemoteUsers(options);
|
|
3880
|
+
async getAllRemoteUsers(options, search) {
|
|
3881
|
+
return this.userApi.getAllRemoteUsers(options, search);
|
|
3746
3882
|
}
|
|
3747
3883
|
/**
|
|
3748
3884
|
* ADMIN: Update user as admin
|
|
@@ -4913,6 +5049,19 @@ class CampaignApi {
|
|
|
4913
5049
|
* Get campaigns with pagination support
|
|
4914
5050
|
* Returns paginated response with metadata (total, page, limit, hasMore)
|
|
4915
5051
|
* Intelligent access: Public gets active only, Business gets own campaigns, Admin gets all
|
|
5052
|
+
*
|
|
5053
|
+
* @param options - Filter, pagination, and include options
|
|
5054
|
+
* @param options.include - Relations to include: 'triggerSources', 'businesses'
|
|
5055
|
+
*
|
|
5056
|
+
* @example
|
|
5057
|
+
* ```typescript
|
|
5058
|
+
* // Get campaigns with trigger sources included
|
|
5059
|
+
* const campaigns = await api.getCampaigns({ include: ['triggerSources'] });
|
|
5060
|
+
* campaigns.data.forEach(c => console.log(c.included?.triggerSources));
|
|
5061
|
+
*
|
|
5062
|
+
* // Get campaigns with both relations
|
|
5063
|
+
* const full = await api.getCampaigns({ include: ['triggerSources', 'businesses'] });
|
|
5064
|
+
* ```
|
|
4916
5065
|
*/
|
|
4917
5066
|
async getCampaigns(options) {
|
|
4918
5067
|
const params = [];
|
|
@@ -4929,14 +5078,28 @@ class CampaignApi {
|
|
|
4929
5078
|
params.push(`sortBy=${options.sortBy}`);
|
|
4930
5079
|
if (options?.sortOrder)
|
|
4931
5080
|
params.push(`sortOrder=${options.sortOrder}`);
|
|
5081
|
+
if (options?.include?.length)
|
|
5082
|
+
params.push(`include=${options.include.join(',')}`);
|
|
4932
5083
|
return this.apiClient.get(`/campaigns?${params.join('&')}`);
|
|
4933
5084
|
}
|
|
4934
5085
|
/**
|
|
4935
5086
|
* PUBLIC: Get campaign by ID
|
|
4936
5087
|
* NEW: /campaigns/{id}
|
|
5088
|
+
*
|
|
5089
|
+
* @param id - Campaign UUID
|
|
5090
|
+
* @param include - Relations to include: 'triggerSources', 'businesses'
|
|
5091
|
+
*
|
|
5092
|
+
* @example
|
|
5093
|
+
* ```typescript
|
|
5094
|
+
* // Get campaign with all relations
|
|
5095
|
+
* const campaign = await api.getCampaignById('123', ['triggerSources', 'businesses']);
|
|
5096
|
+
* console.log(campaign.included?.triggerSources);
|
|
5097
|
+
* console.log(campaign.included?.businesses);
|
|
5098
|
+
* ```
|
|
4937
5099
|
*/
|
|
4938
|
-
async getCampaignById(id) {
|
|
4939
|
-
|
|
5100
|
+
async getCampaignById(id, include) {
|
|
5101
|
+
const params = include?.length ? `?include=${include.join(',')}` : '';
|
|
5102
|
+
return this.apiClient.get(`/campaigns/${id}${params}`);
|
|
4940
5103
|
}
|
|
4941
5104
|
/**
|
|
4942
5105
|
* ADMIN: Create campaign
|
|
@@ -5055,6 +5218,16 @@ class CampaignApi {
|
|
|
5055
5218
|
const response = await this.apiClient.get(`/campaign-triggers?${params.toString()}`);
|
|
5056
5219
|
return normalizeToPaginated(response);
|
|
5057
5220
|
}
|
|
5221
|
+
/**
|
|
5222
|
+
* PUBLIC: Get campaign trigger by ID
|
|
5223
|
+
* NEW: GET /campaign-triggers/{id}
|
|
5224
|
+
*
|
|
5225
|
+
* @param triggerId - Campaign trigger UUID
|
|
5226
|
+
* @returns Campaign trigger details
|
|
5227
|
+
*/
|
|
5228
|
+
async getCampaignTriggerById(triggerId) {
|
|
5229
|
+
return this.apiClient.get(`/campaign-triggers/${triggerId}`);
|
|
5230
|
+
}
|
|
5058
5231
|
/**
|
|
5059
5232
|
* ADMIN: Create campaign trigger
|
|
5060
5233
|
* NEW: POST /campaign-triggers
|
|
@@ -5083,6 +5256,13 @@ class CampaignApi {
|
|
|
5083
5256
|
async setCampaignTrigger(campaignId, triggerId) {
|
|
5084
5257
|
return this.apiClient.put(`/campaign-triggers/${triggerId}/assign/${campaignId}`, {});
|
|
5085
5258
|
}
|
|
5259
|
+
/**
|
|
5260
|
+
* ADMIN: Remove trigger from campaign (unassign)
|
|
5261
|
+
* NEW: DELETE /campaign-triggers/{triggerId}/assign/{campaignId}
|
|
5262
|
+
*/
|
|
5263
|
+
async removeCampaignTrigger(campaignId, triggerId) {
|
|
5264
|
+
return this.apiClient.delete(`/campaign-triggers/${triggerId}/assign/${campaignId}`);
|
|
5265
|
+
}
|
|
5086
5266
|
/**
|
|
5087
5267
|
* ADMIN: Add/Remove condition to trigger
|
|
5088
5268
|
* NEW: PUT /campaign-triggers/{triggerId}/conditions/{conditionId}
|
|
@@ -5154,7 +5334,7 @@ class CampaignApi {
|
|
|
5154
5334
|
* });
|
|
5155
5335
|
* ```
|
|
5156
5336
|
*/
|
|
5157
|
-
async getClaims(filters) {
|
|
5337
|
+
async getClaims(filters, include) {
|
|
5158
5338
|
const params = buildPaginationParams(filters);
|
|
5159
5339
|
// Add filter parameters
|
|
5160
5340
|
if (filters?.campaignId)
|
|
@@ -5163,6 +5343,10 @@ class CampaignApi {
|
|
|
5163
5343
|
params.set('userId', filters.userId);
|
|
5164
5344
|
if (filters?.businessId)
|
|
5165
5345
|
params.set('businessId', filters.businessId);
|
|
5346
|
+
// Add include parameter
|
|
5347
|
+
if (include && include.length > 0) {
|
|
5348
|
+
include.forEach(relation => params.append('include', relation));
|
|
5349
|
+
}
|
|
5166
5350
|
const endpoint = `/campaigns/claims?${params.toString()}`;
|
|
5167
5351
|
const response = await this.apiClient.get(endpoint);
|
|
5168
5352
|
return normalizeToPaginated(response);
|
|
@@ -5172,13 +5356,42 @@ class CampaignApi {
|
|
|
5172
5356
|
* Uses /me endpoint for authenticated user context
|
|
5173
5357
|
*
|
|
5174
5358
|
* @param options - Pagination options
|
|
5359
|
+
* @param include - Optional relations to include (campaign, user, business)
|
|
5175
5360
|
* @returns Paginated list of user's claims
|
|
5176
5361
|
*/
|
|
5177
|
-
async getClaimsLoggedUser(options) {
|
|
5362
|
+
async getClaimsLoggedUser(options, include) {
|
|
5178
5363
|
const params = buildPaginationParams(options);
|
|
5364
|
+
// Add include parameter
|
|
5365
|
+
if (include && include.length > 0) {
|
|
5366
|
+
include.forEach(relation => params.append('include', relation));
|
|
5367
|
+
}
|
|
5179
5368
|
const response = await this.apiClient.get(`/campaigns/claims/me?${params.toString()}`);
|
|
5180
5369
|
return normalizeToPaginated(response);
|
|
5181
5370
|
}
|
|
5371
|
+
// ==========================================
|
|
5372
|
+
// TRIGGER SOURCE ASSIGNMENT (/campaigns/{id}/trigger-sources)
|
|
5373
|
+
// Note: TriggerSource CRUD is in TriggerSourceApi
|
|
5374
|
+
// ==========================================
|
|
5375
|
+
/**
|
|
5376
|
+
* ADMIN: Assign a trigger source to a campaign
|
|
5377
|
+
*
|
|
5378
|
+
* @param campaignId - Campaign UUID
|
|
5379
|
+
* @param triggerSourceId - Trigger source UUID
|
|
5380
|
+
* @returns Updated campaign with trigger source assigned
|
|
5381
|
+
*/
|
|
5382
|
+
async assignTriggerSourceToCampaign(campaignId, triggerSourceId) {
|
|
5383
|
+
return this.apiClient.put(`/campaigns/${campaignId}/trigger-sources/${triggerSourceId}`, {});
|
|
5384
|
+
}
|
|
5385
|
+
/**
|
|
5386
|
+
* ADMIN: Remove a trigger source from a campaign
|
|
5387
|
+
*
|
|
5388
|
+
* @param campaignId - Campaign UUID
|
|
5389
|
+
* @param triggerSourceId - Trigger source UUID
|
|
5390
|
+
* @returns Updated campaign with trigger source removed
|
|
5391
|
+
*/
|
|
5392
|
+
async removeTriggerSourceFromCampaign(campaignId, triggerSourceId) {
|
|
5393
|
+
return this.apiClient.delete(`/campaigns/${campaignId}/trigger-sources/${triggerSourceId}`);
|
|
5394
|
+
}
|
|
5182
5395
|
}
|
|
5183
5396
|
|
|
5184
5397
|
/**
|
|
@@ -5198,9 +5411,11 @@ class CampaignService {
|
|
|
5198
5411
|
// ==========================================
|
|
5199
5412
|
/**
|
|
5200
5413
|
* PUBLIC: Get campaign by ID
|
|
5414
|
+
* @param id - Campaign UUID
|
|
5415
|
+
* @param include - Relations to include: 'triggerSources', 'businesses'
|
|
5201
5416
|
*/
|
|
5202
|
-
async getCampaignById(id) {
|
|
5203
|
-
return this.campaignApi.getCampaignById(id);
|
|
5417
|
+
async getCampaignById(id, include) {
|
|
5418
|
+
return this.campaignApi.getCampaignById(id, include);
|
|
5204
5419
|
}
|
|
5205
5420
|
// ==========================================
|
|
5206
5421
|
// AUTHENTICATED OPERATIONS
|
|
@@ -5213,9 +5428,11 @@ class CampaignService {
|
|
|
5213
5428
|
}
|
|
5214
5429
|
/**
|
|
5215
5430
|
* AUTH: Get claims for logged user with pagination
|
|
5431
|
+
* @param options - Pagination options
|
|
5432
|
+
* @param include - Optional relations to include (campaign, user, business)
|
|
5216
5433
|
*/
|
|
5217
|
-
async getClaimsForLoggedUser(options) {
|
|
5218
|
-
return this.campaignApi.getClaimsLoggedUser(options);
|
|
5434
|
+
async getClaimsForLoggedUser(options, include) {
|
|
5435
|
+
return this.campaignApi.getClaimsLoggedUser(options, include);
|
|
5219
5436
|
}
|
|
5220
5437
|
// ==========================================
|
|
5221
5438
|
// CAMPAIGN OPERATIONS (Paginated)
|
|
@@ -5229,6 +5446,7 @@ class CampaignService {
|
|
|
5229
5446
|
* @param options.limit - Items per page (default: 50)
|
|
5230
5447
|
* @param options.sortBy - Sort field
|
|
5231
5448
|
* @param options.sortOrder - Sort direction
|
|
5449
|
+
* @param options.include - Relations to include: 'triggerSources', 'businesses'
|
|
5232
5450
|
*/
|
|
5233
5451
|
async getCampaigns(options) {
|
|
5234
5452
|
return this.campaignApi.getCampaigns(options);
|
|
@@ -5239,6 +5457,30 @@ class CampaignService {
|
|
|
5239
5457
|
async getCampaignTriggers(options) {
|
|
5240
5458
|
return this.campaignApi.getCampaignTriggers(options);
|
|
5241
5459
|
}
|
|
5460
|
+
/**
|
|
5461
|
+
* ADMIN: Get campaign trigger by ID
|
|
5462
|
+
*/
|
|
5463
|
+
async getCampaignTriggerById(triggerId) {
|
|
5464
|
+
return this.campaignApi.getCampaignTriggerById(triggerId);
|
|
5465
|
+
}
|
|
5466
|
+
/**
|
|
5467
|
+
* ADMIN: Create campaign trigger
|
|
5468
|
+
*/
|
|
5469
|
+
async createCampaignTrigger(data) {
|
|
5470
|
+
return this.campaignApi.createCampaignTrigger(data);
|
|
5471
|
+
}
|
|
5472
|
+
/**
|
|
5473
|
+
* ADMIN: Update campaign trigger
|
|
5474
|
+
*/
|
|
5475
|
+
async updateCampaignTrigger(triggerId, data) {
|
|
5476
|
+
return this.campaignApi.updateCampaignTrigger(triggerId, data);
|
|
5477
|
+
}
|
|
5478
|
+
/**
|
|
5479
|
+
* ADMIN: Delete campaign trigger
|
|
5480
|
+
*/
|
|
5481
|
+
async deleteCampaignTrigger(triggerId) {
|
|
5482
|
+
return this.campaignApi.deleteCampaignTrigger(triggerId);
|
|
5483
|
+
}
|
|
5242
5484
|
/**
|
|
5243
5485
|
* ADMIN: Toggle campaign active status
|
|
5244
5486
|
*/
|
|
@@ -5263,6 +5505,12 @@ class CampaignService {
|
|
|
5263
5505
|
async setCampaignTrigger(campaignId, triggerId) {
|
|
5264
5506
|
return this.campaignApi.setCampaignTrigger(campaignId, triggerId);
|
|
5265
5507
|
}
|
|
5508
|
+
/**
|
|
5509
|
+
* ADMIN: Remove trigger from campaign (unassign)
|
|
5510
|
+
*/
|
|
5511
|
+
async removeCampaignTrigger(campaignId, triggerId) {
|
|
5512
|
+
return this.campaignApi.removeCampaignTrigger(campaignId, triggerId);
|
|
5513
|
+
}
|
|
5266
5514
|
/**
|
|
5267
5515
|
* ADMIN: Update campaign
|
|
5268
5516
|
*/
|
|
@@ -5298,21 +5546,45 @@ class CampaignService {
|
|
|
5298
5546
|
}
|
|
5299
5547
|
/**
|
|
5300
5548
|
* ADMIN: Get campaign claims with optional filters and pagination
|
|
5549
|
+
* @param filters - Filter and pagination options
|
|
5550
|
+
* @param include - Optional relations to include (campaign, user, business)
|
|
5301
5551
|
*/
|
|
5302
|
-
async getCampaignClaims(filters) {
|
|
5303
|
-
return this.campaignApi.getClaims(filters);
|
|
5552
|
+
async getCampaignClaims(filters, include) {
|
|
5553
|
+
return this.campaignApi.getClaims(filters, include);
|
|
5304
5554
|
}
|
|
5305
5555
|
/**
|
|
5306
5556
|
* ADMIN: Get campaign claims by user ID with pagination
|
|
5557
|
+
* @param userId - User ID to filter by
|
|
5558
|
+
* @param options - Pagination options
|
|
5559
|
+
* @param include - Optional relations to include (campaign, user, business)
|
|
5307
5560
|
*/
|
|
5308
|
-
async getCampaignClaimsByUserId(userId, options) {
|
|
5309
|
-
return this.campaignApi.getClaims({ userId, ...options });
|
|
5561
|
+
async getCampaignClaimsByUserId(userId, options, include) {
|
|
5562
|
+
return this.campaignApi.getClaims({ userId, ...options }, include);
|
|
5310
5563
|
}
|
|
5311
5564
|
/**
|
|
5312
5565
|
* ADMIN: Get campaign claims by business ID with pagination
|
|
5566
|
+
* @param businessId - Business ID to filter by
|
|
5567
|
+
* @param options - Pagination options
|
|
5568
|
+
* @param include - Optional relations to include (campaign, user, business)
|
|
5569
|
+
*/
|
|
5570
|
+
async getCampaignClaimsByBusinessId(businessId, options, include) {
|
|
5571
|
+
return this.campaignApi.getClaims({ businessId, ...options }, include);
|
|
5572
|
+
}
|
|
5573
|
+
// ==========================================
|
|
5574
|
+
// TRIGGER SOURCE ASSIGNMENT
|
|
5575
|
+
// Note: TriggerSource CRUD is in TriggerSourceService
|
|
5576
|
+
// ==========================================
|
|
5577
|
+
/**
|
|
5578
|
+
* ADMIN: Assign a trigger source to a campaign
|
|
5313
5579
|
*/
|
|
5314
|
-
async
|
|
5315
|
-
return this.campaignApi.
|
|
5580
|
+
async assignTriggerSourceToCampaign(campaignId, triggerSourceId) {
|
|
5581
|
+
return this.campaignApi.assignTriggerSourceToCampaign(campaignId, triggerSourceId);
|
|
5582
|
+
}
|
|
5583
|
+
/**
|
|
5584
|
+
* ADMIN: Remove a trigger source from a campaign
|
|
5585
|
+
*/
|
|
5586
|
+
async removeTriggerSourceFromCampaign(campaignId, triggerSourceId) {
|
|
5587
|
+
return this.campaignApi.removeTriggerSourceFromCampaign(campaignId, triggerSourceId);
|
|
5316
5588
|
}
|
|
5317
5589
|
}
|
|
5318
5590
|
|
|
@@ -5344,12 +5616,7 @@ class RedemptionApi {
|
|
|
5344
5616
|
* - Public users: Get active redemptions only
|
|
5345
5617
|
* - Admin users: Get all redemptions with optional filtering
|
|
5346
5618
|
*
|
|
5347
|
-
* @param options
|
|
5348
|
-
* @param options.adminAccess - Force admin access (requires admin auth)
|
|
5349
|
-
* @param options.page - Page number (default: 1)
|
|
5350
|
-
* @param options.limit - Items per page (default: 50)
|
|
5351
|
-
* @param options.sortBy - Sort field
|
|
5352
|
-
* @param options.sortOrder - Sort order (asc/desc)
|
|
5619
|
+
* @param options - Filter and pagination options (active, adminAccess, page, limit, sortBy, sortOrder)
|
|
5353
5620
|
* @returns Paginated response with redemptions
|
|
5354
5621
|
*/
|
|
5355
5622
|
async getRedemptions(options) {
|
|
@@ -5416,17 +5683,11 @@ class RedemptionApi {
|
|
|
5416
5683
|
* - Users: See only their own redeems (userId/businessId filters ignored)
|
|
5417
5684
|
* - Admins: Can filter by userId, businessId, or redemptionId
|
|
5418
5685
|
*
|
|
5419
|
-
* @param filters
|
|
5420
|
-
* @param
|
|
5421
|
-
* @param filters.businessId - Admin only: Filter by business ID
|
|
5422
|
-
* @param filters.myRedeems - Force user's own redeems (uses /me endpoint)
|
|
5423
|
-
* @param filters.page - Page number (default: 1)
|
|
5424
|
-
* @param filters.limit - Items per page (default: 50)
|
|
5425
|
-
* @param filters.sortBy - Sort field
|
|
5426
|
-
* @param filters.sortOrder - Sort order (asc/desc)
|
|
5686
|
+
* @param filters - Filter and pagination options (redemptionId, userId, businessId, myRedeems, page, limit, sortBy, sortOrder)
|
|
5687
|
+
* @param include - Optional relations to include (redemption, user, business)
|
|
5427
5688
|
* @returns Paginated response with redemption redeems
|
|
5428
5689
|
*/
|
|
5429
|
-
async getRedemptionRedeems(filters) {
|
|
5690
|
+
async getRedemptionRedeems(filters, include) {
|
|
5430
5691
|
let url = `${this.basePath}/redeems`;
|
|
5431
5692
|
const params = buildPaginationParams(filters);
|
|
5432
5693
|
// Use convenience endpoint for user's own redeems
|
|
@@ -5435,6 +5696,10 @@ class RedemptionApi {
|
|
|
5435
5696
|
if (filters?.redemptionId) {
|
|
5436
5697
|
params.append('redemptionId', filters.redemptionId);
|
|
5437
5698
|
}
|
|
5699
|
+
// Add include parameter
|
|
5700
|
+
if (include && include.length > 0) {
|
|
5701
|
+
include.forEach(relation => params.append('include', relation));
|
|
5702
|
+
}
|
|
5438
5703
|
const response = await this.apiClient.get(`${url}?${params.toString()}`);
|
|
5439
5704
|
return normalizeToPaginated(response);
|
|
5440
5705
|
}
|
|
@@ -5445,14 +5710,26 @@ class RedemptionApi {
|
|
|
5445
5710
|
params.append('userId', filters.userId);
|
|
5446
5711
|
if (filters?.businessId)
|
|
5447
5712
|
params.append('businessId', filters.businessId);
|
|
5713
|
+
// Add include parameter
|
|
5714
|
+
if (include && include.length > 0) {
|
|
5715
|
+
include.forEach(relation => params.append('include', relation));
|
|
5716
|
+
}
|
|
5448
5717
|
const response = await this.apiClient.get(`${url}?${params.toString()}`);
|
|
5449
5718
|
return normalizeToPaginated(response);
|
|
5450
5719
|
}
|
|
5451
5720
|
/**
|
|
5452
5721
|
* UNIFIED: Get specific redemption redeem by ID
|
|
5722
|
+
* @param id - Redemption redeem ID
|
|
5723
|
+
* @param include - Optional relations to include (redemption, user, business)
|
|
5453
5724
|
*/
|
|
5454
|
-
async getRedemptionRedeemById(id) {
|
|
5455
|
-
|
|
5725
|
+
async getRedemptionRedeemById(id, include) {
|
|
5726
|
+
const params = new URLSearchParams();
|
|
5727
|
+
// Add include parameter
|
|
5728
|
+
if (include && include.length > 0) {
|
|
5729
|
+
include.forEach(relation => params.append('include', relation));
|
|
5730
|
+
}
|
|
5731
|
+
const url = `${this.basePath}/redeems/${id}${params.toString() ? `?${params.toString()}` : ''}`;
|
|
5732
|
+
return this.apiClient.get(url);
|
|
5456
5733
|
}
|
|
5457
5734
|
// ==========================================
|
|
5458
5735
|
// USER OPERATIONS (JWT + Project Key)
|
|
@@ -5463,10 +5740,11 @@ class RedemptionApi {
|
|
|
5463
5740
|
* Uses convenience endpoint /redemption-redeems/me with optional filtering
|
|
5464
5741
|
* @param redemptionId - Optional filter by specific redemption
|
|
5465
5742
|
* @param options - Pagination options (page, limit, sortBy, sortOrder)
|
|
5743
|
+
* @param include - Optional relations to include (redemption, user, business)
|
|
5466
5744
|
* @returns Paginated response with user's redemption redeems
|
|
5467
5745
|
*/
|
|
5468
|
-
async getUserRedeems(redemptionId, options) {
|
|
5469
|
-
return this.getRedemptionRedeems({ myRedeems: true, redemptionId, ...options });
|
|
5746
|
+
async getUserRedeems(redemptionId, options, include) {
|
|
5747
|
+
return this.getRedemptionRedeems({ myRedeems: true, redemptionId, ...options }, include);
|
|
5470
5748
|
}
|
|
5471
5749
|
// ==========================================
|
|
5472
5750
|
// ADMIN OPERATIONS (Tenant Admin JWT)
|
|
@@ -5599,15 +5877,19 @@ class RedemptionService {
|
|
|
5599
5877
|
}
|
|
5600
5878
|
/**
|
|
5601
5879
|
* UNIFIED: Get redemption redeems with filtering and pagination
|
|
5880
|
+
* @param filters - Filter options
|
|
5881
|
+
* @param include - Optional relations to include (redemption, user, business)
|
|
5602
5882
|
*/
|
|
5603
|
-
async getRedemptionRedeems(filters) {
|
|
5604
|
-
return this.redemptionApi.getRedemptionRedeems(filters);
|
|
5883
|
+
async getRedemptionRedeems(filters, include) {
|
|
5884
|
+
return this.redemptionApi.getRedemptionRedeems(filters, include);
|
|
5605
5885
|
}
|
|
5606
5886
|
/**
|
|
5607
5887
|
* Convenience: Get user redemptions with pagination
|
|
5888
|
+
* @param options - Pagination options
|
|
5889
|
+
* @param include - Optional relations to include (redemption, user, business)
|
|
5608
5890
|
*/
|
|
5609
|
-
async getUserRedeems(options) {
|
|
5610
|
-
return this.redemptionApi.getUserRedeems(undefined, options);
|
|
5891
|
+
async getUserRedeems(options, include) {
|
|
5892
|
+
return this.redemptionApi.getUserRedeems(undefined, options, include);
|
|
5611
5893
|
}
|
|
5612
5894
|
// ==========================================
|
|
5613
5895
|
// ADMIN OPERATIONS
|
|
@@ -5672,9 +5954,19 @@ class TransactionApi {
|
|
|
5672
5954
|
* Get transaction by ID (public endpoint)
|
|
5673
5955
|
*
|
|
5674
5956
|
* UPDATED: /transaction/{id} → /transactions/{id}
|
|
5957
|
+
* UPDATED: Added support for include parameter to enrich with sender/recipient/business entities
|
|
5958
|
+
*
|
|
5959
|
+
* @param transactionId - Transaction ID
|
|
5960
|
+
* @param include - Optional relations to include (sender, recipient, business)
|
|
5675
5961
|
*/
|
|
5676
|
-
async getTransactionById(transactionId) {
|
|
5677
|
-
|
|
5962
|
+
async getTransactionById(transactionId, include) {
|
|
5963
|
+
let url = `${this.basePath}/${transactionId}`;
|
|
5964
|
+
if (include && include.length > 0) {
|
|
5965
|
+
const params = new URLSearchParams();
|
|
5966
|
+
include.forEach(relation => params.append('include', relation));
|
|
5967
|
+
url += `?${params.toString()}`;
|
|
5968
|
+
}
|
|
5969
|
+
return this.apiClient.get(url);
|
|
5678
5970
|
}
|
|
5679
5971
|
/**
|
|
5680
5972
|
* Unique method to create a transaction
|
|
@@ -5688,13 +5980,78 @@ class TransactionApi {
|
|
|
5688
5980
|
// ==========================================
|
|
5689
5981
|
// AUTHENTICATED USER OPERATIONS
|
|
5690
5982
|
// ==========================================
|
|
5983
|
+
/**
|
|
5984
|
+
* Helper to build query params from TransactionQueryOptions
|
|
5985
|
+
* Handles all filter options and include parameter
|
|
5986
|
+
*/
|
|
5987
|
+
buildTransactionQueryParams(options) {
|
|
5988
|
+
const params = buildPaginationParams(options);
|
|
5989
|
+
if (!options)
|
|
5990
|
+
return params;
|
|
5991
|
+
// Search and identification filters
|
|
5992
|
+
if (options.search)
|
|
5993
|
+
params.append('search', options.search);
|
|
5994
|
+
if (options.participantId)
|
|
5995
|
+
params.append('participantId', options.participantId);
|
|
5996
|
+
if (options.participantAddress)
|
|
5997
|
+
params.append('participantAddress', options.participantAddress);
|
|
5998
|
+
// Business and process filters
|
|
5999
|
+
if (options.engagedBusinessId)
|
|
6000
|
+
params.append('engagedBusinessId', options.engagedBusinessId);
|
|
6001
|
+
if (options.businessScopeId)
|
|
6002
|
+
params.append('businessScopeId', options.businessScopeId);
|
|
6003
|
+
if (options.triggerProcessId)
|
|
6004
|
+
params.append('triggerProcessId', options.triggerProcessId);
|
|
6005
|
+
// Token filters
|
|
6006
|
+
if (options.tokenAddress)
|
|
6007
|
+
params.append('tokenAddress', options.tokenAddress);
|
|
6008
|
+
if (options.tokenType) {
|
|
6009
|
+
if (Array.isArray(options.tokenType)) {
|
|
6010
|
+
options.tokenType.forEach(type => params.append('tokenType', type));
|
|
6011
|
+
}
|
|
6012
|
+
else {
|
|
6013
|
+
params.append('tokenType', options.tokenType);
|
|
6014
|
+
}
|
|
6015
|
+
}
|
|
6016
|
+
// Transaction classification filters (arrays or single values)
|
|
6017
|
+
if (options.role) {
|
|
6018
|
+
if (Array.isArray(options.role)) {
|
|
6019
|
+
options.role.forEach(role => params.append('role', role));
|
|
6020
|
+
}
|
|
6021
|
+
else {
|
|
6022
|
+
params.append('role', options.role);
|
|
6023
|
+
}
|
|
6024
|
+
}
|
|
6025
|
+
if (options.status) {
|
|
6026
|
+
if (Array.isArray(options.status)) {
|
|
6027
|
+
options.status.forEach(status => params.append('status', status));
|
|
6028
|
+
}
|
|
6029
|
+
else {
|
|
6030
|
+
params.append('status', options.status);
|
|
6031
|
+
}
|
|
6032
|
+
}
|
|
6033
|
+
if (options.type) {
|
|
6034
|
+
if (Array.isArray(options.type)) {
|
|
6035
|
+
options.type.forEach(type => params.append('type', type));
|
|
6036
|
+
}
|
|
6037
|
+
else {
|
|
6038
|
+
params.append('type', options.type);
|
|
6039
|
+
}
|
|
6040
|
+
}
|
|
6041
|
+
// Include relations for entity enrichment
|
|
6042
|
+
if (options.include && options.include.length > 0) {
|
|
6043
|
+
options.include.forEach(relation => params.append('include', relation));
|
|
6044
|
+
}
|
|
6045
|
+
return params;
|
|
6046
|
+
}
|
|
5691
6047
|
/**
|
|
5692
6048
|
* AUTH: Get user transaction history with role-based filtering and pagination
|
|
5693
6049
|
*
|
|
5694
|
-
* UPDATED: Uses consolidated /transactions/me endpoint with
|
|
5695
|
-
*
|
|
6050
|
+
* UPDATED: Uses consolidated /transactions/me endpoint with all filter options
|
|
6051
|
+
* UPDATED: Added support for include parameter to enrich with sender/recipient/business entities
|
|
6052
|
+
* UPDATED: Now supports all TransactionFiltersDTO filters (triggerProcessId, status, type, etc.)
|
|
5696
6053
|
*
|
|
5697
|
-
* @param options - Pagination and
|
|
6054
|
+
* @param options - Pagination, filter, and include options
|
|
5698
6055
|
* @returns Paginated list of user's transactions
|
|
5699
6056
|
*
|
|
5700
6057
|
* @example
|
|
@@ -5702,24 +6059,30 @@ class TransactionApi {
|
|
|
5702
6059
|
* // Get first page of all transactions
|
|
5703
6060
|
* const page1 = await transactionApi.getUserTransactionHistory();
|
|
5704
6061
|
*
|
|
5705
|
-
* // Filter by role
|
|
5706
|
-
* const
|
|
6062
|
+
* // Filter by role and status
|
|
6063
|
+
* const completed = await transactionApi.getUserTransactionHistory({
|
|
5707
6064
|
* role: TransactionRole.SENDER,
|
|
6065
|
+
* status: TransactionStatus.COMPLETED,
|
|
5708
6066
|
* page: 1,
|
|
5709
6067
|
* limit: 50
|
|
5710
6068
|
* });
|
|
5711
6069
|
*
|
|
5712
|
-
* //
|
|
5713
|
-
*
|
|
6070
|
+
* // Filter by trigger process (e.g., from campaign claim or redemption)
|
|
6071
|
+
* const claimTransactions = await transactionApi.getUserTransactionHistory({
|
|
6072
|
+
* triggerProcessId: 'claim-abc123',
|
|
6073
|
+
* include: ['sender', 'recipient', 'business']
|
|
6074
|
+
* });
|
|
6075
|
+
*
|
|
6076
|
+
* // Access included entities
|
|
6077
|
+
* claimTransactions.data.forEach(tx => {
|
|
6078
|
+
* if (tx.included?.engagedBusiness) {
|
|
6079
|
+
* console.log('Business:', tx.included.engagedBusiness.displayName);
|
|
6080
|
+
* }
|
|
6081
|
+
* });
|
|
5714
6082
|
* ```
|
|
5715
6083
|
*/
|
|
5716
6084
|
async getUserTransactionHistory(options) {
|
|
5717
|
-
const params =
|
|
5718
|
-
// Add role parameter if specified
|
|
5719
|
-
if (options?.role) {
|
|
5720
|
-
params.set('role', options.role);
|
|
5721
|
-
}
|
|
5722
|
-
// Backend already returns PaginatedResponseDTO - return it directly
|
|
6085
|
+
const params = this.buildTransactionQueryParams(options);
|
|
5723
6086
|
return this.apiClient.get(`${this.basePath}/me?${params.toString()}`);
|
|
5724
6087
|
}
|
|
5725
6088
|
/**
|
|
@@ -5769,45 +6132,17 @@ class TransactionApi {
|
|
|
5769
6132
|
async prepareClientSignedTransaction(request) {
|
|
5770
6133
|
return this.apiClient.post(`${this.basePath}`, request);
|
|
5771
6134
|
}
|
|
5772
|
-
/**
|
|
5773
|
-
* ADMIN: Get all tenant transactions with pagination and filtering
|
|
5774
|
-
*
|
|
5775
|
-
* UPDATED: /transaction/admin → /transactions
|
|
5776
|
-
* FIXED: Now correctly returns paginated response (was incorrectly unwrapping to array)
|
|
5777
|
-
*
|
|
5778
|
-
* @param options - Pagination and filter options
|
|
5779
|
-
* @returns Paginated list of tenant transactions
|
|
5780
|
-
*
|
|
5781
|
-
* @example
|
|
5782
|
-
* ```typescript
|
|
5783
|
-
* // Get first page
|
|
5784
|
-
* const page1 = await transactionApi.getTenantTransactions();
|
|
5785
|
-
*
|
|
5786
|
-
* // Get specific page with custom limit
|
|
5787
|
-
* const page2 = await transactionApi.getTenantTransactions({
|
|
5788
|
-
* page: 2,
|
|
5789
|
-
* limit: 100
|
|
5790
|
-
* });
|
|
5791
|
-
*
|
|
5792
|
-
* // Access data
|
|
5793
|
-
* page1.data.forEach(tx => console.log(tx.id));
|
|
5794
|
-
* ```
|
|
5795
|
-
*
|
|
5796
|
-
* @deprecated Consider using getPaginatedTransactions() for more advanced filtering
|
|
5797
|
-
*/
|
|
5798
|
-
async getTenantTransactions(options) {
|
|
5799
|
-
const params = buildPaginationParams(options);
|
|
5800
|
-
// Backend already returns PaginatedResponseDTO - return it directly
|
|
5801
|
-
return this.apiClient.get(`${this.basePath}?${params.toString()}`);
|
|
5802
|
-
}
|
|
5803
6135
|
/**
|
|
5804
6136
|
* ADMIN: Get paginated transactions with filtering and sorting
|
|
5805
6137
|
*
|
|
5806
6138
|
* UPDATED: /transaction/admin → /transactions (same endpoint, better structure)
|
|
6139
|
+
*
|
|
6140
|
+
* @param options - Pagination request with filters and optional include relations
|
|
5807
6141
|
*/
|
|
5808
|
-
async getPaginatedTransactions(
|
|
5809
|
-
const queryString = this.buildQueryParams(
|
|
5810
|
-
|
|
6142
|
+
async getPaginatedTransactions(options) {
|
|
6143
|
+
const queryString = this.buildQueryParams(options).toString();
|
|
6144
|
+
const fullUrl = `${this.basePath}?${queryString}`;
|
|
6145
|
+
return this.apiClient.get(fullUrl);
|
|
5811
6146
|
}
|
|
5812
6147
|
/**
|
|
5813
6148
|
* ADMIN: Export transactions to CSV
|
|
@@ -5861,7 +6196,7 @@ class TransactionApi {
|
|
|
5861
6196
|
}
|
|
5862
6197
|
/**
|
|
5863
6198
|
* Helper to convert DTO object to URLSearchParams
|
|
5864
|
-
* Handles nested 'filters' object and
|
|
6199
|
+
* Handles nested 'filters' object, arrays, and include parameter correctly.
|
|
5865
6200
|
*/
|
|
5866
6201
|
buildQueryParams(params) {
|
|
5867
6202
|
const query = new URLSearchParams();
|
|
@@ -5874,7 +6209,11 @@ class TransactionApi {
|
|
|
5874
6209
|
query.append('sortBy', params.sortBy);
|
|
5875
6210
|
if (params.sortOrder)
|
|
5876
6211
|
query.append('sortOrder', params.sortOrder);
|
|
5877
|
-
// 2. Handle
|
|
6212
|
+
// 2. Handle Include Relations (root level)
|
|
6213
|
+
if (params.include && params.include.length > 0) {
|
|
6214
|
+
params.include.forEach((relation) => query.append('include', relation));
|
|
6215
|
+
}
|
|
6216
|
+
// 3. Handle Nested Filters
|
|
5878
6217
|
if (params.filters) {
|
|
5879
6218
|
Object.entries(params.filters).forEach(([key, value]) => {
|
|
5880
6219
|
// Skip undefined/null values
|
|
@@ -5912,9 +6251,12 @@ class TransactionService {
|
|
|
5912
6251
|
}
|
|
5913
6252
|
/**
|
|
5914
6253
|
* Get transaction by ID
|
|
6254
|
+
*
|
|
6255
|
+
* @param transactionId - Transaction ID
|
|
6256
|
+
* @param include - Optional relations to include (sender, recipient, business)
|
|
5915
6257
|
*/
|
|
5916
|
-
async getTransactionById(transactionId) {
|
|
5917
|
-
return this.transactionApi.getTransactionById(transactionId);
|
|
6258
|
+
async getTransactionById(transactionId, include) {
|
|
6259
|
+
return this.transactionApi.getTransactionById(transactionId, include);
|
|
5918
6260
|
}
|
|
5919
6261
|
// ==========================================
|
|
5920
6262
|
// AUTHENTICATED OPERATIONS
|
|
@@ -5932,14 +6274,13 @@ class TransactionService {
|
|
|
5932
6274
|
return this.transactionApi.submitSignedTransaction(signedTxData);
|
|
5933
6275
|
}
|
|
5934
6276
|
/**
|
|
5935
|
-
* AUTH: Get user transaction history
|
|
6277
|
+
* AUTH: Get user transaction history with comprehensive filtering
|
|
5936
6278
|
*
|
|
5937
|
-
* @param
|
|
5938
|
-
* @param options - Pagination options
|
|
6279
|
+
* @param options - Query options including filters, pagination, and include relations
|
|
5939
6280
|
* @returns Paginated transaction history
|
|
5940
6281
|
*/
|
|
5941
|
-
async getUserTransactionHistory(
|
|
5942
|
-
return this.transactionApi.getUserTransactionHistory(
|
|
6282
|
+
async getUserTransactionHistory(options) {
|
|
6283
|
+
return this.transactionApi.getUserTransactionHistory(options);
|
|
5943
6284
|
}
|
|
5944
6285
|
/**
|
|
5945
6286
|
* AUTH: Prepare existing transaction for client-side signing
|
|
@@ -5971,20 +6312,11 @@ class TransactionService {
|
|
|
5971
6312
|
/* async createAdminTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
|
|
5972
6313
|
return this.transactionApi.createAdminTransaction(request);
|
|
5973
6314
|
} */
|
|
5974
|
-
/**
|
|
5975
|
-
* ADMIN: Get all tenant transactions with pagination
|
|
5976
|
-
*
|
|
5977
|
-
* @param options - Pagination options
|
|
5978
|
-
* @returns Paginated tenant transactions
|
|
5979
|
-
*/
|
|
5980
|
-
async getTenantTransactions(options) {
|
|
5981
|
-
return this.transactionApi.getTenantTransactions(options || {});
|
|
5982
|
-
}
|
|
5983
6315
|
/**
|
|
5984
6316
|
* ADMIN: Get paginated transactions with filtering and sorting
|
|
5985
6317
|
*/
|
|
5986
|
-
async getPaginatedTransactions(
|
|
5987
|
-
return this.transactionApi.getPaginatedTransactions(
|
|
6318
|
+
async getPaginatedTransactions(options) {
|
|
6319
|
+
return this.transactionApi.getPaginatedTransactions(options);
|
|
5988
6320
|
}
|
|
5989
6321
|
/**
|
|
5990
6322
|
* ADMIN: Export transactions to CSV
|
|
@@ -6739,7 +7071,191 @@ class AnalyticsApi {
|
|
|
6739
7071
|
* ADMIN: Get transaction analytics with filtering and aggregation
|
|
6740
7072
|
*/
|
|
6741
7073
|
async getTransactionAnalytics(request) {
|
|
6742
|
-
return this.apiClient.post('/transactions
|
|
7074
|
+
return this.apiClient.post('/analytics/transactions', request);
|
|
7075
|
+
}
|
|
7076
|
+
/**
|
|
7077
|
+
* ADMIN: Get campaign claim analytics with aggregation (charts, metrics, grouping)
|
|
7078
|
+
*
|
|
7079
|
+
* This endpoint is for aggregated analytics only (groupBy/metrics).
|
|
7080
|
+
* For enriched list data with nested objects, use campaign.getClaims() instead.
|
|
7081
|
+
*
|
|
7082
|
+
* @example Claims per campaign
|
|
7083
|
+
* ```typescript
|
|
7084
|
+
* const response = await analyticsApi.getCampaignClaimAnalytics({
|
|
7085
|
+
* filters: { status: 'COMPLETED' },
|
|
7086
|
+
* groupBy: ['campaignId'],
|
|
7087
|
+
* metrics: ['count'],
|
|
7088
|
+
* sortBy: 'count',
|
|
7089
|
+
* sortOrder: SortOrder.DESC,
|
|
7090
|
+
* limit: 10
|
|
7091
|
+
* });
|
|
7092
|
+
* ```
|
|
7093
|
+
*
|
|
7094
|
+
* @example Claims over time by status
|
|
7095
|
+
* ```typescript
|
|
7096
|
+
* const response = await analyticsApi.getCampaignClaimAnalytics({
|
|
7097
|
+
* groupBy: ['month', 'status'],
|
|
7098
|
+
* metrics: ['count'],
|
|
7099
|
+
* sortBy: 'month',
|
|
7100
|
+
* sortOrder: SortOrder.DESC,
|
|
7101
|
+
* startDate: new Date('2026-01-01'),
|
|
7102
|
+
* endDate: new Date('2026-12-31')
|
|
7103
|
+
* });
|
|
7104
|
+
* ```
|
|
7105
|
+
*/
|
|
7106
|
+
async getCampaignClaimAnalytics(request) {
|
|
7107
|
+
return this.apiClient.post('/analytics/campaign-claims', request);
|
|
7108
|
+
}
|
|
7109
|
+
/**
|
|
7110
|
+
* ADMIN: Get user analytics with engagement metrics
|
|
7111
|
+
*
|
|
7112
|
+
* Returns aggregated user statistics including engagement rate, active users,
|
|
7113
|
+
* transaction metrics, and per-active-user averages (more accurate than all-user averages).
|
|
7114
|
+
*
|
|
7115
|
+
* Request structure matches TransactionAnalytics and CampaignClaimAnalytics for consistency:
|
|
7116
|
+
* - filters object for business-specific scoping
|
|
7117
|
+
* - startDate/endDate at root level for date range filtering
|
|
7118
|
+
*
|
|
7119
|
+
* @param request - Analytics request with optional filters and date range
|
|
7120
|
+
* @returns Aggregated user metrics with per-user and per-active-user averages
|
|
7121
|
+
*
|
|
7122
|
+
* @example Basic user analytics (all time, all businesses)
|
|
7123
|
+
* ```typescript
|
|
7124
|
+
* const analytics = await analyticsApi.getUserAnalytics({});
|
|
7125
|
+
* console.log(`Total users: ${analytics.totalUsers}`);
|
|
7126
|
+
* console.log(`Active users: ${analytics.activeUsers}`);
|
|
7127
|
+
* console.log(`Engagement rate: ${analytics.engagementRate}%`);
|
|
7128
|
+
*
|
|
7129
|
+
* // Per-user averages (includes inactive users)
|
|
7130
|
+
* console.log(`Avg transactions per user: ${analytics.averageTransactionsPerUser}`);
|
|
7131
|
+
*
|
|
7132
|
+
* // Per-active-user averages (only engaged users - more useful!)
|
|
7133
|
+
* console.log(`Avg transactions per active user: ${analytics.averageTransactionsPerActiveUser}`);
|
|
7134
|
+
* ```
|
|
7135
|
+
*
|
|
7136
|
+
* @example Filtered by date range
|
|
7137
|
+
* ```typescript
|
|
7138
|
+
* const analytics = await analyticsApi.getUserAnalytics({
|
|
7139
|
+
* startDate: new Date('2026-01-01'),
|
|
7140
|
+
* endDate: new Date('2026-01-31')
|
|
7141
|
+
* });
|
|
7142
|
+
* console.log(`January metrics:`);
|
|
7143
|
+
* console.log(`Active users: ${analytics.activeUsers}`);
|
|
7144
|
+
* console.log(`New users: ${analytics.newUsers}`);
|
|
7145
|
+
* console.log(`Date range applied: ${analytics.metadata.dateRange?.startDate} - ${analytics.metadata.dateRange?.endDate}`);
|
|
7146
|
+
* ```
|
|
7147
|
+
*
|
|
7148
|
+
* @example Business-specific analytics with filters
|
|
7149
|
+
* ```typescript
|
|
7150
|
+
* const analytics = await analyticsApi.getUserAnalytics({
|
|
7151
|
+
* filters: { businessId: 'biz-123' },
|
|
7152
|
+
* startDate: new Date('2026-01-01'),
|
|
7153
|
+
* endDate: new Date('2026-12-31')
|
|
7154
|
+
* });
|
|
7155
|
+
* console.log(`Business customer engagement in 2026:`);
|
|
7156
|
+
* console.log(`Active customers: ${analytics.activeUsers}`);
|
|
7157
|
+
* console.log(`Avg claims per active customer: ${analytics.averageClaimsPerActiveUser.toFixed(2)}`);
|
|
7158
|
+
* console.log(`Customer engagement rate: ${analytics.engagementRate.toFixed(1)}%`);
|
|
7159
|
+
* ```
|
|
7160
|
+
*/
|
|
7161
|
+
async getUserAnalytics(request = {}) {
|
|
7162
|
+
return this.apiClient.post('/analytics/users', request);
|
|
7163
|
+
}
|
|
7164
|
+
/**
|
|
7165
|
+
* ADMIN: Get user transaction ranking with enriched user data
|
|
7166
|
+
*
|
|
7167
|
+
* Returns ranked list of users with full user details and transaction metrics.
|
|
7168
|
+
* Data enrichment happens via efficient SQL JOINs + UNION (handles legacy transactions).
|
|
7169
|
+
*
|
|
7170
|
+
* Use Cases:
|
|
7171
|
+
* - Admin leaderboards showing top users by activity
|
|
7172
|
+
* - User engagement analysis with full user context
|
|
7173
|
+
* - Identifying power users for campaigns
|
|
7174
|
+
*
|
|
7175
|
+
* @param request - Ranking request with filters, sorting, and limit
|
|
7176
|
+
* @returns Ranked list with user details (email, externalUserId) and transaction metrics
|
|
7177
|
+
*
|
|
7178
|
+
* @example Top 50 users by transaction count
|
|
7179
|
+
* ```typescript
|
|
7180
|
+
* const ranking = await analyticsApi.getUserRanking({
|
|
7181
|
+
* sortBy: 'totalTransactions',
|
|
7182
|
+
* sortOrder: SortOrder.DESC,
|
|
7183
|
+
* limit: 50
|
|
7184
|
+
* });
|
|
7185
|
+
*
|
|
7186
|
+
* ranking.results.forEach((user, index) => {
|
|
7187
|
+
* console.log(`#${index + 1}: ${user.email || user.externalUserId} - ${user.totalTransactions} transactions`);
|
|
7188
|
+
* });
|
|
7189
|
+
* ```
|
|
7190
|
+
*
|
|
7191
|
+
* @example Top users by STAMP spending
|
|
7192
|
+
* ```typescript
|
|
7193
|
+
* const ranking = await analyticsApi.getUserRanking({
|
|
7194
|
+
* filters: { tokenType: 'STAMP' },
|
|
7195
|
+
* sortBy: 'tokenSpent',
|
|
7196
|
+
* sortOrder: SortOrder.DESC,
|
|
7197
|
+
* limit: 20
|
|
7198
|
+
* });
|
|
7199
|
+
* ```
|
|
7200
|
+
*
|
|
7201
|
+
* @example Business-specific leaderboard with date range
|
|
7202
|
+
* ```typescript
|
|
7203
|
+
* const ranking = await analyticsApi.getUserRanking({
|
|
7204
|
+
* filters: {
|
|
7205
|
+
* businessId: 'business-uuid-here',
|
|
7206
|
+
* tokenType: 'CREDIT'
|
|
7207
|
+
* },
|
|
7208
|
+
* sortBy: 'totalTransactions',
|
|
7209
|
+
* sortOrder: SortOrder.DESC,
|
|
7210
|
+
* limit: 100,
|
|
7211
|
+
* startDate: new Date('2026-01-01'),
|
|
7212
|
+
* endDate: new Date('2026-12-31')
|
|
7213
|
+
* });
|
|
7214
|
+
* console.log(`Top ${ranking.totalUsers} users for business in 2026`);
|
|
7215
|
+
* ```
|
|
7216
|
+
*/
|
|
7217
|
+
async getUserRanking(request = {}) {
|
|
7218
|
+
return this.apiClient.post('/analytics/users/ranking', request);
|
|
7219
|
+
}
|
|
7220
|
+
/**
|
|
7221
|
+
* ADMIN: Get business transaction ranking with enriched business data
|
|
7222
|
+
*
|
|
7223
|
+
* Returns ranked list of businesses with full business details and transaction metrics.
|
|
7224
|
+
* Data enrichment happens via efficient SQL JOINs + UNION (handles legacy transactions).
|
|
7225
|
+
*
|
|
7226
|
+
* @param request - Ranking request with filters, sorting, and limit
|
|
7227
|
+
* @returns Ranked list with business details and transaction metrics
|
|
7228
|
+
*
|
|
7229
|
+
* @example Top 50 businesses by transaction count
|
|
7230
|
+
* ```typescript
|
|
7231
|
+
* const ranking = await analyticsApi.getBusinessRanking({
|
|
7232
|
+
* sortBy: 'totalTransactions',
|
|
7233
|
+
* sortOrder: SortOrder.DESC,
|
|
7234
|
+
* limit: 50
|
|
7235
|
+
* });
|
|
7236
|
+
* ```
|
|
7237
|
+
*/
|
|
7238
|
+
async getBusinessRanking(request = {}) {
|
|
7239
|
+
return this.apiClient.post('/analytics/businesses/ranking', request);
|
|
7240
|
+
}
|
|
7241
|
+
/**
|
|
7242
|
+
* ADMIN: Get monthly user retention analytics
|
|
7243
|
+
*
|
|
7244
|
+
* Returns monthly retention data with active/new/returning users and retention rates.
|
|
7245
|
+
* Replaces 13 separate API calls with 1 efficient recursive CTE query.
|
|
7246
|
+
*
|
|
7247
|
+
* @param request - Retention analytics request with monthsBack and filters
|
|
7248
|
+
* @returns Monthly retention data with user metrics and retention rates
|
|
7249
|
+
*
|
|
7250
|
+
* @example Last 13 months retention
|
|
7251
|
+
* ```typescript
|
|
7252
|
+
* const retention = await analyticsApi.getRetentionAnalytics({
|
|
7253
|
+
* monthsBack: 13
|
|
7254
|
+
* });
|
|
7255
|
+
* ```
|
|
7256
|
+
*/
|
|
7257
|
+
async getRetentionAnalytics(request = {}) {
|
|
7258
|
+
return this.apiClient.post('/analytics/users/retention', request);
|
|
6743
7259
|
}
|
|
6744
7260
|
}
|
|
6745
7261
|
|
|
@@ -6764,6 +7280,47 @@ class AnalyticsService {
|
|
|
6764
7280
|
async getTransactionAnalytics(request) {
|
|
6765
7281
|
return this.analyticsApi.getTransactionAnalytics(request);
|
|
6766
7282
|
}
|
|
7283
|
+
/**
|
|
7284
|
+
* ADMIN: Get campaign claim analytics with aggregation
|
|
7285
|
+
*/
|
|
7286
|
+
async getCampaignClaimAnalytics(request) {
|
|
7287
|
+
return this.analyticsApi.getCampaignClaimAnalytics(request);
|
|
7288
|
+
}
|
|
7289
|
+
/**
|
|
7290
|
+
* ADMIN: Get user analytics with engagement metrics
|
|
7291
|
+
*
|
|
7292
|
+
* Returns aggregated user statistics including engagement rate, active users,
|
|
7293
|
+
* transaction metrics, and claims/redemption averages.
|
|
7294
|
+
*/
|
|
7295
|
+
async getUserAnalytics(request = {}) {
|
|
7296
|
+
return this.analyticsApi.getUserAnalytics(request);
|
|
7297
|
+
}
|
|
7298
|
+
/**
|
|
7299
|
+
* ADMIN: Get user transaction ranking with enriched user data
|
|
7300
|
+
*
|
|
7301
|
+
* Returns ranked list of users with full user details (email, externalUserId)
|
|
7302
|
+
* and transaction metrics. Efficient data enrichment via SQL JOINs + UNION.
|
|
7303
|
+
*/
|
|
7304
|
+
async getUserRanking(request = {}) {
|
|
7305
|
+
return this.analyticsApi.getUserRanking(request);
|
|
7306
|
+
}
|
|
7307
|
+
/**
|
|
7308
|
+
* ADMIN: Get business transaction ranking with enriched business data
|
|
7309
|
+
*
|
|
7310
|
+
* Returns ranked list of businesses with full business details and transaction metrics.
|
|
7311
|
+
*/
|
|
7312
|
+
async getBusinessRanking(request = {}) {
|
|
7313
|
+
return this.analyticsApi.getBusinessRanking(request);
|
|
7314
|
+
}
|
|
7315
|
+
/**
|
|
7316
|
+
* ADMIN: Get monthly user retention analytics
|
|
7317
|
+
*
|
|
7318
|
+
* Returns monthly retention data with user metrics and retention rates.
|
|
7319
|
+
* Replaces 13 separate API calls with 1 efficient query.
|
|
7320
|
+
*/
|
|
7321
|
+
async getRetentionAnalytics(request = {}) {
|
|
7322
|
+
return this.analyticsApi.getRetentionAnalytics(request);
|
|
7323
|
+
}
|
|
6767
7324
|
}
|
|
6768
7325
|
|
|
6769
7326
|
/**
|
|
@@ -6813,6 +7370,144 @@ class DonationService {
|
|
|
6813
7370
|
}
|
|
6814
7371
|
}
|
|
6815
7372
|
|
|
7373
|
+
/**
|
|
7374
|
+
* Platform-Agnostic TriggerSource API Client
|
|
7375
|
+
*
|
|
7376
|
+
* Handles all trigger source operations:
|
|
7377
|
+
* - CRUD operations for trigger sources (QR codes, NFC tags, GPS geofences, webhooks, etc.)
|
|
7378
|
+
* - Trigger sources are independent entities that can be assigned to campaigns
|
|
7379
|
+
*
|
|
7380
|
+
* Uses @explorins/pers-shared DTOs for consistency with backend.
|
|
7381
|
+
*/
|
|
7382
|
+
class TriggerSourceApi {
|
|
7383
|
+
constructor(apiClient) {
|
|
7384
|
+
this.apiClient = apiClient;
|
|
7385
|
+
}
|
|
7386
|
+
/**
|
|
7387
|
+
* PUBLIC: Get trigger sources with optional filters and pagination
|
|
7388
|
+
*
|
|
7389
|
+
* @param options - Filter and pagination options
|
|
7390
|
+
* @returns Paginated list of trigger sources
|
|
7391
|
+
*
|
|
7392
|
+
* @example
|
|
7393
|
+
* ```typescript
|
|
7394
|
+
* // Get all QR code trigger sources
|
|
7395
|
+
* const qrSources = await triggerSourceApi.getTriggerSources({ type: 'QR_CODE' });
|
|
7396
|
+
*
|
|
7397
|
+
* // Get trigger sources for a specific campaign
|
|
7398
|
+
* const campaignSources = await triggerSourceApi.getTriggerSources({
|
|
7399
|
+
* campaignId: 'campaign-123',
|
|
7400
|
+
* page: 1,
|
|
7401
|
+
* limit: 20
|
|
7402
|
+
* });
|
|
7403
|
+
* ```
|
|
7404
|
+
*/
|
|
7405
|
+
async getTriggerSources(options) {
|
|
7406
|
+
const params = buildPaginationParams(options);
|
|
7407
|
+
if (options?.type)
|
|
7408
|
+
params.set('type', options.type);
|
|
7409
|
+
if (options?.businessId)
|
|
7410
|
+
params.set('businessId', options.businessId);
|
|
7411
|
+
if (options?.campaignId)
|
|
7412
|
+
params.set('campaignId', options.campaignId);
|
|
7413
|
+
if (options?.active !== undefined)
|
|
7414
|
+
params.set('active', String(options.active));
|
|
7415
|
+
const response = await this.apiClient.get(`/trigger-sources?${params.toString()}`);
|
|
7416
|
+
return normalizeToPaginated(response);
|
|
7417
|
+
}
|
|
7418
|
+
/**
|
|
7419
|
+
* PUBLIC: Get trigger source by ID
|
|
7420
|
+
*
|
|
7421
|
+
* @param triggerSourceId - UUID of the trigger source
|
|
7422
|
+
* @returns Trigger source details
|
|
7423
|
+
*/
|
|
7424
|
+
async getTriggerSourceById(triggerSourceId) {
|
|
7425
|
+
return this.apiClient.get(`/trigger-sources/${triggerSourceId}`);
|
|
7426
|
+
}
|
|
7427
|
+
/**
|
|
7428
|
+
* ADMIN: Create a new trigger source
|
|
7429
|
+
*
|
|
7430
|
+
* @param triggerSource - Trigger source creation data
|
|
7431
|
+
* @returns Created trigger source
|
|
7432
|
+
*
|
|
7433
|
+
* @example
|
|
7434
|
+
* ```typescript
|
|
7435
|
+
* const qrSource = await triggerSourceApi.createTriggerSource({
|
|
7436
|
+
* type: 'QR_CODE',
|
|
7437
|
+
* name: 'Store Entrance QR',
|
|
7438
|
+
* description: 'QR code at main entrance',
|
|
7439
|
+
* businessId: 'business-123',
|
|
7440
|
+
* coordsLatitude: 47.6062,
|
|
7441
|
+
* coordsLongitude: -122.3321
|
|
7442
|
+
* });
|
|
7443
|
+
* ```
|
|
7444
|
+
*/
|
|
7445
|
+
async createTriggerSource(triggerSource) {
|
|
7446
|
+
return this.apiClient.post('/trigger-sources', triggerSource);
|
|
7447
|
+
}
|
|
7448
|
+
/**
|
|
7449
|
+
* ADMIN: Update a trigger source
|
|
7450
|
+
*
|
|
7451
|
+
* @param triggerSourceId - UUID of the trigger source to update
|
|
7452
|
+
* @param triggerSource - Updated trigger source data (partial)
|
|
7453
|
+
* @returns Updated trigger source
|
|
7454
|
+
*/
|
|
7455
|
+
async updateTriggerSource(triggerSourceId, triggerSource) {
|
|
7456
|
+
return this.apiClient.put(`/trigger-sources/${triggerSourceId}`, triggerSource);
|
|
7457
|
+
}
|
|
7458
|
+
/**
|
|
7459
|
+
* ADMIN: Delete (soft delete) a trigger source
|
|
7460
|
+
*
|
|
7461
|
+
* @param triggerSourceId - UUID of the trigger source to delete
|
|
7462
|
+
* @returns Success status
|
|
7463
|
+
*/
|
|
7464
|
+
async deleteTriggerSource(triggerSourceId) {
|
|
7465
|
+
return this.apiClient.delete(`/trigger-sources/${triggerSourceId}`);
|
|
7466
|
+
}
|
|
7467
|
+
}
|
|
7468
|
+
|
|
7469
|
+
/**
|
|
7470
|
+
* Platform-Agnostic TriggerSource Service
|
|
7471
|
+
*
|
|
7472
|
+
* Contains trigger source business logic and operations that work across platforms.
|
|
7473
|
+
* No framework dependencies - pure TypeScript business logic.
|
|
7474
|
+
*/
|
|
7475
|
+
class TriggerSourceService {
|
|
7476
|
+
constructor(triggerSourceApi) {
|
|
7477
|
+
this.triggerSourceApi = triggerSourceApi;
|
|
7478
|
+
}
|
|
7479
|
+
/**
|
|
7480
|
+
* Get trigger sources with optional filters
|
|
7481
|
+
*/
|
|
7482
|
+
async getTriggerSources(options) {
|
|
7483
|
+
return this.triggerSourceApi.getTriggerSources(options);
|
|
7484
|
+
}
|
|
7485
|
+
/**
|
|
7486
|
+
* Get trigger source by ID
|
|
7487
|
+
*/
|
|
7488
|
+
async getTriggerSourceById(triggerSourceId) {
|
|
7489
|
+
return this.triggerSourceApi.getTriggerSourceById(triggerSourceId);
|
|
7490
|
+
}
|
|
7491
|
+
/**
|
|
7492
|
+
* ADMIN: Create a new trigger source
|
|
7493
|
+
*/
|
|
7494
|
+
async createTriggerSource(triggerSource) {
|
|
7495
|
+
return this.triggerSourceApi.createTriggerSource(triggerSource);
|
|
7496
|
+
}
|
|
7497
|
+
/**
|
|
7498
|
+
* ADMIN: Update a trigger source
|
|
7499
|
+
*/
|
|
7500
|
+
async updateTriggerSource(triggerSourceId, triggerSource) {
|
|
7501
|
+
return this.triggerSourceApi.updateTriggerSource(triggerSourceId, triggerSource);
|
|
7502
|
+
}
|
|
7503
|
+
/**
|
|
7504
|
+
* ADMIN: Delete a trigger source
|
|
7505
|
+
*/
|
|
7506
|
+
async deleteTriggerSource(triggerSourceId) {
|
|
7507
|
+
return this.triggerSourceApi.deleteTriggerSource(triggerSourceId);
|
|
7508
|
+
}
|
|
7509
|
+
}
|
|
7510
|
+
|
|
6816
7511
|
/**
|
|
6817
7512
|
* PERS SDK Configuration interfaces and utilities
|
|
6818
7513
|
*
|
|
@@ -6828,8 +7523,7 @@ const DEFAULT_PERS_CONFIG = {
|
|
|
6828
7523
|
timeout: 30000,
|
|
6829
7524
|
retries: 3,
|
|
6830
7525
|
tokenRefreshMargin: 60, // Refresh tokens 60 seconds before expiry
|
|
6831
|
-
backgroundRefreshThreshold: 30
|
|
6832
|
-
autoRestoreSession: true // Automatically restore session on initialization
|
|
7526
|
+
backgroundRefreshThreshold: 30 // Use background refresh if >30s remaining
|
|
6833
7527
|
};
|
|
6834
7528
|
/**
|
|
6835
7529
|
* Internal function to construct API root from environment
|
|
@@ -6897,10 +7591,7 @@ class AuthApi {
|
|
|
6897
7591
|
* Authenticates a user in a business context with role included in JWT.
|
|
6898
7592
|
*
|
|
6899
7593
|
* @param jwt - Authentication token (passkey or Firebase JWT)
|
|
6900
|
-
* @param options - Business authentication options
|
|
6901
|
-
* @param options.businessId - The business ID to authenticate as
|
|
6902
|
-
* - If user has single business membership, auto-selected
|
|
6903
|
-
* - If user has multiple memberships without businessId, throws MULTIPLE_CONTEXT_SELECTION_REQUIRED
|
|
7594
|
+
* @param options - Business authentication options (businessId for multi-business users, auto-selected if single membership)
|
|
6904
7595
|
* @returns Session response with business context and role in JWT
|
|
6905
7596
|
* @throws MultipleContextSelectionError when businessId is required but not provided
|
|
6906
7597
|
*
|
|
@@ -7019,10 +7710,7 @@ class AuthService {
|
|
|
7019
7710
|
* the user's role within that business.
|
|
7020
7711
|
*
|
|
7021
7712
|
* @param jwt - Authentication token (passkey or Firebase JWT)
|
|
7022
|
-
* @param options - Business authentication options
|
|
7023
|
-
* @param options.businessId - The business ID to authenticate as
|
|
7024
|
-
* - If user has single business membership, auto-selected
|
|
7025
|
-
* - If user has multiple memberships without businessId, throws MULTIPLE_CONTEXT_SELECTION_REQUIRED
|
|
7713
|
+
* @param options - Business authentication options (businessId for multi-business users)
|
|
7026
7714
|
* @returns Session response with business context and role baked into JWT
|
|
7027
7715
|
* @throws MultipleContextSelectionRequiredError when businessId is required but not provided
|
|
7028
7716
|
*
|
|
@@ -7139,8 +7827,12 @@ class AuthService {
|
|
|
7139
7827
|
await extendedProvider.setProviderToken(providerToken);
|
|
7140
7828
|
}
|
|
7141
7829
|
if (authType && extendedProvider.setAuthType) {
|
|
7830
|
+
console.log('[AuthService] Storing auth type:', authType);
|
|
7142
7831
|
await extendedProvider.setAuthType(authType);
|
|
7143
7832
|
}
|
|
7833
|
+
else {
|
|
7834
|
+
console.warn('[AuthService] Auth type not stored - authType:', authType, 'has setAuthType:', !!extendedProvider.setAuthType);
|
|
7835
|
+
}
|
|
7144
7836
|
// Emit authenticated status on successful token storage
|
|
7145
7837
|
await this.emitAuthStatus(AuthStatus.AUTHENTICATED);
|
|
7146
7838
|
}
|
|
@@ -7159,8 +7851,15 @@ class TokenRefreshManager {
|
|
|
7159
7851
|
this.authService = authService;
|
|
7160
7852
|
this.authProvider = authProvider;
|
|
7161
7853
|
this.tokenRefreshMarginSeconds = 120; // 2 minutes
|
|
7854
|
+
this.lastValidationTime = 0;
|
|
7855
|
+
this.validationCacheDurationMs = 30000; // 30 seconds
|
|
7162
7856
|
}
|
|
7163
7857
|
async ensureValidToken() {
|
|
7858
|
+
const now = Date.now();
|
|
7859
|
+
// Skip validation if we checked recently (within cache duration)
|
|
7860
|
+
if (now - this.lastValidationTime < this.validationCacheDurationMs) {
|
|
7861
|
+
return;
|
|
7862
|
+
}
|
|
7164
7863
|
try {
|
|
7165
7864
|
const token = await this.authProvider.getToken();
|
|
7166
7865
|
if (!token) {
|
|
@@ -7171,6 +7870,14 @@ class TokenRefreshManager {
|
|
|
7171
7870
|
if (!refreshSuccess) {
|
|
7172
7871
|
await this.authService.handleAuthFailure();
|
|
7173
7872
|
}
|
|
7873
|
+
else {
|
|
7874
|
+
// Update validation time on successful refresh
|
|
7875
|
+
this.lastValidationTime = now;
|
|
7876
|
+
}
|
|
7877
|
+
}
|
|
7878
|
+
else {
|
|
7879
|
+
// Token is valid, update validation time
|
|
7880
|
+
this.lastValidationTime = now;
|
|
7174
7881
|
}
|
|
7175
7882
|
}
|
|
7176
7883
|
catch (error) {
|
|
@@ -7254,42 +7961,77 @@ class MemoryTokenStorage {
|
|
|
7254
7961
|
}
|
|
7255
7962
|
}
|
|
7256
7963
|
/**
|
|
7257
|
-
* Token manager for authentication tokens
|
|
7964
|
+
* Token manager for authentication tokens with memory caching
|
|
7258
7965
|
*/
|
|
7259
7966
|
class AuthTokenManager {
|
|
7260
7967
|
constructor(storage = new LocalStorageTokenStorage()) {
|
|
7261
7968
|
this.storage = storage;
|
|
7969
|
+
this.cache = {};
|
|
7262
7970
|
}
|
|
7263
7971
|
async getAccessToken() {
|
|
7972
|
+
// Return cached value if available
|
|
7973
|
+
if (this.cache.accessToken !== undefined) {
|
|
7974
|
+
return this.cache.accessToken;
|
|
7975
|
+
}
|
|
7264
7976
|
const val = await this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
|
|
7265
7977
|
// Ensure we return string for tokens
|
|
7266
|
-
|
|
7978
|
+
const token = typeof val === 'string' ? val : null;
|
|
7979
|
+
this.cache.accessToken = token;
|
|
7980
|
+
return token;
|
|
7267
7981
|
}
|
|
7268
7982
|
async setAccessToken(token) {
|
|
7983
|
+
this.cache.accessToken = token;
|
|
7269
7984
|
await this.storage.set(AUTH_STORAGE_KEYS.ACCESS_TOKEN, token);
|
|
7270
7985
|
}
|
|
7271
7986
|
async getRefreshToken() {
|
|
7987
|
+
// Return cached value if available
|
|
7988
|
+
if (this.cache.refreshToken !== undefined) {
|
|
7989
|
+
return this.cache.refreshToken;
|
|
7990
|
+
}
|
|
7272
7991
|
const val = await this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
|
|
7273
|
-
|
|
7992
|
+
const token = typeof val === 'string' ? val : null;
|
|
7993
|
+
this.cache.refreshToken = token;
|
|
7994
|
+
return token;
|
|
7274
7995
|
}
|
|
7275
7996
|
async setRefreshToken(token) {
|
|
7997
|
+
this.cache.refreshToken = token;
|
|
7276
7998
|
await this.storage.set(AUTH_STORAGE_KEYS.REFRESH_TOKEN, token);
|
|
7277
7999
|
}
|
|
7278
8000
|
async getProviderToken() {
|
|
8001
|
+
// Return cached value if available
|
|
8002
|
+
if (this.cache.providerToken !== undefined) {
|
|
8003
|
+
return this.cache.providerToken;
|
|
8004
|
+
}
|
|
7279
8005
|
const val = await this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
|
|
7280
|
-
|
|
8006
|
+
const token = typeof val === 'string' ? val : null;
|
|
8007
|
+
this.cache.providerToken = token;
|
|
8008
|
+
return token;
|
|
7281
8009
|
}
|
|
7282
8010
|
async setProviderToken(token) {
|
|
8011
|
+
this.cache.providerToken = token;
|
|
7283
8012
|
await this.storage.set(AUTH_STORAGE_KEYS.PROVIDER_TOKEN, token);
|
|
7284
8013
|
}
|
|
7285
8014
|
async getAuthType() {
|
|
8015
|
+
// Return cached value if available
|
|
8016
|
+
if (this.cache.authType !== undefined) {
|
|
8017
|
+
return this.cache.authType;
|
|
8018
|
+
}
|
|
7286
8019
|
const authType = await this.storage.get(AUTH_STORAGE_KEYS.AUTH_TYPE);
|
|
7287
|
-
|
|
8020
|
+
const type = typeof authType === 'string' ? authType : null;
|
|
8021
|
+
this.cache.authType = type;
|
|
8022
|
+
return type;
|
|
7288
8023
|
}
|
|
7289
8024
|
async setAuthType(authType) {
|
|
8025
|
+
this.cache.authType = authType;
|
|
7290
8026
|
await this.storage.set(AUTH_STORAGE_KEYS.AUTH_TYPE, authType);
|
|
7291
8027
|
}
|
|
7292
8028
|
async setTokens(accessToken, refreshToken, providerToken) {
|
|
8029
|
+
// Update cache for all tokens
|
|
8030
|
+
this.cache.accessToken = accessToken;
|
|
8031
|
+
if (refreshToken)
|
|
8032
|
+
this.cache.refreshToken = refreshToken;
|
|
8033
|
+
if (providerToken)
|
|
8034
|
+
this.cache.providerToken = providerToken;
|
|
7293
8035
|
await this.setAccessToken(accessToken);
|
|
7294
8036
|
if (refreshToken)
|
|
7295
8037
|
await this.setRefreshToken(refreshToken);
|
|
@@ -7297,13 +8039,23 @@ class AuthTokenManager {
|
|
|
7297
8039
|
await this.setProviderToken(providerToken);
|
|
7298
8040
|
}
|
|
7299
8041
|
async clearAllTokens() {
|
|
8042
|
+
// Clear cache
|
|
8043
|
+
this.cache = {};
|
|
7300
8044
|
await this.storage.clear();
|
|
7301
8045
|
}
|
|
7302
8046
|
async hasAccessToken() {
|
|
8047
|
+
// Use cached value if available to avoid storage read
|
|
8048
|
+
if (this.cache.accessToken !== undefined) {
|
|
8049
|
+
return !!this.cache.accessToken;
|
|
8050
|
+
}
|
|
7303
8051
|
const token = await this.getAccessToken();
|
|
7304
8052
|
return !!token;
|
|
7305
8053
|
}
|
|
7306
8054
|
async hasRefreshToken() {
|
|
8055
|
+
// Use cached value if available to avoid storage read
|
|
8056
|
+
if (this.cache.refreshToken !== undefined) {
|
|
8057
|
+
return !!this.cache.refreshToken;
|
|
8058
|
+
}
|
|
7307
8059
|
const token = await this.getRefreshToken();
|
|
7308
8060
|
return !!token;
|
|
7309
8061
|
}
|
|
@@ -7531,6 +8283,7 @@ class DPoPManager {
|
|
|
7531
8283
|
class DefaultAuthProvider {
|
|
7532
8284
|
constructor(config = {}) {
|
|
7533
8285
|
this.config = config;
|
|
8286
|
+
this.authTypeCache = null;
|
|
7534
8287
|
this.authType = config.authType || exports.AccountOwnerType.USER;
|
|
7535
8288
|
const storage = config.storage || this.createStorage();
|
|
7536
8289
|
this.tokenManager = new AuthTokenManager(storage);
|
|
@@ -7591,6 +8344,15 @@ class DefaultAuthProvider {
|
|
|
7591
8344
|
}
|
|
7592
8345
|
async setAccessToken(token) {
|
|
7593
8346
|
await this.tokenManager.setAccessToken(token);
|
|
8347
|
+
// Invalidate cache when token changes
|
|
8348
|
+
this.authTypeCache = null;
|
|
8349
|
+
// Eager DPoP key generation after login
|
|
8350
|
+
if (this.dpopManager) {
|
|
8351
|
+
// Fire and forget - generate keys in background
|
|
8352
|
+
this.dpopManager.ensureKeyPair().catch(err => {
|
|
8353
|
+
console.warn('[DefaultAuthProvider] DPoP key generation failed:', err);
|
|
8354
|
+
});
|
|
8355
|
+
}
|
|
7594
8356
|
}
|
|
7595
8357
|
async setRefreshToken(token) {
|
|
7596
8358
|
await this.tokenManager.setRefreshToken(token);
|
|
@@ -7600,16 +8362,51 @@ class DefaultAuthProvider {
|
|
|
7600
8362
|
}
|
|
7601
8363
|
async clearTokens() {
|
|
7602
8364
|
await this.tokenManager.clearAllTokens();
|
|
8365
|
+
// Invalidate cache on clear
|
|
8366
|
+
this.authTypeCache = null;
|
|
7603
8367
|
if (this.dpopManager) {
|
|
7604
8368
|
await this.dpopManager.clearKeys();
|
|
7605
8369
|
}
|
|
7606
8370
|
}
|
|
7607
8371
|
async setTokens(accessToken, refreshToken, providerToken) {
|
|
7608
8372
|
await this.tokenManager.setTokens(accessToken, refreshToken, providerToken);
|
|
8373
|
+
// Invalidate cache when tokens change
|
|
8374
|
+
this.authTypeCache = null;
|
|
7609
8375
|
}
|
|
7610
8376
|
async hasValidToken() {
|
|
7611
8377
|
return this.tokenManager.hasAccessToken();
|
|
7612
8378
|
}
|
|
8379
|
+
async getAuthType() {
|
|
8380
|
+
// Read from JWT instead of separate storage for single source of truth
|
|
8381
|
+
const token = await this.tokenManager.getAccessToken();
|
|
8382
|
+
if (!token) {
|
|
8383
|
+
this.authTypeCache = null;
|
|
8384
|
+
return null;
|
|
8385
|
+
}
|
|
8386
|
+
// Return cached result if token hasn't changed
|
|
8387
|
+
if (this.authTypeCache && this.authTypeCache.token === token) {
|
|
8388
|
+
return this.authTypeCache.type;
|
|
8389
|
+
}
|
|
8390
|
+
try {
|
|
8391
|
+
// Decode JWT payload (without verification - we just need to read the claim)
|
|
8392
|
+
const parts = token.split('.');
|
|
8393
|
+
if (parts.length !== 3) {
|
|
8394
|
+
console.warn('[DefaultAuthProvider] Invalid JWT format');
|
|
8395
|
+
return null;
|
|
8396
|
+
}
|
|
8397
|
+
const payloadJson = atob(parts[1]);
|
|
8398
|
+
const payload = JSON.parse(payloadJson);
|
|
8399
|
+
// Use accountOwnerType from AuthJWTPayload interface
|
|
8400
|
+
const authType = payload.accountType || null;
|
|
8401
|
+
// Cache result with token signature
|
|
8402
|
+
this.authTypeCache = { token, type: authType };
|
|
8403
|
+
return authType;
|
|
8404
|
+
}
|
|
8405
|
+
catch (error) {
|
|
8406
|
+
console.warn('[DefaultAuthProvider] Failed to parse auth type from JWT:', error);
|
|
8407
|
+
return null;
|
|
8408
|
+
}
|
|
8409
|
+
}
|
|
7613
8410
|
}
|
|
7614
8411
|
|
|
7615
8412
|
/**
|
|
@@ -7804,13 +8601,15 @@ class PersApiClient {
|
|
|
7804
8601
|
* @internal
|
|
7805
8602
|
*/
|
|
7806
8603
|
emitErrorEvent(errorDetails, errorMessage, endpoint, method, status, error) {
|
|
7807
|
-
console.log('[PersApiClient] emitErrorEvent called', {
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
8604
|
+
/* console.log('[PersApiClient] emitErrorEvent called', {
|
|
8605
|
+
hasEvents: !!this._events,
|
|
8606
|
+
instanceId: this._events?.instanceId ?? 'none',
|
|
8607
|
+
subscriberCount: this._events?.subscriberCount ?? 0,
|
|
8608
|
+
domain: errorDetails.domain,
|
|
8609
|
+
code: errorDetails.code,
|
|
8610
|
+
status: status,
|
|
8611
|
+
message: errorDetails.message || errorMessage
|
|
8612
|
+
}); */
|
|
7814
8613
|
this._events?.emitError({
|
|
7815
8614
|
domain: errorDetails.domain || 'external',
|
|
7816
8615
|
type: errorDetails.code || 'API_ERROR',
|
|
@@ -8243,8 +9042,7 @@ class AuthManager {
|
|
|
8243
9042
|
* throws `MULTIPLE_CONTEXT_SELECTION_REQUIRED` error with available options
|
|
8244
9043
|
*
|
|
8245
9044
|
* @param jwtToken - JWT token from auth provider (passkey, Firebase, etc.)
|
|
8246
|
-
* @param options - Business authentication options
|
|
8247
|
-
* @param options.businessId - The business ID to authenticate as (required for multi-business users)
|
|
9045
|
+
* @param options - Business authentication options (businessId for multi-business users)
|
|
8248
9046
|
* @returns Promise resolving to authentication response with business context and role in JWT
|
|
8249
9047
|
* @throws Error with code `MULTIPLE_CONTEXT_SELECTION_REQUIRED` when businessId is needed
|
|
8250
9048
|
*
|
|
@@ -8671,26 +9469,49 @@ class UserManager {
|
|
|
8671
9469
|
* requires administrator privileges and returns full user profiles including
|
|
8672
9470
|
* private information.
|
|
8673
9471
|
*
|
|
8674
|
-
* @
|
|
9472
|
+
* @param options - Pagination options (page, limit, sortBy, sortOrder)
|
|
9473
|
+
* @param search - Optional search query to filter users
|
|
9474
|
+
* @returns Promise resolving to paginated users with complete data
|
|
8675
9475
|
* @throws {PersApiError} When not authenticated as admin
|
|
8676
9476
|
*
|
|
8677
|
-
* @example
|
|
9477
|
+
* @example Basic Usage
|
|
8678
9478
|
* ```typescript
|
|
8679
9479
|
* // Admin operation - requires admin authentication
|
|
8680
9480
|
* try {
|
|
8681
|
-
* const
|
|
8682
|
-
* console.log(`Total users: ${
|
|
9481
|
+
* const result = await sdk.users.getAllUsers();
|
|
9482
|
+
* console.log(`Total users: ${result.pagination.total}`);
|
|
8683
9483
|
*
|
|
8684
|
-
*
|
|
8685
|
-
* console.log(`${user.
|
|
9484
|
+
* result.data.forEach(user => {
|
|
9485
|
+
* console.log(`${user.firstName} - ${user.email} - Active: ${user.isActive}`);
|
|
8686
9486
|
* });
|
|
8687
9487
|
* } catch (error) {
|
|
8688
9488
|
* console.log('Admin access required');
|
|
8689
9489
|
* }
|
|
8690
9490
|
* ```
|
|
9491
|
+
*
|
|
9492
|
+
* @example With Sorting
|
|
9493
|
+
* ```typescript
|
|
9494
|
+
* import { SortOrder } from '@explorins/pers-sdk';
|
|
9495
|
+
*
|
|
9496
|
+
* // Get users sorted by email ascending
|
|
9497
|
+
* const result = await sdk.users.getAllUsers({
|
|
9498
|
+
* page: 1,
|
|
9499
|
+
* limit: 20,
|
|
9500
|
+
* sortBy: 'email',
|
|
9501
|
+
* sortOrder: SortOrder.ASC
|
|
9502
|
+
* });
|
|
9503
|
+
*
|
|
9504
|
+
* // Get users sorted by creation date (newest first)
|
|
9505
|
+
* const recent = await sdk.users.getAllUsers({
|
|
9506
|
+
* page: 1,
|
|
9507
|
+
* limit: 10,
|
|
9508
|
+
* sortBy: 'createdAt',
|
|
9509
|
+
* sortOrder: SortOrder.DESC
|
|
9510
|
+
* });
|
|
9511
|
+
* ```
|
|
8691
9512
|
*/
|
|
8692
|
-
async getAllUsers(options) {
|
|
8693
|
-
return this.userService.getAllRemoteUsers(options);
|
|
9513
|
+
async getAllUsers(options, search) {
|
|
9514
|
+
return this.userService.getAllRemoteUsers(options, search);
|
|
8694
9515
|
}
|
|
8695
9516
|
/**
|
|
8696
9517
|
* Business/Admin: Create or update a user
|
|
@@ -9855,6 +10676,7 @@ class CampaignManager {
|
|
|
9855
10676
|
* business partnerships, eligibility criteria, and claiming requirements.
|
|
9856
10677
|
*
|
|
9857
10678
|
* @param campaignId - Unique campaign identifier
|
|
10679
|
+
* @param include - Relations to include: 'triggerSources', 'businesses'
|
|
9858
10680
|
* @returns Promise resolving to campaign data with complete details
|
|
9859
10681
|
* @throws {PersApiError} When campaign with specified ID is not found
|
|
9860
10682
|
*
|
|
@@ -9883,9 +10705,17 @@ class CampaignManager {
|
|
|
9883
10705
|
* console.log('Campaign not found:', error.message);
|
|
9884
10706
|
* }
|
|
9885
10707
|
* ```
|
|
10708
|
+
*
|
|
10709
|
+
* @example With Include Relations
|
|
10710
|
+
* ```typescript
|
|
10711
|
+
* // Get campaign with trigger sources and businesses included
|
|
10712
|
+
* const campaign = await sdk.campaigns.getCampaignById('campaign-123', ['triggerSources', 'businesses']);
|
|
10713
|
+
* console.log('Trigger sources:', campaign.included?.triggerSources);
|
|
10714
|
+
* console.log('Businesses:', campaign.included?.businesses);
|
|
10715
|
+
* ```
|
|
9886
10716
|
*/
|
|
9887
|
-
async getCampaignById(campaignId) {
|
|
9888
|
-
return this.campaignService.getCampaignById(campaignId);
|
|
10717
|
+
async getCampaignById(campaignId, include) {
|
|
10718
|
+
return this.campaignService.getCampaignById(campaignId, include);
|
|
9889
10719
|
}
|
|
9890
10720
|
/**
|
|
9891
10721
|
* Claim a campaign reward
|
|
@@ -9991,7 +10821,8 @@ class CampaignManager {
|
|
|
9991
10821
|
* ```
|
|
9992
10822
|
*/
|
|
9993
10823
|
async getUserClaims(options) {
|
|
9994
|
-
|
|
10824
|
+
const { include, ...paginationOptions } = options || {};
|
|
10825
|
+
return this.campaignService.getClaimsForLoggedUser(paginationOptions, include);
|
|
9995
10826
|
}
|
|
9996
10827
|
/**
|
|
9997
10828
|
* Get campaigns with pagination support
|
|
@@ -9999,13 +10830,7 @@ class CampaignManager {
|
|
|
9999
10830
|
* Returns campaigns with pagination metadata for efficient data loading.
|
|
10000
10831
|
* Intelligent access: Public gets active only, Business gets own campaigns, Admin gets all.
|
|
10001
10832
|
*
|
|
10002
|
-
* @param options - Pagination and filter options
|
|
10003
|
-
* @param options.active - Filter by active status (true/false/undefined for all)
|
|
10004
|
-
* @param options.businessId - Filter by business engagement
|
|
10005
|
-
* @param options.page - Page number (1-based, default: 1)
|
|
10006
|
-
* @param options.limit - Items per page (default: 50)
|
|
10007
|
-
* @param options.sortBy - Sort field ('name', 'createdAt', 'startDate')
|
|
10008
|
-
* @param options.sortOrder - Sort direction ('ASC' or 'DESC')
|
|
10833
|
+
* @param options - Pagination and filter options (page, limit, sortBy, sortOrder, active, businessId, include)
|
|
10009
10834
|
* @returns Promise resolving to paginated campaigns with metadata
|
|
10010
10835
|
*
|
|
10011
10836
|
* @example
|
|
@@ -10030,6 +10855,11 @@ class CampaignManager {
|
|
|
10030
10855
|
* page: 1,
|
|
10031
10856
|
* limit: 25
|
|
10032
10857
|
* });
|
|
10858
|
+
*
|
|
10859
|
+
* // Include related data (trigger sources and businesses)
|
|
10860
|
+
* const campaignsWithRelations = await sdk.campaigns.getCampaigns({
|
|
10861
|
+
* include: ['triggerSources', 'businesses']
|
|
10862
|
+
* });
|
|
10033
10863
|
* ```
|
|
10034
10864
|
*/
|
|
10035
10865
|
async getCampaigns(options) {
|
|
@@ -10147,57 +10977,188 @@ class CampaignManager {
|
|
|
10147
10977
|
async toggleCampaignTestnet(campaignId) {
|
|
10148
10978
|
return this.campaignService.toggleCampaignTestnet(campaignId);
|
|
10149
10979
|
}
|
|
10980
|
+
// ==========================================
|
|
10981
|
+
// CAMPAIGN TRIGGER OPERATIONS
|
|
10982
|
+
// Rules & limits for campaign activation
|
|
10983
|
+
// ==========================================
|
|
10150
10984
|
/**
|
|
10151
|
-
* Admin: Get campaign triggers
|
|
10985
|
+
* Admin: Get all campaign triggers (paginated)
|
|
10152
10986
|
*
|
|
10153
|
-
* Retrieves all
|
|
10154
|
-
*
|
|
10155
|
-
* Requires administrator privileges.
|
|
10987
|
+
* Retrieves all campaign trigger rules that define rate limits, geo-validation,
|
|
10988
|
+
* conditions, and trigger types for campaign activation.
|
|
10156
10989
|
*
|
|
10157
|
-
* @
|
|
10990
|
+
* @param options - Pagination options
|
|
10991
|
+
* @returns Promise resolving to paginated list of campaign triggers
|
|
10158
10992
|
*
|
|
10159
10993
|
* @example
|
|
10160
10994
|
* ```typescript
|
|
10161
|
-
*
|
|
10162
|
-
*
|
|
10163
|
-
*
|
|
10164
|
-
*
|
|
10165
|
-
* triggers.forEach(trigger => {
|
|
10166
|
-
* console.log(`- ${trigger.name}: ${trigger.description}`);
|
|
10167
|
-
* console.log(` Type: ${trigger.type}`);
|
|
10168
|
-
* console.log(` Parameters: ${JSON.stringify(trigger.parameters)}`);
|
|
10995
|
+
* const triggers = await sdk.campaigns.getCampaignTriggers({ page: 1, limit: 20 });
|
|
10996
|
+
* console.log(`Found ${triggers.total} triggers`);
|
|
10997
|
+
* triggers.data.forEach(trigger => {
|
|
10998
|
+
* console.log(`- ${trigger.name}: max ${trigger.maxPerDayPerUser}/day`);
|
|
10169
10999
|
* });
|
|
10170
11000
|
* ```
|
|
10171
11001
|
*/
|
|
10172
11002
|
async getCampaignTriggers(options) {
|
|
10173
|
-
return this.campaignService.getCampaignTriggers();
|
|
11003
|
+
return this.campaignService.getCampaignTriggers(options);
|
|
11004
|
+
}
|
|
11005
|
+
/**
|
|
11006
|
+
* Admin: Get campaign trigger by ID
|
|
11007
|
+
*
|
|
11008
|
+
* @param triggerId - The campaign trigger ID
|
|
11009
|
+
* @returns Promise resolving to the campaign trigger
|
|
11010
|
+
*
|
|
11011
|
+
* @example
|
|
11012
|
+
* ```typescript
|
|
11013
|
+
* const trigger = await sdk.campaigns.getCampaignTriggerById('trigger-123');
|
|
11014
|
+
* console.log('Max per day:', trigger.maxPerDayPerUser);
|
|
11015
|
+
* ```
|
|
11016
|
+
*/
|
|
11017
|
+
async getCampaignTriggerById(triggerId) {
|
|
11018
|
+
return this.campaignService.getCampaignTriggerById(triggerId);
|
|
10174
11019
|
}
|
|
10175
11020
|
/**
|
|
10176
|
-
* Admin:
|
|
11021
|
+
* Admin: Create a new campaign trigger
|
|
11022
|
+
*
|
|
11023
|
+
* Creates a trigger rule that defines rate limits, geo-validation, and conditions
|
|
11024
|
+
* for campaign activation. Triggers are created independently and then assigned
|
|
11025
|
+
* to campaigns via `setCampaignTrigger()`.
|
|
10177
11026
|
*
|
|
10178
|
-
*
|
|
10179
|
-
*
|
|
11027
|
+
* @param data - Trigger configuration
|
|
11028
|
+
* @returns Promise resolving to created trigger
|
|
11029
|
+
*
|
|
11030
|
+
* @example
|
|
11031
|
+
* ```typescript
|
|
11032
|
+
* const trigger = await sdk.campaigns.createCampaignTrigger({
|
|
11033
|
+
* name: 'Daily Check-in',
|
|
11034
|
+
* maxPerDayPerUser: 1,
|
|
11035
|
+
* maxPerUser: 100,
|
|
11036
|
+
* minCooldownSeconds: 3600,
|
|
11037
|
+
* maxGeoDistanceInMeters: 50,
|
|
11038
|
+
* triggerType: 'CLAIM_BY_USER'
|
|
11039
|
+
* });
|
|
11040
|
+
*
|
|
11041
|
+
* // Then assign to campaign
|
|
11042
|
+
* await sdk.campaigns.setCampaignTrigger(campaignId, trigger.id);
|
|
11043
|
+
* ```
|
|
11044
|
+
*/
|
|
11045
|
+
async createCampaignTrigger(data) {
|
|
11046
|
+
const result = await this.campaignService.createCampaignTrigger(data);
|
|
11047
|
+
this.events?.emitSuccess({
|
|
11048
|
+
domain: 'campaign',
|
|
11049
|
+
type: 'CAMPAIGN_TRIGGER_CREATED',
|
|
11050
|
+
userMessage: `Trigger "${data.name}" created successfully`,
|
|
11051
|
+
details: { triggerId: result.id, name: data.name }
|
|
11052
|
+
});
|
|
11053
|
+
return result;
|
|
11054
|
+
}
|
|
11055
|
+
/**
|
|
11056
|
+
* Admin: Update an existing campaign trigger
|
|
11057
|
+
*
|
|
11058
|
+
* @param triggerId - The campaign trigger ID
|
|
11059
|
+
* @param data - Updated trigger configuration (partial)
|
|
11060
|
+
* @returns Promise resolving to updated trigger
|
|
11061
|
+
*
|
|
11062
|
+
* @example
|
|
11063
|
+
* ```typescript
|
|
11064
|
+
* const updated = await sdk.campaigns.updateCampaignTrigger('trigger-123', {
|
|
11065
|
+
* maxPerDayPerUser: 5,
|
|
11066
|
+
* minCooldownSeconds: 1800
|
|
11067
|
+
* });
|
|
11068
|
+
* ```
|
|
11069
|
+
*/
|
|
11070
|
+
async updateCampaignTrigger(triggerId, data) {
|
|
11071
|
+
const result = await this.campaignService.updateCampaignTrigger(triggerId, data);
|
|
11072
|
+
this.events?.emitSuccess({
|
|
11073
|
+
domain: 'campaign',
|
|
11074
|
+
type: 'CAMPAIGN_TRIGGER_UPDATED',
|
|
11075
|
+
userMessage: 'Trigger updated successfully',
|
|
11076
|
+
details: { triggerId, updates: Object.keys(data) }
|
|
11077
|
+
});
|
|
11078
|
+
return result;
|
|
11079
|
+
}
|
|
11080
|
+
/**
|
|
11081
|
+
* Admin: Delete a campaign trigger
|
|
11082
|
+
*
|
|
11083
|
+
* @param triggerId - The campaign trigger ID
|
|
11084
|
+
* @returns Promise resolving to success status
|
|
11085
|
+
*
|
|
11086
|
+
* @example
|
|
11087
|
+
* ```typescript
|
|
11088
|
+
* await sdk.campaigns.deleteCampaignTrigger('trigger-123');
|
|
11089
|
+
* ```
|
|
11090
|
+
*/
|
|
11091
|
+
async deleteCampaignTrigger(triggerId) {
|
|
11092
|
+
const result = await this.campaignService.deleteCampaignTrigger(triggerId);
|
|
11093
|
+
this.events?.emitSuccess({
|
|
11094
|
+
domain: 'campaign',
|
|
11095
|
+
type: 'CAMPAIGN_TRIGGER_DELETED',
|
|
11096
|
+
userMessage: 'Trigger deleted successfully',
|
|
11097
|
+
details: { triggerId }
|
|
11098
|
+
});
|
|
11099
|
+
return result;
|
|
11100
|
+
}
|
|
11101
|
+
/**
|
|
11102
|
+
* Admin: Assign a trigger to a campaign
|
|
11103
|
+
*
|
|
11104
|
+
* Associates a trigger rule with a campaign. A campaign can have only one trigger
|
|
11105
|
+
* at a time. Assigning a new trigger replaces any existing trigger.
|
|
10180
11106
|
*
|
|
10181
11107
|
* @param campaignId - ID of the campaign
|
|
10182
11108
|
* @param triggerId - ID of the trigger to associate
|
|
10183
|
-
* @returns Promise resolving to updated campaign
|
|
10184
|
-
* @throws {PersApiError} When not authenticated as admin or entities not found
|
|
11109
|
+
* @returns Promise resolving to updated campaign
|
|
10185
11110
|
*
|
|
10186
11111
|
* @example
|
|
10187
11112
|
* ```typescript
|
|
10188
|
-
* // Admin operation - set up automatic campaign trigger
|
|
10189
11113
|
* const updated = await sdk.campaigns.setCampaignTrigger(
|
|
10190
|
-
* '
|
|
10191
|
-
* '
|
|
11114
|
+
* 'campaign-123',
|
|
11115
|
+
* 'trigger-456'
|
|
10192
11116
|
* );
|
|
10193
|
-
*
|
|
10194
|
-
* console.log('Trigger set for campaign:', updated.title);
|
|
10195
|
-
* console.log('Trigger will activate on user registration');
|
|
11117
|
+
* console.log('Trigger assigned:', updated.trigger?.name);
|
|
10196
11118
|
* ```
|
|
10197
11119
|
*/
|
|
10198
11120
|
async setCampaignTrigger(campaignId, triggerId) {
|
|
10199
|
-
|
|
11121
|
+
const result = await this.campaignService.setCampaignTrigger(campaignId, triggerId);
|
|
11122
|
+
this.events?.emitSuccess({
|
|
11123
|
+
domain: 'campaign',
|
|
11124
|
+
type: 'CAMPAIGN_TRIGGER_ASSIGNED',
|
|
11125
|
+
userMessage: 'Trigger assigned to campaign',
|
|
11126
|
+
details: { campaignId, triggerId }
|
|
11127
|
+
});
|
|
11128
|
+
return result;
|
|
11129
|
+
}
|
|
11130
|
+
/**
|
|
11131
|
+
* Admin: Remove a trigger from a campaign
|
|
11132
|
+
*
|
|
11133
|
+
* Removes the trigger rule from a campaign. The trigger itself is not deleted
|
|
11134
|
+
* and can be reassigned to other campaigns.
|
|
11135
|
+
*
|
|
11136
|
+
* @param campaignId - ID of the campaign
|
|
11137
|
+
* @param triggerId - ID of the trigger to remove
|
|
11138
|
+
* @returns Promise resolving to updated campaign
|
|
11139
|
+
*
|
|
11140
|
+
* @example
|
|
11141
|
+
* ```typescript
|
|
11142
|
+
* const updated = await sdk.campaigns.removeCampaignTrigger(
|
|
11143
|
+
* 'campaign-123',
|
|
11144
|
+
* 'trigger-456'
|
|
11145
|
+
* );
|
|
11146
|
+
* console.log('Trigger removed:', updated.trigger === null);
|
|
11147
|
+
* ```
|
|
11148
|
+
*/
|
|
11149
|
+
async removeCampaignTrigger(campaignId, triggerId) {
|
|
11150
|
+
const result = await this.campaignService.removeCampaignTrigger(campaignId, triggerId);
|
|
11151
|
+
this.events?.emitSuccess({
|
|
11152
|
+
domain: 'campaign',
|
|
11153
|
+
type: 'CAMPAIGN_TRIGGER_REMOVED',
|
|
11154
|
+
userMessage: 'Trigger removed from campaign',
|
|
11155
|
+
details: { campaignId, triggerId }
|
|
11156
|
+
});
|
|
11157
|
+
return result;
|
|
10200
11158
|
}
|
|
11159
|
+
// ==========================================
|
|
11160
|
+
// TOKEN UNIT OPERATIONS
|
|
11161
|
+
// ==========================================
|
|
10201
11162
|
/**
|
|
10202
11163
|
* Admin: Create campaign token unit
|
|
10203
11164
|
*
|
|
@@ -10406,8 +11367,8 @@ class CampaignManager {
|
|
|
10406
11367
|
* });
|
|
10407
11368
|
* ```
|
|
10408
11369
|
*/
|
|
10409
|
-
async getCampaignClaims(filters) {
|
|
10410
|
-
return this.campaignService.getCampaignClaims(filters);
|
|
11370
|
+
async getCampaignClaims(filters, include) {
|
|
11371
|
+
return this.campaignService.getCampaignClaims(filters, include);
|
|
10411
11372
|
}
|
|
10412
11373
|
/**
|
|
10413
11374
|
* Admin: Get campaign claims by user ID
|
|
@@ -10442,8 +11403,8 @@ class CampaignManager {
|
|
|
10442
11403
|
* console.log(`\nTotal rewards earned: ${totalRewards}`);
|
|
10443
11404
|
* ```
|
|
10444
11405
|
*/
|
|
10445
|
-
async getCampaignClaimsByUserId(userId, options) {
|
|
10446
|
-
return this.campaignService.getCampaignClaimsByUserId(userId);
|
|
11406
|
+
async getCampaignClaimsByUserId(userId, options, include) {
|
|
11407
|
+
return this.campaignService.getCampaignClaimsByUserId(userId, options, include);
|
|
10447
11408
|
}
|
|
10448
11409
|
/**
|
|
10449
11410
|
* Admin: Get campaign claims by business ID
|
|
@@ -10483,8 +11444,79 @@ class CampaignManager {
|
|
|
10483
11444
|
* console.log(`\nTotal rewards distributed: ${totalRewardsDistributed}`);
|
|
10484
11445
|
* ```
|
|
10485
11446
|
*/
|
|
10486
|
-
async getCampaignClaimsByBusinessId(businessId, options) {
|
|
10487
|
-
return this.campaignService.getCampaignClaimsByBusinessId(businessId);
|
|
11447
|
+
async getCampaignClaimsByBusinessId(businessId, options, include) {
|
|
11448
|
+
return this.campaignService.getCampaignClaimsByBusinessId(businessId, options, include);
|
|
11449
|
+
}
|
|
11450
|
+
// ==========================================
|
|
11451
|
+
// TRIGGER SOURCE ASSIGNMENT
|
|
11452
|
+
// Note: TriggerSource CRUD is in TriggerSourceManager (sdk.triggerSources)
|
|
11453
|
+
// ==========================================
|
|
11454
|
+
/**
|
|
11455
|
+
* Admin: Assign a trigger source to a campaign
|
|
11456
|
+
*
|
|
11457
|
+
* Associates a trigger source with a campaign, enabling the trigger source
|
|
11458
|
+
* to activate campaign rewards when triggered. A campaign can have multiple
|
|
11459
|
+
* trigger sources. Requires administrator privileges.
|
|
11460
|
+
*
|
|
11461
|
+
* Note: To create/update/delete trigger sources, use `sdk.triggerSources`.
|
|
11462
|
+
*
|
|
11463
|
+
* @param campaignId - Campaign UUID
|
|
11464
|
+
* @param triggerSourceId - Trigger source UUID
|
|
11465
|
+
* @returns Promise resolving to updated campaign with trigger source assigned
|
|
11466
|
+
* @throws {PersApiError} When not authenticated as admin or entities not found
|
|
11467
|
+
*
|
|
11468
|
+
* @example
|
|
11469
|
+
* ```typescript
|
|
11470
|
+
* // Create trigger source first
|
|
11471
|
+
* const source = await sdk.triggerSources.create({
|
|
11472
|
+
* type: 'QR_CODE',
|
|
11473
|
+
* name: 'Store Entrance QR'
|
|
11474
|
+
* });
|
|
11475
|
+
*
|
|
11476
|
+
* // Then assign to campaign
|
|
11477
|
+
* const updated = await sdk.campaigns.assignTriggerSource(
|
|
11478
|
+
* 'campaign-123',
|
|
11479
|
+
* source.id
|
|
11480
|
+
* );
|
|
11481
|
+
*
|
|
11482
|
+
* console.log('Trigger source assigned:', updated.triggerSourceIds.length);
|
|
11483
|
+
* ```
|
|
11484
|
+
*/
|
|
11485
|
+
async assignTriggerSource(campaignId, triggerSourceId) {
|
|
11486
|
+
const result = await this.campaignService.assignTriggerSourceToCampaign(campaignId, triggerSourceId);
|
|
11487
|
+
this.events?.emitSuccess({
|
|
11488
|
+
domain: 'campaign',
|
|
11489
|
+
type: 'TRIGGER_SOURCE_ASSIGNED',
|
|
11490
|
+
userMessage: 'Trigger source assigned to campaign',
|
|
11491
|
+
details: { campaignId, triggerSourceId }
|
|
11492
|
+
});
|
|
11493
|
+
return result;
|
|
11494
|
+
}
|
|
11495
|
+
/**
|
|
11496
|
+
* Admin: Remove a trigger source from a campaign
|
|
11497
|
+
*
|
|
11498
|
+
* Removes the association between a trigger source and a campaign. The trigger
|
|
11499
|
+
* source itself is not deleted and can be reassigned to other campaigns.
|
|
11500
|
+
* Requires administrator privileges.
|
|
11501
|
+
*
|
|
11502
|
+
* @param campaignId - Campaign UUID
|
|
11503
|
+
* @param triggerSourceId - Trigger source UUID
|
|
11504
|
+
* @returns Promise resolving to updated campaign with trigger source removed
|
|
11505
|
+
* @throws {PersApiError} When not authenticated as admin or entities not found
|
|
11506
|
+
*
|
|
11507
|
+
* @example
|
|
11508
|
+
* ```typescript
|
|
11509
|
+
* // Remove a trigger source from a campaign
|
|
11510
|
+
* const updated = await sdk.campaigns.removeTriggerSource(
|
|
11511
|
+
* 'campaign-123',
|
|
11512
|
+
* 'source-456'
|
|
11513
|
+
* );
|
|
11514
|
+
*
|
|
11515
|
+
* console.log('Remaining trigger source IDs:', updated.triggerSourceIds.length);
|
|
11516
|
+
* ```
|
|
11517
|
+
*/
|
|
11518
|
+
async removeTriggerSource(campaignId, triggerSourceId) {
|
|
11519
|
+
return this.campaignService.removeTriggerSourceFromCampaign(campaignId, triggerSourceId);
|
|
10488
11520
|
}
|
|
10489
11521
|
/**
|
|
10490
11522
|
* Get the full campaign service for advanced operations
|
|
@@ -10868,8 +11900,57 @@ class RedemptionManager {
|
|
|
10868
11900
|
* });
|
|
10869
11901
|
* ```
|
|
10870
11902
|
*/
|
|
10871
|
-
async getUserRedemptions(options) {
|
|
10872
|
-
return this.redemptionService.getUserRedeems(options);
|
|
11903
|
+
async getUserRedemptions(options, include) {
|
|
11904
|
+
return this.redemptionService.getUserRedeems(options, include);
|
|
11905
|
+
}
|
|
11906
|
+
/**
|
|
11907
|
+
* Admin: Get all redemption redeems with filtering and enriched data
|
|
11908
|
+
*
|
|
11909
|
+
* Retrieves all redemption redeems across the platform with filtering capabilities.
|
|
11910
|
+
* This is an admin-level operation that allows monitoring and analytics of redemption
|
|
11911
|
+
* activity. Supports filtering by user, redemption offer, and enrichment of related entities.
|
|
11912
|
+
*
|
|
11913
|
+
* @param filters - Filter options (userId, redemptionId, pagination)
|
|
11914
|
+
* @param include - Optional relations to include for enrichment
|
|
11915
|
+
* @returns Promise resolving to paginated list of redemption redeems
|
|
11916
|
+
* @throws {PersApiError} When not authenticated as admin
|
|
11917
|
+
*
|
|
11918
|
+
* @example Get All Redeems
|
|
11919
|
+
* ```typescript
|
|
11920
|
+
* // Admin operation - get all redemption redeems
|
|
11921
|
+
* const { data: allRedeems, pagination } = await sdk.redemptions.getRedemptionRedeems();
|
|
11922
|
+
*
|
|
11923
|
+
* console.log(`Total redeems: ${pagination.total}`);
|
|
11924
|
+
* console.log(`Page ${pagination.page} of ${pagination.pages}`);
|
|
11925
|
+
* ```
|
|
11926
|
+
*
|
|
11927
|
+
* @example Filter by User
|
|
11928
|
+
* ```typescript
|
|
11929
|
+
* // Get redeems for specific user
|
|
11930
|
+
* const { data: userRedeems } = await sdk.redemptions.getRedemptionRedeems({
|
|
11931
|
+
* userId: 'user-123',
|
|
11932
|
+
* limit: 50
|
|
11933
|
+
* });
|
|
11934
|
+
*
|
|
11935
|
+
* console.log(`User has ${userRedeems.length} redemptions`);
|
|
11936
|
+
* ```
|
|
11937
|
+
*
|
|
11938
|
+
* @example With Enriched Data
|
|
11939
|
+
* ```typescript
|
|
11940
|
+
* const { data: redeems } = await sdk.redemptions.getRedemptionRedeems(
|
|
11941
|
+
* { page: 1, limit: 20 },
|
|
11942
|
+
* ['redemption', 'user', 'business']
|
|
11943
|
+
* );
|
|
11944
|
+
*
|
|
11945
|
+
* redeems.forEach(redeem => {
|
|
11946
|
+
* const redemptionName = redeem.included?.redemption?.name || 'Unknown';
|
|
11947
|
+
* const userEmail = redeem.included?.user?.email || redeem.userId;
|
|
11948
|
+
* console.log(`${redemptionName} - ${userEmail}`);
|
|
11949
|
+
* });
|
|
11950
|
+
* ```
|
|
11951
|
+
*/
|
|
11952
|
+
async getRedemptionRedeems(filters, include) {
|
|
11953
|
+
return this.redemptionService.getRedemptionRedeems(filters, include);
|
|
10873
11954
|
}
|
|
10874
11955
|
/**
|
|
10875
11956
|
* Admin: Create new redemption offer
|
|
@@ -11112,8 +12193,8 @@ class RedemptionManager {
|
|
|
11112
12193
|
*
|
|
11113
12194
|
* @example Administrative Reporting
|
|
11114
12195
|
* ```typescript
|
|
11115
|
-
* // Admin: Get
|
|
11116
|
-
* const allTransactions = await sdk.transactions.
|
|
12196
|
+
* // Admin: Get paginated transactions for analysis
|
|
12197
|
+
* const allTransactions = await sdk.transactions.getPaginatedTransactions({ page: 1, limit: 100 });
|
|
11117
12198
|
*
|
|
11118
12199
|
* // Export transaction data
|
|
11119
12200
|
* const csvBlob = await sdk.transactions.exportTransactionsCSV();
|
|
@@ -11136,13 +12217,18 @@ class TransactionManager {
|
|
|
11136
12217
|
* Provides complete transaction audit trail and verification data.
|
|
11137
12218
|
*
|
|
11138
12219
|
* @param transactionId - Unique transaction identifier
|
|
12220
|
+
* @param include - Optional relations to include (sender, recipient, business) for enriched entity data
|
|
11139
12221
|
* @returns Promise resolving to complete transaction data
|
|
11140
12222
|
* @throws {PersApiError} When transaction with specified ID is not found or access denied
|
|
11141
12223
|
*
|
|
11142
12224
|
* @example
|
|
11143
12225
|
* ```typescript
|
|
11144
12226
|
* try {
|
|
11145
|
-
*
|
|
12227
|
+
* // Get transaction with enriched sender/recipient data
|
|
12228
|
+
* const transaction = await sdk.transactions.getTransactionById(
|
|
12229
|
+
* 'txn-abc123',
|
|
12230
|
+
* ['sender', 'recipient', 'business']
|
|
12231
|
+
* );
|
|
11146
12232
|
*
|
|
11147
12233
|
* console.log('Transaction Details:');
|
|
11148
12234
|
* console.log('ID:', transaction.id);
|
|
@@ -11155,16 +12241,19 @@ class TransactionManager {
|
|
|
11155
12241
|
* console.log('Description:', transaction.description);
|
|
11156
12242
|
* }
|
|
11157
12243
|
*
|
|
11158
|
-
*
|
|
11159
|
-
*
|
|
12244
|
+
* // Access enriched sender data
|
|
12245
|
+
* if (transaction.included?.sender) {
|
|
12246
|
+
* console.log('Sender:', transaction.included.sender);
|
|
11160
12247
|
* }
|
|
11161
12248
|
*
|
|
11162
|
-
* //
|
|
11163
|
-
* if (transaction.
|
|
11164
|
-
* console.log('
|
|
11165
|
-
*
|
|
11166
|
-
*
|
|
11167
|
-
*
|
|
12249
|
+
* // Access enriched recipient data
|
|
12250
|
+
* if (transaction.included?.recipient) {
|
|
12251
|
+
* console.log('Recipient:', transaction.included.recipient);
|
|
12252
|
+
* }
|
|
12253
|
+
*
|
|
12254
|
+
* // Access enriched business data
|
|
12255
|
+
* if (transaction.included?.engagedBusiness) {
|
|
12256
|
+
* console.log('Business:', transaction.included.engagedBusiness.displayName);
|
|
11168
12257
|
* }
|
|
11169
12258
|
*
|
|
11170
12259
|
* // Show blockchain confirmation
|
|
@@ -11177,8 +12266,8 @@ class TransactionManager {
|
|
|
11177
12266
|
* }
|
|
11178
12267
|
* ```
|
|
11179
12268
|
*/
|
|
11180
|
-
async getTransactionById(transactionId) {
|
|
11181
|
-
return this.transactionService.getTransactionById(transactionId);
|
|
12269
|
+
async getTransactionById(transactionId, include) {
|
|
12270
|
+
return this.transactionService.getTransactionById(transactionId, include);
|
|
11182
12271
|
}
|
|
11183
12272
|
/**
|
|
11184
12273
|
* Create a new transaction
|
|
@@ -11272,150 +12361,62 @@ class TransactionManager {
|
|
|
11272
12361
|
/**
|
|
11273
12362
|
* Get user's transaction history
|
|
11274
12363
|
*
|
|
11275
|
-
* Retrieves transaction history for the authenticated user
|
|
11276
|
-
*
|
|
12364
|
+
* Retrieves transaction history for the authenticated user with comprehensive
|
|
12365
|
+
* filtering options. Provides chronological view of all user's loyalty
|
|
11277
12366
|
* activities including purchases, rewards, redemptions, and transfers.
|
|
12367
|
+
* Optionally enrich with related entities (sender, recipient, business).
|
|
11278
12368
|
*
|
|
11279
|
-
* @param
|
|
11280
|
-
* @
|
|
11281
|
-
* @returns Promise resolving to array of user's transactions
|
|
12369
|
+
* @param options - Query options including filters, pagination, and include relations
|
|
12370
|
+
* @returns Promise resolving to paginated transaction data with optional included entities
|
|
11282
12371
|
*
|
|
11283
12372
|
* @example All Transactions
|
|
11284
12373
|
* ```typescript
|
|
11285
|
-
* const
|
|
12374
|
+
* const result = await sdk.transactions.getUserTransactionHistory();
|
|
11286
12375
|
*
|
|
11287
|
-
* console.log(`
|
|
12376
|
+
* console.log(`Transaction History (${result.data.length} of ${result.pagination.total} transactions)`);
|
|
11288
12377
|
*
|
|
11289
|
-
*
|
|
12378
|
+
* result.data.forEach((transaction, index) => {
|
|
11290
12379
|
* const date = new Date(transaction.createdAt).toLocaleDateString();
|
|
11291
12380
|
* console.log(`\n${index + 1}. ${transaction.type} - ${date}`);
|
|
11292
12381
|
* console.log(` ${transaction.description || 'No description'}`);
|
|
11293
12382
|
* console.log(` Status: ${transaction.status}`);
|
|
11294
|
-
*
|
|
11295
|
-
* if (transaction.amount && transaction.currency) {
|
|
11296
|
-
* console.log(` Amount: ${transaction.amount} ${transaction.currency}`);
|
|
11297
|
-
* }
|
|
11298
|
-
*
|
|
11299
|
-
* if (transaction.business) {
|
|
11300
|
-
* console.log(` Business: ${transaction.business.displayName}`);
|
|
11301
|
-
* }
|
|
11302
12383
|
* });
|
|
11303
12384
|
* ```
|
|
11304
12385
|
*
|
|
11305
|
-
* @example
|
|
12386
|
+
* @example Filter by Role and Status
|
|
11306
12387
|
* ```typescript
|
|
11307
|
-
* // Get only
|
|
11308
|
-
* const
|
|
11309
|
-
*
|
|
11310
|
-
*
|
|
11311
|
-
*
|
|
11312
|
-
*
|
|
11313
|
-
*
|
|
11314
|
-
* if (purchase.amount) {
|
|
11315
|
-
* totalSpent += purchase.amount;
|
|
11316
|
-
* }
|
|
11317
|
-
*
|
|
11318
|
-
* if (purchase.tokensEarned?.length) {
|
|
11319
|
-
* purchase.tokensEarned.forEach(reward => {
|
|
11320
|
-
* totalRewards += reward.amount;
|
|
11321
|
-
* });
|
|
11322
|
-
* }
|
|
12388
|
+
* // Get only completed sent transactions
|
|
12389
|
+
* const sent = await sdk.transactions.getUserTransactionHistory({
|
|
12390
|
+
* role: TransactionRole.SENDER,
|
|
12391
|
+
* status: TransactionStatus.COMPLETED,
|
|
12392
|
+
* include: ['recipient', 'business'],
|
|
12393
|
+
* page: 1,
|
|
12394
|
+
* limit: 50
|
|
11323
12395
|
* });
|
|
11324
12396
|
*
|
|
11325
|
-
*
|
|
11326
|
-
*
|
|
11327
|
-
* console.log(`Total spent: $${totalSpent.toFixed(2)}`);
|
|
11328
|
-
* console.log(`Total rewards earned: ${totalRewards} points`);
|
|
11329
|
-
* ```
|
|
11330
|
-
*
|
|
11331
|
-
* @example Recent Activity
|
|
11332
|
-
* ```typescript
|
|
11333
|
-
* const recentTransactions = await sdk.transactions.getUserTransactionHistory('ALL');
|
|
11334
|
-
*
|
|
11335
|
-
* // Filter to last 30 days
|
|
11336
|
-
* const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
|
11337
|
-
* const recentActivity = recentTransactions.filter(t =>
|
|
11338
|
-
* new Date(t.createdAt) > thirtyDaysAgo
|
|
11339
|
-
* );
|
|
11340
|
-
*
|
|
11341
|
-
* console.log(`Recent Activity (${recentActivity.length} transactions in last 30 days):`);
|
|
11342
|
-
*
|
|
11343
|
-
* // Group by type
|
|
11344
|
-
* const activityByType = recentActivity.reduce((acc, t) => {
|
|
11345
|
-
* acc[t.type] = (acc[t.type] || 0) + 1;
|
|
11346
|
-
* return acc;
|
|
11347
|
-
* }, {});
|
|
11348
|
-
*
|
|
11349
|
-
* Object.entries(activityByType).forEach(([type, count]) => {
|
|
11350
|
-
* console.log(`${type}: ${count} transactions`);
|
|
12397
|
+
* sent.data.forEach(tx => {
|
|
12398
|
+
* console.log(`Sent to: ${tx.included?.recipient?.displayName || tx.recipientAddress}`);
|
|
11351
12399
|
* });
|
|
11352
12400
|
* ```
|
|
11353
|
-
*/
|
|
11354
|
-
async getUserTransactionHistory(role, options) {
|
|
11355
|
-
return this.transactionService.getUserTransactionHistory(role, options);
|
|
11356
|
-
}
|
|
11357
|
-
/**
|
|
11358
|
-
* Admin: Get all tenant transactions
|
|
11359
|
-
*
|
|
11360
|
-
* Retrieves all transactions across the entire tenant/organization for
|
|
11361
|
-
* comprehensive reporting and analysis. This operation requires administrator
|
|
11362
|
-
* privileges and provides system-wide transaction visibility.
|
|
11363
|
-
*
|
|
11364
|
-
* @param limit - Maximum number of transactions to return (default: 1000)
|
|
11365
|
-
* @returns Promise resolving to array of all tenant transactions
|
|
11366
|
-
* @throws {PersApiError} When not authenticated as administrator
|
|
11367
12401
|
*
|
|
11368
|
-
* @example
|
|
12402
|
+
* @example Filter by Trigger Process (Campaign/Redemption)
|
|
11369
12403
|
* ```typescript
|
|
11370
|
-
* //
|
|
11371
|
-
* const
|
|
11372
|
-
*
|
|
11373
|
-
*
|
|
11374
|
-
* console.log(`Total transactions: ${allTransactions.length}`);
|
|
11375
|
-
*
|
|
11376
|
-
* // Analyze by status
|
|
11377
|
-
* const statusCounts = allTransactions.reduce((acc, t) => {
|
|
11378
|
-
* acc[t.status] = (acc[t.status] || 0) + 1;
|
|
11379
|
-
* return acc;
|
|
11380
|
-
* }, {});
|
|
11381
|
-
*
|
|
11382
|
-
* console.log('\nBy status:');
|
|
11383
|
-
* Object.entries(statusCounts).forEach(([status, count]) => {
|
|
11384
|
-
* console.log(`${status}: ${count} transactions`);
|
|
12404
|
+
* // Get all transactions triggered by a specific campaign claim
|
|
12405
|
+
* const claimTxs = await sdk.transactions.getUserTransactionHistory({
|
|
12406
|
+
* triggerProcessId: 'claim-abc123',
|
|
12407
|
+
* include: ['sender', 'recipient', 'business']
|
|
11385
12408
|
* });
|
|
11386
12409
|
*
|
|
11387
|
-
*
|
|
11388
|
-
*
|
|
11389
|
-
*
|
|
11390
|
-
*
|
|
11391
|
-
*
|
|
11392
|
-
*
|
|
11393
|
-
* console.log('\nBy type:');
|
|
11394
|
-
* Object.entries(typeCounts).forEach(([type, count]) => {
|
|
11395
|
-
* console.log(`${type}: ${count} transactions`);
|
|
12410
|
+
* claimTxs.data.forEach(tx => {
|
|
12411
|
+
* console.log('Transaction from claim:', tx.id);
|
|
12412
|
+
* if (tx.included?.engagedBusiness) {
|
|
12413
|
+
* console.log('Business:', tx.included.engagedBusiness.displayName);
|
|
12414
|
+
* }
|
|
11396
12415
|
* });
|
|
11397
|
-
*
|
|
11398
|
-
* // Calculate volume metrics
|
|
11399
|
-
* const totalVolume = allTransactions.reduce((sum, t) =>
|
|
11400
|
-
* sum + (t.amount || 0), 0
|
|
11401
|
-
* );
|
|
11402
|
-
*
|
|
11403
|
-
* const avgTransactionSize = totalVolume / allTransactions.length;
|
|
11404
|
-
*
|
|
11405
|
-
* console.log('\nVolume metrics:');
|
|
11406
|
-
* console.log(`Total volume: $${totalVolume.toFixed(2)}`);
|
|
11407
|
-
* console.log(`Average transaction: $${avgTransactionSize.toFixed(2)}`);
|
|
11408
|
-
*
|
|
11409
|
-
* // Recent activity analysis
|
|
11410
|
-
* const last24Hours = allTransactions.filter(t =>
|
|
11411
|
-
* new Date(t.createdAt) > new Date(Date.now() - 24 * 60 * 60 * 1000)
|
|
11412
|
-
* );
|
|
11413
|
-
*
|
|
11414
|
-
* console.log(`\n⏰ Last 24 hours: ${last24Hours.length} transactions`);
|
|
11415
12416
|
* ```
|
|
11416
12417
|
*/
|
|
11417
|
-
async
|
|
11418
|
-
return this.transactionService.
|
|
12418
|
+
async getUserTransactionHistory(options) {
|
|
12419
|
+
return this.transactionService.getUserTransactionHistory(options);
|
|
11419
12420
|
}
|
|
11420
12421
|
/**
|
|
11421
12422
|
* Admin: Get paginated transactions
|
|
@@ -11424,7 +12425,7 @@ class TransactionManager {
|
|
|
11424
12425
|
* handling. This operation requires administrator privileges and is used for
|
|
11425
12426
|
* detailed transaction analysis and reporting interfaces.
|
|
11426
12427
|
*
|
|
11427
|
-
* @param
|
|
12428
|
+
* @param options - Pagination, filtering parameters, and optional include relations
|
|
11428
12429
|
* @returns Promise resolving to paginated transaction results with metadata
|
|
11429
12430
|
* @throws {PersApiError} When not authenticated as administrator
|
|
11430
12431
|
*
|
|
@@ -11479,9 +12480,24 @@ class TransactionManager {
|
|
|
11479
12480
|
* console.log(`$${transaction.amount} - ${transaction.business?.displayName}`);
|
|
11480
12481
|
* });
|
|
11481
12482
|
* ```
|
|
12483
|
+
*
|
|
12484
|
+
* @example With Included Relations
|
|
12485
|
+
* ```typescript
|
|
12486
|
+
* // Include sender and recipient entities
|
|
12487
|
+
* const result = await sdk.transactions.getPaginatedTransactions({
|
|
12488
|
+
* page: 1,
|
|
12489
|
+
* limit: 50,
|
|
12490
|
+
* include: ['sender', 'recipient', 'business']
|
|
12491
|
+
* });
|
|
12492
|
+
*
|
|
12493
|
+
* result.data.forEach(tx => {
|
|
12494
|
+
* if (tx.included?.sender) console.log('From:', tx.included.sender);
|
|
12495
|
+
* if (tx.included?.recipient) console.log('To:', tx.included.recipient);
|
|
12496
|
+
* });
|
|
12497
|
+
* ```
|
|
11482
12498
|
*/
|
|
11483
|
-
async getPaginatedTransactions(
|
|
11484
|
-
return this.transactionService.getPaginatedTransactions(
|
|
12499
|
+
async getPaginatedTransactions(options) {
|
|
12500
|
+
return this.transactionService.getPaginatedTransactions(options);
|
|
11485
12501
|
}
|
|
11486
12502
|
/**
|
|
11487
12503
|
* Admin: Export transactions as CSV
|
|
@@ -13144,6 +14160,179 @@ class AnalyticsManager {
|
|
|
13144
14160
|
async getTransactionAnalytics(request) {
|
|
13145
14161
|
return this.analyticsService.getTransactionAnalytics(request);
|
|
13146
14162
|
}
|
|
14163
|
+
/**
|
|
14164
|
+
* Get campaign claim analytics with aggregation
|
|
14165
|
+
*
|
|
14166
|
+
* Retrieves aggregated analytics for campaign claims with flexible grouping,
|
|
14167
|
+
* filtering, and metric selection. Provides insights into campaign performance,
|
|
14168
|
+
* claim patterns, and user engagement across the loyalty ecosystem.
|
|
14169
|
+
*
|
|
14170
|
+
* @param request - Analytics request with filters, groupBy, and metrics
|
|
14171
|
+
* @returns Promise resolving to campaign claim analytics data
|
|
14172
|
+
*
|
|
14173
|
+
* @example Claims per campaign
|
|
14174
|
+
* ```typescript
|
|
14175
|
+
* const analytics = await sdk.analytics.getCampaignClaimAnalytics({
|
|
14176
|
+
* filters: { status: 'COMPLETED' },
|
|
14177
|
+
* groupBy: ['campaignId'],
|
|
14178
|
+
* metrics: ['count'],
|
|
14179
|
+
* sortBy: 'count',
|
|
14180
|
+
* sortOrder: 'DESC',
|
|
14181
|
+
* limit: 10
|
|
14182
|
+
* });
|
|
14183
|
+
*
|
|
14184
|
+
* console.log('Top campaigns:', analytics.results);
|
|
14185
|
+
* ```
|
|
14186
|
+
*/
|
|
14187
|
+
async getCampaignClaimAnalytics(request) {
|
|
14188
|
+
return this.analyticsService.getCampaignClaimAnalytics(request);
|
|
14189
|
+
}
|
|
14190
|
+
/**
|
|
14191
|
+
* Get user analytics with engagement metrics
|
|
14192
|
+
*
|
|
14193
|
+
* Retrieves aggregated user statistics including engagement rate, active users,
|
|
14194
|
+
* transaction metrics, and per-active-user averages. Provides comprehensive
|
|
14195
|
+
* insights into user behavior, engagement patterns, and platform adoption metrics.
|
|
14196
|
+
*
|
|
14197
|
+
* NEW: Added per-active-user metrics (averageTransactionsPerActiveUser, etc.) which
|
|
14198
|
+
* show concentrated engagement among active users - more useful than diluted all-user averages.
|
|
14199
|
+
*
|
|
14200
|
+
* Request structure matches TransactionAnalytics and CampaignClaimAnalytics:
|
|
14201
|
+
* - Use filters object for business-specific scoping
|
|
14202
|
+
* - startDate/endDate at root level for date range filtering
|
|
14203
|
+
*
|
|
14204
|
+
* @param request - Analytics request with optional filters and date range
|
|
14205
|
+
* @returns Promise resolving to user analytics data with per-user and per-active-user metrics
|
|
14206
|
+
*
|
|
14207
|
+
* @example Basic user analytics (compare per-user vs per-active-user)
|
|
14208
|
+
* ```typescript
|
|
14209
|
+
* const analytics = await sdk.analytics.getUserAnalytics({});
|
|
14210
|
+
* console.log(`Total users: ${analytics.totalUsers}`);
|
|
14211
|
+
* console.log(`Active users: ${analytics.activeUsers} (${analytics.engagementRate.toFixed(1)}%)`);
|
|
14212
|
+
*
|
|
14213
|
+
* // Per-user averages (includes inactive users - lower numbers)
|
|
14214
|
+
* console.log(`\\nPer-User Averages (all users):`);
|
|
14215
|
+
* console.log(` Transactions: ${analytics.averageTransactionsPerUser.toFixed(2)}`);
|
|
14216
|
+
* console.log(` Claims: ${analytics.averageClaimsPerUser.toFixed(2)}`);
|
|
14217
|
+
* console.log(` Redemptions: ${analytics.averageRedemptionsPerUser.toFixed(2)}`);
|
|
14218
|
+
*
|
|
14219
|
+
* // Per-active-user averages (only engaged users - higher, more meaningful numbers)
|
|
14220
|
+
* console.log(`\\nPer-Active-User Averages (engaged users only):`);
|
|
14221
|
+
* console.log(` Transactions: ${analytics.averageTransactionsPerActiveUser.toFixed(2)}`);
|
|
14222
|
+
* console.log(` Claims: ${analytics.averageClaimsPerActiveUser.toFixed(2)}`);
|
|
14223
|
+
* console.log(` Redemptions: ${analytics.averageRedemptionsPerActiveUser.toFixed(2)}`);
|
|
14224
|
+
* ```
|
|
14225
|
+
*
|
|
14226
|
+
* @example Monthly user analytics with date range
|
|
14227
|
+
* ```typescript
|
|
14228
|
+
* const analytics = await sdk.analytics.getUserAnalytics({
|
|
14229
|
+
* startDate: new Date('2026-02-01'),
|
|
14230
|
+
* endDate: new Date('2026-02-28')
|
|
14231
|
+
* });
|
|
14232
|
+
*
|
|
14233
|
+
* console.log('February 2026 User Metrics:');
|
|
14234
|
+
* console.log(`Total users: ${analytics.totalUsers}`);
|
|
14235
|
+
* console.log(`Active users: ${analytics.activeUsers}`);
|
|
14236
|
+
* console.log(`New users: ${analytics.newUsers}`);
|
|
14237
|
+
* console.log(`Engagement rate: ${analytics.engagementRate.toFixed(2)}%`);
|
|
14238
|
+
* console.log(`Total transaction volume: $${analytics.totalTransactionVolume.toLocaleString()}`);
|
|
14239
|
+
* console.log(`Avg transaction amount: $${analytics.averageTransactionAmount.toFixed(2)}`);
|
|
14240
|
+
* console.log(`\\nFilters applied: ${analytics.metadata.dateRange?.startDate} to ${analytics.metadata.dateRange?.endDate}`);
|
|
14241
|
+
* ```
|
|
14242
|
+
*
|
|
14243
|
+
* @example Business-specific user analytics with filters
|
|
14244
|
+
* ```typescript
|
|
14245
|
+
* const analytics = await sdk.analytics.getUserAnalytics({
|
|
14246
|
+
* filters: { businessId: 'business-123' },
|
|
14247
|
+
* startDate: new Date('2026-01-01'),
|
|
14248
|
+
* endDate: new Date('2026-12-31')
|
|
14249
|
+
* });
|
|
14250
|
+
*
|
|
14251
|
+
* console.log(`Business customer metrics for 2026:`);
|
|
14252
|
+
* console.log(`Active customers: ${analytics.activeUsers} / ${analytics.totalUsers}`);
|
|
14253
|
+
* console.log(`Customer engagement: ${analytics.engagementRate.toFixed(1)}%`);
|
|
14254
|
+
* console.log(`Avg spend per customer: $${analytics.averageTransactionAmount.toFixed(2)}`);
|
|
14255
|
+
* console.log(`Avg spend per active customer: $${(analytics.totalTransactionVolume / analytics.activeUsers).toFixed(2)}`);
|
|
14256
|
+
* console.log(`Avg claims per active customer: ${analytics.averageClaimsPerActiveUser.toFixed(2)}`);
|
|
14257
|
+
* ```
|
|
14258
|
+
*/
|
|
14259
|
+
async getUserAnalytics(request = {}) {
|
|
14260
|
+
return this.analyticsService.getUserAnalytics(request);
|
|
14261
|
+
}
|
|
14262
|
+
/**
|
|
14263
|
+
* Get user transaction ranking with enriched user data
|
|
14264
|
+
*
|
|
14265
|
+
* Retrieves ranked list of users with full user details (email, externalUserId)
|
|
14266
|
+
* and transaction metrics. Data enrichment happens via efficient SQL JOINs + UNION
|
|
14267
|
+
* (handles legacy transactions). Ideal for leaderboards, engagement analysis, and
|
|
14268
|
+
* identifying power users for targeted campaigns.
|
|
14269
|
+
*
|
|
14270
|
+
* Use Cases:
|
|
14271
|
+
* - Admin leaderboards showing top users by activity
|
|
14272
|
+
* - User engagement analysis with full user context
|
|
14273
|
+
* - Identifying power users for campaigns
|
|
14274
|
+
* - Customer segmentation by transaction behavior
|
|
14275
|
+
*
|
|
14276
|
+
* @param request - Ranking request with filters, sorting, and limit
|
|
14277
|
+
* @returns Promise resolving to ranked user list with transaction metrics
|
|
14278
|
+
*
|
|
14279
|
+
* @example Top 50 users by transaction count
|
|
14280
|
+
* ```typescript
|
|
14281
|
+
* const ranking = await sdk.analytics.getUserRanking({
|
|
14282
|
+
* sortBy: 'totalTransactions',
|
|
14283
|
+
* sortOrder: 'DESC',
|
|
14284
|
+
* limit: 50
|
|
14285
|
+
* });
|
|
14286
|
+
*
|
|
14287
|
+
* console.log(`Top ${ranking.totalUsers} users:`);
|
|
14288
|
+
* ranking.results.forEach((user, index) => {
|
|
14289
|
+
* console.log(`#${index + 1}: ${user.email || user.externalUserId}`);
|
|
14290
|
+
* console.log(` Transactions: ${user.totalTransactions}`);
|
|
14291
|
+
* console.log(` Token spent: ${user.tokenSpent}`);
|
|
14292
|
+
* });
|
|
14293
|
+
* ```
|
|
14294
|
+
*
|
|
14295
|
+
* @example Top users by STAMP spending
|
|
14296
|
+
* ```typescript
|
|
14297
|
+
* const ranking = await sdk.analytics.getUserRanking({
|
|
14298
|
+
* filters: { tokenType: 'STAMP' },
|
|
14299
|
+
* sortBy: 'tokenSpent',
|
|
14300
|
+
* sortOrder: 'DESC',
|
|
14301
|
+
* limit: 20
|
|
14302
|
+
* });
|
|
14303
|
+
*
|
|
14304
|
+
* console.log('Top 20 STAMP spenders:');
|
|
14305
|
+
* ranking.results.forEach((user, index) => {
|
|
14306
|
+
* const identifier = user.email || user.externalUserId || user.userId;
|
|
14307
|
+
* console.log(`${index + 1}. ${identifier} - ${user.tokenSpent} STAMP`);
|
|
14308
|
+
* });
|
|
14309
|
+
* ```
|
|
14310
|
+
*/
|
|
14311
|
+
async getUserRanking(request = {}) {
|
|
14312
|
+
return this.analyticsService.getUserRanking(request);
|
|
14313
|
+
}
|
|
14314
|
+
/**
|
|
14315
|
+
* Get business transaction ranking with enriched business data
|
|
14316
|
+
*
|
|
14317
|
+
* Returns ranked list of businesses with full business details and transaction metrics.
|
|
14318
|
+
*
|
|
14319
|
+
* @param request - Ranking request with filters, sorting, and limit
|
|
14320
|
+
* @returns Promise resolving to ranked business list
|
|
14321
|
+
*/
|
|
14322
|
+
async getBusinessRanking(request = {}) {
|
|
14323
|
+
return this.analyticsService.getBusinessRanking(request);
|
|
14324
|
+
}
|
|
14325
|
+
/**
|
|
14326
|
+
* Get monthly user retention analytics
|
|
14327
|
+
*
|
|
14328
|
+
* Returns monthly retention data with user metrics and retention rates.
|
|
14329
|
+
*
|
|
14330
|
+
* @param request - Retention request with monthsBack and filters
|
|
14331
|
+
* @returns Promise resolving to monthly retention data
|
|
14332
|
+
*/
|
|
14333
|
+
async getRetentionAnalytics(request = {}) {
|
|
14334
|
+
return this.analyticsService.getRetentionAnalytics(request);
|
|
14335
|
+
}
|
|
13147
14336
|
/**
|
|
13148
14337
|
* Get the full analytics service for advanced operations
|
|
13149
14338
|
*
|
|
@@ -13238,6 +14427,222 @@ class DonationManager {
|
|
|
13238
14427
|
}
|
|
13239
14428
|
}
|
|
13240
14429
|
|
|
14430
|
+
/**
|
|
14431
|
+
* TriggerSource Manager - Clean, high-level interface for trigger source operations
|
|
14432
|
+
*
|
|
14433
|
+
* Manages trigger sources which are physical or digital activation points for campaigns:
|
|
14434
|
+
* - QR_CODE: Scannable QR codes
|
|
14435
|
+
* - NFC_TAG: NFC tap points
|
|
14436
|
+
* - GPS_GEOFENCE: Location-based triggers
|
|
14437
|
+
* - API_WEBHOOK: External system integration
|
|
14438
|
+
* - TRANSACTION: Purchase/payment based triggers
|
|
14439
|
+
*
|
|
14440
|
+
* Trigger sources are independent entities that can be created, managed, and then
|
|
14441
|
+
* assigned to campaigns. This separation allows reuse and flexible campaign configuration.
|
|
14442
|
+
*
|
|
14443
|
+
* @group Managers
|
|
14444
|
+
* @category TriggerSource Management
|
|
14445
|
+
*
|
|
14446
|
+
* @example Basic TriggerSource Operations
|
|
14447
|
+
* ```typescript
|
|
14448
|
+
* // Get all QR code trigger sources
|
|
14449
|
+
* const qrSources = await sdk.triggerSources.getAll({ type: 'QR_CODE' });
|
|
14450
|
+
*
|
|
14451
|
+
* // Create a new QR code trigger source
|
|
14452
|
+
* const source = await sdk.triggerSources.create({
|
|
14453
|
+
* type: 'QR_CODE',
|
|
14454
|
+
* name: 'Store Entrance QR',
|
|
14455
|
+
* businessId: 'business-123'
|
|
14456
|
+
* });
|
|
14457
|
+
*
|
|
14458
|
+
* // Assign to campaign (via CampaignManager)
|
|
14459
|
+
* await sdk.campaigns.assignTriggerSource('campaign-456', source.id);
|
|
14460
|
+
* ```
|
|
14461
|
+
*/
|
|
14462
|
+
class TriggerSourceManager {
|
|
14463
|
+
constructor(apiClient, events) {
|
|
14464
|
+
this.apiClient = apiClient;
|
|
14465
|
+
this.events = events;
|
|
14466
|
+
const triggerSourceApi = new TriggerSourceApi(apiClient);
|
|
14467
|
+
this.triggerSourceService = new TriggerSourceService(triggerSourceApi);
|
|
14468
|
+
}
|
|
14469
|
+
/**
|
|
14470
|
+
* Get trigger sources with optional filters and pagination
|
|
14471
|
+
*
|
|
14472
|
+
* Retrieves trigger sources (QR codes, NFC tags, GPS geofences, webhooks, etc.)
|
|
14473
|
+
* that can be used to activate campaigns. Supports filtering by type, business,
|
|
14474
|
+
* campaign association, and active status.
|
|
14475
|
+
*
|
|
14476
|
+
* @param options - Filter and pagination options
|
|
14477
|
+
* @returns Promise resolving to paginated trigger sources
|
|
14478
|
+
*
|
|
14479
|
+
* @example
|
|
14480
|
+
* ```typescript
|
|
14481
|
+
* // Get all QR code trigger sources
|
|
14482
|
+
* const qrSources = await sdk.triggerSources.getAll({ type: 'QR_CODE' });
|
|
14483
|
+
*
|
|
14484
|
+
* // Get trigger sources for a specific business
|
|
14485
|
+
* const businessSources = await sdk.triggerSources.getAll({
|
|
14486
|
+
* businessId: 'business-123',
|
|
14487
|
+
* active: true,
|
|
14488
|
+
* page: 1,
|
|
14489
|
+
* limit: 20
|
|
14490
|
+
* });
|
|
14491
|
+
*
|
|
14492
|
+
* // Get trigger sources assigned to a campaign
|
|
14493
|
+
* const campaignSources = await sdk.triggerSources.getAll({
|
|
14494
|
+
* campaignId: 'campaign-456'
|
|
14495
|
+
* });
|
|
14496
|
+
* ```
|
|
14497
|
+
*/
|
|
14498
|
+
async getAll(options) {
|
|
14499
|
+
return this.triggerSourceService.getTriggerSources(options);
|
|
14500
|
+
}
|
|
14501
|
+
/**
|
|
14502
|
+
* Get trigger source by ID
|
|
14503
|
+
*
|
|
14504
|
+
* Retrieves detailed information for a specific trigger source including
|
|
14505
|
+
* its type, location, metadata, and usage analytics.
|
|
14506
|
+
*
|
|
14507
|
+
* @param triggerSourceId - UUID of the trigger source
|
|
14508
|
+
* @returns Promise resolving to trigger source details
|
|
14509
|
+
* @throws {PersApiError} When trigger source with specified ID is not found
|
|
14510
|
+
*
|
|
14511
|
+
* @example
|
|
14512
|
+
* ```typescript
|
|
14513
|
+
* const source = await sdk.triggerSources.getById('source-123');
|
|
14514
|
+
*
|
|
14515
|
+
* console.log('Trigger Source:', source.name);
|
|
14516
|
+
* console.log('Type:', source.type);
|
|
14517
|
+
* console.log('Active:', source.isActive);
|
|
14518
|
+
*
|
|
14519
|
+
* if (source.coordsLatitude && source.coordsLongitude) {
|
|
14520
|
+
* console.log('Location:', source.coordsLatitude, source.coordsLongitude);
|
|
14521
|
+
* }
|
|
14522
|
+
* ```
|
|
14523
|
+
*/
|
|
14524
|
+
async getById(triggerSourceId) {
|
|
14525
|
+
return this.triggerSourceService.getTriggerSourceById(triggerSourceId);
|
|
14526
|
+
}
|
|
14527
|
+
/**
|
|
14528
|
+
* Admin: Create a new trigger source
|
|
14529
|
+
*
|
|
14530
|
+
* Creates a new trigger source (QR code, NFC tag, GPS geofence, webhook, or
|
|
14531
|
+
* transaction-based trigger) that can be assigned to campaigns. Requires
|
|
14532
|
+
* administrator privileges.
|
|
14533
|
+
*
|
|
14534
|
+
* @param triggerSource - Trigger source creation data
|
|
14535
|
+
* @returns Promise resolving to created trigger source
|
|
14536
|
+
* @throws {PersApiError} When not authenticated as admin or validation fails
|
|
14537
|
+
*
|
|
14538
|
+
* @example
|
|
14539
|
+
* ```typescript
|
|
14540
|
+
* // Create a QR code trigger source
|
|
14541
|
+
* const qrSource = await sdk.triggerSources.create({
|
|
14542
|
+
* type: 'QR_CODE',
|
|
14543
|
+
* name: 'Store Entrance QR',
|
|
14544
|
+
* description: 'QR code at main entrance',
|
|
14545
|
+
* businessId: 'business-123',
|
|
14546
|
+
* coordsLatitude: 47.6062,
|
|
14547
|
+
* coordsLongitude: -122.3321
|
|
14548
|
+
* });
|
|
14549
|
+
*
|
|
14550
|
+
* // Create an NFC tag trigger source
|
|
14551
|
+
* const nfcSource = await sdk.triggerSources.create({
|
|
14552
|
+
* type: 'NFC_TAG',
|
|
14553
|
+
* name: 'Product Display NFC',
|
|
14554
|
+
* metadata: { productId: 'SKU-12345' }
|
|
14555
|
+
* });
|
|
14556
|
+
*
|
|
14557
|
+
* // Create a GPS geofence trigger source
|
|
14558
|
+
* const geoSource = await sdk.triggerSources.create({
|
|
14559
|
+
* type: 'GPS_GEOFENCE',
|
|
14560
|
+
* name: 'Store Area',
|
|
14561
|
+
* coordsLatitude: 47.6062,
|
|
14562
|
+
* coordsLongitude: -122.3321,
|
|
14563
|
+
* metadata: { radiusMeters: 100 }
|
|
14564
|
+
* });
|
|
14565
|
+
* ```
|
|
14566
|
+
*/
|
|
14567
|
+
async create(triggerSource) {
|
|
14568
|
+
const result = await this.triggerSourceService.createTriggerSource(triggerSource);
|
|
14569
|
+
this.events?.emitSuccess({
|
|
14570
|
+
domain: 'trigger-source',
|
|
14571
|
+
type: 'TRIGGER_SOURCE_CREATED',
|
|
14572
|
+
userMessage: 'Trigger source created successfully',
|
|
14573
|
+
details: { triggerSourceId: result.id, type: result.type }
|
|
14574
|
+
});
|
|
14575
|
+
return result;
|
|
14576
|
+
}
|
|
14577
|
+
/**
|
|
14578
|
+
* Admin: Update a trigger source
|
|
14579
|
+
*
|
|
14580
|
+
* Updates an existing trigger source's configuration. All fields are optional;
|
|
14581
|
+
* only provided fields will be updated. Requires administrator privileges.
|
|
14582
|
+
*
|
|
14583
|
+
* @param triggerSourceId - UUID of the trigger source to update
|
|
14584
|
+
* @param triggerSource - Updated trigger source data (partial)
|
|
14585
|
+
* @returns Promise resolving to updated trigger source
|
|
14586
|
+
* @throws {PersApiError} When not authenticated as admin or trigger source not found
|
|
14587
|
+
*
|
|
14588
|
+
* @example
|
|
14589
|
+
* ```typescript
|
|
14590
|
+
* // Update trigger source name and location
|
|
14591
|
+
* const updated = await sdk.triggerSources.update('source-123', {
|
|
14592
|
+
* name: 'Updated Store Entrance QR',
|
|
14593
|
+
* description: 'New description',
|
|
14594
|
+
* coordsLatitude: 47.6063,
|
|
14595
|
+
* coordsLongitude: -122.3322
|
|
14596
|
+
* });
|
|
14597
|
+
* ```
|
|
14598
|
+
*/
|
|
14599
|
+
async update(triggerSourceId, triggerSource) {
|
|
14600
|
+
const result = await this.triggerSourceService.updateTriggerSource(triggerSourceId, triggerSource);
|
|
14601
|
+
this.events?.emitSuccess({
|
|
14602
|
+
domain: 'trigger-source',
|
|
14603
|
+
type: 'TRIGGER_SOURCE_UPDATED',
|
|
14604
|
+
userMessage: 'Trigger source updated successfully',
|
|
14605
|
+
details: { triggerSourceId }
|
|
14606
|
+
});
|
|
14607
|
+
return result;
|
|
14608
|
+
}
|
|
14609
|
+
/**
|
|
14610
|
+
* Admin: Delete a trigger source
|
|
14611
|
+
*
|
|
14612
|
+
* Soft deletes a trigger source, making it inactive. The trigger source will
|
|
14613
|
+
* be removed from any campaigns it was assigned to. Requires administrator
|
|
14614
|
+
* privileges.
|
|
14615
|
+
*
|
|
14616
|
+
* @param triggerSourceId - UUID of the trigger source to delete
|
|
14617
|
+
* @returns Promise resolving to success status
|
|
14618
|
+
* @throws {PersApiError} When not authenticated as admin or trigger source not found
|
|
14619
|
+
*
|
|
14620
|
+
* @example
|
|
14621
|
+
* ```typescript
|
|
14622
|
+
* const success = await sdk.triggerSources.delete('source-123');
|
|
14623
|
+
* console.log('Trigger source deleted:', success);
|
|
14624
|
+
* ```
|
|
14625
|
+
*/
|
|
14626
|
+
async delete(triggerSourceId) {
|
|
14627
|
+
const result = await this.triggerSourceService.deleteTriggerSource(triggerSourceId);
|
|
14628
|
+
this.events?.emitSuccess({
|
|
14629
|
+
domain: 'trigger-source',
|
|
14630
|
+
type: 'TRIGGER_SOURCE_DELETED',
|
|
14631
|
+
userMessage: 'Trigger source deleted successfully',
|
|
14632
|
+
details: { triggerSourceId }
|
|
14633
|
+
});
|
|
14634
|
+
return result;
|
|
14635
|
+
}
|
|
14636
|
+
/**
|
|
14637
|
+
* Get the full trigger source service for advanced operations
|
|
14638
|
+
*
|
|
14639
|
+
* @returns TriggerSourceService instance with full API access
|
|
14640
|
+
*/
|
|
14641
|
+
getTriggerSourceService() {
|
|
14642
|
+
return this.triggerSourceService;
|
|
14643
|
+
}
|
|
14644
|
+
}
|
|
14645
|
+
|
|
13241
14646
|
/**
|
|
13242
14647
|
* @fileoverview PERS SDK - Platform-agnostic TypeScript SDK with High-Level Managers
|
|
13243
14648
|
*
|
|
@@ -13351,78 +14756,71 @@ class PersSDK {
|
|
|
13351
14756
|
// Initialize event emitter and wire to API client for error events
|
|
13352
14757
|
this._events = new PersEventEmitter();
|
|
13353
14758
|
this.apiClient.setEvents(this._events);
|
|
13354
|
-
// Initialize domain managers (pass events to managers that emit success events)
|
|
13355
|
-
this._auth = new AuthManager(this.apiClient, this._events);
|
|
13356
|
-
this._users = new UserManager(this.apiClient, this._events);
|
|
13357
|
-
this._userStatus = new UserStatusManager(this.apiClient);
|
|
13358
|
-
this._tokens = new TokenManager(this.apiClient);
|
|
13359
|
-
this._businesses = new BusinessManager(this.apiClient, this._events);
|
|
13360
|
-
this._campaigns = new CampaignManager(this.apiClient, this._events);
|
|
13361
|
-
this._redemptions = new RedemptionManager(this.apiClient, this._events);
|
|
13362
|
-
this._transactions = new TransactionManager(this.apiClient, this._events);
|
|
13363
|
-
this._purchases = new PurchaseManager(this.apiClient);
|
|
13364
|
-
this._files = new FileManager(this.apiClient);
|
|
13365
|
-
this._tenants = new TenantManager(this.apiClient);
|
|
13366
|
-
this._apiKeys = new ApiKeyManager(this.apiClient);
|
|
13367
|
-
this._analytics = new AnalyticsManager(this.apiClient);
|
|
13368
|
-
this._donations = new DonationManager(this.apiClient);
|
|
13369
|
-
// Automatically restore session if enabled and tokens exist
|
|
13370
|
-
if (config.autoRestoreSession !== false) {
|
|
13371
|
-
this.restoreSessionIfTokensExist().catch(err => {
|
|
13372
|
-
console.warn('[PersSDK] Failed to restore session on initialization:', err);
|
|
13373
|
-
});
|
|
13374
|
-
}
|
|
13375
14759
|
}
|
|
13376
14760
|
/**
|
|
13377
|
-
* Restore user session
|
|
14761
|
+
* Restore user session from stored tokens
|
|
13378
14762
|
*
|
|
13379
|
-
*
|
|
13380
|
-
*
|
|
13381
|
-
*
|
|
14763
|
+
* Call this method after SDK initialization to restore the user's session
|
|
14764
|
+
* if valid tokens exist. This is useful for maintaining login state across
|
|
14765
|
+
* app restarts.
|
|
14766
|
+
*
|
|
14767
|
+
* **Important:** Only works for USER and BUSINESS accounts. Admin/tenant
|
|
14768
|
+
* accounts don't support user data fetching, so this will return null for them
|
|
14769
|
+
* (though their tokens remain valid for API calls).
|
|
13382
14770
|
*
|
|
13383
14771
|
* Emits auth domain success event when session is restored.
|
|
13384
14772
|
*
|
|
13385
|
-
* @
|
|
13386
|
-
* @
|
|
14773
|
+
* @returns Promise resolving to User data if session restored, null if no session or admin account
|
|
14774
|
+
* @throws {PersError} If token validation or user fetch fails
|
|
14775
|
+
*
|
|
14776
|
+
* @example
|
|
14777
|
+
* ```typescript
|
|
14778
|
+
* // Call after initial render for better perceived performance
|
|
14779
|
+
* const user = await sdk.restoreSession();
|
|
14780
|
+
* if (user) {
|
|
14781
|
+
* console.log('Welcome back,', user.name);
|
|
14782
|
+
* }
|
|
14783
|
+
* ```
|
|
13387
14784
|
*/
|
|
13388
|
-
async
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
|
|
13397
|
-
|
|
13398
|
-
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
domain: 'authentication',
|
|
13402
|
-
userMessage: 'Session restored successfully',
|
|
13403
|
-
details: { userId: userData.id }
|
|
13404
|
-
});
|
|
13405
|
-
}
|
|
13406
|
-
catch (error) {
|
|
13407
|
-
// Tokens exist but are invalid (expired, revoked, etc.)
|
|
13408
|
-
console.warn('[PersSDK] Failed to restore session, tokens may be invalid:', error);
|
|
13409
|
-
await this._auth.clearAuth();
|
|
13410
|
-
// Emit restoration failed event
|
|
13411
|
-
this._events.emitError({
|
|
13412
|
-
type: 'session_restoration_failed',
|
|
13413
|
-
domain: 'authentication',
|
|
13414
|
-
userMessage: 'Session restoration failed',
|
|
13415
|
-
code: 'SESSION_INVALID',
|
|
13416
|
-
details: { error }
|
|
13417
|
-
});
|
|
13418
|
-
}
|
|
13419
|
-
}
|
|
13420
|
-
else {
|
|
13421
|
-
console.log('[PersSDK] No valid tokens found');
|
|
14785
|
+
async restoreSession() {
|
|
14786
|
+
const hasToken = await this.auth.hasValidAuth();
|
|
14787
|
+
if (!hasToken) {
|
|
14788
|
+
return null;
|
|
14789
|
+
}
|
|
14790
|
+
// Check auth type - only restore session for user and business accounts
|
|
14791
|
+
const authProvider = this.apiClient.getConfig().authProvider;
|
|
14792
|
+
if (authProvider?.getAuthType) {
|
|
14793
|
+
const authType = await authProvider.getAuthType();
|
|
14794
|
+
if (authType === exports.AccountOwnerType.TENANT) {
|
|
14795
|
+
// Admin sessions don't support getCurrentUser(), skip restoration
|
|
14796
|
+
// Tokens are valid and will be used for API calls
|
|
14797
|
+
return null;
|
|
13422
14798
|
}
|
|
13423
14799
|
}
|
|
14800
|
+
try {
|
|
14801
|
+
// Fetch user data to validate tokens and restore session
|
|
14802
|
+
const userData = await this.auth.getCurrentUser();
|
|
14803
|
+
// Emit event through proper event emitter
|
|
14804
|
+
this._events.emitSuccess({
|
|
14805
|
+
type: 'session_restored',
|
|
14806
|
+
domain: 'authentication',
|
|
14807
|
+
userMessage: 'Session restored successfully',
|
|
14808
|
+
details: { userId: userData.id }
|
|
14809
|
+
});
|
|
14810
|
+
return userData;
|
|
14811
|
+
}
|
|
13424
14812
|
catch (error) {
|
|
13425
|
-
|
|
14813
|
+
// Tokens exist but are invalid (expired, revoked, etc.)
|
|
14814
|
+
await this.auth.clearAuth();
|
|
14815
|
+
// Emit restoration failed event
|
|
14816
|
+
this._events.emitError({
|
|
14817
|
+
type: 'session_restoration_failed',
|
|
14818
|
+
domain: 'authentication',
|
|
14819
|
+
userMessage: 'Session restoration failed',
|
|
14820
|
+
code: 'SESSION_INVALID',
|
|
14821
|
+
details: { error }
|
|
14822
|
+
});
|
|
14823
|
+
throw error;
|
|
13426
14824
|
}
|
|
13427
14825
|
}
|
|
13428
14826
|
/**
|
|
@@ -13511,6 +14909,9 @@ class PersSDK {
|
|
|
13511
14909
|
* @see {@link AuthManager} for detailed documentation
|
|
13512
14910
|
*/
|
|
13513
14911
|
get auth() {
|
|
14912
|
+
if (!this._auth) {
|
|
14913
|
+
this._auth = new AuthManager(this.apiClient, this._events);
|
|
14914
|
+
}
|
|
13514
14915
|
return this._auth;
|
|
13515
14916
|
}
|
|
13516
14917
|
/**
|
|
@@ -13524,6 +14925,9 @@ class PersSDK {
|
|
|
13524
14925
|
* ```
|
|
13525
14926
|
*/
|
|
13526
14927
|
get users() {
|
|
14928
|
+
if (!this._users) {
|
|
14929
|
+
this._users = new UserManager(this.apiClient, this._events);
|
|
14930
|
+
}
|
|
13527
14931
|
return this._users;
|
|
13528
14932
|
}
|
|
13529
14933
|
/**
|
|
@@ -13537,6 +14941,9 @@ class PersSDK {
|
|
|
13537
14941
|
* ```
|
|
13538
14942
|
*/
|
|
13539
14943
|
get userStatus() {
|
|
14944
|
+
if (!this._userStatus) {
|
|
14945
|
+
this._userStatus = new UserStatusManager(this.apiClient);
|
|
14946
|
+
}
|
|
13540
14947
|
return this._userStatus;
|
|
13541
14948
|
}
|
|
13542
14949
|
/**
|
|
@@ -13550,6 +14957,9 @@ class PersSDK {
|
|
|
13550
14957
|
* ```
|
|
13551
14958
|
*/
|
|
13552
14959
|
get tokens() {
|
|
14960
|
+
if (!this._tokens) {
|
|
14961
|
+
this._tokens = new TokenManager(this.apiClient);
|
|
14962
|
+
}
|
|
13553
14963
|
return this._tokens;
|
|
13554
14964
|
}
|
|
13555
14965
|
/**
|
|
@@ -13563,6 +14973,9 @@ class PersSDK {
|
|
|
13563
14973
|
* ```
|
|
13564
14974
|
*/
|
|
13565
14975
|
get businesses() {
|
|
14976
|
+
if (!this._businesses) {
|
|
14977
|
+
this._businesses = new BusinessManager(this.apiClient, this._events);
|
|
14978
|
+
}
|
|
13566
14979
|
return this._businesses;
|
|
13567
14980
|
}
|
|
13568
14981
|
/**
|
|
@@ -13576,6 +14989,9 @@ class PersSDK {
|
|
|
13576
14989
|
* ```
|
|
13577
14990
|
*/
|
|
13578
14991
|
get campaigns() {
|
|
14992
|
+
if (!this._campaigns) {
|
|
14993
|
+
this._campaigns = new CampaignManager(this.apiClient, this._events);
|
|
14994
|
+
}
|
|
13579
14995
|
return this._campaigns;
|
|
13580
14996
|
}
|
|
13581
14997
|
/**
|
|
@@ -13589,6 +15005,9 @@ class PersSDK {
|
|
|
13589
15005
|
* ```
|
|
13590
15006
|
*/
|
|
13591
15007
|
get redemptions() {
|
|
15008
|
+
if (!this._redemptions) {
|
|
15009
|
+
this._redemptions = new RedemptionManager(this.apiClient, this._events);
|
|
15010
|
+
}
|
|
13592
15011
|
return this._redemptions;
|
|
13593
15012
|
}
|
|
13594
15013
|
/**
|
|
@@ -13602,6 +15021,9 @@ class PersSDK {
|
|
|
13602
15021
|
* ```
|
|
13603
15022
|
*/
|
|
13604
15023
|
get transactions() {
|
|
15024
|
+
if (!this._transactions) {
|
|
15025
|
+
this._transactions = new TransactionManager(this.apiClient, this._events);
|
|
15026
|
+
}
|
|
13605
15027
|
return this._transactions;
|
|
13606
15028
|
}
|
|
13607
15029
|
/**
|
|
@@ -13615,6 +15037,9 @@ class PersSDK {
|
|
|
13615
15037
|
* ```
|
|
13616
15038
|
*/
|
|
13617
15039
|
get purchases() {
|
|
15040
|
+
if (!this._purchases) {
|
|
15041
|
+
this._purchases = new PurchaseManager(this.apiClient);
|
|
15042
|
+
}
|
|
13618
15043
|
return this._purchases;
|
|
13619
15044
|
}
|
|
13620
15045
|
/**
|
|
@@ -13628,6 +15053,9 @@ class PersSDK {
|
|
|
13628
15053
|
* ```
|
|
13629
15054
|
*/
|
|
13630
15055
|
get files() {
|
|
15056
|
+
if (!this._files) {
|
|
15057
|
+
this._files = new FileManager(this.apiClient);
|
|
15058
|
+
}
|
|
13631
15059
|
return this._files;
|
|
13632
15060
|
}
|
|
13633
15061
|
/**
|
|
@@ -13641,6 +15069,9 @@ class PersSDK {
|
|
|
13641
15069
|
* ```
|
|
13642
15070
|
*/
|
|
13643
15071
|
get tenants() {
|
|
15072
|
+
if (!this._tenants) {
|
|
15073
|
+
this._tenants = new TenantManager(this.apiClient);
|
|
15074
|
+
}
|
|
13644
15075
|
return this._tenants;
|
|
13645
15076
|
}
|
|
13646
15077
|
/**
|
|
@@ -13665,6 +15096,9 @@ class PersSDK {
|
|
|
13665
15096
|
* ```
|
|
13666
15097
|
*/
|
|
13667
15098
|
get apiKeys() {
|
|
15099
|
+
if (!this._apiKeys) {
|
|
15100
|
+
this._apiKeys = new ApiKeyManager(this.apiClient);
|
|
15101
|
+
}
|
|
13668
15102
|
return this._apiKeys;
|
|
13669
15103
|
}
|
|
13670
15104
|
/**
|
|
@@ -13676,6 +15110,9 @@ class PersSDK {
|
|
|
13676
15110
|
* ```
|
|
13677
15111
|
*/
|
|
13678
15112
|
get analytics() {
|
|
15113
|
+
if (!this._analytics) {
|
|
15114
|
+
this._analytics = new AnalyticsManager(this.apiClient);
|
|
15115
|
+
}
|
|
13679
15116
|
return this._analytics;
|
|
13680
15117
|
}
|
|
13681
15118
|
/**
|
|
@@ -13687,8 +15124,45 @@ class PersSDK {
|
|
|
13687
15124
|
* ```
|
|
13688
15125
|
*/
|
|
13689
15126
|
get donations() {
|
|
15127
|
+
if (!this._donations) {
|
|
15128
|
+
this._donations = new DonationManager(this.apiClient);
|
|
15129
|
+
}
|
|
13690
15130
|
return this._donations;
|
|
13691
15131
|
}
|
|
15132
|
+
/**
|
|
15133
|
+
* TriggerSource manager - High-level trigger source operations (Admin Only)
|
|
15134
|
+
*
|
|
15135
|
+
* Provides CRUD operations for managing trigger sources (QR codes, NFC tags,
|
|
15136
|
+
* GPS geofences, API webhooks). TriggerSources are standalone entities that
|
|
15137
|
+
* can be assigned to campaigns via the campaigns manager.
|
|
15138
|
+
*
|
|
15139
|
+
* @returns TriggerSourceManager instance
|
|
15140
|
+
*
|
|
15141
|
+
* @example TriggerSource Operations
|
|
15142
|
+
* ```typescript
|
|
15143
|
+
* // Create a QR code trigger source
|
|
15144
|
+
* const qrSource = await sdk.triggerSources.create({
|
|
15145
|
+
* name: 'Store Entrance QR',
|
|
15146
|
+
* type: 'QR_CODE',
|
|
15147
|
+
* description: 'QR code at main entrance'
|
|
15148
|
+
* });
|
|
15149
|
+
*
|
|
15150
|
+
* // Get all trigger sources
|
|
15151
|
+
* const sources = await sdk.triggerSources.getAll();
|
|
15152
|
+
*
|
|
15153
|
+
* // Update trigger source
|
|
15154
|
+
* await sdk.triggerSources.update(sourceId, { name: 'Updated Name' });
|
|
15155
|
+
*
|
|
15156
|
+
* // Assign to campaign (via campaigns manager)
|
|
15157
|
+
* await sdk.campaigns.assignTriggerSource(campaignId, qrSource.id);
|
|
15158
|
+
* ```
|
|
15159
|
+
*/
|
|
15160
|
+
get triggerSources() {
|
|
15161
|
+
if (!this._triggerSources) {
|
|
15162
|
+
this._triggerSources = new TriggerSourceManager(this.apiClient, this._events);
|
|
15163
|
+
}
|
|
15164
|
+
return this._triggerSources;
|
|
15165
|
+
}
|
|
13692
15166
|
/**
|
|
13693
15167
|
* Gets the API client for direct PERS API requests
|
|
13694
15168
|
*
|
|
@@ -14581,11 +16055,19 @@ const SDKContext = react.createContext(null);
|
|
|
14581
16055
|
// Provider component
|
|
14582
16056
|
const PersSDKProvider = ({ children, config }) => {
|
|
14583
16057
|
const initializingRef = react.useRef(false);
|
|
16058
|
+
// State refs for stable functions to read current values
|
|
16059
|
+
const sdkRef = react.useRef(null);
|
|
16060
|
+
const isInitializedRef = react.useRef(false);
|
|
16061
|
+
const isAuthenticatedRef = react.useRef(false);
|
|
14584
16062
|
const [sdk, setSdk] = react.useState(null);
|
|
14585
16063
|
const [authProvider, setAuthProvider] = react.useState(null);
|
|
14586
16064
|
const [isInitialized, setIsInitialized] = react.useState(false);
|
|
14587
16065
|
const [isAuthenticated, setIsAuthenticated] = react.useState(false);
|
|
14588
16066
|
const [user, setUser] = react.useState(null);
|
|
16067
|
+
// Keep state refs in sync immediately (not in useEffect to avoid race conditions)
|
|
16068
|
+
sdkRef.current = sdk;
|
|
16069
|
+
isInitializedRef.current = isInitialized;
|
|
16070
|
+
isAuthenticatedRef.current = isAuthenticated;
|
|
14589
16071
|
const initialize = react.useCallback(async (config) => {
|
|
14590
16072
|
// Prevent multiple initializations
|
|
14591
16073
|
if (isInitialized || initializingRef.current) {
|
|
@@ -14652,18 +16134,40 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
14652
16134
|
}
|
|
14653
16135
|
}, [config, isInitialized, initialize]);
|
|
14654
16136
|
const refreshUserData = react.useCallback(async () => {
|
|
14655
|
-
|
|
14656
|
-
|
|
16137
|
+
// Read from refs to get current values
|
|
16138
|
+
const currentSdk = sdkRef.current;
|
|
16139
|
+
const currentIsInitialized = isInitializedRef.current;
|
|
16140
|
+
if (!currentSdk || !currentIsInitialized) {
|
|
16141
|
+
throw new Error('SDK not initialized. Cannot refresh user data.');
|
|
14657
16142
|
}
|
|
14658
16143
|
try {
|
|
14659
|
-
const freshUserData = await
|
|
16144
|
+
const freshUserData = await currentSdk.users.getCurrentUser();
|
|
14660
16145
|
setUser(freshUserData);
|
|
14661
16146
|
}
|
|
14662
16147
|
catch (error) {
|
|
14663
16148
|
console.error('Failed to refresh user data:', error);
|
|
14664
16149
|
throw error;
|
|
14665
16150
|
}
|
|
14666
|
-
}, [
|
|
16151
|
+
}, []); // No dependencies - reads from refs
|
|
16152
|
+
const restoreSession = react.useCallback(async () => {
|
|
16153
|
+
// Read from refs to get current values
|
|
16154
|
+
const currentSdk = sdkRef.current;
|
|
16155
|
+
const currentIsInitialized = isInitializedRef.current;
|
|
16156
|
+
if (!currentSdk || !currentIsInitialized) {
|
|
16157
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
16158
|
+
}
|
|
16159
|
+
try {
|
|
16160
|
+
const userData = await currentSdk.restoreSession();
|
|
16161
|
+
if (userData) {
|
|
16162
|
+
setAuthenticationState(userData, true);
|
|
16163
|
+
}
|
|
16164
|
+
return userData;
|
|
16165
|
+
}
|
|
16166
|
+
catch (error) {
|
|
16167
|
+
console.error('[PersSDK] Failed to restore session:', error);
|
|
16168
|
+
throw error;
|
|
16169
|
+
}
|
|
16170
|
+
}, [setAuthenticationState]); // Depends on setAuthenticationState
|
|
14667
16171
|
// Listen for authentication events from core SDK
|
|
14668
16172
|
// Set up immediately when SDK is created (don't wait for isInitialized)
|
|
14669
16173
|
// to catch session_restored events that fire during SDK initialization
|
|
@@ -14676,13 +16180,18 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
14676
16180
|
// Session restored successfully - sync React state
|
|
14677
16181
|
if (event.type === 'session_restored') {
|
|
14678
16182
|
console.log('[PersSDK] Session restoration event received, syncing state...');
|
|
14679
|
-
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
.
|
|
14684
|
-
|
|
14685
|
-
|
|
16183
|
+
// Read user from event details if available, otherwise fetch
|
|
16184
|
+
const userId = event.details?.userId;
|
|
16185
|
+
if (userId) {
|
|
16186
|
+
// User ID available, fetch user data
|
|
16187
|
+
sdk.users.getCurrentUser()
|
|
16188
|
+
.then(userData => {
|
|
16189
|
+
setAuthenticationState(userData, true);
|
|
16190
|
+
})
|
|
16191
|
+
.catch(error => {
|
|
16192
|
+
console.error('[PersSDK] Failed to sync restored session:', error);
|
|
16193
|
+
});
|
|
16194
|
+
}
|
|
14686
16195
|
}
|
|
14687
16196
|
// Session restoration failed or auth error - clear React state
|
|
14688
16197
|
if (event.type === 'session_restoration_failed' || event.code === 'AUTH_FAILED') {
|
|
@@ -14720,28 +16229,17 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
14720
16229
|
const contextValue = react.useMemo(() => ({
|
|
14721
16230
|
// Main SDK instance
|
|
14722
16231
|
sdk,
|
|
14723
|
-
// Manager shortcuts for convenience
|
|
14724
|
-
auth: sdk?.auth || null,
|
|
14725
|
-
users: sdk?.users || null,
|
|
14726
|
-
tokens: sdk?.tokens || null,
|
|
14727
|
-
businesses: sdk?.businesses || null,
|
|
14728
|
-
campaigns: sdk?.campaigns || null,
|
|
14729
|
-
redemptions: sdk?.redemptions || null,
|
|
14730
|
-
transactions: sdk?.transactions || null,
|
|
14731
|
-
purchases: sdk?.purchases || null,
|
|
14732
|
-
tenants: sdk?.tenants || null,
|
|
14733
|
-
analytics: sdk?.analytics || null,
|
|
14734
|
-
donations: sdk?.donations || null,
|
|
14735
16232
|
// Platform-specific providers
|
|
14736
16233
|
authProvider,
|
|
14737
16234
|
// State
|
|
14738
16235
|
isInitialized,
|
|
14739
16236
|
isAuthenticated,
|
|
14740
16237
|
user,
|
|
14741
|
-
// Methods
|
|
16238
|
+
// Methods - expose functions directly, not through refs
|
|
14742
16239
|
initialize,
|
|
14743
16240
|
setAuthenticationState,
|
|
14744
16241
|
refreshUserData,
|
|
16242
|
+
restoreSession,
|
|
14745
16243
|
}), [
|
|
14746
16244
|
sdk,
|
|
14747
16245
|
authProvider,
|
|
@@ -14750,7 +16248,8 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
14750
16248
|
user,
|
|
14751
16249
|
initialize,
|
|
14752
16250
|
setAuthenticationState,
|
|
14753
|
-
refreshUserData
|
|
16251
|
+
refreshUserData,
|
|
16252
|
+
restoreSession
|
|
14754
16253
|
]);
|
|
14755
16254
|
return (jsxRuntime.jsx(SDKContext.Provider, { value: contextValue, children: children }));
|
|
14756
16255
|
};
|
|
@@ -36091,7 +37590,7 @@ const useWeb3 = () => {
|
|
|
36091
37590
|
*/
|
|
36092
37591
|
function useTokenBalances(options) {
|
|
36093
37592
|
const { accountAddress, availableTokens = [], autoLoad = true, refreshInterval = 0 } = options;
|
|
36094
|
-
const { isAuthenticated } = usePersSDK();
|
|
37593
|
+
const { isAuthenticated, sdk } = usePersSDK();
|
|
36095
37594
|
const web3 = useWeb3();
|
|
36096
37595
|
const [tokenBalances, setTokenBalances] = react.useState([]);
|
|
36097
37596
|
const [isLoading, setIsLoading] = react.useState(false);
|
|
@@ -36193,15 +37692,28 @@ function useTokenBalances(options) {
|
|
|
36193
37692
|
loadBalances();
|
|
36194
37693
|
}
|
|
36195
37694
|
}, [autoLoad, isAvailable, availableTokens.length, loadBalances]);
|
|
36196
|
-
//
|
|
37695
|
+
// Event-driven refresh: listen for transaction events instead of polling
|
|
36197
37696
|
react.useEffect(() => {
|
|
36198
|
-
if (refreshInterval
|
|
36199
|
-
|
|
37697
|
+
if (!sdk || refreshInterval <= 0 || !isAvailable)
|
|
37698
|
+
return;
|
|
37699
|
+
// Subscribe to transaction domain events
|
|
37700
|
+
const unsubscribe = sdk.events.subscribe((event) => {
|
|
37701
|
+
if (event.domain === 'transaction' && event.type === 'transaction_completed') {
|
|
37702
|
+
console.log('[useTokenBalances] Transaction completed, refreshing balances...');
|
|
36200
37703
|
loadBalances();
|
|
36201
|
-
}
|
|
36202
|
-
|
|
36203
|
-
|
|
36204
|
-
|
|
37704
|
+
}
|
|
37705
|
+
}, { domains: ['transaction'] });
|
|
37706
|
+
// Also set up a fallback polling interval (much longer than before)
|
|
37707
|
+
// This handles cases where events might be missed
|
|
37708
|
+
const fallbackInterval = Math.max(refreshInterval, 60000); // Minimum 1 minute
|
|
37709
|
+
const intervalId = setInterval(() => {
|
|
37710
|
+
loadBalances();
|
|
37711
|
+
}, fallbackInterval);
|
|
37712
|
+
return () => {
|
|
37713
|
+
unsubscribe();
|
|
37714
|
+
clearInterval(intervalId);
|
|
37715
|
+
};
|
|
37716
|
+
}, [sdk, refreshInterval, isAvailable, loadBalances]);
|
|
36205
37717
|
return {
|
|
36206
37718
|
tokenBalances,
|
|
36207
37719
|
isLoading,
|
|
@@ -36849,25 +38361,32 @@ const useTransactions = () => {
|
|
|
36849
38361
|
}
|
|
36850
38362
|
}, [sdk, isInitialized, signAndSubmitTransactionWithJWT, isSignerAvailable]);
|
|
36851
38363
|
/**
|
|
36852
|
-
* Retrieves a specific transaction by its ID
|
|
38364
|
+
* Retrieves a specific transaction by its ID with optional include relations
|
|
36853
38365
|
*
|
|
36854
38366
|
* @param transactionId - Unique identifier of the transaction
|
|
38367
|
+
* @param include - Optional relations to include (sender, recipient, business) for enriched entity data
|
|
36855
38368
|
* @returns Promise resolving to transaction data or null if not found
|
|
36856
38369
|
* @throws Error if SDK is not initialized
|
|
36857
38370
|
*
|
|
36858
38371
|
* @example
|
|
36859
38372
|
* ```typescript
|
|
36860
38373
|
* const { getTransactionById } = useTransactions();
|
|
38374
|
+
*
|
|
38375
|
+
* // Basic retrieval
|
|
36861
38376
|
* const transaction = await getTransactionById('txn-123');
|
|
36862
|
-
*
|
|
38377
|
+
*
|
|
38378
|
+
* // With enriched sender/recipient data
|
|
38379
|
+
* const enrichedTx = await getTransactionById('txn-123', ['sender', 'recipient', 'business']);
|
|
38380
|
+
* console.log('Sender:', enrichedTx?.included?.sender);
|
|
38381
|
+
* console.log('Business:', enrichedTx?.included?.engagedBusiness?.displayName);
|
|
36863
38382
|
* ```
|
|
36864
38383
|
*/
|
|
36865
|
-
const getTransactionById = react.useCallback(async (transactionId) => {
|
|
38384
|
+
const getTransactionById = react.useCallback(async (transactionId, include) => {
|
|
36866
38385
|
if (!isInitialized || !sdk) {
|
|
36867
38386
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
36868
38387
|
}
|
|
36869
38388
|
try {
|
|
36870
|
-
const result = await sdk.transactions.getTransactionById(transactionId);
|
|
38389
|
+
const result = await sdk.transactions.getTransactionById(transactionId, include);
|
|
36871
38390
|
return result;
|
|
36872
38391
|
}
|
|
36873
38392
|
catch (error) {
|
|
@@ -36876,25 +38395,48 @@ const useTransactions = () => {
|
|
|
36876
38395
|
}
|
|
36877
38396
|
}, [sdk, isInitialized]);
|
|
36878
38397
|
/**
|
|
36879
|
-
* Retrieves transaction history for the authenticated user
|
|
38398
|
+
* Retrieves transaction history for the authenticated user with comprehensive filtering
|
|
36880
38399
|
*
|
|
36881
|
-
*
|
|
36882
|
-
*
|
|
38400
|
+
* Supports filtering by role, status, type, business, token, and more.
|
|
38401
|
+
* Optionally enrich with related entities (sender, recipient, business).
|
|
38402
|
+
*
|
|
38403
|
+
* @param options - Query options including filters, pagination, and include relations
|
|
38404
|
+
* @returns Promise resolving to paginated array of user's transactions
|
|
36883
38405
|
* @throws Error if SDK is not initialized
|
|
36884
38406
|
*
|
|
36885
38407
|
* @example
|
|
36886
38408
|
* ```typescript
|
|
36887
38409
|
* const { getUserTransactionHistory } = useTransactions();
|
|
36888
|
-
*
|
|
36889
|
-
*
|
|
38410
|
+
*
|
|
38411
|
+
* // Simple: Get all transactions
|
|
38412
|
+
* const allTransactions = await getUserTransactionHistory();
|
|
38413
|
+
*
|
|
38414
|
+
* // Filter by role (legacy support)
|
|
38415
|
+
* const sentTransactions = await getUserTransactionHistory({ role: 'SENDER' });
|
|
38416
|
+
*
|
|
38417
|
+
* // Advanced filtering with include relations
|
|
38418
|
+
* const filtered = await getUserTransactionHistory({
|
|
38419
|
+
* role: 'SENDER',
|
|
38420
|
+
* status: 'COMPLETED',
|
|
38421
|
+
* engagedBusinessId: 'business-123',
|
|
38422
|
+
* include: ['recipient', 'business'],
|
|
38423
|
+
* page: 1,
|
|
38424
|
+
* limit: 20
|
|
38425
|
+
* });
|
|
38426
|
+
*
|
|
38427
|
+
* // Access enriched data
|
|
38428
|
+
* filtered.data.forEach(tx => {
|
|
38429
|
+
* console.log('Recipient:', tx.included?.recipient);
|
|
38430
|
+
* console.log('Business:', tx.included?.engagedBusiness?.displayName);
|
|
38431
|
+
* });
|
|
36890
38432
|
* ```
|
|
36891
38433
|
*/
|
|
36892
|
-
const getUserTransactionHistory = react.useCallback(async (
|
|
38434
|
+
const getUserTransactionHistory = react.useCallback(async (options) => {
|
|
36893
38435
|
if (!isInitialized || !sdk) {
|
|
36894
38436
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
36895
38437
|
}
|
|
36896
38438
|
try {
|
|
36897
|
-
const result = await sdk.transactions.getUserTransactionHistory(
|
|
38439
|
+
const result = await sdk.transactions.getUserTransactionHistory(options);
|
|
36898
38440
|
return result;
|
|
36899
38441
|
}
|
|
36900
38442
|
catch (error) {
|
|
@@ -36902,25 +38444,39 @@ const useTransactions = () => {
|
|
|
36902
38444
|
throw error;
|
|
36903
38445
|
}
|
|
36904
38446
|
}, [sdk, isInitialized]);
|
|
36905
|
-
|
|
36906
|
-
|
|
36907
|
-
|
|
36908
|
-
|
|
36909
|
-
|
|
36910
|
-
|
|
36911
|
-
|
|
36912
|
-
|
|
36913
|
-
|
|
36914
|
-
|
|
36915
|
-
|
|
36916
|
-
|
|
36917
|
-
|
|
36918
|
-
|
|
38447
|
+
/**
|
|
38448
|
+
* Admin: Get paginated transactions with optional include relations
|
|
38449
|
+
*
|
|
38450
|
+
* @param params - Pagination and filtering parameters
|
|
38451
|
+
* @param include - Optional relations to include for enriched entity data
|
|
38452
|
+
* @returns Promise resolving to paginated transaction results
|
|
38453
|
+
* @throws Error if SDK is not initialized
|
|
38454
|
+
*
|
|
38455
|
+
* @example
|
|
38456
|
+
* ```typescript
|
|
38457
|
+
* const { getPaginatedTransactions } = useTransactions();
|
|
38458
|
+
*
|
|
38459
|
+
* // Basic pagination
|
|
38460
|
+
* const result = await getPaginatedTransactions({ page: 1, limit: 50 });
|
|
38461
|
+
*
|
|
38462
|
+
* // With include relations
|
|
38463
|
+
* const enrichedResult = await getPaginatedTransactions(
|
|
38464
|
+
* { page: 1, limit: 50, sortBy: 'createdAt', sortOrder: 'DESC' },
|
|
38465
|
+
* include: ['sender', 'recipient', 'business']
|
|
38466
|
+
* });
|
|
38467
|
+
*
|
|
38468
|
+
* enrichedResult.data.forEach(tx => {
|
|
38469
|
+
* console.log('From:', tx.included?.sender);
|
|
38470
|
+
* console.log('To:', tx.included?.recipient);
|
|
38471
|
+
* });
|
|
38472
|
+
* ```
|
|
38473
|
+
*/
|
|
38474
|
+
const getPaginatedTransactions = react.useCallback(async (options) => {
|
|
36919
38475
|
if (!isInitialized || !sdk) {
|
|
36920
38476
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
36921
38477
|
}
|
|
36922
38478
|
try {
|
|
36923
|
-
const result = await sdk.transactions.getPaginatedTransactions(
|
|
38479
|
+
const result = await sdk.transactions.getPaginatedTransactions(options);
|
|
36924
38480
|
return result;
|
|
36925
38481
|
}
|
|
36926
38482
|
catch (error) {
|
|
@@ -36945,7 +38501,6 @@ const useTransactions = () => {
|
|
|
36945
38501
|
createTransaction,
|
|
36946
38502
|
getTransactionById,
|
|
36947
38503
|
getUserTransactionHistory,
|
|
36948
|
-
getTenantTransactions,
|
|
36949
38504
|
getPaginatedTransactions,
|
|
36950
38505
|
exportTransactionsCSV,
|
|
36951
38506
|
isAvailable: isInitialized && !!sdk?.transactions,
|
|
@@ -37178,12 +38733,26 @@ const useCampaigns = () => {
|
|
|
37178
38733
|
throw error;
|
|
37179
38734
|
}
|
|
37180
38735
|
}, [sdk, isInitialized]);
|
|
37181
|
-
|
|
38736
|
+
/**
|
|
38737
|
+
* Get campaign by ID with optional include relations
|
|
38738
|
+
*
|
|
38739
|
+
* @param campaignId - The campaign ID
|
|
38740
|
+
* @param include - Relations to include: 'triggerSources', 'businesses'
|
|
38741
|
+
* @returns Promise resolving to campaign data
|
|
38742
|
+
*
|
|
38743
|
+
* @example
|
|
38744
|
+
* ```typescript
|
|
38745
|
+
* // Get campaign with all related data
|
|
38746
|
+
* const campaign = await getCampaignById('campaign-123', ['triggerSources', 'businesses']);
|
|
38747
|
+
* console.log('Trigger sources:', campaign.included?.triggerSources);
|
|
38748
|
+
* ```
|
|
38749
|
+
*/
|
|
38750
|
+
const getCampaignById = react.useCallback(async (campaignId, include) => {
|
|
37182
38751
|
if (!isInitialized || !sdk) {
|
|
37183
38752
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
37184
38753
|
}
|
|
37185
38754
|
try {
|
|
37186
|
-
const result = await sdk.campaigns.getCampaignById(campaignId);
|
|
38755
|
+
const result = await sdk.campaigns.getCampaignById(campaignId, include);
|
|
37187
38756
|
return result;
|
|
37188
38757
|
}
|
|
37189
38758
|
catch (error) {
|
|
@@ -37207,7 +38776,25 @@ const useCampaigns = () => {
|
|
|
37207
38776
|
throw error;
|
|
37208
38777
|
}
|
|
37209
38778
|
}, [sdk, isInitialized, isAuthenticated]);
|
|
37210
|
-
|
|
38779
|
+
/**
|
|
38780
|
+
* Get user's campaign claims with optional pagination and include relations
|
|
38781
|
+
*
|
|
38782
|
+
* @param options - Optional options including pagination and include relations
|
|
38783
|
+
* @returns Promise resolving to paginated campaign claims
|
|
38784
|
+
*
|
|
38785
|
+
* @example
|
|
38786
|
+
* ```typescript
|
|
38787
|
+
* // Get user's claims with campaign details
|
|
38788
|
+
* const { data: claims } = await getUserClaims({ include: ['campaign', 'business'] });
|
|
38789
|
+
* claims.forEach(claim => {
|
|
38790
|
+
* console.log('Campaign:', claim.included?.campaign?.title);
|
|
38791
|
+
* });
|
|
38792
|
+
*
|
|
38793
|
+
* // With pagination
|
|
38794
|
+
* const { data } = await getUserClaims({ page: 1, limit: 10, include: ['campaign'] });
|
|
38795
|
+
* ```
|
|
38796
|
+
*/
|
|
38797
|
+
const getUserClaims = react.useCallback(async (options) => {
|
|
37211
38798
|
if (!isInitialized || !sdk) {
|
|
37212
38799
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
37213
38800
|
}
|
|
@@ -37216,7 +38803,7 @@ const useCampaigns = () => {
|
|
|
37216
38803
|
return { data: [], pagination: { page: 1, limit: 0, total: 0, pages: 0, hasNext: false, hasPrev: false } };
|
|
37217
38804
|
}
|
|
37218
38805
|
try {
|
|
37219
|
-
const result = await sdk.campaigns.getUserClaims();
|
|
38806
|
+
const result = await sdk.campaigns.getUserClaims(options);
|
|
37220
38807
|
return result;
|
|
37221
38808
|
}
|
|
37222
38809
|
catch (error) {
|
|
@@ -37251,12 +38838,28 @@ const useCampaigns = () => {
|
|
|
37251
38838
|
throw error;
|
|
37252
38839
|
}
|
|
37253
38840
|
}, [sdk, isInitialized]);
|
|
37254
|
-
|
|
38841
|
+
/**
|
|
38842
|
+
* Admin: Get campaign claims with optional filters and include relations
|
|
38843
|
+
*
|
|
38844
|
+
* @param filters - Optional filters for campaign, user, or business
|
|
38845
|
+
* @param include - Relations to include: 'campaign', 'user', 'business'
|
|
38846
|
+
* @returns Promise resolving to paginated campaign claims
|
|
38847
|
+
*
|
|
38848
|
+
* @example
|
|
38849
|
+
* ```typescript
|
|
38850
|
+
* // Get all claims with user details
|
|
38851
|
+
* const { data: claims } = await getCampaignClaims({}, ['user']);
|
|
38852
|
+
*
|
|
38853
|
+
* // Get claims for a specific campaign
|
|
38854
|
+
* const { data } = await getCampaignClaims({ campaignId: 'campaign-123' }, ['user', 'business']);
|
|
38855
|
+
* ```
|
|
38856
|
+
*/
|
|
38857
|
+
const getCampaignClaims = react.useCallback(async (filters, include) => {
|
|
37255
38858
|
if (!isInitialized || !sdk) {
|
|
37256
38859
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
37257
38860
|
}
|
|
37258
38861
|
try {
|
|
37259
|
-
const result = await sdk.campaigns.getCampaignClaims();
|
|
38862
|
+
const result = await sdk.campaigns.getCampaignClaims(filters, include);
|
|
37260
38863
|
return result;
|
|
37261
38864
|
}
|
|
37262
38865
|
catch (error) {
|
|
@@ -37264,12 +38867,19 @@ const useCampaigns = () => {
|
|
|
37264
38867
|
throw error;
|
|
37265
38868
|
}
|
|
37266
38869
|
}, [sdk, isInitialized]);
|
|
37267
|
-
|
|
38870
|
+
/**
|
|
38871
|
+
* Admin: Get campaign claims by user ID with optional include relations
|
|
38872
|
+
*
|
|
38873
|
+
* @param userId - The user ID
|
|
38874
|
+
* @param include - Relations to include: 'campaign', 'user', 'business'
|
|
38875
|
+
* @returns Promise resolving to paginated campaign claims
|
|
38876
|
+
*/
|
|
38877
|
+
const getCampaignClaimsByUserId = react.useCallback(async (userId, include) => {
|
|
37268
38878
|
if (!isInitialized || !sdk) {
|
|
37269
38879
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
37270
38880
|
}
|
|
37271
38881
|
try {
|
|
37272
|
-
const result = await sdk.campaigns.getCampaignClaimsByUserId(userId);
|
|
38882
|
+
const result = await sdk.campaigns.getCampaignClaimsByUserId(userId, undefined, include);
|
|
37273
38883
|
return result;
|
|
37274
38884
|
}
|
|
37275
38885
|
catch (error) {
|
|
@@ -37277,12 +38887,19 @@ const useCampaigns = () => {
|
|
|
37277
38887
|
throw error;
|
|
37278
38888
|
}
|
|
37279
38889
|
}, [sdk, isInitialized]);
|
|
37280
|
-
|
|
38890
|
+
/**
|
|
38891
|
+
* Admin: Get campaign claims by business ID with optional include relations
|
|
38892
|
+
*
|
|
38893
|
+
* @param businessId - The business ID
|
|
38894
|
+
* @param include - Relations to include: 'campaign', 'user', 'business'
|
|
38895
|
+
* @returns Promise resolving to paginated campaign claims
|
|
38896
|
+
*/
|
|
38897
|
+
const getCampaignClaimsByBusinessId = react.useCallback(async (businessId, include) => {
|
|
37281
38898
|
if (!isInitialized || !sdk) {
|
|
37282
38899
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
37283
38900
|
}
|
|
37284
38901
|
try {
|
|
37285
|
-
const result = await sdk.campaigns.getCampaignClaimsByBusinessId(businessId);
|
|
38902
|
+
const result = await sdk.campaigns.getCampaignClaimsByBusinessId(businessId, undefined, include);
|
|
37286
38903
|
return result;
|
|
37287
38904
|
}
|
|
37288
38905
|
catch (error) {
|
|
@@ -37290,6 +38907,76 @@ const useCampaigns = () => {
|
|
|
37290
38907
|
throw error;
|
|
37291
38908
|
}
|
|
37292
38909
|
}, [sdk, isInitialized]);
|
|
38910
|
+
// ==========================================
|
|
38911
|
+
// TRIGGER SOURCE ASSIGNMENT (Admin)
|
|
38912
|
+
// Note: TriggerSource CRUD is in useTriggerSources hook
|
|
38913
|
+
// ==========================================
|
|
38914
|
+
/**
|
|
38915
|
+
* Admin: Assign a trigger source to a campaign
|
|
38916
|
+
*
|
|
38917
|
+
* Associates a trigger source (QR code, NFC tag, etc.) with a campaign.
|
|
38918
|
+
* A campaign can have multiple trigger sources.
|
|
38919
|
+
*
|
|
38920
|
+
* Note: To create/update/delete trigger sources, use `useTriggerSources` hook.
|
|
38921
|
+
*
|
|
38922
|
+
* @param campaignId - Campaign UUID
|
|
38923
|
+
* @param triggerSourceId - Trigger source UUID
|
|
38924
|
+
* @returns Promise resolving to updated campaign
|
|
38925
|
+
*
|
|
38926
|
+
* @example
|
|
38927
|
+
* ```typescript
|
|
38928
|
+
* const { create } = useTriggerSources();
|
|
38929
|
+
* const { assignTriggerSource } = useCampaigns();
|
|
38930
|
+
*
|
|
38931
|
+
* // Create trigger source first
|
|
38932
|
+
* const source = await create({ type: 'QR_CODE', name: 'Store QR' });
|
|
38933
|
+
*
|
|
38934
|
+
* // Then assign to campaign
|
|
38935
|
+
* const updated = await assignTriggerSource('campaign-123', source.id);
|
|
38936
|
+
* ```
|
|
38937
|
+
*/
|
|
38938
|
+
const assignTriggerSource = react.useCallback(async (campaignId, triggerSourceId) => {
|
|
38939
|
+
if (!isInitialized || !sdk) {
|
|
38940
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
38941
|
+
}
|
|
38942
|
+
if (!isAuthenticated) {
|
|
38943
|
+
throw new Error('SDK not authenticated. assignTriggerSource requires admin authentication.');
|
|
38944
|
+
}
|
|
38945
|
+
try {
|
|
38946
|
+
const result = await sdk.campaigns.assignTriggerSource(campaignId, triggerSourceId);
|
|
38947
|
+
return result;
|
|
38948
|
+
}
|
|
38949
|
+
catch (error) {
|
|
38950
|
+
console.error('Failed to assign trigger source:', error);
|
|
38951
|
+
throw error;
|
|
38952
|
+
}
|
|
38953
|
+
}, [sdk, isInitialized, isAuthenticated]);
|
|
38954
|
+
/**
|
|
38955
|
+
* Admin: Remove a trigger source from a campaign
|
|
38956
|
+
*
|
|
38957
|
+
* Removes the association between a trigger source and a campaign.
|
|
38958
|
+
* The trigger source itself is not deleted.
|
|
38959
|
+
*
|
|
38960
|
+
* @param campaignId - Campaign UUID
|
|
38961
|
+
* @param triggerSourceId - Trigger source UUID
|
|
38962
|
+
* @returns Promise resolving to updated campaign
|
|
38963
|
+
*/
|
|
38964
|
+
const removeTriggerSource = react.useCallback(async (campaignId, triggerSourceId) => {
|
|
38965
|
+
if (!isInitialized || !sdk) {
|
|
38966
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
38967
|
+
}
|
|
38968
|
+
if (!isAuthenticated) {
|
|
38969
|
+
throw new Error('SDK not authenticated. removeTriggerSource requires admin authentication.');
|
|
38970
|
+
}
|
|
38971
|
+
try {
|
|
38972
|
+
const result = await sdk.campaigns.removeTriggerSource(campaignId, triggerSourceId);
|
|
38973
|
+
return result;
|
|
38974
|
+
}
|
|
38975
|
+
catch (error) {
|
|
38976
|
+
console.error('Failed to remove trigger source:', error);
|
|
38977
|
+
throw error;
|
|
38978
|
+
}
|
|
38979
|
+
}, [sdk, isInitialized, isAuthenticated]);
|
|
37293
38980
|
return {
|
|
37294
38981
|
getActiveCampaigns,
|
|
37295
38982
|
getCampaignById,
|
|
@@ -37300,6 +38987,8 @@ const useCampaigns = () => {
|
|
|
37300
38987
|
getCampaignClaims,
|
|
37301
38988
|
getCampaignClaimsByUserId,
|
|
37302
38989
|
getCampaignClaimsByBusinessId,
|
|
38990
|
+
assignTriggerSource,
|
|
38991
|
+
removeTriggerSource,
|
|
37303
38992
|
isAvailable: isInitialized && !!sdk?.campaigns,
|
|
37304
38993
|
};
|
|
37305
38994
|
};
|
|
@@ -37328,7 +39017,23 @@ const useRedemptions = () => {
|
|
|
37328
39017
|
throw error;
|
|
37329
39018
|
}
|
|
37330
39019
|
}, [sdk, isInitialized]);
|
|
37331
|
-
|
|
39020
|
+
/**
|
|
39021
|
+
* Get user's redemption history with optional include relations
|
|
39022
|
+
*
|
|
39023
|
+
* @param include - Relations to include: 'redemption', 'user', 'business'
|
|
39024
|
+
* @returns Promise resolving to paginated redemption history
|
|
39025
|
+
*
|
|
39026
|
+
* @example
|
|
39027
|
+
* ```typescript
|
|
39028
|
+
* // Get user redemptions with full redemption details
|
|
39029
|
+
* const { data: redeems } = await getUserRedemptions(['redemption', 'business']);
|
|
39030
|
+
* redeems.forEach(redeem => {
|
|
39031
|
+
* console.log('Redemption:', redeem.included?.redemption?.title);
|
|
39032
|
+
* console.log('Business:', redeem.included?.business?.displayName);
|
|
39033
|
+
* });
|
|
39034
|
+
* ```
|
|
39035
|
+
*/
|
|
39036
|
+
const getUserRedemptions = react.useCallback(async (include) => {
|
|
37332
39037
|
if (!isInitialized || !sdk) {
|
|
37333
39038
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
37334
39039
|
}
|
|
@@ -37337,7 +39042,7 @@ const useRedemptions = () => {
|
|
|
37337
39042
|
return { data: [], pagination: { page: 1, limit: 0, total: 0, pages: 0, hasNext: false, hasPrev: false } };
|
|
37338
39043
|
}
|
|
37339
39044
|
try {
|
|
37340
|
-
const result = await sdk.redemptions.getUserRedemptions();
|
|
39045
|
+
const result = await sdk.redemptions.getUserRedemptions(undefined, include);
|
|
37341
39046
|
return result;
|
|
37342
39047
|
}
|
|
37343
39048
|
catch (error) {
|
|
@@ -37391,6 +39096,40 @@ const useRedemptions = () => {
|
|
|
37391
39096
|
}
|
|
37392
39097
|
}, [sdk, isInitialized, isAuthenticated, signAndSubmitTransactionWithJWT, isSignerAvailable]);
|
|
37393
39098
|
// Admin methods
|
|
39099
|
+
/**
|
|
39100
|
+
* Admin: Get all redemption redeems with filters and include relations
|
|
39101
|
+
*
|
|
39102
|
+
* Retrieves all redemption redeems across the platform with filtering.
|
|
39103
|
+
*
|
|
39104
|
+
* @param filters - Optional filters for user, redemption, and pagination
|
|
39105
|
+
* @param include - Relations to include: 'redemption', 'user', 'business'
|
|
39106
|
+
* @returns Promise resolving to paginated redemption redeems
|
|
39107
|
+
*
|
|
39108
|
+
* @example
|
|
39109
|
+
* ```typescript
|
|
39110
|
+
* // Get all redeems with user details
|
|
39111
|
+
* const { data: redeems } = await getRedemptionRedeems({}, ['user', 'redemption']);
|
|
39112
|
+
*
|
|
39113
|
+
* // Filter by specific user
|
|
39114
|
+
* const { data: userRedeems } = await getRedemptionRedeems(
|
|
39115
|
+
* { userId: 'user-123' },
|
|
39116
|
+
* ['redemption']
|
|
39117
|
+
* );
|
|
39118
|
+
* ```
|
|
39119
|
+
*/
|
|
39120
|
+
const getRedemptionRedeems = react.useCallback(async (filters, include) => {
|
|
39121
|
+
if (!isInitialized || !sdk) {
|
|
39122
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
39123
|
+
}
|
|
39124
|
+
try {
|
|
39125
|
+
const result = await sdk.redemptions.getRedemptionRedeems(filters, include);
|
|
39126
|
+
return result;
|
|
39127
|
+
}
|
|
39128
|
+
catch (error) {
|
|
39129
|
+
console.error('Failed to fetch redemption redeems:', error);
|
|
39130
|
+
throw error;
|
|
39131
|
+
}
|
|
39132
|
+
}, [sdk, isInitialized]);
|
|
37394
39133
|
const createRedemption = react.useCallback(async (redemptionData) => {
|
|
37395
39134
|
if (!isInitialized || !sdk) {
|
|
37396
39135
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
@@ -37448,6 +39187,7 @@ const useRedemptions = () => {
|
|
|
37448
39187
|
getUserRedemptions,
|
|
37449
39188
|
redeem,
|
|
37450
39189
|
getRedemptionTypes,
|
|
39190
|
+
getRedemptionRedeems,
|
|
37451
39191
|
createRedemption,
|
|
37452
39192
|
updateRedemption,
|
|
37453
39193
|
toggleRedemptionStatus,
|
|
@@ -37876,29 +39616,47 @@ const useFiles = () => {
|
|
|
37876
39616
|
/**
|
|
37877
39617
|
* React hook for analytics operations in the PERS SDK
|
|
37878
39618
|
*
|
|
37879
|
-
* Provides
|
|
37880
|
-
*
|
|
39619
|
+
* Provides comprehensive analytics and business intelligence capabilities:
|
|
39620
|
+
* - **Transaction Analytics**: Volume, trends, business performance
|
|
39621
|
+
* - **Campaign Claim Analytics**: Campaign performance and claim patterns
|
|
39622
|
+
* - **User Analytics**: Engagement metrics with per-active-user averages
|
|
39623
|
+
* - **User Ranking**: Leaderboards with full user details
|
|
39624
|
+
* - **Business Ranking**: Business performance rankings
|
|
39625
|
+
* - **Retention Analytics**: Monthly retention metrics
|
|
37881
39626
|
*
|
|
37882
39627
|
* @returns Analytics hook with methods for data analysis
|
|
37883
39628
|
*
|
|
37884
|
-
* @example
|
|
39629
|
+
* @example Basic Transaction Analytics
|
|
37885
39630
|
* ```typescript
|
|
37886
39631
|
* function AnalyticsComponent() {
|
|
37887
39632
|
* const { getTransactionAnalytics } = useAnalytics();
|
|
37888
39633
|
*
|
|
37889
39634
|
* const loadAnalytics = async () => {
|
|
37890
|
-
*
|
|
37891
|
-
*
|
|
37892
|
-
*
|
|
37893
|
-
*
|
|
37894
|
-
*
|
|
37895
|
-
*
|
|
37896
|
-
* } catch (error) {
|
|
37897
|
-
* console.error('Failed to load analytics:', error);
|
|
37898
|
-
* }
|
|
39635
|
+
* const analytics = await getTransactionAnalytics({
|
|
39636
|
+
* startDate: '2024-01-01',
|
|
39637
|
+
* endDate: '2024-01-31',
|
|
39638
|
+
* groupBy: 'day'
|
|
39639
|
+
* });
|
|
39640
|
+
* console.log('Transaction analytics:', analytics);
|
|
37899
39641
|
* };
|
|
39642
|
+
* }
|
|
39643
|
+
* ```
|
|
37900
39644
|
*
|
|
37901
|
-
*
|
|
39645
|
+
* @example User Leaderboard
|
|
39646
|
+
* ```typescript
|
|
39647
|
+
* function LeaderboardScreen() {
|
|
39648
|
+
* const { getUserRanking } = useAnalytics();
|
|
39649
|
+
*
|
|
39650
|
+
* const loadLeaderboard = async () => {
|
|
39651
|
+
* const ranking = await getUserRanking({
|
|
39652
|
+
* sortBy: 'totalTransactions',
|
|
39653
|
+
* sortOrder: 'DESC',
|
|
39654
|
+
* limit: 50
|
|
39655
|
+
* });
|
|
39656
|
+
* ranking.results.forEach((user, i) => {
|
|
39657
|
+
* console.log(`#${i + 1}: ${user.email} - ${user.totalTransactions} txns`);
|
|
39658
|
+
* });
|
|
39659
|
+
* };
|
|
37902
39660
|
* }
|
|
37903
39661
|
* ```
|
|
37904
39662
|
*/
|
|
@@ -37913,16 +39671,12 @@ const useAnalytics = () => {
|
|
|
37913
39671
|
*
|
|
37914
39672
|
* @example
|
|
37915
39673
|
* ```typescript
|
|
37916
|
-
* const { getTransactionAnalytics } = useAnalytics();
|
|
37917
39674
|
* const analytics = await getTransactionAnalytics({
|
|
37918
|
-
* groupBy: ['day'],
|
|
37919
|
-
* metrics: ['count', 'sum'],
|
|
37920
39675
|
* startDate: '2024-01-01',
|
|
37921
39676
|
* endDate: '2024-01-31',
|
|
37922
|
-
*
|
|
39677
|
+
* groupBy: 'day',
|
|
39678
|
+
* metrics: ['count', 'sum']
|
|
37923
39679
|
* });
|
|
37924
|
-
* console.log('Daily transaction analytics:', analytics.results);
|
|
37925
|
-
* console.log('Execution time:', analytics.metadata.executionTime);
|
|
37926
39680
|
* ```
|
|
37927
39681
|
*/
|
|
37928
39682
|
const getTransactionAnalytics = react.useCallback(async (request) => {
|
|
@@ -37938,8 +39692,214 @@ const useAnalytics = () => {
|
|
|
37938
39692
|
throw error;
|
|
37939
39693
|
}
|
|
37940
39694
|
}, [sdk, isInitialized]);
|
|
39695
|
+
/**
|
|
39696
|
+
* Retrieves campaign claim analytics with aggregation
|
|
39697
|
+
*
|
|
39698
|
+
* Provides insights into campaign performance, claim patterns, and user engagement.
|
|
39699
|
+
*
|
|
39700
|
+
* @param request - Analytics request with filters, groupBy, and metrics
|
|
39701
|
+
* @returns Promise resolving to campaign claim analytics data
|
|
39702
|
+
*
|
|
39703
|
+
* @example Claims per campaign
|
|
39704
|
+
* ```typescript
|
|
39705
|
+
* const analytics = await getCampaignClaimAnalytics({
|
|
39706
|
+
* filters: { status: 'COMPLETED' },
|
|
39707
|
+
* groupBy: ['campaignId'],
|
|
39708
|
+
* metrics: ['count'],
|
|
39709
|
+
* sortBy: 'count',
|
|
39710
|
+
* sortOrder: 'DESC',
|
|
39711
|
+
* limit: 10
|
|
39712
|
+
* });
|
|
39713
|
+
* console.log('Top campaigns:', analytics.results);
|
|
39714
|
+
* ```
|
|
39715
|
+
*/
|
|
39716
|
+
const getCampaignClaimAnalytics = react.useCallback(async (request) => {
|
|
39717
|
+
if (!isInitialized || !sdk) {
|
|
39718
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
39719
|
+
}
|
|
39720
|
+
try {
|
|
39721
|
+
const result = await sdk.analytics.getCampaignClaimAnalytics(request);
|
|
39722
|
+
return result;
|
|
39723
|
+
}
|
|
39724
|
+
catch (error) {
|
|
39725
|
+
console.error('Failed to fetch campaign claim analytics:', error);
|
|
39726
|
+
throw error;
|
|
39727
|
+
}
|
|
39728
|
+
}, [sdk, isInitialized]);
|
|
39729
|
+
/**
|
|
39730
|
+
* Retrieves user analytics with engagement metrics
|
|
39731
|
+
*
|
|
39732
|
+
* Includes both per-user and per-active-user metrics for accurate engagement insights.
|
|
39733
|
+
* Per-active-user metrics show concentrated engagement among active users.
|
|
39734
|
+
*
|
|
39735
|
+
* @param request - Analytics request with optional filters and date range
|
|
39736
|
+
* @returns Promise resolving to user analytics data
|
|
39737
|
+
*
|
|
39738
|
+
* @example Compare per-user vs per-active-user metrics
|
|
39739
|
+
* ```typescript
|
|
39740
|
+
* const analytics = await getUserAnalytics({
|
|
39741
|
+
* startDate: new Date('2026-02-01'),
|
|
39742
|
+
* endDate: new Date('2026-02-28')
|
|
39743
|
+
* });
|
|
39744
|
+
*
|
|
39745
|
+
* console.log(`Active users: ${analytics.activeUsers} / ${analytics.totalUsers}`);
|
|
39746
|
+
* console.log(`Engagement rate: ${analytics.engagementRate.toFixed(1)}%`);
|
|
39747
|
+
*
|
|
39748
|
+
* // Per-active-user metrics (more meaningful)
|
|
39749
|
+
* console.log(`Avg transactions per active user: ${analytics.averageTransactionsPerActiveUser}`);
|
|
39750
|
+
* ```
|
|
39751
|
+
*
|
|
39752
|
+
* @example Business-specific analytics
|
|
39753
|
+
* ```typescript
|
|
39754
|
+
* const analytics = await getUserAnalytics({
|
|
39755
|
+
* filters: { businessId: 'business-123' },
|
|
39756
|
+
* startDate: new Date('2026-01-01'),
|
|
39757
|
+
* endDate: new Date('2026-12-31')
|
|
39758
|
+
* });
|
|
39759
|
+
* ```
|
|
39760
|
+
*/
|
|
39761
|
+
const getUserAnalytics = react.useCallback(async (request = {}) => {
|
|
39762
|
+
if (!isInitialized || !sdk) {
|
|
39763
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
39764
|
+
}
|
|
39765
|
+
try {
|
|
39766
|
+
const result = await sdk.analytics.getUserAnalytics(request);
|
|
39767
|
+
return result;
|
|
39768
|
+
}
|
|
39769
|
+
catch (error) {
|
|
39770
|
+
console.error('Failed to fetch user analytics:', error);
|
|
39771
|
+
throw error;
|
|
39772
|
+
}
|
|
39773
|
+
}, [sdk, isInitialized]);
|
|
39774
|
+
/**
|
|
39775
|
+
* Retrieves user transaction ranking with enriched user data
|
|
39776
|
+
*
|
|
39777
|
+
* Returns ranked list of users with full user details (email, externalUserId)
|
|
39778
|
+
* and transaction metrics. Ideal for leaderboards, engagement analysis,
|
|
39779
|
+
* and identifying power users.
|
|
39780
|
+
*
|
|
39781
|
+
* @param request - Ranking request with filters, sorting, and limit
|
|
39782
|
+
* @returns Promise resolving to ranked user list with transaction metrics
|
|
39783
|
+
*
|
|
39784
|
+
* @example Top 50 users by transaction count
|
|
39785
|
+
* ```typescript
|
|
39786
|
+
* const ranking = await getUserRanking({
|
|
39787
|
+
* sortBy: 'totalTransactions',
|
|
39788
|
+
* sortOrder: 'DESC',
|
|
39789
|
+
* limit: 50
|
|
39790
|
+
* });
|
|
39791
|
+
*
|
|
39792
|
+
* ranking.results.forEach((user, index) => {
|
|
39793
|
+
* console.log(`#${index + 1}: ${user.email || user.externalUserId}`);
|
|
39794
|
+
* console.log(` Transactions: ${user.totalTransactions}`);
|
|
39795
|
+
* console.log(` Token spent: ${user.tokenSpent}`);
|
|
39796
|
+
* });
|
|
39797
|
+
* ```
|
|
39798
|
+
*
|
|
39799
|
+
* @example Top STAMP spenders
|
|
39800
|
+
* ```typescript
|
|
39801
|
+
* const ranking = await getUserRanking({
|
|
39802
|
+
* filters: { tokenType: 'STAMP' },
|
|
39803
|
+
* sortBy: 'tokenSpent',
|
|
39804
|
+
* sortOrder: 'DESC',
|
|
39805
|
+
* limit: 20
|
|
39806
|
+
* });
|
|
39807
|
+
* ```
|
|
39808
|
+
*/
|
|
39809
|
+
const getUserRanking = react.useCallback(async (request = {}) => {
|
|
39810
|
+
if (!isInitialized || !sdk) {
|
|
39811
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
39812
|
+
}
|
|
39813
|
+
try {
|
|
39814
|
+
const result = await sdk.analytics.getUserRanking(request);
|
|
39815
|
+
return result;
|
|
39816
|
+
}
|
|
39817
|
+
catch (error) {
|
|
39818
|
+
console.error('Failed to fetch user ranking:', error);
|
|
39819
|
+
throw error;
|
|
39820
|
+
}
|
|
39821
|
+
}, [sdk, isInitialized]);
|
|
39822
|
+
/**
|
|
39823
|
+
* Retrieves business transaction ranking with enriched business data
|
|
39824
|
+
*
|
|
39825
|
+
* Returns ranked list of businesses with transaction metrics for
|
|
39826
|
+
* partner analytics and performance dashboards.
|
|
39827
|
+
*
|
|
39828
|
+
* @param request - Ranking request with filters, sorting, and limit
|
|
39829
|
+
* @returns Promise resolving to ranked business list
|
|
39830
|
+
*
|
|
39831
|
+
* @example Top businesses by transaction count
|
|
39832
|
+
* ```typescript
|
|
39833
|
+
* const ranking = await getBusinessRanking({
|
|
39834
|
+
* sortBy: 'totalTransactions',
|
|
39835
|
+
* sortOrder: 'DESC',
|
|
39836
|
+
* limit: 20
|
|
39837
|
+
* });
|
|
39838
|
+
*
|
|
39839
|
+
* ranking.results.forEach((business, index) => {
|
|
39840
|
+
* console.log(`#${index + 1}: ${business.businessId}`);
|
|
39841
|
+
* console.log(` Transactions: ${business.totalTransactions}`);
|
|
39842
|
+
* console.log(` Token spent: ${business.tokenSpent}`);
|
|
39843
|
+
* });
|
|
39844
|
+
* ```
|
|
39845
|
+
*/
|
|
39846
|
+
const getBusinessRanking = react.useCallback(async (request = {}) => {
|
|
39847
|
+
if (!isInitialized || !sdk) {
|
|
39848
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
39849
|
+
}
|
|
39850
|
+
try {
|
|
39851
|
+
const result = await sdk.analytics.getBusinessRanking(request);
|
|
39852
|
+
return result;
|
|
39853
|
+
}
|
|
39854
|
+
catch (error) {
|
|
39855
|
+
console.error('Failed to fetch business ranking:', error);
|
|
39856
|
+
throw error;
|
|
39857
|
+
}
|
|
39858
|
+
}, [sdk, isInitialized]);
|
|
39859
|
+
/**
|
|
39860
|
+
* Retrieves monthly user retention analytics
|
|
39861
|
+
*
|
|
39862
|
+
* Returns monthly retention data with active, new, and returning users
|
|
39863
|
+
* along with retention rates. Useful for churn analysis and engagement trends.
|
|
39864
|
+
*
|
|
39865
|
+
* @param request - Retention request with monthsBack and filters
|
|
39866
|
+
* @returns Promise resolving to monthly retention data
|
|
39867
|
+
*
|
|
39868
|
+
* @example Get 12 months of retention data
|
|
39869
|
+
* ```typescript
|
|
39870
|
+
* const retention = await getRetentionAnalytics({
|
|
39871
|
+
* monthsBack: 12
|
|
39872
|
+
* });
|
|
39873
|
+
*
|
|
39874
|
+
* retention.results.forEach(month => {
|
|
39875
|
+
* console.log(`${month.month}:`);
|
|
39876
|
+
* console.log(` Active: ${month.activeUsers}`);
|
|
39877
|
+
* console.log(` New: ${month.newUsers}`);
|
|
39878
|
+
* console.log(` Returning: ${month.returningUsers}`);
|
|
39879
|
+
* console.log(` Retention Rate: ${month.retentionRate.toFixed(1)}%`);
|
|
39880
|
+
* });
|
|
39881
|
+
* ```
|
|
39882
|
+
*/
|
|
39883
|
+
const getRetentionAnalytics = react.useCallback(async (request = {}) => {
|
|
39884
|
+
if (!isInitialized || !sdk) {
|
|
39885
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
39886
|
+
}
|
|
39887
|
+
try {
|
|
39888
|
+
const result = await sdk.analytics.getRetentionAnalytics(request);
|
|
39889
|
+
return result;
|
|
39890
|
+
}
|
|
39891
|
+
catch (error) {
|
|
39892
|
+
console.error('Failed to fetch retention analytics:', error);
|
|
39893
|
+
throw error;
|
|
39894
|
+
}
|
|
39895
|
+
}, [sdk, isInitialized]);
|
|
37941
39896
|
return {
|
|
37942
39897
|
getTransactionAnalytics,
|
|
39898
|
+
getCampaignClaimAnalytics,
|
|
39899
|
+
getUserAnalytics,
|
|
39900
|
+
getUserRanking,
|
|
39901
|
+
getBusinessRanking,
|
|
39902
|
+
getRetentionAnalytics,
|
|
37943
39903
|
isAvailable: isInitialized && !!sdk?.analytics,
|
|
37944
39904
|
};
|
|
37945
39905
|
};
|
|
@@ -38315,6 +40275,277 @@ const useEvents = () => {
|
|
|
38315
40275
|
};
|
|
38316
40276
|
};
|
|
38317
40277
|
|
|
40278
|
+
/**
|
|
40279
|
+
* React hook for TriggerSource operations in the PERS SDK
|
|
40280
|
+
*
|
|
40281
|
+
* Manages trigger sources which are physical or digital activation points for campaigns:
|
|
40282
|
+
* - **QR_CODE**: Scannable QR codes at physical locations
|
|
40283
|
+
* - **NFC_TAG**: NFC tap points for contactless interactions
|
|
40284
|
+
* - **GPS_GEOFENCE**: Location-based triggers with radius detection
|
|
40285
|
+
* - **API_WEBHOOK**: External system integration triggers
|
|
40286
|
+
* - **TRANSACTION**: Purchase/payment based triggers
|
|
40287
|
+
*
|
|
40288
|
+
* TriggerSources are standalone entities that can be created, managed, and then
|
|
40289
|
+
* assigned to campaigns. This separation allows reuse across multiple campaigns.
|
|
40290
|
+
*
|
|
40291
|
+
* **Admin Only**: All create, update, and delete operations require admin authentication.
|
|
40292
|
+
*
|
|
40293
|
+
* @returns TriggerSource hook with CRUD operations
|
|
40294
|
+
*
|
|
40295
|
+
* @example Basic TriggerSource Operations
|
|
40296
|
+
* ```typescript
|
|
40297
|
+
* function TriggerSourceManager() {
|
|
40298
|
+
* const {
|
|
40299
|
+
* getAll,
|
|
40300
|
+
* getById,
|
|
40301
|
+
* create,
|
|
40302
|
+
* update,
|
|
40303
|
+
* remove
|
|
40304
|
+
* } = useTriggerSources();
|
|
40305
|
+
*
|
|
40306
|
+
* // List all QR code trigger sources
|
|
40307
|
+
* const loadQRSources = async () => {
|
|
40308
|
+
* const { data: sources } = await getAll({ type: 'QR_CODE' });
|
|
40309
|
+
* console.log('QR Sources:', sources);
|
|
40310
|
+
* };
|
|
40311
|
+
*
|
|
40312
|
+
* // Create a new QR code for a store
|
|
40313
|
+
* const createStoreQR = async () => {
|
|
40314
|
+
* const source = await create({
|
|
40315
|
+
* type: 'QR_CODE',
|
|
40316
|
+
* name: 'Store Entrance QR',
|
|
40317
|
+
* description: 'Scan at the entrance',
|
|
40318
|
+
* businessId: 'business-123',
|
|
40319
|
+
* coordsLatitude: 47.6062,
|
|
40320
|
+
* coordsLongitude: -122.3321
|
|
40321
|
+
* });
|
|
40322
|
+
* console.log('Created:', source.id);
|
|
40323
|
+
* };
|
|
40324
|
+
* }
|
|
40325
|
+
* ```
|
|
40326
|
+
*
|
|
40327
|
+
* @example GPS Geofence Trigger
|
|
40328
|
+
* ```typescript
|
|
40329
|
+
* const { create } = useTriggerSources();
|
|
40330
|
+
*
|
|
40331
|
+
* // Create a GPS geofence around a location
|
|
40332
|
+
* const geofence = await create({
|
|
40333
|
+
* type: 'GPS_GEOFENCE',
|
|
40334
|
+
* name: 'Downtown Area',
|
|
40335
|
+
* coordsLatitude: 47.6062,
|
|
40336
|
+
* coordsLongitude: -122.3321,
|
|
40337
|
+
* metadata: { radiusMeters: 100 }
|
|
40338
|
+
* });
|
|
40339
|
+
* ```
|
|
40340
|
+
*/
|
|
40341
|
+
const useTriggerSources = () => {
|
|
40342
|
+
const { sdk, isInitialized, isAuthenticated } = usePersSDK();
|
|
40343
|
+
/**
|
|
40344
|
+
* Get trigger sources with optional filters and pagination
|
|
40345
|
+
*
|
|
40346
|
+
* Retrieves trigger sources (QR codes, NFC tags, GPS geofences, webhooks, etc.)
|
|
40347
|
+
* that can be used to activate campaigns. Supports filtering by type, business,
|
|
40348
|
+
* campaign association, and active status.
|
|
40349
|
+
*
|
|
40350
|
+
* @param options - Filter and pagination options
|
|
40351
|
+
* @returns Promise resolving to paginated trigger sources
|
|
40352
|
+
*
|
|
40353
|
+
* @example
|
|
40354
|
+
* ```typescript
|
|
40355
|
+
* // Get all QR code trigger sources
|
|
40356
|
+
* const { data: qrSources } = await getAll({ type: 'QR_CODE' });
|
|
40357
|
+
*
|
|
40358
|
+
* // Get trigger sources for a specific business
|
|
40359
|
+
* const { data: businessSources, pagination } = await getAll({
|
|
40360
|
+
* businessId: 'business-123',
|
|
40361
|
+
* active: true,
|
|
40362
|
+
* page: 1,
|
|
40363
|
+
* limit: 20
|
|
40364
|
+
* });
|
|
40365
|
+
*
|
|
40366
|
+
* // Get trigger sources assigned to a campaign
|
|
40367
|
+
* const { data: campaignSources } = await getAll({
|
|
40368
|
+
* campaignId: 'campaign-456'
|
|
40369
|
+
* });
|
|
40370
|
+
* ```
|
|
40371
|
+
*/
|
|
40372
|
+
const getAll = react.useCallback(async (options) => {
|
|
40373
|
+
if (!isInitialized || !sdk) {
|
|
40374
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
40375
|
+
}
|
|
40376
|
+
try {
|
|
40377
|
+
const result = await sdk.triggerSources.getAll(options);
|
|
40378
|
+
return result;
|
|
40379
|
+
}
|
|
40380
|
+
catch (error) {
|
|
40381
|
+
console.error('Failed to fetch trigger sources:', error);
|
|
40382
|
+
throw error;
|
|
40383
|
+
}
|
|
40384
|
+
}, [sdk, isInitialized]);
|
|
40385
|
+
/**
|
|
40386
|
+
* Get trigger source by ID
|
|
40387
|
+
*
|
|
40388
|
+
* Retrieves detailed information for a specific trigger source including
|
|
40389
|
+
* its type, location, metadata, and configuration.
|
|
40390
|
+
*
|
|
40391
|
+
* @param triggerSourceId - UUID of the trigger source
|
|
40392
|
+
* @returns Promise resolving to trigger source details
|
|
40393
|
+
* @throws {PersApiError} When trigger source with specified ID is not found
|
|
40394
|
+
*
|
|
40395
|
+
* @example
|
|
40396
|
+
* ```typescript
|
|
40397
|
+
* const source = await getById('source-123');
|
|
40398
|
+
*
|
|
40399
|
+
* console.log('Trigger Source:', source.name);
|
|
40400
|
+
* console.log('Type:', source.type);
|
|
40401
|
+
* console.log('Active:', source.isActive);
|
|
40402
|
+
*
|
|
40403
|
+
* if (source.coordsLatitude && source.coordsLongitude) {
|
|
40404
|
+
* console.log('Location:', source.coordsLatitude, source.coordsLongitude);
|
|
40405
|
+
* }
|
|
40406
|
+
* ```
|
|
40407
|
+
*/
|
|
40408
|
+
const getById = react.useCallback(async (triggerSourceId) => {
|
|
40409
|
+
if (!isInitialized || !sdk) {
|
|
40410
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
40411
|
+
}
|
|
40412
|
+
try {
|
|
40413
|
+
const result = await sdk.triggerSources.getById(triggerSourceId);
|
|
40414
|
+
return result;
|
|
40415
|
+
}
|
|
40416
|
+
catch (error) {
|
|
40417
|
+
console.error('Failed to fetch trigger source:', error);
|
|
40418
|
+
throw error;
|
|
40419
|
+
}
|
|
40420
|
+
}, [sdk, isInitialized]);
|
|
40421
|
+
/**
|
|
40422
|
+
* Admin: Create a new trigger source
|
|
40423
|
+
*
|
|
40424
|
+
* Creates a new trigger source (QR code, NFC tag, GPS geofence, webhook, or
|
|
40425
|
+
* transaction-based trigger) that can be assigned to campaigns. Requires
|
|
40426
|
+
* administrator privileges.
|
|
40427
|
+
*
|
|
40428
|
+
* @param triggerSource - Trigger source creation data
|
|
40429
|
+
* @returns Promise resolving to created trigger source
|
|
40430
|
+
* @throws {PersApiError} When not authenticated as admin or validation fails
|
|
40431
|
+
*
|
|
40432
|
+
* @example
|
|
40433
|
+
* ```typescript
|
|
40434
|
+
* // Create a QR code trigger source
|
|
40435
|
+
* const qrSource = await create({
|
|
40436
|
+
* type: 'QR_CODE',
|
|
40437
|
+
* name: 'Store Entrance QR',
|
|
40438
|
+
* description: 'QR code at main entrance',
|
|
40439
|
+
* businessId: 'business-123',
|
|
40440
|
+
* coordsLatitude: 47.6062,
|
|
40441
|
+
* coordsLongitude: -122.3321
|
|
40442
|
+
* });
|
|
40443
|
+
*
|
|
40444
|
+
* // Create an NFC tag trigger source
|
|
40445
|
+
* const nfcSource = await create({
|
|
40446
|
+
* type: 'NFC_TAG',
|
|
40447
|
+
* name: 'Product Display NFC',
|
|
40448
|
+
* metadata: { productId: 'SKU-12345' }
|
|
40449
|
+
* });
|
|
40450
|
+
* ```
|
|
40451
|
+
*/
|
|
40452
|
+
const create = react.useCallback(async (triggerSource) => {
|
|
40453
|
+
if (!isInitialized || !sdk) {
|
|
40454
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
40455
|
+
}
|
|
40456
|
+
if (!isAuthenticated) {
|
|
40457
|
+
throw new Error('SDK not authenticated. create requires admin authentication.');
|
|
40458
|
+
}
|
|
40459
|
+
try {
|
|
40460
|
+
const result = await sdk.triggerSources.create(triggerSource);
|
|
40461
|
+
return result;
|
|
40462
|
+
}
|
|
40463
|
+
catch (error) {
|
|
40464
|
+
console.error('Failed to create trigger source:', error);
|
|
40465
|
+
throw error;
|
|
40466
|
+
}
|
|
40467
|
+
}, [sdk, isInitialized, isAuthenticated]);
|
|
40468
|
+
/**
|
|
40469
|
+
* Admin: Update a trigger source
|
|
40470
|
+
*
|
|
40471
|
+
* Updates an existing trigger source's configuration. All fields are optional;
|
|
40472
|
+
* only provided fields will be updated. Requires administrator privileges.
|
|
40473
|
+
*
|
|
40474
|
+
* @param triggerSourceId - UUID of the trigger source to update
|
|
40475
|
+
* @param triggerSource - Updated trigger source data (partial)
|
|
40476
|
+
* @returns Promise resolving to updated trigger source
|
|
40477
|
+
* @throws {PersApiError} When not authenticated as admin or trigger source not found
|
|
40478
|
+
*
|
|
40479
|
+
* @example
|
|
40480
|
+
* ```typescript
|
|
40481
|
+
* // Update trigger source name and location
|
|
40482
|
+
* const updated = await update('source-123', {
|
|
40483
|
+
* name: 'Updated Store Entrance QR',
|
|
40484
|
+
* description: 'New description',
|
|
40485
|
+
* coordsLatitude: 47.6063,
|
|
40486
|
+
* coordsLongitude: -122.3322
|
|
40487
|
+
* });
|
|
40488
|
+
* ```
|
|
40489
|
+
*/
|
|
40490
|
+
const update = react.useCallback(async (triggerSourceId, triggerSource) => {
|
|
40491
|
+
if (!isInitialized || !sdk) {
|
|
40492
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
40493
|
+
}
|
|
40494
|
+
if (!isAuthenticated) {
|
|
40495
|
+
throw new Error('SDK not authenticated. update requires admin authentication.');
|
|
40496
|
+
}
|
|
40497
|
+
try {
|
|
40498
|
+
const result = await sdk.triggerSources.update(triggerSourceId, triggerSource);
|
|
40499
|
+
return result;
|
|
40500
|
+
}
|
|
40501
|
+
catch (error) {
|
|
40502
|
+
console.error('Failed to update trigger source:', error);
|
|
40503
|
+
throw error;
|
|
40504
|
+
}
|
|
40505
|
+
}, [sdk, isInitialized, isAuthenticated]);
|
|
40506
|
+
/**
|
|
40507
|
+
* Admin: Delete a trigger source
|
|
40508
|
+
*
|
|
40509
|
+
* Soft deletes a trigger source, making it inactive. The trigger source will
|
|
40510
|
+
* be removed from any campaigns it was assigned to. Requires administrator
|
|
40511
|
+
* privileges.
|
|
40512
|
+
*
|
|
40513
|
+
* @param triggerSourceId - UUID of the trigger source to delete
|
|
40514
|
+
* @returns Promise resolving to success status
|
|
40515
|
+
* @throws {PersApiError} When not authenticated as admin or trigger source not found
|
|
40516
|
+
*
|
|
40517
|
+
* @example
|
|
40518
|
+
* ```typescript
|
|
40519
|
+
* const success = await remove('source-123');
|
|
40520
|
+
* console.log('Trigger source deleted:', success);
|
|
40521
|
+
* ```
|
|
40522
|
+
*/
|
|
40523
|
+
const remove = react.useCallback(async (triggerSourceId) => {
|
|
40524
|
+
if (!isInitialized || !sdk) {
|
|
40525
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
40526
|
+
}
|
|
40527
|
+
if (!isAuthenticated) {
|
|
40528
|
+
throw new Error('SDK not authenticated. remove requires admin authentication.');
|
|
40529
|
+
}
|
|
40530
|
+
try {
|
|
40531
|
+
const result = await sdk.triggerSources.delete(triggerSourceId);
|
|
40532
|
+
return result;
|
|
40533
|
+
}
|
|
40534
|
+
catch (error) {
|
|
40535
|
+
console.error('Failed to delete trigger source:', error);
|
|
40536
|
+
throw error;
|
|
40537
|
+
}
|
|
40538
|
+
}, [sdk, isInitialized, isAuthenticated]);
|
|
40539
|
+
return {
|
|
40540
|
+
getAll,
|
|
40541
|
+
getById,
|
|
40542
|
+
create,
|
|
40543
|
+
update,
|
|
40544
|
+
remove,
|
|
40545
|
+
isAvailable: isInitialized && !!sdk?.triggerSources,
|
|
40546
|
+
};
|
|
40547
|
+
};
|
|
40548
|
+
|
|
38318
40549
|
// ==========================================
|
|
38319
40550
|
// TOKEN UTILITY FUNCTIONS
|
|
38320
40551
|
// ==========================================
|
|
@@ -38396,12 +40627,22 @@ exports.MEMBERSHIP_ROLE_HIERARCHY = MEMBERSHIP_ROLE_HIERARCHY;
|
|
|
38396
40627
|
exports.NativeTokenTypes = NativeTokenTypes;
|
|
38397
40628
|
exports.PersApiError = PersApiError;
|
|
38398
40629
|
exports.PersSDKProvider = PersSDKProvider;
|
|
40630
|
+
exports.QUERY_OPERATOR_SQL_MAP = QUERY_OPERATOR_SQL_MAP;
|
|
38399
40631
|
exports.ReactNativeDPoPProvider = ReactNativeDPoPProvider;
|
|
38400
40632
|
exports.ReactNativeHttpClient = ReactNativeHttpClient;
|
|
38401
40633
|
exports.ReactNativeSecureStorage = ReactNativeSecureStorage;
|
|
40634
|
+
exports.SOURCE_LOGIC_TYPES = SOURCE_LOGIC_TYPES;
|
|
40635
|
+
exports.SOURCE_LOGIC_TYPE_VALUES = SOURCE_LOGIC_TYPE_VALUES;
|
|
38402
40636
|
exports.SigningStatus = SigningStatus;
|
|
38403
40637
|
exports.TRANSACTION_FORMATS = TRANSACTION_FORMATS;
|
|
38404
40638
|
exports.TRANSACTION_FORMAT_DESCRIPTIONS = TRANSACTION_FORMAT_DESCRIPTIONS;
|
|
40639
|
+
exports.TRIGGER_SOURCE_TYPES = TRIGGER_SOURCE_TYPES;
|
|
40640
|
+
exports.TRIGGER_SOURCE_TYPE_VALUES = TRIGGER_SOURCE_TYPE_VALUES;
|
|
40641
|
+
exports.VALID_CAMPAIGN_CLAIM_RELATIONS = VALID_CAMPAIGN_CLAIM_RELATIONS;
|
|
40642
|
+
exports.VALID_CAMPAIGN_RELATIONS = VALID_CAMPAIGN_RELATIONS;
|
|
40643
|
+
exports.VALID_REDEMPTION_REDEEM_RELATIONS = VALID_REDEMPTION_REDEEM_RELATIONS;
|
|
40644
|
+
exports.VALID_RELATIONS = VALID_RELATIONS;
|
|
40645
|
+
exports.VALID_TRANSACTION_RELATIONS = VALID_TRANSACTION_RELATIONS;
|
|
38405
40646
|
exports.apiPublicKeyTestPrefix = apiPublicKeyTestPrefix;
|
|
38406
40647
|
exports.buildBurnRequest = buildBurnRequest;
|
|
38407
40648
|
exports.buildMintRequest = buildMintRequest;
|
|
@@ -38423,6 +40664,11 @@ exports.hasMinimumRole = hasMinimumRole;
|
|
|
38423
40664
|
exports.initializeReactNativePolyfills = initializeReactNativePolyfills;
|
|
38424
40665
|
exports.isPaginatedResponse = isPaginatedResponse;
|
|
38425
40666
|
exports.isUserIdentifierObject = isUserIdentifierObject;
|
|
40667
|
+
exports.isValidCampaignClaimRelation = isValidCampaignClaimRelation;
|
|
40668
|
+
exports.isValidCampaignRelation = isValidCampaignRelation;
|
|
40669
|
+
exports.isValidRedemptionRedeemRelation = isValidRedemptionRedeemRelation;
|
|
40670
|
+
exports.isValidRelation = isValidRelation;
|
|
40671
|
+
exports.isValidTransactionRelation = isValidTransactionRelation;
|
|
38426
40672
|
exports.normalizeToPaginated = normalizeToPaginated;
|
|
38427
40673
|
exports.registerIdentifierType = registerIdentifierType;
|
|
38428
40674
|
exports.testnetPrefix = testnetPrefix;
|
|
@@ -38443,6 +40689,7 @@ exports.useTokenBalances = useTokenBalances;
|
|
|
38443
40689
|
exports.useTokens = useTokens;
|
|
38444
40690
|
exports.useTransactionSigner = useTransactionSigner;
|
|
38445
40691
|
exports.useTransactions = useTransactions;
|
|
40692
|
+
exports.useTriggerSources = useTriggerSources;
|
|
38446
40693
|
exports.useUserStatus = useUserStatus;
|
|
38447
40694
|
exports.useUsers = useUsers;
|
|
38448
40695
|
exports.useWeb3 = useWeb3;
|