diff --git a/src/app/api/admin/import/furni/organize/route.ts b/src/app/api/admin/import/furni/organize/route.ts new file mode 100644 index 0000000000..0e2889515d --- /dev/null +++ b/src/app/api/admin/import/furni/organize/route.ts @@ -0,0 +1,42 @@ +import { withAdmin } from "@/lib/api-handler"; +import { apiError, apiOk } from "@/lib/api-response"; +import { PERMS } from "@/lib/permissions"; +import { logAudit } from "@/lib/services/audit"; +import { organizeCatalogAll } from "@/lib/services/catalog-repair"; +import { rcon } from "@/lib/services/rcon"; + +// ── Fully automatic catalog organization of the whole database ────── +export const POST = withAdmin( + { permission: PERMS.ASSETS_IMPORT }, + async (_request, ctx) => { + try { + const result = await organizeCatalogAll(); + + // Refresh game server caches so the reorganized catalog shows up. + try { + await rcon.updateCatalog(); + } catch (err) { + console.warn( + "[organize-furni] RCON catalog refresh failed:", + (err as Error).message, + ); + } + + logAudit({ + userId: ctx.session.user.id, + action: "furni_organize", + target: "CatalogItems", + targetId: 0, + after: { + pagesCreated: result.pagesCreated, + catalogAdded: result.catalogAdded, + catalogRehomed: result.catalogRehomed, + }, + }); + + return apiOk({ ...result }); + } catch (err) { + return apiError((err as Error).message, 500); + } + }, +); diff --git a/src/components/admin/studio/studio-client.tsx b/src/components/admin/studio/studio-client.tsx index 7c2daaa666..dd5513aa80 100644 --- a/src/components/admin/studio/studio-client.tsx +++ b/src/components/admin/studio/studio-client.tsx @@ -514,6 +514,37 @@ export function StudioClient({ } }; + const [organizing, setOrganizing] = useState(false); + + async function organizeAll() { + if (organizing) return; + setOrganizing(true); + try { + const res = await adminFetch("/api/admin/import/furni/organize", { + method: "POST", + }); + const data = await res.json(); + if (res.ok) { + const parts = [ + `Catalogus georganiseerd`, + data.pagesCreated > 0 + ? `${data.pagesCreated} pagina's aangemaakt` + : null, + data.catalogAdded > 0 ? `${data.catalogAdded} nieuw` : null, + data.catalogRehomed > 0 ? `${data.catalogRehomed} bijgewerkt` : null, + ].filter(Boolean); + toast.success(parts.join(" · ")); + fetchStats(); + } else { + toast.error(data.error || "Organiseren mislukt"); + } + } catch { + toast.error("Netwerkfout"); + } finally { + setOrganizing(false); + } + } + async function deleteItem() { if (!deleting) return; try { @@ -844,6 +875,22 @@ export function StudioClient({
+ + {selectedNonImported > 0 && (