feat: allow delete event-type platform atoms (#17065)
* feat: allow delete event-type platform atoms * fixup! feat: allow delete event-type platform atoms
This commit is contained in:
@@ -47,26 +47,40 @@ export type EventTypeSetup = RouterOutputs["viewer"]["eventTypes"]["get"]["event
|
||||
export type EventTypeAssignedUsers = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"]["children"];
|
||||
export type EventTypeHosts = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"]["hosts"];
|
||||
|
||||
export const EventType = (
|
||||
props: EventTypeSetupProps & {
|
||||
allActiveWorkflows?: Workflow[];
|
||||
tabMap: TabMap;
|
||||
onDelete: (id: number) => void;
|
||||
isDeleting?: boolean;
|
||||
onConflict: (eventTypes: ChildrenEventType[]) => void;
|
||||
children?: React.ReactNode;
|
||||
handleSubmit: (values: FormValues) => void;
|
||||
formMethods: UseFormReturn<FormValues>;
|
||||
eventTypeApps?: EventTypeApps;
|
||||
isUpdating: boolean;
|
||||
isPlatform?: boolean;
|
||||
tabName: (typeof tabs)[number];
|
||||
tabsNavigation: VerticalTabItemProps[];
|
||||
}
|
||||
) => {
|
||||
const { formMethods, isPlatform, tabName } = props;
|
||||
const { eventType, team, currentUserMembership, tabMap, isUpdating } = props;
|
||||
export type EventTypeComponentProps = EventTypeSetupProps & {
|
||||
allActiveWorkflows?: Workflow[];
|
||||
tabMap: TabMap;
|
||||
onDelete: (id: number) => void;
|
||||
isDeleting?: boolean;
|
||||
onConflict: (eventTypes: ChildrenEventType[]) => void;
|
||||
children?: React.ReactNode;
|
||||
handleSubmit: (values: FormValues) => void;
|
||||
formMethods: UseFormReturn<FormValues>;
|
||||
eventTypeApps?: EventTypeApps;
|
||||
isUpdating: boolean;
|
||||
isPlatform?: boolean;
|
||||
tabName: (typeof tabs)[number];
|
||||
tabsNavigation: VerticalTabItemProps[];
|
||||
allowDelete?: boolean;
|
||||
};
|
||||
|
||||
export const EventType = ({
|
||||
formMethods,
|
||||
isPlatform,
|
||||
tabName,
|
||||
eventType,
|
||||
team,
|
||||
currentUserMembership,
|
||||
tabMap,
|
||||
isUpdating,
|
||||
isUserOrganizationAdmin,
|
||||
onDelete,
|
||||
isDeleting,
|
||||
tabsNavigation,
|
||||
handleSubmit,
|
||||
children,
|
||||
allowDelete = true,
|
||||
}: EventTypeComponentProps) => {
|
||||
const [animationParentRef] = useAutoAnimate<HTMLDivElement>();
|
||||
|
||||
return (
|
||||
@@ -80,16 +94,17 @@ export const EventType = (
|
||||
disableBorder={true}
|
||||
currentUserMembership={currentUserMembership}
|
||||
bookerUrl={eventType.bookerUrl}
|
||||
isUserOrganizationAdmin={props.isUserOrganizationAdmin}
|
||||
onDelete={props.onDelete}
|
||||
isDeleting={props.isDeleting}
|
||||
isUserOrganizationAdmin={isUserOrganizationAdmin}
|
||||
onDelete={onDelete}
|
||||
isDeleting={isDeleting}
|
||||
isPlatform={isPlatform}
|
||||
tabsNavigation={props.tabsNavigation}>
|
||||
<Form form={formMethods} id="event-type-form" handleSubmit={props.handleSubmit}>
|
||||
allowDelete={allowDelete}
|
||||
tabsNavigation={tabsNavigation}>
|
||||
<Form form={formMethods} id="event-type-form" handleSubmit={handleSubmit}>
|
||||
<div ref={animationParentRef}>{tabMap[tabName]}</div>
|
||||
</Form>
|
||||
</EventTypeSingleLayout>
|
||||
{props.children}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -47,6 +47,7 @@ type Props = {
|
||||
isDeleting?: boolean;
|
||||
isPlatform?: boolean;
|
||||
tabsNavigation: VerticalTabItemProps[];
|
||||
allowDelete?: boolean;
|
||||
};
|
||||
|
||||
function EventTypeSingleLayout({
|
||||
@@ -63,6 +64,7 @@ function EventTypeSingleLayout({
|
||||
isDeleting,
|
||||
isPlatform,
|
||||
tabsNavigation,
|
||||
allowDelete = true,
|
||||
}: Props) {
|
||||
const { t } = useLocale();
|
||||
const eventTypesLockedByOrg = eventType.team?.parent?.organizationSettings?.lockEventTypeCreationForUsers;
|
||||
@@ -187,7 +189,7 @@ function EventTypeSingleLayout({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!isChildrenManagedEventType && (
|
||||
{!isChildrenManagedEventType && allowDelete && (
|
||||
<Button
|
||||
color="destructive"
|
||||
variant="icon"
|
||||
@@ -201,7 +203,7 @@ function EventTypeSingleLayout({
|
||||
)}
|
||||
</ButtonGroup>
|
||||
|
||||
<VerticalDivider className="hidden lg:block" />
|
||||
{(!isPlatform || (isPlatform && allowDelete)) && <VerticalDivider className="hidden lg:block" />}
|
||||
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
@@ -229,16 +231,18 @@ function EventTypeSingleLayout({
|
||||
{t("copy_link")}
|
||||
</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="focus:ring-muted">
|
||||
<DropdownItem
|
||||
type="button"
|
||||
color="destructive"
|
||||
StartIcon="trash"
|
||||
disabled={!hasPermsToDelete}
|
||||
onClick={() => setDeleteDialogOpen(true)}>
|
||||
{t("delete")}
|
||||
</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
{allowDelete && (
|
||||
<DropdownMenuItem className="focus:ring-muted">
|
||||
<DropdownItem
|
||||
type="button"
|
||||
color="destructive"
|
||||
StartIcon="trash"
|
||||
disabled={!hasPermsToDelete}
|
||||
onClick={() => setDeleteDialogOpen(true)}>
|
||||
{t("delete")}
|
||||
</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<div className="hover:bg-subtle flex h-9 cursor-pointer flex-row items-center justify-between px-4 py-2 transition">
|
||||
<Skeleton
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { EventTypeSetupProps, FormValues, TabMap } from "@calcom/features/e
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
|
||||
import { useDeleteEventTypeById } from "../../hooks/event-types/private/useDeleteEventTypeById";
|
||||
import { useMe } from "../../hooks/useMe";
|
||||
import { AtomsWrapper } from "../../src/components/atoms-wrapper";
|
||||
import { useToast } from "../../src/components/ui/use-toast";
|
||||
@@ -30,10 +31,19 @@ export type EventTypePlatformWrapperProps = {
|
||||
tabs?: PlatformTabs[];
|
||||
onSuccess?: (eventType: FormValues) => void;
|
||||
onError?: (eventType: FormValues, error: Error) => void;
|
||||
onDeleteSuccess?: () => void;
|
||||
onDeleteError?: (msg: string) => void;
|
||||
allowDelete: boolean;
|
||||
};
|
||||
|
||||
const EventType = ({
|
||||
tabs = ["setup", "availability", "team", "limits", "advanced", "recurring"],
|
||||
onSuccess,
|
||||
onError,
|
||||
onDeleteSuccess,
|
||||
onDeleteError,
|
||||
id,
|
||||
allowDelete = true,
|
||||
...props
|
||||
}: EventTypeSetupProps & EventTypePlatformWrapperProps) => {
|
||||
const { t } = useLocale();
|
||||
@@ -46,6 +56,20 @@ const EventType = ({
|
||||
const [slugExistsChildrenDialogOpen, setSlugExistsChildrenDialogOpen] = useState<ChildrenEventType[]>([]);
|
||||
const { data: user, isLoading: isUserLoading } = useMe();
|
||||
|
||||
const deleteMutation = useDeleteEventTypeById({
|
||||
onSuccess: async () => {
|
||||
showToast(t("event_type_deleted_successfully"), "success");
|
||||
isTeamEventTypeDeleted.current = true;
|
||||
setSlugExistsChildrenDialogOpen([]);
|
||||
setIsOpenAssignmentWarnDialog(false);
|
||||
onDeleteSuccess?.();
|
||||
},
|
||||
onError: (err) => {
|
||||
showToast(err.message, "error");
|
||||
onDeleteError?.(err.message);
|
||||
},
|
||||
});
|
||||
|
||||
const updateMutation = useAtomUpdateEventType({
|
||||
onSuccess: async () => {
|
||||
const currentValues = form.getValues();
|
||||
@@ -58,7 +82,6 @@ const EventType = ({
|
||||
|
||||
// Reset the form with these values as new default values to ensure the correct comparison for dirtyFields eval
|
||||
form.reset(currentValues);
|
||||
|
||||
toast({ description: t("event_type_updated_successfully", { eventTypeTitle: eventType.title }) });
|
||||
onSuccess?.(currentValues);
|
||||
},
|
||||
@@ -141,7 +164,10 @@ const EventType = ({
|
||||
});
|
||||
|
||||
const onDelete = () => {
|
||||
isTeamEventTypeDeleted.current = true;
|
||||
if (allowDelete) {
|
||||
isTeamEventTypeDeleted.current = true;
|
||||
deleteMutation.mutate(id);
|
||||
}
|
||||
};
|
||||
const onConflict = (conflicts: ChildrenEventType[]) => {
|
||||
setSlugExistsChildrenDialogOpen(conflicts);
|
||||
@@ -165,7 +191,8 @@ const EventType = ({
|
||||
isUpdating={updateMutation.isPending}
|
||||
isPlatform
|
||||
tabName={currentTab}
|
||||
tabsNavigation={tabsNavigation}>
|
||||
tabsNavigation={tabsNavigation}
|
||||
allowDelete={allowDelete}>
|
||||
<>
|
||||
{slugExistsChildrenDialogOpen.length ? (
|
||||
<ManagedEventTypeDialog
|
||||
@@ -188,7 +215,15 @@ const EventType = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const EventTypePlatformWrapper = ({ id, tabs, onSuccess, onError }: EventTypePlatformWrapperProps) => {
|
||||
export const EventTypePlatformWrapper = ({
|
||||
id,
|
||||
tabs,
|
||||
onSuccess,
|
||||
onError,
|
||||
onDeleteSuccess,
|
||||
onDeleteError,
|
||||
allowDelete = true,
|
||||
}: EventTypePlatformWrapperProps) => {
|
||||
const { data: eventTypeQueryData } = useAtomsEventTypeById(id);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
@@ -208,5 +243,16 @@ export const EventTypePlatformWrapper = ({ id, tabs, onSuccess, onError }: Event
|
||||
|
||||
if (!eventTypeQueryData) return null;
|
||||
|
||||
return <EventType {...eventTypeQueryData} id={id} tabs={tabs} onSuccess={onSuccess} onError={onError} />;
|
||||
return (
|
||||
<EventType
|
||||
{...eventTypeQueryData}
|
||||
id={id}
|
||||
tabs={tabs}
|
||||
onSuccess={onSuccess}
|
||||
onError={onError}
|
||||
onDeleteSuccess={onDeleteSuccess}
|
||||
onDeleteError={onDeleteError}
|
||||
allowDelete={allowDelete}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
import { V2_ENDPOINTS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import type { ApiResponse, ApiSuccessResponse } from "@calcom/platform-types";
|
||||
import type { EventType } from "@calcom/prisma/client";
|
||||
|
||||
import http from "../../../lib/http";
|
||||
|
||||
export const QUERY_KEY = "use-delete-event-by-id";
|
||||
export type UseDeleteEventTypeProps = {
|
||||
onSuccess?: () => void;
|
||||
onError?: (err: Error) => void;
|
||||
onSettled?: () => void;
|
||||
};
|
||||
export const useDeleteEventTypeById = ({ onSuccess, onError, onSettled }: UseDeleteEventTypeProps) => {
|
||||
return useMutation({
|
||||
onSuccess,
|
||||
onError,
|
||||
onSettled,
|
||||
mutationFn: (id: number) => {
|
||||
if (!id) throw new Error("Event type id is required");
|
||||
const pathname = `/${V2_ENDPOINTS.eventTypes}/${id}`;
|
||||
return http?.delete<ApiResponse<EventType>>(pathname).then((res) => {
|
||||
if (res.data.status === SUCCESS_STATUS) {
|
||||
return (res.data as ApiSuccessResponse<EventType>).data;
|
||||
}
|
||||
throw new Error(res.data.error.message);
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -14,7 +14,11 @@ export default function Bookings(props: { calUsername: string; calEmail: string
|
||||
const router = useRouter();
|
||||
const { isLoading: isLoadingEvents, data: eventTypes, refetch } = useEventTypes(props.calUsername);
|
||||
const { data: teams } = useTeams();
|
||||
const { isLoading: isLoadingTeamEvents, data: teamEventTypes } = useTeamEventTypes(teams?.[0]?.id || 0);
|
||||
const {
|
||||
isLoading: isLoadingTeamEvents,
|
||||
data: teamEventTypes,
|
||||
refetch: refetchTeamEvents,
|
||||
} = useTeamEventTypes(teams?.[0]?.id || 0);
|
||||
const rescheduleUid = (router.query.rescheduleUid as string) ?? "";
|
||||
|
||||
return (
|
||||
@@ -85,6 +89,7 @@ export default function Bookings(props: { calUsername: string; calEmail: string
|
||||
{eventTypeId && (
|
||||
<div>
|
||||
<EventTypeSettings
|
||||
allowDelete={true}
|
||||
id={eventTypeId}
|
||||
tabs={["setup", "limits", "recurring", "advanced"]}
|
||||
onSuccess={(eventType) => {
|
||||
@@ -96,6 +101,12 @@ export default function Bookings(props: { calUsername: string; calEmail: string
|
||||
console.log(eventType);
|
||||
console.error(error);
|
||||
}}
|
||||
onDeleteSuccess={() => {
|
||||
refetch();
|
||||
refetchTeamEvents();
|
||||
setEventTypeId(null);
|
||||
}}
|
||||
onDeleteError={console.error}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user