Fix Studio card markup and improve furni browsing UX
CI / check (push) Successful in 31s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m16s

- Fix invalid nested button elements in furni grid cards that broke
  card borders, hover styles and selection rendering
- Fix stale-closure bug where switching furni source fetched from the
  previously selected source
- Add progress bar plus Cancel/Dismiss controls to batch imports
- Make the detail drawer an overlay on small screens
- Add clear-search and clear-selection actions in the toolbar
- Use pixelated image rendering and keyboard focus rings on cards
This commit is contained in:
openhands committed 2026-08-18 18:49:53 +02:00
1 parent f285a7cd98
commit 3b8bf74e35
3 files changed
+202 -90

No files matched your search

+60 -8
View File
@@ -1,7 +1,8 @@
"use client";
import { Check, Loader2, X } from "lucide-react";
import { Ban, Check, Loader2, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import type { BatchItemStatus } from "./studio-types";
@@ -10,18 +11,30 @@ export function BatchProgress({
done,
succeeded,
failed,
cancelling,
onCancel,
onDismiss,
}: {
progress: Map<string, BatchItemStatus>;
done: boolean;
succeeded: number;
failed: number;
cancelling?: boolean;
onCancel?: () => void;
onDismiss?: () => void;
}) {
const entries = [...progress.entries()];
const total = entries.length;
const completed = entries.filter(
([, s]) => s.status === "done" || s.status === "failed",
).length;
const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
return (
<div className="border-t border-[var(--admin-border)] p-3">
<div className="mb-2 flex items-center justify-between">
<div className="mb-2 flex items-center justify-between gap-3">
<span className="text-xs font-bold uppercase tracking-wider text-[var(--admin-text-muted)]">
Importing…
{done ? "Import finished" : "Importing…"}
</span>
{done ? (
<span
@@ -32,15 +45,31 @@ export function BatchProgress({
: "text-[var(--admin-success)]",
)}
>
✓ {succeeded} ok{failed > 0 ? ` · ${failed} failed` : ""}
{completed}/{total} · {succeeded} ok
{failed > 0 ? ` · ${failed} failed` : ""}
</span>
) : (
<Loader2
size={14}
className="animate-spin text-[var(--admin-accent)]"
/>
<span className="flex items-center gap-2">
<span className="text-xs font-mono text-[var(--admin-text-muted)]">
{completed}/{total}
</span>
<Loader2
size={14}
className="animate-spin text-[var(--admin-accent)]"
/>
</span>
)}
</div>
{!done && (
<div className="mb-2 h-1.5 overflow-hidden rounded-full bg-[var(--admin-accent)]/10">
<div
className="h-full rounded-full bg-[var(--admin-accent)] transition-all duration-300"
style={{ width: `${percent}%` }}
/>
</div>
)}
<div className="max-h-44 space-y-1 overflow-y-auto pr-1">
{entries.map(([classname, s]) => (
<div key={classname} className="flex items-center gap-2 text-xs">
@@ -66,6 +95,29 @@ export function BatchProgress({
</div>
))}
</div>
<div className="mt-2 flex justify-end gap-2">
{done ? (
<Button size="sm" variant="secondary" onClick={onDismiss}>
<Check size={14} />
Dismiss
</Button>
) : (
<Button
size="sm"
variant="outline"
onClick={onCancel}
disabled={cancelling}
>
{cancelling ? (
<Loader2 size={14} className="animate-spin" />
) : (
<Ban size={14} />
)}
Cancel
</Button>
)}
</div>
</div>
);
}