Levin-Aleksey commited on
Commit
2f884df
·
1 Parent(s): 54b041c
package.json ADDED
File without changes
src/pages/index.astro ADDED
File without changes
src/pages/src/components/ChatWidget.tsx ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
+ import { useChat } from 'ai/react';
3
+ import { MessageCircle, X, Send, Loader2, Bot } from 'lucide-react';
4
+
5
+ export default function ChatWidget() {
6
+ const [isOpen, setIsOpen] = useState(false);
7
+
8
+ // Ссылка берется из настроек Space или .env файла
9
+ const apiEndpoint = import.meta.env.PUBLIC_CHAT_API_URL;
10
+
11
+ const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
12
+ api: apiEndpoint,
13
+ });
14
+
15
+ return (
16
+ <div className="fixed bottom-6 right-6 z-50 font-sans">
17
+ {isOpen && (
18
+ <div className="mb-4 w-[380px] h-[550px] bg-white rounded-3xl shadow-[0_20px_50px_rgba(0,0,0,0.15)] border border-slate-100 flex flex-col overflow-hidden animate-in fade-in zoom-in duration-200">
19
+ <div className="bg-slate-900 p-5 text-white flex items-center justify-between">
20
+ <div className="flex items-center gap-3">
21
+ <div className="bg-blue-600 p-2 rounded-xl"><Bot size={20} /></div>
22
+ <div>
23
+ <p className="font-semibold text-sm">Inventory AI</p>
24
+ <p className="text-[10px] text-slate-400">Gemini 1.5 Flash Online</p>
25
+ </div>
26
+ </div>
27
+ <button onClick={() => setIsOpen(false)} className="opacity-50 hover:opacity-100"><X size={20} /></button>
28
+ </div>
29
+
30
+ <div className="flex-1 overflow-y-auto p-4 space-y-4 bg-slate-50">
31
+ {messages.map((m) => (
32
+ <div key={m.id} className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}>
33
+ <div className={`max-w-[85%] px-4 py-2 rounded-2xl text-sm ${
34
+ m.role === 'user' ? 'bg-blue-600 text-white' : 'bg-white border border-slate-200 text-slate-800'
35
+ }`}>
36
+ {m.content}
37
+ </div>
38
+ </div>
39
+ ))}
40
+ {isLoading && <Loader2 className="animate-spin text-blue-600 mx-auto" size={20} />}
41
+ </div>
42
+
43
+ <form onSubmit={handleSubmit} className="p-4 bg-white border-t border-slate-100 flex gap-2">
44
+ <input
45
+ className="flex-1 bg-slate-100 rounded-xl px-4 py-2 text-sm outline-none focus:ring-2 focus:ring-blue-500"
46
+ value={input}
47
+ placeholder="Спросите об оборудовании..."
48
+ onChange={handleInputChange}
49
+ />
50
+ <button type="submit" className="bg-slate-900 text-white p-2 rounded-xl"><Send size={18} /></button>
51
+ </form>
52
+ </div>
53
+ )}
54
+
55
+ <button
56
+ onClick={() => setIsOpen(!isOpen)}
57
+ className="bg-slate-900 text-white w-16 h-16 rounded-full shadow-2xl flex items-center justify-center hover:scale-105 transition-transform"
58
+ >
59
+ {isOpen ? <X size={28} /> : <MessageCircle size={28} />}
60
+ </button>
61
+ </div>
62
+ );
63
+ }