react-native-inapp-inspector 1.1.29 → 1.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/commonjs/components/AnalyticsDetail.js +668 -108
  2. package/dist/commonjs/components/AppHeaderLogo.js +5 -2
  3. package/dist/commonjs/components/ConsoleLogCard.js +81 -59
  4. package/dist/commonjs/components/DomainHeader.js +21 -17
  5. package/dist/commonjs/components/Inspector/AnalyticsTab.js +297 -200
  6. package/dist/commonjs/components/Inspector/ConsoleTab.js +162 -56
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +56 -119
  8. package/dist/commonjs/components/Inspector/LogDetail.js +45 -12
  9. package/dist/commonjs/components/Inspector/NetworkTab.js +4 -1
  10. package/dist/commonjs/components/Inspector/ReduxDetail.js +237 -42
  11. package/dist/commonjs/components/Inspector/ReduxTab.js +456 -35
  12. package/dist/commonjs/components/JsonViewer.js +1 -1
  13. package/dist/commonjs/components/NetworkIcons.js +19 -10
  14. package/dist/commonjs/components/TreeNode.js +12 -2
  15. package/dist/commonjs/constants/version.d.ts +1 -1
  16. package/dist/commonjs/constants/version.js +1 -1
  17. package/dist/commonjs/customHooks/reduxLogger.js +128 -5
  18. package/dist/commonjs/helpers/index.d.ts +1 -1
  19. package/dist/commonjs/helpers/index.js +32 -16
  20. package/dist/commonjs/i18n/locales/en.json +45 -2
  21. package/dist/commonjs/styles/AppColors.d.ts +6 -0
  22. package/dist/commonjs/styles/AppColors.js +6 -0
  23. package/dist/commonjs/styles/index.d.ts +22 -1
  24. package/dist/commonjs/styles/index.js +25 -4
  25. package/dist/commonjs/types/interfaces.d.ts +7 -0
  26. package/dist/esm/components/AnalyticsDetail.js +670 -110
  27. package/dist/esm/components/AppHeaderLogo.js +5 -2
  28. package/dist/esm/components/ConsoleLogCard.js +81 -59
  29. package/dist/esm/components/DomainHeader.js +21 -17
  30. package/dist/esm/components/Inspector/AnalyticsTab.js +298 -201
  31. package/dist/esm/components/Inspector/ConsoleTab.js +163 -57
  32. package/dist/esm/components/Inspector/InspectorHeader.js +57 -120
  33. package/dist/esm/components/Inspector/LogDetail.js +45 -12
  34. package/dist/esm/components/Inspector/NetworkTab.js +4 -1
  35. package/dist/esm/components/Inspector/ReduxDetail.js +239 -44
  36. package/dist/esm/components/Inspector/ReduxTab.js +458 -37
  37. package/dist/esm/components/JsonViewer.js +1 -1
  38. package/dist/esm/components/NetworkIcons.js +19 -10
  39. package/dist/esm/components/TreeNode.js +12 -2
  40. package/dist/esm/constants/version.d.ts +1 -1
  41. package/dist/esm/constants/version.js +1 -1
  42. package/dist/esm/customHooks/reduxLogger.js +128 -5
  43. package/dist/esm/helpers/index.d.ts +1 -1
  44. package/dist/esm/helpers/index.js +32 -16
  45. package/dist/esm/i18n/locales/en.json +45 -2
  46. package/dist/esm/styles/AppColors.d.ts +6 -0
  47. package/dist/esm/styles/AppColors.js +6 -0
  48. package/dist/esm/styles/index.d.ts +22 -1
  49. package/dist/esm/styles/index.js +25 -4
  50. package/dist/esm/types/interfaces.d.ts +7 -0
  51. package/package.json +11 -11
