fix(frontend): restore CTE card padding and hierarchy
This commit is contained in:
@@ -1,54 +1,59 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
|
||||
import { Card, CardContent, CardHeader } from "../components/ui/card";
|
||||
import { Badge } from "../components/ui/badge";
|
||||
import type { CtePlanCte, CtePlanFilter, CtePlanV2 } from "./artifactV2";
|
||||
|
||||
const CHIP = "rounded-md bg-muted px-1.5 py-0.5 font-mono text-xs text-foreground/90";
|
||||
const CHIP =
|
||||
"min-w-0 max-w-full whitespace-pre-wrap [overflow-wrap:anywhere] rounded-md bg-muted px-1.5 py-0.5 font-mono text-xs text-foreground/90";
|
||||
const FIELD_LABEL = "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted-foreground";
|
||||
|
||||
function Chip({ children }: { children: ReactNode }) {
|
||||
return <span className={CHIP}>{children}</span>;
|
||||
}
|
||||
|
||||
function FilterCard({ filter, index }: { filter: CtePlanFilter; index: number }) {
|
||||
function FilterRow({ filter, index }: { filter: CtePlanFilter; index: number }) {
|
||||
return (
|
||||
<div
|
||||
role="group"
|
||||
aria-label={`Filter ${index}`}
|
||||
className="space-y-3 rounded-lg border border-border/60 bg-muted/20 p-3"
|
||||
className="min-w-0 space-y-3 p-3 sm:p-4"
|
||||
>
|
||||
<div className="grid min-w-0 grid-cols-1 gap-3 sm:grid-cols-[minmax(0,1fr)_minmax(5rem,auto)_minmax(0,1fr)]">
|
||||
<div className="grid min-w-0 grid-cols-1 gap-3 md:grid-cols-[minmax(0,1fr)_minmax(5rem,auto)_minmax(0,1fr)]">
|
||||
<div className="min-w-0 space-y-1">
|
||||
<p className={FIELD_LABEL}>Column</p>
|
||||
<code className="block [overflow-wrap:anywhere] font-mono text-xs leading-relaxed text-foreground">
|
||||
<code className="block min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere] font-mono text-xs leading-relaxed text-foreground">
|
||||
{filter.column}
|
||||
</code>
|
||||
</div>
|
||||
<div className="min-w-0 space-y-1 sm:text-center">
|
||||
<div className="min-w-0 space-y-1 md:text-center">
|
||||
<p className={FIELD_LABEL}>Operator</p>
|
||||
<code className="block [overflow-wrap:anywhere] font-mono text-xs font-semibold leading-relaxed text-primary">
|
||||
<code className="block min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere] font-mono text-xs font-semibold leading-relaxed text-primary">
|
||||
{filter.op}
|
||||
</code>
|
||||
</div>
|
||||
<div className="min-w-0 space-y-1">
|
||||
<p className={FIELD_LABEL}>Value</p>
|
||||
<code className="block whitespace-pre-wrap [overflow-wrap:anywhere] font-mono text-xs leading-relaxed text-foreground">
|
||||
<code className="block min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere] font-mono text-xs leading-relaxed text-foreground">
|
||||
{filter.value}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
{(filter.description || filter.rationale) && (
|
||||
<div className="grid gap-x-4 gap-y-2 border-t border-border/50 pt-3 sm:grid-cols-[5.5rem_minmax(0,1fr)]">
|
||||
<div className="grid min-w-0 gap-x-4 gap-y-2 border-t border-border/50 pt-3 md:grid-cols-[5.5rem_minmax(0,1fr)]">
|
||||
{filter.description && (
|
||||
<>
|
||||
<span className={FIELD_LABEL}>Description</span>
|
||||
<p className="text-xs leading-relaxed text-muted-foreground">{filter.description}</p>
|
||||
<p className="min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere] text-xs leading-relaxed text-muted-foreground">
|
||||
{filter.description}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
{filter.rationale && (
|
||||
<>
|
||||
<span className={FIELD_LABEL}>Rationale</span>
|
||||
<p className="text-xs leading-relaxed text-muted-foreground">{filter.rationale}</p>
|
||||
<p className="min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere] text-xs leading-relaxed text-muted-foreground">
|
||||
{filter.rationale}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -61,26 +66,28 @@ function CteCard({ cte, total }: { cte: CtePlanCte; total: number }) {
|
||||
const dependsOn = cte.depends_on ?? [];
|
||||
|
||||
return (
|
||||
<Card className="overflow-hidden">
|
||||
<CardHeader className="gap-3 border-b border-border/60 bg-muted/15 pb-4">
|
||||
<Card className="gap-0 rounded-lg border py-0 shadow-none">
|
||||
<CardHeader className="gap-3 rounded-t-lg border-b border-border/60 bg-muted/15 p-4 sm:p-5">
|
||||
<div className="grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-center gap-2.5">
|
||||
<Badge variant="outline" className="font-mono">
|
||||
CTE {cte.index}/{total}
|
||||
</Badge>
|
||||
<CardTitle className="min-w-0 [overflow-wrap:anywhere] font-mono text-sm leading-relaxed">
|
||||
<h3 className="min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere] font-mono text-sm font-medium leading-relaxed">
|
||||
{cte.name}
|
||||
</CardTitle>
|
||||
</h3>
|
||||
</div>
|
||||
{cte.purpose && (
|
||||
<p className="text-sm leading-relaxed text-muted-foreground">{cte.purpose}</p>
|
||||
<p className="min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere] text-sm leading-relaxed text-muted-foreground">
|
||||
{cte.purpose}
|
||||
</p>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-5 pt-1">
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<section>
|
||||
<CardContent className="flex min-w-0 flex-col gap-5 p-4 sm:p-5">
|
||||
<div className="grid min-w-0 gap-4 md:grid-cols-2">
|
||||
<section className="min-w-0">
|
||||
<h4 className="thot-label mb-2">Depends on</h4>
|
||||
{dependsOn.length ? (
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
<div className="flex min-w-0 flex-wrap gap-1.5">
|
||||
{dependsOn.map((dependency) => (
|
||||
<Chip key={dependency}>{dependency}</Chip>
|
||||
))}
|
||||
@@ -91,9 +98,9 @@ function CteCard({ cte, total }: { cte: CtePlanCte; total: number }) {
|
||||
</section>
|
||||
|
||||
{cte.keys && cte.keys.length > 0 && (
|
||||
<section>
|
||||
<section className="min-w-0">
|
||||
<h4 className="thot-label mb-2">Keys</h4>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
<div className="flex min-w-0 flex-wrap gap-1.5">
|
||||
{cte.keys.map((key) => (
|
||||
<Chip key={key}>{key}</Chip>
|
||||
))}
|
||||
@@ -103,19 +110,19 @@ function CteCard({ cte, total }: { cte: CtePlanCte; total: number }) {
|
||||
</div>
|
||||
|
||||
{cte.tables && cte.tables.length > 0 && (
|
||||
<section>
|
||||
<section className="min-w-0">
|
||||
<h4 className="thot-label mb-2">Tables</h4>
|
||||
<div className="divide-y divide-border/50 rounded-lg border border-border/60">
|
||||
<div className="min-w-0 divide-y divide-border/50 rounded-lg border border-border/60">
|
||||
{cte.tables.map((table) => (
|
||||
<div
|
||||
key={table.name}
|
||||
className="grid min-w-0 gap-1 px-3 py-2.5 sm:grid-cols-[minmax(10rem,0.8fr)_minmax(0,1.2fr)] sm:gap-4"
|
||||
className="grid min-w-0 gap-1 p-3 sm:p-4 md:grid-cols-[minmax(10rem,0.8fr)_minmax(0,1.2fr)] md:gap-4"
|
||||
>
|
||||
<code className="[overflow-wrap:anywhere] font-mono text-xs leading-relaxed text-foreground">
|
||||
<code className="min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere] font-mono text-xs leading-relaxed text-foreground">
|
||||
{table.name}
|
||||
</code>
|
||||
{table.description && (
|
||||
<p className="text-xs leading-relaxed text-muted-foreground">
|
||||
<p className="min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere] text-xs leading-relaxed text-muted-foreground">
|
||||
{table.description}
|
||||
</p>
|
||||
)}
|
||||
@@ -126,20 +133,20 @@ function CteCard({ cte, total }: { cte: CtePlanCte; total: number }) {
|
||||
)}
|
||||
|
||||
{cte.filters && cte.filters.length > 0 && (
|
||||
<section>
|
||||
<section className="min-w-0">
|
||||
<h4 className="thot-label mb-2">Filters</h4>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="min-w-0 divide-y divide-border/50 rounded-lg border border-border/60">
|
||||
{cte.filters.map((f, i) => (
|
||||
<FilterCard key={`${f.column}-${i}`} filter={f} index={i + 1} />
|
||||
<FilterRow key={`${f.column}-${i}`} filter={f} index={i + 1} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{cte.output_columns && cte.output_columns.length > 0 && (
|
||||
<section>
|
||||
<section className="min-w-0">
|
||||
<h4 className="thot-label mb-2">Output columns</h4>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
<div className="flex min-w-0 flex-wrap gap-1.5">
|
||||
{cte.output_columns.map((column) => (
|
||||
<Chip key={column}>{column}</Chip>
|
||||
))}
|
||||
@@ -148,9 +155,11 @@ function CteCard({ cte, total }: { cte: CtePlanCte; total: number }) {
|
||||
)}
|
||||
|
||||
{cte.rationale && (
|
||||
<section className="rounded-lg border-l-2 border-primary/40 bg-primary/5 px-3.5 py-3">
|
||||
<section className="min-w-0 border-t border-border/60 pt-4">
|
||||
<h4 className="thot-label mb-1.5">Rationale</h4>
|
||||
<p className="text-sm leading-relaxed text-foreground/90">{cte.rationale}</p>
|
||||
<p className="whitespace-pre-wrap [overflow-wrap:anywhere] text-sm leading-relaxed text-foreground/90">
|
||||
{cte.rationale}
|
||||
</p>
|
||||
</section>
|
||||
)}
|
||||
</CardContent>
|
||||
@@ -160,17 +169,38 @@ function CteCard({ cte, total }: { cte: CtePlanCte; total: number }) {
|
||||
|
||||
export function CtePlanViewer({ plan }: { plan: CtePlanV2 }) {
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{plan.question && <p className="text-sm leading-relaxed text-muted-foreground">{plan.question}</p>}
|
||||
{plan.strategy && <p className="thot-prose leading-relaxed">{plan.strategy}</p>}
|
||||
<div className="flex min-w-0 flex-col gap-6">
|
||||
<div className="flex min-w-0 flex-col gap-3">
|
||||
{plan.question && (
|
||||
<div className="min-w-0 space-y-1.5">
|
||||
<p className="thot-label">Question</p>
|
||||
<p className="whitespace-pre-wrap [overflow-wrap:anywhere] text-sm leading-relaxed text-muted-foreground">
|
||||
{plan.question}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{plan.strategy && (
|
||||
<div className="min-w-0 space-y-1.5">
|
||||
<p className="thot-label">Strategy</p>
|
||||
<p className="thot-prose whitespace-pre-wrap [overflow-wrap:anywhere] leading-relaxed">
|
||||
{plan.strategy}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-wrap items-center gap-1.5 font-mono text-xs text-muted-foreground">
|
||||
{plan.ctes.map((c, i) => (
|
||||
<span key={c.name} className="flex items-center gap-1.5">
|
||||
{i > 0 && <span aria-hidden="true">→</span>}
|
||||
<span className="text-foreground/90">{c.name}</span>
|
||||
</span>
|
||||
))}
|
||||
<div className="min-w-0 space-y-1.5">
|
||||
<p className="thot-label">Execution order</p>
|
||||
<div className="flex min-w-0 flex-wrap items-center gap-1.5 font-mono text-xs text-muted-foreground">
|
||||
{plan.ctes.map((c, i) => (
|
||||
<span key={c.name} className="flex min-w-0 max-w-full items-center gap-1.5">
|
||||
{i > 0 && <span aria-hidden="true">→</span>}
|
||||
<span className="min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere] text-foreground/90">
|
||||
{c.name}
|
||||
</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
|
||||
Reference in New Issue
Block a user