88 lines
3.2 KiB
TypeScript
88 lines
3.2 KiB
TypeScript
import { Link2 } from "lucide-react";
|
|
import type { WidgetProps } from "./types";
|
|
import { ReservedControls } from "./ReservedControls";
|
|
|
|
export function JoinReviewWidget({ descriptor, onRespond }: WidgetProps) {
|
|
const options = descriptor.options ?? [];
|
|
|
|
return (
|
|
<div className="space-y-4 rounded-xl border border-border/70 bg-card p-4 shadow-sm">
|
|
<div className="space-y-1.5">
|
|
{descriptor.title && (
|
|
<p className="text-[0.95rem] font-semibold leading-snug text-foreground">
|
|
{descriptor.title}
|
|
</p>
|
|
)}
|
|
<p className="text-sm leading-relaxed text-muted-foreground">
|
|
These joins are required by the selected tables and are shown for review.
|
|
To request a correction, use Other — specify.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-2.5">
|
|
{options.map((option, index) => (
|
|
<article
|
|
key={option.id}
|
|
className="rounded-lg border border-border/70 bg-background px-3.5 py-3"
|
|
>
|
|
<div className="flex items-start gap-2.5">
|
|
<span className="mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary">
|
|
<Link2 className="size-3.5" aria-hidden="true" />
|
|
</span>
|
|
<div className="min-w-0 flex-1 space-y-2.5">
|
|
<div className="flex items-baseline gap-2">
|
|
<span className="text-xs font-medium tabular-nums text-muted-foreground">
|
|
{index + 1}
|
|
</span>
|
|
<h3 className="text-sm font-semibold leading-snug text-foreground">
|
|
{option.label}
|
|
</h3>
|
|
</div>
|
|
{option.detail && (
|
|
<code className="block whitespace-pre-wrap break-words rounded-md bg-muted/70 px-3 py-2 font-mono text-xs leading-relaxed text-foreground">
|
|
{option.detail}
|
|
</code>
|
|
)}
|
|
{option.rationale && (
|
|
<div className="grid gap-1 sm:grid-cols-[5rem_minmax(0,1fr)] sm:gap-3">
|
|
<span className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
Rationale
|
|
</span>
|
|
<p className="text-sm leading-relaxed text-muted-foreground">
|
|
{option.rationale}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</article>
|
|
))}
|
|
</div>
|
|
|
|
<button
|
|
className="rounded-md bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground shadow-xs transition-colors hover:bg-[oklch(var(--primary-hover))]"
|
|
onClick={() =>
|
|
onRespond({
|
|
id: descriptor.id,
|
|
kind: "join-review",
|
|
choices: options.map((option) => option.id),
|
|
})
|
|
}
|
|
>
|
|
{descriptor.confirm_label ?? "Continue"}
|
|
</button>
|
|
|
|
<ReservedControls
|
|
reserved={descriptor.reserved}
|
|
onControl={(control, text) =>
|
|
onRespond({
|
|
id: descriptor.id,
|
|
control,
|
|
...(text !== undefined ? { text } : {}),
|
|
})
|
|
}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|