Fix availability that goes from one day to another

This commit is contained in:
Kalle
2026-09-02 21:10:43 +03:00
parent fdbeffc305
commit bfe76a5e2b
5 changed files with 96 additions and 6 deletions

View File

@@ -220,6 +220,32 @@ export function clip(
});
}
/** The ranges cut up into the day tracks they render on, what runs past a track's end continuing on the next day's. */
export function splitByDayTracks(
ranges: Array<TimeRange>,
timezone: string,
): Array<TimeRange> {
return ranges.flatMap((range) => {
const tracks: Array<TimeRange> = [];
let startsAt = range.startsAt;
while (startsAt < range.endsAt) {
const trackEndsAt = dayMinutesToTimestamp({
date: dateInTimezone(startsAt, timezone),
minutes: AVAILABILITY.TRACK_LATER_END_MINUTES,
timezone,
});
const endsAt = Math.min(range.endsAt, trackEndsAt);
if (endsAt <= startsAt) break;
tracks.push({ startsAt, endsAt });
startsAt = endsAt;
}
return tracks;
});
}
/**
* How one person's schedule relates to an event's window. A busy block overlapping it
* wins over anything reported (committed elsewhere, schedule known or not). Otherwise the

View File

@@ -84,10 +84,15 @@ function editorWeek({
Availability.isSameWeek(week.weekStartsAt, range.startsAt),
);
const slots = Availability.splitByDayTracks(
matchingWeek?.slots ?? [],
timezone,
);
const days = ScheduleWeek.days(range, timezone).map(({ date }) => ({
date,
ranges: Availability.mergedDayRanges(
(matchingWeek?.slots ?? [])
slots
.filter(
(slot) =>
Availability.dateInTimezone(slot.startsAt, timezone) === date,

View File

@@ -0,0 +1,50 @@
import { addWeeks } from "date-fns";
import { describe, expect, test } from "vitest";
import * as Availability from "./Availability";
import * as ScheduleWeek from "./ScheduleWeek";
const DAY_SECONDS = 24 * 60 * 60;
const TIMEZONE = "UTC";
const FRIDAY = 4;
const SATURDAY = 5;
describe("ScheduleWeek.memberRow", () => {
test("shows a member free on Saturday when their Friday night range runs into their Saturday morning range", () => {
const weekStartsAt = Availability.weekStartsAt(
addWeeks(new Date(), 1),
TIMEZONE,
);
const range = {
startsAt: weekStartsAt,
endsAt: weekStartsAt + 7 * DAY_SECONDS,
};
const days = ScheduleWeek.days(range, TIMEZONE);
const fridayAt = (minutes: number) =>
Availability.dayMinutesToTimestamp({
date: days[FRIDAY].date,
minutes,
timezone: TIMEZONE,
});
// what SAVE_WEEK stores for Fri 20:00-06:00 + Sat 06:00-10:00
const slots = Availability.normalize([
{ startsAt: fridayAt(20 * 60), endsAt: fridayAt(30 * 60) },
{ startsAt: fridayAt(30 * 60), endsAt: fridayAt(34 * 60) },
]);
const row = ScheduleWeek.memberRow({
userId: 1,
days,
timezone: TIMEZONE,
reportedWeeks: [
{ userId: 1, weekStartsAt, timezone: TIMEZONE, slots, dayNotes: [] },
],
range,
busy: [],
});
expect(row.days[SATURDAY].ranges).toEqual([
{ startsAt: fridayAt(30 * 60), endsAt: fridayAt(34 * 60) },
]);
});
});

View File

@@ -48,8 +48,9 @@ export function weekNumber(range: TimeRange, timezone: string) {
/**
* One member's week bucketed into the viewer's days: effective free time (commitments cut out
* first), the commitments and their notes. Slots land on the viewer-local day they start on,
* wherever the author's week put them, adjacent weeks' spillover included.
* first), the commitments and their notes. Slots land on the viewer-local day track they start
* on, wherever the author's week put them, adjacent weeks' spillover included, and what runs past
* the end of that track continues on the next day.
*/
export function memberRow({
userId,
@@ -89,9 +90,12 @@ export function memberRow({
};
}
const slots = Availability.subtract(
memberWeeks.flatMap((week) => week.slots),
busy,
const slots = Availability.splitByDayTracks(
Availability.subtract(
memberWeeks.flatMap((week) => week.slots),
busy,
),
timezone,
);
return {

View File

@@ -0,0 +1,5 @@
---
navItem: calendar
type: bug
---
Fixed availability that runs into the next morning making that next day look empty on your schedule and on your team's