From 3b50fe075d7d1e2a151d390c651d01b289c48b95 Mon Sep 17 00:00:00 2001 From: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Date: Thu, 21 Sep 2023 08:22:05 +0200 Subject: [PATCH] fix: remove kyc and make text messages to attendee org only (#11389) * add orgs upgrade badge to select * don't allow updating to if no teams or orgs plan * make sure only paying users can request verification codes * add new action helper function * remove kyc code * code clean up * fix verify button UI * fix type errors * add eventTypeRequiresConfirmation everywhere * address feedback --------- Co-authored-by: CarinaWolli --- .../pages/api/trpc/kycVerification/[trpc].ts | 4 - .../settings/admin/kycVerification/index.tsx | 11 --- .../kycVerification/kycVerificationView.tsx | 80 ------------------- apps/web/public/static/locales/en/common.json | 6 +- .../bookings/lib/handleCancelBooking.ts | 16 +--- .../bookings/lib/handleConfirmation.ts | 46 ----------- .../features/bookings/lib/handleNewBooking.ts | 21 +---- .../workflows/components/AddActionDialog.tsx | 10 +-- .../components/KYCVerificationDialog.tsx | 56 ------------- .../components/WorkflowDetailsPage.tsx | 13 --- .../components/WorkflowStepContainer.tsx | 16 ++-- .../ee/workflows/lib/actionHelperFunctions.ts | 3 + .../features/ee/workflows/lib/getOptions.ts | 7 +- .../lib/isEventTypeOwnerKYCVerified.ts | 45 ----------- .../lib/reminders/reminderScheduler.ts | 12 +-- .../settings/layouts/SettingsLayout.tsx | 1 - packages/trpc/react/trpc.ts | 1 - .../trpc/server/routers/viewer/_router.tsx | 2 - .../viewer/kycVerification/_router.tsx | 42 ---------- .../kycVerification/isVerified.handler.ts | 44 ---------- .../kycVerification/isVerified.schema.ts | 1 - .../viewer/kycVerification/verify.handler.ts | 76 ------------------ .../viewer/kycVerification/verify.schema.ts | 8 -- .../getWorkflowActionOptions.handler.ts | 5 +- .../workflows/sendVerificationCode.handler.ts | 21 ++++- .../viewer/workflows/update.handler.ts | 31 ++++--- .../components/badge/KYCVerificationBadge.tsx | 20 ----- .../ui/components/badge/UpgradeOrgsBadge.tsx | 16 ++++ packages/ui/components/badge/index.ts | 2 +- .../ui/components/form/select/components.tsx | 15 ++-- 30 files changed, 89 insertions(+), 542 deletions(-) delete mode 100644 apps/web/pages/api/trpc/kycVerification/[trpc].ts delete mode 100644 apps/web/pages/settings/admin/kycVerification/index.tsx delete mode 100644 apps/web/pages/settings/admin/kycVerification/kycVerificationView.tsx delete mode 100644 packages/features/ee/workflows/components/KYCVerificationDialog.tsx delete mode 100644 packages/features/ee/workflows/lib/isEventTypeOwnerKYCVerified.ts delete mode 100644 packages/trpc/server/routers/viewer/kycVerification/_router.tsx delete mode 100644 packages/trpc/server/routers/viewer/kycVerification/isVerified.handler.ts delete mode 100644 packages/trpc/server/routers/viewer/kycVerification/isVerified.schema.ts delete mode 100644 packages/trpc/server/routers/viewer/kycVerification/verify.handler.ts delete mode 100644 packages/trpc/server/routers/viewer/kycVerification/verify.schema.ts delete mode 100644 packages/ui/components/badge/KYCVerificationBadge.tsx create mode 100644 packages/ui/components/badge/UpgradeOrgsBadge.tsx diff --git a/apps/web/pages/api/trpc/kycVerification/[trpc].ts b/apps/web/pages/api/trpc/kycVerification/[trpc].ts deleted file mode 100644 index a77ef56160..0000000000 --- a/apps/web/pages/api/trpc/kycVerification/[trpc].ts +++ /dev/null @@ -1,4 +0,0 @@ -import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; -import { kycVerificationRouter } from "@calcom/trpc/server/routers/viewer/kycVerification/_router"; - -export default createNextApiHandler(kycVerificationRouter); diff --git a/apps/web/pages/settings/admin/kycVerification/index.tsx b/apps/web/pages/settings/admin/kycVerification/index.tsx deleted file mode 100644 index 0d4a2b7bb1..0000000000 --- a/apps/web/pages/settings/admin/kycVerification/index.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import PageWrapper from "@components/PageWrapper"; -import { getLayout } from "@components/auth/layouts/AdminLayout"; - -import KYCVerificationView from "./kycVerificationView"; - -const KYCVerificationPage = () => ; - -KYCVerificationPage.getLayout = getLayout; -KYCVerificationPage.PageWrapper = PageWrapper; - -export default KYCVerificationPage; diff --git a/apps/web/pages/settings/admin/kycVerification/kycVerificationView.tsx b/apps/web/pages/settings/admin/kycVerification/kycVerificationView.tsx deleted file mode 100644 index 3a55ce2538..0000000000 --- a/apps/web/pages/settings/admin/kycVerification/kycVerificationView.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useForm } from "react-hook-form"; - -import { trpc } from "@calcom/trpc"; -import { Meta, Form, Button, TextField, showToast } from "@calcom/ui"; - -type FormValues = { - name?: string | null; -}; - -export default function KYCVerificationView() { - const teamForm = useForm(); - const userForm = useForm(); - - const mutation = trpc.viewer.kycVerification.verify.useMutation({ - onSuccess: async (data) => { - showToast(`Successfully verified ${data.isTeam ? "team" : "user"} ${data.name}`, "success"); - }, - onError: (error) => { - showToast(`Verification failed: ${error.message}`, "error"); - }, - }); - - return ( -
- -
-
Verify Team
- -
{ - mutation.mutate({ - name: values.name || "", - isTeam: true, - }); - }}> -
- - -
-
-
-
-
Verify User
-
{ - mutation.mutate({ - name: values.name || "", - isTeam: false, - }); - }}> -
- - -
-
-
-
- ); -} diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index a26edb4056..a265afb628 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -1639,6 +1639,7 @@ "minimum_round_robin_hosts_count": "Number of hosts required to attend", "hosts": "Hosts", "upgrade_to_enable_feature": "You need to create a team to enable this feature. Click to create a team.", + "orgs_upgrade_to_enable_feature" : "You need to upgrade to our enterprise plan to enable this feature.", "new_attendee": "New Attendee", "awaiting_approval": "Awaiting Approval", "requires_google_calendar": "This app requires a Google Calendar connection", @@ -2003,11 +2004,6 @@ "requires_booker_email_verification": "Requires booker email verification", "description_requires_booker_email_verification": "To ensure booker's email verification before scheduling events", "requires_confirmation_mandatory": "Text messages can only be sent to attendees when event type requires confirmation.", - "kyc_verification_information": "To ensure security, you have to verify your {{teamOrAccount}} before sending text messages to attendees. Please contact us at {{supportEmail}} and provide the following information:", - "kyc_verification_documents": "
  • Your {{teamOrUser}}
  • For businesses: Attach your Business Verification Documentation
  • For individuals: Attach a government-issued ID
