Fix 404 error when editing default availability & create new availability from event type page (#5085)
* Edit/create new availability * Connect new schedule to event type * Remove comment * Shell back button executes window.history.back * Fix default schedule * Add default badge * Fix type error
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { FormValues } from "pages/event-types/[type]";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import { SingleValueProps, OptionProps, components } from "react-select";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
@@ -7,6 +8,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { weekdayNames } from "@calcom/lib/weekday";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Icon } from "@calcom/ui";
|
||||
import { Badge } from "@calcom/ui/v2";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import Select from "@calcom/ui/v2/core/form/select";
|
||||
import { SkeletonText } from "@calcom/ui/v2/core/skeleton";
|
||||
@@ -16,6 +18,35 @@ import { SelectSkeletonLoader } from "@components/v2/availability/SkeletonLoader
|
||||
type AvailabilityOption = {
|
||||
label: string;
|
||||
value: number;
|
||||
isDefault: boolean;
|
||||
};
|
||||
|
||||
const Option = ({ ...props }: OptionProps<AvailabilityOption>) => {
|
||||
const { label, isDefault } = props.data;
|
||||
return (
|
||||
<components.Option {...props}>
|
||||
<span>{label}</span>
|
||||
{isDefault && (
|
||||
<Badge variant="blue" className="ml-2">
|
||||
Default
|
||||
</Badge>
|
||||
)}
|
||||
</components.Option>
|
||||
);
|
||||
};
|
||||
|
||||
const SingleValue = ({ ...props }: SingleValueProps<AvailabilityOption>) => {
|
||||
const { label, isDefault } = props.data;
|
||||
return (
|
||||
<components.SingleValue {...props}>
|
||||
<span>{label}</span>
|
||||
{isDefault && (
|
||||
<Badge variant="blue" className="ml-2">
|
||||
Default
|
||||
</Badge>
|
||||
)}
|
||||
</components.SingleValue>
|
||||
);
|
||||
};
|
||||
|
||||
const AvailabilitySelect = ({
|
||||
@@ -38,6 +69,7 @@ const AvailabilitySelect = ({
|
||||
const options = schedules.map((schedule) => ({
|
||||
value: schedule.id,
|
||||
label: schedule.name,
|
||||
isDefault: schedule.isDefault,
|
||||
}));
|
||||
|
||||
const value = options.find((option) =>
|
||||
@@ -53,6 +85,8 @@ const AvailabilitySelect = ({
|
||||
onChange={props.onChange}
|
||||
className={classNames("block w-full min-w-0 flex-1 rounded-sm text-sm", className)}
|
||||
value={value}
|
||||
components={{ Option, SingleValue }}
|
||||
isMulti={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -130,7 +164,7 @@ export const AvailabilityTab = () => {
|
||||
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
|
||||
</span>
|
||||
<Button
|
||||
href={`/availability/${scheduleId}`}
|
||||
href={`/availability/${schedule?.schedule.id}`}
|
||||
color="minimal"
|
||||
EndIcon={Icon.FiExternalLink}
|
||||
target="_blank"
|
||||
|
||||
@@ -162,7 +162,7 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-40 col-span-3 space-y-2 lg:col-span-1">
|
||||
<div className="xl:max-w-80 w-full pr-4 sm:p-0">
|
||||
<div className="xl:max-w-80 mt-4 w-full pr-4 sm:p-0">
|
||||
<div>
|
||||
<label htmlFor="timeZone" className="block text-sm font-medium text-gray-700">
|
||||
{t("timezone")}
|
||||
|
||||
@@ -1309,6 +1309,8 @@
|
||||
"cant_find_the_right_video_app_visit_our_app_store": "Can't find the right video app? Visit our <1>App Store</1>.",
|
||||
"install_new_calendar_app": "Install new calendar app",
|
||||
"make_phone_number_required": "Make phone number required for booking event",
|
||||
"new_event_type_availability": "{{eventTypeTitle}} Availability",
|
||||
"error_editing_availability": "Error editing availability",
|
||||
"dont_have_permission": "You don't have permission to access this resource.",
|
||||
"saml_config": "Single Sign-On",
|
||||
"saml_description": "Allow team members to login using an Identity Provider",
|
||||
|
||||
@@ -15,6 +15,7 @@ export const availabilityRouter = createProtectedRouter()
|
||||
.query("list", {
|
||||
async resolve({ ctx }) {
|
||||
const { prisma, user } = ctx;
|
||||
|
||||
const schedules = await prisma.schedule.findMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
@@ -29,10 +30,13 @@ export const availabilityRouter = createProtectedRouter()
|
||||
id: "asc",
|
||||
},
|
||||
});
|
||||
|
||||
const defaultScheduleId = await getDefaultScheduleId(user.id, prisma);
|
||||
|
||||
return {
|
||||
schedules: schedules.map((schedule) => ({
|
||||
...schedule,
|
||||
isDefault: user.defaultScheduleId === schedule.id || schedules.length === 1,
|
||||
isDefault: schedule.id === defaultScheduleId,
|
||||
})),
|
||||
};
|
||||
},
|
||||
@@ -102,6 +106,7 @@ export const availabilityRouter = createProtectedRouter()
|
||||
)
|
||||
)
|
||||
.optional(),
|
||||
eventTypeId: z.number().optional(),
|
||||
}),
|
||||
async resolve({ input, ctx }) {
|
||||
const { user, prisma } = ctx;
|
||||
@@ -112,6 +117,8 @@ export const availabilityRouter = createProtectedRouter()
|
||||
id: user.id,
|
||||
},
|
||||
},
|
||||
// If an eventTypeId is provided then connect the new schedule to that event type
|
||||
...(input.eventTypeId && { eventType: { connect: { id: input.eventTypeId } } }),
|
||||
};
|
||||
|
||||
const availability = getAvailabilityFromSchedule(input.schedule || DEFAULT_SCHEDULE);
|
||||
|
||||
@@ -170,7 +170,7 @@ type LayoutProps = {
|
||||
TopNavContainer?: ReactNode;
|
||||
drawerState?: DrawerState;
|
||||
HeadingLeftIcon?: ReactNode;
|
||||
backPath?: string; // renders back button to specified path
|
||||
backPath?: string | boolean; // renders back button to specified path
|
||||
// use when content needs to expand with flex
|
||||
flexChildrenContainer?: boolean;
|
||||
isPublic?: boolean;
|
||||
@@ -773,7 +773,9 @@ export function ShellMain(props: LayoutProps) {
|
||||
<Button
|
||||
size="icon"
|
||||
color="minimal"
|
||||
onClick={() => router.push(props.backPath as string)}
|
||||
onClick={() =>
|
||||
typeof props.backPath === "string" ? router.push(props.backPath as string) : router.back()
|
||||
}
|
||||
StartIcon={Icon.FiArrowLeft}
|
||||
aria-label="Go Back"
|
||||
className="ltr:mr-2 rtl:ml-2"
|
||||
|
||||
@@ -71,7 +71,24 @@ const Select = <
|
||||
return getReactSelectProps<Option, IsMulti, Group>({ className, components: components || {} });
|
||||
}, [className, components]);
|
||||
|
||||
return <ReactSelect {...reactSelectProps} {...props} />;
|
||||
return (
|
||||
<ReactSelect
|
||||
{...reactSelectProps}
|
||||
{...props}
|
||||
styles={{
|
||||
option: (defaultStyles, state) => ({
|
||||
...defaultStyles,
|
||||
backgroundColor: state.isSelected
|
||||
? state.isFocused
|
||||
? "var(--brand-color)"
|
||||
: "var(--brand-color)"
|
||||
: state.isFocused
|
||||
? "var(--brand-color-dark-mode)"
|
||||
: "var(--brand-text-color)",
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const SelectField = function SelectField<
|
||||
|
||||
Reference in New Issue
Block a user