fix(databases): apply Flowbite colors to data table viewer

This commit is contained in:
2026-06-14 14:15:15 -07:00
parent c5894775f8
commit 103ad8c81f

View File

@@ -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 (
<div style={center}>
<Loader2 size={14} className="animate-spin" />
<span style={{ marginLeft: 8 }}>Querying {schema}.{table}</span>
<span style={{ marginLeft: 8 }}>
Querying {schema}.{table}
</span>
</div>
);
}
@@ -88,23 +108,27 @@ export function TableViewer({ projectId, dbUuid, schema, table }: Props) {
<div style={wrap}>
<div style={meta}>
Showing {data.rows.length} row{data.rows.length === 1 ? "" : "s"}
{data.truncated && " (truncated to first 50)"} ·{" "}
{data.columns.length} column{data.columns.length === 1 ? "" : "s"} ·{" "}
<code style={qual}>{schema}.{table}</code>
{data.truncated && " (truncated to first 50)"} · {data.columns.length}{" "}
column{data.columns.length === 1 ? "" : "s"} ·{" "}
<code style={qual}>
{schema}.{table}
</code>
</div>
<div style={tableScroll}>
<table style={tableEl}>
<thead>
<tr>
{data.columns.map(c => (
<th key={c} style={th}>{c}</th>
{data.columns.map((c) => (
<th key={c} style={th}>
{c}
</th>
))}
</tr>
</thead>
<tbody>
{data.rows.map((row, i) => (
<tr key={i} style={i % 2 === 0 ? trEven : trOdd}>
{data.columns.map(c => (
{data.columns.map((c) => (
<td key={c} style={td} title={row[c]}>
{row[c]}
</td>
@@ -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,
};