latest changes

This commit is contained in:
2026-06-03 17:21:38 +05:30
parent 7e29b6acb7
commit b709add3b6
4 changed files with 508 additions and 184 deletions
+1
View File
@@ -8,6 +8,7 @@ const api = axios.create({
api.interceptors.request.use((config) => { api.interceptors.request.use((config) => {
const token = localStorage.getItem("token"); const token = localStorage.getItem("token");
console.log("Attaching token to request:", token);
config.headers = config.headers || {}; config.headers = config.headers || {};
+50 -32
View File
@@ -28,7 +28,10 @@ import {
FileText, FileText,
ImageIcon, ImageIcon,
RefreshCcw, RefreshCcw,
Sparkles, Sparkles,
CheckCircle2,
BarChart3,
Database,
} from "lucide-react"; } from "lucide-react";
import ErrorBoundary from "@/components/ErrorBoundary"; import ErrorBoundary from "@/components/ErrorBoundary";
@@ -785,40 +788,55 @@ function VegaLiteArtifact({
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
{/* TOP TABS */} {/* TOP TABS */}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <button
onClick={() => setActiveTab("answer")} onClick={() => setActiveTab("answer")}
className={`rounded-xl px-4 py-2 text-sm font-medium transition ${activeTab === "answer" className={`flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-medium transition ${
? "bg-cyan-500 text-white" activeTab === "answer"
: "bg-white/10 text-slate-300 hover:bg-white/20" ? "bg-cyan-500 text-white"
}`} : "bg-white border border-slate-200 text-black hover:bg-slate-50"
> }`}
Answer >
</button> <CheckCircle2
size={16}
className={activeTab === "answer" ? "text-white" : "text-green-600"}
/>
Answer
</button>
<button <button
onClick={() => setActiveTab("chart")} onClick={() => setActiveTab("chart")}
className={`rounded-xl px-4 py-2 text-sm font-medium transition ${activeTab === "chart" className={`flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-medium transition ${
? "bg-cyan-500 text-white" activeTab === "chart"
: "bg-white/10 text-slate-300 hover:bg-white/20" ? "bg-cyan-500 text-white"
}`} : "bg-white border border-slate-200 text-black hover:bg-slate-50"
> }`}
Chart >
</button> <BarChart3
size={16}
className={activeTab === "chart" ? "text-white" : "text-blue-600"}
/>
Chart
</button>
<button <button
onClick={() => setActiveTab("sql")} onClick={() => setActiveTab("sql")}
className={`rounded-xl px-4 py-2 text-sm font-medium transition ${activeTab === "sql" className={`flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-medium transition ${
? "bg-cyan-500 text-white" activeTab === "sql"
: "bg-white/10 text-slate-300 hover:bg-white/20" ? "bg-cyan-500 text-white"
}`} : "bg-white border border-slate-200 text-black hover:bg-slate-50"
> }`}
SQL >
</button> <Database
</div> size={16}
className={activeTab === "sql" ? "text-white" : "text-orange-600"}
/>
SQL
</button>
</div>
{/* EXPORT BUTTONS */} {/* EXPORT BUTTONS */}
<div className="flex flex-wrap gap-3"> {/* <div className="flex flex-wrap gap-3">
<Button <Button
onClick={downloadPDF} onClick={downloadPDF}
className="rounded-xl bg-red-500 hover:bg-red-600" className="rounded-xl bg-red-500 hover:bg-red-600"
@@ -850,7 +868,7 @@ function VegaLiteArtifact({
<ImageIcon className="mr-2 h-4 w-4" /> <ImageIcon className="mr-2 h-4 w-4" />
PNG PNG
</Button> </Button>
</div> </div> */}
</div> </div>
+448 -149
View File
@@ -13,6 +13,7 @@ type ChatMessage = {
role: "user" | "assistant"; role: "user" | "assistant";
content: unknown; content: unknown;
error?: boolean; error?: boolean;
clarification?: boolean;
}; };
function createId() { function createId() {
@@ -34,11 +35,11 @@ function createMessage(role: ChatMessage["role"], content: unknown): ChatMessage
export default function ChatCanvas() { export default function ChatCanvas() {
const [input, setInput] = useState(""); const [input, setInput] = useState("");
const [messages, setMessages] = useState<ChatMessage[]>([]); const [messages, setMessages] = useState<ChatMessage[]>([]);
// const [selectedHistoryId, setSelectedHistoryId] = useState<string | null>(null);
const [error, setError] = useState(""); const [error, setError] = useState("");
const [isSending, setIsSending] = useState(false); const [isSending, setIsSending] = useState(false);
const [showHistory, setShowHistory] = useState(true); const [showHistory, setShowHistory] = useState(true);
const [historyQuestions, setHistoryQuestions] = useState<any[]>([]); const [historyQuestions, setHistoryQuestions] = useState<any[]>([]);
const [showProfileMenu, setShowProfileMenu] = useState(false);
const bottomRef = useRef<HTMLDivElement>(null); const bottomRef = useRef<HTMLDivElement>(null);
@@ -52,28 +53,101 @@ export default function ChatCanvas() {
loadHistory(); loadHistory();
}, []); }, []);
// const loadHistory = async () => {
// try {
// const token = localStorage.getItem("token");
// const res = await api.get("/fetchUserHistory", {
// headers: {
// Authorization: `Bearer ${token}`,
// },
// });
// console.log("History Response =>", res.data);
// if (res.data.success) {
// setHistoryQuestions(res.data.data);
// }
// } catch (err) {
// console.error("History Error =>", err);
// }
// };
const loadHistory = async () => { const loadHistory = async () => {
try { try {
const res = await api.get("/suggestions"); const res = await api.get("/fetchUserHistory");
console.log("History Response =>", res.data);
if (res.data.success) { if (res.data.success) {
setHistoryQuestions(res.data.questions); setHistoryQuestions(res.data.data);
} }
} catch (err) { } catch (err) {
console.error(err); console.error("History Error =>", err);
} }
}; };
// const history = messages.filter((message) => message.role === "user"); // const handleHistoryClick = async (item: any) => {
// try {
// const token = localStorage.getItem("token");
// const handleSelectHistory = (message: ChatMessage) => { // const response = await api.post(
// if (typeof message.content === "string") { // "/fetchHistory",
// setInput(message.content); // {
// setSelectedHistoryId(message.id); // response_id: item.response_id,
// setError(""); // },
// {
// headers: {
// Authorization: `Bearer ${token}`,
// "Content-Type": "application/json",
// },
// }
// );
// console.log("History Detail =>", response.data);
// if (response.data.success) {
// const historyData = response.data.data;
// setMessages([
// createMessage("user", historyData.prompt),
// createMessage("assistant", historyData),
// ]);
// }
// } catch (err) {
// console.error("Fetch History Error", err);
// } // }
// }; // };
const handleHistoryClick = async (item: any) => {
try {
const response = await api.post(
"/fetchHistory",
{
response_id: item.response_id,
}
);
console.log("History Detail =>", response.data);
if (response.data.success) {
const historyData = response.data.data;
setMessages([
createMessage("user", historyData.prompt),
createMessage("assistant", historyData),
]);
}
} catch (err) {
console.error("Fetch History Error", err);
}
};
const onSubmit = async (event: React.FormEvent) => { const onSubmit = async (event: React.FormEvent) => {
event.preventDefault(); event.preventDefault();
@@ -96,10 +170,29 @@ export default function ChatCanvas() {
JSON.stringify(response.data, null, 2) JSON.stringify(response.data, null, 2)
); );
setMessages((current) => [ // setMessages((current) => [
...current, // ...current,
createMessage("assistant", response.data), // createMessage("assistant", response.data),
]); // ]);
if (response.data.type === "clarification") {
setMessages((current) => [
...current,
{
id: createId(),
role: "assistant",
clarification: true,
content: response.data.message,
},
]);
} else {
setMessages((current) => [
...current,
createMessage("assistant", response.data),
]);
}
} }
catch (err) { catch (err) {
console.error(err); console.error(err);
@@ -146,140 +239,260 @@ export default function ChatCanvas() {
}; };
return ( return (
// <main className="min-h-screen bg-muted/30 pb-36"> <main className="min-h-screen bg-gradient-to-br from-white via-slate-50 to-gray-100 pb-36">
<main className="min-h-screen bg-gradient-to-br from-white via-slate-50 to-gray-100 pb-36">
{/* <header className="sticky top-0 z-10 border-b bg-background px-6 py-3">
<div className="flex items-center justify-between">
<div>
<h1 className="text-lg font-semibold">GenBI</h1>
<p className="text-xs text-muted-foreground">
Chat workspace
</p>
</div>
<Button
onClick={logout}
className="h-9"
>
Logout
</Button>
</div>
</header> */}
{/* <header className="sticky top-0 z-10 border-b bg-background px-6 py-3"> */} {/* <header className="sticky top-0 z-10 border-b bg-background px-6 py-3"> */}
<header className="sticky top-0 z-50 border-b border-slate-200/70 bg-white/80 backdrop-blur-xl px-8 py-4 shadow-sm"> <header className="sticky top-0 z-50 border-b border-slate-200 bg-white px-6 py-3">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold bg-gradient-to-r from-indigo-600 to-cyan-600 bg-clip-text text-transparent">GenBI</h1>
<p className="text-sm text-slate-500">
Chat workspace
</p>
<div className="flex items-center gap-2 mt-3"> <div className="flex items-center gap-3">
<button <button
onClick={() => setShowHistory(!showHistory)} onClick={() => setShowHistory(!showHistory)}
className="rounded-xl border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-sm transition-all hover:-translate-y-0.5 hover:bg-slate-50 hover:shadow-md" className="rounded-lg border px-3 py-1.5 text-sm hover:bg-slate-100"
>
{showHistory ? "Hide History" : "Show History"}
</button>
</div>
</div>
<div className="flex items-center gap-2">
{/* <Button
onClick={() => setShowHistory(!showHistory)}
className="border bg-white text-black hover:bg-slate-100"
>
{showHistory ? "Hide History" : "Show History"}
</Button> */}
<Button
onClick={logout}
className="h-9"
> >
Logout
</Button> </button>
<h1 className="text-xl font-semibold">
Yoda
</h1>
</div> </div>
</div> </div>
</header> </header>
{/* <section className="mx-auto flex flex-col gap-4 px-4 py-6 lg:flex-row"> */} {/* <section className="mx-auto flex flex-col gap-4 px-4 py-6 lg:flex-row"> */}
<section className="mx-auto flex gap-6 px-4 py-6"> <section className="mx-auto flex gap-6 px-4 py-6">
{showHistory && (<aside {showHistory && (
className="hidden lg:flex lg:w-80 lg:flex-col lg:sticky lg:top-24 h-[calc(100vh-140px)] overflow-y-auto rounded-3xl border border-slate-200 bg-white/90 backdrop-blur-xl p-5 shadow-[0_10px_40px_rgba(0,0,0,0.08)]">
<div className="mb-4">
<p className="text-xs uppercase tracking-[0.25em] text-muted-foreground">
History
</p>
<h2 className="mt-2 text-xl font-bold text-slate-900">
Recent prompts
</h2>
<p className="mt-2 text-sm text-muted-foreground">
Tap a prompt to reload it into the composer.
</p>
</div>
{/* <div className="space-y-2"> // <aside
{history.length === 0 ? ( // className="hidden lg:flex lg:w-80 lg:flex-col lg:sticky lg:top-24 h-[calc(100vh-140px)] overflow-y-auto rounded-3xl border border-slate-200 bg-white/90 backdrop-blur-xl p-5 shadow-[0_10px_40px_rgba(0,0,0,0.08)]">
<div className="rounded-3xl border border-border/80 bg-muted/70 p-4 text-sm text-muted-foreground"> // <div className="mb-4">
Your latest prompts will appear here as you chat. // <p className="text-xs uppercase tracking-[0.25em] text-muted-foreground">
</div> // History
) : ( // </p>
history // <h2 className="mt-2 text-xl font-bold text-slate-900">
.slice(-10) // Recent prompts
.reverse() // </h2>
.map((message) => ( // <p className="mt-2 text-sm text-muted-foreground">
<button // Tap a prompt to reload it into the composer.
key={message.id} // </p>
type="button" // </div>
onClick={() => handleSelectHistory(message)}
className={cn(
"w-full rounded-3xl border px-3 py-3 text-left text-sm transition", // <div className="space-y-2">
selectedHistoryId === message.id // {historyQuestions.length === 0 ? (
? "border-primary bg-primary/10" // <div className="rounded-3xl border border-border/80 bg-muted/70 p-4 text-sm text-muted-foreground">
: "border-border/70 bg-muted/80 hover:bg-muted/90", // No recent prompts found.
)} // </div>
> // ) : (
<span className="block text-sm text-foreground"> // historyQuestions.map((item, index) => (
{typeof message.content === "string" // <button
? message.content // key={item.response_id}
: JSON.stringify(message.content, null, 2)} // type="button"
</span> // onClick={() => handleHistoryClick(item)}
</button> // className="w-full rounded-2xl border border-slate-200 bg-white px-4 py-3 text-left text-sm transition-all duration-200 hover:-translate-y-1 hover:border-indigo-200 hover:bg-indigo-50/40 hover:shadow-lg"
)) // >
)} // <span className="block text-sm text-foreground">
</div> */} // {item.prompt}
<div className="space-y-2"> // </span>
{historyQuestions.length === 0 ? (
<div className="rounded-3xl border border-border/80 bg-muted/70 p-4 text-sm text-muted-foreground"> // <p className="text-xs text-slate-500 mt-1">
No recent prompts found. // {new Date(item.created_at).toLocaleString()}
</div> // </p>
) : ( // </button>
historyQuestions.map((item, index) => ( // ))
<button // )}
key={index} // </div>
type="button"
onClick={() => setInput(item.question)} // <div className="border-t p-4">
className="w-full rounded-2xl border border-slate-200 bg-white px-4 py-3 text-left text-sm transition-all duration-200 hover:-translate-y-1 hover:border-indigo-200 hover:bg-indigo-50/40 hover:shadow-lg" // <Button
// // variant="outline"
// onClick={logout}
// className="w-full"
// >
// Logout
// </Button>
// </div>
// </aside>
<aside
className="
hidden
lg:flex
lg:w-72
lg:flex-col
lg:sticky
lg:top-20
h-[calc(100vh-90px)]
overflow-hidden
rounded-3xl
border
border-slate-200
bg-white/80
backdrop-blur-xl
shadow-[0_8px_30px_rgba(0,0,0,0.06)]
"
>
{/* Header */}
<div className="border-b border-slate-100 px-4 py-4">
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-400">
Recents
</p>
</div>
{/* History List */}
<div className="flex-1 overflow-y-auto p-3">
{historyQuestions.length === 0 ? (
<div className="rounded-xl bg-slate-50 p-4 text-sm text-slate-500">
No recent prompts found
</div>
) : (
<div className="space-y-1">
{historyQuestions.map((item) => (
<button
key={item.response_id}
onClick={() => handleHistoryClick(item)}
className="
w-full
rounded-xl
px-3
py-3
text-left
transition-all
duration-200
hover:bg-indigo-50
hover:text-indigo-700
"
>
<div className="truncate text-sm font-medium">
{item.prompt}
</div>
<div className="mt-1 text-xs text-slate-400">
{new Date(item.created_at).toLocaleDateString()}
</div>
</button>
))}
</div>
)}
</div>
{/* Profile Section */}
<div className="relative border-t border-slate-100 p-3 bg-gradient-to-r from-slate-50 to-white">
<button
onClick={() => setShowProfileMenu(!showProfileMenu)}
className="
flex
w-full
items-center
gap-3
rounded-2xl
border
border-slate-100
bg-white
p-3
shadow-sm
transition-all
duration-200
hover:border-indigo-200
hover:bg-indigo-50/40
hover:shadow-md
"
>
{/* Avatar */}
<div
className="
flex
h-10
w-10
items-center
justify-center
rounded-full
bg-gradient-to-br
from-indigo-500
to-cyan-500
text-sm
font-semibold
text-white
"
> >
<span className="block text-sm text-foreground"> A
{item.question} </div>
{/* User Info */}
<div className="flex flex-1 flex-col text-left">
<span className="text-sm font-semibold text-slate-800">
Logged In
</span> </span>
<p className="text-sm text-slate-500"> <span className="text-xs text-slate-500">
{item.category} Yoda Workspace
</p> </span>
</button> </div>
))
)} {/* Arrow */}
</div> <svg
</aside>)} className={`h-4 w-4 text-slate-400 transition-transform ${showProfileMenu ? "rotate-180" : ""
}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
</button>
{/* Dropdown */}
{showProfileMenu && (
<div
className="
absolute
bottom-20
left-3
right-3
overflow-hidden
rounded-2xl
border
border-slate-200
bg-white
shadow-xl
z-50
"
>
<button
onClick={logout}
className="
w-full
px-4
py-3
text-left
text-sm
font-medium
text-red-600
transition-colors
hover:bg-red-50
"
>
Logout
</button>
</div>
)}
</div>
</aside>
)}
<div className="flex-1 min-w-0 flex flex-col gap-4"> <div className="flex-1 min-w-0 flex flex-col gap-4">
{messages.length === 0 ? ( {messages.length === 0 ? (
<div className="flex min-h-[65vh] flex-col items-center justify-center text-center"> <div className="flex min-h-[65vh] flex-col items-center justify-center text-center">
<h2 className="mb-3 text-4xl font-extrabold tracking-tight text-slate-900"> <h2 className="mb-3 text-4xl font-extrabold tracking-tight text-slate-900">
Welcome to GenBI Welcome to Yoda
</h2> </h2>
<p className="mb-10 text-lg text-slate-500"> <p className="mb-10 text-lg text-slate-500">
@@ -288,10 +501,9 @@ export default function ChatCanvas() {
<div className="grid gap-3 md:grid-cols-2 max-w-2xl w-full"> <div className="grid gap-3 md:grid-cols-2 max-w-2xl w-full">
{[ {[
"Show sales by region", "What was the total stock availability across different store types last month?",
"Compare revenue by month", "what is the OSA percentage for the month of march 2026?",
"Top 10 products", "Which products had the highest OSA percentage in March 2026?",
"Customer growth trends",
].map((item) => ( ].map((item) => (
<button <button
key={item} key={item}
@@ -313,11 +525,10 @@ export default function ChatCanvas() {
className={cn("flex", isUser ? "justify-end" : "justify-start")} className={cn("flex", isUser ? "justify-end" : "justify-start")}
key={message.id} key={message.id}
> >
<div <div
className={cn( className={cn(
// "max-w-[1100px]",
"max-w-[900px] mx-auto", "max-w-[900px] mx-auto",
isUser isUser
? "rounded-3xl bg-gradient-to-r from-indigo-600 to-cyan-600 px-5 py-3 text-sm text-white shadow-lg" ? "rounded-3xl bg-gradient-to-r from-indigo-600 to-cyan-600 px-5 py-3 text-sm text-white shadow-lg"
@@ -328,15 +539,39 @@ export default function ChatCanvas() {
typeof message.content === "string" typeof message.content === "string"
? message.content ? message.content
: JSON.stringify(message.content, null, 2) : JSON.stringify(message.content, null, 2)
) : ( ) : message.error ? (
<ChartArtifact content={message.content} <div className="rounded-3xl border border-amber-200 bg-amber-50 p-6">
/> <div className="flex items-start gap-4">
)} */} <AlertTriangle className="h-6 w-6 text-amber-600" />
<div>
<h3 className="font-semibold text-amber-800">
Connection Problem
</h3>
<p className="mt-2 text-sm text-amber-700">
{String(message.content)}
</p>
</div>
</div>
</div>
) : (
<ChartArtifact content={message.content} />
)} */}
{isUser ? ( {isUser ? (
typeof message.content === "string" typeof message.content === "string"
? message.content ? message.content
: JSON.stringify(message.content, null, 2) : JSON.stringify(message.content, null, 2)
) : message.clarification ? (
<div className="rounded-3xl border border-blue-200 bg-blue-50 p-6">
<h3 className="font-semibold text-blue-800">
{/* Clarification Needed */}
</h3>
<p className="mt-2 text-sm text-blue-700">
{String(message.content)}
</p>
</div>
) : message.error ? ( ) : message.error ? (
<div className="rounded-3xl border border-amber-200 bg-amber-50 p-6"> <div className="rounded-3xl border border-amber-200 bg-amber-50 p-6">
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
@@ -371,7 +606,7 @@ export default function ChatCanvas() {
<span className="typing-dot" /> <span className="typing-dot" />
</div> </div>
<span className="text-sm font-medium text-foreground/90"> <span className="text-sm font-medium text-foreground/90">
GenBI is typing... Yoda is typing...
</span> </span>
</div> </div>
</div> </div>
@@ -388,10 +623,34 @@ export default function ChatCanvas() {
{/* RIGHT PANEL */} {/* RIGHT PANEL */}
<aside className="hidden xl:block w-[380px] shrink-0"> {/* <aside className="hidden xl:block w-[380px] shrink-0 relative">
<div className="sticky top-24"> <div className="sticky bottom-4">
<div className="rounded-3xl border border-slate-200 bg-white p-6 shadow-[0_10px_40px_rgba(0,0,0,0.08)]">
<div className="mt-5 rounded-3xl border border-slate-200 bg-white p-6 shadow-[0_10px_40px_rgba(0,0,0,0.08)]">
<h4 className="font-medium mb-3">
Quick Prompts
</h4>
<div className="space-y-2">
{[
"What was the total stock availability across different store types last month?",
"what is the OSA percentage for the month of march 2026?",
"Which products had the highest OSA percentage in March 2026?",
].map((item) => (
<button
key={item}
onClick={() => setInput(item)}
className="w-full rounded-lg border p-2 text-left text-sm hover:bg-muted"
>
{item}
</button>
))}
</div>
</div>
<div className="rounded-3xl border border-slate-200 bg-white p-6 shadow-[0_10px_40px_rgba(0,0,0,0.08)]">
<h3 className="font-semibold text-lg"> <h3 className="font-semibold text-lg">
Ask GenBI Ask GenBI
</h3> </h3>
@@ -422,22 +681,29 @@ export default function ChatCanvas() {
</form> </form>
</div> </div>
<div className="mt-5 rounded-3xl border border-slate-200 bg-white p-6 shadow-[0_10px_40px_rgba(0,0,0,0.08)]"> </div>
<h4 className="font-medium mb-3"> </aside> */}
{/* RIGHT PANEL */}
<aside className="hidden xl:block w-[380px] shrink-0">
<div className="fixed right-6 bottom-4 w-[380px] z-40 space-y-4">
{/* Quick Prompts */}
<div className="rounded-3xl border border-slate-200 bg-white p-6 shadow-[0_10px_40px_rgba(0,0,0,0.08)]">
<h4 className="mb-3 font-medium">
Quick Prompts Quick Prompts
</h4> </h4>
<div className="space-y-2"> <div className="space-y-2">
{[ {[
"Show sales by region", "What was the total stock availability across different store types last month?",
"Compare revenue by month", "what is the OSA percentage for the month of march 2026?",
"Top 10 products", "Which products had the highest OSA percentage in March 2026?",
"Customer growth trends",
].map((item) => ( ].map((item) => (
<button <button
key={item} key={item}
onClick={() => setInput(item)} onClick={() => setInput(item)}
className="w-full rounded-lg border p-2 text-left text-sm hover:bg-muted" className="w-full rounded-lg border p-2 text-left text-sm transition hover:bg-slate-100"
> >
{item} {item}
</button> </button>
@@ -445,10 +711,43 @@ export default function ChatCanvas() {
</div> </div>
</div> </div>
{/* Ask GenBI */}
<div className="rounded-3xl border border-slate-200 bg-white p-6 shadow-[0_10px_40px_rgba(0,0,0,0.08)]">
<h3 className="text-lg font-semibold">
Ask Yoda
</h3>
<p className="mt-1 mb-4 text-sm text-slate-500">
Ask questions about your business data
</p>
<form onSubmit={onSubmit} className="space-y-3">
<Input
value={input}
disabled={isSending}
onChange={(e) => {
setInput(e.target.value);
setError("");
}}
placeholder="Type your question..."
/>
<Button
className="h-11 w-full rounded-xl bg-gradient-to-r from-indigo-600 to-cyan-600 text-white hover:opacity-90"
type="submit"
disabled={isSending || !input.trim()}
>
{isSending ? "Sending..." : "Send"}
</Button>
</form>
</div>
</div> </div>
</aside> </aside>
</section> </section>
{/* <form className="fixed inset-x-0 bottom-0 z-20 px-3 pb-4" onSubmit={onSubmit}> */} {/* <form className="fixed inset-x-0 bottom-0 z-20 px-3 pb-4" onSubmit={onSubmit}> */}
</main> </main>
+9 -3
View File
@@ -47,6 +47,10 @@ export default function Login() {
}; };
const login = async () => { const login = async () => {
console.log("Attempting login with:", {
username,
password: password ? "******" : "",
});
if (!validate()) return; if (!validate()) return;
setIsLoading(true); setIsLoading(true);
@@ -57,8 +61,7 @@ export default function Login() {
password, password,
}); });
const response = await api.post<LoginResponse>( const response = await api.post<LoginResponse>("/loginUser",
"/loginUser",
body, body,
{ {
headers: { headers: {
@@ -68,6 +71,8 @@ export default function Login() {
} }
); );
console.log("Response:", response.data);
if ( if (
!response.data.success || !response.data.success ||
!response.data.token !response.data.token
@@ -85,7 +90,8 @@ export default function Login() {
); );
window.location.href = "/dashboard"; window.location.href = "/dashboard";
} catch (err) { }
catch (err) {
console.error(err); console.error(err);
if (isAxiosError(err)) { if (isAxiosError(err)) {