nextjs-chatbot-ui 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/LICENSE +21 -0
- package/README.md +317 -0
- package/components/AdminSetup.tsx +566 -0
- package/components/Chatbot.tsx +465 -0
- package/index.tsx +5 -0
- package/package.json +62 -0
- package/types/admin.ts +29 -0
- package/types/index.ts +51 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { DatabaseType, DatabaseConnection, ColumnSelection, DatabaseConfig, AdminSetupProps } from '../types/admin';
|
|
5
|
+
import clsx from 'clsx';
|
|
6
|
+
|
|
7
|
+
const AdminSetup: React.FC<AdminSetupProps> = ({
|
|
8
|
+
onSave,
|
|
9
|
+
onTestConnection,
|
|
10
|
+
onFetchColumns,
|
|
11
|
+
}) => {
|
|
12
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
13
|
+
const [currentStep, setCurrentStep] = useState<'connection' | 'columns'>('connection');
|
|
14
|
+
const [isConnecting, setIsConnecting] = useState(false);
|
|
15
|
+
const [connectionError, setConnectionError] = useState<string | null>(null);
|
|
16
|
+
const [availableColumns, setAvailableColumns] = useState<string[]>([]);
|
|
17
|
+
const [isLoadingColumns, setIsLoadingColumns] = useState(false);
|
|
18
|
+
|
|
19
|
+
const [dbType, setDbType] = useState<DatabaseType>('mongodb');
|
|
20
|
+
const [connection, setConnection] = useState<DatabaseConnection>({
|
|
21
|
+
type: 'mongodb',
|
|
22
|
+
host: '',
|
|
23
|
+
port: 27017,
|
|
24
|
+
database: '',
|
|
25
|
+
username: '',
|
|
26
|
+
password: '',
|
|
27
|
+
connectionString: '',
|
|
28
|
+
ssl: false,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const [columnSelection, setColumnSelection] = useState<ColumnSelection>({
|
|
32
|
+
embeddingColumns: [],
|
|
33
|
+
llmColumns: [],
|
|
34
|
+
chromaColumns: [],
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const handleDbTypeChange = (type: DatabaseType) => {
|
|
38
|
+
setDbType(type);
|
|
39
|
+
setConnection({
|
|
40
|
+
...connection,
|
|
41
|
+
type,
|
|
42
|
+
port: type === 'mongodb' ? 27017 : 5432,
|
|
43
|
+
});
|
|
44
|
+
setConnectionError(null);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const handleConnectionChange = (field: keyof DatabaseConnection, value: any) => {
|
|
48
|
+
setConnection({
|
|
49
|
+
...connection,
|
|
50
|
+
[field]: value,
|
|
51
|
+
});
|
|
52
|
+
setConnectionError(null);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleTestConnection = async () => {
|
|
56
|
+
if (!onTestConnection) {
|
|
57
|
+
// Default test - just validate fields
|
|
58
|
+
if (!connection.host || !connection.database) {
|
|
59
|
+
setConnectionError('Please fill in all required fields');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
setConnectionError(null);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setIsConnecting(true);
|
|
67
|
+
setConnectionError(null);
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const isValid = await onTestConnection(connection);
|
|
71
|
+
if (isValid) {
|
|
72
|
+
setConnectionError(null);
|
|
73
|
+
// Fetch columns after successful connection
|
|
74
|
+
await handleFetchColumns();
|
|
75
|
+
} else {
|
|
76
|
+
setConnectionError('Connection failed. Please check your credentials.');
|
|
77
|
+
}
|
|
78
|
+
} catch (error: any) {
|
|
79
|
+
setConnectionError(error.message || 'Connection failed. Please try again.');
|
|
80
|
+
} finally {
|
|
81
|
+
setIsConnecting(false);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const handleFetchColumns = async () => {
|
|
86
|
+
if (!onFetchColumns) {
|
|
87
|
+
// Mock columns for demo
|
|
88
|
+
setAvailableColumns(['id', 'title', 'content', 'description', 'category', 'tags', 'created_at', 'updated_at']);
|
|
89
|
+
setIsLoadingColumns(false);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
setIsLoadingColumns(true);
|
|
94
|
+
try {
|
|
95
|
+
const columns = await onFetchColumns(connection);
|
|
96
|
+
setAvailableColumns(columns);
|
|
97
|
+
} catch (error: any) {
|
|
98
|
+
setConnectionError(error.message || 'Failed to fetch columns');
|
|
99
|
+
} finally {
|
|
100
|
+
setIsLoadingColumns(false);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const handleConnectAndNext = async () => {
|
|
105
|
+
// Validate connection fields
|
|
106
|
+
if (dbType === 'mongodb') {
|
|
107
|
+
if (!connection.connectionString && (!connection.host || !connection.database)) {
|
|
108
|
+
setConnectionError('Please provide connection string or host and database');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
if (!connection.host || !connection.database || !connection.username || !connection.password) {
|
|
113
|
+
setConnectionError('Please fill in all required fields');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Test connection
|
|
119
|
+
await handleTestConnection();
|
|
120
|
+
|
|
121
|
+
// If no error, move to next step
|
|
122
|
+
if (!connectionError) {
|
|
123
|
+
setCurrentStep('columns');
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const handleColumnToggle = (column: string, category: keyof ColumnSelection) => {
|
|
128
|
+
setColumnSelection((prev) => {
|
|
129
|
+
const currentColumns = prev[category];
|
|
130
|
+
const isSelected = currentColumns.includes(column);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
...prev,
|
|
134
|
+
[category]: isSelected
|
|
135
|
+
? currentColumns.filter((c) => c !== column)
|
|
136
|
+
: [...currentColumns, column],
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const handleSave = () => {
|
|
142
|
+
const config: DatabaseConfig = {
|
|
143
|
+
connection,
|
|
144
|
+
columnSelection,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
if (onSave) {
|
|
148
|
+
onSave(config);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Close modal and reset
|
|
152
|
+
setIsModalOpen(false);
|
|
153
|
+
setCurrentStep('connection');
|
|
154
|
+
setConnectionError(null);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const handleClose = () => {
|
|
158
|
+
setIsModalOpen(false);
|
|
159
|
+
setCurrentStep('connection');
|
|
160
|
+
setConnectionError(null);
|
|
161
|
+
setColumnSelection({
|
|
162
|
+
embeddingColumns: [],
|
|
163
|
+
llmColumns: [],
|
|
164
|
+
chromaColumns: [],
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<>
|
|
170
|
+
{/* Sidebar Button/Item - This can be integrated into admin sidebar */}
|
|
171
|
+
<button
|
|
172
|
+
onClick={() => setIsModalOpen(true)}
|
|
173
|
+
className="w-full flex items-center gap-3 px-4 py-2.5 text-left hover:bg-gray-100 rounded-lg transition-colors"
|
|
174
|
+
>
|
|
175
|
+
<svg
|
|
176
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
177
|
+
className="h-5 w-5 text-gray-600"
|
|
178
|
+
fill="none"
|
|
179
|
+
viewBox="0 0 24 24"
|
|
180
|
+
stroke="currentColor"
|
|
181
|
+
>
|
|
182
|
+
<path
|
|
183
|
+
strokeLinecap="round"
|
|
184
|
+
strokeLinejoin="round"
|
|
185
|
+
strokeWidth={2}
|
|
186
|
+
d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"
|
|
187
|
+
/>
|
|
188
|
+
</svg>
|
|
189
|
+
<span className="text-sm font-medium text-gray-700">Database Setup</span>
|
|
190
|
+
</button>
|
|
191
|
+
|
|
192
|
+
{/* Modal */}
|
|
193
|
+
{isModalOpen && (
|
|
194
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
|
|
195
|
+
<div className="bg-white rounded-lg shadow-xl w-full max-w-2xl max-h-[90vh] overflow-hidden flex flex-col">
|
|
196
|
+
{/* Modal Header */}
|
|
197
|
+
<div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
|
|
198
|
+
<h2 className="text-xl font-semibold text-gray-900">
|
|
199
|
+
{currentStep === 'connection' ? 'Database Connection' : 'Select Columns'}
|
|
200
|
+
</h2>
|
|
201
|
+
<button
|
|
202
|
+
onClick={handleClose}
|
|
203
|
+
className="text-gray-400 hover:text-gray-600 transition-colors"
|
|
204
|
+
>
|
|
205
|
+
<svg
|
|
206
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
207
|
+
className="h-6 w-6"
|
|
208
|
+
fill="none"
|
|
209
|
+
viewBox="0 0 24 24"
|
|
210
|
+
stroke="currentColor"
|
|
211
|
+
>
|
|
212
|
+
<path
|
|
213
|
+
strokeLinecap="round"
|
|
214
|
+
strokeLinejoin="round"
|
|
215
|
+
strokeWidth={2}
|
|
216
|
+
d="M6 18L18 6M6 6l12 12"
|
|
217
|
+
/>
|
|
218
|
+
</svg>
|
|
219
|
+
</button>
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
{/* Modal Content */}
|
|
223
|
+
<div className="flex-1 overflow-y-auto px-6 py-4">
|
|
224
|
+
{currentStep === 'connection' ? (
|
|
225
|
+
<div className="space-y-6">
|
|
226
|
+
{/* Database Type Selection */}
|
|
227
|
+
<div>
|
|
228
|
+
<label className="block text-sm font-medium text-gray-700 mb-3">
|
|
229
|
+
Database Type
|
|
230
|
+
</label>
|
|
231
|
+
<div className="grid grid-cols-2 gap-4">
|
|
232
|
+
<button
|
|
233
|
+
onClick={() => handleDbTypeChange('mongodb')}
|
|
234
|
+
className={clsx(
|
|
235
|
+
'p-4 border-2 rounded-lg transition-all text-left',
|
|
236
|
+
dbType === 'mongodb'
|
|
237
|
+
? 'border-blue-500 bg-blue-50'
|
|
238
|
+
: 'border-gray-200 hover:border-gray-300'
|
|
239
|
+
)}
|
|
240
|
+
>
|
|
241
|
+
<div className="flex items-center gap-3">
|
|
242
|
+
<div className={clsx(
|
|
243
|
+
'w-5 h-5 rounded-full border-2 flex items-center justify-center',
|
|
244
|
+
dbType === 'mongodb' ? 'border-blue-500' : 'border-gray-300'
|
|
245
|
+
)}>
|
|
246
|
+
{dbType === 'mongodb' && (
|
|
247
|
+
<div className="w-3 h-3 rounded-full bg-blue-500" />
|
|
248
|
+
)}
|
|
249
|
+
</div>
|
|
250
|
+
<div>
|
|
251
|
+
<div className="font-semibold text-gray-900">MongoDB</div>
|
|
252
|
+
<div className="text-xs text-gray-500">NoSQL Database</div>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
</button>
|
|
256
|
+
<button
|
|
257
|
+
onClick={() => handleDbTypeChange('postgres')}
|
|
258
|
+
className={clsx(
|
|
259
|
+
'p-4 border-2 rounded-lg transition-all text-left',
|
|
260
|
+
dbType === 'postgres'
|
|
261
|
+
? 'border-blue-500 bg-blue-50'
|
|
262
|
+
: 'border-gray-200 hover:border-gray-300'
|
|
263
|
+
)}
|
|
264
|
+
>
|
|
265
|
+
<div className="flex items-center gap-3">
|
|
266
|
+
<div className={clsx(
|
|
267
|
+
'w-5 h-5 rounded-full border-2 flex items-center justify-center',
|
|
268
|
+
dbType === 'postgres' ? 'border-blue-500' : 'border-gray-300'
|
|
269
|
+
)}>
|
|
270
|
+
{dbType === 'postgres' && (
|
|
271
|
+
<div className="w-3 h-3 rounded-full bg-blue-500" />
|
|
272
|
+
)}
|
|
273
|
+
</div>
|
|
274
|
+
<div>
|
|
275
|
+
<div className="font-semibold text-gray-900">PostgreSQL</div>
|
|
276
|
+
<div className="text-xs text-gray-500">SQL Database</div>
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
</button>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
{/* Connection Fields */}
|
|
284
|
+
{dbType === 'mongodb' ? (
|
|
285
|
+
<div className="space-y-4">
|
|
286
|
+
<div>
|
|
287
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
288
|
+
Connection String (Recommended)
|
|
289
|
+
</label>
|
|
290
|
+
<input
|
|
291
|
+
type="text"
|
|
292
|
+
value={connection.connectionString || ''}
|
|
293
|
+
onChange={(e) => handleConnectionChange('connectionString', e.target.value)}
|
|
294
|
+
placeholder="mongodb://username:password@host:port/database"
|
|
295
|
+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
296
|
+
/>
|
|
297
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
298
|
+
Or fill individual fields below
|
|
299
|
+
</p>
|
|
300
|
+
</div>
|
|
301
|
+
<div className="border-t border-gray-200 pt-4">
|
|
302
|
+
<div className="grid grid-cols-2 gap-4">
|
|
303
|
+
<div>
|
|
304
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
305
|
+
Host *
|
|
306
|
+
</label>
|
|
307
|
+
<input
|
|
308
|
+
type="text"
|
|
309
|
+
value={connection.host}
|
|
310
|
+
onChange={(e) => handleConnectionChange('host', e.target.value)}
|
|
311
|
+
placeholder="localhost"
|
|
312
|
+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
313
|
+
/>
|
|
314
|
+
</div>
|
|
315
|
+
<div>
|
|
316
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
317
|
+
Port *
|
|
318
|
+
</label>
|
|
319
|
+
<input
|
|
320
|
+
type="number"
|
|
321
|
+
value={connection.port}
|
|
322
|
+
onChange={(e) => handleConnectionChange('port', parseInt(e.target.value) || 27017)}
|
|
323
|
+
placeholder="27017"
|
|
324
|
+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
325
|
+
/>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
<div className="mt-4">
|
|
329
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
330
|
+
Database Name *
|
|
331
|
+
</label>
|
|
332
|
+
<input
|
|
333
|
+
type="text"
|
|
334
|
+
value={connection.database}
|
|
335
|
+
onChange={(e) => handleConnectionChange('database', e.target.value)}
|
|
336
|
+
placeholder="my_database"
|
|
337
|
+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
338
|
+
/>
|
|
339
|
+
</div>
|
|
340
|
+
<div className="mt-4">
|
|
341
|
+
<label className="flex items-center gap-2">
|
|
342
|
+
<input
|
|
343
|
+
type="checkbox"
|
|
344
|
+
checked={connection.ssl || false}
|
|
345
|
+
onChange={(e) => handleConnectionChange('ssl', e.target.checked)}
|
|
346
|
+
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
|
347
|
+
/>
|
|
348
|
+
<span className="text-sm text-gray-700">Enable SSL</span>
|
|
349
|
+
</label>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
) : (
|
|
354
|
+
<div className="space-y-4">
|
|
355
|
+
<div className="grid grid-cols-2 gap-4">
|
|
356
|
+
<div>
|
|
357
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
358
|
+
Host *
|
|
359
|
+
</label>
|
|
360
|
+
<input
|
|
361
|
+
type="text"
|
|
362
|
+
value={connection.host}
|
|
363
|
+
onChange={(e) => handleConnectionChange('host', e.target.value)}
|
|
364
|
+
placeholder="localhost"
|
|
365
|
+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
366
|
+
/>
|
|
367
|
+
</div>
|
|
368
|
+
<div>
|
|
369
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
370
|
+
Port *
|
|
371
|
+
</label>
|
|
372
|
+
<input
|
|
373
|
+
type="number"
|
|
374
|
+
value={connection.port}
|
|
375
|
+
onChange={(e) => handleConnectionChange('port', parseInt(e.target.value) || 5432)}
|
|
376
|
+
placeholder="5432"
|
|
377
|
+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
378
|
+
/>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
<div>
|
|
382
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
383
|
+
Database Name *
|
|
384
|
+
</label>
|
|
385
|
+
<input
|
|
386
|
+
type="text"
|
|
387
|
+
value={connection.database}
|
|
388
|
+
onChange={(e) => handleConnectionChange('database', e.target.value)}
|
|
389
|
+
placeholder="my_database"
|
|
390
|
+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
391
|
+
/>
|
|
392
|
+
</div>
|
|
393
|
+
<div className="grid grid-cols-2 gap-4">
|
|
394
|
+
<div>
|
|
395
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
396
|
+
Username *
|
|
397
|
+
</label>
|
|
398
|
+
<input
|
|
399
|
+
type="text"
|
|
400
|
+
value={connection.username || ''}
|
|
401
|
+
onChange={(e) => handleConnectionChange('username', e.target.value)}
|
|
402
|
+
placeholder="postgres"
|
|
403
|
+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
404
|
+
/>
|
|
405
|
+
</div>
|
|
406
|
+
<div>
|
|
407
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
408
|
+
Password *
|
|
409
|
+
</label>
|
|
410
|
+
<input
|
|
411
|
+
type="password"
|
|
412
|
+
value={connection.password || ''}
|
|
413
|
+
onChange={(e) => handleConnectionChange('password', e.target.value)}
|
|
414
|
+
placeholder="••••••••"
|
|
415
|
+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
416
|
+
/>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
<div>
|
|
420
|
+
<label className="flex items-center gap-2">
|
|
421
|
+
<input
|
|
422
|
+
type="checkbox"
|
|
423
|
+
checked={connection.ssl || false}
|
|
424
|
+
onChange={(e) => handleConnectionChange('ssl', e.target.checked)}
|
|
425
|
+
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
|
426
|
+
/>
|
|
427
|
+
<span className="text-sm text-gray-700">Enable SSL</span>
|
|
428
|
+
</label>
|
|
429
|
+
</div>
|
|
430
|
+
</div>
|
|
431
|
+
)}
|
|
432
|
+
|
|
433
|
+
{connectionError && (
|
|
434
|
+
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
|
|
435
|
+
<p className="text-sm text-red-800">{connectionError}</p>
|
|
436
|
+
</div>
|
|
437
|
+
)}
|
|
438
|
+
</div>
|
|
439
|
+
) : (
|
|
440
|
+
<div className="space-y-6">
|
|
441
|
+
{isLoadingColumns ? (
|
|
442
|
+
<div className="flex items-center justify-center py-8">
|
|
443
|
+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
|
444
|
+
</div>
|
|
445
|
+
) : (
|
|
446
|
+
<>
|
|
447
|
+
<div>
|
|
448
|
+
<p className="text-sm text-gray-600 mb-4">
|
|
449
|
+
Select which columns to use for embedding, LLM processing, and ChromaDB storage.
|
|
450
|
+
</p>
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
{/* Embedding Columns */}
|
|
454
|
+
<div>
|
|
455
|
+
<label className="block text-sm font-medium text-gray-700 mb-3">
|
|
456
|
+
Embedding Columns
|
|
457
|
+
</label>
|
|
458
|
+
<div className="grid grid-cols-2 gap-2 max-h-32 overflow-y-auto border border-gray-200 rounded-lg p-3">
|
|
459
|
+
{availableColumns.map((column) => (
|
|
460
|
+
<label
|
|
461
|
+
key={`embedding-${column}`}
|
|
462
|
+
className="flex items-center gap-2 cursor-pointer hover:bg-gray-50 p-2 rounded"
|
|
463
|
+
>
|
|
464
|
+
<input
|
|
465
|
+
type="checkbox"
|
|
466
|
+
checked={columnSelection.embeddingColumns.includes(column)}
|
|
467
|
+
onChange={() => handleColumnToggle(column, 'embeddingColumns')}
|
|
468
|
+
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
|
469
|
+
/>
|
|
470
|
+
<span className="text-sm text-gray-700">{column}</span>
|
|
471
|
+
</label>
|
|
472
|
+
))}
|
|
473
|
+
</div>
|
|
474
|
+
</div>
|
|
475
|
+
|
|
476
|
+
{/* LLM Columns */}
|
|
477
|
+
<div>
|
|
478
|
+
<label className="block text-sm font-medium text-gray-700 mb-3">
|
|
479
|
+
LLM Columns
|
|
480
|
+
</label>
|
|
481
|
+
<div className="grid grid-cols-2 gap-2 max-h-32 overflow-y-auto border border-gray-200 rounded-lg p-3">
|
|
482
|
+
{availableColumns.map((column) => (
|
|
483
|
+
<label
|
|
484
|
+
key={`llm-${column}`}
|
|
485
|
+
className="flex items-center gap-2 cursor-pointer hover:bg-gray-50 p-2 rounded"
|
|
486
|
+
>
|
|
487
|
+
<input
|
|
488
|
+
type="checkbox"
|
|
489
|
+
checked={columnSelection.llmColumns.includes(column)}
|
|
490
|
+
onChange={() => handleColumnToggle(column, 'llmColumns')}
|
|
491
|
+
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
|
492
|
+
/>
|
|
493
|
+
<span className="text-sm text-gray-700">{column}</span>
|
|
494
|
+
</label>
|
|
495
|
+
))}
|
|
496
|
+
</div>
|
|
497
|
+
</div>
|
|
498
|
+
|
|
499
|
+
{/* ChromaDB Columns */}
|
|
500
|
+
<div>
|
|
501
|
+
<label className="block text-sm font-medium text-gray-700 mb-3">
|
|
502
|
+
ChromaDB Columns
|
|
503
|
+
</label>
|
|
504
|
+
<div className="grid grid-cols-2 gap-2 max-h-32 overflow-y-auto border border-gray-200 rounded-lg p-3">
|
|
505
|
+
{availableColumns.map((column) => (
|
|
506
|
+
<label
|
|
507
|
+
key={`chroma-${column}`}
|
|
508
|
+
className="flex items-center gap-2 cursor-pointer hover:bg-gray-50 p-2 rounded"
|
|
509
|
+
>
|
|
510
|
+
<input
|
|
511
|
+
type="checkbox"
|
|
512
|
+
checked={columnSelection.chromaColumns.includes(column)}
|
|
513
|
+
onChange={() => handleColumnToggle(column, 'chromaColumns')}
|
|
514
|
+
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
|
515
|
+
/>
|
|
516
|
+
<span className="text-sm text-gray-700">{column}</span>
|
|
517
|
+
</label>
|
|
518
|
+
))}
|
|
519
|
+
</div>
|
|
520
|
+
</div>
|
|
521
|
+
</>
|
|
522
|
+
)}
|
|
523
|
+
</div>
|
|
524
|
+
)}
|
|
525
|
+
</div>
|
|
526
|
+
|
|
527
|
+
{/* Modal Footer */}
|
|
528
|
+
<div className="px-6 py-4 border-t border-gray-200 flex items-center justify-between">
|
|
529
|
+
<button
|
|
530
|
+
onClick={currentStep === 'columns' ? () => setCurrentStep('connection') : handleClose}
|
|
531
|
+
className="px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded-lg transition-colors"
|
|
532
|
+
>
|
|
533
|
+
{currentStep === 'columns' ? 'Back' : 'Cancel'}
|
|
534
|
+
</button>
|
|
535
|
+
<div className="flex gap-3">
|
|
536
|
+
{currentStep === 'connection' ? (
|
|
537
|
+
<button
|
|
538
|
+
onClick={handleConnectAndNext}
|
|
539
|
+
disabled={isConnecting || isLoadingColumns}
|
|
540
|
+
className="px-6 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
541
|
+
>
|
|
542
|
+
{isConnecting ? 'Connecting...' : 'Connect & Next'}
|
|
543
|
+
</button>
|
|
544
|
+
) : (
|
|
545
|
+
<button
|
|
546
|
+
onClick={handleSave}
|
|
547
|
+
disabled={
|
|
548
|
+
columnSelection.embeddingColumns.length === 0 &&
|
|
549
|
+
columnSelection.llmColumns.length === 0 &&
|
|
550
|
+
columnSelection.chromaColumns.length === 0
|
|
551
|
+
}
|
|
552
|
+
className="px-6 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
553
|
+
>
|
|
554
|
+
Save Configuration
|
|
555
|
+
</button>
|
|
556
|
+
)}
|
|
557
|
+
</div>
|
|
558
|
+
</div>
|
|
559
|
+
</div>
|
|
560
|
+
</div>
|
|
561
|
+
)}
|
|
562
|
+
</>
|
|
563
|
+
);
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
export default AdminSetup;
|