diff --git a/apps/dokploy/components/dashboard/organization/handle-organization.tsx b/apps/dokploy/components/dashboard/organization/handle-organization.tsx index 249b8ccd0..014c37df1 100644 --- a/apps/dokploy/components/dashboard/organization/handle-organization.tsx +++ b/apps/dokploy/components/dashboard/organization/handle-organization.tsx @@ -9,18 +9,39 @@ import { 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 { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; import { PenBoxIcon, Plus } from "lucide-react"; import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import { z } from "zod"; + +const organizationSchema = z.object({ + name: z.string().min(1, { + message: "Organization name is required", + }), + logo: z.string().optional(), +}); + +type OrganizationFormValues = z.infer; interface Props { organizationId?: string; children?: React.ReactNode; } + export function AddOrganization({ organizationId }: Props) { + const [open, setOpen] = useState(false); const utils = api.useUtils(); const { data: organization } = api.organization.one.useQuery( { @@ -33,24 +54,37 @@ export function AddOrganization({ organizationId }: Props) { const { mutateAsync, isLoading } = organizationId ? api.organization.update.useMutation() : api.organization.create.useMutation(); - const [open, setOpen] = useState(false); - const [name, setName] = useState(""); - const [logo, setLogo] = useState(""); + + const form = useForm({ + resolver: zodResolver(organizationSchema), + defaultValues: { + name: "", + logo: "", + }, + }); useEffect(() => { if (organization) { - setName(organization.name); - setLogo(organization.logo || ""); + form.reset({ + name: organization.name, + logo: organization.logo || "", + }); } - }, [organization]); - const handleSubmit = async () => { - await mutateAsync({ name, logo, organizationId: organizationId ?? "" }) + }, [organization, form]); + + const onSubmit = async (values: OrganizationFormValues) => { + await mutateAsync({ + name: values.name, + logo: values.logo, + organizationId: organizationId ?? "", + }) .then(() => { - setOpen(false); + form.reset(); toast.success( `Organization ${organizationId ? "updated" : "created"} successfully`, ); utils.organization.all.invalidate(); + setOpen(false); }) .catch((error) => { console.error(error); @@ -59,6 +93,7 @@ export function AddOrganization({ organizationId }: Props) { ); }); }; + return ( @@ -67,14 +102,11 @@ export function AddOrganization({ organizationId }: Props) { className="group cursor-pointer hover:bg-blue-500/10" onSelect={(e) => e.preventDefault()} > - + ) : ( { - setOpen(true); - }} onSelect={(e) => e.preventDefault()} >
@@ -97,36 +129,53 @@ export function AddOrganization({ organizationId }: Props) { : "Create a new organization to manage your projects."} -
-
- - setName(e.target.value)} - className="col-span-3" +
+ + ( + + Name + + + + + + )} /> -
-
- - setLogo(e.target.value)} - placeholder="https://example.com/logo.png" - className="col-span-3" + ( + + Logo URL + + + + + + )} /> -
-
- - - + + + + +
); diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 58638509e..805fe8dc7 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -534,7 +534,7 @@ function SidebarLogo() { )} > {/* Organization Logo and Selector */} - +
{ Organization Logo ); }