chore(repo): initialize planarchy workspace

This commit is contained in:
2026-03-14 14:31:09 +01:00
commit dd55d0e78b
769 changed files with 166461 additions and 0 deletions
@@ -0,0 +1,84 @@
import type { PrismaClient } from "@planarchy/db";
import { computeChargeability } from "@planarchy/engine";
import type { WeekdayAvailability } from "@planarchy/shared";
import { listAssignmentBookings } from "../allocation/list-assignment-bookings.js";
export interface GetDashboardChargeabilityOverviewInput {
topN: number;
watchlistThreshold: number;
now?: Date;
}
export async function getDashboardChargeabilityOverview(
db: PrismaClient,
input: GetDashboardChargeabilityOverviewInput,
) {
const now = input.now ?? new Date();
const start = new Date(now.getFullYear(), now.getMonth(), 1);
const end = new Date(now.getFullYear(), now.getMonth() + 1, 0);
const resources = await db.resource.findMany({
where: { isActive: true },
select: {
id: true,
eid: true,
displayName: true,
chapter: true,
chargeabilityTarget: true,
availability: true,
},
});
const bookings = await listAssignmentBookings(db, {
startDate: start,
endDate: end,
resourceIds: resources.map((resource) => resource.id),
});
const stats = resources.map((resource) => {
const availability = resource.availability as unknown as WeekdayAvailability;
const resourceBookings = bookings.filter((booking) => booking.resourceId === resource.id);
const actualAllocations = resourceBookings.filter(
(booking) =>
(booking.status === "CONFIRMED" || booking.status === "ACTIVE") &&
booking.project.status !== "DRAFT" &&
booking.project.status !== "CANCELLED",
);
const actual = computeChargeability(
availability,
actualAllocations,
start,
end,
);
const expected = computeChargeability(
availability,
resourceBookings,
start,
end,
);
return {
id: resource.id,
eid: resource.eid,
displayName: resource.displayName,
chapter: resource.chapter,
chargeabilityTarget: resource.chargeabilityTarget,
actualChargeability: actual.chargeability,
expectedChargeability: expected.chargeability,
};
});
return {
top: [...stats]
.sort((left, right) => right.actualChargeability - left.actualChargeability)
.slice(0, input.topN),
watchlist: [...stats]
.filter(
(resource) =>
resource.actualChargeability <
resource.chargeabilityTarget - input.watchlistThreshold,
)
.sort((left, right) => left.actualChargeability - right.actualChargeability)
.slice(0, input.topN),
month: `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`,
};
}