153 lines
4.0 KiB
TypeScript
153 lines
4.0 KiB
TypeScript
import { countPlanningEntries } from "@capakraken/application";
|
|
import type { Prisma, PrismaClient } from "@capakraken/db";
|
|
import { CreateRoleSchema, UpdateRoleSchema } from "@capakraken/shared";
|
|
import { TRPCError } from "@trpc/server";
|
|
import { z } from "zod";
|
|
|
|
type RolePlanningCountsDb = Pick<PrismaClient, "demandRequirement" | "assignment">;
|
|
type RoleIdentifierDb = Pick<PrismaClient, "role">;
|
|
|
|
type RoleCountRecord = {
|
|
id: string;
|
|
_count: { resourceRoles: number };
|
|
};
|
|
|
|
type RoleListInput = {
|
|
isActive?: boolean | undefined;
|
|
search?: string | undefined;
|
|
};
|
|
|
|
type CreateRoleInput = z.infer<typeof CreateRoleSchema>;
|
|
type UpdateRoleInput = z.infer<typeof UpdateRoleSchema>;
|
|
|
|
export function buildRoleListWhere(input: RoleListInput): Prisma.RoleWhereInput {
|
|
return {
|
|
...(input.isActive !== undefined ? { isActive: input.isActive } : {}),
|
|
...(input.search
|
|
? { name: { contains: input.search, mode: "insensitive" } }
|
|
: {}),
|
|
};
|
|
}
|
|
|
|
export async function loadRolePlanningEntryCounts(
|
|
db: RolePlanningCountsDb,
|
|
roleIds: string[],
|
|
) {
|
|
const { countsByRoleId } = await countPlanningEntries(db, {
|
|
roleIds,
|
|
});
|
|
|
|
return countsByRoleId;
|
|
}
|
|
|
|
export async function attachRolePlanningEntryCounts<
|
|
TRole extends RoleCountRecord,
|
|
>(
|
|
db: RolePlanningCountsDb,
|
|
roles: TRole[],
|
|
): Promise<Array<TRole & { _count: { resourceRoles: number; allocations: number } }>> {
|
|
const countsByRoleId = await loadRolePlanningEntryCounts(
|
|
db,
|
|
roles.map((role) => role.id),
|
|
);
|
|
|
|
return roles.map((role) => ({
|
|
...role,
|
|
_count: {
|
|
...role._count,
|
|
allocations: countsByRoleId.get(role.id) ?? 0,
|
|
},
|
|
}));
|
|
}
|
|
|
|
export async function attachSingleRolePlanningEntryCount<
|
|
TRole extends RoleCountRecord,
|
|
>(
|
|
db: RolePlanningCountsDb,
|
|
role: TRole,
|
|
): Promise<TRole & { _count: { resourceRoles: number; allocations: number } }> {
|
|
return (await attachRolePlanningEntryCounts(db, [role]))[0]!;
|
|
}
|
|
|
|
export async function findRoleByIdentifier<TRole>(
|
|
db: RoleIdentifierDb,
|
|
identifier: string,
|
|
select: Record<string, unknown>,
|
|
): Promise<TRole> {
|
|
const normalizedIdentifier = identifier.trim();
|
|
|
|
let role = await db.role.findUnique({
|
|
where: { id: normalizedIdentifier },
|
|
select,
|
|
}) as TRole | null;
|
|
if (!role) {
|
|
role = await db.role.findUnique({
|
|
where: { name: normalizedIdentifier },
|
|
select,
|
|
}) as TRole | null;
|
|
}
|
|
if (!role) {
|
|
role = await db.role.findFirst({
|
|
where: { name: { equals: normalizedIdentifier, mode: "insensitive" } },
|
|
select,
|
|
}) as TRole | null;
|
|
}
|
|
if (!role) {
|
|
role = await db.role.findFirst({
|
|
where: { name: { contains: normalizedIdentifier, mode: "insensitive" } },
|
|
select,
|
|
}) as TRole | null;
|
|
}
|
|
if (!role) {
|
|
throw new TRPCError({ code: "NOT_FOUND", message: "Role not found" });
|
|
}
|
|
|
|
return role;
|
|
}
|
|
|
|
export function buildRoleCreateData(
|
|
input: CreateRoleInput,
|
|
): Prisma.RoleCreateInput {
|
|
return {
|
|
name: input.name,
|
|
description: input.description ?? null,
|
|
color: input.color ?? null,
|
|
};
|
|
}
|
|
|
|
export function buildRoleUpdateData(
|
|
input: UpdateRoleInput,
|
|
): Prisma.RoleUpdateInput {
|
|
return {
|
|
...(input.name !== undefined ? { name: input.name } : {}),
|
|
...(input.description !== undefined ? { description: input.description } : {}),
|
|
...(input.color !== undefined ? { color: input.color } : {}),
|
|
...(input.isActive !== undefined ? { isActive: input.isActive } : {}),
|
|
};
|
|
}
|
|
|
|
export function appendZeroAllocationCount<
|
|
TRole extends RoleCountRecord,
|
|
>(
|
|
role: TRole,
|
|
): TRole & { _count: { resourceRoles: number; allocations: number } } {
|
|
return {
|
|
...role,
|
|
_count: {
|
|
...role._count,
|
|
allocations: 0,
|
|
},
|
|
};
|
|
}
|
|
|
|
export async function assertRoleNameAvailable(
|
|
db: RoleIdentifierDb,
|
|
name: string,
|
|
ignoreId?: string,
|
|
): Promise<void> {
|
|
const existing = await db.role.findUnique({ where: { name } });
|
|
if (existing && existing.id !== ignoreId) {
|
|
throw new TRPCError({ code: "CONFLICT", message: `Role "${name}" already exists` });
|
|
}
|
|
}
|