react-native-inapp-inspector 1.1.11 → 1.1.13

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 (53) hide show
  1. package/dist/commonjs/components/AnalyticsDetail.js +93 -479
  2. package/dist/commonjs/components/AnalyticsEventCard.js +25 -25
  3. package/dist/commonjs/components/ConsoleLogCard.js +3 -1
  4. package/dist/commonjs/components/HighlightText.js +1 -1
  5. package/dist/commonjs/components/JsonViewer.d.ts +3 -1
  6. package/dist/commonjs/components/JsonViewer.js +327 -9
  7. package/dist/commonjs/components/MetaAccordion.d.ts +10 -1
  8. package/dist/commonjs/components/MetaAccordion.js +121 -24
  9. package/dist/commonjs/components/NetworkIcons.d.ts +1 -0
  10. package/dist/commonjs/components/NetworkIcons.js +8 -1
  11. package/dist/commonjs/components/ReduxTreeView.d.ts +12 -17
  12. package/dist/commonjs/components/ReduxTreeView.js +1045 -632
  13. package/dist/commonjs/components/SectionHeader.d.ts +1 -1
  14. package/dist/commonjs/components/SectionHeader.js +7 -1
  15. package/dist/commonjs/components/TouchableScale.js +8 -0
  16. package/dist/commonjs/constants/version.d.ts +1 -1
  17. package/dist/commonjs/constants/version.js +1 -1
  18. package/dist/commonjs/customHooks/reduxLogger.d.ts +2 -0
  19. package/dist/commonjs/customHooks/reduxLogger.js +2 -0
  20. package/dist/commonjs/helpers/index.d.ts +3 -0
  21. package/dist/commonjs/helpers/index.js +103 -1
  22. package/dist/commonjs/helpers/settingsStore.d.ts +1 -0
  23. package/dist/commonjs/helpers/settingsStore.js +4 -0
  24. package/dist/commonjs/index.js +385 -339
  25. package/dist/commonjs/types/index.d.ts +3 -0
  26. package/dist/esm/components/AnalyticsDetail.js +94 -480
  27. package/dist/esm/components/AnalyticsEventCard.js +25 -25
  28. package/dist/esm/components/ConsoleLogCard.js +3 -1
  29. package/dist/esm/components/HighlightText.js +3 -3
  30. package/dist/esm/components/JsonViewer.d.ts +3 -1
  31. package/dist/esm/components/JsonViewer.js +295 -10
  32. package/dist/esm/components/MetaAccordion.d.ts +10 -1
  33. package/dist/esm/components/MetaAccordion.js +90 -26
  34. package/dist/esm/components/NetworkIcons.d.ts +1 -0
  35. package/dist/esm/components/NetworkIcons.js +6 -0
  36. package/dist/esm/components/ReduxTreeView.d.ts +12 -17
  37. package/dist/esm/components/ReduxTreeView.js +1047 -629
  38. package/dist/esm/components/SectionHeader.d.ts +1 -1
  39. package/dist/esm/components/SectionHeader.js +8 -2
  40. package/dist/esm/components/TouchableScale.js +9 -1
  41. package/dist/esm/constants/version.d.ts +1 -1
  42. package/dist/esm/constants/version.js +1 -1
  43. package/dist/esm/customHooks/reduxLogger.d.ts +2 -0
  44. package/dist/esm/customHooks/reduxLogger.js +2 -0
  45. package/dist/esm/helpers/index.d.ts +3 -0
  46. package/dist/esm/helpers/index.js +100 -1
  47. package/dist/esm/helpers/settingsStore.d.ts +1 -0
  48. package/dist/esm/helpers/settingsStore.js +3 -0
  49. package/dist/esm/index.js +389 -343
  50. package/dist/esm/types/index.d.ts +3 -0
  51. package/example/package-lock.json +33 -74
  52. package/example/package.json +1 -1
  53. package/package.json +1 -1
@@ -89,6 +89,9 @@ export interface SectionHeaderProps {
89
89
  showDiff?: boolean;
90
90
  isDiffing?: boolean;
91
91
  onToggleDiff?: () => void;
92
+ showSearch?: boolean;
93
+ isSearching?: boolean;
94
+ onToggleSearch?: () => void;
92
95
  }
