refactor: remove explicit any across admin forms and catalog actions
Local Build and Deploy / deploy (push) Canceled after 40s

- Type resolveAsChild generically over Base UI render-prop type
- Type catalog/rooms/catalog-items Prisma updates with Prisma.*UpdateInput
- Type EventForm/PollForm with explicit form-value interfaces
- Type EventPrizesManager/PollQuestionsManager with validator input types
- All typecheck, lint, and 312 tests pass
This commit is contained in:
openhands committed 2026-07-18 20:31:18 +02:00
1 parent 907a4399d0
commit 3eaffe658d
8 files changed
+61 -23

No files matched your search

+9 -2
View File
@@ -1,6 +1,7 @@
"use server"; "use server";
import { revalidatePath } from "next/cache"; import { revalidatePath } from "next/cache";
import type { Prisma } from "@/generated/prisma/client";
import { requirePermission } from "@/lib/admin/guard"; import { requirePermission } from "@/lib/admin/guard";
import { PERMS } from "@/lib/permissions"; import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
@@ -55,7 +56,10 @@ export async function updateBcPage({
if (Object.keys(data).length === 0) { if (Object.keys(data).length === 0) {
return { ok: false as const, error: "No valid fields to update" }; return { ok: false as const, error: "No valid fields to update" };
} }
await prisma.catalogPagesBc.update({ where: { id }, data: data as any }); await prisma.catalogPagesBc.update({
where: { id },
data: data as Prisma.CatalogPagesBcUpdateInput,
});
await rcon.updateCatalog(); await rcon.updateCatalog();
await logStaffActivity({ await logStaffActivity({
staffId: staff.id, staffId: staff.id,
@@ -98,7 +102,10 @@ export async function updateBcItem({
if (Object.keys(safe).length === 0) { if (Object.keys(safe).length === 0) {
return { ok: false as const, error: "No valid fields to update" }; return { ok: false as const, error: "No valid fields to update" };
} }
await prisma.catalogItemsBc.update({ where: { id }, data: safe as any }); await prisma.catalogItemsBc.update({
where: { id },
data: safe as Prisma.CatalogItemsBcUpdateInput,
});
await rcon.updateCatalog(); await rcon.updateCatalog();
await logStaffActivity({ await logStaffActivity({
staffId: staff.id, staffId: staff.id,
+2 -2
View File
@@ -301,7 +301,7 @@ export async function updateCatalogItem({
if (Object.keys(safeCatalog).length > 0) { if (Object.keys(safeCatalog).length > 0) {
await prisma.catalogItems.update({ await prisma.catalogItems.update({
where: { id }, where: { id },
data: safeCatalog as any, data: safeCatalog as Prisma.CatalogItemsUpdateInput,
}); });
} }
if (baseItem) { if (baseItem) {
@@ -309,7 +309,7 @@ export async function updateCatalogItem({
if (Object.keys(safeBase).length > 0) { if (Object.keys(safeBase).length > 0) {
await prisma.itemsBase.update({ await prisma.itemsBase.update({
where: { id: baseItem.id }, where: { id: baseItem.id },
data: safeBase as any, data: safeBase as Prisma.ItemsBaseUpdateInput,
}); });
} }
} }
+5 -1
View File
@@ -1,6 +1,7 @@
"use server"; "use server";
import { revalidatePath } from "next/cache"; import { revalidatePath } from "next/cache";
import type { Prisma } from "@/generated/prisma/client";
import { requirePermission } from "@/lib/admin/guard"; import { requirePermission } from "@/lib/admin/guard";
import { PERMS } from "@/lib/permissions"; import { PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
@@ -54,7 +55,10 @@ export async function updateCatalogPage({
if (typeof data.caption === "string" && !data.captionSave) { if (typeof data.caption === "string" && !data.captionSave) {
data.captionSave = data.caption.slice(0, 25); data.captionSave = data.caption.slice(0, 25);
} }
await prisma.catalogPages.update({ where: { id }, data: data as any }); await prisma.catalogPages.update({
where: { id },
data: data as Prisma.CatalogPagesUpdateInput,
});
await rcon.updateCatalog(); await rcon.updateCatalog();
await logStaffActivity({ await logStaffActivity({
staffId: staff.id, staffId: staff.id,
+4 -1
View File
@@ -15,7 +15,10 @@ export async function updateRoomItem(payload: Record<string, unknown>) {
itemId: number; itemId: number;
[key: string]: unknown; [key: string]: unknown;
}; };
await prisma.items.update({ where: { id: itemId }, data: data as any }); await prisma.items.update({
where: { id: itemId },
data: data as Prisma.ItemsUpdateInput,
});
await logStaffActivity({ await logStaffActivity({
staffId: staff.id, staffId: staff.id,
action: "room_item_update", action: "room_item_update",
+15 -6
View File
@@ -16,7 +16,10 @@ import {
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { useServerAction } from "@/hooks/use-server-action"; import { useServerAction } from "@/hooks/use-server-action";
import type { CreateEventInput, UpdateEventInput } from "@/lib/validators/event"; import type {
CreateEventInput,
UpdateEventInput,
} from "@/lib/validators/event";
interface EventType { interface EventType {
id: number; id: number;
@@ -34,8 +37,8 @@ interface EventFormValues {
title: string; title: string;
description: string; description: string;
image?: string | null; image?: string | null;
maxPlayers?: string | null; maxPlayers?: string | number | null;
roomId?: string | null; roomId?: string | number | null;
typeId: number; typeId: number;
status: string; status: string;
isRecurring: number; isRecurring: number;
@@ -60,9 +63,15 @@ export function EventForm({ eventTypes, defaultValues }: EventFormProps) {
function onSubmit(data: EventFormValues) { function onSubmit(data: EventFormValues) {
if (isEditing && defaultValues?.id) { if (isEditing && defaultValues?.id) {
run(() => updateEvent({ id: defaultValues.id!, ...data } as UpdateEventInput), { run(
successMessage: "Event updated!", () =>
}); updateEvent({ id: defaultValues.id!, ...data } as UpdateEventInput & {
id: number;
}),
{
successMessage: "Event updated!",
},
);
} else { } else {
run(() => createEvent(data as CreateEventInput), { run(() => createEvent(data as CreateEventInput), {
successMessage: "Event created!", successMessage: "Event created!",
+10 -3
View File
@@ -75,9 +75,16 @@ export function PollQuestionsManager({ pollId, questions }: Props) {
function handleSubmit(e: React.FormEvent) { function handleSubmit(e: React.FormEvent) {
e.preventDefault(); e.preventDefault();
if (editingId) { if (editingId) {
run(() => updatePollQuestion({ id: editingId, ...form } as PollQuestionUpdateInput), { run(
successMessage: "Question updated!", () =>
}); updatePollQuestion({
id: editingId,
...form,
} as PollQuestionUpdateInput),
{
successMessage: "Question updated!",
},
);
} else { } else {
run(() => addPollQuestion(form), { run(() => addPollQuestion(form), {
successMessage: "Question added!", successMessage: "Question added!",
+9 -3
View File
@@ -50,9 +50,15 @@ export function PollForm({ defaultValues }: PollFormProps) {
function onSubmit(data: PollFormValues) { function onSubmit(data: PollFormValues) {
if (isEditing) { if (isEditing) {
run(() => updatePoll({ id: defaultValues!.id, ...data } as UpdatePollInput), { run(
successMessage: "Poll updated!", () =>
}); updatePoll({ id: defaultValues!.id, ...data } as UpdatePollInput & {
id: number;
}),
{
successMessage: "Poll updated!",
},
);
} else { } else {
run(() => createPoll(data as CreatePollInput), { run(() => createPoll(data as CreatePollInput), {
successMessage: "Poll created!", successMessage: "Poll created!",
+7 -5
View File
@@ -1,20 +1,22 @@
import { isValidElement, type ReactNode } from "react"; import { isValidElement, type ReactElement, type ReactNode } from "react";
/** /**
* Map Radix-style `asChild` onto Base UI's `render` prop. * Map Radix-style `asChild` onto Base UI's `render` prop.
* Generic over the render-prop type so component-specific Base UI render
* signatures (e.g. `UseRenderRenderProp<ButtonState>`) are preserved.
*/ */
export function resolveAsChild( export function resolveAsChild<R = ReactElement | undefined>(
asChild: boolean | undefined, asChild: boolean | undefined,
children: ReactNode, children: ReactNode,
render?: any, render?: R,
): { ): {
render?: any; render?: R;
children: ReactNode; children: ReactNode;
} { } {
if (render != null) return { render, children }; if (render != null) return { render, children };
if (asChild && isValidElement(children)) { if (asChild && isValidElement(children)) {
return { return {
render: children, render: children as R,
children: (children.props as { children?: ReactNode }).children, children: (children.props as { children?: ReactNode }).children,
}; };
} }