feat(platform): checkpoint current implementation state
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
import type { WeekdayAvailability } from "@capakraken/shared";
|
||||
import type { TimelineAssignmentEntry } from "./TimelineContext.js";
|
||||
|
||||
const DAY_KEYS: (keyof WeekdayAvailability)[] = [
|
||||
"sunday",
|
||||
"monday",
|
||||
"tuesday",
|
||||
"wednesday",
|
||||
"thursday",
|
||||
"friday",
|
||||
"saturday",
|
||||
];
|
||||
|
||||
export const DEFAULT_TIMELINE_AVAILABILITY: WeekdayAvailability = {
|
||||
sunday: 0,
|
||||
monday: 8,
|
||||
tuesday: 8,
|
||||
wednesday: 8,
|
||||
thursday: 8,
|
||||
friday: 8,
|
||||
saturday: 0,
|
||||
};
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
|
||||
export function toLocalStartOfDay(value: Date | string): Date {
|
||||
const date = value instanceof Date ? new Date(value) : new Date(value);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
|
||||
export function toLocalDateKey(value: Date | string): string {
|
||||
const date = toLocalStartOfDay(value);
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
export function normalizeTimelineAvailability(value: unknown): WeekdayAvailability {
|
||||
if (!isRecord(value)) {
|
||||
return DEFAULT_TIMELINE_AVAILABILITY;
|
||||
}
|
||||
|
||||
const normalized = { ...DEFAULT_TIMELINE_AVAILABILITY };
|
||||
for (const key of DAY_KEYS) {
|
||||
const next = value[key];
|
||||
if (typeof next === "number" && Number.isFinite(next)) {
|
||||
normalized[key] = next;
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function getTimelineAvailabilityHoursForDate(
|
||||
availability: WeekdayAvailability,
|
||||
date: Date,
|
||||
): number {
|
||||
const dayKey = DAY_KEYS[toLocalStartOfDay(date).getDay()];
|
||||
return dayKey ? (availability[dayKey] ?? 0) : 0;
|
||||
}
|
||||
|
||||
export function getEffectiveAllocationAvailability(
|
||||
allocation: Pick<TimelineAssignmentEntry, "resource" | "metadata">,
|
||||
): WeekdayAvailability {
|
||||
const availability = normalizeTimelineAvailability(allocation.resource?.availability);
|
||||
const metadata = allocation.metadata as Record<string, unknown> | null;
|
||||
const includeSaturday = metadata?.includeSaturday === true;
|
||||
return includeSaturday ? availability : { ...availability, saturday: 0 };
|
||||
}
|
||||
|
||||
export function isAllocationScheduledOnDate(
|
||||
allocation: Pick<TimelineAssignmentEntry, "startDate" | "endDate" | "resource" | "metadata">,
|
||||
date: Date,
|
||||
): boolean {
|
||||
const target = toLocalStartOfDay(date);
|
||||
const start = toLocalStartOfDay(allocation.startDate);
|
||||
const end = toLocalStartOfDay(allocation.endDate);
|
||||
|
||||
if (target < start || target > end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getTimelineAvailabilityHoursForDate(getEffectiveAllocationAvailability(allocation), target) > 0;
|
||||
}
|
||||
|
||||
export function buildAllocationWorkingDaySegments(
|
||||
allocation: Pick<TimelineAssignmentEntry, "startDate" | "endDate" | "resource" | "metadata">,
|
||||
rangeStart?: Date,
|
||||
rangeEnd?: Date,
|
||||
): Array<{ start: Date; end: Date }> {
|
||||
const availability = getEffectiveAllocationAvailability(allocation);
|
||||
const start = toLocalStartOfDay(rangeStart ?? allocation.startDate);
|
||||
const end = toLocalStartOfDay(rangeEnd ?? allocation.endDate);
|
||||
|
||||
if (start > end) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const segments: Array<{ start: Date; end: Date }> = [];
|
||||
let segmentStart: Date | null = null;
|
||||
let segmentEnd: Date | null = null;
|
||||
const cursor = new Date(start);
|
||||
|
||||
while (cursor <= end) {
|
||||
const isWorkingDay = getTimelineAvailabilityHoursForDate(availability, cursor) > 0;
|
||||
|
||||
if (isWorkingDay) {
|
||||
if (!segmentStart) {
|
||||
segmentStart = new Date(cursor);
|
||||
}
|
||||
segmentEnd = new Date(cursor);
|
||||
} else if (segmentStart && segmentEnd) {
|
||||
segments.push({ start: segmentStart, end: segmentEnd });
|
||||
segmentStart = null;
|
||||
segmentEnd = null;
|
||||
}
|
||||
|
||||
cursor.setDate(cursor.getDate() + 1);
|
||||
}
|
||||
|
||||
if (segmentStart && segmentEnd) {
|
||||
segments.push({ start: segmentStart, end: segmentEnd });
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
Reference in New Issue
Block a user