182 lines
5.8 KiB
TypeScript
182 lines
5.8 KiB
TypeScript
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import {
|
|
TimeUnit,
|
|
WorkflowActions,
|
|
WorkflowStep,
|
|
WorkflowTemplates,
|
|
WorkflowTriggerEvents,
|
|
} from "@prisma/client";
|
|
import { isValidPhoneNumber } from "libphonenumber-js";
|
|
import { useSession } from "next-auth/react";
|
|
import { useRouter } from "next/router";
|
|
import { useEffect, useState } from "react";
|
|
import { useForm } from "react-hook-form";
|
|
import { z } from "zod";
|
|
|
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|
import { stringOrNumber } from "@calcom/prisma/zod-utils";
|
|
import { trpc } from "@calcom/trpc/react";
|
|
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
|
|
import { Alert } from "@calcom/ui/Alert";
|
|
import { Icon } from "@calcom/ui/Icon";
|
|
import Loader from "@calcom/ui/Loader";
|
|
import Shell from "@calcom/ui/Shell";
|
|
import { Option } from "@calcom/ui/form/MultiSelectCheckboxes";
|
|
|
|
import LicenseRequired from "../../common/components/LicenseRequired";
|
|
import WorkflowDetailsPage from "../components/WorkflowDetailsPage";
|
|
|
|
export type FormValues = {
|
|
name: string;
|
|
activeOn: Option[];
|
|
steps: WorkflowStep[];
|
|
trigger: WorkflowTriggerEvents;
|
|
time?: number;
|
|
timeUnit?: TimeUnit;
|
|
};
|
|
|
|
const formSchema = z.object({
|
|
name: z.string(),
|
|
activeOn: z.object({ value: z.string(), label: z.string() }).array(),
|
|
trigger: z.nativeEnum(WorkflowTriggerEvents),
|
|
time: z.number().gte(0).optional(),
|
|
timeUnit: z.nativeEnum(TimeUnit).optional(),
|
|
steps: z
|
|
.object({
|
|
id: z.number(),
|
|
stepNumber: z.number(),
|
|
action: z.nativeEnum(WorkflowActions),
|
|
workflowId: z.number(),
|
|
reminderBody: z.string().nullable(),
|
|
emailSubject: z.string().nullable(),
|
|
template: z.nativeEnum(WorkflowTemplates),
|
|
sendTo: z
|
|
.string()
|
|
.refine((val) => isValidPhoneNumber(val))
|
|
.nullable(),
|
|
})
|
|
.array(),
|
|
});
|
|
|
|
const querySchema = z.object({
|
|
workflow: stringOrNumber,
|
|
});
|
|
|
|
function WorkflowPage() {
|
|
const { t } = useLocale();
|
|
const session = useSession();
|
|
const router = useRouter();
|
|
const me = useMeQuery();
|
|
const isFreeUser = me.data?.plan === "FREE";
|
|
|
|
const [editIcon, setEditIcon] = useState(true);
|
|
const [selectedEventTypes, setSelectedEventTypes] = useState<Option[]>([]);
|
|
const [isAllDataLoaded, setIsAllDataLoaded] = useState(false);
|
|
|
|
const form = useForm<FormValues>({
|
|
resolver: zodResolver(formSchema),
|
|
});
|
|
const { workflow: workflowId } = router.isReady ? querySchema.parse(router.query) : { workflow: -1 };
|
|
|
|
const {
|
|
data: workflow,
|
|
isError,
|
|
error,
|
|
dataUpdatedAt,
|
|
} = trpc.useQuery(["viewer.workflows.get", { id: +workflowId }], {
|
|
enabled: router.isReady && !!workflowId,
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (workflow && !form.getValues("name")) {
|
|
setSelectedEventTypes(
|
|
workflow.activeOn.map((active) => ({
|
|
value: String(active.eventType.id),
|
|
label: active.eventType.title,
|
|
})) || []
|
|
);
|
|
const activeOn = workflow.activeOn
|
|
? workflow.activeOn.map((active) => ({
|
|
value: active.eventType.id.toString(),
|
|
label: active.eventType.slug,
|
|
}))
|
|
: undefined;
|
|
form.setValue("name", workflow.name);
|
|
form.setValue("steps", workflow.steps);
|
|
form.setValue("trigger", workflow.trigger);
|
|
form.setValue("time", workflow.time || undefined);
|
|
form.setValue("timeUnit", workflow.timeUnit || undefined);
|
|
form.setValue("activeOn", activeOn || []);
|
|
setIsAllDataLoaded(true);
|
|
}
|
|
}, [dataUpdatedAt]);
|
|
|
|
return (
|
|
<Shell
|
|
title="Title"
|
|
heading={
|
|
session.data?.hasValidLicense &&
|
|
isAllDataLoaded && (
|
|
<div className="group relative cursor-pointer" onClick={() => setEditIcon(false)}>
|
|
{editIcon ? (
|
|
<>
|
|
<h1
|
|
style={{ fontSize: 22, letterSpacing: "-0.0009em" }}
|
|
className="inline pl-0 text-gray-900 focus:text-black group-hover:text-gray-500">
|
|
{form.getValues("name") && form.getValues("name") !== ""
|
|
? form.getValues("name")
|
|
: workflow?.name}
|
|
</h1>
|
|
<Icon.FiEdit2 className="ml-1 -mt-1 inline h-4 w-4 text-gray-700 group-hover:text-gray-500" />
|
|
</>
|
|
) : (
|
|
<div style={{ marginBottom: -11 }}>
|
|
<input
|
|
type="text"
|
|
autoFocus
|
|
style={{ top: -6, fontSize: 22 }}
|
|
required
|
|
className="relative h-10 w-full cursor-pointer border-none bg-transparent pl-0 text-gray-900 hover:text-gray-700 focus:text-black focus:outline-none focus:ring-0"
|
|
placeholder={t("Custom workflow")}
|
|
{...form.register("name")}
|
|
defaultValue={workflow?.name}
|
|
onBlur={() => {
|
|
setEditIcon(true);
|
|
form.getValues("name") === "" && form.setValue("name", workflow?.name || "");
|
|
}}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}>
|
|
<LicenseRequired>
|
|
{isFreeUser ? (
|
|
<Alert className="border " severity="warning" title={t("pro_feature_workflows")} />
|
|
) : (
|
|
<>
|
|
{!isError ? (
|
|
<>
|
|
{isAllDataLoaded ? (
|
|
<WorkflowDetailsPage
|
|
form={form}
|
|
workflowId={+workflowId}
|
|
selectedEventTypes={selectedEventTypes}
|
|
setSelectedEventTypes={setSelectedEventTypes}
|
|
/>
|
|
) : (
|
|
<Loader />
|
|
)}
|
|
</>
|
|
) : (
|
|
<Alert severity="error" title="Something went wrong" message={error.message} />
|
|
)}
|
|
</>
|
|
)}
|
|
</LicenseRequired>
|
|
</Shell>
|
|
);
|
|
}
|
|
|
|
export default WorkflowPage;
|