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.
- package/dist/commonjs/components/AnalyticsDetail.js +668 -108
- package/dist/commonjs/components/AppHeaderLogo.js +5 -2
- package/dist/commonjs/components/ConsoleLogCard.js +81 -59
- package/dist/commonjs/components/DomainHeader.js +21 -17
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +297 -200
- package/dist/commonjs/components/Inspector/ConsoleTab.js +162 -56
- package/dist/commonjs/components/Inspector/InspectorHeader.js +56 -119
- package/dist/commonjs/components/Inspector/LogDetail.js +45 -12
- 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 +1 -1
- package/dist/commonjs/components/NetworkIcons.js +19 -10
- package/dist/commonjs/components/TreeNode.js +12 -2
- 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 +1 -1
- package/dist/commonjs/helpers/index.js +32 -16
- package/dist/commonjs/i18n/locales/en.json +45 -2
- package/dist/commonjs/styles/AppColors.d.ts +6 -0
- package/dist/commonjs/styles/AppColors.js +6 -0
- package/dist/commonjs/styles/index.d.ts +22 -1
- package/dist/commonjs/styles/index.js +25 -4
- package/dist/commonjs/types/interfaces.d.ts +7 -0
- package/dist/esm/components/AnalyticsDetail.js +670 -110
- package/dist/esm/components/AppHeaderLogo.js +5 -2
- package/dist/esm/components/ConsoleLogCard.js +81 -59
- package/dist/esm/components/DomainHeader.js +21 -17
- package/dist/esm/components/Inspector/AnalyticsTab.js +298 -201
- package/dist/esm/components/Inspector/ConsoleTab.js +163 -57
- package/dist/esm/components/Inspector/InspectorHeader.js +57 -120
- package/dist/esm/components/Inspector/LogDetail.js +45 -12
- 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 +1 -1
- package/dist/esm/components/NetworkIcons.js +19 -10
- package/dist/esm/components/TreeNode.js +12 -2
- 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 +1 -1
- package/dist/esm/helpers/index.js +32 -16
- package/dist/esm/i18n/locales/en.json +45 -2
- package/dist/esm/styles/AppColors.d.ts +6 -0
- package/dist/esm/styles/AppColors.js +6 -0
- package/dist/esm/styles/index.d.ts +22 -1
- package/dist/esm/styles/index.js +25 -4
- package/dist/esm/types/interfaces.d.ts +7 -0
- 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
|
|
7
|
-
|
|
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 [
|
|
16
|
-
const [
|
|
17
|
-
const params = event.params ?? {};
|
|
18
|
-
const userProperties = event.userProperties ?? {};
|
|
19
|
-
const
|
|
20
|
-
const
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
{/*
|
|
43
|
-
<View style={
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
-
{/*
|
|
115
|
-
<View style={
|
|
116
|
-
|
|
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: {
|
|
377
|
+
scroll: {
|
|
378
|
+
flex: 1,
|
|
379
|
+
backgroundColor: AppColors.grayBackground,
|
|
380
|
+
},
|
|
129
381
|
content: {
|
|
130
|
-
paddingTop:
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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;
|