react-native-inapp-inspector 1.1.24 → 1.1.26
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/build.gradle +36 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +114 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +28 -0
- package/dist/commonjs/components/AnalyticsEventCard.js +94 -88
- package/dist/commonjs/components/ConsoleLogCard.js +49 -25
- package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
- package/dist/commonjs/components/ErrorBoundary.js +9 -145
- package/dist/commonjs/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
- package/dist/commonjs/components/Inspector/AnalyticsFilterModal.js +1002 -0
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +637 -10
- package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -1
- package/dist/commonjs/components/Inspector/BundleTab.js +826 -406
- package/dist/commonjs/components/Inspector/CrashDetail.d.ts +3 -0
- package/dist/commonjs/components/Inspector/CrashDetail.js +796 -0
- package/dist/commonjs/components/Inspector/CrashFilterModal.d.ts +20 -0
- package/dist/commonjs/components/Inspector/CrashFilterModal.js +597 -0
- package/dist/commonjs/components/Inspector/CrashTab.d.ts +3 -0
- package/dist/commonjs/components/Inspector/CrashTab.js +727 -0
- package/dist/commonjs/components/Inspector/InspectorContext.js +6 -1
- package/dist/commonjs/components/Inspector/InspectorHeader.js +65 -28
- package/dist/commonjs/components/Inspector/LogDetail.js +227 -114
- package/dist/commonjs/components/Inspector/MainScreen.js +13 -4
- package/dist/commonjs/components/Inspector/NetworkDetail.js +131 -81
- package/dist/commonjs/components/Inspector/PerformanceTab.js +20 -10
- package/dist/commonjs/components/Inspector/SettingsPanel.js +90 -1
- package/dist/commonjs/components/Inspector/TabBar.js +15 -3
- package/dist/commonjs/components/NetworkIcons.d.ts +25 -0
- package/dist/commonjs/components/NetworkIcons.js +188 -1
- package/dist/commonjs/components/SegmentedTabs.js +33 -23
- package/dist/commonjs/components/TreeNode.js +13 -12
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/analyticsLogger.js +70 -44
- package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +5 -1
- package/dist/commonjs/customHooks/bundleAnalyzer.js +497 -126
- package/dist/commonjs/customHooks/consoleLogger.d.ts +1 -0
- package/dist/commonjs/customHooks/consoleLogger.js +153 -51
- package/dist/commonjs/customHooks/crashHandler.d.ts +64 -0
- package/dist/commonjs/customHooks/crashHandler.js +681 -0
- package/dist/commonjs/customHooks/networkLogger.js +74 -49
- package/dist/commonjs/customHooks/performanceTracker.d.ts +8 -0
- package/dist/commonjs/customHooks/performanceTracker.js +44 -3
- package/dist/commonjs/decorators/index.d.ts +49 -0
- package/dist/commonjs/decorators/index.js +142 -0
- package/dist/commonjs/helpers/gaAnalyticsRegistry.d.ts +18 -0
- package/dist/commonjs/helpers/gaAnalyticsRegistry.js +174 -0
- package/dist/commonjs/helpers/index.d.ts +7 -4
- package/dist/commonjs/helpers/index.js +102 -64
- package/dist/commonjs/i18n/locales/en.json +182 -4
- package/dist/commonjs/index.d.ts +6 -0
- package/dist/commonjs/index.js +310 -8
- package/dist/commonjs/styles/index.d.ts +4 -1
- package/dist/commonjs/styles/index.js +22 -19
- package/dist/commonjs/types/enums.d.ts +156 -9
- package/dist/commonjs/types/enums.js +139 -2
- package/dist/commonjs/types/interfaces.d.ts +70 -1
- package/dist/esm/components/AnalyticsEventCard.js +95 -89
- package/dist/esm/components/ConsoleLogCard.js +49 -25
- package/dist/esm/components/ErrorBoundary.d.ts +1 -1
- package/dist/esm/components/ErrorBoundary.js +10 -113
- package/dist/esm/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
- package/dist/esm/components/Inspector/AnalyticsFilterModal.js +964 -0
- package/dist/esm/components/Inspector/AnalyticsTab.js +639 -12
- package/dist/esm/components/Inspector/BundleTab.d.ts +1 -1
- package/dist/esm/components/Inspector/BundleTab.js +827 -407
- package/dist/esm/components/Inspector/CrashDetail.d.ts +3 -0
- package/dist/esm/components/Inspector/CrashDetail.js +758 -0
- package/dist/esm/components/Inspector/CrashFilterModal.d.ts +20 -0
- package/dist/esm/components/Inspector/CrashFilterModal.js +557 -0
- package/dist/esm/components/Inspector/CrashTab.d.ts +3 -0
- package/dist/esm/components/Inspector/CrashTab.js +689 -0
- package/dist/esm/components/Inspector/InspectorContext.js +1 -0
- package/dist/esm/components/Inspector/InspectorHeader.js +66 -29
- package/dist/esm/components/Inspector/LogDetail.js +227 -114
- package/dist/esm/components/Inspector/MainScreen.js +14 -5
- package/dist/esm/components/Inspector/NetworkDetail.js +132 -49
- package/dist/esm/components/Inspector/PerformanceTab.js +20 -10
- package/dist/esm/components/Inspector/SettingsPanel.js +91 -2
- package/dist/esm/components/Inspector/TabBar.js +16 -4
- package/dist/esm/components/NetworkIcons.d.ts +25 -0
- package/dist/esm/components/NetworkIcons.js +161 -0
- package/dist/esm/components/SegmentedTabs.js +33 -23
- package/dist/esm/components/TreeNode.js +13 -12
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/analyticsLogger.js +70 -44
- package/dist/esm/customHooks/bundleAnalyzer.d.ts +5 -1
- package/dist/esm/customHooks/bundleAnalyzer.js +493 -125
- package/dist/esm/customHooks/consoleLogger.d.ts +1 -0
- package/dist/esm/customHooks/consoleLogger.js +151 -50
- package/dist/esm/customHooks/crashHandler.d.ts +64 -0
- package/dist/esm/customHooks/crashHandler.js +659 -0
- package/dist/esm/customHooks/networkLogger.js +74 -49
- package/dist/esm/customHooks/performanceTracker.d.ts +8 -0
- package/dist/esm/customHooks/performanceTracker.js +38 -1
- package/dist/esm/decorators/index.d.ts +49 -0
- package/dist/esm/decorators/index.js +137 -0
- package/dist/esm/helpers/gaAnalyticsRegistry.d.ts +18 -0
- package/dist/esm/helpers/gaAnalyticsRegistry.js +169 -0
- package/dist/esm/helpers/index.d.ts +7 -4
- package/dist/esm/helpers/index.js +100 -63
- package/dist/esm/i18n/locales/en.json +182 -4
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +262 -8
- package/dist/esm/styles/index.d.ts +4 -1
- package/dist/esm/styles/index.js +22 -19
- package/dist/esm/types/enums.d.ts +156 -9
- package/dist/esm/types/enums.js +138 -3
- package/dist/esm/types/interfaces.d.ts +70 -1
- package/ios/NetworkInspectorModule.h +6 -0
- package/ios/NetworkInspectorModule.m +125 -0
- package/package.json +5 -2
- package/react-native-inapp-inspector.podspec +18 -0
|
@@ -0,0 +1,964 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { Modal, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View, } from 'react-native';
|
|
3
|
+
import { useInspector } from './InspectorContext';
|
|
4
|
+
import { AppColors } from '../../styles/AppColors';
|
|
5
|
+
import { AppFonts } from '../../styles/AppFonts';
|
|
6
|
+
import { getEventCategory } from '../../helpers';
|
|
7
|
+
import { FilterIcon, CloseWhite, CheckIcon, CartIcon, GlobeIcon, BoltIcon, SparkleIcon, MoneyIcon, ClockIcon, HourglassIcon, FlameIcon, PinIcon, RepeatIcon, TargetGoalIcon, UserIcon, UserCheckIcon, PackageBoxIcon, CodeBracketsIcon, } from '../NetworkIcons';
|
|
8
|
+
import TouchableScale from '../TouchableScale';
|
|
9
|
+
const DEFAULT_FILTERS = {
|
|
10
|
+
categories: new Set(['all']),
|
|
11
|
+
screens: new Set(),
|
|
12
|
+
sources: new Set(['all']),
|
|
13
|
+
userTypes: new Set(['all']),
|
|
14
|
+
timeWindow: 'all',
|
|
15
|
+
payloadComplexity: 'all',
|
|
16
|
+
hasRevenue: false,
|
|
17
|
+
hasItems: false,
|
|
18
|
+
hasUserProps: false,
|
|
19
|
+
hasParams: false,
|
|
20
|
+
onlyDuplicates: false,
|
|
21
|
+
onlyConversions: false,
|
|
22
|
+
sortBy: 'time_desc',
|
|
23
|
+
};
|
|
24
|
+
const AnalyticsFilterModal = ({ visible, onClose, }) => {
|
|
25
|
+
const { analyticsEvents, analyticsSearch, analyticsFilters, setAnalyticsFilters, } = useInspector();
|
|
26
|
+
// Staged Draft State: applied ONLY on tapping "Apply Filters"
|
|
27
|
+
const [draft, setDraft] = useState(() => ({
|
|
28
|
+
...analyticsFilters,
|
|
29
|
+
categories: new Set(analyticsFilters.categories),
|
|
30
|
+
screens: new Set(analyticsFilters.screens),
|
|
31
|
+
sources: new Set(analyticsFilters.sources || ['all']),
|
|
32
|
+
userTypes: new Set(analyticsFilters.userTypes || ['all']),
|
|
33
|
+
}));
|
|
34
|
+
// Re-sync draft whenever modal opens
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (visible) {
|
|
37
|
+
setDraft({
|
|
38
|
+
...analyticsFilters,
|
|
39
|
+
categories: new Set(analyticsFilters.categories),
|
|
40
|
+
screens: new Set(analyticsFilters.screens),
|
|
41
|
+
sources: new Set(analyticsFilters.sources || ['all']),
|
|
42
|
+
userTypes: new Set(analyticsFilters.userTypes || ['all']),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}, [visible, analyticsFilters]);
|
|
46
|
+
// Extract unique screen names from logged events
|
|
47
|
+
const uniqueScreens = useMemo(() => {
|
|
48
|
+
const screens = new Set();
|
|
49
|
+
for (const e of analyticsEvents) {
|
|
50
|
+
const scr = e.screenName ||
|
|
51
|
+
e.params?.firebase_screen ||
|
|
52
|
+
e.params?.screen_name ||
|
|
53
|
+
e.params?.firebase_screen_class ||
|
|
54
|
+
e.screenClass;
|
|
55
|
+
if (scr && typeof scr === 'string' && scr.trim() !== '') {
|
|
56
|
+
screens.add(scr.trim());
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return Array.from(screens);
|
|
60
|
+
}, [analyticsEvents]);
|
|
61
|
+
// Toggle Category in Draft
|
|
62
|
+
const toggleCategory = (cat) => {
|
|
63
|
+
setDraft(prev => {
|
|
64
|
+
const next = new Set(prev.categories);
|
|
65
|
+
if (cat === 'all') {
|
|
66
|
+
return { ...prev, categories: new Set(['all']) };
|
|
67
|
+
}
|
|
68
|
+
next.delete('all');
|
|
69
|
+
if (next.has(cat)) {
|
|
70
|
+
next.delete(cat);
|
|
71
|
+
if (next.size === 0)
|
|
72
|
+
next.add('all');
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
next.add(cat);
|
|
76
|
+
}
|
|
77
|
+
return { ...prev, categories: next };
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
// Toggle Screen in Draft
|
|
81
|
+
const toggleScreen = (scr) => {
|
|
82
|
+
setDraft(prev => {
|
|
83
|
+
const next = new Set(prev.screens);
|
|
84
|
+
if (next.has(scr)) {
|
|
85
|
+
next.delete(scr);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
next.add(scr);
|
|
89
|
+
}
|
|
90
|
+
return { ...prev, screens: next };
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
// Toggle Source in Draft
|
|
94
|
+
const toggleSource = (src) => {
|
|
95
|
+
setDraft(prev => {
|
|
96
|
+
const next = new Set(prev.sources);
|
|
97
|
+
if (src === 'all') {
|
|
98
|
+
return { ...prev, sources: new Set(['all']) };
|
|
99
|
+
}
|
|
100
|
+
next.delete('all');
|
|
101
|
+
if (next.has(src)) {
|
|
102
|
+
next.delete(src);
|
|
103
|
+
if (next.size === 0)
|
|
104
|
+
next.add('all');
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
next.add(src);
|
|
108
|
+
}
|
|
109
|
+
return { ...prev, sources: next };
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
// Toggle User Type in Draft
|
|
113
|
+
const toggleUserType = (ut) => {
|
|
114
|
+
setDraft(prev => {
|
|
115
|
+
const next = new Set(prev.userTypes);
|
|
116
|
+
if (ut === 'all') {
|
|
117
|
+
return { ...prev, userTypes: new Set(['all']) };
|
|
118
|
+
}
|
|
119
|
+
next.delete('all');
|
|
120
|
+
if (next.has(ut)) {
|
|
121
|
+
next.delete(ut);
|
|
122
|
+
if (next.size === 0)
|
|
123
|
+
next.add('all');
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
next.add(ut);
|
|
127
|
+
}
|
|
128
|
+
return { ...prev, userTypes: next };
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
// Toggle Boolean Flag in Draft
|
|
132
|
+
const toggleFlag = (key) => {
|
|
133
|
+
setDraft(prev => ({
|
|
134
|
+
...prev,
|
|
135
|
+
[key]: !prev[key],
|
|
136
|
+
}));
|
|
137
|
+
};
|
|
138
|
+
// Set Time Window in Draft
|
|
139
|
+
const setTimeWindow = (tw) => {
|
|
140
|
+
setDraft(prev => ({ ...prev, timeWindow: tw }));
|
|
141
|
+
};
|
|
142
|
+
// Set Payload Complexity in Draft
|
|
143
|
+
const setPayloadComplexity = (pc) => {
|
|
144
|
+
setDraft(prev => ({ ...prev, payloadComplexity: pc }));
|
|
145
|
+
};
|
|
146
|
+
// Set Sort in Draft
|
|
147
|
+
const setSort = (sortBy) => {
|
|
148
|
+
setDraft(prev => ({ ...prev, sortBy }));
|
|
149
|
+
};
|
|
150
|
+
// Reset Draft to default
|
|
151
|
+
const handleResetDraft = () => {
|
|
152
|
+
setDraft({
|
|
153
|
+
categories: new Set(['all']),
|
|
154
|
+
screens: new Set(),
|
|
155
|
+
sources: new Set(['all']),
|
|
156
|
+
userTypes: new Set(['all']),
|
|
157
|
+
timeWindow: 'all',
|
|
158
|
+
payloadComplexity: 'all',
|
|
159
|
+
hasRevenue: false,
|
|
160
|
+
hasItems: false,
|
|
161
|
+
hasUserProps: false,
|
|
162
|
+
hasParams: false,
|
|
163
|
+
onlyDuplicates: false,
|
|
164
|
+
onlyConversions: false,
|
|
165
|
+
sortBy: 'time_desc',
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
// Commit Draft to actual filters
|
|
169
|
+
const handleApply = () => {
|
|
170
|
+
setAnalyticsFilters({
|
|
171
|
+
...draft,
|
|
172
|
+
categories: new Set(draft.categories),
|
|
173
|
+
screens: new Set(draft.screens),
|
|
174
|
+
sources: new Set(draft.sources),
|
|
175
|
+
userTypes: new Set(draft.userTypes),
|
|
176
|
+
});
|
|
177
|
+
onClose();
|
|
178
|
+
};
|
|
179
|
+
// Discard changes & close
|
|
180
|
+
const handleDiscard = () => {
|
|
181
|
+
setDraft({
|
|
182
|
+
...analyticsFilters,
|
|
183
|
+
categories: new Set(analyticsFilters.categories),
|
|
184
|
+
screens: new Set(analyticsFilters.screens),
|
|
185
|
+
sources: new Set(analyticsFilters.sources || ['all']),
|
|
186
|
+
userTypes: new Set(analyticsFilters.userTypes || ['all']),
|
|
187
|
+
});
|
|
188
|
+
onClose();
|
|
189
|
+
};
|
|
190
|
+
// Calculate matching count for Draft in real-time
|
|
191
|
+
const draftMatchCount = useMemo(() => {
|
|
192
|
+
let events = analyticsEvents;
|
|
193
|
+
if (analyticsSearch) {
|
|
194
|
+
const s = analyticsSearch.toLowerCase();
|
|
195
|
+
events = events.filter(e => e.name.toLowerCase().includes(s) ||
|
|
196
|
+
JSON.stringify(e.params || {}).toLowerCase().includes(s) ||
|
|
197
|
+
(e.screenName ?? '').toLowerCase().includes(s) ||
|
|
198
|
+
(e.pageTitle ?? '').toLowerCase().includes(s));
|
|
199
|
+
}
|
|
200
|
+
if (draft.categories.size > 0 && !draft.categories.has('all')) {
|
|
201
|
+
events = events.filter(e => {
|
|
202
|
+
const cat = getEventCategory(e.name);
|
|
203
|
+
return draft.categories.has(cat);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
if (draft.timeWindow !== 'all') {
|
|
207
|
+
const now = Date.now();
|
|
208
|
+
const cutoff = draft.timeWindow === '1m'
|
|
209
|
+
? now - 60 * 1000
|
|
210
|
+
: draft.timeWindow === '5m'
|
|
211
|
+
? now - 5 * 60 * 1000
|
|
212
|
+
: draft.timeWindow === '15m'
|
|
213
|
+
? now - 15 * 60 * 1000
|
|
214
|
+
: now - 60 * 60 * 1000;
|
|
215
|
+
events = events.filter(e => e.timestamp >= cutoff);
|
|
216
|
+
}
|
|
217
|
+
if (draft.sources.size > 0 && !draft.sources.has('all')) {
|
|
218
|
+
events = events.filter(e => draft.sources.has(e.source || 'manual'));
|
|
219
|
+
}
|
|
220
|
+
if (draft.userTypes.size > 0 && !draft.userTypes.has('all')) {
|
|
221
|
+
events = events.filter(e => {
|
|
222
|
+
const isIdentified = Boolean(e.userId && e.userId.trim() !== '');
|
|
223
|
+
if (draft.userTypes.has('identified') && isIdentified)
|
|
224
|
+
return true;
|
|
225
|
+
if (draft.userTypes.has('anonymous') && !isIdentified)
|
|
226
|
+
return true;
|
|
227
|
+
return false;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
if (draft.payloadComplexity !== 'all') {
|
|
231
|
+
events = events.filter(e => {
|
|
232
|
+
const count = e.params ? Object.keys(e.params).length : 0;
|
|
233
|
+
if (draft.payloadComplexity === 'none')
|
|
234
|
+
return count === 0;
|
|
235
|
+
if (draft.payloadComplexity === 'simple')
|
|
236
|
+
return count >= 1 && count <= 5;
|
|
237
|
+
if (draft.payloadComplexity === 'heavy')
|
|
238
|
+
return count > 5;
|
|
239
|
+
return true;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if (draft.onlyConversions) {
|
|
243
|
+
const CONVERSION_PATTERNS = [
|
|
244
|
+
'purchase',
|
|
245
|
+
'item_purchase',
|
|
246
|
+
'ecommerce_purchase',
|
|
247
|
+
'sign_up',
|
|
248
|
+
'login',
|
|
249
|
+
'lead',
|
|
250
|
+
'generate_lead',
|
|
251
|
+
'tutorial_complete',
|
|
252
|
+
'add_payment_info',
|
|
253
|
+
'begin_checkout',
|
|
254
|
+
];
|
|
255
|
+
events = events.filter(e => CONVERSION_PATTERNS.some(pat => e.name.toLowerCase().includes(pat)));
|
|
256
|
+
}
|
|
257
|
+
if (draft.screens.size > 0) {
|
|
258
|
+
events = events.filter(e => {
|
|
259
|
+
const scr = e.screenName ||
|
|
260
|
+
e.params?.firebase_screen ||
|
|
261
|
+
e.params?.screen_name ||
|
|
262
|
+
e.params?.firebase_screen_class ||
|
|
263
|
+
e.screenClass ||
|
|
264
|
+
'';
|
|
265
|
+
return draft.screens.has(scr);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (draft.hasRevenue) {
|
|
269
|
+
events = events.filter(e => {
|
|
270
|
+
const val = e.params?.value ?? e.params?.price;
|
|
271
|
+
return val !== undefined && val !== null && Number(val) > 0;
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
if (draft.hasItems) {
|
|
275
|
+
events = events.filter(e => Array.isArray(e.params?.items) && e.params.items.length > 0);
|
|
276
|
+
}
|
|
277
|
+
if (draft.hasUserProps) {
|
|
278
|
+
events = events.filter(e => e.userProperties && Object.keys(e.userProperties).length > 0);
|
|
279
|
+
}
|
|
280
|
+
if (draft.hasParams) {
|
|
281
|
+
events = events.filter(e => e.params && Object.keys(e.params).length > 0);
|
|
282
|
+
}
|
|
283
|
+
return events.length;
|
|
284
|
+
}, [analyticsEvents, analyticsSearch, draft]);
|
|
285
|
+
const isAllCategories = draft.categories.has('all') || draft.categories.size === 0;
|
|
286
|
+
const isAllSources = draft.sources.has('all') || draft.sources.size === 0;
|
|
287
|
+
const isAllUserTypes = draft.userTypes.has('all') || draft.userTypes.size === 0;
|
|
288
|
+
return (<Modal visible={visible} transparent animationType="fade" onRequestClose={handleDiscard}>
|
|
289
|
+
<View style={styles.backdrop}>
|
|
290
|
+
<Pressable style={StyleSheet.absoluteFill} onPress={handleDiscard}/>
|
|
291
|
+
|
|
292
|
+
<View style={styles.modalCard}>
|
|
293
|
+
{/* ─── Header ──────────────────────────────────────────────────────── */}
|
|
294
|
+
<View style={styles.header}>
|
|
295
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
296
|
+
<View style={styles.headerIconBox}>
|
|
297
|
+
<FilterIcon color={AppColors.brandPurple} size={15}/>
|
|
298
|
+
</View>
|
|
299
|
+
<View>
|
|
300
|
+
<Text style={styles.headerTitle}>Analytics Filters</Text>
|
|
301
|
+
<Text style={styles.headerSubtitle}>
|
|
302
|
+
GA4 Telemetry Dimensions & Metrics
|
|
303
|
+
</Text>
|
|
304
|
+
</View>
|
|
305
|
+
</View>
|
|
306
|
+
|
|
307
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
308
|
+
<TouchableOpacity onPress={handleResetDraft} hitSlop={8} style={styles.resetBtn}>
|
|
309
|
+
<Text style={styles.resetBtnText}>Reset All</Text>
|
|
310
|
+
</TouchableOpacity>
|
|
311
|
+
|
|
312
|
+
<TouchableOpacity onPress={handleDiscard} hitSlop={10} style={styles.closeBtn}>
|
|
313
|
+
<CloseWhite />
|
|
314
|
+
</TouchableOpacity>
|
|
315
|
+
</View>
|
|
316
|
+
</View>
|
|
317
|
+
|
|
318
|
+
{/* ─── Scrollable Filter Options ──────────────────────────────────── */}
|
|
319
|
+
<ScrollView style={{ maxHeight: 460 }} contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={true}>
|
|
320
|
+
{/* 1. Time Horizon Window */}
|
|
321
|
+
<View style={styles.section}>
|
|
322
|
+
<View style={styles.sectionHeaderRow}>
|
|
323
|
+
<ClockIcon color={AppColors.grayTextWeak} size={12}/>
|
|
324
|
+
<Text style={styles.sectionTitle}>TIME HORIZON</Text>
|
|
325
|
+
</View>
|
|
326
|
+
<View style={styles.chipsWrap}>
|
|
327
|
+
{[
|
|
328
|
+
{ key: 'all', label: 'All Time' },
|
|
329
|
+
{ key: '1m', label: 'Last 1 min' },
|
|
330
|
+
{ key: '5m', label: 'Last 5 mins' },
|
|
331
|
+
{ key: '15m', label: 'Last 15 mins' },
|
|
332
|
+
{ key: '1h', label: 'Last 1 hour' },
|
|
333
|
+
].map(item => (<TouchableScale key={item.key} onPress={() => setTimeWindow(item.key)} style={[
|
|
334
|
+
styles.filterPill,
|
|
335
|
+
draft.timeWindow === item.key &&
|
|
336
|
+
styles.filterPillActive,
|
|
337
|
+
]}>
|
|
338
|
+
<Text style={[
|
|
339
|
+
styles.filterPillText,
|
|
340
|
+
draft.timeWindow === item.key &&
|
|
341
|
+
styles.filterPillTextActive,
|
|
342
|
+
]}>
|
|
343
|
+
{item.label}
|
|
344
|
+
</Text>
|
|
345
|
+
</TouchableScale>))}
|
|
346
|
+
</View>
|
|
347
|
+
</View>
|
|
348
|
+
|
|
349
|
+
{/* 2. Category Dimensions */}
|
|
350
|
+
<View style={styles.section}>
|
|
351
|
+
<View style={styles.sectionHeaderRow}>
|
|
352
|
+
<TargetGoalIcon color={AppColors.grayTextWeak} size={12}/>
|
|
353
|
+
<Text style={styles.sectionTitle}>EVENT CATEGORIES</Text>
|
|
354
|
+
</View>
|
|
355
|
+
<View style={styles.chipsWrap}>
|
|
356
|
+
<TouchableScale onPress={() => toggleCategory('all')} style={[
|
|
357
|
+
styles.filterPill,
|
|
358
|
+
isAllCategories && styles.filterPillActive,
|
|
359
|
+
]}>
|
|
360
|
+
{isAllCategories && (<CheckIcon color={AppColors.white} size={12}/>)}
|
|
361
|
+
<Text style={[
|
|
362
|
+
styles.filterPillText,
|
|
363
|
+
isAllCategories && styles.filterPillTextActive,
|
|
364
|
+
]}>
|
|
365
|
+
All Categories
|
|
366
|
+
</Text>
|
|
367
|
+
</TouchableScale>
|
|
368
|
+
|
|
369
|
+
<TouchableScale onPress={() => toggleCategory('ecommerce')} style={[
|
|
370
|
+
styles.filterPill,
|
|
371
|
+
draft.categories.has('ecommerce') &&
|
|
372
|
+
styles.filterPillActive,
|
|
373
|
+
]}>
|
|
374
|
+
<CartIcon color={draft.categories.has('ecommerce')
|
|
375
|
+
? AppColors.white
|
|
376
|
+
: AppColors.amber700} size={13}/>
|
|
377
|
+
<Text style={[
|
|
378
|
+
styles.filterPillText,
|
|
379
|
+
draft.categories.has('ecommerce') &&
|
|
380
|
+
styles.filterPillTextActive,
|
|
381
|
+
]}>
|
|
382
|
+
Ecommerce
|
|
383
|
+
</Text>
|
|
384
|
+
</TouchableScale>
|
|
385
|
+
|
|
386
|
+
<TouchableScale onPress={() => toggleCategory('page_view')} style={[
|
|
387
|
+
styles.filterPill,
|
|
388
|
+
draft.categories.has('page_view') &&
|
|
389
|
+
styles.filterPillActive,
|
|
390
|
+
]}>
|
|
391
|
+
<GlobeIcon color={draft.categories.has('page_view')
|
|
392
|
+
? AppColors.white
|
|
393
|
+
: AppColors.sky600} size={13}/>
|
|
394
|
+
<Text style={[
|
|
395
|
+
styles.filterPillText,
|
|
396
|
+
draft.categories.has('page_view') &&
|
|
397
|
+
styles.filterPillTextActive,
|
|
398
|
+
]}>
|
|
399
|
+
Screens & Pages
|
|
400
|
+
</Text>
|
|
401
|
+
</TouchableScale>
|
|
402
|
+
|
|
403
|
+
<TouchableScale onPress={() => toggleCategory('system')} style={[
|
|
404
|
+
styles.filterPill,
|
|
405
|
+
draft.categories.has('system') &&
|
|
406
|
+
styles.filterPillActive,
|
|
407
|
+
]}>
|
|
408
|
+
<BoltIcon color={draft.categories.has('system')
|
|
409
|
+
? AppColors.white
|
|
410
|
+
: AppColors.purple} size={13}/>
|
|
411
|
+
<Text style={[
|
|
412
|
+
styles.filterPillText,
|
|
413
|
+
draft.categories.has('system') &&
|
|
414
|
+
styles.filterPillTextActive,
|
|
415
|
+
]}>
|
|
416
|
+
System & Lifecycle
|
|
417
|
+
</Text>
|
|
418
|
+
</TouchableScale>
|
|
419
|
+
|
|
420
|
+
<TouchableScale onPress={() => toggleCategory('custom')} style={[
|
|
421
|
+
styles.filterPill,
|
|
422
|
+
draft.categories.has('custom') &&
|
|
423
|
+
styles.filterPillActive,
|
|
424
|
+
]}>
|
|
425
|
+
<SparkleIcon color={draft.categories.has('custom')
|
|
426
|
+
? AppColors.white
|
|
427
|
+
: AppColors.brandPurple} size={13}/>
|
|
428
|
+
<Text style={[
|
|
429
|
+
styles.filterPillText,
|
|
430
|
+
draft.categories.has('custom') &&
|
|
431
|
+
styles.filterPillTextActive,
|
|
432
|
+
]}>
|
|
433
|
+
Custom Events
|
|
434
|
+
</Text>
|
|
435
|
+
</TouchableScale>
|
|
436
|
+
</View>
|
|
437
|
+
</View>
|
|
438
|
+
|
|
439
|
+
{/* 3. Goals, Monetization & Attributes */}
|
|
440
|
+
<View style={styles.section}>
|
|
441
|
+
<View style={styles.sectionHeaderRow}>
|
|
442
|
+
<MoneyIcon color={AppColors.grayTextWeak} size={12}/>
|
|
443
|
+
<Text style={styles.sectionTitle}>
|
|
444
|
+
MONETIZATION & ATTRIBUTES
|
|
445
|
+
</Text>
|
|
446
|
+
</View>
|
|
447
|
+
<View style={styles.chipsWrap}>
|
|
448
|
+
<TouchableScale onPress={() => toggleFlag('onlyConversions')} style={[
|
|
449
|
+
styles.filterPill,
|
|
450
|
+
draft.onlyConversions && styles.filterPillActive,
|
|
451
|
+
]}>
|
|
452
|
+
<TargetGoalIcon color={draft.onlyConversions
|
|
453
|
+
? AppColors.white
|
|
454
|
+
: AppColors.brandPurple} size={13}/>
|
|
455
|
+
<Text style={[
|
|
456
|
+
styles.filterPillText,
|
|
457
|
+
draft.onlyConversions && styles.filterPillTextActive,
|
|
458
|
+
]}>
|
|
459
|
+
Conversion Events Only
|
|
460
|
+
</Text>
|
|
461
|
+
</TouchableScale>
|
|
462
|
+
|
|
463
|
+
<TouchableScale onPress={() => toggleFlag('hasRevenue')} style={[
|
|
464
|
+
styles.filterPill,
|
|
465
|
+
draft.hasRevenue && styles.filterPillActive,
|
|
466
|
+
]}>
|
|
467
|
+
<MoneyIcon color={draft.hasRevenue
|
|
468
|
+
? AppColors.white
|
|
469
|
+
: AppColors.emerald600} size={13}/>
|
|
470
|
+
<Text style={[
|
|
471
|
+
styles.filterPillText,
|
|
472
|
+
draft.hasRevenue && styles.filterPillTextActive,
|
|
473
|
+
]}>
|
|
474
|
+
With Revenue (Value > 0)
|
|
475
|
+
</Text>
|
|
476
|
+
</TouchableScale>
|
|
477
|
+
|
|
478
|
+
<TouchableScale onPress={() => toggleFlag('hasItems')} style={[
|
|
479
|
+
styles.filterPill,
|
|
480
|
+
draft.hasItems && styles.filterPillActive,
|
|
481
|
+
]}>
|
|
482
|
+
<PackageBoxIcon color={draft.hasItems
|
|
483
|
+
? AppColors.white
|
|
484
|
+
: AppColors.amber700} size={13}/>
|
|
485
|
+
<Text style={[
|
|
486
|
+
styles.filterPillText,
|
|
487
|
+
draft.hasItems && styles.filterPillTextActive,
|
|
488
|
+
]}>
|
|
489
|
+
With Items Payload
|
|
490
|
+
</Text>
|
|
491
|
+
</TouchableScale>
|
|
492
|
+
|
|
493
|
+
<TouchableScale onPress={() => toggleFlag('hasUserProps')} style={[
|
|
494
|
+
styles.filterPill,
|
|
495
|
+
draft.hasUserProps && styles.filterPillActive,
|
|
496
|
+
]}>
|
|
497
|
+
<SparkleIcon color={draft.hasUserProps
|
|
498
|
+
? AppColors.white
|
|
499
|
+
: AppColors.brandPurple} size={13}/>
|
|
500
|
+
<Text style={[
|
|
501
|
+
styles.filterPillText,
|
|
502
|
+
draft.hasUserProps && styles.filterPillTextActive,
|
|
503
|
+
]}>
|
|
504
|
+
With User Properties
|
|
505
|
+
</Text>
|
|
506
|
+
</TouchableScale>
|
|
507
|
+
|
|
508
|
+
<TouchableScale onPress={() => toggleFlag('hasParams')} style={[
|
|
509
|
+
styles.filterPill,
|
|
510
|
+
draft.hasParams && styles.filterPillActive,
|
|
511
|
+
]}>
|
|
512
|
+
<CodeBracketsIcon color={draft.hasParams
|
|
513
|
+
? AppColors.white
|
|
514
|
+
: AppColors.grayText} size={13}/>
|
|
515
|
+
<Text style={[
|
|
516
|
+
styles.filterPillText,
|
|
517
|
+
draft.hasParams && styles.filterPillTextActive,
|
|
518
|
+
]}>
|
|
519
|
+
With Custom Params
|
|
520
|
+
</Text>
|
|
521
|
+
</TouchableScale>
|
|
522
|
+
|
|
523
|
+
<TouchableScale onPress={() => toggleFlag('onlyDuplicates')} style={[
|
|
524
|
+
styles.filterPill,
|
|
525
|
+
draft.onlyDuplicates && styles.filterPillActive,
|
|
526
|
+
]}>
|
|
527
|
+
<RepeatIcon color={draft.onlyDuplicates
|
|
528
|
+
? AppColors.white
|
|
529
|
+
: AppColors.rose600} size={13}/>
|
|
530
|
+
<Text style={[
|
|
531
|
+
styles.filterPillText,
|
|
532
|
+
draft.onlyDuplicates && styles.filterPillTextActive,
|
|
533
|
+
]}>
|
|
534
|
+
Frequent Only (> 1×)
|
|
535
|
+
</Text>
|
|
536
|
+
</TouchableScale>
|
|
537
|
+
</View>
|
|
538
|
+
</View>
|
|
539
|
+
|
|
540
|
+
{/* 4. User Identification */}
|
|
541
|
+
<View style={styles.section}>
|
|
542
|
+
<View style={styles.sectionHeaderRow}>
|
|
543
|
+
<UserIcon color={AppColors.grayTextWeak} size={12}/>
|
|
544
|
+
<Text style={styles.sectionTitle}>USER IDENTIFICATION</Text>
|
|
545
|
+
</View>
|
|
546
|
+
<View style={styles.chipsWrap}>
|
|
547
|
+
<TouchableScale onPress={() => toggleUserType('all')} style={[
|
|
548
|
+
styles.filterPill,
|
|
549
|
+
isAllUserTypes && styles.filterPillActive,
|
|
550
|
+
]}>
|
|
551
|
+
{isAllUserTypes && (<CheckIcon color={AppColors.white} size={12}/>)}
|
|
552
|
+
<Text style={[
|
|
553
|
+
styles.filterPillText,
|
|
554
|
+
isAllUserTypes && styles.filterPillTextActive,
|
|
555
|
+
]}>
|
|
556
|
+
All Users
|
|
557
|
+
</Text>
|
|
558
|
+
</TouchableScale>
|
|
559
|
+
|
|
560
|
+
<TouchableScale onPress={() => toggleUserType('identified')} style={[
|
|
561
|
+
styles.filterPill,
|
|
562
|
+
draft.userTypes.has('identified') &&
|
|
563
|
+
styles.filterPillActive,
|
|
564
|
+
]}>
|
|
565
|
+
<UserCheckIcon color={draft.userTypes.has('identified')
|
|
566
|
+
? AppColors.white
|
|
567
|
+
: AppColors.greenColor} size={13}/>
|
|
568
|
+
<Text style={[
|
|
569
|
+
styles.filterPillText,
|
|
570
|
+
draft.userTypes.has('identified') &&
|
|
571
|
+
styles.filterPillTextActive,
|
|
572
|
+
]}>
|
|
573
|
+
Identified (User ID)
|
|
574
|
+
</Text>
|
|
575
|
+
</TouchableScale>
|
|
576
|
+
|
|
577
|
+
<TouchableScale onPress={() => toggleUserType('anonymous')} style={[
|
|
578
|
+
styles.filterPill,
|
|
579
|
+
draft.userTypes.has('anonymous') &&
|
|
580
|
+
styles.filterPillActive,
|
|
581
|
+
]}>
|
|
582
|
+
<UserIcon color={draft.userTypes.has('anonymous')
|
|
583
|
+
? AppColors.white
|
|
584
|
+
: AppColors.grayTextWeak} size={13}/>
|
|
585
|
+
<Text style={[
|
|
586
|
+
styles.filterPillText,
|
|
587
|
+
draft.userTypes.has('anonymous') &&
|
|
588
|
+
styles.filterPillTextActive,
|
|
589
|
+
]}>
|
|
590
|
+
Anonymous
|
|
591
|
+
</Text>
|
|
592
|
+
</TouchableScale>
|
|
593
|
+
</View>
|
|
594
|
+
</View>
|
|
595
|
+
|
|
596
|
+
{/* 5. Source Logger SDK */}
|
|
597
|
+
<View style={styles.section}>
|
|
598
|
+
<View style={styles.sectionHeaderRow}>
|
|
599
|
+
<BoltIcon color={AppColors.grayTextWeak} size={12}/>
|
|
600
|
+
<Text style={styles.sectionTitle}>EVENT SOURCE</Text>
|
|
601
|
+
</View>
|
|
602
|
+
<View style={styles.chipsWrap}>
|
|
603
|
+
<TouchableScale onPress={() => toggleSource('all')} style={[
|
|
604
|
+
styles.filterPill,
|
|
605
|
+
isAllSources && styles.filterPillActive,
|
|
606
|
+
]}>
|
|
607
|
+
{isAllSources && (<CheckIcon color={AppColors.white} size={12}/>)}
|
|
608
|
+
<Text style={[
|
|
609
|
+
styles.filterPillText,
|
|
610
|
+
isAllSources && styles.filterPillTextActive,
|
|
611
|
+
]}>
|
|
612
|
+
All Sources
|
|
613
|
+
</Text>
|
|
614
|
+
</TouchableScale>
|
|
615
|
+
|
|
616
|
+
<TouchableScale onPress={() => toggleSource('firebase')} style={[
|
|
617
|
+
styles.filterPill,
|
|
618
|
+
draft.sources.has('firebase') &&
|
|
619
|
+
styles.filterPillActive,
|
|
620
|
+
]}>
|
|
621
|
+
<Text style={[
|
|
622
|
+
styles.filterPillText,
|
|
623
|
+
draft.sources.has('firebase') &&
|
|
624
|
+
styles.filterPillTextActive,
|
|
625
|
+
]}>
|
|
626
|
+
Firebase / GA SDK
|
|
627
|
+
</Text>
|
|
628
|
+
</TouchableScale>
|
|
629
|
+
|
|
630
|
+
<TouchableScale onPress={() => toggleSource('manual')} style={[
|
|
631
|
+
styles.filterPill,
|
|
632
|
+
draft.sources.has('manual') &&
|
|
633
|
+
styles.filterPillActive,
|
|
634
|
+
]}>
|
|
635
|
+
<Text style={[
|
|
636
|
+
styles.filterPillText,
|
|
637
|
+
draft.sources.has('manual') &&
|
|
638
|
+
styles.filterPillTextActive,
|
|
639
|
+
]}>
|
|
640
|
+
In-App Logger
|
|
641
|
+
</Text>
|
|
642
|
+
</TouchableScale>
|
|
643
|
+
</View>
|
|
644
|
+
</View>
|
|
645
|
+
|
|
646
|
+
{/* 6. Payload Complexity */}
|
|
647
|
+
<View style={styles.section}>
|
|
648
|
+
<View style={styles.sectionHeaderRow}>
|
|
649
|
+
<CodeBracketsIcon color={AppColors.grayTextWeak} size={12}/>
|
|
650
|
+
<Text style={styles.sectionTitle}>PAYLOAD COMPLEXITY</Text>
|
|
651
|
+
</View>
|
|
652
|
+
<View style={styles.chipsWrap}>
|
|
653
|
+
{[
|
|
654
|
+
{ key: 'all', label: 'All Payloads' },
|
|
655
|
+
{ key: 'none', label: '0 Params' },
|
|
656
|
+
{ key: 'simple', label: '1 - 5 Params' },
|
|
657
|
+
{ key: 'heavy', label: '> 5 Params' },
|
|
658
|
+
].map(item => (<TouchableScale key={item.key} onPress={() => setPayloadComplexity(item.key)} style={[
|
|
659
|
+
styles.filterPill,
|
|
660
|
+
draft.payloadComplexity === item.key &&
|
|
661
|
+
styles.filterPillActive,
|
|
662
|
+
]}>
|
|
663
|
+
<Text style={[
|
|
664
|
+
styles.filterPillText,
|
|
665
|
+
draft.payloadComplexity === item.key &&
|
|
666
|
+
styles.filterPillTextActive,
|
|
667
|
+
]}>
|
|
668
|
+
{item.label}
|
|
669
|
+
</Text>
|
|
670
|
+
</TouchableScale>))}
|
|
671
|
+
</View>
|
|
672
|
+
</View>
|
|
673
|
+
|
|
674
|
+
{/* 7. Discovered Screens (Dimensions) */}
|
|
675
|
+
{uniqueScreens.length > 0 && (<View style={styles.section}>
|
|
676
|
+
<View style={styles.sectionHeaderRow}>
|
|
677
|
+
<PinIcon color={AppColors.grayTextWeak} size={12}/>
|
|
678
|
+
<Text style={styles.sectionTitle}>
|
|
679
|
+
DISCOVERED SCREENS ({uniqueScreens.length})
|
|
680
|
+
</Text>
|
|
681
|
+
</View>
|
|
682
|
+
<View style={styles.chipsWrap}>
|
|
683
|
+
{uniqueScreens.map(scr => {
|
|
684
|
+
const isSelected = draft.screens.has(scr);
|
|
685
|
+
return (<TouchableScale key={scr} onPress={() => toggleScreen(scr)} style={[
|
|
686
|
+
styles.filterPill,
|
|
687
|
+
isSelected && styles.filterPillActive,
|
|
688
|
+
]}>
|
|
689
|
+
<PinIcon color={isSelected
|
|
690
|
+
? AppColors.white
|
|
691
|
+
: AppColors.amber700} size={12}/>
|
|
692
|
+
<Text style={[
|
|
693
|
+
styles.filterPillText,
|
|
694
|
+
isSelected && styles.filterPillTextActive,
|
|
695
|
+
]}>
|
|
696
|
+
{scr}
|
|
697
|
+
</Text>
|
|
698
|
+
</TouchableScale>);
|
|
699
|
+
})}
|
|
700
|
+
</View>
|
|
701
|
+
</View>)}
|
|
702
|
+
|
|
703
|
+
{/* 8. Sort Order */}
|
|
704
|
+
<View style={styles.section}>
|
|
705
|
+
<View style={styles.sectionHeaderRow}>
|
|
706
|
+
<ClockIcon color={AppColors.grayTextWeak} size={12}/>
|
|
707
|
+
<Text style={styles.sectionTitle}>SORT ORDER</Text>
|
|
708
|
+
</View>
|
|
709
|
+
<View style={styles.chipsWrap}>
|
|
710
|
+
<TouchableScale onPress={() => setSort('time_desc')} style={[
|
|
711
|
+
styles.filterPill,
|
|
712
|
+
draft.sortBy === 'time_desc' &&
|
|
713
|
+
styles.filterPillActive,
|
|
714
|
+
]}>
|
|
715
|
+
<ClockIcon color={draft.sortBy === 'time_desc'
|
|
716
|
+
? AppColors.white
|
|
717
|
+
: AppColors.grayText} size={12}/>
|
|
718
|
+
<Text style={[
|
|
719
|
+
styles.filterPillText,
|
|
720
|
+
draft.sortBy === 'time_desc' &&
|
|
721
|
+
styles.filterPillTextActive,
|
|
722
|
+
]}>
|
|
723
|
+
Newest First (Default)
|
|
724
|
+
</Text>
|
|
725
|
+
</TouchableScale>
|
|
726
|
+
|
|
727
|
+
<TouchableScale onPress={() => setSort('time_asc')} style={[
|
|
728
|
+
styles.filterPill,
|
|
729
|
+
draft.sortBy === 'time_asc' &&
|
|
730
|
+
styles.filterPillActive,
|
|
731
|
+
]}>
|
|
732
|
+
<HourglassIcon color={draft.sortBy === 'time_asc'
|
|
733
|
+
? AppColors.white
|
|
734
|
+
: AppColors.grayText} size={12}/>
|
|
735
|
+
<Text style={[
|
|
736
|
+
styles.filterPillText,
|
|
737
|
+
draft.sortBy === 'time_asc' &&
|
|
738
|
+
styles.filterPillTextActive,
|
|
739
|
+
]}>
|
|
740
|
+
Oldest First
|
|
741
|
+
</Text>
|
|
742
|
+
</TouchableScale>
|
|
743
|
+
|
|
744
|
+
<TouchableScale onPress={() => setSort('revenue_desc')} style={[
|
|
745
|
+
styles.filterPill,
|
|
746
|
+
draft.sortBy === 'revenue_desc' &&
|
|
747
|
+
styles.filterPillActive,
|
|
748
|
+
]}>
|
|
749
|
+
<MoneyIcon color={draft.sortBy === 'revenue_desc'
|
|
750
|
+
? AppColors.white
|
|
751
|
+
: AppColors.emerald600} size={12}/>
|
|
752
|
+
<Text style={[
|
|
753
|
+
styles.filterPillText,
|
|
754
|
+
draft.sortBy === 'revenue_desc' &&
|
|
755
|
+
styles.filterPillTextActive,
|
|
756
|
+
]}>
|
|
757
|
+
Highest Revenue
|
|
758
|
+
</Text>
|
|
759
|
+
</TouchableScale>
|
|
760
|
+
|
|
761
|
+
<TouchableScale onPress={() => setSort('count_desc')} style={[
|
|
762
|
+
styles.filterPill,
|
|
763
|
+
draft.sortBy === 'count_desc' &&
|
|
764
|
+
styles.filterPillActive,
|
|
765
|
+
]}>
|
|
766
|
+
<FlameIcon color={draft.sortBy === 'count_desc'
|
|
767
|
+
? AppColors.white
|
|
768
|
+
: AppColors.darkOrange} size={12}/>
|
|
769
|
+
<Text style={[
|
|
770
|
+
styles.filterPillText,
|
|
771
|
+
draft.sortBy === 'count_desc' &&
|
|
772
|
+
styles.filterPillTextActive,
|
|
773
|
+
]}>
|
|
774
|
+
Most Frequent
|
|
775
|
+
</Text>
|
|
776
|
+
</TouchableScale>
|
|
777
|
+
</View>
|
|
778
|
+
</View>
|
|
779
|
+
</ScrollView>
|
|
780
|
+
|
|
781
|
+
{/* ─── Footer Action Bar (Discard + Apply) ────────────────────────── */}
|
|
782
|
+
<View style={styles.footer}>
|
|
783
|
+
<TouchableOpacity onPress={handleDiscard} style={styles.discardBtn} activeOpacity={0.8}>
|
|
784
|
+
<Text style={styles.discardBtnText}>Discard</Text>
|
|
785
|
+
</TouchableOpacity>
|
|
786
|
+
|
|
787
|
+
<TouchableOpacity onPress={handleApply} style={styles.applyBtn} activeOpacity={0.85}>
|
|
788
|
+
<Text style={styles.applyBtnText}>
|
|
789
|
+
Apply ({draftMatchCount} Events)
|
|
790
|
+
</Text>
|
|
791
|
+
</TouchableOpacity>
|
|
792
|
+
</View>
|
|
793
|
+
</View>
|
|
794
|
+
</View>
|
|
795
|
+
</Modal>);
|
|
796
|
+
};
|
|
797
|
+
const styles = StyleSheet.create({
|
|
798
|
+
backdrop: {
|
|
799
|
+
flex: 1,
|
|
800
|
+
backgroundColor: `${AppColors.primaryBlack}85`,
|
|
801
|
+
justifyContent: 'center',
|
|
802
|
+
alignItems: 'center',
|
|
803
|
+
padding: 16,
|
|
804
|
+
},
|
|
805
|
+
modalCard: {
|
|
806
|
+
width: '100%',
|
|
807
|
+
maxWidth: 440,
|
|
808
|
+
backgroundColor: AppColors.white,
|
|
809
|
+
borderRadius: 14,
|
|
810
|
+
borderWidth: 1,
|
|
811
|
+
borderColor: AppColors.dividerColor,
|
|
812
|
+
shadowColor: AppColors.black,
|
|
813
|
+
shadowOpacity: 0.18,
|
|
814
|
+
shadowRadius: 12,
|
|
815
|
+
shadowOffset: { width: 0, height: 4 },
|
|
816
|
+
elevation: 8,
|
|
817
|
+
overflow: 'hidden',
|
|
818
|
+
},
|
|
819
|
+
header: {
|
|
820
|
+
flexDirection: 'row',
|
|
821
|
+
alignItems: 'center',
|
|
822
|
+
justifyContent: 'space-between',
|
|
823
|
+
paddingHorizontal: 14,
|
|
824
|
+
paddingVertical: 12,
|
|
825
|
+
borderBottomWidth: 1,
|
|
826
|
+
borderBottomColor: AppColors.dividerColor,
|
|
827
|
+
backgroundColor: `${AppColors.brandPurple}08`,
|
|
828
|
+
},
|
|
829
|
+
headerIconBox: {
|
|
830
|
+
width: 28,
|
|
831
|
+
height: 28,
|
|
832
|
+
borderRadius: 7,
|
|
833
|
+
backgroundColor: `${AppColors.brandPurple}18`,
|
|
834
|
+
alignItems: 'center',
|
|
835
|
+
justifyContent: 'center',
|
|
836
|
+
},
|
|
837
|
+
headerTitle: {
|
|
838
|
+
fontFamily: AppFonts.interBold,
|
|
839
|
+
fontSize: 13.5,
|
|
840
|
+
color: AppColors.primaryBlack,
|
|
841
|
+
},
|
|
842
|
+
headerSubtitle: {
|
|
843
|
+
fontFamily: AppFonts.interMedium,
|
|
844
|
+
fontSize: 10,
|
|
845
|
+
color: AppColors.grayTextWeak,
|
|
846
|
+
},
|
|
847
|
+
resetBtn: {
|
|
848
|
+
paddingHorizontal: 8,
|
|
849
|
+
paddingVertical: 4,
|
|
850
|
+
borderRadius: 5,
|
|
851
|
+
backgroundColor: `${AppColors.darkOrange}14`,
|
|
852
|
+
borderWidth: 1,
|
|
853
|
+
borderColor: `${AppColors.darkOrange}30`,
|
|
854
|
+
},
|
|
855
|
+
resetBtnText: {
|
|
856
|
+
fontFamily: AppFonts.interBold,
|
|
857
|
+
fontSize: 10,
|
|
858
|
+
color: AppColors.darkOrange,
|
|
859
|
+
},
|
|
860
|
+
closeBtn: {
|
|
861
|
+
width: 24,
|
|
862
|
+
height: 24,
|
|
863
|
+
borderRadius: 12,
|
|
864
|
+
backgroundColor: `${AppColors.primaryBlack}18`,
|
|
865
|
+
alignItems: 'center',
|
|
866
|
+
justifyContent: 'center',
|
|
867
|
+
},
|
|
868
|
+
scrollContent: {
|
|
869
|
+
padding: 14,
|
|
870
|
+
gap: 14,
|
|
871
|
+
},
|
|
872
|
+
section: {
|
|
873
|
+
gap: 7,
|
|
874
|
+
},
|
|
875
|
+
sectionHeaderRow: {
|
|
876
|
+
flexDirection: 'row',
|
|
877
|
+
alignItems: 'center',
|
|
878
|
+
gap: 5,
|
|
879
|
+
},
|
|
880
|
+
sectionTitle: {
|
|
881
|
+
fontFamily: AppFonts.interBold,
|
|
882
|
+
fontSize: 9.5,
|
|
883
|
+
color: AppColors.grayTextWeak,
|
|
884
|
+
letterSpacing: 0.5,
|
|
885
|
+
},
|
|
886
|
+
chipsWrap: {
|
|
887
|
+
flexDirection: 'row',
|
|
888
|
+
flexWrap: 'wrap',
|
|
889
|
+
gap: 6.5,
|
|
890
|
+
},
|
|
891
|
+
filterPill: {
|
|
892
|
+
flexDirection: 'row',
|
|
893
|
+
alignItems: 'center',
|
|
894
|
+
gap: 5,
|
|
895
|
+
paddingHorizontal: 9,
|
|
896
|
+
paddingVertical: 5.5,
|
|
897
|
+
borderRadius: 7,
|
|
898
|
+
backgroundColor: AppColors.grayBackground,
|
|
899
|
+
borderWidth: 1,
|
|
900
|
+
borderColor: AppColors.dividerColor,
|
|
901
|
+
},
|
|
902
|
+
filterPillActive: {
|
|
903
|
+
backgroundColor: AppColors.brandPurple,
|
|
904
|
+
borderColor: AppColors.brandPurple,
|
|
905
|
+
shadowColor: AppColors.brandPurple,
|
|
906
|
+
shadowOffset: { width: 0, height: 1 },
|
|
907
|
+
shadowOpacity: 0.25,
|
|
908
|
+
shadowRadius: 3,
|
|
909
|
+
elevation: 2,
|
|
910
|
+
},
|
|
911
|
+
filterPillText: {
|
|
912
|
+
fontFamily: AppFonts.interMedium,
|
|
913
|
+
fontSize: 11,
|
|
914
|
+
color: AppColors.primaryBlack,
|
|
915
|
+
},
|
|
916
|
+
filterPillTextActive: {
|
|
917
|
+
color: AppColors.white,
|
|
918
|
+
fontFamily: AppFonts.interBold,
|
|
919
|
+
},
|
|
920
|
+
footer: {
|
|
921
|
+
flexDirection: 'row',
|
|
922
|
+
alignItems: 'center',
|
|
923
|
+
gap: 10,
|
|
924
|
+
paddingHorizontal: 14,
|
|
925
|
+
paddingVertical: 12,
|
|
926
|
+
borderTopWidth: 1,
|
|
927
|
+
borderTopColor: AppColors.dividerColor,
|
|
928
|
+
backgroundColor: AppColors.grayBackground,
|
|
929
|
+
},
|
|
930
|
+
discardBtn: {
|
|
931
|
+
flex: 1,
|
|
932
|
+
paddingVertical: 9.5,
|
|
933
|
+
borderRadius: 8,
|
|
934
|
+
borderWidth: 1,
|
|
935
|
+
borderColor: AppColors.dividerColor,
|
|
936
|
+
backgroundColor: AppColors.white,
|
|
937
|
+
alignItems: 'center',
|
|
938
|
+
justifyContent: 'center',
|
|
939
|
+
},
|
|
940
|
+
discardBtnText: {
|
|
941
|
+
fontFamily: AppFonts.interBold,
|
|
942
|
+
fontSize: 12.5,
|
|
943
|
+
color: AppColors.grayTextStrong,
|
|
944
|
+
},
|
|
945
|
+
applyBtn: {
|
|
946
|
+
flex: 2,
|
|
947
|
+
backgroundColor: AppColors.brandPurple,
|
|
948
|
+
paddingVertical: 9.5,
|
|
949
|
+
borderRadius: 8,
|
|
950
|
+
alignItems: 'center',
|
|
951
|
+
justifyContent: 'center',
|
|
952
|
+
shadowColor: AppColors.brandPurple,
|
|
953
|
+
shadowOffset: { width: 0, height: 1 },
|
|
954
|
+
shadowOpacity: 0.25,
|
|
955
|
+
shadowRadius: 3,
|
|
956
|
+
elevation: 2,
|
|
957
|
+
},
|
|
958
|
+
applyBtnText: {
|
|
959
|
+
fontFamily: AppFonts.interBold,
|
|
960
|
+
fontSize: 12.5,
|
|
961
|
+
color: AppColors.white,
|
|
962
|
+
},
|
|
963
|
+
});
|
|
964
|
+
export default AnalyticsFilterModal;
|