fix: #15749 Display availability page as per weekstart setting (#15753)

* update to display availability page as per weekstart setting

* update to accomodate any day

* update for AvailabilityTab

* reusable and tests added

* update test description

---------

Co-authored-by: Amit Sharma <74371312+Amit91848@users.noreply.github.com>
This commit is contained in:
Vijay
2024-07-16 11:31:02 +02:00
committed by GitHub
co-authored by Amit Sharma
parent 13ec930a61
commit 283ca9fc74
7 changed files with 138 additions and 14 deletions
@@ -10,6 +10,7 @@ import type { AvailabilityOption, FormValues } from "@calcom/features/eventtypes
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { weekdayNames } from "@calcom/lib/weekday";
import { weekStartNum } from "@calcom/lib/weekstart";
import { SchedulingType } from "@calcom/prisma/enums";
import { trpc } from "@calcom/trpc/react";
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
@@ -87,14 +88,16 @@ const EventTypeScheduleDetails = memo(
{ enabled: !!scheduleId || !!loggedInUser?.defaultScheduleId || !!selectedScheduleValue }
);
const weekStart = weekStartNum(loggedInUser?.weekStart);
const filterDays = (dayNum: number) =>
schedule?.schedule.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
schedule?.schedule.filter((item) => item.days.includes((dayNum + weekStart) % 7)) || [];
return (
<div>
<div className="border-subtle space-y-4 border-x p-6">
<ol className="table border-collapse text-sm">
{weekdayNames(i18n.language, 1, "long").map((day, index) => {
{weekdayNames(i18n.language, weekStart, "long").map((day, index) => {
const isAvailable = !!filterDays(index).length;
return (
<li key={day} className="my-6 flex border-transparent last:mb-2">
+1
View File
@@ -125,6 +125,7 @@ export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availab
displayOptions={{
hour12: meQuery.data?.timeFormat ? meQuery.data.timeFormat === 12 : undefined,
timeZone: meQuery.data?.timeZone,
weekStart: meQuery.data?.weekStart || "Sunday",
}}
key={schedule.id}
schedule={schedule}
@@ -3,6 +3,7 @@ import { Fragment } from "react";
import { availabilityAsString } from "@calcom/lib/availability";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { sortAvailabilityStrings } from "@calcom/lib/weekstart";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import {
@@ -30,6 +31,7 @@ export function ScheduleListItem({
displayOptions?: {
timeZone?: string;
hour12?: boolean;
weekStart?: string;
};
isDeletable: boolean;
updateDefault: ({ scheduleId, isDefault }: { scheduleId: number; isDefault: boolean }) => void;
@@ -58,12 +60,17 @@ export function ScheduleListItem({
<p className="text-subtle mt-1">
{schedule.availability
.filter((availability) => !!availability.days.length)
.map((availability) => (
<Fragment key={availability.id}>
{availabilityAsString(availability, {
locale: i18n.language,
hour12: displayOptions?.hour12,
})}
.map((availability) =>
availabilityAsString(availability, {
locale: i18n.language,
hour12: displayOptions?.hour12,
})
)
// sort the availability strings as per user's weekstart (settings)
.sort(sortAvailabilityStrings(i18n.language, displayOptions?.weekStart))
.map((availabilityString, index) => (
<Fragment key={index}>
{availabilityString}
<br />
</Fragment>
))}
+83
View File
@@ -0,0 +1,83 @@
import { describe, expect, it } from "vitest";
import { sortAvailabilityStrings } from "./weekstart";
describe("Weekstart tests", () => {
describe("fn: sortAvailabilityStrings", () => {
it("should sort the availability strings when changing weekstart from Sunday to Monday", () => {
const input = [
"Sun, 11:45 AM - 5:00 PM",
"Mon, 7:45 AM - 4:15 PM",
"Tue, 9:30 AM - 5:00 PM",
"Wed, 9:15 AM - 5:00 PM",
"Thu, 9:00 AM - 5:00 PM",
"Fri, 7:45 AM - 5:00 PM",
"Sat, 10:00 AM - 5:00 PM",
];
const expected = [
"Mon, 7:45 AM - 4:15 PM",
"Tue, 9:30 AM - 5:00 PM",
"Wed, 9:15 AM - 5:00 PM",
"Thu, 9:00 AM - 5:00 PM",
"Fri, 7:45 AM - 5:00 PM",
"Sat, 10:00 AM - 5:00 PM",
"Sun, 11:45 AM - 5:00 PM",
];
const result = input.sort(sortAvailabilityStrings("en-US", "Monday"));
expect(result).toEqual(expected);
});
it("should sort the availability strings when changing weekstart from Monday to Sunday", () => {
const input = [
"Mon - Tue, Thu 7:45 AM - 4:15 PM",
"Tue, 5:00 PM - 8:00 PM",
"Wed, 9:15 AM - 5:00 PM",
"Fri, 7:45 AM - 5:00 PM",
"Sat, 10:00 AM - 5:00 PM",
"Sun, 11:45 AM - 5:00 PM",
];
const expected = [
"Sun, 11:45 AM - 5:00 PM",
"Mon - Tue, Thu 7:45 AM - 4:15 PM",
"Tue, 5:00 PM - 8:00 PM",
"Wed, 9:15 AM - 5:00 PM",
"Fri, 7:45 AM - 5:00 PM",
"Sat, 10:00 AM - 5:00 PM",
];
const result = input.sort(sortAvailabilityStrings("en-US", "Sunday"));
expect(result).toEqual(expected);
});
it("should sort the availability strings as per the weekstart (random to tuesday)", () => {
// this input scenario is not possible to occur, as regardless of any day as start of week,
// the weekdays always occur consecutively. But, this to test the sorting function in any case.
const input = [
"Thu, Mon 7:45 AM - 8:45 AM",
"Sat, 10:00 AM - 10:30 AM",
"Tue, Fri 4:00 PM - 4:30 PM",
"Tue, 5:00 PM - 8:00 PM",
"Tue, Sun 9:00 AM - 9:30 AM",
"Wed - Thu, Sat 11:00 AM - 11:30 AM",
];
const expected = [
"Tue, Fri 4:00 PM - 4:30 PM",
"Tue, 5:00 PM - 8:00 PM",
"Tue, Sun 9:00 AM - 9:30 AM",
"Wed - Thu, Sat 11:00 AM - 11:30 AM",
"Thu, Mon 7:45 AM - 8:45 AM",
"Sat, 10:00 AM - 10:30 AM",
];
const result = input.sort(sortAvailabilityStrings("en-US", "Tuesday"));
expect(result).toEqual(expected);
});
});
});
+23
View File
@@ -0,0 +1,23 @@
import { weekdayNames } from "./weekday";
// returns index of input weekName(long) from array starting from Sunday
// as fall back returns 0 i.e. Sunday
export function weekStartNum(weekName = "Sunday") {
const weekDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
const idx = weekDays.indexOf(weekName);
return (idx !== -1 ? idx : 0) as 0 | 1 | 2 | 3 | 4 | 5 | 6;
}
// sorts the availability strings based on the weekStart
export function sortAvailabilityStrings(language: string, weekStart = "Sunday") {
return (a: string, b: string) => {
const weekNames = weekdayNames(language, weekStartNum(weekStart), "short");
const weekIndex = (day: string) => {
for (let i = 0; i < weekNames.length; i++) {
if (day.includes(weekNames[i])) return i;
}
return -1;
};
return weekIndex(a) - weekIndex(b);
};
}
@@ -10,6 +10,7 @@ import WebShell from "@calcom/features/shell/Shell";
import { availabilityAsString } from "@calcom/lib/availability";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { sortAvailabilityStrings } from "@calcom/lib/weekstart";
import type { RouterOutputs } from "@calcom/trpc/react";
import type { TimeRange, WorkingHours } from "@calcom/types/schedule";
import {
@@ -291,11 +292,17 @@ export function AvailabilitySettings({
schedule ? (
schedule.schedule
.filter((availability) => !!availability.days.length)
.map((availability) => (
<span
key={availability.startTime.valueOf().toString()}
className={cn(customClassNames?.subtitlesClassName)}>
{availabilityAsString(availability, { locale: i18n.language, hour12: timeFormat === 12 })}
.map((availability) =>
availabilityAsString(availability, {
locale: i18n.language,
hour12: timeFormat === 12,
})
)
// sort the availability strings as per user's weekstart (settings)
.sort(sortAvailabilityStrings(i18n.language, weekStart))
.map((availabilityString, index) => (
<span key={index} className={cn(customClassNames?.subtitlesClassName)}>
{availabilityString}
<br />
</span>
))
@@ -99,7 +99,7 @@ export const AvailabilitySettingsPlatformWrapper = ({
handleSubmit={async (data) => {
atomSchedule.id && handleUpdate(atomSchedule.id, data);
}}
weekStart="Sunday"
weekStart={me?.data?.weekStart || "Sunday"}
timeFormat={timeFormat}
enableOverrides={enableOverrides}
isLoading={isLoading}