@explorins/pers-sdk-react-native 2.3.0-alpha.3 → 2.3.3
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 +84 -0
- package/dist/hooks/useRedemptions.d.ts +7 -3
- package/dist/hooks/useRedemptions.d.ts.map +1 -1
- package/dist/hooks/useRedemptions.js +7 -1
- package/dist/hooks/useTokens.d.ts +2 -2
- package/dist/hooks/useTokens.d.ts.map +1 -1
- package/dist/index.d.ts +158 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1178 -72
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +17 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/platform-detection.d.ts +35 -0
- package/dist/utils/platform-detection.d.ts.map +1 -0
- package/dist/utils/platform-detection.js +79 -0
- package/package.json +7 -3
- package/src/hooks/useRedemptions.ts +16 -5
- package/src/index.ts +207 -1
- package/src/providers/PersSDKProvider.tsx +19 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/platform-detection.ts +86 -0
package/dist/index.js
CHANGED
|
@@ -2916,6 +2916,18 @@ exports.BalanceMigrationType = void 0;
|
|
|
2916
2916
|
BalanceMigrationType["MERGE"] = "merge";
|
|
2917
2917
|
})(exports.BalanceMigrationType || (exports.BalanceMigrationType = {}));
|
|
2918
2918
|
|
|
2919
|
+
/**
|
|
2920
|
+
* Entity types that support tags for aggregation
|
|
2921
|
+
*/
|
|
2922
|
+
exports.TaggableEntityType = void 0;
|
|
2923
|
+
(function (TaggableEntityType) {
|
|
2924
|
+
TaggableEntityType["CAMPAIGN"] = "campaign";
|
|
2925
|
+
TaggableEntityType["REDEMPTION"] = "redemption";
|
|
2926
|
+
TaggableEntityType["BUSINESS"] = "business";
|
|
2927
|
+
TaggableEntityType["TOKEN_METADATA"] = "token_metadata";
|
|
2928
|
+
TaggableEntityType["USER_STATUS_TYPE"] = "user_status_type";
|
|
2929
|
+
})(exports.TaggableEntityType || (exports.TaggableEntityType = {}));
|
|
2930
|
+
|
|
2919
2931
|
exports.PurchaseStatus = void 0;
|
|
2920
2932
|
(function (PurchaseStatus) {
|
|
2921
2933
|
// after creation of payment and before payment is done by user
|
|
@@ -3764,6 +3776,24 @@ const customFieldEntityTypes = [...customFieldEntityTypesArray];
|
|
|
3764
3776
|
const selectOptionsSourceEntityArray = [...customFieldEntityTypesArray, 'country'];
|
|
3765
3777
|
/** All supported select options source entities for iteration/validation */
|
|
3766
3778
|
const selectOptionsSourceEntities = [...selectOptionsSourceEntityArray];
|
|
3779
|
+
/**
|
|
3780
|
+
* Default label fields for each entity type.
|
|
3781
|
+
* Used when labelField is not explicitly specified.
|
|
3782
|
+
*/
|
|
3783
|
+
const SELECT_OPTIONS_LABEL_DEFAULTS = {
|
|
3784
|
+
business: 'displayName',
|
|
3785
|
+
campaign: 'name',
|
|
3786
|
+
redemption: 'name',
|
|
3787
|
+
user: 'email', // User has no single 'name' field
|
|
3788
|
+
country: 'label', // Static source - uses 'label' from { value, label }
|
|
3789
|
+
};
|
|
3790
|
+
/**
|
|
3791
|
+
* Get the default label field for a select options entity.
|
|
3792
|
+
* Falls back to 'name' for unknown entities.
|
|
3793
|
+
*/
|
|
3794
|
+
function getSelectOptionsLabelDefault(entity) {
|
|
3795
|
+
return SELECT_OPTIONS_LABEL_DEFAULTS[entity] ?? 'name';
|
|
3796
|
+
}
|
|
3767
3797
|
|
|
3768
3798
|
/**
|
|
3769
3799
|
* Core Entity Types
|
|
@@ -3794,6 +3824,8 @@ const EntityTypes = {
|
|
|
3794
3824
|
// Redemption entities
|
|
3795
3825
|
REDEMPTION: 'redemption',
|
|
3796
3826
|
REDEMPTION_TYPE: 'redemption_type',
|
|
3827
|
+
// Booking entities
|
|
3828
|
+
BOOKING: 'booking',
|
|
3797
3829
|
// Infrastructure entities
|
|
3798
3830
|
CONTRACT: 'contract',
|
|
3799
3831
|
SIGNING_ACCOUNT: 'signing_account',
|
|
@@ -4189,6 +4221,34 @@ const LOCATION_TYPES = [
|
|
|
4189
4221
|
'experience', // Experience package (multi-activity, bundle)
|
|
4190
4222
|
'destination', // Destination/location-based (city pass, area access)
|
|
4191
4223
|
];
|
|
4224
|
+
/**
|
|
4225
|
+
* Check if a booking status satisfies a requirement type.
|
|
4226
|
+
*
|
|
4227
|
+
* @param status - Current booking status ('past', 'active', 'future')
|
|
4228
|
+
* @param requirement - Required booking status for eligibility
|
|
4229
|
+
* @returns true if the status satisfies the requirement
|
|
4230
|
+
*
|
|
4231
|
+
* @example
|
|
4232
|
+
* isBookingStatusValid('active', 'active_future'); // true
|
|
4233
|
+
* isBookingStatusValid('past', 'active_future'); // false
|
|
4234
|
+
* isBookingStatusValid('future', 'any'); // true
|
|
4235
|
+
*/
|
|
4236
|
+
function isBookingStatusValid(status, requirement) {
|
|
4237
|
+
switch (requirement) {
|
|
4238
|
+
case 'active':
|
|
4239
|
+
return status === 'active';
|
|
4240
|
+
case 'future':
|
|
4241
|
+
return status === 'future';
|
|
4242
|
+
case 'past':
|
|
4243
|
+
return status === 'past';
|
|
4244
|
+
case 'active_future':
|
|
4245
|
+
return status === 'active' || status === 'future';
|
|
4246
|
+
case 'any':
|
|
4247
|
+
return true;
|
|
4248
|
+
default:
|
|
4249
|
+
return false;
|
|
4250
|
+
}
|
|
4251
|
+
}
|
|
4192
4252
|
|
|
4193
4253
|
/**
|
|
4194
4254
|
* Unified data source tracking for analytics across all entities.
|
|
@@ -4246,6 +4306,7 @@ const DATA_SOURCE_CHANNELS = [
|
|
|
4246
4306
|
'webhook', // External webhook callback
|
|
4247
4307
|
'sync', // Synchronization from external system
|
|
4248
4308
|
'migration', // Data migration
|
|
4309
|
+
'unknown', // Source couldn't be determined (fallback)
|
|
4249
4310
|
];
|
|
4250
4311
|
/**
|
|
4251
4312
|
* Type guard to check if a value is a valid DataSourceChannel.
|
|
@@ -4254,11 +4315,189 @@ function isDataSourceChannel(value) {
|
|
|
4254
4315
|
return typeof value === 'string' && DATA_SOURCE_CHANNELS.includes(value);
|
|
4255
4316
|
}
|
|
4256
4317
|
/**
|
|
4257
|
-
* Default data source
|
|
4318
|
+
* Default data source when source cannot be determined.
|
|
4319
|
+
* Used as fallback when CLS context is unavailable.
|
|
4320
|
+
*
|
|
4321
|
+
* NOTE: 'unknown' indicates a gap in tracking - should be rare.
|
|
4322
|
+
* If you see many 'unknown' sources, check that DataSourceInterceptor is active.
|
|
4258
4323
|
*/
|
|
4259
4324
|
const DEFAULT_DATA_SOURCE = {
|
|
4260
|
-
channel: '
|
|
4325
|
+
channel: 'unknown',
|
|
4326
|
+
};
|
|
4327
|
+
/**
|
|
4328
|
+
* Known domain mappings for auto-detecting source from referrer.
|
|
4329
|
+
* Maps hostnames to normalized source names.
|
|
4330
|
+
*/
|
|
4331
|
+
const KNOWN_SOURCE_DOMAINS = {
|
|
4332
|
+
// Google
|
|
4333
|
+
'google.com': 'google',
|
|
4334
|
+
'google.co.uk': 'google',
|
|
4335
|
+
'google.de': 'google',
|
|
4336
|
+
'google.fr': 'google',
|
|
4337
|
+
'google.es': 'google',
|
|
4338
|
+
'google.it': 'google',
|
|
4339
|
+
'google.nl': 'google',
|
|
4340
|
+
'google.pt': 'google',
|
|
4341
|
+
'google.pl': 'google',
|
|
4342
|
+
'google.ru': 'google',
|
|
4343
|
+
'google.com.br': 'google',
|
|
4344
|
+
'google.ca': 'google',
|
|
4345
|
+
'google.com.au': 'google',
|
|
4346
|
+
'google.co.jp': 'google',
|
|
4347
|
+
'google.co.in': 'google',
|
|
4348
|
+
// Facebook
|
|
4349
|
+
'facebook.com': 'facebook',
|
|
4350
|
+
'fb.com': 'facebook',
|
|
4351
|
+
'm.facebook.com': 'facebook',
|
|
4352
|
+
'l.facebook.com': 'facebook',
|
|
4353
|
+
'lm.facebook.com': 'facebook',
|
|
4354
|
+
// Instagram
|
|
4355
|
+
'instagram.com': 'instagram',
|
|
4356
|
+
'l.instagram.com': 'instagram',
|
|
4357
|
+
// Twitter/X
|
|
4358
|
+
'twitter.com': 'twitter',
|
|
4359
|
+
'x.com': 'twitter',
|
|
4360
|
+
't.co': 'twitter',
|
|
4361
|
+
'mobile.twitter.com': 'twitter',
|
|
4362
|
+
// LinkedIn
|
|
4363
|
+
'linkedin.com': 'linkedin',
|
|
4364
|
+
'lnkd.in': 'linkedin',
|
|
4365
|
+
// YouTube
|
|
4366
|
+
'youtube.com': 'youtube',
|
|
4367
|
+
'youtu.be': 'youtube',
|
|
4368
|
+
'm.youtube.com': 'youtube',
|
|
4369
|
+
// TikTok
|
|
4370
|
+
'tiktok.com': 'tiktok',
|
|
4371
|
+
'vm.tiktok.com': 'tiktok',
|
|
4372
|
+
// Other social
|
|
4373
|
+
'pinterest.com': 'pinterest',
|
|
4374
|
+
'reddit.com': 'reddit',
|
|
4375
|
+
'tumblr.com': 'tumblr',
|
|
4376
|
+
'snapchat.com': 'snapchat',
|
|
4377
|
+
// Search engines
|
|
4378
|
+
'bing.com': 'bing',
|
|
4379
|
+
'yahoo.com': 'yahoo',
|
|
4380
|
+
'duckduckgo.com': 'duckduckgo',
|
|
4381
|
+
'baidu.com': 'baidu',
|
|
4382
|
+
'yandex.com': 'yandex',
|
|
4383
|
+
'yandex.ru': 'yandex',
|
|
4384
|
+
// Email providers (for tracking email clicks)
|
|
4385
|
+
'mail.google.com': 'gmail',
|
|
4386
|
+
'outlook.live.com': 'outlook',
|
|
4387
|
+
'outlook.office.com': 'outlook',
|
|
4261
4388
|
};
|
|
4389
|
+
/**
|
|
4390
|
+
* Auto-detect source from referrer URL domain.
|
|
4391
|
+
* Maps common domains to friendly source names.
|
|
4392
|
+
*
|
|
4393
|
+
* @param referrer - Full referrer URL
|
|
4394
|
+
* @returns Normalized source name or undefined
|
|
4395
|
+
*
|
|
4396
|
+
* @example
|
|
4397
|
+
* detectSourceFromReferrer('https://www.google.com/search?q=test') // 'google'
|
|
4398
|
+
* detectSourceFromReferrer('https://l.facebook.com/...') // 'facebook'
|
|
4399
|
+
* detectSourceFromReferrer('https://partner.example.com') // 'example'
|
|
4400
|
+
*/
|
|
4401
|
+
function detectSourceFromReferrer(referrer) {
|
|
4402
|
+
if (!referrer)
|
|
4403
|
+
return undefined;
|
|
4404
|
+
try {
|
|
4405
|
+
const url = new URL(referrer);
|
|
4406
|
+
const hostname = url.hostname.toLowerCase().replace(/^www\./, '');
|
|
4407
|
+
// Check exact match first
|
|
4408
|
+
if (KNOWN_SOURCE_DOMAINS[hostname]) {
|
|
4409
|
+
return KNOWN_SOURCE_DOMAINS[hostname];
|
|
4410
|
+
}
|
|
4411
|
+
// Check if hostname ends with known domain (for subdomains)
|
|
4412
|
+
for (const [domain, source] of Object.entries(KNOWN_SOURCE_DOMAINS)) {
|
|
4413
|
+
if (hostname.endsWith('.' + domain)) {
|
|
4414
|
+
return source;
|
|
4415
|
+
}
|
|
4416
|
+
}
|
|
4417
|
+
// Fallback: extract main domain name (e.g., 'example' from 'mail.example.com')
|
|
4418
|
+
const parts = hostname.split('.');
|
|
4419
|
+
if (parts.length >= 2) {
|
|
4420
|
+
return parts[parts.length - 2]; // Returns 'example' from 'sub.example.com'
|
|
4421
|
+
}
|
|
4422
|
+
return hostname;
|
|
4423
|
+
}
|
|
4424
|
+
catch {
|
|
4425
|
+
// Invalid URL, return undefined
|
|
4426
|
+
return undefined;
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
/**
|
|
4430
|
+
* Auto-detect channel from User-Agent string.
|
|
4431
|
+
*
|
|
4432
|
+
* @param userAgent - User-Agent header value
|
|
4433
|
+
* @param path - Optional request path for webhook detection
|
|
4434
|
+
* @returns Detected channel
|
|
4435
|
+
*
|
|
4436
|
+
* @example
|
|
4437
|
+
* detectChannelFromUserAgent('Mozilla/5.0 (iPhone; ...)') // 'mobile'
|
|
4438
|
+
* detectChannelFromUserAgent('Mozilla/5.0 (Windows NT...)') // 'web'
|
|
4439
|
+
* detectChannelFromUserAgent('', '/webhook/stripe') // 'webhook'
|
|
4440
|
+
*/
|
|
4441
|
+
function detectChannelFromUserAgent(userAgent, path) {
|
|
4442
|
+
// Check for webhook paths
|
|
4443
|
+
if (path?.includes('/webhook')) {
|
|
4444
|
+
return 'webhook';
|
|
4445
|
+
}
|
|
4446
|
+
const ua = (userAgent || '').toLowerCase();
|
|
4447
|
+
// Check for mobile user-agents
|
|
4448
|
+
if (/mobile|android|iphone|ipad|ipod|blackberry|windows phone/i.test(ua)) {
|
|
4449
|
+
return 'mobile';
|
|
4450
|
+
}
|
|
4451
|
+
// Check for bot/crawler patterns (system)
|
|
4452
|
+
if (/bot|crawler|spider|scraper|curl|wget|python|node-fetch|axios/i.test(ua)) {
|
|
4453
|
+
return 'api';
|
|
4454
|
+
}
|
|
4455
|
+
// Default to web for browser-like user agents
|
|
4456
|
+
return 'web';
|
|
4457
|
+
}
|
|
4458
|
+
/**
|
|
4459
|
+
* Parse UTM parameters from a URL (deep link, referrer, etc.).
|
|
4460
|
+
* Supports standard Google Analytics UTM parameters.
|
|
4461
|
+
*
|
|
4462
|
+
* @param url - Full URL containing UTM query parameters
|
|
4463
|
+
* @returns Parsed UTM parameters object
|
|
4464
|
+
*
|
|
4465
|
+
* @example
|
|
4466
|
+
* // Deep link from email campaign
|
|
4467
|
+
* parseUtmParams('myapp://screen?utm_source=mailchimp&utm_medium=email&utm_campaign=summer_2026')
|
|
4468
|
+
* // Returns: { source: 'mailchimp', medium: 'email', campaign: 'summer_2026' }
|
|
4469
|
+
*
|
|
4470
|
+
* @example
|
|
4471
|
+
* // Web URL with full UTM tracking
|
|
4472
|
+
* parseUtmParams('https://app.example.com?utm_source=google&utm_medium=cpc&utm_campaign=brand&utm_term=loyalty+app')
|
|
4473
|
+
* // Returns: { source: 'google', medium: 'cpc', campaign: 'brand', term: 'loyalty+app' }
|
|
4474
|
+
*/
|
|
4475
|
+
function parseUtmParams(url) {
|
|
4476
|
+
const params = {};
|
|
4477
|
+
try {
|
|
4478
|
+
const urlObj = new URL(url);
|
|
4479
|
+
const searchParams = urlObj.searchParams;
|
|
4480
|
+
const source = searchParams.get('utm_source');
|
|
4481
|
+
const medium = searchParams.get('utm_medium');
|
|
4482
|
+
const campaign = searchParams.get('utm_campaign');
|
|
4483
|
+
const term = searchParams.get('utm_term');
|
|
4484
|
+
const content = searchParams.get('utm_content');
|
|
4485
|
+
if (source)
|
|
4486
|
+
params.source = source;
|
|
4487
|
+
if (medium)
|
|
4488
|
+
params.medium = medium;
|
|
4489
|
+
if (campaign)
|
|
4490
|
+
params.campaign = campaign;
|
|
4491
|
+
if (term)
|
|
4492
|
+
params.term = term;
|
|
4493
|
+
if (content)
|
|
4494
|
+
params.content = content;
|
|
4495
|
+
}
|
|
4496
|
+
catch {
|
|
4497
|
+
// Invalid URL, return empty params
|
|
4498
|
+
}
|
|
4499
|
+
return params;
|
|
4500
|
+
}
|
|
4262
4501
|
|
|
4263
4502
|
/**
|
|
4264
4503
|
* Analytics Constants
|
|
@@ -4513,6 +4752,8 @@ const CommonErrorCodes = {
|
|
|
4513
4752
|
REDEMPTION_ALREADY_PROCESSING: 'REDEMPTION_ALREADY_PROCESSING',
|
|
4514
4753
|
MAX_REDEMPTION: 'MAX_REDEMPTION',
|
|
4515
4754
|
USER_LIMIT: 'USER_LIMIT',
|
|
4755
|
+
REQUIRED_FIELDS_MISSING: 'REQUIRED_FIELDS_MISSING',
|
|
4756
|
+
BOOKING_REQUIREMENT_NOT_MET: 'BOOKING_REQUIREMENT_NOT_MET',
|
|
4516
4757
|
// Authentication errors
|
|
4517
4758
|
INVALID_TOKEN: 'INVALID_TOKEN',
|
|
4518
4759
|
TOKEN_EXPIRED: 'TOKEN_EXPIRED',
|
|
@@ -5488,6 +5729,40 @@ class UserApi {
|
|
|
5488
5729
|
async restoreUser(identifier) {
|
|
5489
5730
|
return this.apiClient.post(`${this.basePath}/${identifier}/restore`);
|
|
5490
5731
|
}
|
|
5732
|
+
// ==========================================
|
|
5733
|
+
// CSV EXPORT
|
|
5734
|
+
// ==========================================
|
|
5735
|
+
/**
|
|
5736
|
+
* ADMIN: Export users to CSV
|
|
5737
|
+
*
|
|
5738
|
+
* Returns a CSV file with user data for the tenant.
|
|
5739
|
+
* Supports date range filtering.
|
|
5740
|
+
*
|
|
5741
|
+
* @param options - Export options (dateFrom, dateTo in ISO format)
|
|
5742
|
+
* @returns CSV blob
|
|
5743
|
+
*
|
|
5744
|
+
* @example
|
|
5745
|
+
* ```typescript
|
|
5746
|
+
* // Export all users
|
|
5747
|
+
* const blob = await sdk.users.exportCSV();
|
|
5748
|
+
*
|
|
5749
|
+
* // Export users created in date range
|
|
5750
|
+
* const blob = await sdk.users.exportCSV({
|
|
5751
|
+
* dateFrom: '2026-01-01',
|
|
5752
|
+
* dateTo: '2026-05-31'
|
|
5753
|
+
* });
|
|
5754
|
+
* ```
|
|
5755
|
+
*/
|
|
5756
|
+
async exportCSV(options) {
|
|
5757
|
+
const params = new URLSearchParams();
|
|
5758
|
+
if (options?.dateFrom)
|
|
5759
|
+
params.append('dateFrom', options.dateFrom);
|
|
5760
|
+
if (options?.dateTo)
|
|
5761
|
+
params.append('dateTo', options.dateTo);
|
|
5762
|
+
const queryString = params.toString();
|
|
5763
|
+
const url = `${this.basePath}/export/csv${queryString ? `?${queryString}` : ''}`;
|
|
5764
|
+
return this.apiClient.get(url, 'blob');
|
|
5765
|
+
}
|
|
5491
5766
|
}
|
|
5492
5767
|
|
|
5493
5768
|
/**
|
|
@@ -7395,6 +7670,47 @@ class CampaignApi {
|
|
|
7395
7670
|
async removeTriggerSourceFromCampaign(campaignId, triggerSourceId) {
|
|
7396
7671
|
return this.apiClient.delete(`/campaigns/${campaignId}/trigger-sources/${triggerSourceId}`);
|
|
7397
7672
|
}
|
|
7673
|
+
// ==========================================
|
|
7674
|
+
// CSV EXPORT
|
|
7675
|
+
// ==========================================
|
|
7676
|
+
/**
|
|
7677
|
+
* ADMIN: Export campaigns to CSV
|
|
7678
|
+
*
|
|
7679
|
+
* Returns a CSV file with campaign data for the tenant.
|
|
7680
|
+
* Supports date range filtering.
|
|
7681
|
+
*
|
|
7682
|
+
* @param options - Export options (dateFrom, dateTo)
|
|
7683
|
+
* @returns CSV blob
|
|
7684
|
+
*/
|
|
7685
|
+
async exportCSV(options) {
|
|
7686
|
+
const params = new URLSearchParams();
|
|
7687
|
+
if (options?.dateFrom)
|
|
7688
|
+
params.append('dateFrom', options.dateFrom);
|
|
7689
|
+
if (options?.dateTo)
|
|
7690
|
+
params.append('dateTo', options.dateTo);
|
|
7691
|
+
const queryString = params.toString();
|
|
7692
|
+
const url = `/campaigns/export/csv${queryString ? `?${queryString}` : ''}`;
|
|
7693
|
+
return this.apiClient.get(url, 'blob');
|
|
7694
|
+
}
|
|
7695
|
+
/**
|
|
7696
|
+
* ADMIN: Export campaign claims to CSV
|
|
7697
|
+
*
|
|
7698
|
+
* Returns a CSV file with all campaign claim data.
|
|
7699
|
+
* Supports date range filtering.
|
|
7700
|
+
*
|
|
7701
|
+
* @param options - Export options (dateFrom, dateTo)
|
|
7702
|
+
* @returns CSV blob
|
|
7703
|
+
*/
|
|
7704
|
+
async exportClaimsCSV(options) {
|
|
7705
|
+
const params = new URLSearchParams();
|
|
7706
|
+
if (options?.dateFrom)
|
|
7707
|
+
params.append('dateFrom', options.dateFrom);
|
|
7708
|
+
if (options?.dateTo)
|
|
7709
|
+
params.append('dateTo', options.dateTo);
|
|
7710
|
+
const queryString = params.toString();
|
|
7711
|
+
const url = `/campaigns/claims/export/csv${queryString ? `?${queryString}` : ''}`;
|
|
7712
|
+
return this.apiClient.get(url, 'blob');
|
|
7713
|
+
}
|
|
7398
7714
|
}
|
|
7399
7715
|
|
|
7400
7716
|
/**
|
|
@@ -7902,6 +8218,47 @@ class RedemptionApi {
|
|
|
7902
8218
|
const result = await this.getRedemptions({ active: true });
|
|
7903
8219
|
return [...result.data];
|
|
7904
8220
|
}
|
|
8221
|
+
// ==========================================
|
|
8222
|
+
// CSV EXPORT
|
|
8223
|
+
// ==========================================
|
|
8224
|
+
/**
|
|
8225
|
+
* ADMIN: Export redemptions to CSV
|
|
8226
|
+
*
|
|
8227
|
+
* Returns a CSV file with redemption data for the tenant.
|
|
8228
|
+
* Supports date range filtering.
|
|
8229
|
+
*
|
|
8230
|
+
* @param options - Export options (dateFrom, dateTo)
|
|
8231
|
+
* @returns CSV blob
|
|
8232
|
+
*/
|
|
8233
|
+
async exportCSV(options) {
|
|
8234
|
+
const params = new URLSearchParams();
|
|
8235
|
+
if (options?.dateFrom)
|
|
8236
|
+
params.append('dateFrom', options.dateFrom);
|
|
8237
|
+
if (options?.dateTo)
|
|
8238
|
+
params.append('dateTo', options.dateTo);
|
|
8239
|
+
const queryString = params.toString();
|
|
8240
|
+
const url = `${this.basePath}/export/csv${queryString ? `?${queryString}` : ''}`;
|
|
8241
|
+
return this.apiClient.get(url, 'blob');
|
|
8242
|
+
}
|
|
8243
|
+
/**
|
|
8244
|
+
* ADMIN: Export redemption redeems to CSV
|
|
8245
|
+
*
|
|
8246
|
+
* Returns a CSV file with all redemption redeem data.
|
|
8247
|
+
* Supports date range filtering.
|
|
8248
|
+
*
|
|
8249
|
+
* @param options - Export options (dateFrom, dateTo)
|
|
8250
|
+
* @returns CSV blob
|
|
8251
|
+
*/
|
|
8252
|
+
async exportRedeemsCSV(options) {
|
|
8253
|
+
const params = new URLSearchParams();
|
|
8254
|
+
if (options?.dateFrom)
|
|
8255
|
+
params.append('dateFrom', options.dateFrom);
|
|
8256
|
+
if (options?.dateTo)
|
|
8257
|
+
params.append('dateTo', options.dateTo);
|
|
8258
|
+
const queryString = params.toString();
|
|
8259
|
+
const url = `${this.basePath}/redeems/export/csv${queryString ? `?${queryString}` : ''}`;
|
|
8260
|
+
return this.apiClient.get(url, 'blob');
|
|
8261
|
+
}
|
|
7905
8262
|
}
|
|
7906
8263
|
|
|
7907
8264
|
/**
|
|
@@ -8106,7 +8463,7 @@ class TransactionApi {
|
|
|
8106
8463
|
// AUTHENTICATED USER OPERATIONS
|
|
8107
8464
|
// ==========================================
|
|
8108
8465
|
/**
|
|
8109
|
-
* Helper to build query params from
|
|
8466
|
+
* Helper to build query params from TransactionQueryParams
|
|
8110
8467
|
* Handles all filter options and include parameter
|
|
8111
8468
|
*/
|
|
8112
8469
|
buildTransactionQueryParams(options) {
|
|
@@ -8163,6 +8520,11 @@ class TransactionApi {
|
|
|
8163
8520
|
params.append('type', options.type);
|
|
8164
8521
|
}
|
|
8165
8522
|
}
|
|
8523
|
+
// Date range filters
|
|
8524
|
+
if (options.dateFrom)
|
|
8525
|
+
params.append('dateFrom', options.dateFrom);
|
|
8526
|
+
if (options.dateTo)
|
|
8527
|
+
params.append('dateTo', options.dateTo);
|
|
8166
8528
|
// Include relations for entity enrichment
|
|
8167
8529
|
if (options.include && options.include.length > 0) {
|
|
8168
8530
|
options.include.forEach(relation => params.append('include', relation));
|
|
@@ -8272,10 +8634,27 @@ class TransactionApi {
|
|
|
8272
8634
|
/**
|
|
8273
8635
|
* ADMIN: Export transactions to CSV
|
|
8274
8636
|
*
|
|
8275
|
-
*
|
|
8637
|
+
* Returns a CSV file with transaction data for the tenant.
|
|
8638
|
+
* Supports date range filtering.
|
|
8639
|
+
*
|
|
8640
|
+
* @param options - Export options (dateFrom, dateTo)
|
|
8641
|
+
* @returns CSV blob
|
|
8642
|
+
*/
|
|
8643
|
+
async exportCSV(options) {
|
|
8644
|
+
const params = new URLSearchParams();
|
|
8645
|
+
if (options?.dateFrom)
|
|
8646
|
+
params.append('dateFrom', options.dateFrom);
|
|
8647
|
+
if (options?.dateTo)
|
|
8648
|
+
params.append('dateTo', options.dateTo);
|
|
8649
|
+
const queryString = params.toString();
|
|
8650
|
+
const url = `${this.basePath}/export/csv${queryString ? `?${queryString}` : ''}`;
|
|
8651
|
+
return this.apiClient.get(url, 'blob');
|
|
8652
|
+
}
|
|
8653
|
+
/**
|
|
8654
|
+
* @deprecated Use exportCSV() instead
|
|
8276
8655
|
*/
|
|
8277
8656
|
async exportTransactionsCSV() {
|
|
8278
|
-
return this.
|
|
8657
|
+
return this.exportCSV();
|
|
8279
8658
|
}
|
|
8280
8659
|
// ==========================================
|
|
8281
8660
|
// NEW ADMIN QUERY METHODS
|
|
@@ -8446,8 +8825,8 @@ class TransactionService {
|
|
|
8446
8825
|
/**
|
|
8447
8826
|
* ADMIN: Export transactions to CSV
|
|
8448
8827
|
*/
|
|
8449
|
-
async exportTransactionsCSV() {
|
|
8450
|
-
return this.transactionApi.
|
|
8828
|
+
async exportTransactionsCSV(options) {
|
|
8829
|
+
return this.transactionApi.exportCSV(options);
|
|
8451
8830
|
}
|
|
8452
8831
|
// ==========================================
|
|
8453
8832
|
// QUERY & ANALYTICS OPERATIONS
|
|
@@ -11696,10 +12075,139 @@ class DefaultAuthProvider {
|
|
|
11696
12075
|
/** SDK package name */
|
|
11697
12076
|
const SDK_NAME = "@explorins/pers-sdk";
|
|
11698
12077
|
/** SDK version - injected from package.json at build time */
|
|
11699
|
-
const SDK_VERSION = "2.
|
|
12078
|
+
const SDK_VERSION = "2.3.3";
|
|
11700
12079
|
/** Full SDK identifier for headers */
|
|
11701
12080
|
const SDK_USER_AGENT = `${SDK_NAME}/${SDK_VERSION}`;
|
|
11702
12081
|
|
|
12082
|
+
/**
|
|
12083
|
+
* Tracking Manager - Handles data source and platform tracking
|
|
12084
|
+
*
|
|
12085
|
+
* Manages attribution data for analytics across all user actions.
|
|
12086
|
+
*
|
|
12087
|
+
* @module @explorins/pers-sdk/core/tracking
|
|
12088
|
+
*/
|
|
12089
|
+
/**
|
|
12090
|
+
* Manages data source and platform tracking state
|
|
12091
|
+
*/
|
|
12092
|
+
class TrackingManager {
|
|
12093
|
+
constructor(options) {
|
|
12094
|
+
this.configDataSource = options?.dataSource;
|
|
12095
|
+
this.configPlatform = options?.platform;
|
|
12096
|
+
}
|
|
12097
|
+
/**
|
|
12098
|
+
* Set data source tracking info (replaces existing)
|
|
12099
|
+
*
|
|
12100
|
+
* @example
|
|
12101
|
+
* ```typescript
|
|
12102
|
+
* tracking.setDataSource({
|
|
12103
|
+
* channel: 'mobile',
|
|
12104
|
+
* medium: 'push',
|
|
12105
|
+
* campaign: 'summer_2026',
|
|
12106
|
+
* });
|
|
12107
|
+
* ```
|
|
12108
|
+
*/
|
|
12109
|
+
setDataSource(dataSource) {
|
|
12110
|
+
this._dataSource = dataSource;
|
|
12111
|
+
}
|
|
12112
|
+
/**
|
|
12113
|
+
* Update data source tracking info (merges with existing)
|
|
12114
|
+
*
|
|
12115
|
+
* @example
|
|
12116
|
+
* ```typescript
|
|
12117
|
+
* tracking.updateDataSource({
|
|
12118
|
+
* campaign: 'new_campaign',
|
|
12119
|
+
* });
|
|
12120
|
+
* ```
|
|
12121
|
+
*/
|
|
12122
|
+
updateDataSource(dataSource) {
|
|
12123
|
+
const existing = this._dataSource ?? this.configDataSource;
|
|
12124
|
+
this._dataSource = { ...existing, ...dataSource };
|
|
12125
|
+
}
|
|
12126
|
+
/**
|
|
12127
|
+
* Set platform info for analytics
|
|
12128
|
+
*
|
|
12129
|
+
* @example
|
|
12130
|
+
* ```typescript
|
|
12131
|
+
* tracking.setPlatform({
|
|
12132
|
+
* os: 'iOS',
|
|
12133
|
+
* osVersion: '17.4',
|
|
12134
|
+
* app: 'MyApp',
|
|
12135
|
+
* appVersion: '2.3.1',
|
|
12136
|
+
* deviceType: 'phone',
|
|
12137
|
+
* });
|
|
12138
|
+
* ```
|
|
12139
|
+
*/
|
|
12140
|
+
setPlatform(platform) {
|
|
12141
|
+
this._platform = platform;
|
|
12142
|
+
}
|
|
12143
|
+
/**
|
|
12144
|
+
* Get current data source config
|
|
12145
|
+
*/
|
|
12146
|
+
getDataSource() {
|
|
12147
|
+
return this._dataSource ?? this.configDataSource;
|
|
12148
|
+
}
|
|
12149
|
+
/**
|
|
12150
|
+
* Get current platform info
|
|
12151
|
+
*/
|
|
12152
|
+
getPlatform() {
|
|
12153
|
+
return this._platform ?? this.configPlatform;
|
|
12154
|
+
}
|
|
12155
|
+
/**
|
|
12156
|
+
* Check if tracking is properly configured
|
|
12157
|
+
*
|
|
12158
|
+
* Returns warnings for missing recommended tracking data.
|
|
12159
|
+
* Useful for development/debugging to ensure proper attribution.
|
|
12160
|
+
*/
|
|
12161
|
+
validate() {
|
|
12162
|
+
const warnings = [];
|
|
12163
|
+
const dataSource = this.getDataSource();
|
|
12164
|
+
const platform = this.getPlatform();
|
|
12165
|
+
// Check channel for non-web contexts
|
|
12166
|
+
if (!dataSource?.channel) {
|
|
12167
|
+
warnings.push('No channel set. Native apps should set channel: "mobile"');
|
|
12168
|
+
}
|
|
12169
|
+
// Check platform info
|
|
12170
|
+
if (!platform || Object.keys(platform).length === 0) {
|
|
12171
|
+
warnings.push('No platform info set. Consider calling setPlatform() for better analytics');
|
|
12172
|
+
}
|
|
12173
|
+
return {
|
|
12174
|
+
isValid: warnings.length === 0,
|
|
12175
|
+
warnings,
|
|
12176
|
+
};
|
|
12177
|
+
}
|
|
12178
|
+
/**
|
|
12179
|
+
* Build HTTP headers for data source tracking
|
|
12180
|
+
*
|
|
12181
|
+
* @internal - Used by PersApiClient
|
|
12182
|
+
*/
|
|
12183
|
+
buildHeaders() {
|
|
12184
|
+
const headers = {};
|
|
12185
|
+
const dataSource = this.getDataSource();
|
|
12186
|
+
const platform = this.getPlatform();
|
|
12187
|
+
if (dataSource) {
|
|
12188
|
+
if (dataSource.channel) {
|
|
12189
|
+
headers['x-source-channel'] = dataSource.channel;
|
|
12190
|
+
}
|
|
12191
|
+
if (dataSource.medium) {
|
|
12192
|
+
headers['x-source-medium'] = dataSource.medium;
|
|
12193
|
+
}
|
|
12194
|
+
if (dataSource.campaign) {
|
|
12195
|
+
headers['x-source-campaign'] = dataSource.campaign;
|
|
12196
|
+
}
|
|
12197
|
+
if (dataSource.source) {
|
|
12198
|
+
headers['x-source'] = dataSource.source;
|
|
12199
|
+
}
|
|
12200
|
+
if (dataSource.referrer) {
|
|
12201
|
+
headers['Referer'] = dataSource.referrer;
|
|
12202
|
+
}
|
|
12203
|
+
}
|
|
12204
|
+
if (platform && Object.keys(platform).length > 0) {
|
|
12205
|
+
headers['x-platform'] = JSON.stringify(platform);
|
|
12206
|
+
}
|
|
12207
|
+
return headers;
|
|
12208
|
+
}
|
|
12209
|
+
}
|
|
12210
|
+
|
|
11703
12211
|
// packages/pers-sdk/src/core/pers-api-client.ts
|
|
11704
12212
|
/**
|
|
11705
12213
|
* PERS API Client - Platform-agnostic HTTP client with authentication
|
|
@@ -11748,6 +12256,11 @@ class PersApiClient {
|
|
|
11748
12256
|
}
|
|
11749
12257
|
this.authService = new AuthService(this.authApi, this.mergedConfig.authProvider);
|
|
11750
12258
|
this.refreshManager = new TokenRefreshManager(this.authService, this.mergedConfig.authProvider);
|
|
12259
|
+
// Initialize tracking manager
|
|
12260
|
+
this.tracking = new TrackingManager({
|
|
12261
|
+
dataSource: this.mergedConfig.dataSource,
|
|
12262
|
+
platform: this.mergedConfig.platform,
|
|
12263
|
+
});
|
|
11751
12264
|
if (this.mergedConfig.authProvider) {
|
|
11752
12265
|
this.initializationPromise = this.initialize();
|
|
11753
12266
|
}
|
|
@@ -12032,24 +12545,8 @@ class PersApiClient {
|
|
|
12032
12545
|
else if (this.mergedConfig.apiProjectKey) {
|
|
12033
12546
|
headers['x-project-key'] = this.mergedConfig.apiProjectKey;
|
|
12034
12547
|
}
|
|
12035
|
-
// Add data source
|
|
12036
|
-
|
|
12037
|
-
if (dataSource) {
|
|
12038
|
-
headers['x-source-channel'] = dataSource.channel;
|
|
12039
|
-
if (dataSource.medium) {
|
|
12040
|
-
headers['x-source-medium'] = dataSource.medium;
|
|
12041
|
-
}
|
|
12042
|
-
if (dataSource.campaign) {
|
|
12043
|
-
headers['x-source-campaign'] = dataSource.campaign;
|
|
12044
|
-
}
|
|
12045
|
-
if (dataSource.source) {
|
|
12046
|
-
headers['x-source'] = dataSource.source;
|
|
12047
|
-
}
|
|
12048
|
-
// Note: referrer is typically set by browser, but can be overridden
|
|
12049
|
-
if (dataSource.referrer) {
|
|
12050
|
-
headers['Referer'] = dataSource.referrer;
|
|
12051
|
-
}
|
|
12052
|
-
}
|
|
12548
|
+
// Add tracking headers (data source + platform)
|
|
12549
|
+
Object.assign(headers, this.tracking.buildHeaders());
|
|
12053
12550
|
return headers;
|
|
12054
12551
|
}
|
|
12055
12552
|
// ==========================================
|
|
@@ -12073,6 +12570,33 @@ class PersApiClient {
|
|
|
12073
12570
|
getOriginalConfig() {
|
|
12074
12571
|
return this.config;
|
|
12075
12572
|
}
|
|
12573
|
+
// ==========================================
|
|
12574
|
+
// DATA SOURCE TRACKING
|
|
12575
|
+
// ==========================================
|
|
12576
|
+
/** Set data source tracking info (replaces existing) */
|
|
12577
|
+
setDataSource(dataSource) {
|
|
12578
|
+
this.tracking.setDataSource(dataSource);
|
|
12579
|
+
}
|
|
12580
|
+
/** Update data source tracking info (merges with existing) */
|
|
12581
|
+
updateDataSource(dataSource) {
|
|
12582
|
+
this.tracking.updateDataSource(dataSource);
|
|
12583
|
+
}
|
|
12584
|
+
/** Set platform info for analytics */
|
|
12585
|
+
setPlatform(platform) {
|
|
12586
|
+
this.tracking.setPlatform(platform);
|
|
12587
|
+
}
|
|
12588
|
+
/** Get current data source config */
|
|
12589
|
+
getDataSource() {
|
|
12590
|
+
return this.tracking.getDataSource();
|
|
12591
|
+
}
|
|
12592
|
+
/** Get current platform info */
|
|
12593
|
+
getPlatform() {
|
|
12594
|
+
return this.tracking.getPlatform();
|
|
12595
|
+
}
|
|
12596
|
+
/** Validate tracking configuration (for debugging) */
|
|
12597
|
+
validateTracking() {
|
|
12598
|
+
return this.tracking.validate();
|
|
12599
|
+
}
|
|
12076
12600
|
}
|
|
12077
12601
|
|
|
12078
12602
|
/**
|
|
@@ -13291,6 +13815,37 @@ class UserManager {
|
|
|
13291
13815
|
});
|
|
13292
13816
|
return result;
|
|
13293
13817
|
}
|
|
13818
|
+
/**
|
|
13819
|
+
* Admin: Export users as CSV
|
|
13820
|
+
*
|
|
13821
|
+
* Generates a comprehensive CSV export of all tenant users for
|
|
13822
|
+
* external analysis, reporting, or compliance purposes. This operation
|
|
13823
|
+
* requires administrator privileges and creates a downloadable file.
|
|
13824
|
+
*
|
|
13825
|
+
* @param options - Optional date range filters
|
|
13826
|
+
* @param options.dateFrom - Start date filter (ISO date string)
|
|
13827
|
+
* @param options.dateTo - End date filter (ISO date string)
|
|
13828
|
+
* @returns Promise resolving to CSV blob for download
|
|
13829
|
+
* @throws {PersApiError} When not authenticated as administrator or export fails
|
|
13830
|
+
*
|
|
13831
|
+
* @example Export All Users
|
|
13832
|
+
* ```typescript
|
|
13833
|
+
* const csvBlob = await sdk.users.exportCSV();
|
|
13834
|
+
* const downloadUrl = URL.createObjectURL(csvBlob);
|
|
13835
|
+
* ```
|
|
13836
|
+
*
|
|
13837
|
+
* @example Export Users Within Date Range
|
|
13838
|
+
* ```typescript
|
|
13839
|
+
* const csvBlob = await sdk.users.exportCSV({
|
|
13840
|
+
* dateFrom: '2024-01-01',
|
|
13841
|
+
* dateTo: '2024-12-31'
|
|
13842
|
+
* });
|
|
13843
|
+
* ```
|
|
13844
|
+
*/
|
|
13845
|
+
async exportCSV(options) {
|
|
13846
|
+
const userApi = new UserApi(this.apiClient);
|
|
13847
|
+
return userApi.exportCSV(options);
|
|
13848
|
+
}
|
|
13294
13849
|
/**
|
|
13295
13850
|
* Get the user service for advanced operations
|
|
13296
13851
|
*
|
|
@@ -14753,7 +15308,13 @@ class CampaignManager {
|
|
|
14753
15308
|
* ```
|
|
14754
15309
|
*/
|
|
14755
15310
|
async getCampaigns(options) {
|
|
14756
|
-
|
|
15311
|
+
// Convert Date objects to ISO strings for API compatibility
|
|
15312
|
+
const normalizedOptions = options ? {
|
|
15313
|
+
...options,
|
|
15314
|
+
startDate: options.startDate instanceof Date ? options.startDate.toISOString() : options.startDate,
|
|
15315
|
+
endDate: options.endDate instanceof Date ? options.endDate.toISOString() : options.endDate,
|
|
15316
|
+
} : undefined;
|
|
15317
|
+
return this.campaignService.getCampaigns(normalizedOptions);
|
|
14757
15318
|
}
|
|
14758
15319
|
/**
|
|
14759
15320
|
* Admin: Create new campaign
|
|
@@ -15402,6 +15963,60 @@ class CampaignManager {
|
|
|
15402
15963
|
async removeTriggerSource(campaignId, triggerSourceId) {
|
|
15403
15964
|
return this.campaignService.removeTriggerSourceFromCampaign(campaignId, triggerSourceId);
|
|
15404
15965
|
}
|
|
15966
|
+
/**
|
|
15967
|
+
* Admin: Export campaigns as CSV
|
|
15968
|
+
*
|
|
15969
|
+
* Generates a comprehensive CSV export of all tenant campaigns for
|
|
15970
|
+
* external analysis, reporting, or compliance purposes. This operation
|
|
15971
|
+
* requires administrator privileges and creates a downloadable file.
|
|
15972
|
+
*
|
|
15973
|
+
* @param options - Optional date range filters
|
|
15974
|
+
* @param options.dateFrom - Start date filter (ISO date string)
|
|
15975
|
+
* @param options.dateTo - End date filter (ISO date string)
|
|
15976
|
+
* @returns Promise resolving to CSV blob for download
|
|
15977
|
+
* @throws {PersApiError} When not authenticated as administrator or export fails
|
|
15978
|
+
*
|
|
15979
|
+
* @example Export All Campaigns
|
|
15980
|
+
* ```typescript
|
|
15981
|
+
* const csvBlob = await sdk.campaigns.exportCSV();
|
|
15982
|
+
* const downloadUrl = URL.createObjectURL(csvBlob);
|
|
15983
|
+
* ```
|
|
15984
|
+
*
|
|
15985
|
+
* @example Export Campaigns Within Date Range
|
|
15986
|
+
* ```typescript
|
|
15987
|
+
* const csvBlob = await sdk.campaigns.exportCSV({
|
|
15988
|
+
* dateFrom: '2024-01-01',
|
|
15989
|
+
* dateTo: '2024-12-31'
|
|
15990
|
+
* });
|
|
15991
|
+
* ```
|
|
15992
|
+
*/
|
|
15993
|
+
async exportCSV(options) {
|
|
15994
|
+
const campaignApi = new CampaignApi(this.apiClient);
|
|
15995
|
+
return campaignApi.exportCSV(options);
|
|
15996
|
+
}
|
|
15997
|
+
/**
|
|
15998
|
+
* Admin: Export campaign claims as CSV
|
|
15999
|
+
*
|
|
16000
|
+
* Generates a comprehensive CSV export of all campaign claim records for
|
|
16001
|
+
* external analysis, reporting, or compliance purposes. This operation
|
|
16002
|
+
* requires administrator privileges and creates a downloadable file.
|
|
16003
|
+
*
|
|
16004
|
+
* @param options - Optional date range filters
|
|
16005
|
+
* @param options.dateFrom - Start date filter (ISO date string)
|
|
16006
|
+
* @param options.dateTo - End date filter (ISO date string)
|
|
16007
|
+
* @returns Promise resolving to CSV blob for download
|
|
16008
|
+
* @throws {PersApiError} When not authenticated as administrator or export fails
|
|
16009
|
+
*
|
|
16010
|
+
* @example Export All Claims
|
|
16011
|
+
* ```typescript
|
|
16012
|
+
* const csvBlob = await sdk.campaigns.exportClaimsCSV();
|
|
16013
|
+
* const downloadUrl = URL.createObjectURL(csvBlob);
|
|
16014
|
+
* ```
|
|
16015
|
+
*/
|
|
16016
|
+
async exportClaimsCSV(options) {
|
|
16017
|
+
const campaignApi = new CampaignApi(this.apiClient);
|
|
16018
|
+
return campaignApi.exportClaimsCSV(options);
|
|
16019
|
+
}
|
|
15405
16020
|
/**
|
|
15406
16021
|
* Get the full campaign service for advanced operations
|
|
15407
16022
|
*
|
|
@@ -16208,6 +16823,60 @@ class RedemptionManager {
|
|
|
16208
16823
|
async deleteRedemption(id) {
|
|
16209
16824
|
return this.redemptionService.deleteRedemption(id);
|
|
16210
16825
|
}
|
|
16826
|
+
/**
|
|
16827
|
+
* Admin: Export redemptions as CSV
|
|
16828
|
+
*
|
|
16829
|
+
* Generates a comprehensive CSV export of all tenant redemptions for
|
|
16830
|
+
* external analysis, reporting, or compliance purposes. This operation
|
|
16831
|
+
* requires administrator privileges and creates a downloadable file.
|
|
16832
|
+
*
|
|
16833
|
+
* @param options - Optional date range filters
|
|
16834
|
+
* @param options.dateFrom - Start date filter (ISO date string)
|
|
16835
|
+
* @param options.dateTo - End date filter (ISO date string)
|
|
16836
|
+
* @returns Promise resolving to CSV blob for download
|
|
16837
|
+
* @throws {PersApiError} When not authenticated as administrator or export fails
|
|
16838
|
+
*
|
|
16839
|
+
* @example Export All Redemptions
|
|
16840
|
+
* ```typescript
|
|
16841
|
+
* const csvBlob = await sdk.redemptions.exportCSV();
|
|
16842
|
+
* const downloadUrl = URL.createObjectURL(csvBlob);
|
|
16843
|
+
* ```
|
|
16844
|
+
*
|
|
16845
|
+
* @example Export Redemptions Within Date Range
|
|
16846
|
+
* ```typescript
|
|
16847
|
+
* const csvBlob = await sdk.redemptions.exportCSV({
|
|
16848
|
+
* dateFrom: '2024-01-01',
|
|
16849
|
+
* dateTo: '2024-12-31'
|
|
16850
|
+
* });
|
|
16851
|
+
* ```
|
|
16852
|
+
*/
|
|
16853
|
+
async exportCSV(options) {
|
|
16854
|
+
const redemptionApi = new RedemptionApi(this.apiClient);
|
|
16855
|
+
return redemptionApi.exportCSV(options);
|
|
16856
|
+
}
|
|
16857
|
+
/**
|
|
16858
|
+
* Admin: Export redemption redeems as CSV
|
|
16859
|
+
*
|
|
16860
|
+
* Generates a comprehensive CSV export of all redemption redeem records for
|
|
16861
|
+
* external analysis, reporting, or compliance purposes. This operation
|
|
16862
|
+
* requires administrator privileges and creates a downloadable file.
|
|
16863
|
+
*
|
|
16864
|
+
* @param options - Optional date range filters
|
|
16865
|
+
* @param options.dateFrom - Start date filter (ISO date string)
|
|
16866
|
+
* @param options.dateTo - End date filter (ISO date string)
|
|
16867
|
+
* @returns Promise resolving to CSV blob for download
|
|
16868
|
+
* @throws {PersApiError} When not authenticated as administrator or export fails
|
|
16869
|
+
*
|
|
16870
|
+
* @example Export All Redeems
|
|
16871
|
+
* ```typescript
|
|
16872
|
+
* const csvBlob = await sdk.redemptions.exportRedeemsCSV();
|
|
16873
|
+
* const downloadUrl = URL.createObjectURL(csvBlob);
|
|
16874
|
+
* ```
|
|
16875
|
+
*/
|
|
16876
|
+
async exportRedeemsCSV(options) {
|
|
16877
|
+
const redemptionApi = new RedemptionApi(this.apiClient);
|
|
16878
|
+
return redemptionApi.exportRedeemsCSV(options);
|
|
16879
|
+
}
|
|
16211
16880
|
/**
|
|
16212
16881
|
* Get the full redemption service for advanced operations
|
|
16213
16882
|
*
|
|
@@ -16601,54 +17270,36 @@ class TransactionManager {
|
|
|
16601
17270
|
* external analysis, reporting, or compliance purposes. This operation
|
|
16602
17271
|
* requires administrator privileges and creates a downloadable file.
|
|
16603
17272
|
*
|
|
17273
|
+
* @param options - Optional date range filters
|
|
17274
|
+
* @param options.dateFrom - Start date filter (ISO date string)
|
|
17275
|
+
* @param options.dateTo - End date filter (ISO date string)
|
|
16604
17276
|
* @returns Promise resolving to CSV blob for download
|
|
16605
17277
|
* @throws {PersApiError} When not authenticated as administrator or export fails
|
|
16606
17278
|
*
|
|
16607
|
-
* @example
|
|
17279
|
+
* @example Export All Transactions
|
|
16608
17280
|
* ```typescript
|
|
16609
|
-
*
|
|
16610
|
-
*
|
|
16611
|
-
* const csvBlob = await sdk.transactions.exportTransactionsCSV();
|
|
16612
|
-
*
|
|
16613
|
-
* // Create download link
|
|
16614
|
-
* const downloadUrl = URL.createObjectURL(csvBlob);
|
|
16615
|
-
* const link = document.createElement('a');
|
|
16616
|
-
* link.href = downloadUrl;
|
|
16617
|
-
* link.download = `transactions-${new Date().toISOString().split('T')[0]}.csv`;
|
|
16618
|
-
*
|
|
16619
|
-
* // Trigger download
|
|
16620
|
-
* document.body.appendChild(link);
|
|
16621
|
-
* link.click();
|
|
16622
|
-
* document.body.removeChild(link);
|
|
16623
|
-
*
|
|
16624
|
-
* // Clean up object URL
|
|
16625
|
-
* URL.revokeObjectURL(downloadUrl);
|
|
16626
|
-
*
|
|
16627
|
-
* console.log('Transaction export downloaded successfully');
|
|
16628
|
-
*
|
|
16629
|
-
* } catch (error) {
|
|
16630
|
-
* console.log('Export failed:', error.message);
|
|
16631
|
-
* }
|
|
17281
|
+
* const csvBlob = await sdk.transactions.exportCSV();
|
|
17282
|
+
* const downloadUrl = URL.createObjectURL(csvBlob);
|
|
16632
17283
|
* ```
|
|
16633
17284
|
*
|
|
16634
|
-
* @example
|
|
17285
|
+
* @example Export Transactions Within Date Range
|
|
16635
17286
|
* ```typescript
|
|
16636
|
-
*
|
|
16637
|
-
*
|
|
16638
|
-
*
|
|
16639
|
-
*
|
|
16640
|
-
* const buffer = Buffer.from(await csvBlob.arrayBuffer());
|
|
16641
|
-
*
|
|
16642
|
-
* // Save to file system
|
|
16643
|
-
* const fs = require('fs');
|
|
16644
|
-
* const filename = `transactions-export-${Date.now()}.csv`;
|
|
16645
|
-
* fs.writeFileSync(filename, buffer);
|
|
16646
|
-
*
|
|
16647
|
-
* console.log(`Transaction data exported to ${filename}`);
|
|
17287
|
+
* const csvBlob = await sdk.transactions.exportCSV({
|
|
17288
|
+
* dateFrom: '2024-01-01',
|
|
17289
|
+
* dateTo: '2024-12-31'
|
|
17290
|
+
* });
|
|
16648
17291
|
* ```
|
|
16649
17292
|
*/
|
|
16650
|
-
async
|
|
16651
|
-
return this.transactionService.exportTransactionsCSV();
|
|
17293
|
+
async exportCSV(options) {
|
|
17294
|
+
return this.transactionService.exportTransactionsCSV(options);
|
|
17295
|
+
}
|
|
17296
|
+
/**
|
|
17297
|
+
* Admin: Export transactions as CSV
|
|
17298
|
+
*
|
|
17299
|
+
* @deprecated Use `exportCSV()` instead for consistent naming across all managers
|
|
17300
|
+
*/
|
|
17301
|
+
async exportTransactionsCSV(options) {
|
|
17302
|
+
return this.exportCSV(options);
|
|
16652
17303
|
}
|
|
16653
17304
|
/**
|
|
16654
17305
|
* Prepare existing transaction for client-side signing
|
|
@@ -18854,10 +19505,10 @@ class WebhookApi {
|
|
|
18854
19505
|
params.append('hookId', options.webhookId);
|
|
18855
19506
|
if (options?.status)
|
|
18856
19507
|
params.append('status', options.status);
|
|
18857
|
-
if (options?.
|
|
18858
|
-
params.append('dateFrom', options.
|
|
18859
|
-
if (options?.
|
|
18860
|
-
params.append('dateTo', options.
|
|
19508
|
+
if (options?.dateFrom)
|
|
19509
|
+
params.append('dateFrom', options.dateFrom);
|
|
19510
|
+
if (options?.dateTo)
|
|
19511
|
+
params.append('dateTo', options.dateTo);
|
|
18861
19512
|
if (options?.page)
|
|
18862
19513
|
params.append('page', String(options.page));
|
|
18863
19514
|
if (options?.limit)
|
|
@@ -19346,7 +19997,7 @@ class WebhookManager {
|
|
|
19346
19997
|
* // Get executions for specific webhook
|
|
19347
19998
|
* const executions = await sdk.webhooks.getExecutions({
|
|
19348
19999
|
* webhookId: 'webhook-123',
|
|
19349
|
-
*
|
|
20000
|
+
* dateFrom: '2024-01-01'
|
|
19350
20001
|
* });
|
|
19351
20002
|
* ```
|
|
19352
20003
|
*/
|
|
@@ -20729,6 +21380,28 @@ class BookingApi {
|
|
|
20729
21380
|
async delete(id) {
|
|
20730
21381
|
return this.apiClient.delete(`${this.basePath}/${id}`);
|
|
20731
21382
|
}
|
|
21383
|
+
// ==========================================
|
|
21384
|
+
// CSV EXPORT
|
|
21385
|
+
// ==========================================
|
|
21386
|
+
/**
|
|
21387
|
+
* ADMIN: Export bookings to CSV
|
|
21388
|
+
*
|
|
21389
|
+
* Returns a CSV file with booking data for the tenant.
|
|
21390
|
+
* Supports date range filtering.
|
|
21391
|
+
*
|
|
21392
|
+
* @param options - Export options (dateFrom, dateTo in ISO format)
|
|
21393
|
+
* @returns CSV blob
|
|
21394
|
+
*/
|
|
21395
|
+
async exportCSV(options) {
|
|
21396
|
+
const params = new URLSearchParams();
|
|
21397
|
+
if (options?.dateFrom)
|
|
21398
|
+
params.append('dateFrom', options.dateFrom);
|
|
21399
|
+
if (options?.dateTo)
|
|
21400
|
+
params.append('dateTo', options.dateTo);
|
|
21401
|
+
const queryString = params.toString();
|
|
21402
|
+
const url = `${this.basePath}/export/csv${queryString ? `?${queryString}` : ''}`;
|
|
21403
|
+
return this.apiClient.get(url, 'blob');
|
|
21404
|
+
}
|
|
20732
21405
|
}
|
|
20733
21406
|
|
|
20734
21407
|
/**
|
|
@@ -21181,6 +21854,28 @@ class PersSDK {
|
|
|
21181
21854
|
this.setupWalletEventsAutoConnect();
|
|
21182
21855
|
// Auto-restore session from stored tokens (if enabled, default: true)
|
|
21183
21856
|
this.setupAutoRestoreSession();
|
|
21857
|
+
// Dev-mode tracking validation (helps developers ensure proper attribution)
|
|
21858
|
+
this.validateTrackingInDev();
|
|
21859
|
+
}
|
|
21860
|
+
/**
|
|
21861
|
+
* Validate tracking configuration in development mode
|
|
21862
|
+
* @internal
|
|
21863
|
+
*/
|
|
21864
|
+
validateTrackingInDev() {
|
|
21865
|
+
// Only run in development (check common env vars)
|
|
21866
|
+
const isDev = (typeof process !== 'undefined' && process.env?.['NODE_ENV'] === 'development') ||
|
|
21867
|
+
(typeof window !== 'undefined' && window.__DEV__);
|
|
21868
|
+
if (!isDev)
|
|
21869
|
+
return;
|
|
21870
|
+
// Delay check to allow time for setDataSource/setPlatform calls
|
|
21871
|
+
setTimeout(() => {
|
|
21872
|
+
const result = this.apiClient.validateTracking();
|
|
21873
|
+
if (!result.isValid) {
|
|
21874
|
+
console.warn('[PERS SDK] Tracking configuration warnings:');
|
|
21875
|
+
result.warnings.forEach(w => console.warn(` ⚠️ ${w}`));
|
|
21876
|
+
console.warn('[PERS SDK] Call sdk.setDataSource() and sdk.setPlatform() for better analytics.');
|
|
21877
|
+
}
|
|
21878
|
+
}, 2000);
|
|
21184
21879
|
}
|
|
21185
21880
|
/**
|
|
21186
21881
|
* Setup automatic session restoration from stored tokens
|
|
@@ -21881,6 +22576,74 @@ class PersSDK {
|
|
|
21881
22576
|
// Reset manager so it picks up new config
|
|
21882
22577
|
this._walletEvents = undefined;
|
|
21883
22578
|
}
|
|
22579
|
+
// ==========================================
|
|
22580
|
+
// DATA SOURCE TRACKING
|
|
22581
|
+
// ==========================================
|
|
22582
|
+
/**
|
|
22583
|
+
* Set data source tracking for analytics attribution
|
|
22584
|
+
*
|
|
22585
|
+
* Use this to track where users and actions originate from.
|
|
22586
|
+
* Web apps typically only need medium/campaign/source (channel is auto-detected).
|
|
22587
|
+
* Native apps must set channel: 'mobile'.
|
|
22588
|
+
*
|
|
22589
|
+
* @param dataSource - Data source configuration
|
|
22590
|
+
*
|
|
22591
|
+
* @example Web app with UTM params
|
|
22592
|
+
* ```typescript
|
|
22593
|
+
* sdk.setDataSource({
|
|
22594
|
+
* medium: 'email',
|
|
22595
|
+
* campaign: 'summer_2026',
|
|
22596
|
+
* source: 'mailchimp',
|
|
22597
|
+
* });
|
|
22598
|
+
* ```
|
|
22599
|
+
*
|
|
22600
|
+
* @example Native app
|
|
22601
|
+
* ```typescript
|
|
22602
|
+
* sdk.setDataSource({
|
|
22603
|
+
* channel: 'mobile',
|
|
22604
|
+
* medium: 'push',
|
|
22605
|
+
* campaign: 'retention_30d',
|
|
22606
|
+
* });
|
|
22607
|
+
* ```
|
|
22608
|
+
*/
|
|
22609
|
+
setDataSource(dataSource) {
|
|
22610
|
+
this.apiClient.setDataSource(dataSource);
|
|
22611
|
+
}
|
|
22612
|
+
/**
|
|
22613
|
+
* Update data source (merges with existing values)
|
|
22614
|
+
*
|
|
22615
|
+
* @param dataSource - Partial data source to merge
|
|
22616
|
+
*
|
|
22617
|
+
* @example Update campaign after deep link
|
|
22618
|
+
* ```typescript
|
|
22619
|
+
* sdk.updateDataSource({ campaign: 'new_campaign' });
|
|
22620
|
+
* ```
|
|
22621
|
+
*/
|
|
22622
|
+
updateDataSource(dataSource) {
|
|
22623
|
+
this.apiClient.updateDataSource(dataSource);
|
|
22624
|
+
}
|
|
22625
|
+
/**
|
|
22626
|
+
* Set platform info for device analytics
|
|
22627
|
+
*
|
|
22628
|
+
* Native apps should call this on initialization with device info.
|
|
22629
|
+
* Web apps can optionally send browser/OS info.
|
|
22630
|
+
*
|
|
22631
|
+
* @param platform - Platform information
|
|
22632
|
+
*
|
|
22633
|
+
* @example Native app
|
|
22634
|
+
* ```typescript
|
|
22635
|
+
* sdk.setPlatform({
|
|
22636
|
+
* os: 'iOS',
|
|
22637
|
+
* osVersion: '17.4',
|
|
22638
|
+
* app: 'MyApp',
|
|
22639
|
+
* appVersion: '2.3.1',
|
|
22640
|
+
* deviceType: 'phone',
|
|
22641
|
+
* });
|
|
22642
|
+
* ```
|
|
22643
|
+
*/
|
|
22644
|
+
setPlatform(platform) {
|
|
22645
|
+
this.apiClient.setPlatform(platform);
|
|
22646
|
+
}
|
|
21884
22647
|
/**
|
|
21885
22648
|
* Gets the API client for direct PERS API requests
|
|
21886
22649
|
*
|
|
@@ -21970,6 +22733,108 @@ function warnIfProblematicEnvironment(feature) {
|
|
|
21970
22733
|
}
|
|
21971
22734
|
}
|
|
21972
22735
|
|
|
22736
|
+
/**
|
|
22737
|
+
* DataSource Tracking - SDK Extensions
|
|
22738
|
+
*
|
|
22739
|
+
* Re-exports from @explorins/pers-shared with SDK-specific utilities.
|
|
22740
|
+
*
|
|
22741
|
+
* Most types and utilities come from pers-shared:
|
|
22742
|
+
* - DataSource, DataSourceChannel
|
|
22743
|
+
* - Platform, UtmParams, WebDataSourceConfig, NativeDataSourceConfig
|
|
22744
|
+
* - parseUtmParams(), detectSourceFromReferrer(), KNOWN_SOURCE_DOMAINS
|
|
22745
|
+
*
|
|
22746
|
+
* SDK adds:
|
|
22747
|
+
* - detectWebPlatform() - Browser User-Agent detection (requires DOM)
|
|
22748
|
+
*
|
|
22749
|
+
* @module @explorins/pers-sdk/data-source
|
|
22750
|
+
*/
|
|
22751
|
+
/**
|
|
22752
|
+
* Detect web platform from User-Agent (browser environments only)
|
|
22753
|
+
*
|
|
22754
|
+
* This uses `navigator.userAgent` which is only available in browsers.
|
|
22755
|
+
* For Node.js or React Native, use platform-specific detection.
|
|
22756
|
+
*
|
|
22757
|
+
* @example
|
|
22758
|
+
* ```typescript
|
|
22759
|
+
* const platform = detectWebPlatform();
|
|
22760
|
+
* sdk.setPlatform(platform);
|
|
22761
|
+
* // { os: 'macOS', browser: 'Chrome', browserVersion: '125.0', deviceType: 'desktop' }
|
|
22762
|
+
* ```
|
|
22763
|
+
*/
|
|
22764
|
+
function detectWebPlatform() {
|
|
22765
|
+
if (typeof navigator === 'undefined') {
|
|
22766
|
+
return {};
|
|
22767
|
+
}
|
|
22768
|
+
const ua = navigator.userAgent;
|
|
22769
|
+
const platform = {};
|
|
22770
|
+
if (!ua)
|
|
22771
|
+
return platform;
|
|
22772
|
+
// Device type detection
|
|
22773
|
+
if (/mobile|android|iphone|ipod/i.test(ua)) {
|
|
22774
|
+
platform.deviceType = 'phone';
|
|
22775
|
+
}
|
|
22776
|
+
else if (/ipad|tablet/i.test(ua)) {
|
|
22777
|
+
platform.deviceType = 'tablet';
|
|
22778
|
+
}
|
|
22779
|
+
else {
|
|
22780
|
+
platform.deviceType = 'desktop';
|
|
22781
|
+
}
|
|
22782
|
+
// OS detection
|
|
22783
|
+
if (/windows/i.test(ua)) {
|
|
22784
|
+
platform.os = 'Windows';
|
|
22785
|
+
const match = ua.match(/Windows NT ([\d.]+)/);
|
|
22786
|
+
if (match)
|
|
22787
|
+
platform.osVersion = match[1];
|
|
22788
|
+
}
|
|
22789
|
+
else if (/macintosh|mac os/i.test(ua) && !/iphone|ipad|ipod/i.test(ua)) {
|
|
22790
|
+
platform.os = 'macOS';
|
|
22791
|
+
const match = ua.match(/Mac OS X ([\d_.]+)/);
|
|
22792
|
+
if (match)
|
|
22793
|
+
platform.osVersion = match[1].replace(/_/g, '.');
|
|
22794
|
+
}
|
|
22795
|
+
else if (/linux/i.test(ua) && !/android/i.test(ua)) {
|
|
22796
|
+
platform.os = 'Linux';
|
|
22797
|
+
}
|
|
22798
|
+
else if (/iphone|ipad|ipod/i.test(ua)) {
|
|
22799
|
+
platform.os = 'iOS';
|
|
22800
|
+
const match = ua.match(/OS ([\d_]+)/);
|
|
22801
|
+
if (match)
|
|
22802
|
+
platform.osVersion = match[1].replace(/_/g, '.');
|
|
22803
|
+
}
|
|
22804
|
+
else if (/android/i.test(ua)) {
|
|
22805
|
+
platform.os = 'Android';
|
|
22806
|
+
const match = ua.match(/Android ([\d.]+)/);
|
|
22807
|
+
if (match)
|
|
22808
|
+
platform.osVersion = match[1];
|
|
22809
|
+
}
|
|
22810
|
+
// Browser detection
|
|
22811
|
+
if (/edg\//i.test(ua)) {
|
|
22812
|
+
platform.browser = 'Edge';
|
|
22813
|
+
const match = ua.match(/Edg\/([\d.]+)/);
|
|
22814
|
+
if (match)
|
|
22815
|
+
platform.browserVersion = match[1];
|
|
22816
|
+
}
|
|
22817
|
+
else if (/chrome/i.test(ua) && !/edg/i.test(ua)) {
|
|
22818
|
+
platform.browser = 'Chrome';
|
|
22819
|
+
const match = ua.match(/Chrome\/([\d.]+)/);
|
|
22820
|
+
if (match)
|
|
22821
|
+
platform.browserVersion = match[1];
|
|
22822
|
+
}
|
|
22823
|
+
else if (/safari/i.test(ua) && !/chrome/i.test(ua)) {
|
|
22824
|
+
platform.browser = 'Safari';
|
|
22825
|
+
const match = ua.match(/Version\/([\d.]+)/);
|
|
22826
|
+
if (match)
|
|
22827
|
+
platform.browserVersion = match[1];
|
|
22828
|
+
}
|
|
22829
|
+
else if (/firefox/i.test(ua)) {
|
|
22830
|
+
platform.browser = 'Firefox';
|
|
22831
|
+
const match = ua.match(/Firefox\/([\d.]+)/);
|
|
22832
|
+
if (match)
|
|
22833
|
+
platform.browserVersion = match[1];
|
|
22834
|
+
}
|
|
22835
|
+
return platform;
|
|
22836
|
+
}
|
|
22837
|
+
|
|
21973
22838
|
/**
|
|
21974
22839
|
* AsyncStorage Token Storage for React Native Mobile Platforms
|
|
21975
22840
|
*
|
|
@@ -22607,6 +23472,62 @@ class ReactNativeHttpClient {
|
|
|
22607
23472
|
}
|
|
22608
23473
|
}
|
|
22609
23474
|
|
|
23475
|
+
/**
|
|
23476
|
+
* React Native Platform Detection Utilities
|
|
23477
|
+
*
|
|
23478
|
+
* Provides automatic platform info detection for React Native apps.
|
|
23479
|
+
*
|
|
23480
|
+
* @module @explorins/pers-sdk-react-native/utils
|
|
23481
|
+
*/
|
|
23482
|
+
// DeviceInfo is optional - dynamically loaded if available
|
|
23483
|
+
let DeviceInfo = null;
|
|
23484
|
+
try {
|
|
23485
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
23486
|
+
DeviceInfo = require('react-native-device-info').default;
|
|
23487
|
+
}
|
|
23488
|
+
catch {
|
|
23489
|
+
// react-native-device-info not installed - will use basic detection
|
|
23490
|
+
}
|
|
23491
|
+
/**
|
|
23492
|
+
* Detect platform info for React Native apps
|
|
23493
|
+
*
|
|
23494
|
+
* Automatically gathers OS, version, app info, and device type.
|
|
23495
|
+
* Uses react-native-device-info for enhanced detection if installed.
|
|
23496
|
+
*
|
|
23497
|
+
* @example
|
|
23498
|
+
* ```typescript
|
|
23499
|
+
* import { detectReactNativePlatform } from '@explorins/pers-sdk-react-native';
|
|
23500
|
+
*
|
|
23501
|
+
* // Get platform info
|
|
23502
|
+
* const platform = await detectReactNativePlatform();
|
|
23503
|
+
* // { os: 'iOS', osVersion: '17.4', app: 'MyApp', appVersion: '2.3.1', deviceType: 'phone' }
|
|
23504
|
+
*
|
|
23505
|
+
* // Set in SDK
|
|
23506
|
+
* sdk.setPlatform(platform);
|
|
23507
|
+
* ```
|
|
23508
|
+
*/
|
|
23509
|
+
async function detectReactNativePlatform() {
|
|
23510
|
+
const platform = {
|
|
23511
|
+
os: reactNative.Platform.OS === 'ios' ? 'iOS' : reactNative.Platform.OS === 'android' ? 'Android' : reactNative.Platform.OS,
|
|
23512
|
+
osVersion: reactNative.Platform.Version?.toString(),
|
|
23513
|
+
deviceType: 'phone', // Default fallback
|
|
23514
|
+
};
|
|
23515
|
+
if (DeviceInfo) {
|
|
23516
|
+
try {
|
|
23517
|
+
// App info
|
|
23518
|
+
platform.app = await DeviceInfo.getApplicationName();
|
|
23519
|
+
platform.appVersion = DeviceInfo.getVersion();
|
|
23520
|
+
// Device type detection
|
|
23521
|
+
const isTablet = await DeviceInfo.isTablet();
|
|
23522
|
+
platform.deviceType = isTablet ? 'tablet' : 'phone';
|
|
23523
|
+
}
|
|
23524
|
+
catch (error) {
|
|
23525
|
+
console.debug('[Platform] DeviceInfo error, using basic detection');
|
|
23526
|
+
}
|
|
23527
|
+
}
|
|
23528
|
+
return platform;
|
|
23529
|
+
}
|
|
23530
|
+
|
|
22610
23531
|
// Create the context
|
|
22611
23532
|
const SDKContext = react.createContext(null);
|
|
22612
23533
|
/**
|
|
@@ -22694,6 +23615,22 @@ const PersSDKProvider = ({ children, config }) => {
|
|
|
22694
23615
|
};
|
|
22695
23616
|
// Initialize PersSDK with platform-aware auth provider
|
|
22696
23617
|
const sdkInstance = new PersSDK(httpClient, sdkConfig);
|
|
23618
|
+
// Auto-configure data source and platform for React Native
|
|
23619
|
+
// Native apps MUST set channel as backend cannot auto-detect without browser User-Agent
|
|
23620
|
+
if (reactNative.Platform.OS !== 'web') {
|
|
23621
|
+
sdkInstance.setDataSource({
|
|
23622
|
+
channel: 'mobile',
|
|
23623
|
+
source: config.dataSource?.source || 'app',
|
|
23624
|
+
medium: config.dataSource?.medium,
|
|
23625
|
+
campaign: config.dataSource?.campaign,
|
|
23626
|
+
});
|
|
23627
|
+
// Auto-detect platform info asynchronously
|
|
23628
|
+
detectReactNativePlatform().then(platformInfo => {
|
|
23629
|
+
sdkInstance.setPlatform(platformInfo);
|
|
23630
|
+
}).catch(error => {
|
|
23631
|
+
console.debug('[PersSDK] Platform detection failed:', error);
|
|
23632
|
+
});
|
|
23633
|
+
}
|
|
22697
23634
|
// Subscribe to auth events BEFORE exposing SDK to consumers
|
|
22698
23635
|
// This ensures no events are missed due to React timing
|
|
22699
23636
|
eventUnsubscribeRef.current = sdkInstance.events.subscribe((event) => {
|
|
@@ -47477,7 +48414,13 @@ const useRedemptions = () => {
|
|
|
47477
48414
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
47478
48415
|
}
|
|
47479
48416
|
try {
|
|
47480
|
-
|
|
48417
|
+
// Convert Date to ISO string for API compatibility
|
|
48418
|
+
const apiOptions = options ? {
|
|
48419
|
+
...options,
|
|
48420
|
+
startDate: options.startDate instanceof Date ? options.startDate.toISOString() : options.startDate,
|
|
48421
|
+
endDate: options.endDate instanceof Date ? options.endDate.toISOString() : options.endDate,
|
|
48422
|
+
} : undefined;
|
|
48423
|
+
const result = await sdk.redemptions.getRedemptions(apiOptions);
|
|
47481
48424
|
return result;
|
|
47482
48425
|
}
|
|
47483
48426
|
catch (error) {
|
|
@@ -49379,6 +50322,158 @@ const useTriggerSources = () => {
|
|
|
49379
50322
|
};
|
|
49380
50323
|
};
|
|
49381
50324
|
|
|
50325
|
+
/**
|
|
50326
|
+
* React hook for booking management operations
|
|
50327
|
+
*
|
|
50328
|
+
* Provides methods to create, read, update, and delete bookings,
|
|
50329
|
+
* as well as checking if a user has a valid booking for redemption eligibility.
|
|
50330
|
+
*
|
|
50331
|
+
* @example
|
|
50332
|
+
* ```typescript
|
|
50333
|
+
* import { useBookings } from '@explorins/pers-sdk-react-native';
|
|
50334
|
+
*
|
|
50335
|
+
* function BookingScreen() {
|
|
50336
|
+
* const { getBookings, createBooking, hasValidBooking } = useBookings();
|
|
50337
|
+
*
|
|
50338
|
+
* // List all bookings for a user
|
|
50339
|
+
* const userBookings = await getBookings({ userId: 'user-123' });
|
|
50340
|
+
*
|
|
50341
|
+
* // Create a booking
|
|
50342
|
+
* const newBooking = await createBooking({
|
|
50343
|
+
* userId: 'user-123',
|
|
50344
|
+
* locationName: 'Resort Hotel',
|
|
50345
|
+
* checkInDate: '2026-06-01',
|
|
50346
|
+
* checkOutDate: '2026-06-05'
|
|
50347
|
+
* });
|
|
50348
|
+
*
|
|
50349
|
+
* // Check if user has valid booking for redemption
|
|
50350
|
+
* const isEligible = await hasValidBooking('user-123', { status: 'active' });
|
|
50351
|
+
* }
|
|
50352
|
+
* ```
|
|
50353
|
+
*/
|
|
50354
|
+
const useBookings = () => {
|
|
50355
|
+
const { sdk, isInitialized, isAuthenticated } = usePersSDK();
|
|
50356
|
+
/**
|
|
50357
|
+
* Get all bookings with optional filters
|
|
50358
|
+
*
|
|
50359
|
+
* @param options - Query options including userId, businessId, status filters
|
|
50360
|
+
* @returns Array of booking DTOs
|
|
50361
|
+
*/
|
|
50362
|
+
const getBookings = react.useCallback(async (options) => {
|
|
50363
|
+
if (!isInitialized || !sdk) {
|
|
50364
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
50365
|
+
}
|
|
50366
|
+
try {
|
|
50367
|
+
return await sdk.bookings.getAll(options);
|
|
50368
|
+
}
|
|
50369
|
+
catch (error) {
|
|
50370
|
+
console.error('Failed to fetch bookings:', error);
|
|
50371
|
+
throw error;
|
|
50372
|
+
}
|
|
50373
|
+
}, [sdk, isInitialized]);
|
|
50374
|
+
/**
|
|
50375
|
+
* Get a single booking by ID
|
|
50376
|
+
*
|
|
50377
|
+
* @param id - Booking UUID
|
|
50378
|
+
* @returns Booking DTO
|
|
50379
|
+
*/
|
|
50380
|
+
const getBookingById = react.useCallback(async (id) => {
|
|
50381
|
+
if (!isInitialized || !sdk) {
|
|
50382
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
50383
|
+
}
|
|
50384
|
+
try {
|
|
50385
|
+
return await sdk.bookings.getById(id);
|
|
50386
|
+
}
|
|
50387
|
+
catch (error) {
|
|
50388
|
+
console.error('Failed to fetch booking:', error);
|
|
50389
|
+
throw error;
|
|
50390
|
+
}
|
|
50391
|
+
}, [sdk, isInitialized]);
|
|
50392
|
+
/**
|
|
50393
|
+
* Create a new booking
|
|
50394
|
+
*
|
|
50395
|
+
* @param data - Booking data including userId, locationName, dates
|
|
50396
|
+
* @returns Created booking DTO
|
|
50397
|
+
*/
|
|
50398
|
+
const createBooking = react.useCallback(async (data) => {
|
|
50399
|
+
if (!isInitialized || !sdk) {
|
|
50400
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
50401
|
+
}
|
|
50402
|
+
try {
|
|
50403
|
+
return await sdk.bookings.create(data);
|
|
50404
|
+
}
|
|
50405
|
+
catch (error) {
|
|
50406
|
+
console.error('Failed to create booking:', error);
|
|
50407
|
+
throw error;
|
|
50408
|
+
}
|
|
50409
|
+
}, [sdk, isInitialized]);
|
|
50410
|
+
/**
|
|
50411
|
+
* Update an existing booking
|
|
50412
|
+
*
|
|
50413
|
+
* @param id - Booking UUID
|
|
50414
|
+
* @param data - Partial booking data to update
|
|
50415
|
+
* @returns Updated booking DTO
|
|
50416
|
+
*/
|
|
50417
|
+
const updateBooking = react.useCallback(async (id, data) => {
|
|
50418
|
+
if (!isInitialized || !sdk) {
|
|
50419
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
50420
|
+
}
|
|
50421
|
+
try {
|
|
50422
|
+
return await sdk.bookings.update(id, data);
|
|
50423
|
+
}
|
|
50424
|
+
catch (error) {
|
|
50425
|
+
console.error('Failed to update booking:', error);
|
|
50426
|
+
throw error;
|
|
50427
|
+
}
|
|
50428
|
+
}, [sdk, isInitialized]);
|
|
50429
|
+
/**
|
|
50430
|
+
* Delete a booking
|
|
50431
|
+
*
|
|
50432
|
+
* @param id - Booking UUID
|
|
50433
|
+
*/
|
|
50434
|
+
const deleteBooking = react.useCallback(async (id) => {
|
|
50435
|
+
if (!isInitialized || !sdk) {
|
|
50436
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
50437
|
+
}
|
|
50438
|
+
try {
|
|
50439
|
+
await sdk.bookings.delete(id);
|
|
50440
|
+
}
|
|
50441
|
+
catch (error) {
|
|
50442
|
+
console.error('Failed to delete booking:', error);
|
|
50443
|
+
throw error;
|
|
50444
|
+
}
|
|
50445
|
+
}, [sdk, isInitialized]);
|
|
50446
|
+
/**
|
|
50447
|
+
* Check if a user has a valid booking
|
|
50448
|
+
*
|
|
50449
|
+
* @param userId - User ID to check
|
|
50450
|
+
* @param options - Validation options including status filter
|
|
50451
|
+
* @returns true if user has valid booking matching criteria
|
|
50452
|
+
*/
|
|
50453
|
+
const hasValidBooking = react.useCallback(async (userId, options) => {
|
|
50454
|
+
if (!isInitialized || !sdk) {
|
|
50455
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
50456
|
+
}
|
|
50457
|
+
try {
|
|
50458
|
+
return await sdk.bookings.hasValidBooking(userId, options);
|
|
50459
|
+
}
|
|
50460
|
+
catch (error) {
|
|
50461
|
+
console.error('Failed to check booking validity:', error);
|
|
50462
|
+
throw error;
|
|
50463
|
+
}
|
|
50464
|
+
}, [sdk, isInitialized]);
|
|
50465
|
+
return {
|
|
50466
|
+
getBookings,
|
|
50467
|
+
getBookingById,
|
|
50468
|
+
createBooking,
|
|
50469
|
+
updateBooking,
|
|
50470
|
+
deleteBooking,
|
|
50471
|
+
hasValidBooking,
|
|
50472
|
+
isInitialized,
|
|
50473
|
+
isAuthenticated,
|
|
50474
|
+
};
|
|
50475
|
+
};
|
|
50476
|
+
|
|
49382
50477
|
exports.ACTIVE_USER_DEFINITION = ACTIVE_USER_DEFINITION;
|
|
49383
50478
|
exports.ANALYTICS_GROUP_BY_TYPES = ANALYTICS_GROUP_BY_TYPES;
|
|
49384
50479
|
exports.ANALYTICS_METRIC_TYPES = ANALYTICS_METRIC_TYPES;
|
|
@@ -49447,6 +50542,7 @@ exports.IMAGE_CDN_MAX_INPUT_SIZE = IMAGE_CDN_MAX_INPUT_SIZE;
|
|
|
49447
50542
|
exports.IMAGE_CDN_PRESETS = IMAGE_CDN_PRESETS;
|
|
49448
50543
|
exports.IMAGE_CDN_SUPPORTED_FORMATS = IMAGE_CDN_SUPPORTED_FORMATS;
|
|
49449
50544
|
exports.IndexedDBTokenStorage = IndexedDBTokenStorage;
|
|
50545
|
+
exports.KNOWN_SOURCE_DOMAINS = KNOWN_SOURCE_DOMAINS;
|
|
49450
50546
|
exports.LOCATION_TYPES = LOCATION_TYPES;
|
|
49451
50547
|
exports.LocalStorageTokenStorage = LocalStorageTokenStorage;
|
|
49452
50548
|
exports.LogoutRequired = LogoutRequired;
|
|
@@ -49476,6 +50572,7 @@ exports.RedemptionService = RedemptionService;
|
|
|
49476
50572
|
exports.SDK_NAME = SDK_NAME;
|
|
49477
50573
|
exports.SDK_USER_AGENT = SDK_USER_AGENT;
|
|
49478
50574
|
exports.SDK_VERSION = SDK_VERSION;
|
|
50575
|
+
exports.SELECT_OPTIONS_LABEL_DEFAULTS = SELECT_OPTIONS_LABEL_DEFAULTS;
|
|
49479
50576
|
exports.SOURCE_LOGIC_TYPES = SOURCE_LOGIC_TYPES;
|
|
49480
50577
|
exports.SOURCE_LOGIC_TYPE_VALUES = SOURCE_LOGIC_TYPE_VALUES;
|
|
49481
50578
|
exports.SdkErrorCodes = SdkErrorCodes;
|
|
@@ -49494,6 +50591,7 @@ exports.TokenRefreshNeeded = TokenRefreshNeeded;
|
|
|
49494
50591
|
exports.TokenSDK = TokenSDK;
|
|
49495
50592
|
exports.TokenService = TokenService;
|
|
49496
50593
|
exports.TokenValidityTypes = TokenValidityTypes;
|
|
50594
|
+
exports.TrackingManager = TrackingManager;
|
|
49497
50595
|
exports.TransactionApi = TransactionApi;
|
|
49498
50596
|
exports.TransactionManager = TransactionManager;
|
|
49499
50597
|
exports.TransactionService = TransactionService;
|
|
@@ -49545,8 +50643,12 @@ exports.createReactNativeAuthProvider = createReactNativeAuthProvider;
|
|
|
49545
50643
|
exports.createUserIdentifierObject = createUserIdentifierObject;
|
|
49546
50644
|
exports.createUserStatusSDK = createUserStatusSDK;
|
|
49547
50645
|
exports.customFieldEntityTypes = customFieldEntityTypes;
|
|
50646
|
+
exports.detectChannelFromUserAgent = detectChannelFromUserAgent;
|
|
49548
50647
|
exports.detectEnvironment = detectEnvironment;
|
|
49549
50648
|
exports.detectIdentifierType = detectIdentifierType;
|
|
50649
|
+
exports.detectReactNativePlatform = detectReactNativePlatform;
|
|
50650
|
+
exports.detectSourceFromReferrer = detectSourceFromReferrer;
|
|
50651
|
+
exports.detectWebPlatform = detectWebPlatform;
|
|
49550
50652
|
exports.environment = environment;
|
|
49551
50653
|
exports.extractData = extractData;
|
|
49552
50654
|
exports.extractDeadlineFromSigningData = extractDeadlineFromSigningData;
|
|
@@ -49558,10 +50660,12 @@ exports.getBuiltInFieldDefinition = getBuiltInFieldDefinition;
|
|
|
49558
50660
|
exports.getMetadataFromTokenUnitResponse = getMetadataFromTokenUnitResponse;
|
|
49559
50661
|
exports.getOperatorText = getOperatorText;
|
|
49560
50662
|
exports.getPresetDimensions = getPresetDimensions;
|
|
50663
|
+
exports.getSelectOptionsLabelDefault = getSelectOptionsLabelDefault;
|
|
49561
50664
|
exports.getSigningUrl = getSigningUrl;
|
|
49562
50665
|
exports.globalCacheService = globalCacheService;
|
|
49563
50666
|
exports.hasMinimumRole = hasMinimumRole;
|
|
49564
50667
|
exports.initializeReactNativePolyfills = initializeReactNativePolyfills;
|
|
50668
|
+
exports.isBookingStatusValid = isBookingStatusValid;
|
|
49565
50669
|
exports.isBuiltInUserField = isBuiltInUserField;
|
|
49566
50670
|
exports.isConnectedMessage = isConnectedMessage;
|
|
49567
50671
|
exports.isDataSourceChannel = isDataSourceChannel;
|
|
@@ -49598,6 +50702,7 @@ exports.needsSubmission = needsSubmission;
|
|
|
49598
50702
|
exports.normalizeFilters = normalizeFilters;
|
|
49599
50703
|
exports.normalizeToPaginated = normalizeToPaginated;
|
|
49600
50704
|
exports.operatorText = operatorText;
|
|
50705
|
+
exports.parseUtmParams = parseUtmParams;
|
|
49601
50706
|
exports.registerIdentifierType = registerIdentifierType;
|
|
49602
50707
|
exports.selectOptionsSourceEntities = selectOptionsSourceEntities;
|
|
49603
50708
|
exports.testnetPrefix = testnetPrefix;
|
|
@@ -49605,6 +50710,7 @@ exports.testnetShortPrefix = testnetShortPrefix;
|
|
|
49605
50710
|
exports.uniqueUserIdentifierKeys = uniqueUserIdentifierKeys;
|
|
49606
50711
|
exports.useAnalytics = useAnalytics;
|
|
49607
50712
|
exports.useAuth = useAuth;
|
|
50713
|
+
exports.useBookings = useBookings;
|
|
49608
50714
|
exports.useBusiness = useBusiness;
|
|
49609
50715
|
exports.useCampaigns = useCampaigns;
|
|
49610
50716
|
exports.useDonations = useDonations;
|