From 8508a6f1a083f8c01881760fcdba0f938ea87633 Mon Sep 17 00:00:00 2001 From: Vijay Date: Mon, 29 Jul 2024 09:32:59 +0530 Subject: [PATCH] fix: #15697 Assignment warning modal poping up ,even when hosts are added . (#15699) * removed reduntand check on assignedusers * update for managed eventtype * formatted * comments * Updated comments * extracted checkForEmptyAssignment and added tests * updated comments * updated to show dialog also when not yet saved * update translations * updated comments * updated translation * updated types * chore: update naming and comments * Delete apps/web/lib/checkForEmptyAssignment.ts deleted since file was renamed * Delete apps/web/test/lib/checkForEmptyAssignment.test.ts deleted as the file is renamed to be more descriptive * update to do teamcheck before * chore: revert name to CheckForEmptyAssignment * updated check conditions * updated useEffect dependencies --------- Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> --- apps/web/lib/checkForEmptyAssignment.ts | 29 +++++ .../views/event-types-single-view.tsx | 81 +++++++------- .../test/lib/CheckForEmptyAssignment.test.ts | 102 ++++++++++++++++++ 3 files changed, 171 insertions(+), 41 deletions(-) create mode 100644 apps/web/lib/checkForEmptyAssignment.ts create mode 100644 apps/web/test/lib/CheckForEmptyAssignment.test.ts diff --git a/apps/web/lib/checkForEmptyAssignment.ts b/apps/web/lib/checkForEmptyAssignment.ts new file mode 100644 index 0000000000..a2e5d5101c --- /dev/null +++ b/apps/web/lib/checkForEmptyAssignment.ts @@ -0,0 +1,29 @@ +import type { EventTypeAssignedUsers, EventTypeHosts } from "~/event-types/views/event-types-single-view"; + +// This function checks if EventType requires assignment. +// returns true: if EventType requires assignment but there is no assignment yet done by the user. +// returns false: for all other scenarios. +export function checkForEmptyAssignment({ + assignedUsers, + hosts, + isManagedEventType, + assignAllTeamMembers, +}: { + assignedUsers: EventTypeAssignedUsers; + hosts: EventTypeHosts; + isManagedEventType: boolean; + assignAllTeamMembers: boolean; +}): boolean { + // If Team-events have assignAllTeamMembers checked, return false as assignemnt is complete. + if (assignAllTeamMembers) { + return false; + } + + // For managed eventtype check if assigned users are empty. + // For non-managed eventtype check if hosts are empty. + if (isManagedEventType ? assignedUsers.length === 0 : hosts.length === 0) { + return true; + } + + return false; +} diff --git a/apps/web/modules/event-types/views/event-types-single-view.tsx b/apps/web/modules/event-types/views/event-types-single-view.tsx index 927227ab73..a6138db64b 100644 --- a/apps/web/modules/event-types/views/event-types-single-view.tsx +++ b/apps/web/modules/event-types/views/event-types-single-view.tsx @@ -26,6 +26,7 @@ import { HttpError } from "@calcom/lib/http-error"; import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { validateBookerLayouts } from "@calcom/lib/validateBookerLayouts"; import type { Prisma } from "@calcom/prisma/client"; +import { SchedulingType } from "@calcom/prisma/enums"; import type { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import { eventTypeBookingFields } from "@calcom/prisma/zod-utils"; import type { RouterOutputs } from "@calcom/trpc/react"; @@ -33,6 +34,7 @@ import { trpc } from "@calcom/trpc/react"; import { Form, showToast } from "@calcom/ui"; import type { AppProps } from "@lib/app-providers"; +import { checkForEmptyAssignment } from "@lib/checkForEmptyAssignment"; import { EventTypeSingleLayout } from "@components/eventtype/EventTypeSingleLayout"; @@ -144,6 +146,8 @@ const querySchema = z.object({ export type EventTypeSetupProps = RouterOutputs["viewer"]["eventTypes"]["get"]; export type EventTypeSetup = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"]; +export type EventTypeAssignedUsers = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"]["children"]; +export type EventTypeHosts = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"]["hosts"]; export const locationsResolver = (t: TFunction) => { return z @@ -400,40 +404,35 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf formState: { isDirty: isFormDirty, dirtyFields }, } = formMethods; - // useEffect(() => { - // const handleRouteChange = (url: string) => { - // const paths = url.split("/"); - // - // // Check if event is managed event type - skip if there is assigned users - // const assignedUsers = eventType.children; - // const isManagedEventType = eventType.schedulingType === SchedulingType.MANAGED; - // if (eventType.assignAllTeamMembers) { - // return; - // } else if (isManagedEventType && assignedUsers.length > 0) { - // return; - // } - // - // const hosts = eventType.hosts; - // if ( - // !leaveWithoutAssigningHosts.current && - // !!team && - // (hosts.length === 0 || assignedUsers.length === 0) && - // (url === "/event-types" || paths[1] !== "event-types") - // ) { - // setIsOpenAssignmentWarnDialog(true); - // setPendingRoute(url); - // router.events.emit( - // "routeChangeError", - // new Error(`Aborted route change to ${url} because none was assigned to team event`) - // ); - // throw "Aborted"; - // } - // }; - // router.events.on("routeChangeStart", handleRouteChange); - // return () => { - // router.events.off("routeChangeStart", handleRouteChange); - // }; - // }, [router]); + useEffect(() => { + const handleRouteChange = (url: string) => { + const paths = url.split("/"); + + if ( + !!team && + !leaveWithoutAssigningHosts.current && + (url === "/event-types" || paths[1] !== "event-types") && + checkForEmptyAssignment({ + assignedUsers: eventType.children, + hosts: eventType.hosts, + assignAllTeamMembers: eventType.assignAllTeamMembers, + isManagedEventType: eventType.schedulingType === SchedulingType.MANAGED, + }) + ) { + setIsOpenAssignmentWarnDialog(true); + setPendingRoute(url); + router.events.emit( + "routeChangeError", + new Error(`Aborted route change to ${url} because none was assigned to team event`) + ); + throw "Aborted"; + } + }; + router.events.on("routeChangeStart", handleRouteChange); + return () => { + router.events.off("routeChangeStart", handleRouteChange); + }; + }, [router, eventType.hosts, eventType.children, eventType.assignAllTeamMembers]); const appsMetadata = formMethods.getValues("metadata")?.apps; const availability = formMethods.watch("availability"); @@ -837,13 +836,13 @@ const EventTypePage = (props: EventTypeSetupProps & { allActiveWorkflows?: Workf }} /> ) : null} - {/**/} + ); }; diff --git a/apps/web/test/lib/CheckForEmptyAssignment.test.ts b/apps/web/test/lib/CheckForEmptyAssignment.test.ts new file mode 100644 index 0000000000..e8508e7089 --- /dev/null +++ b/apps/web/test/lib/CheckForEmptyAssignment.test.ts @@ -0,0 +1,102 @@ +import { describe, expect, it } from "vitest"; + +import { checkForEmptyAssignment } from "../../lib/checkForEmptyAssignment"; + +describe("Tests to Check if Event Types have empty Assignment", () => { + it("should return true if managed event type has no assigned users", () => { + expect( + checkForEmptyAssignment({ + assignedUsers: [], + assignAllTeamMembers: false, + hosts: [{ userId: 101, isFixed: false, priority: 2 }], + isManagedEventType: true, + }) + ).toBe(true); + }); + it("should return true if non-managed event type has no hosts assigned", () => { + expect( + checkForEmptyAssignment({ + assignedUsers: [ + { + created: true, + owner: { + id: 101, + avatar: "avatar.svg", + email: "firstuser@cal.com", + membership: "OWNER", + name: "First user", + username: "firstuser", + profile: { + username: "firstuser", + upId: "usr-101", + id: null, + organization: null, + organizationId: null, + }, + avatarUrl: null, + nonProfileUsername: null, + }, + slug: "managedevent", + hidden: false, + }, + ], + assignAllTeamMembers: false, + hosts: [], + isManagedEventType: false, + }) + ).toBe(true); + }); + it("should return false if assignAllTeamMembers is selected", () => { + expect( + checkForEmptyAssignment({ + assignedUsers: [], + assignAllTeamMembers: true, + hosts: [], + isManagedEventType: false, + }) + ).toBe(false); + }); + it("should return false if non-managed event type has hosts assigned", () => { + expect( + checkForEmptyAssignment({ + assignedUsers: [], + assignAllTeamMembers: false, + hosts: [{ userId: 101, isFixed: false, priority: 2 }], + isManagedEventType: false, + }) + ).toBe(false); + }); + it("should return false if managed event type has assigned users", () => { + expect( + checkForEmptyAssignment({ + assignedUsers: [ + { + created: true, + owner: { + id: 101, + avatar: "avatar.svg", + email: "firstuser@cal.com", + membership: "OWNER", + name: "First user", + username: "firstuser", + profile: { + username: "firstuser", + upId: "usr-101", + id: null, + organization: null, + organizationId: null, + }, + avatarUrl: null, + nonProfileUsername: null, + }, + slug: "managedevent", + hidden: false, + }, + ], + assignAllTeamMembers: false, + hosts: [], + isManagedEventType: true, + }) + ).toBe(false); + }); +});