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:
Morgan
2024-10-15 14:40:57 +00:00
committed by GitHub
parent f0cfbede7e
commit 9c3f4ec055
5 changed files with 150 additions and 43 deletions
@@ -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>
)}