From 95e3f55f26e12723c46e5d05abd5f8d73d3c2108 Mon Sep 17 00:00:00 2001 From: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Date: Tue, 18 Oct 2022 14:47:15 +0200 Subject: [PATCH] Add checkbox that defines if SMS reminder phone number should be required (#5047) * add phone number required checkbox * make input required only if numberRequired is true * fix error that smsReminderNumber is null * code clean up * fix typo * save no input as null * fix code change Co-authored-by: CarinaWolli Co-authored-by: alannnc --- .../components/booking/pages/BookingPage.tsx | 15 ++++++++--- apps/web/public/static/locales/en/common.json | 4 +-- .../components/v2/AddActionDialog.tsx | 25 +++++++++++++++++-- .../components/v2/WorkflowDetailsPage.tsx | 3 ++- .../components/v2/WorkflowStepContainer.tsx | 20 ++++++++++++--- .../lib/reminders/smsReminderManager.ts | 2 +- .../ee/workflows/pages/v2/workflow.tsx | 2 +- .../migration.sql | 2 ++ packages/prisma/schema.prisma | 1 + .../trpc/server/routers/viewer/workflows.tsx | 2 ++ 10 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 packages/prisma/migrations/20221017115710_add_number_required_to_workflow_step/migration.sql diff --git a/apps/web/components/booking/pages/BookingPage.tsx b/apps/web/components/booking/pages/BookingPage.tsx index 1e24c77e6c..8ce8882d01 100644 --- a/apps/web/components/booking/pages/BookingPage.tsx +++ b/apps/web/components/booking/pages/BookingPage.tsx @@ -266,7 +266,8 @@ const BookingPage = ({ smsReminderNumber: z .string() .refine((val) => isValidPhoneNumber(val)) - .optional(), + .optional() + .nullable(), }) .passthrough(); @@ -358,7 +359,9 @@ const BookingPage = ({ hasHashedBookingLink, hashedLink, smsReminderNumber: - selectedLocationType === LocationType.Phone ? booking.phone : booking.smsReminderNumber, + selectedLocationType === LocationType.Phone + ? booking.phone + : booking.smsReminderNumber || undefined, ethSignature: gateState.rainbowToken, })); recurringMutation.mutate(recurringBookings); @@ -386,7 +389,9 @@ const BookingPage = ({ hasHashedBookingLink, hashedLink, smsReminderNumber: - selectedLocationType === LocationType.Phone ? booking.phone : booking.smsReminderNumber, + selectedLocationType === LocationType.Phone + ? booking.phone + : booking.smsReminderNumber || undefined, ethSignature: gateState.rainbowToken, }); } @@ -400,6 +405,7 @@ const BookingPage = ({ "dark:placeholder:text-darkgray-600 focus:border-brand dark:border-darkgray-300 dark:text-darkgray-900 block w-full rounded-md border-gray-300 text-sm focus:ring-black disabled:bg-gray-200 disabled:hover:cursor-not-allowed dark:bg-transparent dark:selection:bg-green-500 disabled:dark:text-gray-500"; let isSmsReminderNumberNeeded = false; + let isSmsReminderNumberRequired = false; if (eventType.workflows.length > 0) { eventType.workflows.forEach((workflowReference) => { @@ -407,6 +413,7 @@ const BookingPage = ({ workflowReference.workflow.steps.forEach((step) => { if (step.action === WorkflowActions.SMS_ATTENDEE) { isSmsReminderNumberNeeded = true; + isSmsReminderNumberRequired = step.numberRequired || false; return; } }); @@ -786,7 +793,7 @@ const BookingPage = ({ name="smsReminderNumber" placeholder={t("enter_phone_number")} id="smsReminderNumber" - required + required={isSmsReminderNumberRequired} /> {bookingForm.formState.errors.smsReminderNumber && ( diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index ae63a772b0..2556d8be0d 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -1191,7 +1191,6 @@ "additional_notes_info": "The Additional notes of booking", "attendee_name_info": "The person booking's name", "to": "To", - "attendee_required_enter_number": "This will require the attendee to enter a phone number when booking", "workflow_turned_on_successfully": "{{workflowName}} workflow turned {{offOn}} successfully", "download_responses": "Download Responses", "create_your_first_form": "Create your first form", @@ -1314,5 +1313,6 @@ "how_long_after": "How long after event ends?", "no_available_slots": "No Available slots", "time_available": "Time available", - "install_new_calendar_app": "Install new calendar app" + "install_new_calendar_app": "Install new calendar app", + "make_phone_number_required": "Make phone number required for booking event" } diff --git a/packages/features/ee/workflows/components/v2/AddActionDialog.tsx b/packages/features/ee/workflows/components/v2/AddActionDialog.tsx index 0a0b01be66..1ebb9dc2c6 100644 --- a/packages/features/ee/workflows/components/v2/AddActionDialog.tsx +++ b/packages/features/ee/workflows/components/v2/AddActionDialog.tsx @@ -18,6 +18,7 @@ import { Label, Select, } from "@calcom/ui/v2"; +import CheckboxField from "@calcom/ui/v2/core/form/Checkbox"; import { WORKFLOW_ACTIONS } from "../../lib/constants"; import { getWorkflowActionOptions } from "../../lib/getOptions"; @@ -25,12 +26,13 @@ import { getWorkflowActionOptions } from "../../lib/getOptions"; interface IAddActionDialog { isOpenDialog: boolean; setIsOpenDialog: Dispatch>; - addAction: (action: WorkflowActions, sendTo?: string) => void; + addAction: (action: WorkflowActions, sendTo?: string, numberRequired?: boolean) => void; } type AddActionFormValues = { action: WorkflowActions; sendTo?: string; + numberRequired?: boolean; }; export const AddActionDialog = (props: IAddActionDialog) => { @@ -46,6 +48,7 @@ export const AddActionDialog = (props: IAddActionDialog) => { .string() .refine((val) => isValidPhoneNumber(val) || val.includes("@")) .optional(), + numberRequired: z.boolean().optional(), }); const form = useForm({ @@ -64,9 +67,10 @@ export const AddActionDialog = (props: IAddActionDialog) => {
{ - addAction(values.action, values.sendTo); + addAction(values.action, values.sendTo, values.numberRequired); form.unregister("sendTo"); form.unregister("action"); + form.unregister("numberRequired"); setIsOpenDialog(false); setIsPhoneNumberNeeded(false); setIsEmailAddressNeeded(false); @@ -96,6 +100,7 @@ export const AddActionDialog = (props: IAddActionDialog) => { setIsPhoneNumberNeeded(false); } form.unregister("sendTo"); + form.unregister("numberRequired"); form.clearErrors("action"); form.clearErrors("sendTo"); } @@ -109,6 +114,21 @@ export const AddActionDialog = (props: IAddActionDialog) => {

{form.formState.errors.action.message}

)} + {form.getValues("action") === WorkflowActions.SMS_ATTENDEE && ( +
+ ( + form.setValue("numberRequired", e.target.checked)} + /> + )} + /> +
+ )} {isPhoneNumberNeeded && (
@@ -140,6 +160,7 @@ export const AddActionDialog = (props: IAddActionDialog) => { setIsOpenDialog(false); form.unregister("sendTo"); form.unregister("action"); + form.unregister("numberRequired"); setIsPhoneNumberNeeded(false); setIsEmailAddressNeeded(false); }}> diff --git a/packages/features/ee/workflows/components/v2/WorkflowDetailsPage.tsx b/packages/features/ee/workflows/components/v2/WorkflowDetailsPage.tsx index d743e16566..18aad85ee0 100644 --- a/packages/features/ee/workflows/components/v2/WorkflowDetailsPage.tsx +++ b/packages/features/ee/workflows/components/v2/WorkflowDetailsPage.tsx @@ -49,7 +49,7 @@ export default function WorkflowDetailsPage(props: Props) { [data] ); - const addAction = (action: WorkflowActions, sendTo?: string) => { + const addAction = (action: WorkflowActions, sendTo?: string, numberRequired?: boolean) => { const steps = form.getValues("steps"); const id = steps?.length > 0 @@ -72,6 +72,7 @@ export default function WorkflowDetailsPage(props: Props) { reminderBody: null, emailSubject: null, template: WorkflowTemplates.CUSTOM, + numberRequired: numberRequired || false, }; steps?.push(step); form.setValue("steps", steps); diff --git a/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx b/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx index 116399649e..6bd18dd8b5 100644 --- a/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx +++ b/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx @@ -19,6 +19,7 @@ import { Icon } from "@calcom/ui/Icon"; import PhoneInput from "@calcom/ui/form/PhoneInputLazy"; import { Button, DialogClose, DialogContent } from "@calcom/ui/v2"; import ConfirmationDialogContent from "@calcom/ui/v2/core/ConfirmationDialogContent"; +import CheckboxField from "@calcom/ui/v2/core/form/Checkbox"; import { EmailField, Label, TextArea } from "@calcom/ui/v2/core/form/fields"; import Select from "@calcom/ui/v2/core/form/select"; @@ -305,9 +306,22 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { }} /> {form.getValues(`steps.${step.stepNumber - 1}.action`) === WorkflowActions.SMS_ATTENDEE && ( -
- -

{t("attendee_required_enter_number")}

+
+ ( + + form.setValue(`steps.${step.stepNumber - 1}.numberRequired`, e.target.checked) + } + /> + )} + />
)}
diff --git a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts index eb315fe16b..12b6d14a93 100644 --- a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts @@ -89,7 +89,7 @@ export const scheduleSMSReminder = async ( } if (message.length > 0 && reminderPhone) { - //send SMS when event is booked/cancelled/Reschdeuled + //send SMS when event is booked/cancelled/rescheduled if ( triggerEvent === WorkflowTriggerEvents.NEW_EVENT || triggerEvent === WorkflowTriggerEvents.EVENT_CANCELLED || diff --git a/packages/features/ee/workflows/pages/v2/workflow.tsx b/packages/features/ee/workflows/pages/v2/workflow.tsx index b9e4ebd91a..838e20eecb 100644 --- a/packages/features/ee/workflows/pages/v2/workflow.tsx +++ b/packages/features/ee/workflows/pages/v2/workflow.tsx @@ -18,7 +18,6 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import { HttpError } from "@calcom/lib/http-error"; import { stringOrNumber } from "@calcom/prisma/zod-utils"; import { trpc } from "@calcom/trpc/react"; -import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery"; import { Option } from "@calcom/ui/form/MultiSelectCheckboxes"; import { Alert, Button, Form, showToast } from "@calcom/ui/v2"; import Shell from "@calcom/ui/v2/core/Shell"; @@ -53,6 +52,7 @@ const formSchema = z.object({ reminderBody: z.string().nullable(), emailSubject: z.string().nullable(), template: z.nativeEnum(WorkflowTemplates), + numberRequired: z.boolean().nullable(), sendTo: z .string() .refine((val) => isValidPhoneNumber(val) || val.includes("@")) diff --git a/packages/prisma/migrations/20221017115710_add_number_required_to_workflow_step/migration.sql b/packages/prisma/migrations/20221017115710_add_number_required_to_workflow_step/migration.sql new file mode 100644 index 0000000000..9e98476246 --- /dev/null +++ b/packages/prisma/migrations/20221017115710_add_number_required_to_workflow_step/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "WorkflowStep" ADD COLUMN "numberRequired" BOOLEAN; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 67f2b3585e..00b53dc320 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -565,6 +565,7 @@ model WorkflowStep { emailSubject String? template WorkflowTemplates @default(REMINDER) workflowReminders WorkflowReminder[] + numberRequired Boolean? } model Workflow { diff --git a/packages/trpc/server/routers/viewer/workflows.tsx b/packages/trpc/server/routers/viewer/workflows.tsx index 0acd8e9498..04d0881f82 100644 --- a/packages/trpc/server/routers/viewer/workflows.tsx +++ b/packages/trpc/server/routers/viewer/workflows.tsx @@ -229,6 +229,7 @@ export const workflowsRouter = createProtectedRouter() reminderBody: z.string().optional().nullable(), emailSubject: z.string().optional().nullable(), template: z.enum(WORKFLOW_TEMPLATES), + numberRequired: z.boolean().nullable(), }) .array(), trigger: z.enum(WORKFLOW_TRIGGER_EVENTS), @@ -528,6 +529,7 @@ export const workflowsRouter = createProtectedRouter() reminderBody: newStep.template === WorkflowTemplates.CUSTOM ? newStep.reminderBody : null, emailSubject: newStep.template === WorkflowTemplates.CUSTOM ? newStep.emailSubject : null, template: newStep.template, + numberRequired: newStep.numberRequired, }, }); //cancel all reminders of step and create new ones (not for newEventTypes)