93
96
  export interface TreeNodeProps {
94
97
  data: unknown;
@@ -1,297 +1,125 @@
1
1
  import React, { useState } from 'react';
2
- import { Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native';
3
- import LinearGradient from 'react-native-linear-gradient';
2
+ import { ScrollView, StyleSheet, Text, View, } from 'react-native';
4
3
  // Components
5
4
  import CopyButton from './CopyButton';
6
5
  import JsonViewer from './JsonViewer';
7
- // Icons
8
- import { ClearIcon } from './NetworkIcons';
6
+ import TouchableScale from './TouchableScale';
9
7
  // Utils
10
8
  import { AppFonts } from '../styles/AppFonts';
9
+ import { getSize } from '../helpers';
11
10
  // Stylesheet
12
11
  import { AppColors } from '../styles/AppColors';
13
- import styles from '../styles';
14
- import { getEventColor } from './AnalyticsEventCard';
15
- // Helpers
16
- import { formatDateTimeToAnalytics } from '../helpers';
17
- // Sub Components
18
- const ParamRowItem = ({ paramKey, value, }) => {
19
- const isObject = typeof value === 'object' && value !== null;
20
- const valStr = isObject ? JSON.stringify(value, null, 2) : String(value);
21
- const [expanded, setExpanded] = useState(false);
22
- const isLong = valStr.length > 80 || valStr.includes('\n') || isObject;
23
- return (<View style={[detailStyles.dataBox, { backgroundColor: AppColors.primaryLight, borderColor: AppColors.grayBorderSecondary }]}>
24
- <View style={detailStyles.dataBoxHeader}>
25
- <Text style={[detailStyles.dataBoxKey, { color: AppColors.primaryBlack }]} numberOfLines={1}>
26
- {paramKey}
27
- </Text>
28
- <CopyButton value={valStr} label={paramKey}/>
29
- </View>
30
- {expanded && isObject ? (<View style={[detailStyles.jsonBlock, { backgroundColor: AppColors.grayBackground, borderColor: AppColors.grayBorderSecondary }]}>
31
- <JsonViewer data={value}/>
32
- </View>) : (<Text style={[detailStyles.dataBoxVal, { color: AppColors.primaryBlack }]} selectable numberOfLines={expanded ? undefined : 2}>
33
- {valStr}
34
- </Text>)}
35
- {isLong && (<Pressable onPress={() => setExpanded(!expanded)} style={[detailStyles.showMoreBtn, { backgroundColor: AppColors.grayBackground }]}>
36
- <Text style={[detailStyles.showMoreText, { color: AppColors.grayTextStrong }]}>
37
- {expanded ? 'Show Less' : 'Show More'}
38
- </Text>
39
- </Pressable>)}
40
- </View>);
41
- };
42
- const ParamTable = ({ data, emptyLabel, }) => {
43
- const entries = Object.keys(data).map(key => ({
44
- key,
45
- value: data[key],
46
- }));
47
- if (entries.length === 0) {
48
- return (<View style={[detailStyles.emptyParams, { backgroundColor: AppColors.primaryLight, borderColor: AppColors.grayBorderSecondary }]}>
49
- <Text style={[detailStyles.emptyParamsText, { color: AppColors.grayTextWeak }]}>
50
- {emptyLabel ?? 'No parameters'}
51
- </Text>
52
- </View>);
53
- }
54
- return (<View style={detailStyles.paramList}>
55
- {entries.map(({ key, value }) => (<ParamRowItem key={key} paramKey={key} value={value}/>))}
56
- </View>);
57
- };
58
- const SectionCard = ({ title, children, count, accentColor = AppColors.purple, }) => {
59
- if (!title) {
60
- return (<View style={{ paddingHorizontal: 16, paddingTop: 12, paddingBottom: 20 }}>
61
- {children}
62
- </View>);
63
- }
64
- return (<View style={[detailStyles.sectionCard, { backgroundColor: AppColors.primaryLight, borderColor: AppColors.grayBorderSecondary }]}>
65
- <View style={detailStyles.sectionHeader}>
66
- <View style={detailStyles.sectionTitleRow}>
67
- <View style={[
68
- detailStyles.sectionAccentDot,
69
- { backgroundColor: accentColor },
70
- ]}/>
71
- <Text style={[detailStyles.sectionTitle, { color: AppColors.primaryBlack }]}>{title.toUpperCase()}</Text>
72
- </View>
73
- {typeof count === 'number' && (<View style={[
74
- detailStyles.countBadge,
75
- {
76
- borderColor: `${accentColor}55`,
77
- backgroundColor: `${accentColor}12`,
78
- },
79
- ]}>
80
- <Text style={[detailStyles.countBadgeText, { color: accentColor }]}>
81
- {count}
82
- </Text>
83
- </View>)}
84
- </View>
85
- <View style={{ paddingHorizontal: 14, paddingBottom: 14 }}>{children}</View>
86
- </View>);
87
- };
88
12
  const AnalyticsDetail = ({ event, }) => {
89
- const [search, setSearch] = useState('');
90
- const [activeTab, setActiveTab] = useState('unformatted');
91
- const SOURCE_COLORS = {
92
- firebase: '#E07B1A',
93
- manual: AppColors.purple,
94
- };
13
+ const [expandDepth, setExpandDepth] = useState(1);
14
+ const [treeKey, setTreeKey] = useState(0);
95
15
  const params = event.params ?? {};
96
16
  const userProperties = event.userProperties ?? {};
97
- const eventColor = getEventColor(event.name);
98
- const sourceColor = SOURCE_COLORS[event.source] ?? AppColors.purple;
99
- const paramCount = Object.keys(params).length;
100
17
  const upCount = Object.keys(userProperties).length;
101
- const topMetrics = [];
102
- if (event.screenName) {
103
- topMetrics.push({
104
- key: 'Screen',
105
- value: event.screenName,
106
- color: AppColors.skyBlue,
107
- });
108
- }
109
- if (event.userId) {
110
- topMetrics.push({
111
- key: 'User ID',
112
- value: event.userId,
113
- color: AppColors.greenColor,
114
- });
115
- }
116
- if (event.pageTitle) {
117
- topMetrics.push({
118
- key: 'Page',
119
- value: event.pageTitle,
120
- color: AppColors.purple,
121
- });
122
- }
123
- if (params?.primary_category) {
124
- topMetrics.push({
125
- key: 'Category',
126
- value: String(params.primary_category),
127
- color: AppColors.purple,
128
- });
129
- }
130
- if (params?.flow) {
131
- topMetrics.push({
132
- key: 'Flow',
133
- value: String(params.flow),
134
- color: AppColors.greenColor,
135
- });
136
- }
137
- if (params?.market_language) {
138
- topMetrics.push({
139
- key: 'Language',
140
- value: String(params.market_language),
141
- color: '#F59E0B',
142
- }); // Amber
143
- }
144
- if (params?.tripType) {
145
- topMetrics.push({
146
- key: 'Trip Type',
147
- value: String(params.tripType),
148
- color: '#3B82F6',
149
- }); // Blue
18
+ const jsonData = {
19
+ name: event.name,
20
+ params: params,
21
+ ...(upCount > 0 ? { userProperties } : {}),
22
+ };
23
+ const isObject = typeof jsonData === 'object' && jsonData !== null;
24
+ const isArray = Array.isArray(jsonData);
25
+ let typeLabel = typeof jsonData;
26
+ let countLabel = '';
27
+ if (jsonData === null) {
28
+ typeLabel = 'null';
150
29
  }
151
- if (userProperties?.platform_type) {
152
- topMetrics.push({
153
- key: 'Platform',
154
- value: String(userProperties.platform_type),
155
- color: AppColors.skyBlue,
156
- });
30
+ else if (isArray) {
31
+ typeLabel = 'array';
32
+ countLabel = `${jsonData.length} items`;
157
33
  }
158
- if (userProperties?.login_status) {
159
- topMetrics.push({
160
- key: 'Login Status',
161
- value: String(userProperties.login_status),
162
- color: '#64748B',
163
- });
34
+ else if (isObject) {
35
+ typeLabel = 'object';
36
+ countLabel = `${Object.keys(jsonData).length} keys`;
164
37
  }
165
- topMetrics.unshift({
166
- key: 'Source',
167
- value: event.source,
168
- color: sourceColor,
169
- });
170
38
  return (<ScrollView style={[detailStyles.scroll, { backgroundColor: AppColors.grayBackground }]} contentContainerStyle={detailStyles.content} showsVerticalScrollIndicator contentInsetAdjustmentBehavior="never" automaticallyAdjustContentInsets={false}>
171
- <LinearGradient colors={[
172
- `${eventColor}18`,
173
- `${eventColor}06`,
174
- AppColors.grayBackground,
175
- ]} style={detailStyles.hero}>
176
- <View style={{
39
+
40
+ {/* Clean Toolbar */}
41
+ <View style={{
177
42
  flexDirection: 'row',
178
- width: '100%',
179
43
  justifyContent: 'space-between',
180
- alignItems: 'flex-start',
44
+ alignItems: 'center',
181
45
  paddingHorizontal: 16,
182
- paddingTop: 14,
183
- marginBottom: 8,
46
+ paddingVertical: 12,
47
+ borderBottomWidth: 1,
48
+ borderBottomColor: AppColors.grayBorderSecondary,
49
+ backgroundColor: AppColors.primaryLight,
50
+ }}>
51
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
52
+ <TouchableScale style={{
53
+ flexDirection: 'row',
54
+ alignItems: 'center',
55
+ gap: 6,
56
+ backgroundColor: AppColors.grayBackground,
57
+ paddingHorizontal: 12,
58
+ paddingVertical: 6,
59
+ borderRadius: 8,
60
+ borderWidth: 1,
61
+ borderColor: AppColors.grayBorderSecondary,
62
+ }} onPress={() => {
63
+ const nextDepth = expandDepth === 99 ? 1 : 99;
64
+ setExpandDepth(nextDepth);
65
+ setTreeKey(prev => prev + 1);
184
66
  }}>
185
- <View style={{ flex: 1, paddingRight: 10 }}>
186
67
  <Text style={{
187
68
  fontFamily: AppFonts.interBold,
188
- fontSize: 18,
189
- color: AppColors.primaryBlack,
190
- marginBottom: 4,
191
- }} numberOfLines={2}>
192
- {event.name}
193
- </Text>
194
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
195
- <View style={{
196
- width: 8,
197
- height: 8,
198
- borderRadius: 4,
199
- backgroundColor: sourceColor,
200
- }}/>
201
- <Text style={{
202
- fontFamily: AppFonts.interMedium,
203
69
  fontSize: 12,
204
- color: AppColors.grayTextStrong,
70
+ color: AppColors.purple,
71
+ }}>
72
+ {expandDepth === 99 ? 'Collapse All' : 'Expand All'}
73
+ </Text>
74
+ </TouchableScale>
75
+ <View style={{
76
+ flexDirection: 'row',
77
+ alignItems: 'center',
78
+ backgroundColor: AppColors.grayBackground,
79
+ borderRadius: 6,
80
+ overflow: 'hidden',
81
+ borderWidth: 1,
82
+ borderColor: AppColors.grayBorderSecondary,
205
83
  }}>
206
- {formatDateTimeToAnalytics(event.timestamp)}
84
+ <View style={{ paddingHorizontal: 6, paddingVertical: 4, backgroundColor: AppColors.purple + '18' }}>
85
+ <Text style={{ fontFamily: AppFonts.interBold, fontSize: 9, color: AppColors.purple, letterSpacing: 0.3 }}>
86
+ {typeLabel.toUpperCase()}
207
87
  </Text>
208
88
  </View>
89
+ {countLabel ? (<View style={{ paddingHorizontal: 6, paddingVertical: 4 }}>
90
+ <Text style={{ fontFamily: AppFonts.interMedium, fontSize: 9, color: AppColors.grayText }}>
91
+ {countLabel}
92
+ </Text>
93
+ </View>) : null}
94
+ </View>
95
+ <View style={{
96
+ backgroundColor: AppColors.grayBackground,
97
+ borderRadius: 6,
98
+ paddingHorizontal: 6,
99
+ paddingVertical: 4,
100
+ borderWidth: 1,
101
+ borderColor: AppColors.grayBorderSecondary,
102
+ }}>
103
+ <Text style={{ fontFamily: AppFonts.interMedium, fontSize: 9, color: AppColors.grayText }}>
104
+ {getSize(jsonData)}
105
+ </Text>
209
106
  </View>
210
- <CopyButton value={JSON.stringify({ name: event.name, params: params }, null, 2)} label="Copy JSON"/>
211
107
  </View>
212
108
 
213
- {topMetrics.length > 0 && (<ScrollView horizontal showsHorizontalScrollIndicator={false} style={detailStyles.metricsScroll} contentContainerStyle={detailStyles.metricsGrid}>
214
- {topMetrics.map((item, idx) => (<View key={idx} style={[
215
- detailStyles.metricCard,
216
- {
217
- borderLeftColor: item.color,
218
- backgroundColor: AppColors.primaryLight,
219
- },
220
- ]}>
221
- <Text style={[detailStyles.metricLabel, { color: AppColors.grayTextWeak }]}>{item.key}</Text>
222
- <Text style={[detailStyles.metricValue, { color: item.color }]} numberOfLines={1}>
223
- {item.value}
224
- </Text>
225
- </View>))}
226
- </ScrollView>)}
227
- </LinearGradient>
109
+ <CopyButton value={JSON.stringify(jsonData, null, 2)} label="Copy JSON"/>
110
+ </View>
228
111
 
229
- <View style={[detailStyles.tabRow, { backgroundColor: AppColors.grayBackground }]}>
230
- <Pressable style={[
231
- detailStyles.tabButton,
232
- activeTab === 'unformatted' && [detailStyles.tabActive, { backgroundColor: AppColors.primaryLight }],
233
- ]} onPress={() => setActiveTab('unformatted')}>
234
- <Text style={[
235
- detailStyles.tabText,
236
- { color: AppColors.grayTextWeak },
237
- activeTab === 'unformatted' && [detailStyles.tabTextActive, { color: AppColors.primaryBlack }],
238
- ]}>
239
- JSON Viewer
240
- </Text>
241
- </Pressable>
242
- <Pressable style={[
243
- detailStyles.tabButton,
244
- activeTab === 'formatted' && [detailStyles.tabActive, { backgroundColor: AppColors.primaryLight }],
245
- ]} onPress={() => setActiveTab('formatted')}>
246
- <Text style={[
247
- detailStyles.tabText,
248
- { color: AppColors.grayTextWeak },
249
- activeTab === 'formatted' && [detailStyles.tabTextActive, { color: AppColors.primaryBlack }],
112
+ {/* Direct JSON Viewer View */}
113
+ <View style={[
114
+ detailStyles.jsonBlockContainer,
115
+ {
116
+ backgroundColor: AppColors.primaryLight,
117
+ borderColor: AppColors.grayBorderSecondary,
118
+ marginTop: 16,
119
+ },
250
120
  ]}>
251
- Tabular View
252
- </Text>
253
- </Pressable>
121
+ <JsonViewer key={treeKey} data={jsonData} defaultExpandDepth={expandDepth} fullHeight/>
254
122
  </View>
255
-
256
- {activeTab === 'unformatted' && (<View style={detailStyles.searchRow}>
257
- <View style={[styles.detailSearchBox, { backgroundColor: AppColors.primaryLight, borderColor: AppColors.grayBorderSecondary }]}>
258
- <TextInput placeholder="Search JSON data..." placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={[styles.detailSearchInput, { color: AppColors.primaryBlack }]} autoCorrect={false} autoCapitalize="none"/>
259
- {search.length > 0 && (<Pressable onPress={() => setSearch('')} hitSlop={10} style={{ padding: 8 }}>
260
- <ClearIcon color={AppColors.grayTextWeak} size={14}/>
261
- </Pressable>)}
262
- </View>
263
- </View>)}
264
-
265
- {activeTab === 'formatted' && (<View style={{ gap: 10 }}>
266
- {paramCount > 0 && (<SectionCard title="Event Parameters" count={paramCount} accentColor={eventColor}>
267
- <ParamTable data={params} emptyLabel="No parameters"/>
268
- </SectionCard>)}
269
-
270
- {upCount > 0 && (<SectionCard title="User Properties" count={upCount} accentColor={AppColors.greenColor}>
271
- <ParamTable data={userProperties} emptyLabel="No user properties"/>
272
- </SectionCard>)}
273
- </View>)}
274
-
275
- {activeTab === 'unformatted' && (<SectionCard accentColor={AppColors.purple}>
276
- <View style={[detailStyles.jsonBlock, { marginLeft: 0, marginTop: 0, backgroundColor: AppColors.grayBackground, borderColor: AppColors.grayBorderSecondary }]}>
277
- <JsonViewer data={{
278
- name: event.name,
279
- params: params,
280
- ...(upCount > 0
281
- ? {
282
- userProperties: userProperties,
283
- }
284
- : {}),
285
- }} search={search}/>
286
- </View>
287
- </SectionCard>)}
288
-
289
- {/* Empty state */}
290
- {paramCount === 0 && upCount === 0 && activeTab !== 'unformatted' && (<View style={[detailStyles.emptyParams, { backgroundColor: AppColors.primaryLight, borderColor: AppColors.grayBorderSecondary }]}>
291
- <Text style={[detailStyles.emptyParamsText, { color: AppColors.grayTextWeak }]}>
292
- No parameters recorded for this event
293
- </Text>
294
- </View>)}
295
123
  </ScrollView>);
296
124
  };
297
125
  const detailStyles = StyleSheet.create({
@@ -300,226 +128,12 @@ const detailStyles = StyleSheet.create({
300
128
  paddingTop: 0,
301
129
  paddingBottom: 40,
302
130
  },
303
- hero: {
304
- paddingTop: 0,
305
- paddingBottom: 16,
306
- paddingHorizontal: 0,
307
- gap: 8,
308
- marginTop: 0,
309
- marginBottom: 0,
310
- width: '100%',
311
- alignSelf: 'stretch',
312
- },
313
- searchRow: {
314
- paddingHorizontal: 16,
315
- marginVertical: 6,
316
- },
317
- sectionCard: {
318
- marginHorizontal: 0,
319
- borderRadius: 12,
320
- overflow: 'hidden',
321
- borderWidth: 1,
322
- borderColor: AppColors.grayBorderSecondary,
323
- backgroundColor: AppColors.primaryLight,
324
- shadowColor: '#000',
325
- shadowOpacity: 0.04,
326
- shadowRadius: 4,
327
- shadowOffset: { width: 0, height: 1 },
328
- elevation: 1,
329
- },
330
- sectionHeader: {
331
- flexDirection: 'row',
332
- alignItems: 'center',
333
- justifyContent: 'space-between',
334
- paddingHorizontal: 14,
335
- paddingVertical: 12,
336
- },
337
- sectionTitleRow: {
338
- flexDirection: 'row',
339
- alignItems: 'center',
340
- gap: 8,
341
- flex: 1,
342
- },
343
- sectionAccentDot: {
344
- width: 8,
345
- height: 8,
346
- borderRadius: 4,
347
- },
348
- sectionTitle: {
349
- fontFamily: AppFonts.interBold,
350
- fontSize: 13,
351
- color: AppColors.primaryBlack,
352
- },
353
- sectionActions: {
354
- flexDirection: 'row',
355
- alignItems: 'center',
356
- gap: 6,
357
- },
358
- countBadge: {
359
- paddingHorizontal: 7,
360
- paddingVertical: 2,
361
- borderRadius: 8,
362
- borderWidth: 1,
363
- },
364
- countBadgeText: {
365
- fontFamily: AppFonts.interBold,
366
- fontSize: 10,
367
- },
368
- paramList: {
369
- gap: 8,
370
- },
371
- emptyParams: {
372
- marginVertical: 12,
373
- paddingVertical: 18,
374
- paddingHorizontal: 14,
375
- borderRadius: 10,
376
- borderWidth: 1,
377
- borderColor: AppColors.grayBorderSecondary,
378
- backgroundColor: AppColors.primaryLight,
379
- alignItems: 'center',
380
- justifyContent: 'center',
381
- },
382
- emptyParamsText: {
383
- fontFamily: AppFonts.interMedium,
384
- fontSize: 13,
385
- color: AppColors.grayTextWeak,
386
- textAlign: 'center',
387
- lineHeight: 18,
388
- },
389
- dataBox: {
390
- backgroundColor: '#FFFFFF',
391
- borderRadius: 8,
392
- borderWidth: 1,
393
- borderColor: '#E5E7EB',
394
- padding: 12,
395
- },
396
- dataBoxHeader: {
397
- flexDirection: 'row',
398
- justifyContent: 'space-between',
399
- alignItems: 'center',
400
- marginBottom: 8,
401
- },
402
- dataBoxKey: {
403
- fontFamily: AppFonts.interBold,
404
- fontWeight: 'bold',
405
- fontSize: 13,
406
- color: AppColors.primaryBlack,
407
- flex: 1,
408
- paddingRight: 8,
409
- },
410
- dataBoxVal: {
411
- fontFamily: AppFonts.interRegular,
412
- fontSize: 13,
413
- color: AppColors.primaryBlack,
414
- lineHeight: 18,
415
- },
416
- showMoreBtn: {
417
- marginTop: 8,
418
- alignSelf: 'flex-start',
419
- backgroundColor: '#F3F4F6',
420
- paddingHorizontal: 10,
421
- paddingVertical: 4,
422
- borderRadius: 4,
423
- },
424
- showMoreText: {
425
- fontFamily: AppFonts.interMedium,
426
- fontSize: 11,
427
- color: '#4B5563', // matching previous UI color
428
- },
429
- jsonBlock: {
430
- padding: 12,
431
- borderWidth: 1,
432
- borderColor: '#E0E7FF',
433
- borderRadius: 8,
434
- backgroundColor: '#EEF2FF',
435
- },
436
- metricsScroll: {
437
- height: 92,
438
- overflow: 'visible',
439
- },
440
- metricsGrid: {
441
- gap: 8,
442
- marginTop: 4,
443
- paddingHorizontal: 16,
444
- paddingBottom: 14,
445
- },
446
- metricCard: {
447
- backgroundColor: '#FFFFFF',
448
- paddingVertical: 8,
449
- paddingHorizontal: 10,
450
- borderRadius: 6,
451
- borderLeftWidth: 3,
452
- width: 130, // fixed width for horizontal scrolling
453
- shadowColor: '#000',
454
- shadowOpacity: 0.04,
455
- shadowRadius: 4,
456
- shadowOffset: { width: 0, height: 2 },
457
- elevation: 2,
458
- alignItems: 'flex-start',
459
- },
460
- metricLabel: {
461
- fontFamily: AppFonts.interMedium,
462
- fontSize: 10,
463
- color: AppColors.grayTextWeak,
464
- textTransform: 'uppercase',
465
- marginBottom: 4,
466
- },
467
- metricValue: {
468
- fontFamily: AppFonts.interBold,
469
- fontSize: 13,
470
- },
471
- tabRow: {
472
- flexDirection: 'row',
131
+ jsonBlockContainer: {
473
132
  marginHorizontal: 16,
474
- marginTop: 6,
475
- backgroundColor: '#E5E7EB',
476
- borderRadius: 8,
477
- padding: 4,
478
- },
479
- tabButton: {
480
- flex: 1,
481
- flexDirection: 'row',
482
- alignItems: 'center',
483
- justifyContent: 'center',
484
- paddingVertical: 8,
485
- borderRadius: 6,
486
- gap: 6,
487
- },
488
- tabActive: {
489
- backgroundColor: '#FFFFFF',
490
- shadowColor: '#000',
491
- shadowOpacity: 0.1,
492
- shadowRadius: 2,
493
- shadowOffset: { width: 0, height: 1 },
494
- elevation: 2,
495
- },
496
- tabText: {
497
- fontFamily: AppFonts.interMedium,
498
- fontSize: 13,
499
- color: AppColors.grayTextWeak,
500
- },
501
- tabTextActive: {
502
- fontFamily: AppFonts.interBold,
503
- color: AppColors.primaryBlack,
504
- },
505
- tabBadge: {
506
- backgroundColor: '#D1D5DB',
507
- paddingHorizontal: 6,
508
- paddingVertical: 2,
509
- borderRadius: 10,
510
- },
511
- tabBadgeActive: {
512
- backgroundColor: AppColors.primaryLight,
133
+ marginTop: 16,
134
+ marginBottom: 16,
513
135
  borderWidth: 1,
514
- borderColor: '#E5E7EB',
515
- },
516
- tabBadgeText: {
517
- fontFamily: AppFonts.interBold,
518
- fontSize: 10,
519
- color: '#6B7280',
520
- },
521
- tabBadgeTextActive: {
522
- color: AppColors.primaryBlack,
136
+ borderRadius: 12,
523
137
  },
524
138
  });
525
139
  export default AnalyticsDetail;
@@ -136,34 +136,34 @@ const AnalyticsEventCard = React.memo(function AnalyticsEventCard({ event, onPre
136
136
  {userPropCount > 0 && (<View style={[cardStyles.chip, { backgroundColor: AppColors.grayBackground, borderColor: AppColors.grayBorderSecondary }]}>
137
137
  <Text style={[cardStyles.chipText, { color: AppColors.grayText }]}>★ {userPropCount} props</Text>
138
138
  </View>)}
139
- </View>
140
139
 
141
- {/* Inline Mini Graph for E-commerce Items */}
142
- {(() => {
140
+ {(() => {
143
141
  const items = event.params?.items;
144
- if (!Array.isArray(items) || items.length === 0)
145
- return null;
146
- const prices = items
147
- .map((item) => parseFloat(item.price || item.value))
148
- .filter(p => !isNaN(p));
149
- if (prices.length === 0)
150
- return null;
151
- const max = Math.max(...prices);
152
- if (max === 0)
153
- return null;
154
- return (<View style={cardStyles.miniGraphWrapper}>
155
- {prices.map((val, idx) => {
156
- const hPct = (val / max) * 100;
157
- return (<View key={idx} style={[
158
- cardStyles.miniGraphBar,
159
- {
160
- height: `${Math.max(15, hPct)}%`,
161
- backgroundColor: color,
162
- },
163
- ]}/>);
164
- })}
165
- </View>);
142
+ if (Array.isArray(items) && items.length > 0) {
143
+ return (<View style={[cardStyles.chip, { backgroundColor: '#FEF3C7', borderColor: '#FDE68A' }]}>
144
+ <Text style={[cardStyles.chipText, { color: '#D97706', fontFamily: AppFonts.interBold }]}>
145
+ 🛒 {items.length} {items.length === 1 ? 'item' : 'items'}
146
+ </Text>
147
+ </View>);
148
+ }
149
+ return null;
150
+ })()}
151
+
152
+ {(() => {
153
+ const val = event.params?.value ?? event.params?.price;
154
+ const currency = event.params?.currency ?? '';
155
+ const isPrimitive = typeof val === 'string' || typeof val === 'number';
156
+ if (isPrimitive) {
157
+ const currencyStr = typeof currency === 'string' || typeof currency === 'number' ? String(currency) : '';
158
+ return (<View style={[cardStyles.chip, { backgroundColor: '#ECFDF5', borderColor: '#A7F3D0' }]}>
159
+ <Text style={[cardStyles.chipText, { color: '#059669', fontFamily: AppFonts.interBold }]}>
160
+ 💰 {String(val)} {currencyStr}
161
+ </Text>
162
+ </View>);
163
+ }
164
+ return null;
166
165
  })()}
166
+ </View>
167
167
  </View>
168
168
  </TouchableScale>
169
169
  </View>);