latest changes
This commit is contained in:
@@ -8,6 +8,7 @@ const api = axios.create({
|
||||
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem("token");
|
||||
console.log("Attaching token to request:", token);
|
||||
|
||||
config.headers = config.headers || {};
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ import {
|
||||
ImageIcon,
|
||||
RefreshCcw,
|
||||
Sparkles,
|
||||
CheckCircle2,
|
||||
BarChart3,
|
||||
Database,
|
||||
} from "lucide-react";
|
||||
|
||||
import ErrorBoundary from "@/components/ErrorBoundary";
|
||||
@@ -788,37 +791,52 @@ function VegaLiteArtifact({
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
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 ${
|
||||
activeTab === "answer"
|
||||
? "bg-cyan-500 text-white"
|
||||
: "bg-white/10 text-slate-300 hover:bg-white/20"
|
||||
: "bg-white border border-slate-200 text-black hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
<CheckCircle2
|
||||
size={16}
|
||||
className={activeTab === "answer" ? "text-white" : "text-green-600"}
|
||||
/>
|
||||
Answer
|
||||
</button>
|
||||
|
||||
<button
|
||||
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 ${
|
||||
activeTab === "chart"
|
||||
? "bg-cyan-500 text-white"
|
||||
: "bg-white/10 text-slate-300 hover:bg-white/20"
|
||||
: "bg-white border border-slate-200 text-black hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
<BarChart3
|
||||
size={16}
|
||||
className={activeTab === "chart" ? "text-white" : "text-blue-600"}
|
||||
/>
|
||||
Chart
|
||||
</button>
|
||||
|
||||
<button
|
||||
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 ${
|
||||
activeTab === "sql"
|
||||
? "bg-cyan-500 text-white"
|
||||
: "bg-white/10 text-slate-300 hover:bg-white/20"
|
||||
: "bg-white border border-slate-200 text-black hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
<Database
|
||||
size={16}
|
||||
className={activeTab === "sql" ? "text-white" : "text-orange-600"}
|
||||
/>
|
||||
SQL
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* EXPORT BUTTONS */}
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{/* <div className="flex flex-wrap gap-3">
|
||||
<Button
|
||||
onClick={downloadPDF}
|
||||
className="rounded-xl bg-red-500 hover:bg-red-600"
|
||||
@@ -850,7 +868,7 @@ function VegaLiteArtifact({
|
||||
<ImageIcon className="mr-2 h-4 w-4" />
|
||||
PNG
|
||||
</Button>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
+429
-130
@@ -13,6 +13,7 @@ type ChatMessage = {
|
||||
role: "user" | "assistant";
|
||||
content: unknown;
|
||||
error?: boolean;
|
||||
clarification?: boolean;
|
||||
};
|
||||
|
||||
function createId() {
|
||||
@@ -34,11 +35,11 @@ function createMessage(role: ChatMessage["role"], content: unknown): ChatMessage
|
||||
export default function ChatCanvas() {
|
||||
const [input, setInput] = useState("");
|
||||
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
||||
// const [selectedHistoryId, setSelectedHistoryId] = useState<string | null>(null);
|
||||
const [error, setError] = useState("");
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
const [showHistory, setShowHistory] = useState(true);
|
||||
const [historyQuestions, setHistoryQuestions] = useState<any[]>([]);
|
||||
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
||||
|
||||
const bottomRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -52,28 +53,101 @@ export default function ChatCanvas() {
|
||||
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 () => {
|
||||
try {
|
||||
const res = await api.get("/suggestions");
|
||||
const res = await api.get("/fetchUserHistory");
|
||||
|
||||
console.log("History Response =>", res.data);
|
||||
|
||||
if (res.data.success) {
|
||||
setHistoryQuestions(res.data.questions);
|
||||
setHistoryQuestions(res.data.data);
|
||||
}
|
||||
} 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) => {
|
||||
// if (typeof message.content === "string") {
|
||||
// setInput(message.content);
|
||||
// setSelectedHistoryId(message.id);
|
||||
// setError("");
|
||||
// const response = await api.post(
|
||||
// "/fetchHistory",
|
||||
// {
|
||||
// response_id: item.response_id,
|
||||
// },
|
||||
// {
|
||||
// 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) => {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -96,11 +170,30 @@ export default function ChatCanvas() {
|
||||
JSON.stringify(response.data, null, 2)
|
||||
);
|
||||
|
||||
// setMessages((current) => [
|
||||
// ...current,
|
||||
// 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) {
|
||||
console.error(err);
|
||||
|
||||
@@ -146,140 +239,260 @@ export default function ChatCanvas() {
|
||||
};
|
||||
|
||||
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">
|
||||
{/* <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-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>
|
||||
<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
|
||||
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>
|
||||
|
||||
<h1 className="text-xl font-semibold">
|
||||
Yoda
|
||||
</h1>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* <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">
|
||||
{showHistory && (<aside
|
||||
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.
|
||||
{showHistory && (
|
||||
|
||||
// <aside
|
||||
// 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">
|
||||
// {historyQuestions.length === 0 ? (
|
||||
// <div className="rounded-3xl border border-border/80 bg-muted/70 p-4 text-sm text-muted-foreground">
|
||||
// No recent prompts found.
|
||||
// </div>
|
||||
// ) : (
|
||||
// historyQuestions.map((item, index) => (
|
||||
// <button
|
||||
// key={item.response_id}
|
||||
// type="button"
|
||||
// onClick={() => handleHistoryClick(item)}
|
||||
// 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">
|
||||
// {item.prompt}
|
||||
// </span>
|
||||
|
||||
// <p className="text-xs text-slate-500 mt-1">
|
||||
// {new Date(item.created_at).toLocaleString()}
|
||||
// </p>
|
||||
// </button>
|
||||
// ))
|
||||
// )}
|
||||
// </div>
|
||||
|
||||
// <div className="border-t p-4">
|
||||
// <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>
|
||||
|
||||
{/* <div className="space-y-2">
|
||||
{history.length === 0 ? (
|
||||
<div className="rounded-3xl border border-border/80 bg-muted/70 p-4 text-sm text-muted-foreground">
|
||||
Your latest prompts will appear here as you chat.
|
||||
</div>
|
||||
) : (
|
||||
history
|
||||
.slice(-10)
|
||||
.reverse()
|
||||
.map((message) => (
|
||||
<button
|
||||
key={message.id}
|
||||
type="button"
|
||||
onClick={() => handleSelectHistory(message)}
|
||||
className={cn(
|
||||
"w-full rounded-3xl border px-3 py-3 text-left text-sm transition",
|
||||
selectedHistoryId === message.id
|
||||
? "border-primary bg-primary/10"
|
||||
: "border-border/70 bg-muted/80 hover:bg-muted/90",
|
||||
)}
|
||||
>
|
||||
<span className="block text-sm text-foreground">
|
||||
{typeof message.content === "string"
|
||||
? message.content
|
||||
: JSON.stringify(message.content, null, 2)}
|
||||
</span>
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
</div> */}
|
||||
<div className="space-y-2">
|
||||
{/* History List */}
|
||||
<div className="flex-1 overflow-y-auto p-3">
|
||||
{historyQuestions.length === 0 ? (
|
||||
<div className="rounded-3xl border border-border/80 bg-muted/70 p-4 text-sm text-muted-foreground">
|
||||
No recent prompts found.
|
||||
<div className="rounded-xl bg-slate-50 p-4 text-sm text-slate-500">
|
||||
No recent prompts found
|
||||
</div>
|
||||
) : (
|
||||
historyQuestions.map((item, index) => (
|
||||
<div className="space-y-1">
|
||||
{historyQuestions.map((item) => (
|
||||
<button
|
||||
key={index}
|
||||
type="button"
|
||||
onClick={() => setInput(item.question)}
|
||||
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"
|
||||
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
|
||||
"
|
||||
>
|
||||
<span className="block text-sm text-foreground">
|
||||
{item.question}
|
||||
</span>
|
||||
<div className="truncate text-sm font-medium">
|
||||
{item.prompt}
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-slate-500">
|
||||
{item.category}
|
||||
</p>
|
||||
<div className="mt-1 text-xs text-slate-400">
|
||||
{new Date(item.created_at).toLocaleDateString()}
|
||||
</div>
|
||||
</button>
|
||||
))
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</aside>)}
|
||||
|
||||
{/* 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
|
||||
"
|
||||
>
|
||||
A
|
||||
</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 className="text-xs text-slate-500">
|
||||
Yoda Workspace
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Arrow */}
|
||||
<svg
|
||||
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">
|
||||
{messages.length === 0 ? (
|
||||
|
||||
<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">
|
||||
Welcome to GenBI
|
||||
Welcome to Yoda
|
||||
</h2>
|
||||
|
||||
<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">
|
||||
{[
|
||||
"Show sales by region",
|
||||
"Compare revenue by month",
|
||||
"Top 10 products",
|
||||
"Customer growth trends",
|
||||
"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}
|
||||
@@ -317,7 +529,6 @@ export default function ChatCanvas() {
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
// "max-w-[1100px]",
|
||||
"max-w-[900px] mx-auto",
|
||||
isUser
|
||||
? "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"
|
||||
? message.content
|
||||
: JSON.stringify(message.content, null, 2)
|
||||
) : (
|
||||
<ChartArtifact content={message.content}
|
||||
/>
|
||||
)} */}
|
||||
) : message.error ? (
|
||||
<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 ? (
|
||||
typeof message.content === "string"
|
||||
? message.content
|
||||
: 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 ? (
|
||||
<div className="rounded-3xl border border-amber-200 bg-amber-50 p-6">
|
||||
<div className="flex items-start gap-4">
|
||||
@@ -371,7 +606,7 @@ export default function ChatCanvas() {
|
||||
<span className="typing-dot" />
|
||||
</div>
|
||||
<span className="text-sm font-medium text-foreground/90">
|
||||
GenBI is typing...
|
||||
Yoda is typing...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -388,8 +623,32 @@ export default function ChatCanvas() {
|
||||
|
||||
|
||||
{/* RIGHT PANEL */}
|
||||
<aside className="hidden xl:block w-[380px] shrink-0">
|
||||
<div className="sticky top-24">
|
||||
{/* <aside className="hidden xl:block w-[380px] shrink-0 relative">
|
||||
<div className="sticky bottom-4">
|
||||
|
||||
|
||||
|
||||
<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">
|
||||
@@ -422,22 +681,29 @@ export default function ChatCanvas() {
|
||||
</form>
|
||||
</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)]">
|
||||
<h4 className="font-medium mb-3">
|
||||
</div>
|
||||
</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
|
||||
</h4>
|
||||
|
||||
<div className="space-y-2">
|
||||
{[
|
||||
"Show sales by region",
|
||||
"Compare revenue by month",
|
||||
"Top 10 products",
|
||||
"Customer growth trends",
|
||||
"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"
|
||||
className="w-full rounded-lg border p-2 text-left text-sm transition hover:bg-slate-100"
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
@@ -445,10 +711,43 @@ export default function ChatCanvas() {
|
||||
</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>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
{/* <form className="fixed inset-x-0 bottom-0 z-20 px-3 pb-4" onSubmit={onSubmit}> */}
|
||||
|
||||
</main>
|
||||
|
||||
+9
-3
@@ -47,6 +47,10 @@ export default function Login() {
|
||||
};
|
||||
|
||||
const login = async () => {
|
||||
console.log("Attempting login with:", {
|
||||
username,
|
||||
password: password ? "******" : "",
|
||||
});
|
||||
if (!validate()) return;
|
||||
|
||||
setIsLoading(true);
|
||||
@@ -57,8 +61,7 @@ export default function Login() {
|
||||
password,
|
||||
});
|
||||
|
||||
const response = await api.post<LoginResponse>(
|
||||
"/loginUser",
|
||||
const response = await api.post<LoginResponse>("/loginUser",
|
||||
body,
|
||||
{
|
||||
headers: {
|
||||
@@ -68,6 +71,8 @@ export default function Login() {
|
||||
}
|
||||
);
|
||||
|
||||
console.log("Response:", response.data);
|
||||
|
||||
if (
|
||||
!response.data.success ||
|
||||
!response.data.token
|
||||
@@ -85,7 +90,8 @@ export default function Login() {
|
||||
);
|
||||
|
||||
window.location.href = "/dashboard";
|
||||
} catch (err) {
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
|
||||
if (isAxiosError(err)) {
|
||||
|
||||
Reference in New Issue
Block a user