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 };
+ }),
};