"use client"; import Link from "next/link"; import { useTranslations } from "next-intl"; import { useState } from "react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; 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"; import { useSourceAssetChecks } from "./use-source-asset-checks"; export function ImportReview({ items, sourceId, source, translation, onCancel, busy = false, onConfirm, }: { items: FurniItem[]; sourceId?: string; source: string; translation: string; onCancel: () => void; busy?: boolean; onConfirm: (items: FurniItem[]) => void; }) { const preflightT = useTranslations("admin.studio.preflight"); const reviewT = useTranslations("admin.studio.review"); 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>({}); const [uploading, setUploading] = useState(null); async function attach(item: FurniItem, file: File) { setUploading(item.classname); try { const form = new FormData(); form.set("classname", item.classname); form.set("file", file); const response = await adminFetch("/api/admin/studio/import-attachment", { method: "POST", body: form, }); const data = await response.json(); if (!response.ok) throw Error(data.error || reviewT("uploadFailed")); setAttachments((current) => ({ ...current, [item.classname]: data.attachmentId, })); toast.success(diagnosticsT("attached")); } catch (error) { toast.error( error instanceof Error ? error.message : reviewT("uploadFailed"), ); } finally { setUploading(null); } } const progressT = useTranslations("pages.admin.importProgress"); const inspection = useFurnitureInspection( items.map((item) => item.classname), ); const assets = useSourceAssetChecks(items, sourceId, inspection); const unavailable = assets.items.filter( (item) => item.state === "missing" && !attachments[item.classname], ); const [expanded, setExpanded] = useState(items[0]?.classname ?? ""); const [step, setStep] = useState<"review" | "confirm">("review"); const [page, setPage] = useState(0); const localByName = new Map( inspection.items.map((item) => [item.classname, item]), ); const complete = items.every((item) => localByName.has(item.classname)); const ready = items.filter((item) => { const local = localByName.get(item.classname); return local && compareFurniture(item, local).state !== "conflict"; }); const blocked = items.filter((item) => { const local = localByName.get(item.classname); return local && compareFurniture(item, local).state === "conflict"; }).length; const existing = items.filter((item) => { const local = localByName.get(item.classname); return ( local && local.sql.length > 0 && compareFurniture(item, local).state !== "conflict" ); }).length; 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" ? reviewItems : ready).length / 50), ); return ( { if (!open && !busy && !uploading) onCancel(); }} > {step === "review" ? reviewT("titleReview") : reviewT("titleConfirm")} {reviewT("steps")}

{reviewT("source")} {source}

{reviewT("destination")}{" "} {reviewT("destinationValue")}

{reviewT("translation")} {translation}

{reviewT("selection")}{" "} {reviewT("selectionCount", { count: items.length })}

{assets.loading && !inspection.loading && (

{reviewT("checkingSource", { count: assets.items.length })}

)} {assets.error && (

{assets.error}

)} {assets.items.some((item) => item.state === "unknown") && (

{reviewT("unknownSources")}

)} {unavailable.map((check) => (

{reviewT("sourceNotFound", { name: check.classname })}

{progressT("recoveryHint")}

))} {inspection.loading ? (

{reviewT("checkingLocal")}

) : inspection.error ? (

{inspection.error}

) : !complete ? (

{reviewT("incomplete")}

) : ( <>

{reviewT("summary", { ready: ready.length, existing, blocked, warnings: unavailable.length, })}

{blocked > 0 && (

{completenessT("excludedConflicts", { count: blocked })}

{completenessT("openAudit")}
)} {inspection.items.some( (item) => !item.furnidataReadable || item.icon.exists === null || item.nitro.exists === null, ) && (

{reviewT("localUnverified")}

)} {step === "review" ? (
{(["all", "complete", "conflict", "verify"] as const).map( (filter) => ( ), )}
{reviewItems.length === 0 && (

{preflightT("empty")}

)} {pageItems.map((item) => { const local = localByName.get(item.classname); if (!local) return null; const comparison = compareFurniture(item, local); const recommendation = importRecommendation(local); return (
{expanded === item.classname && (
{local.nitro.exists !== true && ( )} {assets.items.find( (check) => check.classname === item.classname, ) && ( check.classname === item.classname, )} /> )}
)}
); })}
) : ( <>

{reviewT("confirmIntro", { count: ready.length })}

{reviewT("confirmEstimates")}

    {pageItems.map((item) => { const placement = previewAutoCatalog( item.classname, item.type, ); return (
  • {item.name}

    {reviewT("placement", { category: placement.categoryLabel, credits: placement.credits, points: placement.points, })}

  • ); })}
)} )}
{!inspection.loading && !inspection.error && pageCount > 1 && (
{reviewT("page", { page: page + 1, total: pageCount })}
)}
); }