feat(platform): checkpoint current implementation state
This commit is contained in:
@@ -96,4 +96,24 @@ describe("checkDuplicateAssignment", () => {
|
||||
const result = checkDuplicateAssignment("r1", "p1", "2026-04-01", "2026-05-01", [proposed]);
|
||||
expect(result.isDuplicate).toBe(true);
|
||||
});
|
||||
|
||||
it("formats conflict dates from local-midnight Date objects without shifting the calendar day", () => {
|
||||
const localDateAssignment: ExistingAssignment = {
|
||||
...base,
|
||||
startDate: new Date(2026, 3, 6),
|
||||
endDate: new Date(2026, 3, 17),
|
||||
};
|
||||
|
||||
const result = checkDuplicateAssignment(
|
||||
"r1",
|
||||
"p1",
|
||||
new Date(2026, 3, 9),
|
||||
new Date(2026, 3, 10),
|
||||
[localDateAssignment],
|
||||
);
|
||||
|
||||
expect(result.isDuplicate).toBe(true);
|
||||
expect(result.message).toContain("2026-04-06");
|
||||
expect(result.message).toContain("2026-04-17");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,10 +20,28 @@ export interface DuplicateCheckResult {
|
||||
|
||||
const ACTIVE_STATUSES = new Set(["CONFIRMED", "ACTIVE", "PROPOSED"]);
|
||||
|
||||
function toTime(d: Date | string): number {
|
||||
const dt = typeof d === "string" ? new Date(d) : d;
|
||||
dt.setHours(0, 0, 0, 0);
|
||||
return dt.getTime();
|
||||
function toCalendarParts(value: Date | string): [year: number, month: number, day: number] {
|
||||
if (typeof value === "string") {
|
||||
const match = value.match(/^(\d{4})-(\d{2})-(\d{2})/u);
|
||||
if (match) {
|
||||
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
||||
}
|
||||
|
||||
const date = new Date(value);
|
||||
return [date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()];
|
||||
}
|
||||
|
||||
return [value.getFullYear(), value.getMonth() + 1, value.getDate()];
|
||||
}
|
||||
|
||||
function toCalendarTime(value: Date | string): number {
|
||||
const [year, month, day] = toCalendarParts(value);
|
||||
return Date.UTC(year, month - 1, day);
|
||||
}
|
||||
|
||||
function formatCalendarDate(value: Date | string): string {
|
||||
const [year, month, day] = toCalendarParts(value);
|
||||
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,8 +63,8 @@ export function checkDuplicateAssignment(
|
||||
existingAssignments: ExistingAssignment[],
|
||||
excludeAssignmentId?: string,
|
||||
): DuplicateCheckResult {
|
||||
const newStart = toTime(startDate);
|
||||
const newEnd = toTime(endDate);
|
||||
const newStart = toCalendarTime(startDate);
|
||||
const newEnd = toCalendarTime(endDate);
|
||||
|
||||
for (const existing of existingAssignments) {
|
||||
// Skip self (for updates)
|
||||
@@ -60,12 +78,12 @@ export function checkDuplicateAssignment(
|
||||
if (!ACTIVE_STATUSES.has(existing.status)) continue;
|
||||
|
||||
// Check date overlap: existingStart <= newEnd && existingEnd >= newStart
|
||||
const existStart = toTime(existing.startDate);
|
||||
const existEnd = toTime(existing.endDate);
|
||||
const existStart = toCalendarTime(existing.startDate);
|
||||
const existEnd = toCalendarTime(existing.endDate);
|
||||
|
||||
if (existStart <= newEnd && existEnd >= newStart) {
|
||||
const startStr = new Date(existing.startDate).toISOString().slice(0, 10);
|
||||
const endStr = new Date(existing.endDate).toISOString().slice(0, 10);
|
||||
const startStr = formatCalendarDate(existing.startDate);
|
||||
const endStr = formatCalendarDate(existing.endDate);
|
||||
return {
|
||||
isDuplicate: true,
|
||||
conflictingAssignment: existing,
|
||||
|
||||
Reference in New Issue
Block a user