feat(admin): items_base browser and AdminPageShell on core pages
CI / check (push) Successful in 21s
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 59s
CI / deploy (push) Failing after 9s

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-30 20:41:31 +02:00
1 parent 7138ae4445
commit 98613af875
42 files changed
+2196 -698

No files matched your search

+115
View File
@@ -0,0 +1,115 @@
"use server";
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { Prisma } from "@/generated/prisma/client";
import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
import { adminAction } from "@/lib/safe-action";
import { ActionError, actionOk } from "@/lib/safe-action-shared";
import { rcon } from "@/lib/services/rcon";
import { logStaffActivity } from "@/lib/services/staff-activity";
const ITEMS_BASE_FIELDS = [
"publicName",
"itemName",
"type",
"spriteId",
"width",
"length",
"stackHeight",
"allowStack",
"allowSit",
"allowLay",
"allowWalk",
"allowGift",
"allowTrade",
"allowRecycle",
"allowMarketplaceSell",
"allowInventoryStack",
"interactionType",
"interactionModesCount",
"vendingIds",
"multiheight",
"customparams",
"effectIdMale",
"effectIdFemale",
"clothingOnWalk",
] as const;
const updateSchema = z.object({
id: z.coerce.number().int().positive(),
fields: z.record(z.string(), z.unknown()),
});
function pickAllowed(
fields: Record<string, unknown>,
allowed: readonly string[],
): Record<string, unknown> {
const out: Record<string, unknown> = {};
for (const key of allowed) {
if (Object.hasOwn(fields, key) && fields[key] !== undefined) {
out[key] = fields[key];
}
}
return out;
}
export const updateItemsBase = adminAction(
{ permission: PERMS.CATALOG_EDIT, schema: updateSchema },
async (ctx) => {
const { id, fields } = ctx.data;
const safe = pickAllowed(fields, ITEMS_BASE_FIELDS);
if (Object.keys(safe).length === 0) {
throw new ActionError("No valid fields to update");
}
const existing = await prisma.itemsBase.findUnique({
where: { id },
select: { id: true },
});
if (!existing) throw new ActionError("Item not found");
// Coerce common numeric / decimal fields from form strings.
const data: Record<string, unknown> = { ...safe };
for (const key of [
"spriteId",
"width",
"length",
"allowStack",
"allowSit",
"allowLay",
"allowWalk",
"allowGift",
"allowTrade",
"allowRecycle",
"allowMarketplaceSell",
"allowInventoryStack",
"interactionModesCount",
"effectIdMale",
"effectIdFemale",
]) {
if (data[key] !== undefined) data[key] = Number(data[key]);
}
if (data.stackHeight !== undefined) {
data.stackHeight = new Prisma.Decimal(String(data.stackHeight));
}
await prisma.itemsBase.update({
where: { id },
data: data as Prisma.ItemsBaseUpdateInput,
});
await rcon.updateCatalog().catch(() => false);
await logStaffActivity({
staffId: Number(ctx.session.user.id),
action: "items_base_update",
description: `Updated items_base #${id}`,
targetType: "items_base",
targetId: id,
});
revalidatePath("/admin/items");
revalidatePath(`/admin/items/${id}`);
revalidatePath("/admin/catalog");
return actionOk({ id });
},
);