dashboard design is fixed
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# name: Deploy GenBi App
|
||||
# on:
|
||||
# push:
|
||||
# branches:
|
||||
# - main
|
||||
# jobs:
|
||||
# deploy:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Checkout Code
|
||||
# uses: actions/checkout@v3
|
||||
|
||||
|
||||
# - name: Copy files to VPS
|
||||
# uses: appleboy/scp-action@master
|
||||
# with:
|
||||
# host: 172.236.185.26
|
||||
# username: root
|
||||
# password: LinodeUser#26
|
||||
# port: 22
|
||||
# source: "./*"
|
||||
# target: "/root/IR-Node-Setup"
|
||||
|
||||
|
||||
# - name: Execute SSH Commands
|
||||
# uses: appleboy/ssh-action@master
|
||||
# with:
|
||||
# host: 172.236.185.26
|
||||
# username: root
|
||||
# password: LinodeUser#26
|
||||
# port: 22
|
||||
# script: |
|
||||
# cd /root/genbi-UI
|
||||
# npm install
|
||||
# npm run build
|
||||
# sudo cp -r dist/* /var/www/react-app/
|
||||
Generated
+32
-20
@@ -160,7 +160,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
|
||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
@@ -717,6 +716,29 @@
|
||||
"@noble/ciphers": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.1",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||
@@ -1195,7 +1217,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
|
||||
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
@@ -3205,7 +3226,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz",
|
||||
"integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@@ -3228,7 +3248,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz",
|
||||
"integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@@ -3239,7 +3258,6 @@
|
||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@@ -3323,7 +3341,6 @@
|
||||
"integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.59.4",
|
||||
"@typescript-eslint/types": "8.59.4",
|
||||
@@ -3607,7 +3624,6 @@
|
||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -3992,7 +4008,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.10.12",
|
||||
"caniuse-lite": "^1.0.30001782",
|
||||
@@ -5203,7 +5218,6 @@
|
||||
"integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
@@ -5622,7 +5636,8 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz",
|
||||
"integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
@@ -6256,7 +6271,6 @@
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.23.tgz",
|
||||
"integrity": "sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
}
|
||||
@@ -8721,7 +8735,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.12",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -9139,7 +9152,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz",
|
||||
"integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -9149,7 +9161,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz",
|
||||
"integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -10540,7 +10551,6 @@
|
||||
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -10835,7 +10845,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vega/-/vega-6.2.0.tgz",
|
||||
"integrity": "sha512-BIwalIcEGysJdQDjeVUmMWB3e50jPDNAMfLJscjEvpunU9bSt7X1OYnQxkg3uBwuRRI4nWfFZO9uIW910nLeGw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"vega-crossfilter": "~5.1.0",
|
||||
"vega-dataflow": "~6.1.0",
|
||||
@@ -10902,6 +10911,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vega-embed/-/vega-embed-7.1.0.tgz",
|
||||
"integrity": "sha512-ZmEIn5XJrQt7fSh2lwtSdXG/9uf3yIqZnvXFEwBJRppiBgrEWZcZbj6VK3xn8sNTFQ+sQDXW5sl/6kmbAW3s5A==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-json-patch": "^3.1.1",
|
||||
"json-stringify-pretty-compact": "^4.0.0",
|
||||
@@ -10925,6 +10935,7 @@
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
|
||||
"integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
@@ -11036,6 +11047,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vega-interpreter/-/vega-interpreter-2.2.1.tgz",
|
||||
"integrity": "sha512-o+4ZEme2mdFLewlpF76dwPWW2VkZ3TAF3DMcq75/NzA5KPvnN4wnlCM8At2FVawbaHRyGdVkJSS5ROF5KwpHPQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"vega-util": "^2.1.0"
|
||||
}
|
||||
@@ -11057,7 +11069,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vega-lite/-/vega-lite-6.4.3.tgz",
|
||||
"integrity": "sha512-d/7hPjfz560UERaQuTmGgIVfXAe3g2hJWeC+igDeaGohUdEoNrHLXgR/yTOBT8vV/lIuuKnw+0/xWWblkDwkMQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"json-stringify-pretty-compact": "~4.0.0",
|
||||
"tslib": "~2.8.1",
|
||||
@@ -11172,7 +11183,8 @@
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vega-schema-url-parser/-/vega-schema-url-parser-3.0.2.tgz",
|
||||
"integrity": "sha512-xAnR7KAvNPYewI3O0l5QGdT8Tv0+GCZQjqfP39cW/hbe/b3aYMAQ39vm8O2wfXUHzm04xTe7nolcsx8WQNVLRQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/vega-selections": {
|
||||
"version": "6.1.2",
|
||||
@@ -11199,6 +11211,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vega-themes/-/vega-themes-3.0.0.tgz",
|
||||
"integrity": "sha512-1iFiI3BNmW9FrsLnDLx0ZKEddsCitRY3XmUAwp6qmp+p+IXyJYc9pfjlVj9E6KXBPfm4cQyU++s0smKNiWzO4g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://app.hubspot.com/payments/GyPC972GD9Rt"
|
||||
},
|
||||
@@ -11223,6 +11236,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vega-tooltip/-/vega-tooltip-1.0.0.tgz",
|
||||
"integrity": "sha512-P1R0JP29v0qnTuwzCQ0SPJlkjAzr6qeyj+H4VgUFSykHmHc1OBxda//XBaFDl/bZgIscEMvjKSjZpXd84x3aZQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"vega-util": "^2.0.0"
|
||||
},
|
||||
@@ -11346,7 +11360,6 @@
|
||||
"integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.32.0",
|
||||
"picomatch": "^4.0.4",
|
||||
@@ -11633,7 +11646,6 @@
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
||||
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import axios from "axios";
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: "http://172.237.44.156:3000",
|
||||
baseURL: "http://172.236.185.26:3000",
|
||||
timeout: 120000,
|
||||
|
||||
});
|
||||
|
||||
@@ -420,7 +420,7 @@ function VegaLiteArtifact({
|
||||
const chartRef =
|
||||
useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [renderError, setRenderError] = useState("");
|
||||
|
||||
@@ -429,7 +429,7 @@ function VegaLiteArtifact({
|
||||
|
||||
const [activeTab, setActiveTab] = useState<
|
||||
"answer" | "chart" | "sql"
|
||||
>("chart");
|
||||
>("answer");
|
||||
|
||||
const safeSpec = useMemo(
|
||||
() => sanitizeSpec(spec),
|
||||
@@ -444,7 +444,15 @@ function VegaLiteArtifact({
|
||||
let result: Result | undefined;
|
||||
|
||||
const renderChart = async () => {
|
||||
if (!chartRef.current) return;
|
||||
if (activeTab !== "chart") {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chartRef.current) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
@@ -514,7 +522,7 @@ function VegaLiteArtifact({
|
||||
return () => {
|
||||
result?.view.finalize();
|
||||
};
|
||||
}, [safeSpec,activeTab]);
|
||||
}, [safeSpec, activeTab]);
|
||||
|
||||
/* =====================================================
|
||||
GET VALUES
|
||||
@@ -778,22 +786,22 @@ function VegaLiteArtifact({
|
||||
===================================================== */
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden rounded-[28px] border border-white/20 bg-white/80 shadow-[0_20px_100px_rgba(0,0,0,0.15)] backdrop-blur-xl">
|
||||
<div className="w-full overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
|
||||
{/* ======================================
|
||||
HEADER
|
||||
====================================== */}
|
||||
|
||||
<div className="border-b bg-gradient-to-r from-slate-50 via-blue-50 to-indigo-50 px-6 py-5">
|
||||
<div className="border-b border-slate-200 bg-slate-50 px-3 py-3 sm:px-5 sm:py-4">
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex min-w-0 flex-col gap-4">
|
||||
|
||||
{/* TOP TABS */}
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
<button
|
||||
onClick={() => setActiveTab("answer")}
|
||||
className={`flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-medium transition ${
|
||||
className={`flex h-10 items-center gap-2 rounded-xl px-3 text-sm font-medium transition sm:px-4 ${
|
||||
activeTab === "answer"
|
||||
? "bg-cyan-500 text-white"
|
||||
? "bg-cyan-600 text-white"
|
||||
: "bg-white border border-slate-200 text-black hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
@@ -806,9 +814,9 @@ function VegaLiteArtifact({
|
||||
|
||||
<button
|
||||
onClick={() => setActiveTab("chart")}
|
||||
className={`flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-medium transition ${
|
||||
className={`flex h-10 items-center gap-2 rounded-xl px-3 text-sm font-medium transition sm:px-4 ${
|
||||
activeTab === "chart"
|
||||
? "bg-cyan-500 text-white"
|
||||
? "bg-cyan-600 text-white"
|
||||
: "bg-white border border-slate-200 text-black hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
@@ -821,9 +829,9 @@ function VegaLiteArtifact({
|
||||
|
||||
<button
|
||||
onClick={() => setActiveTab("sql")}
|
||||
className={`flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-medium transition ${
|
||||
className={`flex h-10 items-center gap-2 rounded-xl px-3 text-sm font-medium transition sm:px-4 ${
|
||||
activeTab === "sql"
|
||||
? "bg-cyan-500 text-white"
|
||||
? "bg-cyan-600 text-white"
|
||||
: "bg-white border border-slate-200 text-black hover:bg-slate-50"
|
||||
}`}
|
||||
>
|
||||
@@ -879,20 +887,20 @@ function VegaLiteArtifact({
|
||||
====================================== */}
|
||||
|
||||
|
||||
<div className="bg-gradient-to-br from-slate-50 via-white to-indigo-50 p-6">
|
||||
<div className="rounded-3xl border border-slate-200 bg-white p-5 shadow-inner">
|
||||
<div className="relative min-h-[500px] w-full">
|
||||
<div className="bg-white p-3 sm:p-5">
|
||||
<div className="rounded-2xl border border-slate-200 bg-white p-3 sm:p-4">
|
||||
<div className="relative min-h-[360px] w-full sm:min-h-[460px]">
|
||||
{/* LOADER */}
|
||||
|
||||
{loading && (
|
||||
<div className="absolute inset-0 z-20 flex items-center justify-center rounded-3xl bg-white/80 backdrop-blur-sm">
|
||||
{activeTab === "chart" && loading && (
|
||||
<div className="absolute inset-0 z-20 flex items-center justify-center rounded-2xl bg-white/80 backdrop-blur-sm">
|
||||
<div className="space-y-5 text-center">
|
||||
<div className="mx-auto flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-r from-indigo-500 to-cyan-500 shadow-2xl">
|
||||
<Sparkles className="h-10 w-10 animate-pulse text-white" />
|
||||
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-full bg-gradient-to-r from-indigo-500 to-cyan-500 shadow-lg sm:h-20 sm:w-20">
|
||||
<Sparkles className="h-8 w-8 animate-pulse text-white sm:h-10 sm:w-10" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-slate-700">
|
||||
<h2 className="text-lg font-bold text-slate-700 sm:text-xl">
|
||||
Generating
|
||||
Visualization
|
||||
</h2>
|
||||
@@ -921,32 +929,32 @@ function VegaLiteArtifact({
|
||||
{activeTab === "chart" && (
|
||||
<div
|
||||
ref={chartRef}
|
||||
className="min-h-[500px] w-full max-w-none overflow-hidden rounded-2xl"
|
||||
className="min-h-[360px] w-full max-w-none overflow-auto rounded-xl sm:min-h-[460px]"
|
||||
/>
|
||||
)}
|
||||
{activeTab === "answer" && (
|
||||
<div className="space-y-6 p-6">
|
||||
<div className="space-y-4 p-1 sm:p-3">
|
||||
|
||||
{/* Summary Cards */}
|
||||
<div className="grid gap-4 md:grid-cols-3">
|
||||
<div className="grid gap-3 sm:grid-cols-3">
|
||||
|
||||
<div className="rounded-xl border bg-white p-6 shadow-sm">
|
||||
<div className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
|
||||
<p className="text-sm text-slate-500">Rows</p>
|
||||
<h2 className="mt-2 text-4xl font-bold">
|
||||
<h2 className="mt-2 text-3xl font-bold text-slate-950">
|
||||
{answer?.data?.length ?? answer?.totalRows ?? 0}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border bg-white p-6 shadow-sm">
|
||||
<div className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
|
||||
<p className="text-sm text-slate-500">Columns</p>
|
||||
<h2 className="mt-2 text-4xl font-bold">
|
||||
<h2 className="mt-2 text-3xl font-bold text-slate-950">
|
||||
{answer?.columns?.length ?? 0}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border bg-white p-6 shadow-sm">
|
||||
<div className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
|
||||
<p className="text-sm text-slate-500">Chart</p>
|
||||
<h2 className="mt-2 text-lg font-semibold">
|
||||
<h2 className="mt-2 text-base font-semibold text-slate-950">
|
||||
{spec ? "Available" : "Not Available"}
|
||||
</h2>
|
||||
</div>
|
||||
@@ -956,14 +964,14 @@ function VegaLiteArtifact({
|
||||
{/* Data Table */}
|
||||
{Array.isArray(answer?.data) &&
|
||||
answer.data.length > 0 && (
|
||||
<div className="overflow-auto rounded-xl border">
|
||||
<table className="w-full border-collapse">
|
||||
<div className="max-h-[460px] overflow-auto rounded-xl border border-slate-200">
|
||||
<table className="min-w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="bg-slate-100">
|
||||
{Object.keys(answer.data[0]).map((key) => (
|
||||
<th
|
||||
key={key}
|
||||
className="border px-4 py-3 text-left font-semibold"
|
||||
className="whitespace-nowrap border-b border-r border-slate-200 px-4 py-3 text-left font-semibold text-slate-800"
|
||||
>
|
||||
{key}
|
||||
</th>
|
||||
@@ -985,7 +993,7 @@ function VegaLiteArtifact({
|
||||
(value: any, idx: number) => (
|
||||
<td
|
||||
key={idx}
|
||||
className="border px-4 py-3"
|
||||
className="whitespace-nowrap border-b border-r border-slate-200 px-4 py-3 text-slate-700"
|
||||
>
|
||||
{String(value)}
|
||||
</td>
|
||||
@@ -1003,12 +1011,12 @@ function VegaLiteArtifact({
|
||||
)}
|
||||
|
||||
{activeTab === "sql" && (
|
||||
<div className="p-6">
|
||||
<div className="p-1 sm:p-3">
|
||||
<h3 className="mb-3 text-lg font-semibold">
|
||||
SQL Query
|
||||
</h3>
|
||||
|
||||
<pre className="overflow-auto rounded-xl bg-slate-900 p-4 text-sm text-green-400">
|
||||
<pre className="max-h-[460px] overflow-auto rounded-xl bg-slate-950 p-4 text-sm text-green-300">
|
||||
{sql}
|
||||
</pre>
|
||||
</div>
|
||||
@@ -1076,8 +1084,8 @@ export default function ChartArtifact({
|
||||
console.log("FINAL SPEC =>", finalSpec);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Card className="overflow-hidden border border-white/20 bg-white/70 shadow-[0_20px_100px_rgba(0,0,0,0.15)] backdrop-blur-xl">
|
||||
<div className="w-full min-w-0">
|
||||
<Card className="overflow-hidden border-0 bg-transparent shadow-none">
|
||||
<CardContent className="p-0">
|
||||
{finalSpec ? (
|
||||
<ErrorBoundary>
|
||||
@@ -1088,7 +1096,7 @@ export default function ChartArtifact({
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
) : (
|
||||
<div className="prose prose-sm max-w-none rounded-3xl bg-white p-8 shadow-inner">
|
||||
<div className="prose prose-sm max-w-none overflow-auto rounded-2xl bg-white p-4 sm:p-6">
|
||||
<ReactMarkdown>
|
||||
{typeof content ===
|
||||
"string"
|
||||
|
||||
+102
-108
@@ -6,7 +6,7 @@ import ChartArtifact from "@/components/ChartArtifact";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { AlertTriangle, ChevronDown, LogOut, Menu } from "lucide-react";
|
||||
|
||||
type ChatMessage = {
|
||||
id: string;
|
||||
@@ -239,21 +239,22 @@ export default function ChatCanvas() {
|
||||
};
|
||||
|
||||
return (
|
||||
<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-slate-50 pb-32 xl:pb-6">
|
||||
|
||||
{/* <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 bg-white px-6 py-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<header className="sticky top-0 z-50 border-b border-slate-200 bg-white/95 px-4 py-3 backdrop-blur sm:px-6">
|
||||
<div className="mx-auto flex max-w-[1520px] items-center justify-between">
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => setShowHistory(!showHistory)}
|
||||
className="rounded-lg border px-3 py-1.5 text-sm hover:bg-slate-100"
|
||||
className="inline-flex h-10 w-10 items-center justify-center rounded-full border border-slate-200 bg-white text-slate-700 shadow-sm transition hover:bg-slate-100"
|
||||
aria-label="Toggle recent prompts"
|
||||
>
|
||||
☰
|
||||
<Menu className="h-5 w-5" />
|
||||
</button>
|
||||
|
||||
<h1 className="text-xl font-semibold">
|
||||
<h1 className="text-lg font-semibold sm:text-xl">
|
||||
Yoda
|
||||
</h1>
|
||||
</div>
|
||||
@@ -262,93 +263,65 @@ export default function ChatCanvas() {
|
||||
</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">
|
||||
<section
|
||||
className={cn(
|
||||
"mx-auto grid max-w-[1520px] grid-cols-1 gap-4 px-3 py-4 sm:px-4 sm:py-5 lg:gap-5 xl:gap-6",
|
||||
showHistory
|
||||
? "lg:grid-cols-[18rem_minmax(0,1fr)] xl:grid-cols-[18rem_minmax(0,1fr)_22rem]"
|
||||
: "xl:grid-cols-[minmax(0,1fr)_22rem]",
|
||||
)}
|
||||
>
|
||||
{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>
|
||||
<>
|
||||
<button
|
||||
aria-label="Close recent prompts"
|
||||
className="fixed inset-0 z-40 bg-slate-950/20 backdrop-blur-[1px] lg:hidden"
|
||||
onClick={() => setShowHistory(false)}
|
||||
type="button"
|
||||
/>
|
||||
|
||||
<aside
|
||||
className="
|
||||
hidden
|
||||
lg:flex
|
||||
lg:w-72
|
||||
fixed
|
||||
bottom-3
|
||||
left-3
|
||||
top-3
|
||||
z-50
|
||||
flex
|
||||
w-[min(18rem,calc(100vw-1.5rem))]
|
||||
flex-col
|
||||
lg:bottom-auto
|
||||
lg:left-auto
|
||||
lg:z-auto
|
||||
lg:w-auto
|
||||
lg:flex-col
|
||||
lg:sticky
|
||||
lg:top-20
|
||||
h-[calc(100vh-90px)]
|
||||
lg:h-[calc(100vh-96px)]
|
||||
overflow-hidden
|
||||
rounded-3xl
|
||||
rounded-2xl
|
||||
border
|
||||
border-slate-200
|
||||
bg-white/80
|
||||
bg-white
|
||||
backdrop-blur-xl
|
||||
shadow-[0_8px_30px_rgba(0,0,0,0.06)]
|
||||
shadow-sm
|
||||
"
|
||||
>
|
||||
{/* 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">
|
||||
<div className="border-b border-slate-100 px-4 py-3">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">
|
||||
Recents
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* History List */}
|
||||
<div className="flex-1 overflow-y-auto p-3">
|
||||
<div className="min-h-0 flex-1 overflow-y-auto p-2">
|
||||
{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">
|
||||
<div className="space-y-0.5">
|
||||
{historyQuestions.map((item) => (
|
||||
<button
|
||||
key={item.response_id}
|
||||
@@ -357,7 +330,7 @@ export default function ChatCanvas() {
|
||||
w-full
|
||||
rounded-xl
|
||||
px-3
|
||||
py-3
|
||||
py-2.5
|
||||
text-left
|
||||
transition-all
|
||||
duration-200
|
||||
@@ -365,11 +338,11 @@ export default function ChatCanvas() {
|
||||
hover:text-indigo-700
|
||||
"
|
||||
>
|
||||
<div className="truncate text-sm font-medium">
|
||||
<div className="truncate text-sm font-medium leading-5 text-slate-900">
|
||||
{item.prompt}
|
||||
</div>
|
||||
|
||||
<div className="mt-1 text-xs text-slate-400">
|
||||
<div className="mt-0.5 text-xs leading-4 text-slate-400">
|
||||
{new Date(item.created_at).toLocaleDateString()}
|
||||
</div>
|
||||
</button>
|
||||
@@ -379,7 +352,7 @@ export default function ChatCanvas() {
|
||||
</div>
|
||||
|
||||
{/* Profile Section */}
|
||||
<div className="relative border-t border-slate-100 p-3 bg-gradient-to-r from-slate-50 to-white">
|
||||
<div className="relative border-t border-slate-100 bg-slate-50 p-2.5">
|
||||
<button
|
||||
onClick={() => setShowProfileMenu(!showProfileMenu)}
|
||||
className="
|
||||
@@ -391,7 +364,7 @@ export default function ChatCanvas() {
|
||||
border
|
||||
border-slate-100
|
||||
bg-white
|
||||
p-3
|
||||
p-2.5
|
||||
shadow-sm
|
||||
transition-all
|
||||
duration-200
|
||||
@@ -404,15 +377,16 @@ export default function ChatCanvas() {
|
||||
<div
|
||||
className="
|
||||
flex
|
||||
h-10
|
||||
w-10
|
||||
h-9
|
||||
w-9
|
||||
shrink-0
|
||||
items-center
|
||||
justify-center
|
||||
rounded-full
|
||||
bg-gradient-to-br
|
||||
from-indigo-500
|
||||
to-cyan-500
|
||||
text-sm
|
||||
text-xs
|
||||
font-semibold
|
||||
text-white
|
||||
"
|
||||
@@ -422,30 +396,20 @@ export default function ChatCanvas() {
|
||||
|
||||
{/* User Info */}
|
||||
<div className="flex flex-1 flex-col text-left">
|
||||
<span className="text-sm font-semibold text-slate-800">
|
||||
<span className="text-sm font-semibold leading-5 text-slate-800">
|
||||
Logged In
|
||||
</span>
|
||||
|
||||
<span className="text-xs text-slate-500">
|
||||
<span className="truncate text-xs leading-4 text-slate-500">
|
||||
Yoda Workspace
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Arrow */}
|
||||
<svg
|
||||
<ChevronDown
|
||||
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 */}
|
||||
@@ -468,7 +432,10 @@ export default function ChatCanvas() {
|
||||
<button
|
||||
onClick={logout}
|
||||
className="
|
||||
flex
|
||||
w-full
|
||||
items-center
|
||||
gap-2
|
||||
px-4
|
||||
py-3
|
||||
text-left
|
||||
@@ -479,36 +446,38 @@ export default function ChatCanvas() {
|
||||
hover:bg-red-50
|
||||
"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</aside>
|
||||
</>
|
||||
|
||||
)}
|
||||
<div className="flex-1 min-w-0 flex flex-col gap-4">
|
||||
<div className="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">
|
||||
<div className="flex min-h-[56vh] flex-col items-center justify-center rounded-2xl border border-slate-200 bg-white px-4 py-10 text-center shadow-sm sm:px-8">
|
||||
<h2 className="mb-3 text-3xl font-extrabold tracking-tight text-slate-900 sm:text-4xl">
|
||||
Welcome to Yoda
|
||||
</h2>
|
||||
|
||||
<p className="mb-10 text-lg text-slate-500">
|
||||
<p className="mb-8 max-w-xl text-base text-slate-500 sm:text-lg">
|
||||
Ask questions about your business data
|
||||
</p>
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-2 max-w-2xl w-full">
|
||||
<div className="grid w-full max-w-2xl gap-3 md:grid-cols-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?",
|
||||
"what is the OSA percentage for the month of April 2026?",
|
||||
].map((item) => (
|
||||
<button
|
||||
key={item}
|
||||
onClick={() => setInput(item)}
|
||||
className="rounded-2xl border border-slate-200 bg-white p-5 text-left shadow-sm transition-all duration-200 hover:-translate-y-1 hover:border-indigo-200 hover:bg-indigo-50/40 hover:shadow-xl"
|
||||
className="rounded-xl border border-slate-200 bg-white p-4 text-left text-sm shadow-sm transition-all duration-200 hover:border-indigo-200 hover:bg-indigo-50/40 hover:shadow-md sm:p-5"
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
@@ -529,10 +498,10 @@ export default function ChatCanvas() {
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"max-w-[900px] mx-auto",
|
||||
"mx-auto max-w-[920px]",
|
||||
isUser
|
||||
? "rounded-3xl bg-gradient-to-r from-indigo-600 to-cyan-600 px-5 py-3 text-sm text-white shadow-lg"
|
||||
: "w-full min-w-0 rounded-3xl border border-slate-200 bg-white p-4 shadow-sm",
|
||||
? "max-w-[min(720px,85vw)] rounded-2xl bg-gradient-to-r from-indigo-600 to-cyan-600 px-4 py-3 text-sm text-white shadow-sm sm:px-5"
|
||||
: "w-full min-w-0 rounded-2xl border border-slate-200 bg-white p-2 shadow-sm sm:p-3",
|
||||
)}
|
||||
>
|
||||
{/* {isUser ? (
|
||||
@@ -598,7 +567,7 @@ export default function ChatCanvas() {
|
||||
|
||||
{isSending ? (
|
||||
<article className="flex justify-start">
|
||||
<div className="max-w-[760px] rounded-3xl border border-slate-200 bg-white px-5 py-4 shadow-lg">
|
||||
<div className="max-w-[760px] rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2 rounded-full bg-background px-3 py-2 shadow-inner shadow-black/5">
|
||||
<span className="typing-dot" />
|
||||
@@ -685,11 +654,11 @@ export default function ChatCanvas() {
|
||||
</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">
|
||||
<aside className="hidden min-w-0 xl:block">
|
||||
<div className="sticky top-20 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)]">
|
||||
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm">
|
||||
<h4 className="mb-3 font-medium">
|
||||
Quick Prompts
|
||||
</h4>
|
||||
@@ -698,12 +667,12 @@ export default function ChatCanvas() {
|
||||
{[
|
||||
"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?",
|
||||
"what is the OSA percentage for the month of April 2026?"
|
||||
].map((item) => (
|
||||
<button
|
||||
key={item}
|
||||
onClick={() => setInput(item)}
|
||||
className="w-full rounded-lg border p-2 text-left text-sm transition hover:bg-slate-100"
|
||||
className="w-full rounded-xl border border-slate-200 p-3 text-left text-sm leading-snug transition hover:bg-slate-100"
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
@@ -712,7 +681,7 @@ export default function ChatCanvas() {
|
||||
</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)]">
|
||||
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm">
|
||||
<h3 className="text-lg font-semibold">
|
||||
Ask Yoda
|
||||
</h3>
|
||||
@@ -746,6 +715,31 @@ export default function ChatCanvas() {
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<form
|
||||
onSubmit={onSubmit}
|
||||
className="fixed inset-x-0 bottom-0 z-40 border-t border-slate-200 bg-white/95 px-3 py-3 shadow-[0_-8px_30px_rgba(15,23,42,0.08)] backdrop-blur xl:hidden"
|
||||
>
|
||||
<div className="mx-auto flex max-w-3xl gap-2">
|
||||
<Input
|
||||
value={input}
|
||||
disabled={isSending}
|
||||
onChange={(e) => {
|
||||
setInput(e.target.value);
|
||||
setError("");
|
||||
}}
|
||||
placeholder="Type your question..."
|
||||
className="h-11 min-w-0 rounded-xl"
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="h-11 shrink-0 rounded-xl bg-gradient-to-r from-indigo-600 to-cyan-600 px-5 text-white hover:opacity-90"
|
||||
type="submit"
|
||||
disabled={isSending || !input.trim()}
|
||||
>
|
||||
{isSending ? "Sending..." : "Send"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
{/* <form className="fixed inset-x-0 bottom-0 z-20 px-3 pb-4" onSubmit={onSubmit}> */}
|
||||
|
||||
Reference in New Issue
Block a user