", - "verify_team_or_account": "Verify {{teamOrAccount}}", - "verify_account": "Verify Account", - "kyc_verification": "KYC Verification", "organizations": "Organizations", "org_admin_other_teams": "Other teams", "org_admin_other_teams_description": "Here you can see teams inside your organization that you are not part of. You can add yourself to them if needed.", diff --git a/packages/features/bookings/lib/handleCancelBooking.ts b/packages/features/bookings/lib/handleCancelBooking.ts index 9ae7959a8d..37758d3f34 100644 --- a/packages/features/bookings/lib/handleCancelBooking.ts +++ b/packages/features/bookings/lib/handleCancelBooking.ts @@ -9,7 +9,6 @@ import { deleteMeeting, updateMeeting } from "@calcom/core/videoClient"; import dayjs from "@calcom/dayjs"; import { sendCancelledEmails, sendCancelledSeatEmails } from "@calcom/emails"; import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses"; -import { isEventTypeOwnerKYCVerified } from "@calcom/features/ee/workflows/lib/isEventTypeOwnerKYCVerified"; import { deleteScheduledEmailReminder } from "@calcom/features/ee/workflows/lib/reminders/emailReminderManager"; import { sendCancelledReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler"; import { deleteScheduledSMSReminder } from "@calcom/features/ee/workflows/lib/reminders/smsReminderManager"; @@ -71,17 +70,6 @@ async function getBookingToDelete(id: number | undefined, uid: string | undefine select: { id: true, hideBranding: true, - metadata: true, - teams: { - select: { - accepted: true, - team: { - select: { - metadata: true, - }, - }, - }, - }, }, }, teamId: true, @@ -299,8 +287,6 @@ async function handler(req: CustomRequest) { ); await Promise.all(promises); - const isKYCVerified = isEventTypeOwnerKYCVerified(bookingToDelete.eventType); - //Workflows - schedule reminders if (bookingToDelete.eventType?.workflows) { await sendCancelledReminders({ @@ -311,7 +297,7 @@ async function handler(req: CustomRequest) { ...{ eventType: { slug: bookingToDelete.eventType.slug } }, }, hideBranding: !!bookingToDelete.eventType.owner?.hideBranding, - isKYCVerified, + eventTypeRequiresConfirmation: bookingToDelete.eventType.requiresConfirmation, }); } diff --git a/packages/features/bookings/lib/handleConfirmation.ts b/packages/features/bookings/lib/handleConfirmation.ts index 8ffb3cba78..ec049110fd 100644 --- a/packages/features/bookings/lib/handleConfirmation.ts +++ b/packages/features/bookings/lib/handleConfirmation.ts @@ -3,7 +3,6 @@ import type { Prisma, Workflow, WorkflowsOnEventTypes, WorkflowStep } from "@pri import type { EventManagerUser } from "@calcom/core/EventManager"; import EventManager from "@calcom/core/EventManager"; import { sendScheduledEmails } from "@calcom/emails"; -import { isEventTypeOwnerKYCVerified } from "@calcom/features/ee/workflows/lib/isEventTypeOwnerKYCVerified"; import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler"; import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks"; import { scheduleTrigger } from "@calcom/features/webhooks/lib/scheduleTrigger"; @@ -87,18 +86,8 @@ export async function handleConfirmation(args: { eventType: { bookingFields: Prisma.JsonValue | null; slug: string; - team: { - metadata: Prisma.JsonValue; - } | null; owner: { hideBranding?: boolean | null; - metadata: Prisma.JsonValue; - teams: { - accepted: boolean; - team: { - metadata: Prisma.JsonValue; - }; - }[]; } | null; workflows: (WorkflowsOnEventTypes & { workflow: Workflow & { @@ -135,25 +124,9 @@ export async function handleConfirmation(args: { select: { slug: true, bookingFields: true, - team: { - select: { - metadata: true, - }, - }, owner: { select: { hideBranding: true, - metadata: true, - teams: { - select: { - accepted: true, - team: { - select: { - metadata: true, - }, - }, - }, - }, }, }, workflows: { @@ -202,25 +175,9 @@ export async function handleConfirmation(args: { select: { slug: true, bookingFields: true, - team: { - select: { - metadata: true, - }, - }, owner: { select: { hideBranding: true, - metadata: true, - teams: { - select: { - accepted: true, - team: { - select: { - metadata: true, - }, - }, - }, - }, }, }, workflows: { @@ -250,8 +207,6 @@ export async function handleConfirmation(args: { updatedBookings.push(updatedBooking); } - const isKYCVerified = isEventTypeOwnerKYCVerified(updatedBookings[0].eventType); - //Workflows - set reminders for confirmed events try { for (let index = 0; index < updatedBookings.length; index++) { @@ -276,7 +231,6 @@ export async function handleConfirmation(args: { isFirstRecurringEvent: isFirstBooking, hideBranding: !!updatedBookings[index].eventType?.owner?.hideBranding, eventTypeRequiresConfirmation: true, - isKYCVerified, }); } } diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index 732409696c..f4890a2c1a 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -39,7 +39,6 @@ import { allowDisablingAttendeeConfirmationEmails, allowDisablingHostConfirmationEmails, } from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails"; -import { isEventTypeOwnerKYCVerified } from "@calcom/features/ee/workflows/lib/isEventTypeOwnerKYCVerified"; import { cancelWorkflowReminders, scheduleWorkflowReminders, @@ -260,7 +259,6 @@ const getEventTypesFromDB = async (eventTypeId: number) => { select: { id: true, name: true, - metadata: true, }, }, bookingFields: true, @@ -292,17 +290,6 @@ const getEventTypesFromDB = async (eventTypeId: number) => { owner: { select: { hideBranding: true, - metadata: true, - teams: { - select: { - accepted: true, - team: { - select: { - metadata: true, - }, - }, - }, - }, }, }, workflows: { @@ -379,7 +366,7 @@ async function ensureAvailableUsers( } ) { const availableUsers: IsFixedAwareUser[] = []; - const duration = dayjs(input.dateTo).diff(input.dateFrom, 'minute'); + const duration = dayjs(input.dateTo).diff(input.dateFrom, "minute"); const originalBookingDuration = input.originalRescheduledBooking ? dayjs(input.originalRescheduledBooking.endTime).diff( @@ -1199,8 +1186,6 @@ async function handler( const subscribersMeetingEnded = await getWebhooks(subscriberOptionsMeetingEnded); - const isKYCVerified = isEventTypeOwnerKYCVerified(eventType); - const handleSeats = async () => { let resultBooking: | (Partial & { @@ -1769,7 +1754,7 @@ async function handler( isFirstRecurringEvent: true, emailAttendeeSendToOverride: bookerEmail, seatReferenceUid: evt.attendeeSeatId, - isKYCVerified, + eventTypeRequiresConfirmation: eventType.requiresConfirmation, }); } catch (error) { log.error("Error while scheduling workflow reminders", error); @@ -2416,7 +2401,7 @@ async function handler( isFirstRecurringEvent: true, hideBranding: !!eventType.owner?.hideBranding, seatReferenceUid: evt.attendeeSeatId, - isKYCVerified, + eventTypeRequiresConfirmation: eventType.requiresConfirmation, }); } catch (error) { log.error("Error while scheduling workflow reminders", error); diff --git a/packages/features/ee/workflows/components/AddActionDialog.tsx b/packages/features/ee/workflows/components/AddActionDialog.tsx index 5c56de3b46..3c327d02ce 100644 --- a/packages/features/ee/workflows/components/AddActionDialog.tsx +++ b/packages/features/ee/workflows/components/AddActionDialog.tsx @@ -39,7 +39,6 @@ interface IAddActionDialog { senderId?: string, senderName?: string ) => void; - setKYCVerificationDialogOpen: () => void; } interface ISelectActionOption { @@ -57,7 +56,7 @@ type AddActionFormValues = { export const AddActionDialog = (props: IAddActionDialog) => { const { t } = useLocale(); - const { isOpenDialog, setIsOpenDialog, addAction, setKYCVerificationDialogOpen } = props; + const { isOpenDialog, setIsOpenDialog, addAction } = props; const [isPhoneNumberNeeded, setIsPhoneNumberNeeded] = useState(false); const [isSenderIdNeeded, setIsSenderIdNeeded] = useState(false); const [isEmailAddressNeeded, setIsEmailAddressNeeded] = useState(false); @@ -171,14 +170,13 @@ export const AddActionDialog = (props: IAddActionDialog) => { onChange={handleSelectAction} options={actionOptions.map((option) => ({ ...option, - verificationAction: () => setKYCVerificationDialogOpen(), }))} isOptionDisabled={(option: { label: string; value: WorkflowActions; - needsUpgrade: boolean; - needsVerification: boolean; - }) => option.needsUpgrade || option.needsVerification} + needsTeamsUpgrade: boolean; + needsOrgsUpgrade: boolean; + }) => option.needsTeamsUpgrade || option.needsOrgsUpgrade} /> ); }} diff --git a/packages/features/ee/workflows/components/KYCVerificationDialog.tsx b/packages/features/ee/workflows/components/KYCVerificationDialog.tsx deleted file mode 100644 index 71d0225416..0000000000 --- a/packages/features/ee/workflows/components/KYCVerificationDialog.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Trans } from "next-i18next"; -import type { Dispatch, SetStateAction } from "react"; - -import { SUPPORT_MAIL_ADDRESS } from "@calcom/lib/constants"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { Dialog, DialogContent, DialogClose, DialogFooter } from "@calcom/ui"; - -export const KYCVerificationDialog = (props: { - isOpenDialog: boolean; - setIsOpenDialog: Dispatch>; - isPartOfTeam: boolean; -}) => { - const { isOpenDialog, setIsOpenDialog, isPartOfTeam } = props; - const { t } = useLocale(); - - const isTeamString = isPartOfTeam ? "team" : ""; - - return ( - - -
-
- - ), - }} - values={{ - supportEmail: - SUPPORT_MAIL_ADDRESS === "help@cal.com" ? "support@cal.com" : SUPPORT_MAIL_ADDRESS, - teamOrAccount: isTeamString || "account", - }} - /> -
- , ul:
    }} - values={{ teamOrUser: isPartOfTeam ? "team URL" : "user name" }} - /> -
- - - -
-
- ); -}; diff --git a/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx b/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx index 6e2d487e17..b57d254247 100644 --- a/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx +++ b/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx @@ -5,7 +5,6 @@ import type { UseFormReturn } from "react-hook-form"; import { Controller } from "react-hook-form"; import { SENDER_ID, SENDER_NAME } from "@calcom/lib/constants"; -import { useHasTeamPlan } from "@calcom/lib/hooks/useHasPaidPlan"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { WorkflowTemplates } from "@calcom/prisma/enums"; import type { WorkflowActions } from "@calcom/prisma/enums"; @@ -19,7 +18,6 @@ import { isSMSAction, isWhatsappAction } from "../lib/actionHelperFunctions"; import type { FormValues } from "../pages/workflow"; import { AddActionDialog } from "./AddActionDialog"; import { DeleteDialog } from "./DeleteDialog"; -import { KYCVerificationDialog } from "./KYCVerificationDialog"; import WorkflowStepContainer from "./WorkflowStepContainer"; type User = RouterOutputs["viewer"]["me"]; @@ -41,15 +39,12 @@ export default function WorkflowDetailsPage(props: Props) { const router = useRouter(); const [isAddActionDialogOpen, setIsAddActionDialogOpen] = useState(false); - const [isKYCVerificationDialogOpen, setKYCVerificationDialogOpen] = useState(false); const [reload, setReload] = useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const { data, isLoading } = trpc.viewer.eventTypes.getByViewer.useQuery(); - const isPartOfTeam = useHasTeamPlan(); - const eventTypeOptions = useMemo( () => data?.eventTypeGroups.reduce((options, group) => { @@ -177,7 +172,6 @@ export default function WorkflowDetailsPage(props: Props) { user={props.user} teamId={teamId} readOnly={props.readOnly} - setKYCVerificationDialogOpen={setKYCVerificationDialogOpen} /> )} @@ -194,7 +188,6 @@ export default function WorkflowDetailsPage(props: Props) { setReload={setReload} teamId={teamId} readOnly={props.readOnly} - setKYCVerificationDialogOpen={setKYCVerificationDialogOpen} /> ); })} @@ -222,12 +215,6 @@ export default function WorkflowDetailsPage(props: Props) { isOpenDialog={isAddActionDialogOpen} setIsOpenDialog={setIsAddActionDialogOpen} addAction={addAction} - setKYCVerificationDialogOpen={() => setKYCVerificationDialogOpen(true)} - /> - >; teamId?: number; readOnly: boolean; - setKYCVerificationDialogOpen: Dispatch>; }; export default function WorkflowStepContainer(props: WorkflowStepProps) { @@ -331,15 +330,13 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { } if (step && step.action) { - const templateValue = form.watch(`steps.${step.stepNumber - 1}.template`); const actionString = t(`${step.action.toLowerCase()}_action`); const selectedAction = { label: actionString.charAt(0).toUpperCase() + actionString.slice(1), value: step.action, - needsUpgrade: false, - needsVerification: false, - verificationAction: () => props.setKYCVerificationDialogOpen(true), + needsTeamsUpgrade: false, + needsOrgsUpgrade: false, }; const selectedTemplate = { label: t(`${step.template.toLowerCase()}`), value: step.template }; @@ -530,14 +527,13 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { defaultValue={selectedAction} options={actionOptions?.map((option) => ({ ...option, - verificationAction: () => props.setKYCVerificationDialogOpen(true), }))} isOptionDisabled={(option: { label: string; value: WorkflowActions; - needsUpgrade: boolean; - needsVerification: boolean; - }) => option.needsUpgrade || option.needsVerification} + needsTeamsUpgrade: boolean; + needsOrgsUpgrade: boolean; + }) => option.needsTeamsUpgrade || option.needsOrgsUpgrade} /> ); }} @@ -617,7 +613,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { />