diff --git a/apps/dokploy/components/dashboard/organization/handle-organization.tsx b/apps/dokploy/components/dashboard/organization/handle-organization.tsx index 4c5ba3e48..b09e638fd 100644 --- a/apps/dokploy/components/dashboard/organization/handle-organization.tsx +++ b/apps/dokploy/components/dashboard/organization/handle-organization.tsx @@ -1,10 +1,11 @@ import { standardSchemaResolver as zodResolver } from "@hookform/resolvers/standard-schema"; -import { GlobeIcon, PenBoxIcon, Plus, X } from "lucide-react"; +import { PenBoxIcon, Plus, X } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { Logo } from "@/components/shared/logo"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -255,8 +256,10 @@ export function AddOrganization({ control={form.control} name="name" render={({ field }) => ( - - Name + +
+ Name +
-
+
{field.value ? ( // biome-ignore lint/performance/noImgElement: user uploaded logo preview Logo preview ) : ( - + )}
diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 4c76795f5..97e0706ba 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -43,6 +43,7 @@ import Link from "next/link"; import { usePathname } from "next/navigation"; import { useEffect, useState } from "react"; import { toast } from "sonner"; +import { TruncateTooltip } from "@/components/shared/truncate-tooltip"; import { Badge } from "@/components/ui/badge"; import { Breadcrumb, @@ -656,9 +657,12 @@ function SidebarLogo() { )} >
-

- {activeOrganization?.name ?? "Select Organization"} -

+ {haveValidLicense && ( Enterprise diff --git a/apps/dokploy/components/shared/truncate-tooltip.tsx b/apps/dokploy/components/shared/truncate-tooltip.tsx new file mode 100644 index 000000000..f07165a27 --- /dev/null +++ b/apps/dokploy/components/shared/truncate-tooltip.tsx @@ -0,0 +1,78 @@ +import { Tooltip as TooltipPrimitive } from "radix-ui"; +import type React from "react"; +import { useEffect, useRef, useState } from "react"; +import { + Tooltip, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; + +interface Props extends React.HTMLAttributes { + text: string; +} + +export const TruncateTooltip = ({ text, className, ...props }: Props) => { + const textRef = useRef(null); + const [isTruncated, setIsTruncated] = useState(false); + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + const element = textRef.current; + if (!element) return; + + const checkTruncation = () => { + const truncated = element.scrollWidth > element.clientWidth; + setIsTruncated(truncated); + if (!truncated) { + setIsOpen(false); + } + }; + + checkTruncation(); + + const resizeObserver = new ResizeObserver(() => { + checkTruncation(); + }); + + resizeObserver.observe(element); + + return () => { + resizeObserver.disconnect(); + }; + }, [text]); + + const content = ( +

+ {text} +

+ ); + + return ( + + { + // Only allow opening if it's actually truncated + if (isTruncated) { + setIsOpen(open); + } else { + setIsOpen(false); + } + }} + > + {content} + + +

{text}

+
+
+
+
+ ); +}; diff --git a/apps/dokploy/server/api/routers/organization.ts b/apps/dokploy/server/api/routers/organization.ts index 98d36dd2b..80c4d4bc6 100644 --- a/apps/dokploy/server/api/routers/organization.ts +++ b/apps/dokploy/server/api/routers/organization.ts @@ -25,7 +25,7 @@ export const organizationRouter = createTRPCRouter({ create: protectedProcedure .input( z.object({ - name: z.string(), + name: z.string().min(1), logo: z.string().optional(), }), ) @@ -130,7 +130,7 @@ export const organizationRouter = createTRPCRouter({ .input( z.object({ organizationId: z.string(), - name: z.string(), + name: z.string().min(1), logo: z.string().optional(), defaultRole: z.string().min(1).nullable().optional(), }),