yaver-feedback-react-native 0.2.0

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.
@@ -0,0 +1,313 @@
1
+ import React, { useState } from 'react';
2
+ import {
3
+ Modal,
4
+ Pressable,
5
+ ScrollView,
6
+ StyleSheet,
7
+ Text,
8
+ TouchableOpacity,
9
+ View,
10
+ } from 'react-native';
11
+ import type { TestFix, TestSession } from './types';
12
+
13
+ export interface FixReportProps {
14
+ /** Test session data with fixes list */
15
+ session: TestSession | null;
16
+ /** Whether the modal is visible */
17
+ visible: boolean;
18
+ /** Called when the user closes the report */
19
+ onClose: () => void;
20
+ /** Accent color (matches FloatingButton) */
21
+ color?: string;
22
+ }
23
+
24
+ /**
25
+ * Fix Report viewer — shows a markdown-style list of all fixes
26
+ * applied by the AI agent during a test session.
27
+ *
28
+ * Each fix shows the file, description, and error that triggered it.
29
+ * Tap a fix to expand and see the diff/code snippet.
30
+ *
31
+ * Fixes are NOT committed — they're staged changes the developer
32
+ * can review, accept, or revert.
33
+ */
34
+ export const FixReport: React.FC<FixReportProps> = ({
35
+ session,
36
+ visible,
37
+ onClose,
38
+ color = '#6366f1',
39
+ }) => {
40
+ const [expanded, setExpanded] = useState<Set<string>>(new Set());
41
+
42
+ const toggleExpand = (id: string) => {
43
+ setExpanded((prev) => {
44
+ const next = new Set(prev);
45
+ if (next.has(id)) next.delete(id);
46
+ else next.add(id);
47
+ return next;
48
+ });
49
+ };
50
+
51
+ const fixes = session?.fixes ?? [];
52
+
53
+ return (
54
+ <Modal visible={visible} animationType="slide" transparent>
55
+ <View style={s.overlay}>
56
+ <View style={s.container}>
57
+ {/* Header */}
58
+ <View style={s.header}>
59
+ <View style={{ flex: 1 }}>
60
+ <Text style={s.title}>Test Report</Text>
61
+ {session && (
62
+ <Text style={s.subtitle}>
63
+ {session.screensTested}/{session.screensDiscovered} screens
64
+ {' \u00B7 '}
65
+ {session.errorsFound} errors
66
+ {' \u00B7 '}
67
+ {fixes.length} fixes
68
+ </Text>
69
+ )}
70
+ </View>
71
+ <TouchableOpacity onPress={onClose} style={s.closeBtn}>
72
+ <Text style={s.closeBtnText}>{'\u2715'}</Text>
73
+ </TouchableOpacity>
74
+ </View>
75
+
76
+ {/* Status bar */}
77
+ {session?.active && (
78
+ <View style={[s.statusBar, { backgroundColor: `${color}20` }]}>
79
+ <View style={[s.statusDot, { backgroundColor: color }]} />
80
+ <Text style={[s.statusText, { color }]}>
81
+ {session.status || 'Testing...'}
82
+ </Text>
83
+ </View>
84
+ )}
85
+
86
+ {/* Fix list */}
87
+ <ScrollView style={s.list} contentContainerStyle={s.listContent}>
88
+ {fixes.length === 0 ? (
89
+ <View style={s.empty}>
90
+ <Text style={s.emptyText}>
91
+ {session?.active
92
+ ? 'Agent is exploring the app...'
93
+ : 'No fixes yet. Start a test session.'}
94
+ </Text>
95
+ </View>
96
+ ) : (
97
+ fixes.map((fix, i) => (
98
+ <FixItem
99
+ key={fix.id}
100
+ fix={fix}
101
+ index={i + 1}
102
+ expanded={expanded.has(fix.id)}
103
+ onToggle={() => toggleExpand(fix.id)}
104
+ color={color}
105
+ />
106
+ ))
107
+ )}
108
+ </ScrollView>
109
+
110
+ {/* Summary footer */}
111
+ {fixes.length > 0 && !session?.active && (
112
+ <View style={s.footer}>
113
+ <Text style={s.footerText}>
114
+ {fixes.filter((f) => f.verified).length}/{fixes.length} verified
115
+ {' \u00B7 '}
116
+ Changes are staged, not committed.
117
+ </Text>
118
+ </View>
119
+ )}
120
+ </View>
121
+ </View>
122
+ </Modal>
123
+ );
124
+ };
125
+
126
+ function FixItem({
127
+ fix,
128
+ index,
129
+ expanded,
130
+ onToggle,
131
+ color,
132
+ }: {
133
+ fix: TestFix;
134
+ index: number;
135
+ expanded: boolean;
136
+ onToggle: () => void;
137
+ color: string;
138
+ }) {
139
+ return (
140
+ <Pressable onPress={onToggle} style={s.fixItem}>
141
+ {/* Fix header */}
142
+ <View style={s.fixHeader}>
143
+ <View style={[s.fixIndex, { backgroundColor: fix.verified ? '#22c55e20' : `${color}20` }]}>
144
+ <Text style={[s.fixIndexText, { color: fix.verified ? '#22c55e' : color }]}>
145
+ {fix.verified ? '\u2713' : index}
146
+ </Text>
147
+ </View>
148
+ <View style={{ flex: 1 }}>
149
+ <Text style={s.fixDesc}>{fix.description}</Text>
150
+ <Text style={s.fixFile}>
151
+ {fix.file}{fix.line ? `:${fix.line}` : ''}
152
+ </Text>
153
+ </View>
154
+ <Text style={s.expandIcon}>{expanded ? '\u25B2' : '\u25BC'}</Text>
155
+ </View>
156
+
157
+ {/* Error that triggered fix */}
158
+ {fix.error && (
159
+ <View style={s.fixError}>
160
+ <Text style={s.fixErrorText}>{fix.error}</Text>
161
+ </View>
162
+ )}
163
+
164
+ {/* Expanded: diff/code */}
165
+ {expanded && fix.diff && (
166
+ <View style={s.diffBlock}>
167
+ <Text style={s.diffText}>{fix.diff}</Text>
168
+ </View>
169
+ )}
170
+ </Pressable>
171
+ );
172
+ }
173
+
174
+ const s = StyleSheet.create({
175
+ overlay: {
176
+ flex: 1,
177
+ backgroundColor: 'rgba(0,0,0,0.8)',
178
+ justifyContent: 'flex-end',
179
+ },
180
+ container: {
181
+ backgroundColor: '#0a0a0a',
182
+ borderTopLeftRadius: 20,
183
+ borderTopRightRadius: 20,
184
+ maxHeight: '85%',
185
+ borderWidth: 1,
186
+ borderColor: '#1a1a1a',
187
+ borderBottomWidth: 0,
188
+ },
189
+ header: {
190
+ flexDirection: 'row',
191
+ alignItems: 'center',
192
+ padding: 16,
193
+ borderBottomWidth: 1,
194
+ borderBottomColor: '#1a1a1a',
195
+ },
196
+ title: {
197
+ fontSize: 16,
198
+ fontWeight: '700',
199
+ color: '#e5e5e5',
200
+ fontFamily: 'Courier',
201
+ },
202
+ subtitle: {
203
+ fontSize: 11,
204
+ color: '#666',
205
+ marginTop: 2,
206
+ fontFamily: 'Courier',
207
+ },
208
+ closeBtn: { padding: 8 },
209
+ closeBtnText: { color: '#666', fontSize: 16 },
210
+ statusBar: {
211
+ flexDirection: 'row',
212
+ alignItems: 'center',
213
+ paddingHorizontal: 16,
214
+ paddingVertical: 8,
215
+ gap: 8,
216
+ },
217
+ statusDot: {
218
+ width: 6,
219
+ height: 6,
220
+ borderRadius: 3,
221
+ },
222
+ statusText: {
223
+ fontSize: 11,
224
+ fontWeight: '600',
225
+ fontFamily: 'Courier',
226
+ },
227
+ list: { flex: 1 },
228
+ listContent: { padding: 12, gap: 8 },
229
+ empty: {
230
+ padding: 32,
231
+ alignItems: 'center',
232
+ },
233
+ emptyText: {
234
+ color: '#444',
235
+ fontSize: 13,
236
+ fontFamily: 'Courier',
237
+ },
238
+ fixItem: {
239
+ backgroundColor: '#111',
240
+ borderRadius: 10,
241
+ padding: 12,
242
+ borderWidth: 1,
243
+ borderColor: '#1a1a1a',
244
+ },
245
+ fixHeader: {
246
+ flexDirection: 'row',
247
+ alignItems: 'center',
248
+ gap: 10,
249
+ },
250
+ fixIndex: {
251
+ width: 24,
252
+ height: 24,
253
+ borderRadius: 12,
254
+ alignItems: 'center',
255
+ justifyContent: 'center',
256
+ },
257
+ fixIndexText: {
258
+ fontSize: 11,
259
+ fontWeight: '700',
260
+ fontFamily: 'Courier',
261
+ },
262
+ fixDesc: {
263
+ fontSize: 12,
264
+ color: '#e5e5e5',
265
+ fontWeight: '600',
266
+ },
267
+ fixFile: {
268
+ fontSize: 10,
269
+ color: '#666',
270
+ fontFamily: 'Courier',
271
+ marginTop: 2,
272
+ },
273
+ expandIcon: {
274
+ color: '#444',
275
+ fontSize: 10,
276
+ },
277
+ fixError: {
278
+ marginTop: 8,
279
+ backgroundColor: '#f8717110',
280
+ borderRadius: 6,
281
+ padding: 8,
282
+ },
283
+ fixErrorText: {
284
+ fontSize: 10,
285
+ color: '#f87171',
286
+ fontFamily: 'Courier',
287
+ },
288
+ diffBlock: {
289
+ marginTop: 8,
290
+ backgroundColor: '#0d0d0d',
291
+ borderRadius: 6,
292
+ padding: 10,
293
+ borderWidth: 1,
294
+ borderColor: '#1a1a1a',
295
+ },
296
+ diffText: {
297
+ fontSize: 10,
298
+ color: '#22c55e',
299
+ fontFamily: 'Courier',
300
+ lineHeight: 16,
301
+ },
302
+ footer: {
303
+ borderTopWidth: 1,
304
+ borderTopColor: '#1a1a1a',
305
+ padding: 12,
306
+ alignItems: 'center',
307
+ },
308
+ footerText: {
309
+ fontSize: 10,
310
+ color: '#666',
311
+ fontFamily: 'Courier',
312
+ },
313
+ });