source: lan-frontend/src/components/AIAssistantDrawer.jsx@ 2058e5c

Last change on this file since 2058e5c was 2058e5c, checked in by istevanoska <ilinastevanoska@…>, 6 months ago

Working / before login

  • Property mode set to 100644
File size: 3.6 KB
Line 
1import React from "react";
2
3import { useEffect, useRef, useState } from "react";
4
5export default function AIAssistantDrawer({ open, onClose, computers, scope, setScope }) {
6 const [messages, setMessages] = useState([
7 { role: "assistant", text: "Постави прашање за Sysmon, процеси, мрежа. Можеш да избереш компјутер (scope) горе." },
8 ]);
9 const [input, setInput] = useState("");
10 const [sending, setSending] = useState(false);
11 const bottomRef = useRef(null);
12
13 useEffect(() => {
14 if (bottomRef.current) bottomRef.current.scrollIntoView({ behavior: "smooth" });
15 }, [messages, open]);
16
17 async function send() {
18 const q = input.trim();
19 if (!q || sending) return;
20
21 setInput("");
22 setMessages((m) => [...m, { role: "user", text: q }]);
23 setSending(true);
24
25 try {
26 const r = await fetch("/api/chat", {
27 method: "POST",
28 headers: { "Content-Type": "application/json" },
29 body: JSON.stringify({ question: q, computer_name: scope || null }),
30 });
31 const data = await r.json();
32 setMessages((m) => [...m, { role: "assistant", text: data.answer || "(no answer)" }]);
33 } catch (e) {
34 setMessages((m) => [...m, { role: "assistant", text: `Грешка: ${String(e.message || e)}` }]);
35 } finally {
36 setSending(false);
37 }
38 }
39
40 if (!open) return null;
41
42 return (
43 <>
44 <div className="drawer-backdrop" onMouseDown={onClose} />
45 <div className="drawer" onMouseDown={(e) => e.stopPropagation()}>
46 <div className="drawer-head">
47 <div>
48 <div className="drawer-title">🤖 AI Assistant</div>
49 <div className="drawer-sub">Uses /api/chat (RAG over your logs)</div>
50 </div>
51 <button className="btn" onClick={onClose}>Close</button>
52 </div>
53
54 <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--border)" }}>
55 <div className="row">
56 <select className="select" value={scope || ""} onChange={(e) => setScope(e.target.value || null)}>
57 <option value="">All computers</option>
58 {(computers || []).map((c) => (
59 <option key={c.name} value={c.name}>{c.name}</option>
60 ))}
61 </select>
62 </div>
63 <div className="small">Tip: “Top процеси по RAM” или “сомнителни sysmon events”</div>
64 </div>
65
66 <div className="drawer-body">
67 {messages.map((m, i) => (
68 <div key={i} className={`bubble ${m.role === "user" ? "user" : ""}`}>
69 <div style={{ fontSize: 11, color: "var(--dim)", marginBottom: 6 }}>
70 {m.role === "user" ? "You" : "Assistant"}
71 </div>
72 <div style={{ whiteSpace: "pre-wrap" }}>{m.text}</div>
73 </div>
74 ))}
75 <div ref={bottomRef} />
76 </div>
77
78 <div className="drawer-foot">
79 <div className="row">
80 <textarea
81 className="textarea"
82 rows={2}
83 value={input}
84 placeholder="Постави прашање… (Enter = send, Shift+Enter нов ред)"
85 onChange={(e) => setInput(e.target.value)}
86 onKeyDown={(e) => {
87 if (e.key === "Enter" && !e.shiftKey) {
88 e.preventDefault();
89 send();
90 }
91 }}
92 />
93 <button className="btn primary" onClick={send} disabled={sending || !input.trim()}>
94 {sending ? "Sending…" : "Send"}
95 </button>
96 </div>
97 </div>
98 </div>
99 </>
100 );
101}
Note: See TracBrowser for help on using the repository browser.