Skip to content

Commit

Permalink
Account for interval time when sorting customer calendar view (aside …
Browse files Browse the repository at this point in the history
…from slot date)
  • Loading branch information
ikusteu committed Sep 10, 2023
1 parent c97ab23 commit e2618f2
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ import {
slotsByDay,
} from "../../__testData__/slots";
import { baseSlot } from "@eisbuk/testing/slots";
import { getBookingsForCalendar, getMonthEmptyForBooking } from "../slots";
import {
getBookedAndAttendedSlotsForCalendar,
getBookingsForCalendar,
getMonthEmptyForBooking,
} from "../slots";

// set date mock to be a consistent date throughout
const mockDate = DateTime.fromISO("2022-02-05");
Expand Down Expand Up @@ -349,5 +353,108 @@ describe("Selectors ->", () => {
},
]);
});

test.only("should sort the slots (by date, and by time intraday)", () => {
const monthStr = "2022-01";
const date = "2022-01-01";
const tomorrow = "2022-01-02";

const intervals = {
"09:00-10:00": {
startTime: "09:00",
endTime: "10:00",
},
"10:00-11:00": {
startTime: "10:00",
endTime: "11:00",
},
"11:00-12:00": {
startTime: "11:00",
endTime: "12:00",
},
};

const [slot1, slot2, slot3, slot4] = [
{
...baseSlot,
id: "slot-1",
intervals,
date,
categories: [Category.Competitive],
},
{
...baseSlot,
id: "slot-2",
intervals,
date,
categories: [Category.Competitive],
},
{
...baseSlot,
id: "slot-3",
intervals,
date,
categories: [Category.Competitive],
},
{
...baseSlot,
id: "slot-4",
intervals,
date: tomorrow,
categories: [Category.Competitive],
},
];

const store = setupBookingsTest({
category: Category.Competitive,
// Any day from the test month would do
date: DateTime.fromISO(date),
slotsByDay: {
[monthStr]: {
// We're adding slots in an arbitrary order to intrduce additional entropy before sorting
[date]: { [slot3.id]: slot3, [slot2.id]: slot2, [slot1.id]: slot1 },
[tomorrow]: { [slot4.id]: slot4 },
},
},
});

store.dispatch(
// We're booking the slots in the same order as they appear in the store,
// this should fail the test unless the fix (tested by this) is not in place.
updateLocalDocuments(BookingSubCollection.BookedSlots, {
[slot3.id]: {
date: slot3.date,
// Last interval (should appear last of all the slots in the day)
interval: "11:00-12:00",
},
[slot1.id]: {
date: slot1.date,
// First interval (should appear first, regardless of the order it's been added)
interval: "09:00-10:00",
},
[slot4.id]: {
date: slot4.date,
// Regerdless of this not being the last interval, this slot should appear last as the date sorting takes precenence
interval: "09:00-10:00",
},
})
);
// Second slot should be attended, not booked to verify that the final result sorts all results, regerdless of the booked state
store.dispatch(
updateLocalDocuments(BookingSubCollection.AttendedSlots, {
[slot2.id]: {
date: slot2.date,
// Second interval (we want this slot to appear 2nd - be merged with the booked slots, and then sorted)
interval: "10:00-11:00",
},
})
);

// It's enough to just check that the ids appear in the desired order
const ids = getBookedAndAttendedSlotsForCalendar(store.getState()).map(
({ id }) => id
);
expect(ids).toEqual(["slot-1", "slot-2", "slot-3", "slot-4"]);
});
});
});
56 changes: 36 additions & 20 deletions packages/client/src/store/selectors/bookings/slots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,13 @@ export const getMonthEmptyForBooking = (state: LocalStore): boolean => {
return isEmpty(getSlotsForCustomer(state));
};

type BookingsList = Array<SlotInterface & { interval: SlotInterval }>;
type BookedAndAttendedList = Array<
SlotInterface & { interval: SlotInterval } & { booked: boolean }
>;
type BookingsEntry = SlotInterface & {
interval: SlotInterval;
booked: true;
};
type BookingsList = Array<BookingsEntry>;

export const getBookingsForCalendar = (
state: LocalStore
): (SlotInterface & { interval: SlotInterval })[] => {
export const getBookingsForCalendar = (state: LocalStore): BookingsList => {
// Current month in view is determined by `currentDate` in Redux store
const monthString = getCalendarDay(state).toISO().substring(0, 7);
// Get all booked slots
Expand All @@ -150,17 +149,21 @@ export const getBookingsForCalendar = (
const slotsForAMonth = slotsByMonth[monthString] || {};

return Object.entries(bookedSlots)
.filter(([, { date }]) => Boolean(slotsForAMonth[date]))
.reduce(
(acc, [slotId, { date, interval: bookedInterval, bookingNotes }]) => {
// If this returns undefined, our slot isn't in date range
const dayOfBookedSlot = slotsForAMonth[date];
if (!dayOfBookedSlot) {
return acc;
}
const bookedSlot = dayOfBookedSlot[slotId];
const bookedSlot = slotsForAMonth[date][slotId];
const interval = bookedSlot.intervals[bookedInterval];
const completeBookingEntry = { ...bookedSlot, interval, bookingNotes };
return [...acc, completeBookingEntry];
return [
...acc,
{
...bookedSlot,
interval,
bookingNotes,
booked: true,
} as BookingsEntry,
];
},
[] as BookingsList
)
Expand All @@ -170,9 +173,15 @@ export const getBookingsForCalendar = (
export const getHasBookingsForCalendar = (state: LocalStore): boolean =>
Boolean(getBookingsForCalendar(state).length);

type CalendarSlotEntry = SlotInterface & {
interval: SlotInterval;
booked: boolean;
};
type CalendarSlotList = CalendarSlotEntry[];

export const getBookedAndAttendedSlotsForCalendar = (
state: LocalStore
): (SlotInterface & { interval: SlotInterval } & { booked: boolean })[] => {
): CalendarSlotList => {
// Current month in view is determined by `currentDate` in Redux store
const monthString = getCalendarDay(state).toISO().substring(0, 7);

Expand Down Expand Up @@ -200,7 +209,7 @@ export const getBookedAndAttendedSlotsForCalendar = (
};
return [...acc, completeAttendanceEntry];
},
[] as BookedAndAttendedList
[] as CalendarSlotList
);
const bookedSlotsObj = Object.entries(bookedSlots).reduce(
(acc, [slotId, { date, interval: bookedInterval, bookingNotes }]) => {
Expand All @@ -220,9 +229,16 @@ export const getBookedAndAttendedSlotsForCalendar = (
};
return [...acc, completeBookingEntry];
},
[] as BookedAndAttendedList
);
return [...attendedSlotsObj, ...bookedSlotsObj].sort((a, b) =>
a.date < b.date ? -1 : 1
[] as CalendarSlotList
);
return [...attendedSlotsObj, ...bookedSlotsObj].sort(sortCalendarSlots);
};

const sortCalendarSlots = (a: CalendarSlotEntry, b: CalendarSlotEntry) =>
a.date < b.date
? -1
: a.date > b.date
? 1
: a.interval.startTime < b.interval.startTime
? -1
: 1;

0 comments on commit e2618f2

Please sign in to comment.