style(admin): prettier-format match dialog; reset unmatched page in onChange

Run prettier over the Tooltip-wrapped poster JSX, and reset the unmatched-items
page in the search input's onChange rather than a useEffect (avoids the
react-hooks/set-state-in-effect warning / cascading renders).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Silo Server Developer
2026-05-28 17:25:36 +02:00
co-authored by Claude Opus 4.7
parent 49efc3846c
commit d9b57f28e2
2 changed files with 68 additions and 69 deletions
+62 -62
View File
@@ -172,69 +172,69 @@ export default function MatchItemDialog({ item, open, onOpenChange }: MatchItemD
<div className="flex min-h-0 min-w-0 flex-1 flex-col gap-2 overflow-hidden">
<Label className="shrink-0">Results</Label>
<TooltipProvider delayDuration={150}>
<div className="overlay-scroll min-h-0 flex-1 space-y-1 overflow-y-auto overscroll-contain pr-1 pb-1">
{candidates.map((candidate, index) => {
const candidateKey = Object.entries(candidate.provider_ids)
.map(([k, v]) => `${k}-${v}`)
.join("_");
return (
<button
key={`${candidateKey}-${index}`}
type="button"
className={cn(
"flex w-full min-w-0 items-start gap-3 rounded-lg border p-3 text-left transition-colors",
selectedCandidate === candidate
? "border-primary bg-primary/5"
: "border-border hover:bg-muted/50",
)}
onClick={() => setSelectedCandidate(candidate)}
data-testid="match-candidate"
>
{candidate.image_url ? (
<Tooltip>
<TooltipTrigger asChild>
<img
src={candidate.image_url}
alt=""
className="h-24 w-16 shrink-0 cursor-zoom-in rounded object-cover"
/>
</TooltipTrigger>
<TooltipContent
side="right"
className="border-border/60 overflow-hidden border bg-transparent p-0 shadow-xl"
>
<img
src={candidate.image_url}
alt={candidate.title}
className="h-72 w-48 rounded-md object-cover"
/>
</TooltipContent>
</Tooltip>
) : (
<div className="bg-muted h-24 w-16 shrink-0 rounded" />
)}
<div className="min-w-0 flex-1">
<div className="truncate text-sm font-medium">{candidate.title}</div>
<div className="text-muted-foreground text-xs">
{candidate.year ? candidate.year : ""}
<div className="overlay-scroll min-h-0 flex-1 space-y-1 overflow-y-auto overscroll-contain pr-1 pb-1">
{candidates.map((candidate, index) => {
const candidateKey = Object.entries(candidate.provider_ids)
.map(([k, v]) => `${k}-${v}`)
.join("_");
return (
<button
key={`${candidateKey}-${index}`}
type="button"
className={cn(
"flex w-full min-w-0 items-start gap-3 rounded-lg border p-3 text-left transition-colors",
selectedCandidate === candidate
? "border-primary bg-primary/5"
: "border-border hover:bg-muted/50",
)}
onClick={() => setSelectedCandidate(candidate)}
data-testid="match-candidate"
>
{candidate.image_url ? (
<Tooltip>
<TooltipTrigger asChild>
<img
src={candidate.image_url}
alt=""
className="h-24 w-16 shrink-0 cursor-zoom-in rounded object-cover"
/>
</TooltipTrigger>
<TooltipContent
side="right"
className="border-border/60 overflow-hidden border bg-transparent p-0 shadow-xl"
>
<img
src={candidate.image_url}
alt={candidate.title}
className="h-72 w-48 rounded-md object-cover"
/>
</TooltipContent>
</Tooltip>
) : (
<div className="bg-muted h-24 w-16 shrink-0 rounded" />
)}
<div className="min-w-0 flex-1">
<div className="truncate text-sm font-medium">{candidate.title}</div>
<div className="text-muted-foreground text-xs">
{candidate.year ? candidate.year : ""}
</div>
<div className="mt-1 flex min-w-0 flex-wrap gap-1">
{candidate.sources.map((source) => (
<Badge key={source} variant="outline" className="text-[10px]">
{source}
</Badge>
))}
{candidate.sources.length > 1 && (
<Badge variant="secondary" className="text-[10px]">
{candidate.sources.length} sources agree
</Badge>
)}
</div>
</div>
<div className="mt-1 flex min-w-0 flex-wrap gap-1">
{candidate.sources.map((source) => (
<Badge key={source} variant="outline" className="text-[10px]">
{source}
</Badge>
))}
{candidate.sources.length > 1 && (
<Badge variant="secondary" className="text-[10px]">
{candidate.sources.length} sources agree
</Badge>
)}
</div>
</div>
</button>
);
})}
</div>
</button>
);
})}
</div>
</TooltipProvider>
</div>
)}
+6 -7
View File
@@ -1666,12 +1666,6 @@ function UnmatchedItemsSection() {
const rangeEnd = Math.min((clamped + 1) * UNMATCHED_PAGE_SIZE, total);
const items = data?.items ?? [];
// The search is applied server-side (spans the whole table, not just this
// page); reset to the first page whenever the debounced query changes.
useEffect(() => {
setPage(0);
}, [debouncedSearch]);
// Hide the section only when there are genuinely no unmatched items and no
// active search — keep it mounted while searching so the box and the
// "no matches" state stay visible even when a query returns nothing.
@@ -1700,7 +1694,12 @@ function UnmatchedItemsSection() {
<Input
placeholder="Search all unmatched items by title, library, or type..."
value={search}
onChange={(e) => setSearch(e.target.value)}
onChange={(e) => {
// Search is server-side and spans the whole table; jump back to
// the first page so results start at the top as the query changes.
setSearch(e.target.value);
setPage(0);
}}
className="h-8 pl-8 text-xs"
/>
</div>