diff --git a/apps/web/src/app/(app)/resources/ResourcesClient.tsx b/apps/web/src/app/(app)/resources/ResourcesClient.tsx index 2cd73ae..2cef176 100644 --- a/apps/web/src/app/(app)/resources/ResourcesClient.tsx +++ b/apps/web/src/app/(app)/resources/ResourcesClient.tsx @@ -47,7 +47,9 @@ type ModalState = type ConfirmState = | { type: "closed" } | { type: "batchDeactivate"; ids: string[] } - | { type: "deactivate"; resource: Resource }; + | { type: "deactivate"; resource: Resource } + | { type: "delete"; resource: Resource } + | { type: "batchDelete"; ids: string[] }; type ActiveFilter = "active" | "inactive" | "all"; type BooleanFilter = "all" | "yes" | "no"; @@ -163,7 +165,7 @@ export function ResourcesClient() { const selection = useSelection(); const utils = trpc.useUtils(); - const { canViewScores, canViewCosts } = usePermissions(); + const { canViewScores, canViewCosts, canManageUsers } = usePermissions(); const { customFieldFilters, setCustomFieldFilter, @@ -323,6 +325,19 @@ export function ResourcesClient() { selection.clear(); }, }); + const hardDeleteMutation = trpc.resource.hardDelete.useMutation({ + onSuccess: () => { + void utils.resource.directory.invalidate(); + void utils.resource.listStaff.invalidate(); + }, + }); + const batchHardDeleteMutation = trpc.resource.batchHardDelete.useMutation({ + onSuccess: () => { + void utils.resource.directory.invalidate(); + void utils.resource.listStaff.invalidate(); + selection.clear(); + }, + }); useEffect(() => { selection.clear(); @@ -348,6 +363,10 @@ export function ResourcesClient() { deactivateMutation.mutate({ id: confirm.resource.id }); } else if (confirm.type === "batchDeactivate") { batchDeactivateMutation.mutate({ ids: confirm.ids }); + } else if (confirm.type === "delete") { + hardDeleteMutation.mutate({ id: confirm.resource.id }); + } else if (confirm.type === "batchDelete") { + batchHardDeleteMutation.mutate({ ids: confirm.ids }); } setConfirm({ type: "closed" }); } @@ -1384,6 +1403,21 @@ export function ResourcesClient() { > {isDeactivating ? "Deactivating…" : "Deactivate"} + {canManageUsers && ( + + )} ); @@ -1426,6 +1460,16 @@ export function ResourcesClient() { onClick: () => setConfirm({ type: "batchDeactivate", ids: selection.selectedArray }), disabled: batchDeactivateMutation.isPending, }, + ...(canManageUsers + ? [ + { + label: `Delete ${selection.count > 0 ? `(${selection.count})` : ""}`, + variant: "danger" as const, + onClick: () => setConfirm({ type: "batchDelete", ids: selection.selectedArray }), + disabled: batchHardDeleteMutation.isPending, + }, + ] + : []), ]} /> @@ -1469,6 +1513,26 @@ export function ResourcesClient() { onCancel={() => setConfirm({ type: "closed" })} /> )} + {confirm.type === "delete" && ( + setConfirm({ type: "closed" })} + /> + )} + {confirm.type === "batchDelete" && ( + setConfirm({ type: "closed" })} + /> + )} { + const resources = await ctx.db.resource.findMany({ + where: { id: { in: input.ids } }, + select: { id: true, displayName: true, eid: true }, + }); + + await ctx.db.$transaction([ + ctx.db.assignment.deleteMany({ where: { resourceId: { in: input.ids } } }), + ctx.db.vacation.deleteMany({ where: { resourceId: { in: input.ids } } }), + ctx.db.resource.deleteMany({ where: { id: { in: input.ids } } }), + ]); + + await ctx.db.auditLog.createMany({ + data: resources.map((r) => ({ + entityType: "Resource", + entityId: r.id, + action: "DELETE", + userId: ctx.dbUser?.id, + changes: { before: r } as unknown as import("@capakraken/db").Prisma.InputJsonValue, + })), + }); + + return { deleted: resources.length }; + }), };