Implement editing of slots in ClientSchedule
Implement tracking of time slots along with editing and restoration of singularly edited time slots. This provides a simpler interface to work with when rendering tables of time slots that can be edited than directly manipulating events and shifts containing an array of slots.
This commit is contained in:
parent
73bb12c104
commit
ce9f758f84
4 changed files with 432 additions and 44 deletions
|
@ -1,7 +1,7 @@
|
|||
import { ClientEntity, ClientSchedule, ClientScheduleEventSlot, ClientScheduleLocation, toIso } from "./client-schedule";
|
||||
import { ClientEntity, ClientSchedule, ClientScheduleEventSlot, ClientScheduleLocation, ClientScheduleShiftSlot, toIso } from "./client-schedule";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import type { ApiSchedule } from "~/shared/types/api";
|
||||
import type { Living } from "~/shared/types/common";
|
||||
import type { Id, Living } from "~/shared/types/common";
|
||||
import { DateTime, FixedOffsetZone } from "~/shared/utils/luxon";
|
||||
|
||||
const locale = "en-GB";
|
||||
|
@ -17,11 +17,11 @@ function fixtureClientSchedule() {
|
|||
const events = [
|
||||
new ClientScheduleEvent(
|
||||
1, now, false, "Up", false, "", false, "What's Up?", 0,
|
||||
[new ClientScheduleEventSlot(1, now, now.plus({ hours: 1 }), [left], new Set(), 0)],
|
||||
idMap([new ClientScheduleEventSlot(1, false, 1, now, now.plus({ hours: 1 }), [left], new Set(), 0)]),
|
||||
),
|
||||
new ClientScheduleEvent(
|
||||
2, now, false, "Down", false, "", false, "", 0,
|
||||
[new ClientScheduleEventSlot(2, now, now.plus({ hours: 2 }), [right], new Set(), 0)],
|
||||
idMap([new ClientScheduleEventSlot(2, false, 2, now, now.plus({ hours: 2 }), [right], new Set(), 0)]),
|
||||
),
|
||||
];
|
||||
|
||||
|
@ -30,11 +30,11 @@ function fixtureClientSchedule() {
|
|||
const shifts = [
|
||||
new ClientScheduleShift(
|
||||
1, now, false, red, "White", "",
|
||||
[new ClientScheduleShiftSlot(1, now, now.plus({ hours: 1 }), new Set())],
|
||||
idMap([new ClientScheduleShiftSlot(1, false, 1, now, now.plus({ hours: 1 }), new Set())]),
|
||||
),
|
||||
new ClientScheduleShift(
|
||||
2, now, false, blue, "Black", "Is dark.",
|
||||
[new ClientScheduleShiftSlot(2, now, now.plus({ hours: 2 }), new Set())],
|
||||
idMap([new ClientScheduleShiftSlot(2, false, 2, now, now.plus({ hours: 2 }), new Set())]),
|
||||
),
|
||||
];
|
||||
|
||||
|
@ -211,7 +211,7 @@ describe("class ClientSchedule", () => {
|
|||
],
|
||||
[
|
||||
"event",
|
||||
() => new ClientScheduleEvent(3, now, false, "New location", false, "", false, "", 0, [])
|
||||
() => new ClientScheduleEvent(3, now, false, "New location", false, "", false, "", 0, new Map())
|
||||
],
|
||||
[
|
||||
"role",
|
||||
|
@ -219,7 +219,7 @@ describe("class ClientSchedule", () => {
|
|||
],
|
||||
[
|
||||
"shift",
|
||||
(schedule) => new ClientScheduleShift(3, now, false, schedule.roles.get(1)!, "New location", "", [])
|
||||
(schedule) => new ClientScheduleShift(3, now, false, schedule.roles.get(1)!, "New location", "", new Map())
|
||||
],
|
||||
] as const;
|
||||
for (const [name, create] of entityTests) {
|
||||
|
@ -249,7 +249,7 @@ describe("class ClientSchedule", () => {
|
|||
expect((schedule as any)[`isModified${Name}`](1)).toBe(true);
|
||||
expect((schedule as any)[`original${Name}s`].get(1)).toBe(original);
|
||||
if (name === "location") {
|
||||
expect(schedule.events.get(1)!.slots[0].locations[0]).toBe(schedule.locations.get(1));
|
||||
expect(schedule.events.get(1)!.slots.get(1)!.locations[0]).toBe(schedule.locations.get(1));
|
||||
} else if (name === "role") {
|
||||
expect(schedule.shifts.get(1)!.role).toBe(schedule.roles.get(1));
|
||||
}
|
||||
|
@ -289,4 +289,199 @@ describe("class ClientSchedule", () => {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
function validateSlotRelations(
|
||||
slots: Map<Id, ClientScheduleShiftSlot> | Map<Id, ClientScheduleEventSlot>,
|
||||
entites: Map<Id, ClientScheduleShift> | Map<Id, ClientScheduleEvent>,
|
||||
prefix: string,
|
||||
) {
|
||||
const remainingIds = new Set(slots.keys())
|
||||
for (const shift of entites.values()) {
|
||||
for (const slot of shift.slots.values()) {
|
||||
if (!slots.has(slot.id)) {
|
||||
throw Error(`${prefix}: shift ${shift.name}:${shift.id} has slot ${slot.id} that is not in shiftSlots`);
|
||||
}
|
||||
if (slots.get(slot.id) !== slot) {
|
||||
throw Error(`${prefix}: shift ${shift.name}:${shift.id} has slot ${slot.id} which does not match the corresponding slot in shiftSlots.`);
|
||||
}
|
||||
if (!remainingIds.has(slot.id)) {
|
||||
throw Error(`${prefix}: shift ${shift.name}:${shift.id} has slot ${slot.id} that has been seen twice.`);
|
||||
}
|
||||
remainingIds.delete(slot.id);
|
||||
}
|
||||
}
|
||||
if (remainingIds.size) {
|
||||
throw Error(`${prefix}: shiftSlots ${[...remainingIds].join(", ")} does not have a corresponding shift`);
|
||||
}
|
||||
}
|
||||
|
||||
describe("event slot", () => {
|
||||
test("edit", () => {
|
||||
const schedule = fixtureClientSchedule();
|
||||
expect(schedule.modified).toBe(false);
|
||||
expect(schedule.isModifiedEvent(1)).toBe(false);
|
||||
expect(schedule.isModifiedEvent(2)).toBe(false);
|
||||
expect(schedule.isModifiedEventSlot(1)).toBe(false);
|
||||
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
||||
// Modify
|
||||
schedule.editEventSlot(schedule.eventSlots.get(1)!, { start: later });
|
||||
// Check
|
||||
expect(schedule.modified).toBe(true);
|
||||
expect(schedule.isModifiedEvent(1)).toBe(true);
|
||||
expect(schedule.isModifiedEvent(2)).toBe(false);
|
||||
expect(schedule.isModifiedEventSlot(1)).toBe(true);
|
||||
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
||||
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
||||
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
||||
validateSlotRelations(schedule.eventSlots, schedule.events, "current");
|
||||
validateSlotRelations(schedule.eventSlots, schedule.events, "original");
|
||||
});
|
||||
test("move to another event", () => {
|
||||
const schedule = fixtureClientSchedule();
|
||||
expect(schedule.modified).toBe(false);
|
||||
expect(schedule.isModifiedEvent(1)).toBe(false);
|
||||
expect(schedule.isModifiedEvent(2)).toBe(false);
|
||||
expect(schedule.isModifiedEventSlot(1)).toBe(false);
|
||||
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
||||
// Modify
|
||||
schedule.editEventSlot(schedule.eventSlots.get(1)!, { eventId: 2 });
|
||||
// Check
|
||||
expect(schedule.modified).toBe(true);
|
||||
expect(schedule.isModifiedEvent(1)).toBe(true);
|
||||
expect(schedule.isModifiedEvent(2)).toBe(true);
|
||||
expect(schedule.isModifiedEventSlot(1)).toBe(true);
|
||||
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
||||
expect(schedule.events.get(1)!.slots.size).toBe(0);
|
||||
expect(schedule.events.get(2)!.slots.size).toBe(2);
|
||||
validateSlotRelations(schedule.eventSlots, schedule.events, "current");
|
||||
validateSlotRelations(schedule.eventSlots, schedule.events, "original");
|
||||
// Move back
|
||||
schedule.editEventSlot(schedule.eventSlots.get(1)!, { eventId: 1 });
|
||||
// Check
|
||||
expect(schedule.modified).toBe(true);
|
||||
expect(schedule.isModifiedEvent(1)).toBe(true);
|
||||
expect(schedule.isModifiedEvent(2)).toBe(true);
|
||||
expect(schedule.isModifiedEventSlot(1)).toBe(true);
|
||||
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
||||
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
||||
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
||||
validateSlotRelations(schedule.eventSlots, schedule.events, "current");
|
||||
validateSlotRelations(schedule.eventSlots, schedule.events, "original");
|
||||
});
|
||||
test("restore", () => {
|
||||
const schedule = fixtureClientSchedule();
|
||||
schedule.editEventSlot(schedule.eventSlots.get(1)!, { start: later });
|
||||
schedule.restoreEventSlot(1);
|
||||
// Check
|
||||
expect(schedule.modified).toBe(true);
|
||||
expect(schedule.isModifiedEvent(1)).toBe(true);
|
||||
expect(schedule.isModifiedEvent(2)).toBe(false);
|
||||
expect(schedule.isModifiedEventSlot(1)).toBe(false);
|
||||
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
||||
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
||||
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
||||
validateSlotRelations(schedule.eventSlots, schedule.events, "current");
|
||||
validateSlotRelations(schedule.eventSlots, schedule.events, "original");
|
||||
});
|
||||
test("restore from another event", () => {
|
||||
const schedule = fixtureClientSchedule();
|
||||
schedule.editEventSlot(schedule.eventSlots.get(1)!, { eventId: 2 });
|
||||
schedule.restoreEventSlot(1);
|
||||
// Check
|
||||
expect(schedule.modified).toBe(true);
|
||||
expect(schedule.isModifiedEvent(1)).toBe(true);
|
||||
expect(schedule.isModifiedEvent(2)).toBe(true);
|
||||
expect(schedule.isModifiedEventSlot(1)).toBe(false);
|
||||
expect(schedule.isModifiedEventSlot(2)).toBe(false);
|
||||
expect(schedule.events.get(1)!.slots.size).toBe(1);
|
||||
expect(schedule.events.get(2)!.slots.size).toBe(1);
|
||||
validateSlotRelations(schedule.eventSlots, schedule.events, "current");
|
||||
validateSlotRelations(schedule.eventSlots, schedule.events, "original");
|
||||
});
|
||||
});
|
||||
|
||||
describe("shift slot", () => {
|
||||
test("edit", () => {
|
||||
const schedule = fixtureClientSchedule();
|
||||
expect(schedule.modified).toBe(false);
|
||||
expect(schedule.isModifiedShift(1)).toBe(false);
|
||||
expect(schedule.isModifiedShift(2)).toBe(false);
|
||||
expect(schedule.isModifiedShiftSlot(1)).toBe(false);
|
||||
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
||||
// Modify
|
||||
schedule.editShiftSlot(schedule.shiftSlots.get(1)!, { start: later });
|
||||
// Check
|
||||
expect(schedule.modified).toBe(true);
|
||||
expect(schedule.isModifiedShift(1)).toBe(true);
|
||||
expect(schedule.isModifiedShift(2)).toBe(false);
|
||||
expect(schedule.isModifiedShiftSlot(1)).toBe(true);
|
||||
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
||||
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
||||
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
||||
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "current");
|
||||
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "original");
|
||||
});
|
||||
test("move to another shift", () => {
|
||||
const schedule = fixtureClientSchedule();
|
||||
expect(schedule.modified).toBe(false);
|
||||
expect(schedule.isModifiedShift(1)).toBe(false);
|
||||
expect(schedule.isModifiedShift(2)).toBe(false);
|
||||
expect(schedule.isModifiedShiftSlot(1)).toBe(false);
|
||||
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
||||
// Modify
|
||||
schedule.editShiftSlot(schedule.shiftSlots.get(1)!, { shiftId: 2 });
|
||||
// Check
|
||||
expect(schedule.modified).toBe(true);
|
||||
expect(schedule.isModifiedShift(1)).toBe(true);
|
||||
expect(schedule.isModifiedShift(2)).toBe(true);
|
||||
expect(schedule.isModifiedShiftSlot(1)).toBe(true);
|
||||
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
||||
expect(schedule.shifts.get(1)!.slots.size).toBe(0);
|
||||
expect(schedule.shifts.get(2)!.slots.size).toBe(2);
|
||||
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "current");
|
||||
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "original");
|
||||
// Move back
|
||||
schedule.editShiftSlot(schedule.shiftSlots.get(1)!, { shiftId: 1 });
|
||||
// Check
|
||||
expect(schedule.modified).toBe(true);
|
||||
expect(schedule.isModifiedShift(1)).toBe(true);
|
||||
expect(schedule.isModifiedShift(2)).toBe(true);
|
||||
expect(schedule.isModifiedShiftSlot(1)).toBe(true);
|
||||
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
||||
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
||||
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
||||
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "current");
|
||||
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "original");
|
||||
});
|
||||
test("restore", () => {
|
||||
const schedule = fixtureClientSchedule();
|
||||
schedule.editShiftSlot(schedule.shiftSlots.get(1)!, { start: later });
|
||||
schedule.restoreShiftSlot(1);
|
||||
// Check
|
||||
expect(schedule.modified).toBe(true);
|
||||
expect(schedule.isModifiedShift(1)).toBe(true);
|
||||
expect(schedule.isModifiedShift(2)).toBe(false);
|
||||
expect(schedule.isModifiedShiftSlot(1)).toBe(false);
|
||||
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
||||
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
||||
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
||||
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "current");
|
||||
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "original");
|
||||
});
|
||||
test("restore from another shift", () => {
|
||||
const schedule = fixtureClientSchedule();
|
||||
schedule.editShiftSlot(schedule.shiftSlots.get(1)!, { shiftId: 2 });
|
||||
schedule.restoreShiftSlot(1);
|
||||
// Check
|
||||
expect(schedule.modified).toBe(true);
|
||||
expect(schedule.isModifiedShift(1)).toBe(true);
|
||||
expect(schedule.isModifiedShift(2)).toBe(true);
|
||||
expect(schedule.isModifiedShiftSlot(1)).toBe(false);
|
||||
expect(schedule.isModifiedShiftSlot(2)).toBe(false);
|
||||
expect(schedule.shifts.get(1)!.slots.size).toBe(1);
|
||||
expect(schedule.shifts.get(2)!.slots.size).toBe(1);
|
||||
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "current");
|
||||
validateSlotRelations(schedule.shiftSlots, schedule.shifts, "original");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue