diff --git a/apps/web/pages/availability/[schedule].tsx b/apps/web/pages/availability/[schedule].tsx index a4e320249f..20d7eabd09 100644 --- a/apps/web/pages/availability/[schedule].tsx +++ b/apps/web/pages/availability/[schedule].tsx @@ -74,7 +74,7 @@ const DateOverride = ({ workingHours }: { workingHours: WorkingHours[] }) => { excludedDates={fields.map((field) => yyyymmdd(field.ranges[0].start))} onChange={(ranges) => append({ ranges })} Trigger={ - } @@ -128,7 +128,9 @@ export default function Availability({ schedule }: { schedule: number }) { } + render={({ field }) => ( + + )} /> } subtitle={ diff --git a/apps/web/pages/availability/troubleshoot.tsx b/apps/web/pages/availability/troubleshoot.tsx index bef2365538..b033bb66e9 100644 --- a/apps/web/pages/availability/troubleshoot.tsx +++ b/apps/web/pages/availability/troubleshoot.tsx @@ -1,10 +1,10 @@ -import { useState } from "react"; - import dayjs from "@calcom/dayjs"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { RouterOutputs, trpc } from "@calcom/trpc/react"; import { Shell, SkeletonText } from "@calcom/ui"; +import useRouterQuery from "@lib/hooks/useRouterQuery"; + type User = RouterOutputs["viewer"]["me"]; export interface IBusySlot { @@ -16,7 +16,9 @@ export interface IBusySlot { const AvailabilityView = ({ user }: { user: User }) => { const { t } = useLocale(); - const [selectedDate, setSelectedDate] = useState(dayjs()); + const { date, setQuery: setSelectedDate } = useRouterQuery("date"); + const selectedDate = dayjs(date); + const formattedSelectedDate = selectedDate.format("YYYY-MM-DD"); const { data, isLoading } = trpc.viewer.availability.user.useQuery( { @@ -30,6 +32,17 @@ const AvailabilityView = ({ user }: { user: User }) => { } ); + const overrides = + data?.dateOverrides.reduce((acc, override) => { + if ( + formattedSelectedDate !== dayjs(override.start).format("YYYY-MM-DD") && + formattedSelectedDate !== dayjs(override.end).format("YYYY-MM-DD") + ) + return acc; + acc.push({ ...override, source: "Date override" }); + return acc; + }, [] as IBusySlot[]) || []; + return (
@@ -37,9 +50,9 @@ const AvailabilityView = ({ user }: { user: User }) => { { - if (e.target.value) setSelectedDate(dayjs(e.target.value)); + if (e.target.value) setSelectedDate(e.target.value); }} /> {t("hover_over_bold_times_tip")} @@ -49,39 +62,47 @@ const AvailabilityView = ({ user }: { user: User }) => { {t("your_day_starts_at")} {convertMinsToHrsMins(user.startTime)}
- {isLoading ? ( - <> - - - - ) : data && data.busy.length > 0 ? ( - data.busy - .sort((a: IBusySlot, b: IBusySlot) => (a.start > b.start ? -1 : 1)) - .map((slot: IBusySlot) => ( -
-
- {t("calendar_shows_busy_between")}{" "} - - {dayjs(slot.start).format("HH:mm")} - {" "} - {t("and")}{" "} - - {dayjs(slot.end).format("HH:mm")} - {" "} - {t("on")} {dayjs(slot.start).format("D")}{" "} - {t(dayjs(slot.start).format("MMMM").toLowerCase())} {dayjs(slot.start).format("YYYY")} - {slot.title && ` - (${slot.title})`} - {slot.source && {` - (source: ${slot.source})`}} + {(() => { + if (isLoading) + return ( + <> + + + + ); + + if (data && (data.busy.length > 0 || overrides.length > 0)) + return [...data.busy, ...overrides] + .sort((a: IBusySlot, b: IBusySlot) => (a.start > b.start ? -1 : 1)) + .map((slot: IBusySlot) => ( +
+
+ {t("calendar_shows_busy_between")}{" "} + + {dayjs(slot.start).format("HH:mm")} + {" "} + {t("and")}{" "} + + {dayjs(slot.end).format("HH:mm")} + {" "} + {t("on")} {dayjs(slot.start).format("D")}{" "} + {t(dayjs(slot.start).format("MMMM").toLowerCase())} {dayjs(slot.start).format("YYYY")} + {slot.title && ` - (${slot.title})`} + {slot.source && {` - (source: ${slot.source})`}} +
-
- )) - ) : ( -
-
{t("calendar_no_busy_slots")}
-
- )} + )); + return ( +
+
{t("calendar_no_busy_slots")}
+
+ ); + })()}
diff --git a/apps/web/playwright/availability.e2e.ts b/apps/web/playwright/availability.e2e.ts new file mode 100644 index 0000000000..4bebb0f0d8 --- /dev/null +++ b/apps/web/playwright/availability.e2e.ts @@ -0,0 +1,54 @@ +import { expect } from "@playwright/test"; + +import dayjs from "@calcom/dayjs"; + +import { test } from "./lib/fixtures"; + +test.describe.configure({ mode: "parallel" }); + +test.describe("Availablity tests", () => { + test.beforeEach(async ({ page, users }) => { + const user = await users.create(); + await user.login(); + await page.goto("/availability"); + // We wait until loading is finished + await page.waitForSelector('[data-testid="schedules"]'); + }); + + test.afterEach(async ({ users }) => { + await users.deleteAll(); + }); + + test("Date Overrides", async ({ page }) => { + await test.step("Can add a date override", async () => { + await page.locator('[data-testid="schedules"] > li a').click(); + await page.locator('[data-testid="add-override"]').click(); + await page.locator('[id="modal-title"]').waitFor(); + await page.locator('[data-testid="incrementMonth"]').click(); + await page.locator('[data-testid="day"][data-disabled="false"]').nth(0).click(); + await page.locator('[data-testid="date-override-mark-unavailable"]').click(); + await page.locator('[data-testid="add-override-submit-btn"]').click(); + await expect(page.locator('[data-testid="date-overrides-list"] > li')).toHaveCount(1); + await page.locator('[form="availability-form"][type="submit"]').click(); + }); + + await test.step("Date override is displayed in troubleshooter", async () => { + const response = await page.waitForResponse("**/api/trpc/viewer.availability.schedule.update?batch=1"); + const json = await response.json(); + // @ts-expect-error trust me bro + const date = json[0].result.data.json.schedule.availability.find((a) => !!a.date); + const troubleshooterURL = `/availability/troubleshoot?date=${dayjs(date.date).format("YYYY-MM-DD")}`; + await page.goto(troubleshooterURL); + await expect(page.locator('[data-testid="troubleshooter-busy-time"]')).toHaveCount(1); + }); + }); + + test("Availablity pages", async ({ page }) => { + await test.step("Can add a new schedule", async () => { + await page.locator('[data-testid="new-schedule"]').click(); + await page.locator('[id="name"]').fill("More working hours"); + page.locator('[type="submit"]').click(); + await expect(page.locator("[data-testid=availablity-title]")).toHaveValue("More working hours"); + }); + }); +}); diff --git a/packages/features/schedules/components/DateOverrideInputDialog.tsx b/packages/features/schedules/components/DateOverrideInputDialog.tsx index f9a0282fe5..1fb8484753 100644 --- a/packages/features/schedules/components/DateOverrideInputDialog.tsx +++ b/packages/features/schedules/components/DateOverrideInputDialog.tsx @@ -146,10 +146,16 @@ const DateOverrideForm = ({ label={t("date_overrides_mark_all_day_unavailable_one")} checked={datesUnavailable} onCheckedChange={setDatesUnavailable} + data-testid="date-override-mark-unavailable" />
- diff --git a/packages/features/schedules/components/DateOverrideList.tsx b/packages/features/schedules/components/DateOverrideList.tsx index 5637b291ea..146a0a9e5c 100644 --- a/packages/features/schedules/components/DateOverrideList.tsx +++ b/packages/features/schedules/components/DateOverrideList.tsx @@ -38,7 +38,7 @@ const DateOverrideList = ({ }; return ( -
    +
      {items.map((item, index) => (