diff --git a/vibn-frontend/components/project/table-viewer.tsx b/vibn-frontend/components/project/table-viewer.tsx index f7e232f9..a1866ce6 100644 --- a/vibn-frontend/components/project/table-viewer.tsx +++ b/vibn-frontend/components/project/table-viewer.tsx @@ -7,6 +7,7 @@ */ import { useEffect, useState } from "react"; +import { THEME } from "@/components/project/dashboard-ui"; import { Loader2, AlertCircle, Info } from "lucide-react"; interface PreviewedTable { @@ -37,32 +38,51 @@ export function TableViewer({ projectId, dbUuid, schema, table }: Props) { setError(null); setData(null); - const url = `/api/projects/${projectId}/databases/${dbUuid}/preview` + + const url = + `/api/projects/${projectId}/databases/${dbUuid}/preview` + `?schema=${encodeURIComponent(schema)}&table=${encodeURIComponent(table)}`; fetch(url, { credentials: "include", signal: ctrl.signal }) - .then(async r => { + .then(async (r) => { let body: unknown = {}; - try { body = await r.json(); } catch {/* keep {} */} - if (!r.ok) throw new Error((body as { error?: string }).error || `HTTP ${r.status}`); + try { + body = await r.json(); + } catch { + /* keep {} */ + } + if (!r.ok) + throw new Error( + (body as { error?: string }).error || `HTTP ${r.status}`, + ); return body as PreviewedTable; }) - .then(d => { if (!cancelled) setData(d); }) - .catch(err => { + .then((d) => { + if (!cancelled) setData(d); + }) + .catch((err) => { if (cancelled) return; if (err?.name === "AbortError") setError("Timed out after 12s."); else setError(err?.message || "Failed to load preview"); }) - .finally(() => { clearTimeout(t); if (!cancelled) setLoading(false); }); + .finally(() => { + clearTimeout(t); + if (!cancelled) setLoading(false); + }); - return () => { cancelled = true; ctrl.abort(); clearTimeout(t); }; + return () => { + cancelled = true; + ctrl.abort(); + clearTimeout(t); + }; }, [projectId, dbUuid, schema, table]); if (loading) { return (
{schema}.{table}
+ {data.truncated && " (truncated to first 50)"} · {data.columns.length}{" "}
+ column{data.columns.length === 1 ? "" : "s"} ·{" "}
+
+ {schema}.{table}
+
| {c} | + {data.columns.map((c) => ( ++ {c} + | ))}
|---|---|
| {row[c]} | @@ -129,52 +153,83 @@ const INK = { } as const; const wrap: React.CSSProperties = { - display: "flex", flexDirection: "column", gap: 8, minHeight: 0, flex: 1, + display: "flex", + flexDirection: "column", + gap: 8, + minHeight: 0, + flex: 1, }; const meta: React.CSSProperties = { - fontSize: "0.74rem", color: INK.mid, + fontSize: "0.74rem", + color: INK.mid, }; const qual: React.CSSProperties = { - fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', + fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: INK.ink, }; const tableScroll: React.CSSProperties = { - flex: 1, minHeight: 0, overflow: "auto", - border: `1px solid ${INK.borderSoft}`, borderRadius: 6, + flex: 1, + minHeight: 0, + overflow: "auto", + border: `1px solid ${INK.borderSoft}`, + borderRadius: 6, }; const tableEl: React.CSSProperties = { borderCollapse: "collapse", - fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', + fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: "0.76rem", width: "100%", }; const th: React.CSSProperties = { - position: "sticky", top: 0, - textAlign: "left", padding: "6px 10px", - background: "#fafaf6", color: INK.ink, - fontWeight: 600, fontSize: "0.72rem", + position: "sticky", + top: 0, + textAlign: "left", + padding: "6px 10px", + background: "#fafaf6", + color: INK.ink, + fontWeight: 600, + fontSize: "0.72rem", borderBottom: `1px solid ${INK.border}`, whiteSpace: "nowrap", }; const td: React.CSSProperties = { - padding: "5px 10px", color: INK.ink, + padding: "5px 10px", + color: INK.ink, borderBottom: `1px solid ${INK.borderSoft}`, - whiteSpace: "nowrap", maxWidth: 360, - overflow: "hidden", textOverflow: "ellipsis", + whiteSpace: "nowrap", + maxWidth: 360, + overflow: "hidden", + textOverflow: "ellipsis", }; const trEven: React.CSSProperties = { background: "#fff" }; const trOdd: React.CSSProperties = { background: "#fcfaf3" }; const center: React.CSSProperties = { - flex: 1, display: "flex", alignItems: "center", justifyContent: "center", - color: INK.mid, fontSize: "0.85rem", + flex: 1, + display: "flex", + alignItems: "center", + justifyContent: "center", + color: INK.mid, + fontSize: "0.85rem", }; const errorBox: React.CSSProperties = { - display: "flex", alignItems: "center", gap: 6, - padding: "10px 12px", fontSize: "0.82rem", color: "#7a1f15", - background: "#fbe9e7", border: `1px solid #f4c2bc`, borderRadius: 8, + display: "flex", + alignItems: "center", + gap: 6, + padding: "10px 12px", + fontSize: "0.82rem", + color: "#7a1f15", + background: "#fbe9e7", + border: `1px solid #f4c2bc`, + borderRadius: 8, }; const infoBox: React.CSSProperties = { - display: "flex", alignItems: "center", gap: 6, - padding: "10px 12px", fontSize: "0.82rem", color: INK.mid, - background: "#fafaf6", border: `1px dashed ${INK.borderSoft}`, borderRadius: 8, + display: "flex", + alignItems: "center", + gap: 6, + padding: "10px 12px", + fontSize: "0.82rem", + color: INK.mid, + background: "#fafaf6", + border: `1px dashed ${INK.borderSoft}`, + borderRadius: 8, };