Implementing additional feedback from Workflows PR (#3456)
* implement feedback * put formSchema outside of react component * don't show new workflow button for free Users * code clean up Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Omar López <zomars@me.com>
This commit is contained in:
parent
3ff43520eb
commit
13d13c71c6
|
@ -1,6 +1,6 @@
|
|||
import { WorkflowActions, WorkflowTemplates } from "@prisma/client";
|
||||
import { useRouter } from "next/router";
|
||||
import { useState, useEffect, Dispatch, SetStateAction } from "react";
|
||||
import { useState, Dispatch, SetStateAction, useMemo } from "react";
|
||||
import { Controller, UseFormReturn } from "react-hook-form";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
@ -10,14 +10,14 @@ import { Button } from "@calcom/ui";
|
|||
import { Form } from "@calcom/ui/form/fields";
|
||||
import { AddActionDialog } from "@ee/components/workflows/AddActionDialog";
|
||||
import WorkflowStepContainer from "@ee/components/workflows/WorkflowStepContainer";
|
||||
import { Option, FormValues } from "@ee/pages/workflows/[workflow]";
|
||||
import { FormValues } from "@ee/pages/workflows/[workflow]";
|
||||
|
||||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import MultiSelectCheckboxes from "@components/ui/form/MultiSelectCheckboxes";
|
||||
import MultiSelectCheckboxes, { Option } from "@components/ui/form/MultiSelectCheckboxes";
|
||||
|
||||
interface Props {
|
||||
form: UseFormReturn<FormValues, any>;
|
||||
form: UseFormReturn<FormValues>;
|
||||
workflowId: number;
|
||||
selectedEventTypes: Option[];
|
||||
setSelectedEventTypes: Dispatch<SetStateAction<Option[]>>;
|
||||
|
@ -29,30 +29,31 @@ export default function WorkflowDetailsPage(props: Props) {
|
|||
const router = useRouter();
|
||||
const utils = trpc.useContext();
|
||||
|
||||
const [evenTypeOptions, setEventTypeOptions] = useState<Option[]>([]);
|
||||
const [isAddActionDialogOpen, setIsAddActionDialogOpen] = useState(false);
|
||||
const [reload, setReload] = useState(false);
|
||||
const [editCounter, setEditCounter] = useState(0);
|
||||
|
||||
const { data, isLoading } = trpc.useQuery(["viewer.eventTypes"]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
let options: Option[] = [];
|
||||
data.eventTypeGroups.forEach((group) => {
|
||||
const eventTypeOptions = group.eventTypes.map((eventType) => {
|
||||
return { value: String(eventType.id), label: eventType.title };
|
||||
});
|
||||
options = [...options, ...eventTypeOptions];
|
||||
});
|
||||
setEventTypeOptions(options);
|
||||
}
|
||||
}, [isLoading]);
|
||||
const eventTypeOptions = useMemo(
|
||||
() =>
|
||||
data?.eventTypeGroups.reduce(
|
||||
(options, group) => [
|
||||
...options,
|
||||
...group.eventTypes.map((eventType) => ({
|
||||
value: String(eventType.id),
|
||||
label: eventType.title,
|
||||
})),
|
||||
],
|
||||
[] as Option[]
|
||||
) || [],
|
||||
[data]
|
||||
);
|
||||
|
||||
const updateMutation = trpc.useMutation("viewer.workflows.update", {
|
||||
onSuccess: async ({ workflow }) => {
|
||||
if (workflow) {
|
||||
await utils.setQueryData(["viewer.workflows.get", { id: +workflow.id }], workflow);
|
||||
utils.setQueryData(["viewer.workflows.get", { id: +workflow.id }], workflow);
|
||||
|
||||
showToast(
|
||||
t("workflow_updated_successfully", {
|
||||
|
@ -74,7 +75,7 @@ export default function WorkflowDetailsPage(props: Props) {
|
|||
const addAction = (action: WorkflowActions, sendTo?: string) => {
|
||||
const steps = form.getValues("steps");
|
||||
const id =
|
||||
steps && steps.length > 0
|
||||
steps?.length > 0
|
||||
? steps.sort((a, b) => {
|
||||
return a.id - b.id;
|
||||
})[0].id - 1
|
||||
|
@ -130,7 +131,7 @@ export default function WorkflowDetailsPage(props: Props) {
|
|||
render={() => {
|
||||
return (
|
||||
<MultiSelectCheckboxes
|
||||
options={evenTypeOptions}
|
||||
options={eventTypeOptions}
|
||||
isLoading={isLoading}
|
||||
setSelected={setSelectedEventTypes}
|
||||
selected={selectedEventTypes}
|
||||
|
|
|
@ -11,6 +11,8 @@ import { Dispatch, SetStateAction, useState } from "react";
|
|||
import { Controller, UseFormReturn } from "react-hook-form";
|
||||
import PhoneInput from "react-phone-number-input";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Button } from "@calcom/ui";
|
||||
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
|
||||
import Select from "@calcom/ui/form/Select";
|
||||
|
@ -23,12 +25,9 @@ import {
|
|||
} from "@ee/lib/workflows/getOptions";
|
||||
import { FormValues } from "@ee/pages/workflows/[workflow]";
|
||||
|
||||
import classNames from "@lib/classNames";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
|
||||
type WorkflowStepProps = {
|
||||
step?: WorkflowStep;
|
||||
form: UseFormReturn<FormValues, any>;
|
||||
form: UseFormReturn<FormValues>;
|
||||
reload?: boolean;
|
||||
setReload?: Dispatch<SetStateAction<boolean>>;
|
||||
editCounter: number;
|
||||
|
|
|
@ -46,7 +46,10 @@ export const scheduleEmailReminder = async (
|
|||
const scheduledDate =
|
||||
timeBefore.time && timeUnit ? dayjs(startTime).subtract(timeBefore.time, timeUnit) : null;
|
||||
|
||||
if (!process.env.SENDGRID_API_KEY || !process.env.SENDGRID_EMAIL) return;
|
||||
if (!process.env.SENDGRID_API_KEY || !process.env.SENDGRID_EMAIL) {
|
||||
console.error("Sendgrid credentials are missing from the .env file");
|
||||
return;
|
||||
}
|
||||
|
||||
const batchIdResponse = await client.request({
|
||||
url: "/v3/mail/batch",
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
WorkflowTemplates,
|
||||
WorkflowActions,
|
||||
WorkflowMethods,
|
||||
} from "@prisma/client/";
|
||||
} from "@prisma/client";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
|
|
@ -24,11 +24,7 @@ import useMeQuery from "@lib/hooks/useMeQuery";
|
|||
import { trpc } from "@lib/trpc";
|
||||
|
||||
import Shell from "@components/Shell";
|
||||
|
||||
export type Option = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
import { Option } from "@components/ui/form/MultiSelectCheckboxes";
|
||||
|
||||
export type FormValues = {
|
||||
name: string;
|
||||
|
@ -39,6 +35,30 @@ export type FormValues = {
|
|||
timeUnit?: TimeUnit;
|
||||
};
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string(),
|
||||
activeOn: z.object({ value: z.string(), label: z.string() }).array(),
|
||||
trigger: z.enum(WORKFLOW_TRIGGER_EVENTS),
|
||||
time: z.number().gte(0).optional(),
|
||||
timeUnit: z.enum(TIME_UNIT).optional(),
|
||||
steps: z
|
||||
.object({
|
||||
id: z.number(),
|
||||
stepNumber: z.number(),
|
||||
action: z.enum(WORKFLOW_ACTIONS),
|
||||
workflowId: z.number(),
|
||||
reminderBody: z.string().optional().nullable(),
|
||||
emailSubject: z.string().optional().nullable(),
|
||||
template: z.enum(WORKFLOW_TEMPLATES),
|
||||
sendTo: z
|
||||
.string()
|
||||
.refine((val) => isValidPhoneNumber(val))
|
||||
.optional()
|
||||
.nullable(),
|
||||
})
|
||||
.array(),
|
||||
});
|
||||
|
||||
function WorkflowPage() {
|
||||
const { t } = useLocale();
|
||||
const session = useSession();
|
||||
|
@ -50,30 +70,6 @@ function WorkflowPage() {
|
|||
const [selectedEventTypes, setSelectedEventTypes] = useState<Option[]>([]);
|
||||
const [isAllDataLoaded, setIsAllDataLoaded] = useState(false);
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string(),
|
||||
activeOn: z.object({ value: z.string(), label: z.string() }).array(),
|
||||
trigger: z.enum(WORKFLOW_TRIGGER_EVENTS),
|
||||
time: z.number().gte(0).optional(),
|
||||
timeUnit: z.enum(TIME_UNIT).optional(),
|
||||
steps: z
|
||||
.object({
|
||||
id: z.number(),
|
||||
stepNumber: z.number(),
|
||||
action: z.enum(WORKFLOW_ACTIONS),
|
||||
workflowId: z.number(),
|
||||
reminderBody: z.string().optional().nullable(),
|
||||
emailSubject: z.string().optional().nullable(),
|
||||
template: z.enum(WORKFLOW_TEMPLATES),
|
||||
sendTo: z
|
||||
.string()
|
||||
.refine((val) => isValidPhoneNumber(val))
|
||||
.optional()
|
||||
.nullable(),
|
||||
})
|
||||
.array(),
|
||||
});
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
});
|
||||
|
@ -113,15 +109,12 @@ function WorkflowPage() {
|
|||
}
|
||||
}, [dataUpdatedAt]);
|
||||
|
||||
if (isLoading) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Shell
|
||||
title="Title"
|
||||
heading={
|
||||
session.data?.hasValidLicense && (
|
||||
session.data?.hasValidLicense &&
|
||||
isAllDataLoaded && (
|
||||
<div className="group relative cursor-pointer" onClick={() => setEditIcon(false)}>
|
||||
{editIcon ? (
|
||||
<>
|
||||
|
|
|
@ -26,7 +26,7 @@ function WorkflowsPage() {
|
|||
<Shell
|
||||
heading={t("workflows")}
|
||||
subtitle={t("workflows_to_automate_notifications")}
|
||||
CTA={session.data?.hasValidLicense ? <NewWorkflowButton /> : <></>}>
|
||||
CTA={session.data?.hasValidLicense && !isFreeUser ? <NewWorkflowButton /> : <></>}>
|
||||
<LicenseRequired>
|
||||
{isLoading ? (
|
||||
<Loader />
|
||||
|
|
|
@ -1,17 +1,5 @@
|
|||
import dayjs from "@calcom/dayjs";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import toArray from "dayjs/plugin/toArray";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
|
||||
import BaseEmail from "./_base-email";
|
||||
import { BookingInfo } from "@calcom/web/ee/lib/workflows/reminders/smsReminderManager";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(localizedFormat);
|
||||
dayjs.extend(toArray);
|
||||
|
||||
export default class WorkflowReminderEmail extends BaseEmail {
|
||||
sendTo: string;
|
||||
body: string;
|
||||
|
|
Loading…
Reference in New Issue
Block a user