"use client"; import EmojiPicker, { Theme } from "emoji-picker-react"; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "ui/dialog"; import { Input } from "ui/input"; import { Textarea } from "ui/textarea"; import { Label } from "ui/label"; import { Button } from "ui/button"; import { useTheme } from "next-themes"; import { useObjectState } from "@/hooks/use-object-state"; import { useState } from "react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, } from "ui/dropdown-menu"; import { Avatar, AvatarFallback, AvatarImage } from "ui/avatar"; import { Loader } from "lucide-react"; import { safe } from "ts-safe"; import { z } from "zod"; import { DBWorkflow, WorkflowIcon } from "app-types/workflow"; import { handleErrorWithToast } from "ui/shared-toast"; import { toast } from "sonner"; import { useRouter } from "next/navigation"; import { cn, createDebounce } from "lib/utils"; import { mutate } from "swr"; import { useTranslations } from "next-intl"; import { BACKGROUND_COLORS } from "lib/const"; const colorUpdateDebounce = createDebounce(); const defaultConfig = { id: undefined as string | undefined, icon: { type: "emoji", value: "https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f916.png", style: { backgroundColor: BACKGROUND_COLORS[0], }, } as WorkflowIcon, name: "", description: "", }; const zodSchema = z.object({ id: z.string().optional(), name: z .string() .min(1) .regex(/^[a-zA-Z -]+$/), description: z.string().max(200).optional(), icon: z.object({ type: z.enum(["emoji"]), value: z.string().min(1), style: z .object({ backgroundColor: z.string().min(1), }) .optional(), }), }); export function EditWorkflowPopup({ children, defaultValue, submitAfterRoute = true, onSave, open, onOpenChange, }: { children?: React.ReactNode; defaultValue?: Pick; submitAfterRoute?: boolean; open?: boolean; onSave?: (workflow: DBWorkflow) => void; onOpenChange?: (open: boolean) => void; }) { const t = useTranslations(); const { theme } = useTheme(); const getInitialConfig = () => { return defaultValue ? { description: defaultValue.description || "", icon: defaultValue.icon || defaultConfig.icon, name: defaultValue.name || "", id: defaultValue.id || "", } : { ...defaultConfig }; }; const [config, setConfig] = useObjectState( getInitialConfig(), ); const router = useRouter(); const [loading, setLoading] = useState(false); const handleSubmit = async () => { setLoading(true); toast.promise( safe(() => zodSchema.parse(config)) .map(async (body) => { const response = await fetch("/api/workflow", { method: "POST", body: JSON.stringify(body), }); const data = await response.json(); return data as DBWorkflow; }) .ifOk((workflow) => { onOpenChange?.(false); mutate("/api/workflow"); if (submitAfterRoute) { router.push(`/workflow/${workflow.id}`); } onSave?.(workflow); }) .ifFail(handleErrorWithToast) .watch(() => setLoading(false)) .unwrap(), { success: t("Common.success"), loading: t("Common.saving"), }, ); }; return ( { !open && setConfig(getInitialConfig()); onOpenChange?.(open); }} > {children} {t("Workflow.createWorkflow")}

{t("Workflow.createWorkflowDescription")}

{t("Workflow.workflowDescription")}

{/* Left: Form */}
setConfig({ name: e.target.value })} autoFocus className="bg-input border-transparent" id="workflow-name" placeholder={t("Workflow.workflowNamePlaceholder")} />
{BACKGROUND_COLORS.map((color, index) => (
{ setConfig({ icon: { ...config.icon, style: { backgroundColor: color }, }, }); }} style={{ backgroundColor: color }} >
))}
{ colorUpdateDebounce(() => { setConfig({ icon: { ...config.icon!, style: { backgroundColor: e.target.value }, }, }); }, 100); }} />
{ setConfig({ icon: { ...config.icon, value: emoji.imageUrl, }, }); }} />