react-native-inapp-inspector 1.1.28 → 1.1.31
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/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +29 -80
- package/dist/commonjs/components/AnalyticsDetail.js +668 -108
- package/dist/commonjs/components/AppHeaderLogo.js +15 -15
- package/dist/commonjs/components/ConsoleLogCard.js +112 -66
- package/dist/commonjs/components/CopyButton.js +15 -1
- package/dist/commonjs/components/DomainHeader.js +64 -32
- package/dist/commonjs/components/HighlightText.js +7 -1
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +332 -221
- package/dist/commonjs/components/Inspector/ConsoleTab.js +162 -56
- package/dist/commonjs/components/Inspector/InspectorHeader.js +70 -119
- package/dist/commonjs/components/Inspector/LogDetail.js +53 -17
- package/dist/commonjs/components/Inspector/MainScreen.js +4 -0
- package/dist/commonjs/components/Inspector/NetworkDetail.js +24 -24
- package/dist/commonjs/components/Inspector/NetworkTab.js +4 -1
- package/dist/commonjs/components/Inspector/ReduxDetail.js +237 -42
- package/dist/commonjs/components/Inspector/ReduxTab.js +456 -35
- package/dist/commonjs/components/JsonViewer.js +26 -4
- package/dist/commonjs/components/LogCard.js +103 -11
- package/dist/commonjs/components/NetworkIcons.js +19 -10
- package/dist/commonjs/components/Toast.d.ts +3 -0
- package/dist/commonjs/components/Toast.js +152 -0
- package/dist/commonjs/components/TouchableScale.d.ts +1 -0
- package/dist/commonjs/components/TouchableScale.js +3 -3
- package/dist/commonjs/components/TreeNode.js +12 -2
- package/dist/commonjs/constants/index.js +6 -6
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/reduxLogger.js +128 -5
- package/dist/commonjs/helpers/index.d.ts +4 -0
- package/dist/commonjs/helpers/index.js +107 -92
- package/dist/commonjs/helpers/toast.d.ts +4 -0
- package/dist/commonjs/helpers/toast.js +20 -0
- package/dist/commonjs/i18n/locales/en.json +49 -3
- package/dist/commonjs/styles/AppColors.d.ts +6 -0
- package/dist/commonjs/styles/AppColors.js +6 -0
- package/dist/commonjs/styles/index.d.ts +82 -15
- package/dist/commonjs/styles/index.js +92 -26
- package/dist/commonjs/types/interfaces.d.ts +7 -0
- package/dist/esm/components/AnalyticsDetail.js +670 -110
- package/dist/esm/components/AppHeaderLogo.js +15 -15
- package/dist/esm/components/ConsoleLogCard.js +114 -68
- package/dist/esm/components/CopyButton.js +15 -1
- package/dist/esm/components/DomainHeader.js +64 -32
- package/dist/esm/components/HighlightText.js +7 -1
- package/dist/esm/components/Inspector/AnalyticsTab.js +334 -223
- package/dist/esm/components/Inspector/ConsoleTab.js +163 -57
- package/dist/esm/components/Inspector/InspectorHeader.js +71 -120
- package/dist/esm/components/Inspector/LogDetail.js +55 -19
- package/dist/esm/components/Inspector/MainScreen.js +4 -0
- package/dist/esm/components/Inspector/NetworkDetail.js +25 -25
- package/dist/esm/components/Inspector/NetworkTab.js +4 -1
- package/dist/esm/components/Inspector/ReduxDetail.js +239 -44
- package/dist/esm/components/Inspector/ReduxTab.js +458 -37
- package/dist/esm/components/JsonViewer.js +26 -4
- package/dist/esm/components/LogCard.js +105 -13
- package/dist/esm/components/NetworkIcons.js +19 -10
- package/dist/esm/components/Toast.d.ts +3 -0
- package/dist/esm/components/Toast.js +117 -0
- package/dist/esm/components/TouchableScale.d.ts +1 -0
- package/dist/esm/components/TouchableScale.js +3 -3
- package/dist/esm/components/TreeNode.js +12 -2
- package/dist/esm/constants/index.js +6 -6
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/reduxLogger.js +128 -5
- package/dist/esm/helpers/index.d.ts +4 -0
- package/dist/esm/helpers/index.js +102 -92
- package/dist/esm/helpers/toast.d.ts +4 -0
- package/dist/esm/helpers/toast.js +15 -0
- package/dist/esm/i18n/locales/en.json +49 -3
- package/dist/esm/styles/AppColors.d.ts +6 -0
- package/dist/esm/styles/AppColors.js +6 -0
- package/dist/esm/styles/index.d.ts +82 -15
- package/dist/esm/styles/index.js +92 -26
- package/dist/esm/types/interfaces.d.ts +7 -0
- package/ios/NetworkInspectorModule.m +0 -10
- package/package.json +13 -10
|
@@ -9,11 +9,11 @@ import styles from '../../styles';
|
|
|
9
9
|
import { AppColors } from '../../styles/AppColors';
|
|
10
10
|
import { AppFonts } from '../../styles/AppFonts';
|
|
11
11
|
import { useTranslation } from '../../i18n';
|
|
12
|
-
import Svg, { Path, Line, Circle, G, Defs, LinearGradient as SvgLinearGradient, Stop, } from 'react-native-svg';
|
|
12
|
+
import Svg, { Path, Rect, Line, Circle, G, Defs, LinearGradient as SvgLinearGradient, Stop, } from 'react-native-svg';
|
|
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
|
-
//
|
|
90
|
+
// Multi-select category toggle from top button group
|
|
91
91
|
const toggleCategoryFilter = (cat) => {
|
|
92
92
|
animateNextLayout();
|
|
93
93
|
setAnalyticsFilters(prev => {
|
|
94
|
-
|
|
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
|
-
|
|
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
|
-
{/* ──
|
|
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:
|
|
512
|
+
marginTop: 2,
|
|
223
513
|
marginBottom: 8,
|
|
224
514
|
backgroundColor: AppColors.grayBackground,
|
|
225
515
|
borderRadius: 10,
|
|
@@ -299,41 +589,55 @@ const AnalyticsHeader = React.memo(() => {
|
|
|
299
589
|
</View>
|
|
300
590
|
</View>
|
|
301
591
|
|
|
302
|
-
{/* ──
|
|
592
|
+
{/* ── Crashlytics-Style Multi-Bar Histogram with Trendline Overlay ── */}
|
|
303
593
|
<View style={{ height: chartHeight, position: 'relative' }}>
|
|
304
594
|
<Svg width="100%" height={chartHeight} viewBox={`0 0 ${svgWidth} ${chartHeight}`}>
|
|
305
595
|
<Defs>
|
|
306
|
-
<SvgLinearGradient id="
|
|
307
|
-
<Stop offset="0%" stopColor=
|
|
308
|
-
<Stop offset="
|
|
309
|
-
|
|
596
|
+
<SvgLinearGradient id="crashlyticsBarGrad" x1="0" y1="0" x2="0" y2="1">
|
|
597
|
+
<Stop offset="0%" stopColor="#8B5CF6" stopOpacity="0.9"/>
|
|
598
|
+
<Stop offset="100%" stopColor="#6366F1" stopOpacity="0.6"/>
|
|
599
|
+
</SvgLinearGradient>
|
|
600
|
+
<SvgLinearGradient id="crashlyticsAreaGrad" x1="0" y1="0" x2="0" y2="1">
|
|
601
|
+
<Stop offset="0%" stopColor="#8B5CF6" stopOpacity="0.25"/>
|
|
602
|
+
<Stop offset="80%" stopColor="#8B5CF6" stopOpacity="0.04"/>
|
|
603
|
+
<Stop offset="100%" stopColor="#8B5CF6" stopOpacity="0.0"/>
|
|
310
604
|
</SvgLinearGradient>
|
|
311
605
|
</Defs>
|
|
312
606
|
|
|
313
|
-
{/*
|
|
314
|
-
<Line x1="
|
|
315
|
-
<Line x1="
|
|
607
|
+
{/* Subtle Grid Guidelines */}
|
|
608
|
+
<Line x1="6" y1={chartHeight - 6} x2={svgWidth - 6} y2={chartHeight - 6} stroke="#E2E8F0" strokeWidth="1"/>
|
|
609
|
+
<Line x1="6" y1={chartHeight / 2} x2={svgWidth - 6} y2={chartHeight / 2} stroke="#F1F5F9" strokeWidth="1" strokeDasharray="4,4"/>
|
|
316
610
|
|
|
317
|
-
{/* Glowing Area
|
|
318
|
-
<Path d={histogramData.areaPath} fill="url(#
|
|
611
|
+
{/* Glowing Area Underlay */}
|
|
612
|
+
<Path d={histogramData.areaPath} fill="url(#crashlyticsAreaGrad)"/>
|
|
319
613
|
|
|
320
|
-
{/*
|
|
321
|
-
<Path d={histogramData.linePath} fill="none" stroke={AppColors.brandPurple} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
322
|
-
|
|
323
|
-
{/* Data Points & Active Bucket Indicators */}
|
|
614
|
+
{/* Vertical Crashlytics Histogram Bars */}
|
|
324
615
|
{histogramData.buckets.map((b, idx) => {
|
|
325
616
|
const isSelected = selectedBucketIdx === idx;
|
|
326
|
-
|
|
327
|
-
|
|
617
|
+
const barWidth = 10;
|
|
618
|
+
const barX = b.x - barWidth / 2;
|
|
619
|
+
const barHeight = Math.max(3, chartHeight - 8 - b.y);
|
|
620
|
+
const barY = chartHeight - 8 - barHeight;
|
|
621
|
+
return (<G key={`bar-${b.index}`}>
|
|
622
|
+
{/* Background slot */}
|
|
623
|
+
<Rect x={barX} y={6} width={barWidth} height={chartHeight - 14} rx={3} fill={isSelected ? '#EDE9FE' : '#F8FAFC'}/>
|
|
328
624
|
|
|
329
|
-
{
|
|
625
|
+
{/* Active Event Bar */}
|
|
626
|
+
{b.total > 0 && (<Rect x={barX} y={barY} width={barWidth} height={barHeight} rx={3} fill="url(#crashlyticsBarGrad)"/>)}
|
|
330
627
|
|
|
331
|
-
{
|
|
332
|
-
|
|
333
|
-
<Circle cx={b.x} cy={b.y} r="3" fill={AppColors.brandPurple}/>
|
|
334
|
-
</G>)}
|
|
628
|
+
{/* Selected Cursor Line */}
|
|
629
|
+
{isSelected && (<Line x1={b.x} y1={4} x2={b.x} y2={chartHeight - 6} stroke="#8B5CF6" strokeWidth="1.5" strokeDasharray="2,2"/>)}
|
|
335
630
|
</G>);
|
|
336
631
|
})}
|
|
632
|
+
|
|
633
|
+
{/* Crashlytics Apex Trendline */}
|
|
634
|
+
<Path d={histogramData.linePath} fill="none" stroke="#8B5CF6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
635
|
+
|
|
636
|
+
{/* Pulsing Dot on Current Active Time Bucket */}
|
|
637
|
+
{histogramData.buckets.length > 0 && (<G>
|
|
638
|
+
<Circle cx={histogramData.buckets[histogramData.buckets.length - 1].x} cy={histogramData.buckets[histogramData.buckets.length - 1].y} r="5.5" fill="#8B5CF6" opacity="0.25"/>
|
|
639
|
+
<Circle cx={histogramData.buckets[histogramData.buckets.length - 1].x} cy={histogramData.buckets[histogramData.buckets.length - 1].y} r="3" fill="#7C3AED"/>
|
|
640
|
+
</G>)}
|
|
337
641
|
</Svg>
|
|
338
642
|
|
|
339
643
|
{/* Invisible Touch Columns for Interactive Bucket Selection */}
|
|
@@ -423,199 +727,6 @@ const AnalyticsHeader = React.memo(() => {
|
|
|
423
727
|
</View>
|
|
424
728
|
</View>
|
|
425
729
|
|
|
426
|
-
{/* ── Category Breakdown Distribution Bar & Interactive Filter Buttons ─ */}
|
|
427
|
-
<View style={{ marginTop: 2, marginBottom: 4 }}>
|
|
428
|
-
{/* Multi-color Proportional Segmented Bar */}
|
|
429
|
-
<View style={{
|
|
430
|
-
height: 5,
|
|
431
|
-
borderRadius: 2.5,
|
|
432
|
-
backgroundColor: `${AppColors.grayBorderSecondary}40`,
|
|
433
|
-
flexDirection: 'row',
|
|
434
|
-
overflow: 'hidden',
|
|
435
|
-
marginBottom: 8,
|
|
436
|
-
}}>
|
|
437
|
-
{categoryStats.ecommerce > 0 && (<View style={{
|
|
438
|
-
flex: categoryStats.ecommerce,
|
|
439
|
-
backgroundColor: AppColors.amber500,
|
|
440
|
-
}}/>)}
|
|
441
|
-
{categoryStats.page_view > 0 && (<View style={{
|
|
442
|
-
flex: categoryStats.page_view,
|
|
443
|
-
backgroundColor: AppColors.sky500,
|
|
444
|
-
}}/>)}
|
|
445
|
-
{categoryStats.system > 0 && (<View style={{
|
|
446
|
-
flex: categoryStats.system,
|
|
447
|
-
backgroundColor: AppColors.purple,
|
|
448
|
-
}}/>)}
|
|
449
|
-
{categoryStats.custom > 0 && (<View style={{
|
|
450
|
-
flex: categoryStats.custom,
|
|
451
|
-
backgroundColor: AppColors.brandPurple,
|
|
452
|
-
}}/>)}
|
|
453
|
-
</View>
|
|
454
|
-
|
|
455
|
-
{/* 4 Interactive Category Filter Buttons */}
|
|
456
|
-
<View style={{
|
|
457
|
-
flexDirection: 'row',
|
|
458
|
-
flexWrap: 'wrap',
|
|
459
|
-
gap: 6,
|
|
460
|
-
}}>
|
|
461
|
-
{/* Ecommerce Button */}
|
|
462
|
-
{(() => {
|
|
463
|
-
const isSelected = !analyticsFilters.categories.has('all') &&
|
|
464
|
-
analyticsFilters.categories.has('ecommerce');
|
|
465
|
-
return (<TouchableScale onPress={() => toggleCategoryFilter('ecommerce')} style={[
|
|
466
|
-
{
|
|
467
|
-
flexDirection: 'row',
|
|
468
|
-
alignItems: 'center',
|
|
469
|
-
gap: 4.5,
|
|
470
|
-
backgroundColor: isSelected
|
|
471
|
-
? AppColors.amber500
|
|
472
|
-
: `${AppColors.amber500}14`,
|
|
473
|
-
paddingHorizontal: 7,
|
|
474
|
-
paddingVertical: 4.5,
|
|
475
|
-
borderRadius: 6,
|
|
476
|
-
borderWidth: 1,
|
|
477
|
-
borderColor: isSelected
|
|
478
|
-
? AppColors.amber500
|
|
479
|
-
: `${AppColors.amber500}35`,
|
|
480
|
-
},
|
|
481
|
-
isSelected && {
|
|
482
|
-
shadowColor: AppColors.amber500,
|
|
483
|
-
shadowOffset: { width: 0, height: 1 },
|
|
484
|
-
shadowOpacity: 0.25,
|
|
485
|
-
shadowRadius: 2,
|
|
486
|
-
elevation: 2,
|
|
487
|
-
},
|
|
488
|
-
]}>
|
|
489
|
-
<CartIcon color={isSelected ? AppColors.white : AppColors.amber700} size={11}/>
|
|
490
|
-
<Text style={{
|
|
491
|
-
fontFamily: AppFonts.interBold,
|
|
492
|
-
fontSize: 10,
|
|
493
|
-
color: isSelected ? AppColors.white : AppColors.amber700,
|
|
494
|
-
}}>
|
|
495
|
-
Ecommerce: {categoryStats.ecommerce} ({categoryStats.ecommercePct}%)
|
|
496
|
-
</Text>
|
|
497
|
-
</TouchableScale>);
|
|
498
|
-
})()}
|
|
499
|
-
|
|
500
|
-
{/* Screens Button */}
|
|
501
|
-
{(() => {
|
|
502
|
-
const isSelected = !analyticsFilters.categories.has('all') &&
|
|
503
|
-
analyticsFilters.categories.has('page_view');
|
|
504
|
-
return (<TouchableScale onPress={() => toggleCategoryFilter('page_view')} style={[
|
|
505
|
-
{
|
|
506
|
-
flexDirection: 'row',
|
|
507
|
-
alignItems: 'center',
|
|
508
|
-
gap: 4.5,
|
|
509
|
-
backgroundColor: isSelected
|
|
510
|
-
? AppColors.sky600
|
|
511
|
-
: `${AppColors.sky500}14`,
|
|
512
|
-
paddingHorizontal: 7,
|
|
513
|
-
paddingVertical: 4.5,
|
|
514
|
-
borderRadius: 6,
|
|
515
|
-
borderWidth: 1,
|
|
516
|
-
borderColor: isSelected
|
|
517
|
-
? AppColors.sky600
|
|
518
|
-
: `${AppColors.sky500}35`,
|
|
519
|
-
},
|
|
520
|
-
isSelected && {
|
|
521
|
-
shadowColor: AppColors.sky600,
|
|
522
|
-
shadowOffset: { width: 0, height: 1 },
|
|
523
|
-
shadowOpacity: 0.25,
|
|
524
|
-
shadowRadius: 2,
|
|
525
|
-
elevation: 2,
|
|
526
|
-
},
|
|
527
|
-
]}>
|
|
528
|
-
<GlobeIcon color={isSelected ? AppColors.white : AppColors.sky600} size={11}/>
|
|
529
|
-
<Text style={{
|
|
530
|
-
fontFamily: AppFonts.interBold,
|
|
531
|
-
fontSize: 10,
|
|
532
|
-
color: isSelected ? AppColors.white : AppColors.sky600,
|
|
533
|
-
}}>
|
|
534
|
-
Screens: {categoryStats.page_view} ({categoryStats.page_viewPct}%)
|
|
535
|
-
</Text>
|
|
536
|
-
</TouchableScale>);
|
|
537
|
-
})()}
|
|
538
|
-
|
|
539
|
-
{/* System Button */}
|
|
540
|
-
{(() => {
|
|
541
|
-
const isSelected = !analyticsFilters.categories.has('all') &&
|
|
542
|
-
analyticsFilters.categories.has('system');
|
|
543
|
-
return (<TouchableScale onPress={() => toggleCategoryFilter('system')} style={[
|
|
544
|
-
{
|
|
545
|
-
flexDirection: 'row',
|
|
546
|
-
alignItems: 'center',
|
|
547
|
-
gap: 4.5,
|
|
548
|
-
backgroundColor: isSelected
|
|
549
|
-
? AppColors.purple
|
|
550
|
-
: `${AppColors.purple}14`,
|
|
551
|
-
paddingHorizontal: 7,
|
|
552
|
-
paddingVertical: 4.5,
|
|
553
|
-
borderRadius: 6,
|
|
554
|
-
borderWidth: 1,
|
|
555
|
-
borderColor: isSelected
|
|
556
|
-
? AppColors.purple
|
|
557
|
-
: `${AppColors.purple}35`,
|
|
558
|
-
},
|
|
559
|
-
isSelected && {
|
|
560
|
-
shadowColor: AppColors.purple,
|
|
561
|
-
shadowOffset: { width: 0, height: 1 },
|
|
562
|
-
shadowOpacity: 0.25,
|
|
563
|
-
shadowRadius: 2,
|
|
564
|
-
elevation: 2,
|
|
565
|
-
},
|
|
566
|
-
]}>
|
|
567
|
-
<BoltIcon color={isSelected ? AppColors.white : AppColors.purple} size={11}/>
|
|
568
|
-
<Text style={{
|
|
569
|
-
fontFamily: AppFonts.interBold,
|
|
570
|
-
fontSize: 10,
|
|
571
|
-
color: isSelected ? AppColors.white : AppColors.purple,
|
|
572
|
-
}}>
|
|
573
|
-
System: {categoryStats.system} ({categoryStats.systemPct}%)
|
|
574
|
-
</Text>
|
|
575
|
-
</TouchableScale>);
|
|
576
|
-
})()}
|
|
577
|
-
|
|
578
|
-
{/* Custom Button */}
|
|
579
|
-
{(() => {
|
|
580
|
-
const isSelected = !analyticsFilters.categories.has('all') &&
|
|
581
|
-
analyticsFilters.categories.has('custom');
|
|
582
|
-
return (<TouchableScale onPress={() => toggleCategoryFilter('custom')} style={[
|
|
583
|
-
{
|
|
584
|
-
flexDirection: 'row',
|
|
585
|
-
alignItems: 'center',
|
|
586
|
-
gap: 4.5,
|
|
587
|
-
backgroundColor: isSelected
|
|
588
|
-
? AppColors.brandPurple
|
|
589
|
-
: `${AppColors.brandPurple}14`,
|
|
590
|
-
paddingHorizontal: 7,
|
|
591
|
-
paddingVertical: 4.5,
|
|
592
|
-
borderRadius: 6,
|
|
593
|
-
borderWidth: 1,
|
|
594
|
-
borderColor: isSelected
|
|
595
|
-
? AppColors.brandPurple
|
|
596
|
-
: `${AppColors.brandPurple}35`,
|
|
597
|
-
},
|
|
598
|
-
isSelected && {
|
|
599
|
-
shadowColor: AppColors.brandPurple,
|
|
600
|
-
shadowOffset: { width: 0, height: 1 },
|
|
601
|
-
shadowOpacity: 0.25,
|
|
602
|
-
shadowRadius: 2,
|
|
603
|
-
elevation: 2,
|
|
604
|
-
},
|
|
605
|
-
]}>
|
|
606
|
-
<SparkleIcon color={isSelected ? AppColors.white : AppColors.brandPurple} size={11}/>
|
|
607
|
-
<Text style={{
|
|
608
|
-
fontFamily: AppFonts.interBold,
|
|
609
|
-
fontSize: 10,
|
|
610
|
-
color: isSelected ? AppColors.white : AppColors.brandPurple,
|
|
611
|
-
}}>
|
|
612
|
-
Custom: {categoryStats.custom} ({categoryStats.customPct}%)
|
|
613
|
-
</Text>
|
|
614
|
-
</TouchableScale>);
|
|
615
|
-
})()}
|
|
616
|
-
</View>
|
|
617
|
-
</View>
|
|
618
|
-
|
|
619
730
|
{/* ── Top Screens / Views Mini Leaderboard (GA4 Realtime Overview) ──── */}
|
|
620
731
|
{topScreens.length > 0 && (<View style={{
|
|
621
732
|
marginTop: 8,
|