diff --git a/src/app/admin/studio/audit/page.tsx b/src/app/admin/studio/audit/page.tsx
index d655b260..e929fc39 100644
--- a/src/app/admin/studio/audit/page.tsx
+++ b/src/app/admin/studio/audit/page.tsx
@@ -1,5 +1,8 @@
+import { ClipboardCheck } from "lucide-react";
import { redirect } from "next/navigation";
+import { getTranslations } from "next-intl/server";
import { AuditClient } from "@/app/admin/import/audit/audit-client";
+import { StudioPage } from "@/components/admin/studio/studio-page";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
export default async function StudioAuditPage(_props: {
@@ -9,16 +12,14 @@ export default async function StudioAuditPage(_props: {
if (!canAccess(permissions, PERMS.ASSETS_IMPORT, session.user.rank)) {
redirect("/admin");
}
+ const t = await getTranslations("admin.studio");
return (
-
-
-
Catalog Audit
-
- Scan your entire furniture catalog for missing entries, broken
- references, missing files, and items available from clone sources.
-
-
+
-
+
);
}
diff --git a/src/app/admin/studio/badges/page.tsx b/src/app/admin/studio/badges/page.tsx
index de779611..b597aeda 100644
--- a/src/app/admin/studio/badges/page.tsx
+++ b/src/app/admin/studio/badges/page.tsx
@@ -1,5 +1,8 @@
+import { BadgeCheck } from "lucide-react";
import { redirect } from "next/navigation";
+import { getTranslations } from "next-intl/server";
import { ImportBadgesClient } from "@/app/admin/import/badges/import-badges-client";
+import { StudioPage } from "@/components/admin/studio/studio-page";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
export default async function StudioBadgesPage(_props: {
@@ -9,9 +12,14 @@ export default async function StudioBadgesPage(_props: {
if (!canAccess(permissions, PERMS.ASSETS_IMPORT, session.user.rank)) {
redirect("/admin");
}
+ const t = await getTranslations("admin.studio");
return (
-
+
-
+
);
}
diff --git a/src/app/admin/studio/clone/page.tsx b/src/app/admin/studio/clone/page.tsx
index a626098e..0c281d98 100644
--- a/src/app/admin/studio/clone/page.tsx
+++ b/src/app/admin/studio/clone/page.tsx
@@ -1,5 +1,8 @@
+import { Copy } from "lucide-react";
import { redirect } from "next/navigation";
+import { getTranslations } from "next-intl/server";
import { ImportCloneClient } from "@/app/admin/import/clone/import-clone-client";
+import { StudioPage } from "@/components/admin/studio/studio-page";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
export default async function StudioClonePage(_props: {
@@ -9,9 +12,14 @@ export default async function StudioClonePage(_props: {
if (!canAccess(permissions, PERMS.ASSETS_IMPORT, session.user.rank)) {
redirect("/admin");
}
+ const t = await getTranslations("admin.studio");
return (
-
+
-
+
);
}
diff --git a/src/app/admin/studio/clothing/page.tsx b/src/app/admin/studio/clothing/page.tsx
index 342bfee7..26544c20 100644
--- a/src/app/admin/studio/clothing/page.tsx
+++ b/src/app/admin/studio/clothing/page.tsx
@@ -1,5 +1,8 @@
+import { Shirt } from "lucide-react";
import { redirect } from "next/navigation";
+import { getTranslations } from "next-intl/server";
import { ImportClothingClient } from "@/app/admin/import/clothing/import-clothing-client";
+import { StudioPage } from "@/components/admin/studio/studio-page";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
export default async function StudioClothingPage(_props: {
@@ -9,9 +12,14 @@ export default async function StudioClothingPage(_props: {
if (!canAccess(permissions, PERMS.ASSETS_IMPORT, session.user.rank)) {
redirect("/admin");
}
+ const t = await getTranslations("admin.studio");
return (
-
+
-
+
);
}
diff --git a/src/app/admin/studio/effects/page.tsx b/src/app/admin/studio/effects/page.tsx
index 3ef39a6c..db57e65e 100644
--- a/src/app/admin/studio/effects/page.tsx
+++ b/src/app/admin/studio/effects/page.tsx
@@ -1,5 +1,8 @@
+import { Dumbbell } from "lucide-react";
import { redirect } from "next/navigation";
+import { getTranslations } from "next-intl/server";
import { ImportEffectsClient } from "@/app/admin/import/effects/import-effects-client";
+import { StudioPage } from "@/components/admin/studio/studio-page";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
export default async function StudioEffectsPage(_props: {
@@ -9,9 +12,14 @@ export default async function StudioEffectsPage(_props: {
if (!canAccess(permissions, PERMS.ASSETS_IMPORT, session.user.rank)) {
redirect("/admin");
}
+ const t = await getTranslations("admin.studio");
return (
-
+
-
+
);
}
diff --git a/src/app/admin/studio/loading.tsx b/src/app/admin/studio/loading.tsx
new file mode 100644
index 00000000..19191038
--- /dev/null
+++ b/src/app/admin/studio/loading.tsx
@@ -0,0 +1,30 @@
+import { Skeleton } from "@/components/ui/skeleton";
+
+/**
+ * Loading state for any Catalog Studio sub-page. Mirrors the StudioPage
+ * header (icon tile, title, subtitle) and a body block so navigation between
+ * studio tools shows a stable skeleton instead of a blank pause.
+ */
+export default function StudioLoading() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/admin/studio/maintenance/page.tsx b/src/app/admin/studio/maintenance/page.tsx
index 9cd1e8d5..1828ef3a 100644
--- a/src/app/admin/studio/maintenance/page.tsx
+++ b/src/app/admin/studio/maintenance/page.tsx
@@ -1,7 +1,9 @@
+import { Wrench } from "lucide-react";
import { redirect } from "next/navigation";
import { getTranslations } from "next-intl/server";
import { CatalogIntegrityPanel } from "@/components/admin/catalog/catalog-integrity-panel";
import { CatalogMaintenancePanel } from "@/components/admin/catalog/catalog-maintenance-panel";
+import { StudioPage } from "@/components/admin/studio/studio-page";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
export default async function StudioCatalogMaintenancePage() {
@@ -16,20 +18,27 @@ export default async function StudioCatalogMaintenancePage() {
session.user.rank,
);
const t = await getTranslations("pages.admin.catalog.integrity");
+ const studioT = await getTranslations("admin.studio");
return (
-
-
- {canRepair && (
-
-
- {t("legacyTitle")}
-
-
- {t("legacyHint")}
-
-
-
- )}
-
+
+
+
+ {canRepair && (
+
+
+ {t("legacyTitle")}
+
+
+ {t("legacyHint")}
+
+
+
+ )}
+
+
);
}
diff --git a/src/app/admin/studio/nitro-cleanup/page.tsx b/src/app/admin/studio/nitro-cleanup/page.tsx
index c8915a4e..426ba769 100644
--- a/src/app/admin/studio/nitro-cleanup/page.tsx
+++ b/src/app/admin/studio/nitro-cleanup/page.tsx
@@ -1,5 +1,8 @@
+import { FileX2 } from "lucide-react";
import { redirect } from "next/navigation";
+import { getTranslations } from "next-intl/server";
import { NitroCleanupPanel } from "@/components/admin/studio/nitro-cleanup-panel";
+import { StudioPage } from "@/components/admin/studio/studio-page";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
export default async function StudioNitroCleanupPage() {
@@ -7,16 +10,14 @@ export default async function StudioNitroCleanupPage() {
if (!canAccess(permissions, PERMS.ASSETS_IMPORT, session.user.rank)) {
redirect("/admin");
}
+ const t = await getTranslations("admin.studio");
return (
-
-
-
Nitro cleanup
-
- Scan the nitro directories for fake bundles (no matching furniture)
- and broken bundles (unparseable) and remove them.
-
-
+
-
+
);
}
diff --git a/src/app/admin/studio/pets/page.tsx b/src/app/admin/studio/pets/page.tsx
index b95d3925..03571bc7 100644
--- a/src/app/admin/studio/pets/page.tsx
+++ b/src/app/admin/studio/pets/page.tsx
@@ -1,5 +1,8 @@
+import { Cat } from "lucide-react";
import { redirect } from "next/navigation";
+import { getTranslations } from "next-intl/server";
import { ImportPetsClient } from "@/app/admin/import/pets/import-pets-client";
+import { StudioPage } from "@/components/admin/studio/studio-page";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
export default async function StudioPetsPage(_props: {
@@ -9,9 +12,14 @@ export default async function StudioPetsPage(_props: {
if (!canAccess(permissions, PERMS.ASSETS_IMPORT, session.user.rank)) {
redirect("/admin");
}
+ const t = await getTranslations("admin.studio");
return (
-
+
-
+
);
}
diff --git a/src/app/admin/studio/repair-icons/page.tsx b/src/app/admin/studio/repair-icons/page.tsx
index c4b428ec..f52bd10c 100644
--- a/src/app/admin/studio/repair-icons/page.tsx
+++ b/src/app/admin/studio/repair-icons/page.tsx
@@ -1,5 +1,8 @@
+import { Image as ImageIcon } from "lucide-react";
import { redirect } from "next/navigation";
+import { getTranslations } from "next-intl/server";
import { RepairIconsClient } from "@/app/admin/import/repair-icons/repair-icons-client";
+import { StudioPage } from "@/components/admin/studio/studio-page";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
export default async function StudioRepairIconsPage(_props: {
@@ -9,19 +12,14 @@ export default async function StudioRepairIconsPage(_props: {
if (!canAccess(permissions, PERMS.ASSETS_IMPORT, session.user.rank)) {
redirect("/admin");
}
+ const t = await getTranslations("admin.studio");
return (
-
-
-
- Repair Missing Icons
-
-
- Scan all existing furniture and download any missing icon files. Icons
- are extracted from local .nitro bundles or fetched from configured
- clone sources.
-