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,157 @@
import type { PrismaClient } from "@planarchy/db";
import { AllocationStatus } from "@planarchy/shared";
import { buildSplitAllocationReadModel } from "../allocation/build-split-allocation-read-model.js";
import { listAssignmentBookings } from "../allocation/list-assignment-bookings.js";
import { calculateInclusiveDays } from "./shared.js";
export async function getDashboardOverview(db: PrismaClient) {
const [
totalResources,
activeResources,
totalProjects,
allProjects,
allDemandRequirements,
allAssignments,
budgetBookings,
recentActivity,
allResources,
] = await Promise.all([
db.resource.count(),
db.resource.count({ where: { isActive: true } }),
db.project.count(),
db.project.findMany({ select: { status: true, budgetCents: true } }),
db.demandRequirement.findMany({
select: {
id: true,
projectId: true,
startDate: true,
endDate: true,
hoursPerDay: true,
percentage: true,
role: true,
roleId: true,
headcount: true,
status: true,
metadata: true,
createdAt: true,
updatedAt: true,
},
}),
db.assignment.findMany({
select: {
id: true,
demandRequirementId: true,
resourceId: true,
projectId: true,
startDate: true,
endDate: true,
hoursPerDay: true,
percentage: true,
role: true,
roleId: true,
dailyCostCents: true,
status: true,
metadata: true,
createdAt: true,
updatedAt: true,
},
}),
listAssignmentBookings(db, {}),
db.auditLog.findMany({
orderBy: { createdAt: "desc" },
take: 10,
select: { id: true, entityType: true, action: true, createdAt: true },
}),
db.resource.findMany({
select: { chapter: true, chargeabilityTarget: true },
}),
]);
const planningReadModel = buildSplitAllocationReadModel({
demandRequirements: allDemandRequirements,
assignments: allAssignments,
});
const totalAllocations = planningReadModel.allocations.length;
const activeAllocations = planningReadModel.allocations.filter(
(allocation) => allocation.status !== AllocationStatus.CANCELLED,
).length;
const totalCostCents = budgetBookings.reduce(
(sum, booking) =>
sum +
(booking.dailyCostCents ?? 0) *
calculateInclusiveDays(booking.startDate, booking.endDate),
0,
);
const totalBudgetCents = allProjects.reduce(
(sum, project) => sum + (project.budgetCents ?? 0),
0,
);
const avgUtilizationPercent =
totalBudgetCents > 0
? Math.round((totalCostCents / totalBudgetCents) * 100)
: 0;
const statusCountMap = new Map<string, number>();
for (const project of allProjects) {
statusCountMap.set(
project.status,
(statusCountMap.get(project.status) ?? 0) + 1,
);
}
const chapterMap = new Map<
string,
{ resourceCount: number; chargeabilitySum: number }
>();
for (const resource of allResources) {
const chapter = resource.chapter ?? "Unassigned";
const existing = chapterMap.get(chapter) ?? {
resourceCount: 0,
chargeabilitySum: 0,
};
chapterMap.set(chapter, {
resourceCount: existing.resourceCount + 1,
chargeabilitySum:
existing.chargeabilitySum + (resource.chargeabilityTarget ?? 0),
});
}
return {
totalResources,
activeResources,
totalProjects,
activeProjects: allProjects.filter((project) => project.status === "ACTIVE")
.length,
totalAllocations,
activeAllocations,
budgetSummary: {
totalBudgetCents,
totalCostCents,
avgUtilizationPercent,
},
recentActivity: recentActivity.map((activity) => ({
id: activity.id,
entityType: activity.entityType,
action: activity.action,
createdAt: activity.createdAt,
})),
projectsByStatus: [...statusCountMap.entries()].map(([status, count]) => ({
status,
count,
})),
chapterUtilization: [...chapterMap.entries()].map(([chapter, data]) => ({
chapter,
resourceCount: data.resourceCount,
avgChargeabilityTarget:
data.resourceCount > 0
? Math.round(data.chargeabilitySum / data.resourceCount)
: 0,
})),
};
}