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
@@ -1,141 +1,701 @@
1
- import React, { useState } from 'react';
2
- import { ScrollView, StyleSheet, Text, View, } from 'react-native';
1
+ import React, { useState, useMemo } from 'react';
2
+ import { Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native';
3
3
  // Components
4
4
  import CopyButton from './CopyButton';
5
5
  import JsonViewer from './JsonViewer';
6
- import TouchableScale from './TouchableScale';
7
- // Utils
6
+ import SegmentedTabs from './SegmentedTabs';
7
+ import HighlightText from './HighlightText';
8
+ import AnimatedEntrance from './AnimatedEntrance';
9
+ // Helpers & Icons
8
10
  import { AppFonts } from '../styles/AppFonts';
9
- import { getSize } from '../helpers';
10
- // Stylesheet
11
11
  import { AppColors } from '../styles/AppColors';
12
+ import { getSize, formatDateTime } from '../helpers';
13
+ import { getEventCategory } from '../helpers/gaAnalyticsRegistry';
12
14
  import { useTranslation } from '../i18n';
15
+ import { SparkleIcon, ClockIcon, SearchIcon, ClearIcon, LayersIcon, CartIcon, GlobeIcon, BoltIcon, PinIcon, MoneyIcon, PrettyIcon, RawIcon, TableIcon, DocIcon, } from './NetworkIcons';
13
16
  const AnalyticsDetail = ({ event, }) => {
14
17
  const { t } = useTranslation();
15
- const [expandDepth, setExpandDepth] = useState(1);
16
- const [treeKey, setTreeKey] = useState(0);
17
- const params = event.params ?? {};
18
- const userProperties = event.userProperties ?? {};
19
- const upCount = Object.keys(userProperties).length;
20
- const jsonData = {
18
+ const [activeViewTab, setActiveViewTab] = useState('overview');
19
+ const [paramSearch, setParamSearch] = useState('');
20
+ const params = useMemo(() => event.params ?? {}, [event.params]);
21
+ const userProperties = useMemo(() => event.userProperties ?? {}, [event.userProperties]);
22
+ const paramKeys = useMemo(() => Object.keys(params), [params]);
23
+ const userPropKeys = useMemo(() => Object.keys(userProperties), [userProperties]);
24
+ const category = useMemo(() => getEventCategory(event.name, params), [event.name, params]);
25
+ // Combined full JSON payload
26
+ const fullJsonData = useMemo(() => ({
21
27
  name: event.name,
22
- params: params,
23
- ...(upCount > 0 ? { userProperties } : {}),
24
- };
25
- const isObject = typeof jsonData === 'object' && jsonData !== null;
26
- const isArray = Array.isArray(jsonData);
27
- let typeLabel = typeof jsonData;
28
- let countLabel = '';
29
- if (jsonData === null) {
30
- typeLabel = 'null';
31
- }
32
- else if (isArray) {
33
- typeLabel = 'array';
34
- countLabel = `${jsonData.length} ${t('analytics.items')}`;
35
- }
36
- else if (isObject) {
37
- typeLabel = 'object';
38
- countLabel = `${Object.keys(jsonData).length} keys`;
39
- }
40
- return (<ScrollView style={[detailStyles.scroll, { backgroundColor: AppColors.grayBackground }]} contentContainerStyle={detailStyles.content} showsVerticalScrollIndicator contentInsetAdjustmentBehavior="never" automaticallyAdjustContentInsets={false}>
28
+ timestamp: event.timestamp,
29
+ source: event.source,
30
+ ...(event.screenName ? { screenName: event.screenName } : {}),
31
+ ...(event.screenClass ? { screenClass: event.screenClass } : {}),
32
+ ...(event.userId ? { userId: event.userId } : {}),
33
+ ...(event.sessionId ? { sessionId: event.sessionId } : {}),
34
+ ...(event.trackingId ? { trackingId: event.trackingId } : {}),
35
+ params,
36
+ ...(userPropKeys.length > 0 ? { userProperties } : {}),
37
+ }), [event, params, userPropKeys.length, userProperties]);
38
+ // E-commerce revenue calculation
39
+ const revenueInfo = useMemo(() => {
40
+ const val = params.value ?? params.price;
41
+ const currency = params.currency ?? 'USD';
42
+ if (val !== undefined && val !== null && !isNaN(Number(val))) {
43
+ return {
44
+ amount: Number(val).toFixed(2),
45
+ currency: String(currency).toUpperCase(),
46
+ };
47
+ }
48
+ return null;
49
+ }, [params]);
50
+ // Items count if an items array exists
51
+ const itemsCount = useMemo(() => {
52
+ if (Array.isArray(params.items)) {
53
+ return params.items.length;
54
+ }
55
+ return null;
56
+ }, [params.items]);
57
+ // Screen Context
58
+ const screenContext = useMemo(() => {
59
+ return (event.screenName ||
60
+ params.firebase_screen ||
61
+ params.screen_name ||
62
+ params.page_title ||
63
+ event.screenClass ||
64
+ params.firebase_screen_class);
65
+ }, [event.screenName, event.screenClass, params]);
66
+ // Filtered parameter entries
67
+ const filteredParamEntries = useMemo(() => {
68
+ const entries = Object.entries(params);
69
+ if (!paramSearch.trim())
70
+ return entries;
71
+ const q = paramSearch.toLowerCase();
72
+ return entries.filter(([k, v]) => {
73
+ const valStr = typeof v === 'object' ? JSON.stringify(v) : String(v);
74
+ return k.toLowerCase().includes(q) || valStr.toLowerCase().includes(q);
75
+ });
76
+ }, [params, paramSearch]);
77
+ // Category Theme Meta
78
+ const catMeta = useMemo(() => {
79
+ switch (category) {
80
+ case 'ecommerce':
81
+ return {
82
+ label: t('analytics.ecommerceCategory'),
83
+ color: AppColors.amber700,
84
+ bg: AppColors.amber100,
85
+ border: AppColors.amber200,
86
+ icon: (color) => <CartIcon color={color} size={11}/>,
87
+ };
88
+ case 'page_view':
89
+ return {
90
+ label: t('analytics.screensCategory'),
91
+ color: AppColors.sky600,
92
+ bg: AppColors.sky100,
93
+ border: AppColors.sky400,
94
+ icon: (color) => <GlobeIcon color={color} size={11}/>,
95
+ };
96
+ case 'system':
97
+ return {
98
+ label: t('analytics.systemCategory'),
99
+ color: AppColors.purple,
100
+ bg: AppColors.purple100,
101
+ border: AppColors.purple200,
102
+ icon: (color) => <BoltIcon color={color} size={11}/>,
103
+ };
104
+ default:
105
+ return {
106
+ label: t('analytics.customCategory'),
107
+ color: AppColors.brandPurple,
108
+ bg: AppColors.purple100,
109
+ border: AppColors.purple200,
110
+ icon: (color) => <SparkleIcon color={color} size={11}/>,
111
+ };
112
+ }
113
+ }, [category, t]);
114
+ const viewTabs = [
115
+ {
116
+ key: 'overview',
117
+ label: `${t('analytics.overviewTab')} (${paramKeys.length})`,
118
+ icon: (isActive) => (<TableIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
119
+ },
120
+ {
121
+ key: 'tree',
122
+ label: t('analytics.jsonTreeTab'),
123
+ icon: (isActive) => (<PrettyIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
124
+ },
125
+ {
126
+ key: 'raw',
127
+ label: t('analytics.rawPayloadTab'),
128
+ icon: (isActive) => (<RawIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
129
+ },
130
+ ];
131
+ return (<ScrollView style={detailStyles.scroll} contentContainerStyle={detailStyles.content} showsVerticalScrollIndicator contentInsetAdjustmentBehavior="never" automaticallyAdjustContentInsets={false}>
41
132
 
42
- {/* Clean Toolbar */}
43
- <View style={{
44
- flexDirection: 'row',
45
- justifyContent: 'space-between',
46
- alignItems: 'center',
47
- paddingHorizontal: 16,
48
- paddingVertical: 12,
49
- borderBottomWidth: 1,
50
- borderBottomColor: AppColors.grayBorderSecondary,
51
- backgroundColor: AppColors.primaryLight,
52
- }}>
53
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
54
- <TouchableScale style={{
55
- flexDirection: 'row',
56
- alignItems: 'center',
57
- gap: 6,
58
- backgroundColor: AppColors.grayBackground,
59
- paddingHorizontal: 12,
60
- paddingVertical: 6,
61
- borderRadius: 8,
62
- borderWidth: 1,
63
- borderColor: AppColors.grayBorderSecondary,
64
- }} onPress={() => {
65
- const nextDepth = expandDepth === 99 ? 1 : 99;
66
- setExpandDepth(nextDepth);
67
- setTreeKey(prev => prev + 1);
68
- }}>
69
- <Text style={{
70
- fontFamily: AppFonts.interBold,
71
- fontSize: 12,
72
- color: AppColors.purple,
73
- }}>
74
- {expandDepth === 99 ? t('common.collapseAll') : t('common.expandAll')}
75
- </Text>
76
- </TouchableScale>
77
- <View style={{
78
- flexDirection: 'row',
79
- alignItems: 'center',
80
- backgroundColor: AppColors.grayBackground,
81
- borderRadius: 6,
82
- overflow: 'hidden',
83
- borderWidth: 1,
84
- borderColor: AppColors.grayBorderSecondary,
85
- }}>
86
- <View style={{ paddingHorizontal: 6, paddingVertical: 4, backgroundColor: AppColors.purple + '18' }}>
87
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 9, color: AppColors.purple, letterSpacing: 0.3 }}>
88
- {typeLabel.toUpperCase()}
133
+ {/* ── 1. Hero Event Header Card ────────────────────────────────────── */}
134
+ <View style={detailStyles.heroCard}>
135
+ {/* Top Badges Row */}
136
+ <View style={detailStyles.heroTopRow}>
137
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap', flex: 1 }}>
138
+ {/* Source Badge */}
139
+ <View style={[
140
+ detailStyles.sourceBadge,
141
+ event.source === 'firebase'
142
+ ? { backgroundColor: AppColors.amberBg, borderColor: AppColors.amberBorder }
143
+ : { backgroundColor: AppColors.purpleBg, borderColor: AppColors.purpleBorder },
144
+ ]}>
145
+ <View style={[
146
+ detailStyles.sourceDot,
147
+ {
148
+ backgroundColor: event.source === 'firebase'
149
+ ? AppColors.firebaseOrange
150
+ : AppColors.purple,
151
+ },
152
+ ]}/>
153
+ <Text style={[
154
+ detailStyles.sourceBadgeText,
155
+ {
156
+ color: event.source === 'firebase'
157
+ ? AppColors.amber800Warm
158
+ : AppColors.purple,
159
+ },
160
+ ]}>
161
+ {event.source === 'firebase' ? t('analytics.sourceFirebase') : t('analytics.sourceCustom')}
162
+ </Text>
163
+ </View>
164
+
165
+ {/* Category Badge */}
166
+ <View style={[
167
+ detailStyles.catBadge,
168
+ { backgroundColor: catMeta.bg, borderColor: catMeta.border },
169
+ ]}>
170
+ {catMeta.icon(catMeta.color)}
171
+ <Text style={[detailStyles.catBadgeText, { color: catMeta.color }]}>
172
+ {catMeta.label}
89
173
  </Text>
90
174
  </View>
91
- {countLabel ? (<View style={{ paddingHorizontal: 6, paddingVertical: 4 }}>
92
- <Text style={{ fontFamily: AppFonts.interMedium, fontSize: 9, color: AppColors.grayText }}>
93
- {countLabel}
94
- </Text>
95
- </View>) : null}
96
175
  </View>
97
- <View style={{
98
- backgroundColor: AppColors.grayBackground,
99
- borderRadius: 6,
100
- paddingHorizontal: 6,
101
- paddingVertical: 4,
102
- borderWidth: 1,
103
- borderColor: AppColors.grayBorderSecondary,
104
- }}>
105
- <Text style={{ fontFamily: AppFonts.interMedium, fontSize: 9, color: AppColors.grayText }}>
106
- {getSize(jsonData)}
176
+
177
+ {/* Copy Full Event JSON Button */}
178
+ <CopyButton value={JSON.stringify(fullJsonData, null, 2)} label={t('analytics.eventDetails')}/>
179
+ </View>
180
+
181
+ {/* Event Name Heading */}
182
+ <View style={detailStyles.titleRow}>
183
+ <Text style={detailStyles.eventTitle} selectable numberOfLines={2}>
184
+ {event.name}
185
+ </Text>
186
+ </View>
187
+
188
+ {/* Sub Details Row: Timestamp & Screen Location */}
189
+ <View style={detailStyles.metaRow}>
190
+ <View style={detailStyles.timePill}>
191
+ <ClockIcon color={AppColors.purple} size={11}/>
192
+ <Text style={detailStyles.timeText}>
193
+ {formatDateTime(event.timestamp)}
107
194
  </Text>
108
195
  </View>
196
+
197
+ {screenContext && (<View style={detailStyles.screenPill}>
198
+ <PinIcon color={AppColors.sky600} size={11}/>
199
+ <Text style={detailStyles.screenText} numberOfLines={1}>
200
+ {String(screenContext)}
201
+ </Text>
202
+ </View>)}
109
203
  </View>
110
204
 
111
- <CopyButton value={JSON.stringify(jsonData, null, 2)} label={t('common.copyJson')}/>
205
+ {/* ── 2. Metric Pills Summary ──────────────────────────────────────── */}
206
+ <View style={detailStyles.metricsBar}>
207
+ <View style={detailStyles.metricPill}>
208
+ <Text style={detailStyles.metricLabel}>Params:</Text>
209
+ <Text style={detailStyles.metricValue}>{paramKeys.length}</Text>
210
+ </View>
211
+
212
+ {userPropKeys.length > 0 && (<View style={detailStyles.metricPill}>
213
+ <Text style={detailStyles.metricLabel}>User Props:</Text>
214
+ <Text style={detailStyles.metricValue}>{userPropKeys.length}</Text>
215
+ </View>)}
216
+
217
+ <View style={detailStyles.metricPill}>
218
+ <Text style={detailStyles.metricLabel}>Size:</Text>
219
+ <Text style={detailStyles.metricValue}>{getSize(fullJsonData)}</Text>
220
+ </View>
221
+
222
+ {revenueInfo && (<View style={detailStyles.revenuePill}>
223
+ <MoneyIcon color={AppColors.emerald600} size={11}/>
224
+ <Text style={detailStyles.revenueValue}>
225
+ ${revenueInfo.amount} {revenueInfo.currency}
226
+ </Text>
227
+ </View>)}
228
+
229
+ {itemsCount !== null && (<View style={detailStyles.itemsPill}>
230
+ <Text style={detailStyles.itemsValue}>
231
+ 📦 {itemsCount} items
232
+ </Text>
233
+ </View>)}
234
+ </View>
112
235
  </View>
113
236
 
114
- {/* Direct JSON Viewer View */}
115
- <View style={[
116
- detailStyles.jsonBlockContainer,
117
- {
118
- backgroundColor: AppColors.primaryLight,
119
- borderColor: AppColors.grayBorderSecondary,
120
- marginTop: 16,
121
- },
122
- ]}>
123
- <JsonViewer key={treeKey} data={jsonData} defaultExpandDepth={expandDepth} fullHeight/>
237
+ {/* ── 3. View Switcher Tabs (Overview, Tree, Raw) ───────────────────── */}
238
+ <View style={detailStyles.tabsWrap}>
239
+ <SegmentedTabs tabs={viewTabs} activeKey={activeViewTab} onChange={key => setActiveViewTab(key)}/>
124
240
  </View>
241
+
242
+ {/* ── 4. Main Tab Body Content ───────────────────────────────────────── */}
243
+ {activeViewTab === 'overview' && (<View style={detailStyles.tabContent}>
244
+ {/* Metadata Card (Session, User ID, Client ID) */}
245
+ {(event.userId || event.sessionId || event.trackingId) && (<View style={detailStyles.sectionCard}>
246
+ <View style={detailStyles.sectionHeader}>
247
+ <LayersIcon color={AppColors.purple} size={12}/>
248
+ <Text style={detailStyles.sectionTitle}>SESSION & IDENTITY</Text>
249
+ </View>
250
+ <View style={detailStyles.sectionBody}>
251
+ {event.userId && (<View style={detailStyles.kvRow}>
252
+ <Text style={detailStyles.kvKey}>User ID:</Text>
253
+ <Text style={detailStyles.kvVal} selectable>{event.userId}</Text>
254
+ </View>)}
255
+ {event.sessionId && (<View style={detailStyles.kvRow}>
256
+ <Text style={detailStyles.kvKey}>Session ID:</Text>
257
+ <Text style={detailStyles.kvVal} selectable>{event.sessionId}</Text>
258
+ </View>)}
259
+ {event.trackingId && (<View style={detailStyles.kvRow}>
260
+ <Text style={detailStyles.kvKey}>Tracking ID:</Text>
261
+ <Text style={detailStyles.kvVal} selectable>{event.trackingId}</Text>
262
+ </View>)}
263
+ </View>
264
+ </View>)}
265
+
266
+ {/* Parameters Table Section */}
267
+ <View style={detailStyles.sectionCard}>
268
+ <View style={detailStyles.sectionHeaderBetween}>
269
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
270
+ <TableIcon color={AppColors.brandPurple} size={12}/>
271
+ <Text style={detailStyles.sectionTitle}>
272
+ EVENT PARAMETERS ({paramKeys.length})
273
+ </Text>
274
+ </View>
275
+ <Text style={detailStyles.sectionSubtitle}>
276
+ Key-Value Attributes
277
+ </Text>
278
+ </View>
279
+
280
+ {/* Search filter within parameters (if > 4 params) */}
281
+ {paramKeys.length > 4 && (<View style={detailStyles.searchBar}>
282
+ <SearchIcon color={AppColors.grayTextWeak} size={13}/>
283
+ <TextInput placeholder="Filter parameters by key or value..." placeholderTextColor={AppColors.grayTextWeak} value={paramSearch} onChangeText={setParamSearch} style={detailStyles.searchInput} autoCorrect={false} autoCapitalize="none"/>
284
+ {paramSearch.length > 0 && (<Pressable onPress={() => setParamSearch('')} hitSlop={8}>
285
+ <ClearIcon color={AppColors.grayTextWeak} size={13}/>
286
+ </Pressable>)}
287
+ </View>)}
288
+
289
+ {paramKeys.length === 0 ? (<View style={detailStyles.emptyParamsBox}>
290
+ <DocIcon color={AppColors.grayTextWeak} size={16}/>
291
+ <Text style={detailStyles.emptyParamsText}>
292
+ No custom parameters recorded for this event.
293
+ </Text>
294
+ </View>) : filteredParamEntries.length === 0 ? (<View style={detailStyles.emptyParamsBox}>
295
+ <Text style={detailStyles.emptyParamsText}>
296
+ No parameters matching "{paramSearch}"
297
+ </Text>
298
+ </View>) : (<View style={detailStyles.paramList}>
299
+ {filteredParamEntries.map(([key, val], pIdx) => {
300
+ const isObject = typeof val === 'object' && val !== null;
301
+ const isArray = Array.isArray(val);
302
+ let typeStr = typeof val;
303
+ if (val === null)
304
+ typeStr = 'null';
305
+ else if (isArray)
306
+ typeStr = `array [${val.length}]`;
307
+ else if (isObject)
308
+ typeStr = 'object';
309
+ const valString = isObject ? JSON.stringify(val, null, 2) : String(val);
310
+ // Highlight standard GA4 ecom/screen keys
311
+ const isKeyHighlighted = key === 'value' ||
312
+ key === 'price' ||
313
+ key === 'currency' ||
314
+ key === 'screen_name' ||
315
+ key === 'item_id' ||
316
+ key === 'item_name';
317
+ return (<AnimatedEntrance key={key} index={pIdx} distance={4}>
318
+ <View style={detailStyles.paramCard}>
319
+ {/* Param Top Row: Key + Type + Copy */}
320
+ <View style={detailStyles.paramCardHeader}>
321
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flex: 1, minWidth: 0 }}>
322
+ <HighlightText text={key} search={paramSearch} style={[
323
+ detailStyles.paramKeyText,
324
+ isKeyHighlighted && detailStyles.paramKeyHighlighted,
325
+ ]} highlightStyle={detailStyles.highlight}/>
326
+ <View style={detailStyles.typeBadge}>
327
+ <Text style={detailStyles.typeBadgeText}>
328
+ {typeStr.toUpperCase()}
329
+ </Text>
330
+ </View>
331
+ </View>
332
+
333
+ <CopyButton value={valString} label={key}/>
334
+ </View>
335
+
336
+ {/* Param Value Display */}
337
+ <View style={detailStyles.paramValBox}>
338
+ {isObject ? (<JsonViewer data={val} search={paramSearch} hideTabs wrap/>) : (<HighlightText text={valString} search={paramSearch} style={detailStyles.paramValText} highlightStyle={detailStyles.highlight} selectable/>)}
339
+ </View>
340
+ </View>
341
+ </AnimatedEntrance>);
342
+ })}
343
+ </View>)}
344
+ </View>
345
+
346
+ {/* User Properties Section (if any) */}
347
+ {userPropKeys.length > 0 && (<View style={detailStyles.sectionCard}>
348
+ <View style={detailStyles.sectionHeader}>
349
+ <SparkleIcon color={AppColors.purple} size={12}/>
350
+ <Text style={detailStyles.sectionTitle}>
351
+ USER PROPERTIES SNAPSHOT ({userPropKeys.length})
352
+ </Text>
353
+ </View>
354
+ <View style={detailStyles.sectionBody}>
355
+ {userPropKeys.map(k => (<View key={k} style={detailStyles.kvRow}>
356
+ <Text style={detailStyles.kvKey}>{k}:</Text>
357
+ <Text style={detailStyles.kvVal} selectable>
358
+ {String(userProperties[k])}
359
+ </Text>
360
+ </View>))}
361
+ </View>
362
+ </View>)}
363
+ </View>)}
364
+
365
+ {/* ── JSON Tree View ────────────────────────────────────────────────── */}
366
+ {activeViewTab === 'tree' && (<View style={detailStyles.jsonContainer}>
367
+ <JsonViewer data={fullJsonData} defaultExpandDepth={1} mode="pretty" fullHeight/>
368
+ </View>)}
369
+
370
+ {/* ── Raw Payload View ──────────────────────────────────────────────── */}
371
+ {activeViewTab === 'raw' && (<View style={detailStyles.jsonContainer}>
372
+ <JsonViewer data={fullJsonData} mode="raw" fullHeight/>
373
+ </View>)}
125
374
  </ScrollView>);
126
375
  };
127
376
  const detailStyles = StyleSheet.create({
128
- scroll: { flex: 1, backgroundColor: AppColors.grayBackground },
377
+ scroll: {
378
+ flex: 1,
379
+ backgroundColor: AppColors.grayBackground,
380
+ },
129
381
  content: {
130
- paddingTop: 0,
382
+ paddingTop: 12,
131
383
  paddingBottom: 40,
384
+ paddingHorizontal: 12,
385
+ },
386
+ heroCard: {
387
+ backgroundColor: AppColors.white,
388
+ borderRadius: 12,
389
+ padding: 14,
390
+ borderWidth: 1,
391
+ borderColor: AppColors.dividerColor,
392
+ shadowColor: AppColors.primaryBlack,
393
+ shadowOffset: { width: 0, height: 1 },
394
+ shadowOpacity: 0.04,
395
+ shadowRadius: 4,
396
+ elevation: 2,
397
+ marginBottom: 12,
398
+ },
399
+ heroTopRow: {
400
+ flexDirection: 'row',
401
+ alignItems: 'center',
402
+ justifyContent: 'space-between',
403
+ marginBottom: 10,
404
+ },
405
+ sourceBadge: {
406
+ flexDirection: 'row',
407
+ alignItems: 'center',
408
+ gap: 4.5,
409
+ paddingHorizontal: 7,
410
+ paddingVertical: 3,
411
+ borderRadius: 5,
412
+ borderWidth: 1,
132
413
  },
133
- jsonBlockContainer: {
134
- marginHorizontal: 16,
135
- marginTop: 16,
136
- marginBottom: 16,
414
+ sourceDot: {
415
+ width: 6,
416
+ height: 6,
417
+ borderRadius: 3,
418
+ },
419
+ sourceBadgeText: {
420
+ fontFamily: AppFonts.interBold,
421
+ fontSize: 9.5,
422
+ letterSpacing: 0.4,
423
+ },
424
+ catBadge: {
425
+ flexDirection: 'row',
426
+ alignItems: 'center',
427
+ gap: 4,
428
+ paddingHorizontal: 7,
429
+ paddingVertical: 3,
430
+ borderRadius: 5,
431
+ borderWidth: 1,
432
+ },
433
+ catBadgeText: {
434
+ fontFamily: AppFonts.interBold,
435
+ fontSize: 9.5,
436
+ letterSpacing: 0.3,
437
+ },
438
+ titleRow: {
439
+ marginBottom: 8,
440
+ },
441
+ eventTitle: {
442
+ fontFamily: AppFonts.interBold,
443
+ fontSize: 16,
444
+ color: AppColors.primaryBlack,
445
+ lineHeight: 22,
446
+ },
447
+ metaRow: {
448
+ flexDirection: 'row',
449
+ alignItems: 'center',
450
+ gap: 6,
451
+ flexWrap: 'wrap',
452
+ marginBottom: 10,
453
+ },
454
+ timePill: {
455
+ flexDirection: 'row',
456
+ alignItems: 'center',
457
+ gap: 4,
458
+ backgroundColor: `${AppColors.purple}14`,
459
+ paddingHorizontal: 7,
460
+ paddingVertical: 3,
461
+ borderRadius: 5,
462
+ borderWidth: 1,
463
+ borderColor: `${AppColors.purple}2E`,
464
+ },
465
+ timeText: {
466
+ fontFamily: AppFonts.interMedium,
467
+ fontSize: 10.5,
468
+ color: AppColors.purple,
469
+ },
470
+ screenPill: {
471
+ flexDirection: 'row',
472
+ alignItems: 'center',
473
+ gap: 4,
474
+ backgroundColor: `${AppColors.sky500}14`,
475
+ paddingHorizontal: 7,
476
+ paddingVertical: 3,
477
+ borderRadius: 5,
478
+ borderWidth: 1,
479
+ borderColor: `${AppColors.sky500}2E`,
480
+ maxWidth: 220,
481
+ },
482
+ screenText: {
483
+ fontFamily: AppFonts.interMedium,
484
+ fontSize: 10.5,
485
+ color: AppColors.sky600,
486
+ },
487
+ metricsBar: {
488
+ flexDirection: 'row',
489
+ alignItems: 'center',
490
+ gap: 6,
491
+ flexWrap: 'wrap',
492
+ paddingTop: 8,
493
+ borderTopWidth: 1,
494
+ borderTopColor: AppColors.dividerColor,
495
+ },
496
+ metricPill: {
497
+ flexDirection: 'row',
498
+ alignItems: 'center',
499
+ gap: 4,
500
+ backgroundColor: AppColors.grayBackground,
501
+ paddingHorizontal: 7,
502
+ paddingVertical: 3,
503
+ borderRadius: 5,
504
+ borderWidth: 1,
505
+ borderColor: AppColors.dividerColor,
506
+ },
507
+ metricLabel: {
508
+ fontFamily: AppFonts.interMedium,
509
+ fontSize: 10.5,
510
+ color: AppColors.grayTextWeak,
511
+ },
512
+ metricValue: {
513
+ fontFamily: AppFonts.interBold,
514
+ fontSize: 10.5,
515
+ color: AppColors.primaryBlack,
516
+ },
517
+ revenuePill: {
518
+ flexDirection: 'row',
519
+ alignItems: 'center',
520
+ gap: 4,
521
+ backgroundColor: AppColors.emeraldBg,
522
+ paddingHorizontal: 7,
523
+ paddingVertical: 3,
524
+ borderRadius: 5,
525
+ borderWidth: 1,
526
+ borderColor: AppColors.emeraldBorder,
527
+ },
528
+ revenueValue: {
529
+ fontFamily: AppFonts.interBold,
530
+ fontSize: 10.5,
531
+ color: '#047857',
532
+ },
533
+ itemsPill: {
534
+ backgroundColor: '#EFF6FF',
535
+ paddingHorizontal: 7,
536
+ paddingVertical: 3,
537
+ borderRadius: 5,
538
+ borderWidth: 1,
539
+ borderColor: '#BFDBFE',
540
+ },
541
+ itemsValue: {
542
+ fontFamily: AppFonts.interBold,
543
+ fontSize: 10.5,
544
+ color: '#1D4ED8',
545
+ },
546
+ tabsWrap: {
547
+ marginBottom: 12,
548
+ },
549
+ tabContent: {
550
+ gap: 12,
551
+ },
552
+ sectionCard: {
553
+ backgroundColor: AppColors.white,
554
+ borderRadius: 12,
555
+ padding: 12,
556
+ borderWidth: 1,
557
+ borderColor: AppColors.dividerColor,
558
+ shadowColor: AppColors.primaryBlack,
559
+ shadowOffset: { width: 0, height: 1 },
560
+ shadowOpacity: 0.04,
561
+ shadowRadius: 4,
562
+ elevation: 2,
563
+ },
564
+ sectionHeader: {
565
+ flexDirection: 'row',
566
+ alignItems: 'center',
567
+ gap: 6,
568
+ marginBottom: 10,
569
+ paddingBottom: 6,
570
+ borderBottomWidth: 1,
571
+ borderBottomColor: AppColors.dividerColor,
572
+ },
573
+ sectionHeaderBetween: {
574
+ flexDirection: 'row',
575
+ alignItems: 'center',
576
+ justifyContent: 'space-between',
577
+ marginBottom: 10,
578
+ paddingBottom: 6,
579
+ borderBottomWidth: 1,
580
+ borderBottomColor: AppColors.dividerColor,
581
+ },
582
+ sectionTitle: {
583
+ fontFamily: AppFonts.interBold,
584
+ fontSize: 11,
585
+ color: AppColors.slate700,
586
+ letterSpacing: 0.4,
587
+ },
588
+ sectionSubtitle: {
589
+ fontFamily: AppFonts.interMedium,
590
+ fontSize: 10,
591
+ color: AppColors.grayTextWeak,
592
+ },
593
+ sectionBody: {
594
+ gap: 6,
595
+ },
596
+ kvRow: {
597
+ flexDirection: 'row',
598
+ alignItems: 'center',
599
+ justifyContent: 'space-between',
600
+ paddingVertical: 3,
601
+ },
602
+ kvKey: {
603
+ fontFamily: AppFonts.interMedium,
604
+ fontSize: 11,
605
+ color: AppColors.grayTextWeak,
606
+ },
607
+ kvVal: {
608
+ fontFamily: AppFonts.interBold,
609
+ fontSize: 11,
610
+ color: AppColors.primaryBlack,
611
+ },
612
+ searchBar: {
613
+ flexDirection: 'row',
614
+ alignItems: 'center',
615
+ backgroundColor: AppColors.grayBackground,
616
+ borderRadius: 8,
617
+ paddingHorizontal: 8,
618
+ paddingVertical: 5,
137
619
  borderWidth: 1,
620
+ borderColor: AppColors.dividerColor,
621
+ marginBottom: 10,
622
+ gap: 6,
623
+ },
624
+ searchInput: {
625
+ flex: 1,
626
+ fontFamily: AppFonts.interRegular,
627
+ fontSize: 11,
628
+ color: AppColors.primaryBlack,
629
+ paddingVertical: 0,
630
+ },
631
+ emptyParamsBox: {
632
+ alignItems: 'center',
633
+ justifyContent: 'center',
634
+ paddingVertical: 20,
635
+ gap: 6,
636
+ },
637
+ emptyParamsText: {
638
+ fontFamily: AppFonts.interMedium,
639
+ fontSize: 11,
640
+ color: AppColors.grayTextWeak,
641
+ },
642
+ paramList: {
643
+ gap: 8,
644
+ },
645
+ paramCard: {
646
+ backgroundColor: AppColors.grayBackground,
647
+ borderRadius: 8,
648
+ padding: 9,
649
+ borderWidth: 1,
650
+ borderColor: AppColors.dividerColor,
651
+ },
652
+ paramCardHeader: {
653
+ flexDirection: 'row',
654
+ alignItems: 'center',
655
+ justifyContent: 'space-between',
656
+ marginBottom: 4,
657
+ },
658
+ paramKeyText: {
659
+ fontFamily: AppFonts.interBold,
660
+ fontSize: 11.5,
661
+ color: AppColors.primaryBlack,
662
+ },
663
+ paramKeyHighlighted: {
664
+ color: AppColors.brandPurple,
665
+ },
666
+ typeBadge: {
667
+ backgroundColor: '#E2E8F0',
668
+ paddingHorizontal: 5,
669
+ paddingVertical: 1.5,
670
+ borderRadius: 3,
671
+ },
672
+ typeBadgeText: {
673
+ fontFamily: AppFonts.interBold,
674
+ fontSize: 8.5,
675
+ color: AppColors.slate700,
676
+ letterSpacing: 0.3,
677
+ },
678
+ paramValBox: {
679
+ marginTop: 2,
680
+ },
681
+ paramValText: {
682
+ fontFamily: AppFonts.interRegular,
683
+ fontSize: 11,
684
+ color: AppColors.slate700,
685
+ lineHeight: 16,
686
+ },
687
+ jsonContainer: {
138
688
  borderRadius: 12,
689
+ borderWidth: 1,
690
+ borderColor: AppColors.dividerColor,
691
+ overflow: 'hidden',
692
+ backgroundColor: AppColors.white,
693
+ minHeight: 300,
694
+ },
695
+ highlight: {
696
+ backgroundColor: '#FEF08A',
697
+ color: '#854D0E',
698
+ fontFamily: AppFonts.interBold,
139
699
  },
140
700
  });
141
701
  export default AnalyticsDetail;