vorqard-ai-sdk 1.0.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.
- package/README.md +209 -0
- package/assets/Ai_icon.png +0 -0
- package/package.json +34 -0
- package/src/api/sdkClient.js +22 -0
- package/src/components/ChatCards.js +5 -0
- package/src/components/ChatComponents.js +208 -0
- package/src/components/ChatStyles.js +759 -0
- package/src/components/ChatUtils.js +33 -0
- package/src/components/ReportAnalysisComponents.js +170 -0
- package/src/components/cards/BookingConfirmationCard.js +69 -0
- package/src/components/cards/DoctorCards.js +152 -0
- package/src/components/cards/DoctorTypeSelectionCard.js +40 -0
- package/src/components/cards/HospitalCards.js +136 -0
- package/src/components/cards/SlotSelectionCard.js +86 -0
- package/src/hooks/useHealthChat.js +145 -0
- package/src/hooks/useReportAnalysis.js +93 -0
- package/src/index.js +30 -0
- package/src/screens/AIHealthChatScreen.js +610 -0
- package/src/screens/AIReportAnalysisScreen.js +77 -0
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
FlatList,
|
|
8
|
+
KeyboardAvoidingView,
|
|
9
|
+
Platform,
|
|
10
|
+
ActivityIndicator,
|
|
11
|
+
ScrollView,
|
|
12
|
+
Image,
|
|
13
|
+
StatusBar,
|
|
14
|
+
Dimensions,
|
|
15
|
+
Alert,
|
|
16
|
+
} from 'react-native';
|
|
17
|
+
import Ionicons from 'react-native-vector-icons/Ionicons';
|
|
18
|
+
import LinearGradient from 'react-native-linear-gradient';
|
|
19
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
20
|
+
|
|
21
|
+
import { useHealthChat } from '../hooks/useHealthChat';
|
|
22
|
+
import { MessageBubble, InputPanel } from '../components/ChatComponents';
|
|
23
|
+
|
|
24
|
+
const { width: SW } = Dimensions.get('window');
|
|
25
|
+
|
|
26
|
+
const AI_ACTIONS = [
|
|
27
|
+
{
|
|
28
|
+
id: 'analyze_report',
|
|
29
|
+
title: 'Analyze Report',
|
|
30
|
+
desc: 'Upload and understand your medical reports',
|
|
31
|
+
icon: 'document-text',
|
|
32
|
+
colors: ['#EEF2FF', '#DBEAFE'],
|
|
33
|
+
iconColor: '#3B82F6',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'explain_prescription',
|
|
37
|
+
title: 'Explain Prescription',
|
|
38
|
+
desc: 'Understand your medicines better',
|
|
39
|
+
icon: 'medical',
|
|
40
|
+
colors: ['#FFF7ED', '#FFEDD5'],
|
|
41
|
+
iconColor: '#F97316',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'book_appointment',
|
|
45
|
+
title: 'Book Appointment',
|
|
46
|
+
desc: 'View and manage appointments',
|
|
47
|
+
icon: 'calendar',
|
|
48
|
+
colors: ['#FFF1F2', '#FFE4E6'],
|
|
49
|
+
iconColor: '#EF4444',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'medical_history',
|
|
53
|
+
title: 'Medical History',
|
|
54
|
+
desc: 'View your health history',
|
|
55
|
+
icon: 'newspaper',
|
|
56
|
+
colors: ['#F0FDFA', '#CCFBF1'],
|
|
57
|
+
iconColor: '#14B8A6',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'find_doctor',
|
|
61
|
+
title: 'Find Doctor',
|
|
62
|
+
desc: 'Find the right doctor for you',
|
|
63
|
+
icon: 'person',
|
|
64
|
+
colors: ['#F5F3FF', '#EDE9FE'],
|
|
65
|
+
iconColor: '#8B5CF6',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'voice_assistant',
|
|
69
|
+
title: 'Voice Assistant',
|
|
70
|
+
desc: 'Talk to Vorqard AI with your voice',
|
|
71
|
+
icon: 'mic',
|
|
72
|
+
colors: ['#FFF7ED', '#FFEDD5'],
|
|
73
|
+
iconColor: '#F97316',
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
export const AIHealthChatScreen = ({
|
|
78
|
+
patientName = '',
|
|
79
|
+
onClose,
|
|
80
|
+
aiLogo,
|
|
81
|
+
onAnalyzeReportPress,
|
|
82
|
+
}) => {
|
|
83
|
+
const {
|
|
84
|
+
messages,
|
|
85
|
+
inputText,
|
|
86
|
+
setInputText,
|
|
87
|
+
loading,
|
|
88
|
+
flatListRef,
|
|
89
|
+
loadChatContext,
|
|
90
|
+
sendMessage,
|
|
91
|
+
patientContext,
|
|
92
|
+
clearChat,
|
|
93
|
+
} = useHealthChat(patientName);
|
|
94
|
+
|
|
95
|
+
const [chatMode, setChatMode] = useState(false); // false = hub view, true = chat view
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
loadChatContext();
|
|
99
|
+
// If there are existing messages, jump straight to chat mode
|
|
100
|
+
if (messages.length > 0) setChatMode(true);
|
|
101
|
+
}, []);
|
|
102
|
+
|
|
103
|
+
// When user sends a message, switch to chat mode
|
|
104
|
+
const handleSend = (text) => {
|
|
105
|
+
setChatMode(true);
|
|
106
|
+
sendMessage(text);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Start a new conversation
|
|
110
|
+
const handleNewChat = () => {
|
|
111
|
+
clearChat();
|
|
112
|
+
setChatMode(false);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// ─── Welcome Hero Card ─────────────────────────────────────────────────────
|
|
116
|
+
const renderHeroCard = () => (
|
|
117
|
+
<View style={styles.heroCard}>
|
|
118
|
+
{/* Decorative blob */}
|
|
119
|
+
<View style={styles.heroBlobRight} />
|
|
120
|
+
<View style={styles.heroBlobLeft} />
|
|
121
|
+
|
|
122
|
+
<View style={styles.heroTopRow}>
|
|
123
|
+
<View style={styles.heroLeft}>
|
|
124
|
+
{aiLogo ? (
|
|
125
|
+
<Image source={aiLogo} style={styles.heroAvatar} />
|
|
126
|
+
) : (
|
|
127
|
+
<View style={styles.heroAvatarFallback}>
|
|
128
|
+
<Ionicons name="sparkles" size={18} color="#FFF" />
|
|
129
|
+
</View>
|
|
130
|
+
)}
|
|
131
|
+
<View style={{ marginLeft: 10, flex: 1 }}>
|
|
132
|
+
<Text style={styles.heroGreeting}>
|
|
133
|
+
Hi {patientName || 'there'}! 👋
|
|
134
|
+
</Text>
|
|
135
|
+
<Text style={styles.heroBody}>
|
|
136
|
+
I see your last visit was for{'\n'}
|
|
137
|
+
<Text style={styles.heroHighlight}>
|
|
138
|
+
{patientContext?.diagnosis || '...'}
|
|
139
|
+
</Text>
|
|
140
|
+
{' '}on{' '}
|
|
141
|
+
<Text style={styles.heroHighlight}>
|
|
142
|
+
{patientContext?.date || '...'}
|
|
143
|
+
</Text>.
|
|
144
|
+
</Text>
|
|
145
|
+
<Text style={styles.heroSub}>How can I help you today?</Text>
|
|
146
|
+
</View>
|
|
147
|
+
</View>
|
|
148
|
+
{/* Medical clipboard icon */}
|
|
149
|
+
<View style={styles.heroIllustration}>
|
|
150
|
+
<Ionicons name="clipboard" size={42} color="#BFDBFE" />
|
|
151
|
+
</View>
|
|
152
|
+
</View>
|
|
153
|
+
|
|
154
|
+
{/* Snapshot row */}
|
|
155
|
+
<View style={styles.snapshotRow}>
|
|
156
|
+
<View style={styles.snapshotItem}>
|
|
157
|
+
<View style={[styles.snapshotIcon, { backgroundColor: '#EEF2FF' }]}>
|
|
158
|
+
<Ionicons name="calendar-outline" size={16} color="#3B82F6" />
|
|
159
|
+
</View>
|
|
160
|
+
<Text style={styles.snapshotLabel}>Last Visit</Text>
|
|
161
|
+
<Text style={styles.snapshotValue}>{patientContext?.date || '—'}</Text>
|
|
162
|
+
</View>
|
|
163
|
+
<View style={styles.snapshotDivider} />
|
|
164
|
+
<View style={styles.snapshotItem}>
|
|
165
|
+
<View style={[styles.snapshotIcon, { backgroundColor: '#F5F3FF' }]}>
|
|
166
|
+
<Ionicons name="medkit-outline" size={16} color="#8B5CF6" />
|
|
167
|
+
</View>
|
|
168
|
+
<Text style={styles.snapshotLabel}>Condition</Text>
|
|
169
|
+
<Text style={styles.snapshotValue} numberOfLines={1}>
|
|
170
|
+
{patientContext?.diagnosis || '—'}
|
|
171
|
+
</Text>
|
|
172
|
+
</View>
|
|
173
|
+
<View style={styles.snapshotDivider} />
|
|
174
|
+
<View style={styles.snapshotItem}>
|
|
175
|
+
<View style={[styles.snapshotIcon, { backgroundColor: '#ECFDF5' }]}>
|
|
176
|
+
<Ionicons name="shield-checkmark-outline" size={16} color="#10B981" />
|
|
177
|
+
</View>
|
|
178
|
+
<Text style={styles.snapshotLabel}>Status</Text>
|
|
179
|
+
<Text style={[styles.snapshotValue, { color: '#10B981' }]}>
|
|
180
|
+
{patientContext?.status || 'Completed'}
|
|
181
|
+
</Text>
|
|
182
|
+
</View>
|
|
183
|
+
</View>
|
|
184
|
+
</View>
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// ─── Actions Grid ──────────────────────────────────────────────────────────
|
|
188
|
+
const renderActionsGrid = () => (
|
|
189
|
+
<View style={styles.actionsSection}>
|
|
190
|
+
<Text style={styles.actionsSectionTitle}>How can I help you?</Text>
|
|
191
|
+
<View style={styles.actionsGrid}>
|
|
192
|
+
{AI_ACTIONS.map((action) => (
|
|
193
|
+
<TouchableOpacity
|
|
194
|
+
key={action.id}
|
|
195
|
+
style={styles.actionCard}
|
|
196
|
+
onPress={() => {
|
|
197
|
+
if (action.id === 'analyze_report') {
|
|
198
|
+
if (onAnalyzeReportPress) {
|
|
199
|
+
onAnalyzeReportPress();
|
|
200
|
+
} else {
|
|
201
|
+
Alert.alert("Coming Soon", "Report Analysis Coming Soon");
|
|
202
|
+
}
|
|
203
|
+
} else if (action.id === 'find_doctor') {
|
|
204
|
+
Alert.alert("Coming Soon", "Coming Soon");
|
|
205
|
+
} else if (action.id === 'voice_assistant') {
|
|
206
|
+
Alert.alert("Coming Soon", "Voice Feature Coming Soon");
|
|
207
|
+
} else {
|
|
208
|
+
handleSend(action.title);
|
|
209
|
+
}
|
|
210
|
+
}}
|
|
211
|
+
activeOpacity={0.7}
|
|
212
|
+
>
|
|
213
|
+
<LinearGradient
|
|
214
|
+
colors={action.colors}
|
|
215
|
+
style={styles.actionIconCircle}
|
|
216
|
+
start={{ x: 0, y: 0 }}
|
|
217
|
+
end={{ x: 1, y: 1 }}
|
|
218
|
+
>
|
|
219
|
+
<Ionicons name={action.icon} size={24} color={action.iconColor} />
|
|
220
|
+
</LinearGradient>
|
|
221
|
+
<Text style={styles.actionTitle}>{action.title}</Text>
|
|
222
|
+
<Text style={styles.actionDesc}>{action.desc}</Text>
|
|
223
|
+
</TouchableOpacity>
|
|
224
|
+
))}
|
|
225
|
+
</View>
|
|
226
|
+
</View>
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// ─── Empty state / Today divider ──────────────────────────────────────────
|
|
230
|
+
const renderTodayState = () => (
|
|
231
|
+
<View style={styles.todayContainer}>
|
|
232
|
+
<View style={styles.todayDividerRow}>
|
|
233
|
+
<View style={styles.todayLine} />
|
|
234
|
+
<View style={styles.todayBadge}>
|
|
235
|
+
<Text style={styles.todayBadgeText}>Today</Text>
|
|
236
|
+
</View>
|
|
237
|
+
<View style={styles.todayLine} />
|
|
238
|
+
</View>
|
|
239
|
+
<View style={styles.emptyStateBox}>
|
|
240
|
+
<Text style={styles.emptyStateSparkle}>✨ ✦</Text>
|
|
241
|
+
<Text style={styles.emptyStateTitle}>
|
|
242
|
+
Your AI health assistant is here to help.
|
|
243
|
+
</Text>
|
|
244
|
+
<Text style={styles.emptyStateBody}>
|
|
245
|
+
Ask anything about your health, reports, medicines and more.
|
|
246
|
+
</Text>
|
|
247
|
+
</View>
|
|
248
|
+
</View>
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// ─── Hub content (before any chat) ────────────────────────────────────────
|
|
252
|
+
const renderHubContent = () => (
|
|
253
|
+
<ScrollView
|
|
254
|
+
style={styles.hubScroll}
|
|
255
|
+
contentContainerStyle={styles.hubScrollContent}
|
|
256
|
+
showsVerticalScrollIndicator={false}
|
|
257
|
+
keyboardShouldPersistTaps="handled"
|
|
258
|
+
>
|
|
259
|
+
{renderHeroCard()}
|
|
260
|
+
{renderActionsGrid()}
|
|
261
|
+
{renderTodayState()}
|
|
262
|
+
</ScrollView>
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// ─── Chat content (after first message) ───────────────────────────────────
|
|
266
|
+
const renderChatContent = () => (
|
|
267
|
+
<FlatList
|
|
268
|
+
ref={flatListRef}
|
|
269
|
+
data={messages}
|
|
270
|
+
keyExtractor={(_, i) => i.toString()}
|
|
271
|
+
renderItem={({ item }) => (
|
|
272
|
+
<MessageBubble
|
|
273
|
+
item={item}
|
|
274
|
+
aiLogo={aiLogo}
|
|
275
|
+
onOptionPress={(opt) => handleSend(opt.label)}
|
|
276
|
+
/>
|
|
277
|
+
)}
|
|
278
|
+
contentContainerStyle={styles.flatListContent}
|
|
279
|
+
onContentSizeChange={() => flatListRef.current?.scrollToEnd({ animated: true })}
|
|
280
|
+
onLayout={() => flatListRef.current?.scrollToEnd({ animated: true })}
|
|
281
|
+
showsVerticalScrollIndicator={false}
|
|
282
|
+
ListEmptyComponent={
|
|
283
|
+
loading ? (
|
|
284
|
+
<ActivityIndicator style={{ marginTop: 60 }} color="#3B82F6" size="large" />
|
|
285
|
+
) : null
|
|
286
|
+
}
|
|
287
|
+
/>
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
<SafeAreaView style={styles.safeArea} edges={['top', 'bottom']}>
|
|
292
|
+
<StatusBar barStyle="dark-content" backgroundColor="#EEF2FF" />
|
|
293
|
+
|
|
294
|
+
{/* ─── Header Bar ───────────────────────────────────────────────────── */}
|
|
295
|
+
<View style={styles.header}>
|
|
296
|
+
{onClose ? (
|
|
297
|
+
<TouchableOpacity onPress={onClose} style={styles.headerBtn} activeOpacity={0.7}>
|
|
298
|
+
<Ionicons name="arrow-back" size={22} color="#1E293B" />
|
|
299
|
+
</TouchableOpacity>
|
|
300
|
+
) : <View style={styles.headerBtn} />}
|
|
301
|
+
|
|
302
|
+
<View style={styles.headerCenter}>
|
|
303
|
+
{aiLogo ? (
|
|
304
|
+
<Image source={aiLogo} style={styles.headerLogo} />
|
|
305
|
+
) : (
|
|
306
|
+
<View style={styles.headerLogoFallback}>
|
|
307
|
+
<Ionicons name="sparkles" size={14} color="#FFF" />
|
|
308
|
+
</View>
|
|
309
|
+
)}
|
|
310
|
+
<Text style={styles.headerTitle}>VORQARD AI</Text>
|
|
311
|
+
</View>
|
|
312
|
+
|
|
313
|
+
<TouchableOpacity onPress={handleNewChat} style={styles.headerBtn} activeOpacity={0.7}>
|
|
314
|
+
<Ionicons name="add-circle-outline" size={26} color="#6366F1" />
|
|
315
|
+
</TouchableOpacity>
|
|
316
|
+
</View>
|
|
317
|
+
|
|
318
|
+
<KeyboardAvoidingView
|
|
319
|
+
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
320
|
+
style={styles.flex}
|
|
321
|
+
keyboardVerticalOffset={Platform.OS === 'ios' ? 90 : 0}
|
|
322
|
+
>
|
|
323
|
+
<LinearGradient
|
|
324
|
+
colors={['#EEF2FF', '#E8EFFE', '#F0F4FF']}
|
|
325
|
+
style={styles.flex}
|
|
326
|
+
start={{ x: 0, y: 0 }}
|
|
327
|
+
end={{ x: 1, y: 1 }}
|
|
328
|
+
>
|
|
329
|
+
{chatMode ? renderChatContent() : renderHubContent()}
|
|
330
|
+
|
|
331
|
+
<InputPanel
|
|
332
|
+
value={inputText}
|
|
333
|
+
onChangeText={setInputText}
|
|
334
|
+
onSend={handleSend}
|
|
335
|
+
loading={loading}
|
|
336
|
+
onVoicePress={() => {
|
|
337
|
+
setChatMode(true);
|
|
338
|
+
sendMessage('Launch Voice Assistant');
|
|
339
|
+
}}
|
|
340
|
+
aiLogo={aiLogo}
|
|
341
|
+
/>
|
|
342
|
+
</LinearGradient>
|
|
343
|
+
</KeyboardAvoidingView>
|
|
344
|
+
</SafeAreaView>
|
|
345
|
+
);
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const CARD_WIDTH = (SW - 52) / 3;
|
|
349
|
+
|
|
350
|
+
const styles = StyleSheet.create({
|
|
351
|
+
safeArea: {
|
|
352
|
+
flex: 1,
|
|
353
|
+
backgroundColor: '#EEF2FF',
|
|
354
|
+
},
|
|
355
|
+
flex: { flex: 1 },
|
|
356
|
+
|
|
357
|
+
// ── Header ─────────────────────────────────────────────────────────────────
|
|
358
|
+
header: {
|
|
359
|
+
flexDirection: 'row',
|
|
360
|
+
alignItems: 'center',
|
|
361
|
+
justifyContent: 'space-between',
|
|
362
|
+
paddingHorizontal: 12,
|
|
363
|
+
paddingVertical: 10,
|
|
364
|
+
backgroundColor: '#FFFFFF',
|
|
365
|
+
borderBottomWidth: 1,
|
|
366
|
+
borderBottomColor: '#EEF2FF',
|
|
367
|
+
elevation: 2,
|
|
368
|
+
shadowColor: '#C7D2FE',
|
|
369
|
+
shadowOffset: { width: 0, height: 2 },
|
|
370
|
+
shadowOpacity: 0.15,
|
|
371
|
+
shadowRadius: 4,
|
|
372
|
+
},
|
|
373
|
+
headerBtn: {
|
|
374
|
+
width: 40,
|
|
375
|
+
height: 40,
|
|
376
|
+
justifyContent: 'center',
|
|
377
|
+
alignItems: 'center',
|
|
378
|
+
},
|
|
379
|
+
headerCenter: {
|
|
380
|
+
flexDirection: 'row',
|
|
381
|
+
alignItems: 'center',
|
|
382
|
+
gap: 8,
|
|
383
|
+
},
|
|
384
|
+
headerLogo: { width: 28, height: 28, borderRadius: 8 },
|
|
385
|
+
headerLogoFallback: {
|
|
386
|
+
width: 28,
|
|
387
|
+
height: 28,
|
|
388
|
+
borderRadius: 8,
|
|
389
|
+
backgroundColor: '#6366F1',
|
|
390
|
+
justifyContent: 'center',
|
|
391
|
+
alignItems: 'center',
|
|
392
|
+
},
|
|
393
|
+
headerTitle: {
|
|
394
|
+
fontSize: 15,
|
|
395
|
+
fontWeight: '800',
|
|
396
|
+
color: '#1E293B',
|
|
397
|
+
letterSpacing: 0.3,
|
|
398
|
+
},
|
|
399
|
+
|
|
400
|
+
// ── Hub scroll ─────────────────────────────────────────────────────────────
|
|
401
|
+
hubScroll: { flex: 1 },
|
|
402
|
+
hubScrollContent: { padding: 16, paddingBottom: 8 },
|
|
403
|
+
|
|
404
|
+
// ── Hero Card ──────────────────────────────────────────────────────────────
|
|
405
|
+
heroCard: {
|
|
406
|
+
backgroundColor: '#FFFFFF',
|
|
407
|
+
borderRadius: 20,
|
|
408
|
+
padding: 18,
|
|
409
|
+
marginBottom: 16,
|
|
410
|
+
overflow: 'hidden',
|
|
411
|
+
shadowColor: '#C7D2FE',
|
|
412
|
+
shadowOffset: { width: 0, height: 4 },
|
|
413
|
+
shadowOpacity: 0.3,
|
|
414
|
+
shadowRadius: 12,
|
|
415
|
+
elevation: 4,
|
|
416
|
+
},
|
|
417
|
+
heroBlobRight: {
|
|
418
|
+
position: 'absolute',
|
|
419
|
+
width: 130,
|
|
420
|
+
height: 130,
|
|
421
|
+
borderRadius: 65,
|
|
422
|
+
backgroundColor: 'rgba(147,197,253,0.15)',
|
|
423
|
+
right: -30,
|
|
424
|
+
top: -30,
|
|
425
|
+
},
|
|
426
|
+
heroBlobLeft: {
|
|
427
|
+
position: 'absolute',
|
|
428
|
+
width: 80,
|
|
429
|
+
height: 80,
|
|
430
|
+
borderRadius: 40,
|
|
431
|
+
backgroundColor: 'rgba(196,181,253,0.12)',
|
|
432
|
+
left: -20,
|
|
433
|
+
bottom: 20,
|
|
434
|
+
},
|
|
435
|
+
heroTopRow: {
|
|
436
|
+
flexDirection: 'row',
|
|
437
|
+
alignItems: 'flex-start',
|
|
438
|
+
marginBottom: 16,
|
|
439
|
+
},
|
|
440
|
+
heroLeft: { flexDirection: 'row', flex: 1 },
|
|
441
|
+
heroAvatar: { width: 44, height: 44, borderRadius: 12 },
|
|
442
|
+
heroAvatarFallback: {
|
|
443
|
+
width: 44,
|
|
444
|
+
height: 44,
|
|
445
|
+
borderRadius: 12,
|
|
446
|
+
backgroundColor: '#6366F1',
|
|
447
|
+
justifyContent: 'center',
|
|
448
|
+
alignItems: 'center',
|
|
449
|
+
},
|
|
450
|
+
heroGreeting: {
|
|
451
|
+
fontSize: 17,
|
|
452
|
+
fontWeight: '800',
|
|
453
|
+
color: '#1E293B',
|
|
454
|
+
marginBottom: 4,
|
|
455
|
+
},
|
|
456
|
+
heroBody: {
|
|
457
|
+
fontSize: 13,
|
|
458
|
+
color: '#475569',
|
|
459
|
+
lineHeight: 20,
|
|
460
|
+
marginBottom: 4,
|
|
461
|
+
},
|
|
462
|
+
heroHighlight: {
|
|
463
|
+
fontWeight: '800',
|
|
464
|
+
color: '#1E293B',
|
|
465
|
+
},
|
|
466
|
+
heroSub: {
|
|
467
|
+
fontSize: 13,
|
|
468
|
+
color: '#64748B',
|
|
469
|
+
marginTop: 2,
|
|
470
|
+
},
|
|
471
|
+
heroIllustration: {
|
|
472
|
+
width: 60,
|
|
473
|
+
height: 60,
|
|
474
|
+
borderRadius: 16,
|
|
475
|
+
backgroundColor: '#EEF2FF',
|
|
476
|
+
justifyContent: 'center',
|
|
477
|
+
alignItems: 'center',
|
|
478
|
+
marginLeft: 8,
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
// ── Snapshot row ───────────────────────────────────────────────────────────
|
|
482
|
+
snapshotRow: {
|
|
483
|
+
flexDirection: 'row',
|
|
484
|
+
alignItems: 'center',
|
|
485
|
+
borderTopWidth: 1,
|
|
486
|
+
borderTopColor: '#F1F5F9',
|
|
487
|
+
paddingTop: 14,
|
|
488
|
+
gap: 0,
|
|
489
|
+
},
|
|
490
|
+
snapshotItem: {
|
|
491
|
+
flex: 1,
|
|
492
|
+
alignItems: 'center',
|
|
493
|
+
gap: 3,
|
|
494
|
+
},
|
|
495
|
+
snapshotIcon: {
|
|
496
|
+
width: 32,
|
|
497
|
+
height: 32,
|
|
498
|
+
borderRadius: 16,
|
|
499
|
+
justifyContent: 'center',
|
|
500
|
+
alignItems: 'center',
|
|
501
|
+
marginBottom: 2,
|
|
502
|
+
},
|
|
503
|
+
snapshotLabel: {
|
|
504
|
+
fontSize: 10,
|
|
505
|
+
color: '#94A3B8',
|
|
506
|
+
fontWeight: '600',
|
|
507
|
+
textTransform: 'uppercase',
|
|
508
|
+
letterSpacing: 0.4,
|
|
509
|
+
},
|
|
510
|
+
snapshotValue: {
|
|
511
|
+
fontSize: 12,
|
|
512
|
+
fontWeight: '800',
|
|
513
|
+
color: '#1E293B',
|
|
514
|
+
textAlign: 'center',
|
|
515
|
+
},
|
|
516
|
+
snapshotDivider: {
|
|
517
|
+
width: 1,
|
|
518
|
+
height: 40,
|
|
519
|
+
backgroundColor: '#F1F5F9',
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
// ── Actions Grid ───────────────────────────────────────────────────────────
|
|
523
|
+
actionsSection: { marginBottom: 16 },
|
|
524
|
+
actionsSectionTitle: {
|
|
525
|
+
fontSize: 16,
|
|
526
|
+
fontWeight: '800',
|
|
527
|
+
color: '#1E293B',
|
|
528
|
+
marginBottom: 12,
|
|
529
|
+
},
|
|
530
|
+
actionsGrid: {
|
|
531
|
+
flexDirection: 'row',
|
|
532
|
+
flexWrap: 'wrap',
|
|
533
|
+
gap: 10,
|
|
534
|
+
},
|
|
535
|
+
actionCard: {
|
|
536
|
+
width: CARD_WIDTH,
|
|
537
|
+
backgroundColor: '#FFFFFF',
|
|
538
|
+
borderRadius: 16,
|
|
539
|
+
padding: 12,
|
|
540
|
+
alignItems: 'center',
|
|
541
|
+
shadowColor: '#C7D2FE',
|
|
542
|
+
shadowOffset: { width: 0, height: 2 },
|
|
543
|
+
shadowOpacity: 0.2,
|
|
544
|
+
shadowRadius: 8,
|
|
545
|
+
elevation: 2,
|
|
546
|
+
},
|
|
547
|
+
actionIconCircle: {
|
|
548
|
+
width: 50,
|
|
549
|
+
height: 50,
|
|
550
|
+
borderRadius: 25,
|
|
551
|
+
justifyContent: 'center',
|
|
552
|
+
alignItems: 'center',
|
|
553
|
+
marginBottom: 8,
|
|
554
|
+
},
|
|
555
|
+
actionTitle: {
|
|
556
|
+
fontSize: 11,
|
|
557
|
+
fontWeight: '800',
|
|
558
|
+
color: '#1E293B',
|
|
559
|
+
textAlign: 'center',
|
|
560
|
+
marginBottom: 3,
|
|
561
|
+
},
|
|
562
|
+
actionDesc: {
|
|
563
|
+
fontSize: 9.5,
|
|
564
|
+
color: '#94A3B8',
|
|
565
|
+
textAlign: 'center',
|
|
566
|
+
lineHeight: 14,
|
|
567
|
+
fontWeight: '500',
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
// ── Today / Empty State ────────────────────────────────────────────────────
|
|
571
|
+
todayContainer: { marginBottom: 8 },
|
|
572
|
+
todayDividerRow: {
|
|
573
|
+
flexDirection: 'row',
|
|
574
|
+
alignItems: 'center',
|
|
575
|
+
marginBottom: 14,
|
|
576
|
+
},
|
|
577
|
+
todayLine: { flex: 1, height: 1, backgroundColor: '#E2E8F0' },
|
|
578
|
+
todayBadge: {
|
|
579
|
+
backgroundColor: '#FFFFFF',
|
|
580
|
+
borderWidth: 1,
|
|
581
|
+
borderColor: '#E2E8F0',
|
|
582
|
+
paddingHorizontal: 12,
|
|
583
|
+
paddingVertical: 4,
|
|
584
|
+
borderRadius: 12,
|
|
585
|
+
marginHorizontal: 8,
|
|
586
|
+
},
|
|
587
|
+
todayBadgeText: {
|
|
588
|
+
fontSize: 11,
|
|
589
|
+
fontWeight: '700',
|
|
590
|
+
color: '#64748B',
|
|
591
|
+
},
|
|
592
|
+
emptyStateBox: { alignItems: 'center', paddingVertical: 8 },
|
|
593
|
+
emptyStateSparkle: { fontSize: 20, marginBottom: 6, letterSpacing: 4 },
|
|
594
|
+
emptyStateTitle: {
|
|
595
|
+
fontSize: 13,
|
|
596
|
+
fontWeight: '700',
|
|
597
|
+
color: '#475569',
|
|
598
|
+
textAlign: 'center',
|
|
599
|
+
marginBottom: 4,
|
|
600
|
+
},
|
|
601
|
+
emptyStateBody: {
|
|
602
|
+
fontSize: 12,
|
|
603
|
+
color: '#94A3B8',
|
|
604
|
+
textAlign: 'center',
|
|
605
|
+
lineHeight: 18,
|
|
606
|
+
},
|
|
607
|
+
|
|
608
|
+
// ── Chat FlatList ──────────────────────────────────────────────────────────
|
|
609
|
+
flatListContent: { paddingVertical: 20, paddingHorizontal: 4 },
|
|
610
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, StyleSheet, ScrollView, ActivityIndicator, TouchableOpacity, Text } from 'react-native';
|
|
3
|
+
import LinearGradient from 'react-native-linear-gradient';
|
|
4
|
+
import Ionicons from 'react-native-vector-icons/Ionicons';
|
|
5
|
+
|
|
6
|
+
import { useReportAnalysis } from '../hooks/useReportAnalysis';
|
|
7
|
+
import { UploadArea, ImagePreview, ResultsPanel } from '../components/ReportAnalysisComponents';
|
|
8
|
+
|
|
9
|
+
export const AIReportAnalysisScreen = ({ onClose, onChatWithAIPress }) => {
|
|
10
|
+
const {
|
|
11
|
+
selectedImage,
|
|
12
|
+
loading,
|
|
13
|
+
result,
|
|
14
|
+
fadeAnim,
|
|
15
|
+
pickFromGallery,
|
|
16
|
+
captureWithCamera,
|
|
17
|
+
analyzeReport,
|
|
18
|
+
clearImage,
|
|
19
|
+
} = useReportAnalysis();
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<View style={styles.container}>
|
|
23
|
+
<LinearGradient colors={['#071428', '#0D2248']} style={styles.header}>
|
|
24
|
+
{onClose && (
|
|
25
|
+
<TouchableOpacity onPress={onClose} style={styles.backBtn}>
|
|
26
|
+
<Ionicons name="arrow-back" size={22} color="#fff" />
|
|
27
|
+
</TouchableOpacity>
|
|
28
|
+
)}
|
|
29
|
+
<View style={{ flex: 1, alignItems: 'center', marginRight: onClose ? 36 : 0 }}>
|
|
30
|
+
<Text style={styles.headerTitle}>AI Report Analysis</Text>
|
|
31
|
+
<Text style={styles.headerSub}>Powered by Gemini Vision</Text>
|
|
32
|
+
</View>
|
|
33
|
+
</LinearGradient>
|
|
34
|
+
|
|
35
|
+
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.scroll}>
|
|
36
|
+
{!selectedImage ? (
|
|
37
|
+
<UploadArea captureWithCamera={captureWithCamera} pickFromGallery={pickFromGallery} />
|
|
38
|
+
) : (
|
|
39
|
+
<ImagePreview
|
|
40
|
+
selectedImage={selectedImage}
|
|
41
|
+
clearImage={clearImage}
|
|
42
|
+
analyzeReport={analyzeReport}
|
|
43
|
+
loading={loading}
|
|
44
|
+
/>
|
|
45
|
+
)}
|
|
46
|
+
|
|
47
|
+
{loading && (
|
|
48
|
+
<View style={styles.loadingCard}>
|
|
49
|
+
<ActivityIndicator size="large" color="#2563EB" />
|
|
50
|
+
<Text style={styles.loadingText}>Gemini AI is reading your report...</Text>
|
|
51
|
+
<Text style={styles.loadingSubText}>This may take a few seconds</Text>
|
|
52
|
+
</View>
|
|
53
|
+
)}
|
|
54
|
+
|
|
55
|
+
{result && !loading && (
|
|
56
|
+
<ResultsPanel
|
|
57
|
+
result={result}
|
|
58
|
+
fadeAnim={fadeAnim}
|
|
59
|
+
onChatWithAIPress={onChatWithAIPress}
|
|
60
|
+
/>
|
|
61
|
+
)}
|
|
62
|
+
</ScrollView>
|
|
63
|
+
</View>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const styles = StyleSheet.create({
|
|
68
|
+
container: { flex: 1, backgroundColor: '#F8FAFC' },
|
|
69
|
+
header: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 20, paddingVertical: 16 },
|
|
70
|
+
backBtn: { width: 36, height: 36, justifyContent: 'center', alignItems: 'center' },
|
|
71
|
+
headerTitle: { color: '#fff', fontSize: 16, fontWeight: 'bold', textAlign: 'center' },
|
|
72
|
+
headerSub: { color: '#60A5FA', fontSize: 10, textAlign: 'center', marginTop: 2 },
|
|
73
|
+
scroll: { padding: 20, paddingBottom: 40 },
|
|
74
|
+
loadingCard: { backgroundColor: '#fff', borderRadius: 16, padding: 30, alignItems: 'center', marginBottom: 16, elevation: 3 },
|
|
75
|
+
loadingText: { fontSize: 15, fontWeight: '600', color: '#1E293B', marginTop: 16 },
|
|
76
|
+
loadingSubText: { fontSize: 13, color: '#94A3B8', marginTop: 4 },
|
|
77
|
+
});
|