feat(studio): preserve work position and guide import completion
This commit is contained in:
1 parent
c5a2b44807
commit
89af4eb1f6
6 files changed
+425
-10
No files matched your search
@@ -17,6 +17,7 @@ import { adminFetch } from "@/lib/admin-fetch";
|
||||
import { previewAutoCatalog } from "@/lib/furni/auto-catalog";
|
||||
import { compareFurniture } from "@/lib/furni/studio-inspection";
|
||||
import { FurnitureComparison } from "./furniture-inspector";
|
||||
import { importRecommendation } from "./import-recommendation";
|
||||
import { ImportSourceDiagnostics } from "./import-source-diagnostics";
|
||||
import type { FurniItem } from "./studio-types";
|
||||
import { useFurnitureInspection } from "./use-furniture-inspection";
|
||||
@@ -39,6 +40,10 @@ export function ImportReview({
|
||||
busy?: boolean;
|
||||
onConfirm: (items: FurniItem[]) => void;
|
||||
}) {
|
||||
const preflightT = useTranslations("admin.studio.preflight");
|
||||
const [reviewFilter, setReviewFilter] = useState<
|
||||
"all" | "complete" | "conflict" | "verify"
|
||||
>("all");
|
||||
const diagnosticsT = useTranslations("admin.studio.importDiagnostics");
|
||||
const completenessT = useTranslations("admin.studio.completeness");
|
||||
const [attachments, setAttachments] = useState<Record<string, string>>({});
|
||||
@@ -97,13 +102,26 @@ export function ImportReview({
|
||||
compareFurniture(item, local).state !== "conflict"
|
||||
);
|
||||
}).length;
|
||||
const pageItems = (step === "review" ? items : ready).slice(
|
||||
const recommendations = new Map(
|
||||
inspection.items.map((local) => [
|
||||
local.classname,
|
||||
importRecommendation(local),
|
||||
]),
|
||||
);
|
||||
const reviewItems =
|
||||
reviewFilter === "all"
|
||||
? items
|
||||
: items.filter(
|
||||
(item) =>
|
||||
recommendations.get(item.classname)?.action === reviewFilter,
|
||||
);
|
||||
const pageItems = (step === "review" ? reviewItems : ready).slice(
|
||||
page * 50,
|
||||
(page + 1) * 50,
|
||||
);
|
||||
const pageCount = Math.max(
|
||||
1,
|
||||
Math.ceil((step === "review" ? items : ready).length / 50),
|
||||
Math.ceil((step === "review" ? reviewItems : ready).length / 50),
|
||||
);
|
||||
return (
|
||||
<Dialog
|
||||
@@ -220,10 +238,49 @@ export function ImportReview({
|
||||
)}
|
||||
{step === "review" ? (
|
||||
<div className="space-y-2">
|
||||
<fieldset
|
||||
className="flex flex-wrap gap-2"
|
||||
aria-label={preflightT("filterLabel")}
|
||||
>
|
||||
{(["all", "complete", "conflict", "verify"] as const).map(
|
||||
(filter) => (
|
||||
<Button
|
||||
key={filter}
|
||||
variant="outline"
|
||||
aria-pressed={reviewFilter === filter}
|
||||
onClick={() => {
|
||||
setReviewFilter(filter);
|
||||
setPage(0);
|
||||
setExpanded("");
|
||||
}}
|
||||
>
|
||||
{preflightT(`filters.${filter}`, {
|
||||
count:
|
||||
filter === "all"
|
||||
? items.length
|
||||
: items.filter(
|
||||
(item) =>
|
||||
recommendations.get(item.classname)
|
||||
?.action === filter,
|
||||
).length,
|
||||
})}
|
||||
</Button>
|
||||
),
|
||||
)}
|
||||
</fieldset>
|
||||
{reviewItems.length === 0 && (
|
||||
<p
|
||||
role="status"
|
||||
className="p-3 text-sm text-[var(--admin-text-muted)]"
|
||||
>
|
||||
{preflightT("empty")}
|
||||
</p>
|
||||
)}
|
||||
{pageItems.map((item) => {
|
||||
const local = localByName.get(item.classname);
|
||||
if (!local) return null;
|
||||
const comparison = compareFurniture(item, local);
|
||||
const recommendation = importRecommendation(local);
|
||||
return (
|
||||
<div
|
||||
key={item.classname}
|
||||
@@ -244,6 +301,31 @@ export function ImportReview({
|
||||
<span className="block text-xs text-[var(--admin-text-muted)]">
|
||||
{item.classname}
|
||||
</span>
|
||||
<span className="mt-1 block text-xs font-medium">
|
||||
{preflightT(`actions.${recommendation.action}`)}
|
||||
</span>
|
||||
{recommendation.missing.length > 0 && (
|
||||
<span className="mt-1 block text-xs text-[var(--admin-warning)]">
|
||||
{preflightT("missing", {
|
||||
parts: recommendation.missing
|
||||
.map((part) =>
|
||||
completenessT(`parts.${part}`),
|
||||
)
|
||||
.join(", "),
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
{recommendation.unknown.length > 0 && (
|
||||
<span className="mt-1 block text-xs text-[var(--admin-text-muted)]">
|
||||
{preflightT("unknown", {
|
||||
parts: recommendation.unknown
|
||||
.map((part) =>
|
||||
completenessT(`parts.${part}`),
|
||||
)
|
||||
.join(", "),
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span>
|
||||
{comparison.state === "new"
|
||||
|
||||
Reference in new issue
Block a user