@thead-vantage/react 2.13.0 → 2.15.0
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/package.json +1 -1
- package/src/components/AdBanner.tsx +2 -2
- package/src/components/AdDisplay.tsx +13 -2
- package/src/lib/ads.ts +52 -12
package/package.json
CHANGED
|
@@ -58,7 +58,7 @@ export function AdBanner({
|
|
|
58
58
|
setDevMode(response.dev_mode || false);
|
|
59
59
|
|
|
60
60
|
// Track impression (will be skipped in dev mode)
|
|
61
|
-
trackImpression(response.ad.id);
|
|
61
|
+
trackImpression(response.ad.id, apiKey, apiUrl);
|
|
62
62
|
} else {
|
|
63
63
|
// Create a more detailed error message
|
|
64
64
|
const errorDetails = [];
|
|
@@ -106,7 +106,7 @@ export function AdBanner({
|
|
|
106
106
|
|
|
107
107
|
const handleClick = () => {
|
|
108
108
|
if (ad) {
|
|
109
|
-
trackClick(ad.id);
|
|
109
|
+
trackClick(ad.id, apiKey, apiUrl);
|
|
110
110
|
// The link will handle navigation
|
|
111
111
|
}
|
|
112
112
|
};
|
|
@@ -42,7 +42,14 @@ export default function AdDisplay({ position, className = '' }: AdDisplayProps)
|
|
|
42
42
|
setDevMode(response.dev_mode || false);
|
|
43
43
|
|
|
44
44
|
// Track impression (will be skipped in dev mode)
|
|
45
|
-
|
|
45
|
+
// Note: AdDisplay doesn't have apiKey, so tracking will fail gracefully
|
|
46
|
+
// This component may need to be updated to accept apiKey prop if tracking is needed
|
|
47
|
+
if (response.ad.id) {
|
|
48
|
+
// Silently fail if no API key available
|
|
49
|
+
trackImpression(response.ad.id, '').catch(() => {
|
|
50
|
+
// Tracking failed - this is expected without API key
|
|
51
|
+
});
|
|
52
|
+
}
|
|
46
53
|
} else {
|
|
47
54
|
setError('No ad available');
|
|
48
55
|
}
|
|
@@ -59,7 +66,11 @@ export default function AdDisplay({ position, className = '' }: AdDisplayProps)
|
|
|
59
66
|
|
|
60
67
|
const handleClick = () => {
|
|
61
68
|
if (ad) {
|
|
62
|
-
|
|
69
|
+
// Note: AdDisplay doesn't have apiKey, so tracking will fail gracefully
|
|
70
|
+
// This component may need to be updated to accept apiKey prop if tracking is needed
|
|
71
|
+
trackClick(ad.id, '').catch(() => {
|
|
72
|
+
// Tracking failed - this is expected without API key
|
|
73
|
+
});
|
|
63
74
|
// The link will handle navigation
|
|
64
75
|
}
|
|
65
76
|
};
|
package/src/lib/ads.ts
CHANGED
|
@@ -363,11 +363,20 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
|
|
|
363
363
|
/**
|
|
364
364
|
* Track an ad impression (when ad is viewed)
|
|
365
365
|
* Sends tracking directly to TheAd Vantage API (or skips in dev mode)
|
|
366
|
+
*
|
|
367
|
+
* @param adId - The ID of the ad being tracked
|
|
368
|
+
* @param apiKey - The API key for the platform (required for CORS validation and platform identification)
|
|
369
|
+
* @param apiUrl - Optional API base URL override
|
|
366
370
|
*/
|
|
367
|
-
export async function trackImpression(adId: string): Promise<void> {
|
|
371
|
+
export async function trackImpression(adId: string, apiKey: string, apiUrl?: string): Promise<void> {
|
|
368
372
|
try {
|
|
373
|
+
if (!apiKey) {
|
|
374
|
+
console.warn('[AdBanner] Cannot track impression: API key is required');
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
369
378
|
// Get the API base URL to determine where to send tracking
|
|
370
|
-
const apiBaseUrl = getApiBaseUrl();
|
|
379
|
+
const apiBaseUrl = getApiBaseUrl(apiUrl);
|
|
371
380
|
|
|
372
381
|
// Build tracking URL - remove /api/ads if present, then append /api/ads/track
|
|
373
382
|
let trackingUrl = apiBaseUrl.trim();
|
|
@@ -387,6 +396,10 @@ export async function trackImpression(adId: string): Promise<void> {
|
|
|
387
396
|
trackingUrl = trackingUrl.replace('thead-vantage.com', 'www.thead-vantage.com');
|
|
388
397
|
}
|
|
389
398
|
|
|
399
|
+
// Add API key to query string (recommended for CORS preflight validation)
|
|
400
|
+
const url = new URL(trackingUrl);
|
|
401
|
+
url.searchParams.set('api_key', apiKey);
|
|
402
|
+
|
|
390
403
|
// Check if we should skip tracking (dev mode)
|
|
391
404
|
const useDevFlags = shouldUseDevFlags();
|
|
392
405
|
if (useDevFlags) {
|
|
@@ -394,7 +407,7 @@ export async function trackImpression(adId: string): Promise<void> {
|
|
|
394
407
|
return;
|
|
395
408
|
}
|
|
396
409
|
|
|
397
|
-
const response = await fetch(
|
|
410
|
+
const response = await fetch(url.toString(), {
|
|
398
411
|
method: 'POST',
|
|
399
412
|
mode: 'cors', // Explicitly enable CORS
|
|
400
413
|
credentials: 'omit', // Don't send cookies
|
|
@@ -409,18 +422,25 @@ export async function trackImpression(adId: string): Promise<void> {
|
|
|
409
422
|
});
|
|
410
423
|
|
|
411
424
|
if (!response.ok) {
|
|
412
|
-
|
|
425
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
426
|
+
console.warn(`[AdBanner] Tracking impression failed: ${response.status} ${response.statusText}`, {
|
|
427
|
+
errorText: errorText.substring(0, 200),
|
|
428
|
+
});
|
|
413
429
|
return;
|
|
414
430
|
}
|
|
415
431
|
|
|
416
432
|
const data = await response.json();
|
|
417
433
|
if (data.dev_mode) {
|
|
418
434
|
console.log(`[DEV] Impression tracking skipped for ad: ${adId}`);
|
|
435
|
+
} else {
|
|
436
|
+
console.debug(`[AdBanner] Impression tracked successfully for ad: ${adId}`);
|
|
419
437
|
}
|
|
420
438
|
} catch (error) {
|
|
421
|
-
// Handle CORS errors
|
|
439
|
+
// Handle CORS errors gracefully (tracking failures shouldn't break the app)
|
|
422
440
|
if (error instanceof TypeError && (error.message.includes('CORS') || error.message.includes('Failed to fetch'))) {
|
|
423
|
-
|
|
441
|
+
// This is expected until the server configures CORS for /api/ads/track endpoint
|
|
442
|
+
// The ad still displays correctly, tracking just won't work until server-side CORS is fixed
|
|
443
|
+
console.debug(`[AdBanner] Tracking impression failed (CORS): The /api/ads/track endpoint needs CORS headers configured on the server. Ad still displayed successfully.`);
|
|
424
444
|
} else {
|
|
425
445
|
console.error('Error tracking impression:', error);
|
|
426
446
|
}
|
|
@@ -431,11 +451,20 @@ export async function trackImpression(adId: string): Promise<void> {
|
|
|
431
451
|
/**
|
|
432
452
|
* Track an ad click
|
|
433
453
|
* Sends tracking directly to TheAd Vantage API (or skips in dev mode)
|
|
454
|
+
*
|
|
455
|
+
* @param adId - The ID of the ad being tracked
|
|
456
|
+
* @param apiKey - The API key for the platform (required for CORS validation and platform identification)
|
|
457
|
+
* @param apiUrl - Optional API base URL override
|
|
434
458
|
*/
|
|
435
|
-
export async function trackClick(adId: string): Promise<void> {
|
|
459
|
+
export async function trackClick(adId: string, apiKey: string, apiUrl?: string): Promise<void> {
|
|
436
460
|
try {
|
|
461
|
+
if (!apiKey) {
|
|
462
|
+
console.warn('[AdBanner] Cannot track click: API key is required');
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
437
466
|
// Get the API base URL to determine where to send tracking
|
|
438
|
-
const apiBaseUrl = getApiBaseUrl();
|
|
467
|
+
const apiBaseUrl = getApiBaseUrl(apiUrl);
|
|
439
468
|
|
|
440
469
|
// Build tracking URL - remove /api/ads if present, then append /api/ads/track
|
|
441
470
|
let trackingUrl = apiBaseUrl.trim();
|
|
@@ -455,6 +484,10 @@ export async function trackClick(adId: string): Promise<void> {
|
|
|
455
484
|
trackingUrl = trackingUrl.replace('thead-vantage.com', 'www.thead-vantage.com');
|
|
456
485
|
}
|
|
457
486
|
|
|
487
|
+
// Add API key to query string (recommended for CORS preflight validation)
|
|
488
|
+
const url = new URL(trackingUrl);
|
|
489
|
+
url.searchParams.set('api_key', apiKey);
|
|
490
|
+
|
|
458
491
|
// Check if we should skip tracking (dev mode)
|
|
459
492
|
const useDevFlags = shouldUseDevFlags();
|
|
460
493
|
if (useDevFlags) {
|
|
@@ -462,7 +495,7 @@ export async function trackClick(adId: string): Promise<void> {
|
|
|
462
495
|
return;
|
|
463
496
|
}
|
|
464
497
|
|
|
465
|
-
const response = await fetch(
|
|
498
|
+
const response = await fetch(url.toString(), {
|
|
466
499
|
method: 'POST',
|
|
467
500
|
mode: 'cors', // Explicitly enable CORS
|
|
468
501
|
credentials: 'omit', // Don't send cookies
|
|
@@ -477,18 +510,25 @@ export async function trackClick(adId: string): Promise<void> {
|
|
|
477
510
|
});
|
|
478
511
|
|
|
479
512
|
if (!response.ok) {
|
|
480
|
-
|
|
513
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
514
|
+
console.warn(`[AdBanner] Tracking click failed: ${response.status} ${response.statusText}`, {
|
|
515
|
+
errorText: errorText.substring(0, 200),
|
|
516
|
+
});
|
|
481
517
|
return;
|
|
482
518
|
}
|
|
483
519
|
|
|
484
520
|
const data = await response.json();
|
|
485
521
|
if (data.dev_mode) {
|
|
486
522
|
console.log(`[DEV] Click tracking skipped for ad: ${adId}`);
|
|
523
|
+
} else {
|
|
524
|
+
console.debug(`[AdBanner] Click tracked successfully for ad: ${adId}`);
|
|
487
525
|
}
|
|
488
526
|
} catch (error) {
|
|
489
|
-
// Handle CORS errors
|
|
527
|
+
// Handle CORS errors gracefully (tracking failures shouldn't break the app)
|
|
490
528
|
if (error instanceof TypeError && (error.message.includes('CORS') || error.message.includes('Failed to fetch'))) {
|
|
491
|
-
|
|
529
|
+
// This is expected until the server configures CORS for /api/ads/track endpoint
|
|
530
|
+
// The ad still works correctly, click tracking just won't work until server-side CORS is fixed
|
|
531
|
+
console.debug(`[AdBanner] Tracking click failed (CORS): The /api/ads/track endpoint needs CORS headers configured on the server. Ad still works correctly.`);
|
|
492
532
|
} else {
|
|
493
533
|
console.error('Error tracking click:', error);
|
|
494
534
|
}
|