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
package/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# VORQARD AI Health Assistant SDK
|
|
2
|
+
|
|
3
|
+
[](https://github.com/AbhivornGroupIn/VORQARD_SDK_AI)
|
|
4
|
+
|
|
5
|
+
The VORQARD AI SDK is a standalone React Native / Expo module that provides a conversational AI clinical health assistant and a structured medical report analyzer.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🚀 Step-by-Step Integration Guide
|
|
10
|
+
|
|
11
|
+
Follow these steps to integrate the AI SDK into your React Native / Expo application (such as the Groupin App):
|
|
12
|
+
|
|
13
|
+
### Step 1: Copy the SDK Folder
|
|
14
|
+
Copy the `VORQARD_AI_SDK` directory into your project structure. A common setup is to place it side-by-side with your host application:
|
|
15
|
+
```text
|
|
16
|
+
/my-workspace
|
|
17
|
+
├── groupin-app/ (Your main mobile application directory)
|
|
18
|
+
└── VORQARD_AI_SDK/ (This SDK directory)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Step 2: Install Peer Dependencies
|
|
22
|
+
The SDK relies on standard Expo and React Native libraries for image picking, safe area views, icons, and network requests.
|
|
23
|
+
|
|
24
|
+
Run the following command in your main application folder (`groupin-app/`):
|
|
25
|
+
```bash
|
|
26
|
+
npx expo install axios expo-linear-gradient expo-image-picker expo-camera @expo/vector-icons react-native-safe-area-context
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Step 3: Link the SDK to Your App
|
|
30
|
+
In your main application folder (`groupin-app/`), install the SDK package as a local file dependency by running:
|
|
31
|
+
```bash
|
|
32
|
+
npm install ../VORQARD_AI_SDK
|
|
33
|
+
```
|
|
34
|
+
*(This automatically adds `"vorqard-ai-sdk": "file:../VORQARD_AI_SDK"` to your dependencies in `package.json` and configures node module resolution)*.
|
|
35
|
+
|
|
36
|
+
### Step 4: Render the AI Assistant Screen
|
|
37
|
+
Import `VorqardAIAssistant` and add it to any screen, layout, or modal in your navigation stack.
|
|
38
|
+
|
|
39
|
+
Here is a basic template:
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
import React from 'react';
|
|
43
|
+
import { SafeAreaView, StyleSheet } from 'react-native';
|
|
44
|
+
import { VorqardAIAssistant } from 'vorqard-ai-sdk';
|
|
45
|
+
|
|
46
|
+
export default function AIExpertScreen({ navigation }) {
|
|
47
|
+
// Retrieve the patient's authenticated token and first name from your app's auth state
|
|
48
|
+
const userToken = "eyJhbGciOi..."; // Active patient JWT session token
|
|
49
|
+
const patientName = "Jagadish"; // Used for personalized greeting
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<SafeAreaView style={styles.container}>
|
|
53
|
+
<VorqardAIAssistant
|
|
54
|
+
backendUrl="https://app.vorqard.com/api" // VORQARD production API endpoint
|
|
55
|
+
userToken={userToken} // Authentication JWT
|
|
56
|
+
patientName={patientName} // Custom patient name
|
|
57
|
+
onClose={() => navigation.goBack()} // Callback when close/back button is pressed
|
|
58
|
+
aiLogo={require('./assets/AI_Icon.png')} // Optional: custom icon for AI replies
|
|
59
|
+
/>
|
|
60
|
+
</SafeAreaView>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const styles = StyleSheet.create({
|
|
65
|
+
container: {
|
|
66
|
+
flex: 1,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 🛠 Advanced / Decoupled Usage
|
|
74
|
+
|
|
75
|
+
If you want to render your own custom user interface and only use the state management and API integration layer of the SDK, you can import the initialization client and custom hooks directly:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
import { initSDKClient, getSDKClient, useHealthChat } from 'vorqard-ai-sdk';
|
|
79
|
+
|
|
80
|
+
// 1. Initialize the HTTP client configuration
|
|
81
|
+
initSDKClient('https://app.vorqard.com/api', 'JWT_TOKEN_HERE');
|
|
82
|
+
|
|
83
|
+
// 2. Consume the custom health chat state hook in your own component
|
|
84
|
+
const MyCustomChat = () => {
|
|
85
|
+
const { messages, sendMessage, loading } = useHealthChat('chat', 'Jagadish');
|
|
86
|
+
// messages contains: [{ id, role, content, ui_action, timestamp }]
|
|
87
|
+
// sendMessage is a function to send the next prompt
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 🌐 Backend API Endpoints Reference
|
|
94
|
+
|
|
95
|
+
The SDK automatically communicates with the following backend routes. Ensure your API gateway/server allows the client's JWT token authorization on these endpoints:
|
|
96
|
+
|
|
97
|
+
### 1. Patient Dashboard Context
|
|
98
|
+
* **Path:** `GET /patients/dashboard/me`
|
|
99
|
+
* **Headers:** `Authorization: Bearer <userToken>`
|
|
100
|
+
* **Description:** Fetches core summary details of the logged-in patient (e.g. name, date of last visit) to personalize the greeting.
|
|
101
|
+
|
|
102
|
+
### 2. Patient Medical Records
|
|
103
|
+
* **Path:** `GET /patients/records/me`
|
|
104
|
+
* **Headers:** `Authorization: Bearer <userToken>`
|
|
105
|
+
* **Description:** Fetches past clinical records and consult history to provide relevant diagnostic context to the AI model.
|
|
106
|
+
|
|
107
|
+
### 3. AI Health Chat Engine
|
|
108
|
+
* **Path:** `POST /patients/ai/chat`
|
|
109
|
+
* **Headers:** `Authorization: Bearer <userToken>`
|
|
110
|
+
* **Request Body:**
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"message": "book appointment",
|
|
114
|
+
"history": [
|
|
115
|
+
{ "role": "user", "content": "hello" },
|
|
116
|
+
{ "role": "assistant", "content": "Hi there! How can I help you today?" }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
* **Response Body:**
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"response": "Here is a list of hospital-associated doctors...",
|
|
124
|
+
"ui_action": {
|
|
125
|
+
"type": "selection",
|
|
126
|
+
"title": "Select Doctor",
|
|
127
|
+
"options": [
|
|
128
|
+
{ "id": "129", "label": "Dr. Veeha Reddy", "name": "Dr. Veeha Reddy" }
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
* **Description:** The chat router interface. It passes user utterances and conversational history to return clinical AI responses and rich-action metadata.
|
|
134
|
+
|
|
135
|
+
### 4. AI Report Analyzer (Optional)
|
|
136
|
+
* **Path:** `POST /patients/ai/analyze-report`
|
|
137
|
+
* **Headers:** `Authorization: Bearer <userToken>`
|
|
138
|
+
* **Request Body:** `Multipart Form-Data` (keys: `file`)
|
|
139
|
+
* **Description:** Submits physical medical report images for OCR scanning and structured health scoring.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 🤝 Integration Path Options
|
|
144
|
+
|
|
145
|
+
Groupin can choose one of two integration approaches based on their requirements:
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### Option A: SDK Integration (Recommended — AI-powered)
|
|
150
|
+
Use the SDK component described in this README. The AI chat handles hospital search, doctor discovery, slot picking, and appointment booking — all through a single conversational interface.
|
|
151
|
+
|
|
152
|
+
**What Groupin needs to provide:**
|
|
153
|
+
- `backendUrl` — VORQARD API base URL
|
|
154
|
+
- `userToken` — Patient JWT token (from OTP login)
|
|
155
|
+
|
|
156
|
+
**Endpoints used internally by SDK (no manual calls needed):**
|
|
157
|
+
|
|
158
|
+
| Endpoint | Method | Purpose |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| `/auth/send-otp-mobile` | POST | Send OTP to patient phone |
|
|
161
|
+
| `/auth/verify-otp-mobile` | POST | Verify OTP → get JWT token |
|
|
162
|
+
| `/patients/ai/chat` | POST | Full AI booking + health chat |
|
|
163
|
+
| `/patients/ai/analyze-report` | POST | Medical report OCR analysis |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
### Option B: Direct Partner API (Custom UI — no SDK required)
|
|
168
|
+
If Groupin prefers to build their own UI and call VORQARD APIs directly from their backend server.
|
|
169
|
+
|
|
170
|
+
**Authentication:** All requests must include the partner API key in the header:
|
|
171
|
+
```
|
|
172
|
+
X-Api-Key: <provided_by_vorqard>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Backend URL:** `https://app.vorqard.com/api`
|
|
176
|
+
|
|
177
|
+
**Available Partner Endpoints:**
|
|
178
|
+
|
|
179
|
+
| Endpoint | Method | Purpose |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| `/partner/groupin/sync-user` | POST | Register/sync a Groupin user as a VORQARD patient |
|
|
182
|
+
| `/partner/groupin/hospitals` | GET | Fetch all available hospitals |
|
|
183
|
+
| `/partner/groupin/doctors` | GET | Fetch doctors (filter by specialty or hospital) |
|
|
184
|
+
| `/partner/groupin/book-appointment` | POST | Book an appointment directly |
|
|
185
|
+
|
|
186
|
+
**Example — Sync User Request Body:**
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"phone_number": "9876543210",
|
|
190
|
+
"user_name": "Ravi Kumar",
|
|
191
|
+
"email": "ravi@example.com",
|
|
192
|
+
"gender": "Male",
|
|
193
|
+
"date_of_birth": "1990-05-15",
|
|
194
|
+
"blood_group": "O+",
|
|
195
|
+
"location": "Hyderabad"
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Example — Book Appointment Request Body:**
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"vorqard_patient_id": 42,
|
|
203
|
+
"vorqard_doctor_id": 7,
|
|
204
|
+
"date": "2026-06-20",
|
|
205
|
+
"time_slot": "10:00"
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vorqard-ai-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Standalone React Native SDK for VORQARD AI Health Assistant and Report Analyzer",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/AbhivornGroupIn/VORQARD_SDK_AI.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/AbhivornGroupIn/VORQARD_SDK_AI#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/AbhivornGroupIn/VORQARD_SDK_AI/issues"
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"react": ">=18.0.0",
|
|
16
|
+
"react-native": ">=0.70.0",
|
|
17
|
+
"axios": "^1.0.0",
|
|
18
|
+
"react-native-linear-gradient": "*",
|
|
19
|
+
"react-native-image-crop-picker": "*",
|
|
20
|
+
"react-native-vision-camera": "*",
|
|
21
|
+
"react-native-vector-icons": "*",
|
|
22
|
+
"react-native-safe-area-context": "*"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"vorqard",
|
|
26
|
+
"ai",
|
|
27
|
+
"sdk",
|
|
28
|
+
"react-native",
|
|
29
|
+
"healthcare"
|
|
30
|
+
],
|
|
31
|
+
"author": "VORQARD Developer",
|
|
32
|
+
"license": "Abhivorn Technologies"
|
|
33
|
+
}
|
|
34
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
|
|
3
|
+
let client = null;
|
|
4
|
+
|
|
5
|
+
export const initSDKClient = (backendUrl, token) => {
|
|
6
|
+
client = axios.create({
|
|
7
|
+
baseURL: backendUrl,
|
|
8
|
+
headers: {
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
...(token ? { Authorization: `Bearer ${token}` } : {})
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const getSDKClient = () => {
|
|
16
|
+
if (!client) {
|
|
17
|
+
console.warn('VORQARD AI SDK: Client was not initialized. Call initSDKClient(backendUrl, token) first.');
|
|
18
|
+
// Fallback to basic client
|
|
19
|
+
client = axios.create();
|
|
20
|
+
}
|
|
21
|
+
return client;
|
|
22
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { DoctorTypeSelectionCard } from './cards/DoctorTypeSelectionCard';
|
|
2
|
+
export { HospitalSelectionList, HospitalProfileCard } from './cards/HospitalCards';
|
|
3
|
+
export { DoctorSelectionList, DoctorProfileCard } from './cards/DoctorCards';
|
|
4
|
+
export { SlotSelectionCard } from './cards/SlotSelectionCard';
|
|
5
|
+
export { BookingConfirmationCard } from './cards/BookingConfirmationCard';
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
TextInput,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
Image,
|
|
8
|
+
StyleSheet,
|
|
9
|
+
Dimensions,
|
|
10
|
+
ActivityIndicator,
|
|
11
|
+
Linking,
|
|
12
|
+
Platform,
|
|
13
|
+
} from 'react-native';
|
|
14
|
+
import Ionicons from 'react-native-vector-icons/Ionicons';
|
|
15
|
+
import LinearGradient from 'react-native-linear-gradient';
|
|
16
|
+
import { styles } from './ChatStyles';
|
|
17
|
+
import {
|
|
18
|
+
DoctorTypeSelectionCard,
|
|
19
|
+
HospitalSelectionList,
|
|
20
|
+
HospitalProfileCard,
|
|
21
|
+
DoctorSelectionList,
|
|
22
|
+
DoctorProfileCard,
|
|
23
|
+
SlotSelectionCard,
|
|
24
|
+
BookingConfirmationCard
|
|
25
|
+
} from './ChatCards';
|
|
26
|
+
|
|
27
|
+
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
|
28
|
+
|
|
29
|
+
// ─── Message Bubble ───────────────────────────────────────────────────────────
|
|
30
|
+
export const MessageBubble = ({ item, aiLogo, onOptionPress }) => {
|
|
31
|
+
const isUser = item.role === 'user';
|
|
32
|
+
|
|
33
|
+
const renderAssistantText = (content) => {
|
|
34
|
+
const parts = content.split(/(VORQARD AI|Vorqard AI)/gi);
|
|
35
|
+
return (
|
|
36
|
+
<Text style={styles.assistantText}>
|
|
37
|
+
{parts.map((part, i) =>
|
|
38
|
+
/VORQARD AI/i.test(part) ? (
|
|
39
|
+
<Text key={i} style={styles.highlightText}>{part}</Text>
|
|
40
|
+
) : (
|
|
41
|
+
<Text key={i}>{part}</Text>
|
|
42
|
+
)
|
|
43
|
+
)}
|
|
44
|
+
</Text>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// 7. Route UI Action to the correct Rich Component
|
|
49
|
+
const renderUIAction = () => {
|
|
50
|
+
if (!item.ui_action) return null;
|
|
51
|
+
const { type, title, options, doctor, hospital, selected_date } = item.ui_action;
|
|
52
|
+
|
|
53
|
+
if (type === 'selection') {
|
|
54
|
+
if (title === 'Select Doctor Type') {
|
|
55
|
+
return <DoctorTypeSelectionCard options={options} onOptionPress={onOptionPress} />;
|
|
56
|
+
}
|
|
57
|
+
if (title === 'Select Hospital') {
|
|
58
|
+
return <HospitalSelectionList options={options} onOptionPress={onOptionPress} />;
|
|
59
|
+
}
|
|
60
|
+
if (title === 'Select Doctor') {
|
|
61
|
+
return <DoctorSelectionList options={options} onOptionPress={onOptionPress} />;
|
|
62
|
+
}
|
|
63
|
+
// Fallback selection list
|
|
64
|
+
return (
|
|
65
|
+
<View style={styles.optionsContainer}>
|
|
66
|
+
{options.map((opt) => (
|
|
67
|
+
<TouchableOpacity
|
|
68
|
+
key={opt.id}
|
|
69
|
+
style={styles.optionButton}
|
|
70
|
+
onPress={() => onOptionPress?.(opt)}
|
|
71
|
+
activeOpacity={0.7}
|
|
72
|
+
>
|
|
73
|
+
<Text style={styles.optionButtonText}>{opt.label}</Text>
|
|
74
|
+
</TouchableOpacity>
|
|
75
|
+
))}
|
|
76
|
+
</View>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (type === 'doctor_profile') {
|
|
81
|
+
return <DoctorProfileCard doctor={doctor} options={options} onOptionPress={onOptionPress} />;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (type === 'hospital_profile') {
|
|
85
|
+
return <HospitalProfileCard hospital={hospital} options={options} onOptionPress={onOptionPress} />;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (type === 'slot_selection') {
|
|
89
|
+
return <SlotSelectionCard doctor={doctor} selectedDate={selected_date} options={options} onOptionPress={onOptionPress} />;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (type === 'booking_confirmation') {
|
|
93
|
+
return <BookingConfirmationCard ui={item.ui_action} onOptionPress={onOptionPress} />;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return null;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<View style={[styles.bubbleContainer, isUser ? styles.userAlign : styles.assistantAlign]}>
|
|
101
|
+
{!isUser && (
|
|
102
|
+
aiLogo ? (
|
|
103
|
+
<Image source={aiLogo} style={styles.avatarImage} />
|
|
104
|
+
) : (
|
|
105
|
+
<View style={styles.avatarFallback}>
|
|
106
|
+
<Ionicons name="sparkles" size={14} color="#FFF" />
|
|
107
|
+
</View>
|
|
108
|
+
)
|
|
109
|
+
)}
|
|
110
|
+
|
|
111
|
+
{isUser ? (
|
|
112
|
+
<LinearGradient
|
|
113
|
+
colors={['#6366F1', '#3B82F6']}
|
|
114
|
+
start={{ x: 0, y: 0 }}
|
|
115
|
+
end={{ x: 1, y: 1 }}
|
|
116
|
+
style={[styles.bubble, styles.userBubble]}
|
|
117
|
+
>
|
|
118
|
+
<Text style={styles.userText}>{item.content}</Text>
|
|
119
|
+
{item.timestamp && (
|
|
120
|
+
<Text style={styles.userTime}>
|
|
121
|
+
{new Date(item.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
|
122
|
+
</Text>
|
|
123
|
+
)}
|
|
124
|
+
</LinearGradient>
|
|
125
|
+
) : (
|
|
126
|
+
<View style={{ flexDirection: 'column', maxWidth: SCREEN_WIDTH * 0.76 }}>
|
|
127
|
+
<View style={[styles.bubble, styles.assistantBubble]}>
|
|
128
|
+
{renderAssistantText(item.content)}
|
|
129
|
+
{item.timestamp && (
|
|
130
|
+
<Text style={styles.assistantTime}>
|
|
131
|
+
{new Date(item.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
|
132
|
+
</Text>
|
|
133
|
+
)}
|
|
134
|
+
</View>
|
|
135
|
+
{renderUIAction()}
|
|
136
|
+
</View>
|
|
137
|
+
)}
|
|
138
|
+
</View>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// ─── Input Panel ──────────────────────────────────────────────────────────────
|
|
143
|
+
export const InputPanel = ({ value, onChangeText, onSend, onVoicePress, loading, aiLogo }) => {
|
|
144
|
+
const hasText = value.trim().length > 0;
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<View style={styles.inputOuterContainer}>
|
|
148
|
+
<LinearGradient
|
|
149
|
+
colors={['#6366F1', '#EC4899', '#F97316']}
|
|
150
|
+
start={{ x: 0, y: 0 }}
|
|
151
|
+
end={{ x: 1, y: 0 }}
|
|
152
|
+
style={styles.inputBorderGradient}
|
|
153
|
+
>
|
|
154
|
+
<View style={styles.inputInner}>
|
|
155
|
+
{aiLogo ? (
|
|
156
|
+
<Image source={aiLogo} style={{ width: 18, height: 18, marginLeft: 2, borderRadius: 4 }} />
|
|
157
|
+
) : (
|
|
158
|
+
<Ionicons name="sparkles" size={18} color="#6366F1" style={{ marginLeft: 2 }} />
|
|
159
|
+
)}
|
|
160
|
+
|
|
161
|
+
<TextInput
|
|
162
|
+
placeholder="Ask Vorqard AI about reports, history..."
|
|
163
|
+
placeholderTextColor="#94A3B8"
|
|
164
|
+
value={value}
|
|
165
|
+
onChangeText={onChangeText}
|
|
166
|
+
style={styles.textInput}
|
|
167
|
+
multiline
|
|
168
|
+
maxLength={500}
|
|
169
|
+
/>
|
|
170
|
+
|
|
171
|
+
<View style={styles.rightIcons}>
|
|
172
|
+
{!hasText && (
|
|
173
|
+
<TouchableOpacity style={styles.iconBtn} activeOpacity={0.7}>
|
|
174
|
+
<Ionicons name="attach" size={20} color="#94A3B8" />
|
|
175
|
+
</TouchableOpacity>
|
|
176
|
+
)}
|
|
177
|
+
|
|
178
|
+
{!hasText && (
|
|
179
|
+
<TouchableOpacity style={styles.iconBtn} onPress={onVoicePress} activeOpacity={0.7}>
|
|
180
|
+
<Ionicons name="mic" size={20} color="#94A3B8" />
|
|
181
|
+
</TouchableOpacity>
|
|
182
|
+
)}
|
|
183
|
+
|
|
184
|
+
<TouchableOpacity
|
|
185
|
+
onPress={() => onSend(value)}
|
|
186
|
+
disabled={!hasText || loading}
|
|
187
|
+
activeOpacity={0.8}
|
|
188
|
+
>
|
|
189
|
+
<LinearGradient
|
|
190
|
+
colors={hasText ? ['#10B981', '#3B82F6'] : ['#E2E8F0', '#E2E8F0']}
|
|
191
|
+
start={{ x: 0, y: 0 }}
|
|
192
|
+
end={{ x: 1, y: 1 }}
|
|
193
|
+
style={styles.sendButton}
|
|
194
|
+
>
|
|
195
|
+
{loading ? (
|
|
196
|
+
<ActivityIndicator color="#FFF" size="small" />
|
|
197
|
+
) : (
|
|
198
|
+
<Ionicons name="arrow-up" size={18} color={hasText ? '#FFF' : '#94A3B8'} />
|
|
199
|
+
)}
|
|
200
|
+
</LinearGradient>
|
|
201
|
+
</TouchableOpacity>
|
|
202
|
+
</View>
|
|
203
|
+
</View>
|
|
204
|
+
</LinearGradient>
|
|
205
|
+
</View>
|
|
206
|
+
);
|
|
207
|
+
};
|
|
208
|
+
|