@@ -13,7 +13,7 @@ import Svg, { Path, Rect, Line, Circle, G, Defs, LinearGradient as SvgLinearGrad
13
13
  import TouchableScale from '../TouchableScale';
14
14
  import { getCurrentUserId, getCurrentUserProperties, getDefaultEventParameters, getCollectionEnabled, } from '../../customHooks/analyticsLogger';
15
15
  import { getEventCategory, } from '../../helpers';
16
- import { SearchIcon, ClearIcon, TrashIcon, EmptyRadarIcon, HeaderPauseIcon, FilterIcon, CartIcon, GlobeIcon, BoltIcon, SparkleIcon, PinIcon, MoneyIcon, } from '../NetworkIcons';
16
+ import { LayersIcon, CheckIcon, SearchIcon, ClearIcon, TrashIcon, EmptyRadarIcon, HeaderPauseIcon, FilterIcon, CartIcon, GlobeIcon, BoltIcon, SparkleIcon, PinIcon, MoneyIcon, } from '../NetworkIcons';
17
17
  const AnalyticsHeader = React.memo(() => {
18
18
  const { t } = useTranslation();
19
19
  const { analyticsEvents, filteredAnalyticsEvents, analyticsFilters, setAnalyticsFilters, analyticsHeaderExpanded, setAnalyticsHeaderExpanded, } = useInspector();
@@ -87,17 +87,36 @@ const AnalyticsHeader = React.memo(() => {
87
87
  barWidthPct: Math.round((count / maxVal) * 100),
88
88
  }));
89
89
  }, [filteredAnalyticsEvents]);
