mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-09 04:36:02 -05:00
Fix availability that goes from one day to another
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
50
app/features/availability/core/ScheduleWeek.test.ts
Normal file
50
app/features/availability/core/ScheduleWeek.test.ts
Normal 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) },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -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 {
|
||||
|
||||
5
changelog/2026-09-02-schedule-overnight-days.md
Normal file
5
changelog/2026-09-02-schedule-overnight-days.md
Normal 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
|
||||
Reference in New Issue
Block a user