import { useEffect, useState } from "react"; import { createPortal } from "react-dom"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { useAddVariableMutation, useUpdateVariableMutation, useVariableQuery, } from "../../../hooks/useVariable"; import { useNotyf } from "../../../hooks/useNotyf"; const UPPER_SNAKE_CASE_REGEX = /^[A-Z][A-Z0-9_]*$/; const schema = z.object({ name: z .string() .min(1, "Name is required") .regex( UPPER_SNAKE_CASE_REGEX, "Name must be in UPPER_SNAKE_CASE (e.g. MY_VARIABLE)", ), value: z.string().min(1, "Value is required"), }); type FormValues = z.infer; export type AddEnvironmentVariableModalProps = { isOpen: boolean; onClose: () => void; sandboxId: string; variableId?: string; }; function AddEnvironmentVariableModal({ isOpen, onClose, sandboxId, variableId, }: AddEnvironmentVariableModalProps) { const [isLoading, setIsLoading] = useState(false); const notyf = useNotyf(); const { mutateAsync: addVariable } = useAddVariableMutation(); const { mutateAsync: updateVariable } = useUpdateVariableMutation(); const { data } = useVariableQuery(variableId!); const { register, handleSubmit, reset, setValue, formState: { errors }, } = useForm({ resolver: zodResolver(schema), }); const handleNameChange = (e: React.ChangeEvent) => { const transformed = e.target.value .toUpperCase() .replace(/\s+/g, "_") .replace(/[^A-Z0-9_]/g, ""); setValue("name", transformed, { shouldValidate: true }); }; useEffect(() => { if (data) { setValue("name", data.variable.name); setValue("value", data.variable.value); } }, [data, setValue]); useEffect(() => { const handleEscapeKey = (event: KeyboardEvent) => { if (event.key === "Escape" && isOpen) { onClose(); } }; document.addEventListener("keydown", handleEscapeKey); return () => { document.removeEventListener("keydown", handleEscapeKey); }; }, [isOpen, onClose]); const handleBackdropClick = (e: React.MouseEvent) => { e.stopPropagation(); if (e.target === e.currentTarget) { reset(); onClose(); } }; const handleContentClick = (e: React.MouseEvent) => { e.stopPropagation(); }; const handleCloseButton = (e: React.MouseEvent) => { reset(); e.stopPropagation(); onClose(); }; const onSubmit = async (data: FormValues) => { setIsLoading(true); try { if (variableId) { await updateVariable({ id: variableId, name: data.name, value: data.value, sandboxId, }); setIsLoading(false); reset(); onClose(); notyf.open("primary", "Variable updated successfully!"); return; } await addVariable({ sandboxId, name: data.name, value: data.value, }); setIsLoading(false); reset(); onClose(); notyf.open("primary", "Variable added successfully!"); } catch { notyf.open("error", "Failed to add variable!"); setIsLoading(false); reset(); onClose(); return; } }; if (!isOpen) return null; return createPortal( <>
{variableId ? "Edit Variable" : "Add Variable"}
{errors.name && ( {errors.name.message} )}
{errors.value && ( {errors.value.message} )}
e.stopPropagation()} >
, document.body, ); } export default AddEnvironmentVariableModal;