import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { PlusIcon, SquarePen } from "lucide-react"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; const AddProjectSchema = z.object({ name: z .string() .min(1, "Project name is required") .refine( (name) => { const trimmedName = name.trim(); const validNameRegex = /^[\p{L}\p{N}_-][\p{L}\p{N}\s_-]*[\p{L}\p{N}_-]$/u; return validNameRegex.test(trimmedName); }, { message: "Project name must start and end with a letter, number, hyphen or underscore. Spaces are allowed in between.", }, ) .refine((name) => !/^\d/.test(name.trim()), { message: "Project name cannot start with a number", }) .transform((name) => name.trim()), description: z.string().optional(), }); type AddProject = z.infer; interface Props { projectId?: string; } export const HandleProject = ({ projectId }: Props) => { const utils = api.useUtils(); const [isOpen, setIsOpen] = useState(false); const { mutateAsync, error, isError } = projectId ? api.project.update.useMutation() : api.project.create.useMutation(); const { data, refetch } = api.project.one.useQuery( { projectId: projectId || "", }, { enabled: !!projectId, }, ); const router = useRouter(); const form = useForm({ defaultValues: { description: "", name: "", }, resolver: zodResolver(AddProjectSchema), }); useEffect(() => { form.reset({ description: data?.description ?? "", name: data?.name ?? "", }); }, [form, form.reset, form.formState.isSubmitSuccessful, data]); const onSubmit = async (data: AddProject) => { await mutateAsync({ name: data.name, description: data.description, projectId: projectId || "", }) .then(async (data) => { await utils.project.all.invalidate(); toast.success(projectId ? "Project Updated" : "Project Created"); setIsOpen(false); if (!projectId) { router.push(`/dashboard/project/${data?.projectId}`); } else { refetch(); } }) .catch(() => { toast.error( projectId ? "Error updating a project" : "Error creating a project", ); }); }; return ( {projectId ? ( e.preventDefault()} > Update ) : ( )} {projectId ? "Update" : "Add a"} project The home of something big! {isError && {error?.message}}
( Name )} />
( Description