cd78f72f33
Complete rename of all technical identifiers across the codebase: Package names (11 packages): - @planarchy/* → @capakraken/* in all package.json, tsconfig, imports Import statements: 277 files, 548 occurrences replaced Database & Docker: - PostgreSQL user/db: planarchy → capakraken - Docker volumes: planarchy_pgdata → capakraken_pgdata - Connection strings updated in docker-compose, .env, CI CI/CD: - GitHub Actions workflow: all filter commands updated - Test database credentials updated Infrastructure: - Redis channel: planarchy:sse → capakraken:sse - Logger service name: planarchy-api → capakraken-api - Anonymization seed updated - Start/stop/restart scripts updated Test data: - Seed emails: @planarchy.dev → @capakraken.dev - E2E test credentials: all 11 spec files updated - Email defaults: @planarchy.app → @capakraken.app - localStorage keys: planarchy_* → capakraken_* Documentation: 30+ .md files updated Verification: - pnpm install: workspace resolution works - TypeScript: only pre-existing TS2589 (no new errors) - Engine: 310/310 tests pass - Staffing: 37/37 tests pass Co-Authored-By: claude-flow <ruv@ruv.net>
81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import type { WeekdayAvailability } from "@capakraken/shared";
|
|
|
|
export interface ChargeabilityAllocation {
|
|
startDate: Date;
|
|
endDate: Date;
|
|
hoursPerDay: number;
|
|
}
|
|
|
|
export interface ChargeabilityResult {
|
|
availableHours: number;
|
|
bookedHours: number;
|
|
chargeability: number; // 0-100, rounded
|
|
}
|
|
|
|
// Maps JS getDay() (0=Sun..6=Sat) to WeekdayAvailability keys
|
|
const DAY_KEYS: (keyof WeekdayAvailability)[] = [
|
|
"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday",
|
|
];
|
|
|
|
/** Count working hours a resource has available in [start, end] based on their schedule. */
|
|
export function computeAvailableHours(
|
|
availability: WeekdayAvailability,
|
|
start: Date,
|
|
end: Date,
|
|
): number {
|
|
let hours = 0;
|
|
const cur = new Date(start);
|
|
cur.setHours(0, 0, 0, 0);
|
|
const endNorm = new Date(end);
|
|
endNorm.setHours(0, 0, 0, 0);
|
|
while (cur <= endNorm) {
|
|
const key = DAY_KEYS[cur.getDay()];
|
|
hours += key ? (availability[key] ?? 0) : 0;
|
|
cur.setDate(cur.getDate() + 1);
|
|
}
|
|
return hours;
|
|
}
|
|
|
|
/** Count booked hours from allocations overlapping [start, end], working days only. */
|
|
export function computeBookedHours(
|
|
availability: WeekdayAvailability,
|
|
allocations: ChargeabilityAllocation[],
|
|
start: Date,
|
|
end: Date,
|
|
): number {
|
|
let hours = 0;
|
|
const startNorm = new Date(start); startNorm.setHours(0, 0, 0, 0);
|
|
const endNorm = new Date(end); endNorm.setHours(0, 0, 0, 0);
|
|
for (const alloc of allocations) {
|
|
const aStart = new Date(alloc.startDate); aStart.setHours(0, 0, 0, 0);
|
|
const aEnd = new Date(alloc.endDate); aEnd.setHours(0, 0, 0, 0);
|
|
const overlapStart = aStart > startNorm ? aStart : startNorm;
|
|
const overlapEnd = aEnd < endNorm ? aEnd : endNorm;
|
|
if (overlapStart > overlapEnd) continue;
|
|
const cur = new Date(overlapStart);
|
|
while (cur <= overlapEnd) {
|
|
const key = DAY_KEYS[cur.getDay()];
|
|
if (key && (availability[key] ?? 0) > 0) {
|
|
hours += alloc.hoursPerDay;
|
|
}
|
|
cur.setDate(cur.getDate() + 1);
|
|
}
|
|
}
|
|
return hours;
|
|
}
|
|
|
|
/** Compute chargeability metrics for a resource over a date range. */
|
|
export function computeChargeability(
|
|
availability: WeekdayAvailability,
|
|
allocations: ChargeabilityAllocation[],
|
|
start: Date,
|
|
end: Date,
|
|
): ChargeabilityResult {
|
|
const availableHours = computeAvailableHours(availability, start, end);
|
|
const bookedHours = computeBookedHours(availability, allocations, start, end);
|
|
const chargeability = availableHours > 0
|
|
? Math.min(100, Math.round((bookedHours / availableHours) * 100))
|
|
: 0;
|
|
return { availableHours, bookedHours, chargeability };
|
|
}
|