90
- // Toggle category filter from top button group
90
+ // Multi-select category toggle from top button group
91
91
  const toggleCategoryFilter = (cat) => {
92
92
  animateNextLayout();
93
93
  setAnalyticsFilters(prev => {
94
- const isSelected = !prev.categories.has('all') && prev.categories.has(cat);
95
- if (isSelected) {
94
+ if (cat === 'all') {
96
95
  return { ...prev, categories: new Set(['all']) };
97
96
  }
97
+ const nextCategories = new Set(prev.categories);
98
+ if (nextCategories.has('all')) {
99
+ nextCategories.delete('all');
100
+ nextCategories.add(cat);
101
+ }
98
102
  else {
99
- return { ...prev, categories: new Set([cat]) };
103
+ if (nextCategories.has(cat)) {
104
+ nextCategories.delete(cat);
105
+ if (nextCategories.size === 0) {
106
+ nextCategories.add('all');
107
+ }
108
+ }
109
+ else {
110
+ nextCategories.add(cat);
111
+ if (nextCategories.has('ecommerce') &&
112
+ nextCategories.has('page_view') &&
113
+ nextCategories.has('system') &&
114
+ nextCategories.has('custom')) {
115
+ return { ...prev, categories: new Set(['all']) };
116
+ }
117
+ }
100
118
  }
119
+ return { ...prev, categories: nextCategories };
101
120
  });
102
121
  };
103
122
  // 12-Bucket GA4 Realtime Stacked Histogram & Spline Wave Graph (-30m / 30s to NOW)
@@ -177,7 +196,7 @@ const AnalyticsHeader = React.memo(() => {
177
196
  }, [filteredAnalyticsEvents, chartHeight, svgWidth, t]);
178
197
  const selectedBucket = selectedBucketIdx != null ? histogramData.buckets[selectedBucketIdx] : null;
179
198
  return (<View style={styles.analyticsHeaderCard}>
180
- {/* ── Top Header Row (GA4 Style) ─────────────────────────────────────── */}
199
+ {/* ── 1. Top Header Row (GA4 Style) ─────────────────────────────────── */}
181
200
  <View style={styles.analyticsHeaderTop}>
182
201
  <View style={{ flex: 1 }}>
183
202
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
@@ -217,9 +236,280 @@ const AnalyticsHeader = React.memo(() => {
217
236
  </TouchableOpacity>
218
237
  </View>
219
238
 
220
- {/* ── GA4 Realtime Metric Hero & Activity Chart ─────────────────────── */}
239
+ {/* ── 2. Multi-Select Category Filter Buttons (AT THE TOP) ──────────── */}
240
+ <View style={{ marginTop: 10, marginBottom: 8 }}>
241
+ {/* Multi-color Proportional Segmented Bar */}
242
+ <View style={{
243
+ height: 4.5,
244
+ borderRadius: 2.5,
245
+ backgroundColor: `${AppColors.grayBorderSecondary}40`,
246
+ flexDirection: 'row',
247
+ overflow: 'hidden',
248
+ marginBottom: 8,
249
+ }}>
250
+ {categoryStats.ecommerce > 0 && (<View style={{
251
+ flex: categoryStats.ecommerce,
252
+ backgroundColor: AppColors.amber500,
253
+ }}/>)}
254
+ {categoryStats.page_view > 0 && (<View style={{
255
+ flex: categoryStats.page_view,
256
+ backgroundColor: AppColors.sky500,
257
+ }}/>)}
258
+ {categoryStats.system > 0 && (<View style={{
259
+ flex: categoryStats.system,
260
+ backgroundColor: AppColors.purple,
261
+ }}/>)}
262
+ {categoryStats.custom > 0 && (<View style={{
263
+ flex: categoryStats.custom,
264
+ backgroundColor: AppColors.brandPurple,
265
+ }}/>)}
266
+ </View>
267
+
268
+ {/* Multi-Select Category Buttons with Checkboxes */}
269
+ <View style={{
270
+ flexDirection: 'row',
271
+ flexWrap: 'wrap',
272
+ gap: 6,
273
+ }}>
274
+ {/* All Button */}
275
+ {(() => {
276
+ const isAllSelected = analyticsFilters.categories.has('all');
277
+ return (<TouchableScale onPress={() => toggleCategoryFilter('all')} style={[
278
+ {
279
+ flexDirection: 'row',
280
+ alignItems: 'center',
281
+ gap: 5,
282
+ backgroundColor: isAllSelected ? AppColors.indigo600Alt : AppColors.indigo50,
283
+ paddingHorizontal: 8,
284
+ paddingVertical: 5,
285
+ borderRadius: 7,
286
+ borderWidth: 1,
287
+ borderColor: isAllSelected ? AppColors.indigo600 : AppColors.indigo400,
288
+ },
289
+ isAllSelected && {
290
+ shadowColor: AppColors.indigo600Alt,
291
+ shadowOffset: { width: 0, height: 1 },
292
+ shadowOpacity: 0.28,
293
+ shadowRadius: 2.5,
294
+ elevation: 2,
295
+ },
296
+ ]}>
297
+ <View style={{
298
+ width: 12,
299
+ height: 12,
300
+ borderRadius: 3,
301
+ borderWidth: 1.2,
302
+ borderColor: isAllSelected ? AppColors.white : AppColors.indigo400,
303
+ backgroundColor: isAllSelected ? AppColors.white : 'transparent',
304
+ alignItems: 'center',
305
+ justifyContent: 'center',
306
+ }}>
307
+ {isAllSelected && <CheckIcon color={AppColors.indigo600Alt} size={8}/>}
308
+ </View>
309
+ <LayersIcon color={isAllSelected ? AppColors.white : AppColors.indigo600Alt} size={11}/>
310
+ <Text style={{
311
+ fontFamily: AppFonts.interBold,
312
+ fontSize: 10,
313
+ color: isAllSelected ? AppColors.white : AppColors.indigo600Alt,
314
+ }}>
315
+ {t('analytics.allCategory')}: {categoryStats.totalCount}
316
+ </Text>
317
+ </TouchableScale>);
318
+ })()}
319
+
320
+ {/* Ecommerce Button */}
321
+ {(() => {
322
+ const isSelected = !analyticsFilters.categories.has('all') &&
323
+ analyticsFilters.categories.has('ecommerce');
324
+ return (<TouchableScale onPress={() => toggleCategoryFilter('ecommerce')} style={[
325
+ {
326
+ flexDirection: 'row',
327
+ alignItems: 'center',
328
+ gap: 5,
329
+ backgroundColor: isSelected ? AppColors.amber600 : AppColors.amber100,
330
+ paddingHorizontal: 8,
331
+ paddingVertical: 5,
332
+ borderRadius: 7,
333
+ borderWidth: 1,
334
+ borderColor: isSelected ? AppColors.amber700 : AppColors.amber200,
335
+ },
336
+ isSelected && {
337
+ shadowColor: AppColors.amber600,
338
+ shadowOffset: { width: 0, height: 1 },
339
+ shadowOpacity: 0.28,
340
+ shadowRadius: 2.5,
341
+ elevation: 2,
342
+ },
343
+ ]}>
344
+ <View style={{
345
+ width: 12,
346
+ height: 12,
347
+ borderRadius: 3,
348
+ borderWidth: 1.2,
349
+ borderColor: isSelected ? AppColors.white : AppColors.amber500,
350
+ backgroundColor: isSelected ? AppColors.white : 'transparent',
351
+ alignItems: 'center',
352
+ justifyContent: 'center',
353
+ }}>
354
+ {isSelected && <CheckIcon color={AppColors.amber600} size={8}/>}
355
+ </View>
356
+ <CartIcon color={isSelected ? AppColors.white : AppColors.amber700} size={11}/>
357
+ <Text style={{
358
+ fontFamily: AppFonts.interBold,
359
+ fontSize: 10,
360
+ color: isSelected ? AppColors.white : AppColors.amber800Warm,
361
+ }}>
362
+ {t('analytics.ecommerceCategory')}: {categoryStats.ecommerce} ({categoryStats.ecommercePct}%)
363
+ </Text>
364
+ </TouchableScale>);
365
+ })()}
366
+
367
+ {/* Screens Button */}
368
+ {(() => {
369
+ const isSelected = !analyticsFilters.categories.has('all') &&
370
+ analyticsFilters.categories.has('page_view');
371
+ return (<TouchableScale onPress={() => toggleCategoryFilter('page_view')} style={[
372
+ {
373
+ flexDirection: 'row',
374
+ alignItems: 'center',
375
+ gap: 5,
376
+ backgroundColor: isSelected ? AppColors.sky600 : AppColors.sky100,
377
+ paddingHorizontal: 8,
378
+ paddingVertical: 5,
379
+ borderRadius: 7,
380
+ borderWidth: 1,
381
+ borderColor: isSelected ? AppColors.blue700 : AppColors.sky400,
382
+ },
383
+ isSelected && {
384
+ shadowColor: AppColors.sky600,
385
+ shadowOffset: { width: 0, height: 1 },
386
+ shadowOpacity: 0.28,
387
+ shadowRadius: 2.5,
388
+ elevation: 2,
389
+ },
390
+ ]}>
391
+ <View style={{
392
+ width: 12,
393
+ height: 12,
394
+ borderRadius: 3,
395
+ borderWidth: 1.2,
396
+ borderColor: isSelected ? AppColors.white : AppColors.sky400,
397
+ backgroundColor: isSelected ? AppColors.white : 'transparent',
398
+ alignItems: 'center',
399
+ justifyContent: 'center',
400
+ }}>
401
+ {isSelected && <CheckIcon color={AppColors.sky600} size={8}/>}
402
+ </View>
403
+ <GlobeIcon color={isSelected ? AppColors.white : AppColors.sky600} size={11}/>
404
+ <Text style={{
405
+ fontFamily: AppFonts.interBold,
406
+ fontSize: 10,
407
+ color: isSelected ? AppColors.white : AppColors.sky600,
408
+ }}>
409
+ {t('analytics.screensCategory')}: {categoryStats.page_view} ({categoryStats.page_viewPct}%)
410
+ </Text>
411
+ </TouchableScale>);
412
+ })()}
413
+
414
+ {/* System Button */}
415
+ {(() => {
416
+ const isSelected = !analyticsFilters.categories.has('all') &&
417
+ analyticsFilters.categories.has('system');
418
+ return (<TouchableScale onPress={() => toggleCategoryFilter('system')} style={[
419
+ {
420
+ flexDirection: 'row',
421
+ alignItems: 'center',
422
+ gap: 5,
423
+ backgroundColor: isSelected ? AppColors.violet600 : AppColors.purple100,
424
+ paddingHorizontal: 8,
425
+ paddingVertical: 5,
426
+ borderRadius: 7,
427
+ borderWidth: 1,
428
+ borderColor: isSelected ? AppColors.purpleShade700 : AppColors.purple200,
429
+ },
430
+ isSelected && {
431
+ shadowColor: AppColors.violet600,
432
+ shadowOffset: { width: 0, height: 1 },
433
+ shadowOpacity: 0.28,
434
+ shadowRadius: 2.5,
435
+ elevation: 2,
436
+ },
437
+ ]}>
438
+ <View style={{
439
+ width: 12,
440
+ height: 12,
441
+ borderRadius: 3,
442
+ borderWidth: 1.2,
443
+ borderColor: isSelected ? AppColors.white : AppColors.purple400,
444
+ backgroundColor: isSelected ? AppColors.white : 'transparent',
445
+ alignItems: 'center',
446
+ justifyContent: 'center',
447
+ }}>
448
+ {isSelected && <CheckIcon color={AppColors.violet600} size={8}/>}
449
+ </View>
450
+ <BoltIcon color={isSelected ? AppColors.white : AppColors.purple} size={11}/>
451
+ <Text style={{
452
+ fontFamily: AppFonts.interBold,
453
+ fontSize: 10,
454
+ color: isSelected ? AppColors.white : AppColors.brandPurple,
455
+ }}>
456
+ {t('analytics.systemCategory')}: {categoryStats.system} ({categoryStats.systemPct}%)
457
+ </Text>
458
+ </TouchableScale>);
459
+ })()}
460
+
461
+ {/* Custom Button */}
462
+ {(() => {
463
+ const isSelected = !analyticsFilters.categories.has('all') &&
464
+ analyticsFilters.categories.has('custom');
465
+ return (<TouchableScale onPress={() => toggleCategoryFilter('custom')} style={[
466
+ {
467
+ flexDirection: 'row',
468
+ alignItems: 'center',
469
+ gap: 5,
470
+ backgroundColor: isSelected ? AppColors.pink600 : AppColors.pink100,
471
+ paddingHorizontal: 8,
472
+ paddingVertical: 5,
473
+ borderRadius: 7,
474
+ borderWidth: 1,
475
+ borderColor: isSelected ? AppColors.rose700 : AppColors.roseBorder,
476
+ },
477
+ isSelected && {
478
+ shadowColor: AppColors.pink600,
479
+ shadowOffset: { width: 0, height: 1 },
480
+ shadowOpacity: 0.28,
481
+ shadowRadius: 2.5,
482
+ elevation: 2,
483
+ },
484
+ ]}>
485
+ <View style={{
486
+ width: 12,
487
+ height: 12,
488
+ borderRadius: 3,
489
+ borderWidth: 1.2,
490
+ borderColor: isSelected ? AppColors.white : AppColors.pink400,
491
+ backgroundColor: isSelected ? AppColors.white : 'transparent',
492
+ alignItems: 'center',
493
+ justifyContent: 'center',
494
+ }}>
495
+ {isSelected && <CheckIcon color={AppColors.pink600} size={8}/>}
496
+ </View>
497
+ <SparkleIcon color={isSelected ? AppColors.white : AppColors.brandPurple} size={11}/>
498
+ <Text style={{
499
+ fontFamily: AppFonts.interBold,
500
+ fontSize: 10,
501
+ color: isSelected ? AppColors.white : AppColors.pink600,
502
+ }}>
503
+ {t('analytics.customCategory')}: {categoryStats.custom} ({categoryStats.customPct}%)
504
+ </Text>
505
+ </TouchableScale>);
506
+ })()}
507
+ </View>
508
+ </View>
509
+
510
+ {/* ── 3. GA4 Realtime Metric Hero & Activity Chart (UNDER CATEGORIES) ─ */}
221
511
  <View style={{
222
- marginTop: 10,
512
+ marginTop: 2,
223
513
  marginBottom: 8,
224
514
  backgroundColor: AppColors.grayBackground,
225
515
  borderRadius: 10,
@@ -437,199 +727,6 @@ const AnalyticsHeader = React.memo(() => {
437
727
  </View>
438
728
  </View>
439
729
 
440
- {/* ── Category Breakdown Distribution Bar & Interactive Filter Buttons ─ */}
441
- <View style={{ marginTop: 2, marginBottom: 4 }}>
442
- {/* Multi-color Proportional Segmented Bar */}
443
- <View style={{
444
- height: 5,
445
- borderRadius: 2.5,
446
- backgroundColor: `${AppColors.grayBorderSecondary}40`,
447
- flexDirection: 'row',
448
- overflow: 'hidden',
449
- marginBottom: 8,
450
- }}>
451
- {categoryStats.ecommerce > 0 && (<View style={{
452
- flex: categoryStats.ecommerce,
453
- backgroundColor: AppColors.amber500,
454
- }}/>)}
455
- {categoryStats.page_view > 0 && (<View style={{
456
- flex: categoryStats.page_view,
457
- backgroundColor: AppColors.sky500,
458
- }}/>)}
459
- {categoryStats.system > 0 && (<View style={{
460
- flex: categoryStats.system,
461
- backgroundColor: AppColors.purple,
462
- }}/>)}
463
- {categoryStats.custom > 0 && (<View style={{
464
- flex: categoryStats.custom,
465
- backgroundColor: AppColors.brandPurple,
466
- }}/>)}
467
- </View>
468
-
469
- {/* 4 Interactive Category Filter Buttons */}
470
- <View style={{
471
- flexDirection: 'row',
472
- flexWrap: 'wrap',
473
- gap: 6,
474
- }}>
475
- {/* Ecommerce Button */}
476
- {(() => {
477
- const isSelected = !analyticsFilters.categories.has('all') &&
478
- analyticsFilters.categories.has('ecommerce');
479
- return (<TouchableScale onPress={() => toggleCategoryFilter('ecommerce')} style={[
480
- {
481
- flexDirection: 'row',
482
- alignItems: 'center',
483
- gap: 4.5,
484
- backgroundColor: isSelected
485
- ? AppColors.amber500
486
- : `${AppColors.amber500}14`,
487
- paddingHorizontal: 7,
488
- paddingVertical: 4.5,
489
- borderRadius: 6,
490
- borderWidth: 1,
491
- borderColor: isSelected
492
- ? AppColors.amber500
493
- : `${AppColors.amber500}35`,
494
- },
495
- isSelected && {
496
- shadowColor: AppColors.amber500,
497
- shadowOffset: { width: 0, height: 1 },
498
- shadowOpacity: 0.25,
499
- shadowRadius: 2,
500
- elevation: 2,
501
- },
502
- ]}>
503
- <CartIcon color={isSelected ? AppColors.white : AppColors.amber700} size={11}/>
504
- <Text style={{
505
- fontFamily: AppFonts.interBold,
506
- fontSize: 10,
507
- color: isSelected ? AppColors.white : AppColors.amber700,
508
- }}>
509
- Ecommerce: {categoryStats.ecommerce} ({categoryStats.ecommercePct}%)
510
- </Text>
511
- </TouchableScale>);
512
- })()}
513
-
514
- {/* Screens Button */}
515
- {(() => {
516
- const isSelected = !analyticsFilters.categories.has('all') &&
517
- analyticsFilters.categories.has('page_view');
518
- return (<TouchableScale onPress={() => toggleCategoryFilter('page_view')} style={[
519
- {
520
- flexDirection: 'row',
521
- alignItems: 'center',
522
- gap: 4.5,
523
- backgroundColor: isSelected
524
- ? AppColors.sky600
525
- : `${AppColors.sky500}14`,
526
- paddingHorizontal: 7,
527
- paddingVertical: 4.5,
528
- borderRadius: 6,
529
- borderWidth: 1,
530
- borderColor: isSelected
531
- ? AppColors.sky600
532
- : `${AppColors.sky500}35`,
533
- },
534
- isSelected && {
535
- shadowColor: AppColors.sky600,
536
- shadowOffset: { width: 0, height: 1 },
537
- shadowOpacity: 0.25,
538
- shadowRadius: 2,
539
- elevation: 2,
540
- },
541
- ]}>
542
- <GlobeIcon color={isSelected ? AppColors.white : AppColors.sky600} size={11}/>
543
- <Text style={{
544
- fontFamily: AppFonts.interBold,
545
- fontSize: 10,
546
- color: isSelected ? AppColors.white : AppColors.sky600,
547
- }}>
548
- Screens: {categoryStats.page_view} ({categoryStats.page_viewPct}%)
549
- </Text>
550
- </TouchableScale>);
551
- })()}
552
-
553
- {/* System Button */}
554
- {(() => {
555
- const isSelected = !analyticsFilters.categories.has('all') &&
556
- analyticsFilters.categories.has('system');
557
- return (<TouchableScale onPress={() => toggleCategoryFilter('system')} style={[
558
- {
559
- flexDirection: 'row',
560
- alignItems: 'center',
561
- gap: 4.5,
562
- backgroundColor: isSelected
563
- ? AppColors.purple
564
- : `${AppColors.purple}14`,
565
- paddingHorizontal: 7,
566
- paddingVertical: 4.5,
567
- borderRadius: 6,
568
- borderWidth: 1,
569
- borderColor: isSelected
570
- ? AppColors.purple
571
- : `${AppColors.purple}35`,
572
- },
573
- isSelected && {
574
- shadowColor: AppColors.purple,
575
- shadowOffset: { width: 0, height: 1 },
576
- shadowOpacity: 0.25,
577
- shadowRadius: 2,
578
- elevation: 2,
579
- },
580
- ]}>
581
- <BoltIcon color={isSelected ? AppColors.white : AppColors.purple} size={11}/>
582
- <Text style={{
583
- fontFamily: AppFonts.interBold,
584
- fontSize: 10,
585
- color: isSelected ? AppColors.white : AppColors.purple,
586
- }}>
587
- System: {categoryStats.system} ({categoryStats.systemPct}%)
588
- </Text>
589
- </TouchableScale>);
590
- })()}
591
-
592
- {/* Custom Button */}
593
- {(() => {
594
- const isSelected = !analyticsFilters.categories.has('all') &&
595
- analyticsFilters.categories.has('custom');
596
- return (<TouchableScale onPress={() => toggleCategoryFilter('custom')} style={[
597
- {
598
- flexDirection: 'row',
599
- alignItems: 'center',
600
- gap: 4.5,
601
- backgroundColor: isSelected
602
- ? AppColors.brandPurple
603
- : `${AppColors.brandPurple}14`,
604
- paddingHorizontal: 7,
605
- paddingVertical: 4.5,
606
- borderRadius: 6,
607
- borderWidth: 1,
608
- borderColor: isSelected
609
- ? AppColors.brandPurple
610
- : `${AppColors.brandPurple}35`,
611
- },
612
- isSelected && {
613
- shadowColor: AppColors.brandPurple,
614
- shadowOffset: { width: 0, height: 1 },
615
- shadowOpacity: 0.25,
616
- shadowRadius: 2,
617
- elevation: 2,
618
- },
619
- ]}>
620
- <SparkleIcon color={isSelected ? AppColors.white : AppColors.brandPurple} size={11}/>
621
- <Text style={{
622
- fontFamily: AppFonts.interBold,
623
- fontSize: 10,
624
- color: isSelected ? AppColors.white : AppColors.brandPurple,
625
- }}>
626
- Custom: {categoryStats.custom} ({categoryStats.customPct}%)
627
- </Text>
628
- </TouchableScale>);
629
- })()}
630
- </View>
631
- </View>
632
-
633
730
  {/* ── Top Screens / Views Mini Leaderboard (GA4 Realtime Overview) ──── */}
634
731
  {topScreens.length > 0 && (<View style={{
635
732
  marginTop: 8,