Removed some of the UTC logic which shouldn't be there (#7425)
* Removed some of the UTC logic which shouldn't be there * Upgrade rhf, remove SSR on [schedule] page * Fix potential bug with date * Amend schedule trpc call & don't show edit availability if id is undefined * type fix * Order date overrides in list by date * Changed logic to check for unavailable * Fix bug report spotted by @CarinaWolli I can save date overrides for march 1st and 2nd (my date/time when saving was march 1st at 11PM GMT-5). It is saved successfully in the database however the date overrides don't show up in the list. That's why: - new Date() is utc so it is already march 2nd - override.date has always timestamp 00:00 so it is also smaller than the utc time which was in my case 2023-03-02T04:00 * Actual fix, gotta remember it's the schedule tz
This commit is contained in:
@@ -113,7 +113,7 @@ const EventTypeScheduleDetails = () => {
|
||||
);
|
||||
|
||||
const filterDays = (dayNum: number) =>
|
||||
schedule?.schedule.availability.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
|
||||
schedule?.schedule.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
|
||||
|
||||
return (
|
||||
<div className="space-y-4 rounded border px-6 pb-4">
|
||||
@@ -156,15 +156,17 @@ const EventTypeScheduleDetails = () => {
|
||||
<FiGlobe className="ltr:mr-2 rtl:ml-2" />
|
||||
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
|
||||
</span>
|
||||
<Button
|
||||
href={`/availability/${schedule?.schedule.id}`}
|
||||
disabled={isLoading}
|
||||
color="minimal"
|
||||
EndIcon={FiExternalLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
{t("edit_availability")}
|
||||
</Button>
|
||||
{!!schedule?.id && (
|
||||
<Button
|
||||
href={`/availability/${schedule.id}`}
|
||||
disabled={isLoading}
|
||||
color="minimal"
|
||||
EndIcon={FiExternalLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
{t("edit_availability")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-easy-crop": "^3.5.2",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-hook-form": "^7.34.2",
|
||||
"react-hook-form": "^7.43.3",
|
||||
"react-hot-toast": "^2.3.0",
|
||||
"react-intl": "^5.25.1",
|
||||
"react-live-chat-loader": "^2.7.3",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
@@ -9,7 +8,7 @@ import Shell from "@calcom/features/shell/Shell";
|
||||
import { availabilityAsString } from "@calcom/lib/availability";
|
||||
import { yyyymmdd } from "@calcom/lib/date-fns";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { stringOrNumber } from "@calcom/prisma/zod-utils";
|
||||
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
|
||||
import type { Schedule as ScheduleType, TimeRange, WorkingHours } from "@calcom/types/schedule";
|
||||
@@ -35,10 +34,8 @@ import { HttpError } from "@lib/core/http/error";
|
||||
import { SelectSkeletonLoader } from "@components/availability/SkeletonLoader";
|
||||
import EditableHeading from "@components/ui/EditableHeading";
|
||||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
const querySchema = z.object({
|
||||
schedule: stringOrNumber,
|
||||
schedule: z.coerce.number().positive().optional(),
|
||||
});
|
||||
|
||||
type AvailabilityFormValues = {
|
||||
@@ -88,16 +85,29 @@ const DateOverride = ({ workingHours }: { workingHours: WorkingHours[] }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default function Availability({ schedule }: { schedule: number }) {
|
||||
export default function Availability() {
|
||||
const { t, i18n } = useLocale();
|
||||
const router = useRouter();
|
||||
const utils = trpc.useContext();
|
||||
const me = useMeQuery();
|
||||
const {
|
||||
data: { schedule: scheduleId },
|
||||
} = useTypedQuery(querySchema);
|
||||
|
||||
const { timeFormat } = me.data || { timeFormat: null };
|
||||
const { data, isLoading } = trpc.viewer.availability.schedule.get.useQuery({ scheduleId: schedule });
|
||||
const { data: defaultValues } = trpc.viewer.availability.defaultValues.useQuery({ scheduleId: schedule });
|
||||
const form = useForm<AvailabilityFormValues>({ defaultValues });
|
||||
const { control } = form;
|
||||
const { data: schedule, isLoading } = trpc.viewer.availability.schedule.get.useQuery(
|
||||
{ scheduleId },
|
||||
{
|
||||
enabled: !!scheduleId,
|
||||
}
|
||||
);
|
||||
|
||||
const form = useForm<AvailabilityFormValues>({
|
||||
values: schedule && {
|
||||
...schedule,
|
||||
schedule: schedule?.availability || [],
|
||||
},
|
||||
});
|
||||
const updateMutation = trpc.viewer.availability.schedule.update.useMutation({
|
||||
onSuccess: async ({ prevDefaultId, currentDefaultId, ...data }) => {
|
||||
if (prevDefaultId && currentDefaultId) {
|
||||
@@ -144,7 +154,7 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
return (
|
||||
<Shell
|
||||
backPath="/availability"
|
||||
title={data?.schedule.name ? data.schedule.name + " | " + t("availability") : t("availability")}
|
||||
title={schedule?.name ? schedule.name + " | " + t("availability") : t("availability")}
|
||||
heading={
|
||||
<Controller
|
||||
control={form.control}
|
||||
@@ -155,8 +165,8 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
/>
|
||||
}
|
||||
subtitle={
|
||||
data ? (
|
||||
data.schedule.availability
|
||||
schedule ? (
|
||||
schedule.schedule
|
||||
.filter((availability) => !!availability.days.length)
|
||||
.map((availability) => (
|
||||
<span key={availability.id}>
|
||||
@@ -179,7 +189,7 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
</Skeleton>
|
||||
<Switch
|
||||
id="hiddenSwitch"
|
||||
disabled={isLoading || data?.isDefault}
|
||||
disabled={isLoading || schedule?.isDefault}
|
||||
checked={form.watch("isDefault")}
|
||||
onCheckedChange={(e) => {
|
||||
form.setValue("isDefault", e);
|
||||
@@ -199,7 +209,7 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
confirmBtnText={t("delete")}
|
||||
loadingText={t("delete")}
|
||||
onConfirm={() => {
|
||||
deleteMutation.mutate({ scheduleId: schedule });
|
||||
scheduleId && deleteMutation.mutate({ scheduleId });
|
||||
}}>
|
||||
{t("delete_schedule_description")}
|
||||
</ConfirmationDialogContent>
|
||||
@@ -218,11 +228,12 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
form={form}
|
||||
id="availability-form"
|
||||
handleSubmit={async ({ dateOverrides, ...values }) => {
|
||||
updateMutation.mutate({
|
||||
scheduleId: schedule,
|
||||
dateOverrides: dateOverrides.flatMap((override) => override.ranges),
|
||||
...values,
|
||||
});
|
||||
scheduleId &&
|
||||
updateMutation.mutate({
|
||||
scheduleId,
|
||||
dateOverrides: dateOverrides.flatMap((override) => override.ranges),
|
||||
...values,
|
||||
});
|
||||
}}
|
||||
className="flex flex-col sm:mx-0 xl:flex-row xl:space-x-6">
|
||||
<div className="flex-1 flex-row xl:mr-0">
|
||||
@@ -230,7 +241,7 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
<div>
|
||||
{typeof me.data?.weekStart === "string" && (
|
||||
<Schedule
|
||||
control={control}
|
||||
control={form.control}
|
||||
name="schedule"
|
||||
weekStart={
|
||||
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].indexOf(
|
||||
@@ -242,7 +253,7 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-6 rounded-md border">
|
||||
{data?.workingHours && <DateOverride workingHours={data.workingHours} />}
|
||||
{schedule?.workingHours && <DateOverride workingHours={schedule.workingHours} />}
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-40 col-span-3 space-y-2 lg:col-span-1">
|
||||
@@ -282,20 +293,3 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
</Shell>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
||||
const params = querySchema.safeParse(ctx.params);
|
||||
const ssr = await ssrInit(ctx);
|
||||
|
||||
if (!params.success) return { notFound: true };
|
||||
|
||||
const scheduleId = params.data.schedule;
|
||||
await ssr.viewer.availability.schedule.get.fetch({ scheduleId });
|
||||
await ssr.viewer.availability.defaultValues.fetch({ scheduleId });
|
||||
return {
|
||||
props: {
|
||||
schedule: scheduleId,
|
||||
trpcState: ssr.dehydrate(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"description": "Apple calendar runs both the macOS and iOS mobile operating systems. Offering online cloud backup of calendars using Apple’s iCloud service, it can sync with Google Calendar and Microsoft Exchange Server. Users can schedule events in their day that include time, location, duration, and extra notes.",
|
||||
"dependencies": {
|
||||
"@calcom/prisma": "*",
|
||||
"react-hook-form": "^7.34.2"
|
||||
"react-hook-form": "^7.43.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@calcom/types": "*"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"@calcom/lib": "*",
|
||||
"@calcom/prisma": "*",
|
||||
"@calcom/ui": "*",
|
||||
"react-hook-form": "^7.34.2"
|
||||
"react-hook-form": "^7.43.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@calcom/types": "*"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"@calcom/lib": "*",
|
||||
"@calcom/prisma": "*",
|
||||
"@calcom/ui": "*",
|
||||
"react-hook-form": "^7.34.2",
|
||||
"react-hook-form": "^7.43.3",
|
||||
"ews-javascript-api": "^0.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"@calcom/lib": "*",
|
||||
"@calcom/prisma": "*",
|
||||
"@calcom/ui": "*",
|
||||
"react-hook-form": "^7.34.2",
|
||||
"react-hook-form": "^7.43.3",
|
||||
"ews-javascript-api": "^0.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { useState, useMemo } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import dayjs, { Dayjs } from "@calcom/dayjs";
|
||||
import type { Dayjs } from "@calcom/dayjs";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { daysInMonth, yyyymmdd } from "@calcom/lib/date-fns";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
|
||||
import { WorkingHours } from "@calcom/types/schedule";
|
||||
import type { WorkingHours } from "@calcom/types/schedule";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -19,12 +20,8 @@ import {
|
||||
} from "@calcom/ui";
|
||||
|
||||
import DatePicker from "../../calendars/DatePicker";
|
||||
import { DayRanges, TimeRange } from "./Schedule";
|
||||
|
||||
const ALL_DAY_RANGE = {
|
||||
start: new Date(dayjs.utc().hour(0).minute(0).second(0).format()),
|
||||
end: new Date(dayjs.utc().hour(0).minute(0).second(0).format()),
|
||||
};
|
||||
import type { TimeRange } from "./Schedule";
|
||||
import { DayRanges } from "./Schedule";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
const noop = () => {};
|
||||
@@ -70,37 +67,33 @@ const DateOverrideForm = ({
|
||||
[browsingDate]
|
||||
);
|
||||
|
||||
const form = useForm<{ range: TimeRange[] }>();
|
||||
const { reset } = form;
|
||||
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
reset({
|
||||
range: value.map((range) => ({
|
||||
start: new Date(
|
||||
dayjs.utc().hour(range.start.getUTCHours()).minute(range.start.getUTCMinutes()).second(0).format()
|
||||
),
|
||||
end: new Date(
|
||||
dayjs.utc().hour(range.end.getUTCHours()).minute(range.end.getUTCMinutes()).second(0).format()
|
||||
),
|
||||
})),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const dayRanges = (workingHours || []).reduce((dayRanges, workingHour) => {
|
||||
if (date && workingHour.days.includes(date.day())) {
|
||||
dayRanges.push({
|
||||
start: dayjs.utc().startOf("day").add(workingHour.startTime, "minute").toDate(),
|
||||
end: dayjs.utc().startOf("day").add(workingHour.endTime, "minute").toDate(),
|
||||
});
|
||||
}
|
||||
return dayRanges;
|
||||
}, [] as TimeRange[]);
|
||||
reset({
|
||||
range: dayRanges,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [date, value]);
|
||||
const form = useForm({
|
||||
values: {
|
||||
range: value
|
||||
? value.map((range) => ({
|
||||
start: new Date(
|
||||
dayjs
|
||||
.utc()
|
||||
.hour(range.start.getUTCHours())
|
||||
.minute(range.start.getUTCMinutes())
|
||||
.second(0)
|
||||
.format()
|
||||
),
|
||||
end: new Date(
|
||||
dayjs.utc().hour(range.end.getUTCHours()).minute(range.end.getUTCMinutes()).second(0).format()
|
||||
),
|
||||
}))
|
||||
: (workingHours || []).reduce((dayRanges, workingHour) => {
|
||||
if (date && workingHour.days.includes(date.day())) {
|
||||
dayRanges.push({
|
||||
start: dayjs.utc().startOf("day").add(workingHour.startTime, "minute").toDate(),
|
||||
end: dayjs.utc().startOf("day").add(workingHour.endTime, "minute").toDate(),
|
||||
});
|
||||
}
|
||||
return dayRanges;
|
||||
}, [] as TimeRange[]),
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Form
|
||||
@@ -108,7 +101,15 @@ const DateOverrideForm = ({
|
||||
handleSubmit={(values) => {
|
||||
if (!date) return;
|
||||
onChange(
|
||||
(datesUnavailable ? [ALL_DAY_RANGE] : values.range).map((item) => ({
|
||||
(datesUnavailable
|
||||
? [
|
||||
{
|
||||
start: date.utc(true).startOf("day").toDate(),
|
||||
end: date.utc(true).startOf("day").add(1, "day").toDate(),
|
||||
},
|
||||
]
|
||||
: values.range
|
||||
).map((item) => ({
|
||||
start: date.hour(item.start.getHours()).minute(item.start.getMinutes()).toDate(),
|
||||
end: date.hour(item.end.getHours()).minute(item.end.getMinutes()).toDate(),
|
||||
}))
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
import { UseFieldArrayRemove } from "react-hook-form";
|
||||
import type { UseFieldArrayRemove } from "react-hook-form";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { TimeRange, WorkingHours } from "@calcom/types/schedule";
|
||||
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
|
||||
import type { TimeRange, WorkingHours } from "@calcom/types/schedule";
|
||||
import { Button, DialogTrigger, Tooltip } from "@calcom/ui";
|
||||
import { FiEdit2, FiTrash2 } from "@calcom/ui/components/icon";
|
||||
|
||||
import DateOverrideInputDialog from "./DateOverrideInputDialog";
|
||||
|
||||
const sortByDate = (a: { ranges: TimeRange[]; id: string }, b: { ranges: TimeRange[]; id: string }) => {
|
||||
return a.ranges[0].start > b.ranges[0].start ? 1 : -1;
|
||||
};
|
||||
|
||||
const useSettings = () => {
|
||||
const { data } = useMeQuery();
|
||||
return {
|
||||
hour12: data?.timeFormat === 12,
|
||||
timeZone: data?.timeZone,
|
||||
};
|
||||
};
|
||||
|
||||
const DateOverrideList = ({
|
||||
items,
|
||||
remove,
|
||||
@@ -22,17 +35,18 @@ const DateOverrideList = ({
|
||||
excludedDates?: string[];
|
||||
}) => {
|
||||
const { t, i18n } = useLocale();
|
||||
const { hour12 } = useSettings();
|
||||
if (!items.length) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const timeSpan = ({ start, end }: TimeRange) => {
|
||||
return (
|
||||
new Intl.DateTimeFormat(i18n.language, { hour: "numeric", minute: "numeric", hour12: true }).format(
|
||||
new Intl.DateTimeFormat(i18n.language, { hour: "numeric", minute: "numeric", hour12 }).format(
|
||||
new Date(start.toISOString().slice(0, -1))
|
||||
) +
|
||||
" - " +
|
||||
new Intl.DateTimeFormat(i18n.language, { hour: "numeric", minute: "numeric", hour12: true }).format(
|
||||
new Intl.DateTimeFormat(i18n.language, { hour: "numeric", minute: "numeric", hour12 }).format(
|
||||
new Date(end.toISOString().slice(0, -1))
|
||||
)
|
||||
);
|
||||
@@ -40,7 +54,7 @@ const DateOverrideList = ({
|
||||
|
||||
return (
|
||||
<ul className="rounded border border-gray-200" data-testid="date-overrides-list">
|
||||
{items.map((item, index) => (
|
||||
{items.sort(sortByDate).map((item, index) => (
|
||||
<li key={item.id} className="flex justify-between border-b px-5 py-4 last:border-b-0">
|
||||
<div>
|
||||
<h3 className="text-sm text-gray-900">
|
||||
@@ -50,7 +64,7 @@ const DateOverrideList = ({
|
||||
day: "numeric",
|
||||
}).format(item.ranges[0].start)}
|
||||
</h3>
|
||||
{item.ranges[0].end.getUTCHours() === 0 && item.ranges[0].end.getUTCMinutes() === 0 ? (
|
||||
{item.ranges[0].start.valueOf() - item.ranges[0].end.valueOf() === 0 ? (
|
||||
<p className="text-xs text-gray-500">{t("unavailable")}</p>
|
||||
) : (
|
||||
item.ranges.map((range, i) => (
|
||||
|
||||
@@ -59,76 +59,6 @@ export const availabilityRouter = router({
|
||||
.query(({ input }) => {
|
||||
return getUserAvailability(input);
|
||||
}),
|
||||
defaultValues: authedProcedure.input(z.object({ scheduleId: z.number() })).query(async ({ ctx, input }) => {
|
||||
const { prisma, user } = ctx;
|
||||
const schedule = await prisma.schedule.findUnique({
|
||||
where: {
|
||||
id: input.scheduleId || (await getDefaultScheduleId(user.id, prisma)),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
userId: true,
|
||||
name: true,
|
||||
availability: true,
|
||||
timeZone: true,
|
||||
eventType: {
|
||||
select: {
|
||||
_count: true,
|
||||
id: true,
|
||||
eventName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!schedule || schedule.userId !== user.id) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
});
|
||||
}
|
||||
const availability = convertScheduleToAvailability(schedule);
|
||||
return {
|
||||
name: schedule.name,
|
||||
rawSchedule: schedule,
|
||||
schedule: availability.map((a) =>
|
||||
a.map((startAndEnd) => ({
|
||||
...startAndEnd,
|
||||
// Turn our limited granularity into proper end of day.
|
||||
end: new Date(startAndEnd.end.toISOString().replace("23:59:00.000Z", "23:59:59.999Z")),
|
||||
}))
|
||||
),
|
||||
dateOverrides: schedule.availability.reduce((acc, override) => {
|
||||
// only iff future date override
|
||||
if (!override.date || override.date < new Date()) {
|
||||
return acc;
|
||||
}
|
||||
const newValue = {
|
||||
start: dayjs
|
||||
.utc(override.date)
|
||||
.hour(override.startTime.getUTCHours())
|
||||
.minute(override.startTime.getUTCMinutes())
|
||||
.toDate(),
|
||||
end: dayjs
|
||||
.utc(override.date)
|
||||
.hour(override.endTime.getUTCHours())
|
||||
.minute(override.endTime.getUTCMinutes())
|
||||
.toDate(),
|
||||
};
|
||||
const dayRangeIndex = acc.findIndex(
|
||||
// early return prevents override.date from ever being empty.
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
(item) => yyyymmdd(item.ranges[0].start) === yyyymmdd(override.date!)
|
||||
);
|
||||
if (dayRangeIndex === -1) {
|
||||
acc.push({ ranges: [newValue] });
|
||||
return acc;
|
||||
}
|
||||
acc[dayRangeIndex].ranges.push(newValue);
|
||||
return acc;
|
||||
}, [] as { ranges: TimeRange[] }[]),
|
||||
timeZone: schedule.timeZone || user.timeZone,
|
||||
isDefault: !input.scheduleId || user.defaultScheduleId === schedule.id,
|
||||
};
|
||||
}),
|
||||
schedule: router({
|
||||
get: authedProcedure
|
||||
.input(
|
||||
@@ -162,15 +92,52 @@ export const availabilityRouter = router({
|
||||
code: "UNAUTHORIZED",
|
||||
});
|
||||
}
|
||||
const availability = convertScheduleToAvailability(schedule);
|
||||
const timeZone = schedule.timeZone || user.timeZone;
|
||||
return {
|
||||
schedule,
|
||||
id: schedule.id,
|
||||
name: schedule.name,
|
||||
workingHours: getWorkingHours(
|
||||
{ timeZone: schedule.timeZone || undefined },
|
||||
schedule.availability || []
|
||||
),
|
||||
availability,
|
||||
timeZone: schedule.timeZone || user.timeZone,
|
||||
schedule: schedule.availability,
|
||||
availability: convertScheduleToAvailability(schedule).map((a) =>
|
||||
a.map((startAndEnd) => ({
|
||||
...startAndEnd,
|
||||
// Turn our limited granularity into proper end of day.
|
||||
end: new Date(startAndEnd.end.toISOString().replace("23:59:00.000Z", "23:59:59.999Z")),
|
||||
}))
|
||||
),
|
||||
timeZone,
|
||||
dateOverrides: schedule.availability.reduce((acc, override) => {
|
||||
// only iff future date override
|
||||
if (!override.date || dayjs.tz(override.date, timeZone).isBefore(dayjs(), "day")) {
|
||||
return acc;
|
||||
}
|
||||
const newValue = {
|
||||
start: dayjs
|
||||
.utc(override.date)
|
||||
.hour(override.startTime.getUTCHours())
|
||||
.minute(override.startTime.getUTCMinutes())
|
||||
.toDate(),
|
||||
end: dayjs
|
||||
.utc(override.date)
|
||||
.hour(override.endTime.getUTCHours())
|
||||
.minute(override.endTime.getUTCMinutes())
|
||||
.toDate(),
|
||||
};
|
||||
const dayRangeIndex = acc.findIndex(
|
||||
// early return prevents override.date from ever being empty.
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
(item) => yyyymmdd(item.ranges[0].start) === yyyymmdd(override.date!)
|
||||
);
|
||||
if (dayRangeIndex === -1) {
|
||||
acc.push({ ranges: [newValue] });
|
||||
return acc;
|
||||
}
|
||||
acc[dayRangeIndex].ranges.push(newValue);
|
||||
return acc;
|
||||
}, [] as { ranges: TimeRange[] }[]),
|
||||
isDefault: !input.scheduleId || user.defaultScheduleId === schedule.id,
|
||||
};
|
||||
}),
|
||||
@@ -469,10 +436,6 @@ const setupDefaultSchedule = async (userId: number, scheduleId: number, prisma:
|
||||
});
|
||||
};
|
||||
|
||||
const _isDefaultSchedule = (scheduleId: number, user: Partial<User>) => {
|
||||
return !user.defaultScheduleId || user.defaultScheduleId === scheduleId;
|
||||
};
|
||||
|
||||
const getDefaultScheduleId = async (userId: number, prisma: PrismaClient) => {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-colorful": "^5.6.0",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-hook-form": "^7.34.2",
|
||||
"react-hook-form": "^7.43.3",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-select": "^5.4.0"
|
||||
},
|
||||
|
||||
@@ -23014,6 +23014,11 @@ react-hook-form@^7.34.2:
|
||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.34.2.tgz#9ac6d1a309a7c4aaa369d1269357a70e9e9bf4de"
|
||||
integrity sha512-1lYWbEqr0GW7HHUjMScXMidGvV0BE2RJV3ap2BL7G0EJirkqpccTaawbsvBO8GZaB3JjCeFBEbnEWI1P8ZoLRQ==
|
||||
|
||||
react-hook-form@^7.43.3:
|
||||
version "7.43.3"
|
||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.43.3.tgz#780af64ea1f3c5864626a377e302bfcc7750af6f"
|
||||
integrity sha512-LV6Fixh+hirrl6dXbM78aB6n//82aKbsNbcofF3wc6nx1UJLu3Jj/gsg1E5C9iISnLX+du8VTUyBUz2aCy+H7w==
|
||||
|
||||
react-hot-toast@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.3.0.tgz#70b3d183ac2a4afb6b17cda4a7f4cfe02e730415"
|
||||
|
||||
Reference in New Issue
Block a user