svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
+ className,
+ )}
+ {...props}
+ />
+ );
+}
+
+export {
+ Avatar,
+ AvatarImage,
+ AvatarFallback,
+ AvatarGroup,
+ AvatarGroupCount,
+ AvatarBadge,
+};
diff --git a/apps/dokploy/components/ui/badge.tsx b/apps/dokploy/components/ui/badge.tsx
index 7d1480058..ef7e697dd 100644
--- a/apps/dokploy/components/ui/badge.tsx
+++ b/apps/dokploy/components/ui/badge.tsx
@@ -1,19 +1,24 @@
import { cva, type VariantProps } from "class-variance-authority";
+import { Slot } from "radix-ui";
import type * as React from "react";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold whitespace-nowrap transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2.5 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg:not(.cursor-pointer)]:pointer-events-none [&>svg]:size-3!",
{
variants: {
variant: {
- default:
- "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
secondary:
- "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
destructive:
- "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
+ "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
+ outline:
+ "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
+ ghost:
+ "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
+ link: "text-primary underline-offset-4 hover:underline",
red: "border-transparent select-none items-center whitespace-nowrap font-medium bg-red-600/20 dark:bg-red-500/15 text-destructive text-xs h-4 px-1 py-1 rounded-md",
yellow:
"border-transparent select-none items-center whitespace-nowrap font-medium bg-yellow-600/20 dark:bg-yellow-500/15 dark:text-yellow-500 text-yellow-600 text-xs h-4 px-1 py-1 rounded-md",
@@ -24,7 +29,6 @@ const badgeVariants = cva(
blue: "border-transparent select-none items-center whitespace-nowrap font-medium bg-blue-600/20 dark:bg-blue-500/15 dark:text-blue-500 text-blue-600 text-xs h-4 px-1 py-1 rounded-md",
blank:
"border-transparent select-none items-center whitespace-nowrap font-medium dark:bg-white/15 bg-black/15 text-foreground text-xs h-4 px-1 py-1 rounded-md",
- outline: "text-foreground",
},
},
defaultVariants: {
@@ -33,13 +37,22 @@ const badgeVariants = cva(
},
);
-export interface BadgeProps
- extends React.HTMLAttributes
,
- VariantProps {}
+function Badge({
+ className,
+ variant = "default",
+ asChild = false,
+ ...props
+}: React.ComponentProps<"span"> &
+ VariantProps & { asChild?: boolean }) {
+ const Comp = asChild ? Slot.Root : "span";
-function Badge({ className, variant, ...props }: BadgeProps) {
return (
-
+
);
}
diff --git a/apps/dokploy/components/ui/breadcrumb.tsx b/apps/dokploy/components/ui/breadcrumb.tsx
index 8bf95ea32..86373c1a6 100644
--- a/apps/dokploy/components/ui/breadcrumb.tsx
+++ b/apps/dokploy/components/ui/breadcrumb.tsx
@@ -1,107 +1,111 @@
-import { Slot } from "@radix-ui/react-slot";
-import { ChevronRight, MoreHorizontal } from "lucide-react";
-import * as React from "react";
-
+import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
+import { Slot } from "radix-ui";
+import type * as React from "react";
import { cn } from "@/lib/utils";
-const Breadcrumb = React.forwardRef<
- HTMLElement,
- React.ComponentPropsWithoutRef<"nav"> & {
- separator?: React.ReactNode;
- }
->(({ ...props }, ref) => );
-Breadcrumb.displayName = "Breadcrumb";
+function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
+ return (
+
+ );
+}
-const BreadcrumbList = React.forwardRef<
- HTMLOListElement,
- React.ComponentPropsWithoutRef<"ol">
->(({ className, ...props }, ref) => (
-
-));
-BreadcrumbList.displayName = "BreadcrumbList";
+function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
+ return (
+
+ );
+}
-const BreadcrumbItem = React.forwardRef<
- HTMLLIElement,
- React.ComponentPropsWithoutRef<"li">
->(({ className, ...props }, ref) => (
-
-));
-BreadcrumbItem.displayName = "BreadcrumbItem";
+function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
+ return (
+
+ );
+}
-const BreadcrumbLink = React.forwardRef<
- HTMLAnchorElement,
- React.ComponentPropsWithoutRef<"a"> & {
- asChild?: boolean;
- }
->(({ asChild, className, ...props }, ref) => {
- const Comp = asChild ? Slot : "a";
+function BreadcrumbLink({
+ asChild,
+ className,
+ ...props
+}: React.ComponentProps<"a"> & {
+ asChild?: boolean;
+}) {
+ const Comp = asChild ? Slot.Root : "a";
return (
);
-});
-BreadcrumbLink.displayName = "BreadcrumbLink";
+}
-const BreadcrumbPage = React.forwardRef<
- HTMLSpanElement,
- React.ComponentPropsWithoutRef<"span">
->(({ className, ...props }, ref) => (
-
-));
-BreadcrumbPage.displayName = "BreadcrumbPage";
+function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
+ return (
+
+ );
+}
-const BreadcrumbSeparator = ({
+function BreadcrumbSeparator({
children,
className,
...props
-}: React.ComponentProps<"li">) => (
- svg]:w-3.5 [&>svg]:h-3.5", className)}
- {...props}
- >
- {children ?? }
-
-);
-BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
+}: React.ComponentProps<"li">) {
+ return (
+ svg]:size-3.5", className)}
+ {...props}
+ >
+ {children ?? }
+
+ );
+}
-const BreadcrumbEllipsis = ({
+function BreadcrumbEllipsis({
className,
...props
-}: React.ComponentProps<"span">) => (
-
-
- More
-
-);
-BreadcrumbEllipsis.displayName = "BreadcrumbEllipsis";
+}: React.ComponentProps<"span">) {
+ return (
+ svg]:size-4",
+ className,
+ )}
+ {...props}
+ >
+
+ More
+
+ );
+}
export {
Breadcrumb,
diff --git a/apps/dokploy/components/ui/button.tsx b/apps/dokploy/components/ui/button.tsx
index 7f9645c7e..f31410ab4 100644
--- a/apps/dokploy/components/ui/button.tsx
+++ b/apps/dokploy/components/ui/button.tsx
@@ -1,29 +1,38 @@
-import { Slot, Slottable } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { Loader2 } from "lucide-react";
-import * as React from "react";
+import { Slot } from "radix-ui";
+import type * as React from "react";
+
import { cn } from "@/lib/utils";
const buttonVariants = cva(
- "inline-flex items-center justify-center whitespace-nowrap select-none rounded-lg transition-all will-change-transform active:hover:scale-[0.98] active:hover:transform text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
+ "group/button inline-flex shrink-0 cursor-pointer items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
- destructive:
- "bg-destructive text-destructive-foreground hover:bg-destructive/70",
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
outline:
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
+ "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
secondary:
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
- ghost: "hover:bg-accent hover:text-accent-foreground",
+ "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
+ ghost:
+ "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
+ destructive:
+ "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
- default: "h-10 px-4 py-2",
- sm: "h-9 rounded-md px-3",
- lg: "h-11 rounded-md px-8",
- icon: "h-10 w-10",
+ default:
+ "h-10 gap-1.5 px-4 py-2 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
+ xs: "h-7 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
+ sm: "h-9 gap-1.5 px-3 in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-4",
+ lg: "h-11 gap-1.5 px-8 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
+ icon: "size-10",
+ "icon-xs":
+ "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
+ "icon-sm":
+ "size-9 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
+ "icon-lg": "size-11",
},
},
defaultVariants: {
@@ -33,50 +42,35 @@ const buttonVariants = cva(
},
);
-export interface ButtonProps
- extends React.ButtonHTMLAttributes,
- VariantProps {
- asChild?: boolean;
- isLoading?: boolean;
- children?: React.ReactNode;
+function Button({
+ className,
+ variant = "default",
+ size = "default",
+ asChild = false,
+ isLoading = false,
+ children,
+ disabled,
+ ...props
+}: React.ComponentProps<"button"> &
+ VariantProps & {
+ asChild?: boolean;
+ isLoading?: boolean;
+ }) {
+ const Comp = asChild ? Slot.Root : "button";
+
+ return (
+
+ {isLoading && }
+ {children}
+
+ );
}
-const Button = React.forwardRef(
- (
- {
- className,
- variant,
- size,
- children,
- isLoading = false,
- asChild = false,
- ...props
- },
- ref,
- ) => {
- const Comp = asChild ? Slot : "button";
- const type = props.type ?? undefined;
-
- return (
- <>
-
- {isLoading && }
- {children}
-
- >
- );
- },
-);
-Button.displayName = "Button";
-
export { Button, buttonVariants };
diff --git a/apps/dokploy/components/ui/calendar.tsx b/apps/dokploy/components/ui/calendar.tsx
index 6e05e4807..6412c4085 100644
--- a/apps/dokploy/components/ui/calendar.tsx
+++ b/apps/dokploy/components/ui/calendar.tsx
@@ -1,68 +1,226 @@
-import { ChevronLeft, ChevronRight } from "lucide-react";
-import type * as React from "react";
-import { DayPicker } from "react-day-picker";
-
-import { buttonVariants } from "@/components/ui/button";
+import {
+ ChevronDownIcon,
+ ChevronLeftIcon,
+ ChevronRightIcon,
+} from "lucide-react";
+import * as React from "react";
+import {
+ type DayButton,
+ DayPicker,
+ getDefaultClassNames,
+ type Locale,
+} from "react-day-picker";
+import { Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
-export type CalendarProps = React.ComponentProps;
-
function Calendar({
className,
classNames,
showOutsideDays = true,
+ captionLayout = "label",
+ buttonVariant = "ghost",
+ locale,
+ formatters,
+ components,
...props
-}: CalendarProps) {
+}: React.ComponentProps & {
+ buttonVariant?: React.ComponentProps["variant"];
+}) {
+ const defaultClassNames = getDefaultClassNames();
+
return (
svg]:rotate-180`,
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
+ className,
+ )}
+ captionLayout={captionLayout}
+ locale={locale}
+ formatters={{
+ formatMonthDropdown: (date) =>
+ date.toLocaleString(locale?.code, { month: "short" }),
+ ...formatters,
+ }}
classNames={{
- months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
- month: "space-y-4",
- caption: "flex justify-center pt-1 relative items-center",
- caption_label: "text-sm font-medium",
- nav: "space-x-1 flex items-center",
- nav_button: cn(
- buttonVariants({ variant: "outline" }),
- "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100",
+ root: cn("w-fit", defaultClassNames.root),
+ months: cn(
+ "relative flex flex-col gap-4 md:flex-row",
+ defaultClassNames.months,
+ ),
+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
+ nav: cn(
+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
+ defaultClassNames.nav,
+ ),
+ button_previous: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
+ defaultClassNames.button_previous,
+ ),
+ button_next: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
+ defaultClassNames.button_next,
+ ),
+ month_caption: cn(
+ "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
+ defaultClassNames.month_caption,
+ ),
+ dropdowns: cn(
+ "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
+ defaultClassNames.dropdowns,
+ ),
+ dropdown_root: cn(
+ "relative rounded-(--cell-radius)",
+ defaultClassNames.dropdown_root,
+ ),
+ dropdown: cn(
+ "absolute inset-0 bg-popover opacity-0",
+ defaultClassNames.dropdown,
+ ),
+ caption_label: cn(
+ "font-medium select-none",
+ captionLayout === "label"
+ ? "text-sm"
+ : "flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
+ defaultClassNames.caption_label,
+ ),
+ month_grid: "w-full border-collapse",
+ weekdays: cn("flex", defaultClassNames.weekdays),
+ weekday: cn(
+ "flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none",
+ defaultClassNames.weekday,
+ ),
+ week: cn("mt-2 flex w-full", defaultClassNames.week),
+ week_number_header: cn(
+ "w-(--cell-size) select-none",
+ defaultClassNames.week_number_header,
+ ),
+ week_number: cn(
+ "text-[0.8rem] text-muted-foreground select-none",
+ defaultClassNames.week_number,
),
- nav_button_previous: "absolute left-1",
- nav_button_next: "absolute right-1",
- table: "w-full border-collapse space-y-1",
- head_row: "flex",
- head_cell:
- "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
- row: "flex w-full mt-2",
- cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
day: cn(
- buttonVariants({ variant: "ghost" }),
- "h-9 w-9 p-0 font-normal aria-selected:opacity-100",
+ "group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)",
+ props.showWeekNumber
+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)"
+ : "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)",
+ defaultClassNames.day,
),
- day_range_end: "day-range-end",
- day_selected:
- "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
- day_today: "bg-accent text-accent-foreground",
- day_outside:
- "day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
- day_disabled: "text-muted-foreground opacity-50",
- day_range_middle:
- "aria-selected:bg-accent aria-selected:text-accent-foreground",
- day_hidden: "invisible",
+ range_start: cn(
+ "relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted",
+ defaultClassNames.range_start,
+ ),
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
+ range_end: cn(
+ "relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted",
+ defaultClassNames.range_end,
+ ),
+ today: cn(
+ "rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none",
+ defaultClassNames.today,
+ ),
+ outside: cn(
+ "text-muted-foreground aria-selected:text-muted-foreground",
+ defaultClassNames.outside,
+ ),
+ disabled: cn(
+ "text-muted-foreground opacity-50",
+ defaultClassNames.disabled,
+ ),
+ hidden: cn("invisible", defaultClassNames.hidden),
...classNames,
}}
components={{
- IconLeft: ({ className, ...props }) => (
-
- ),
- IconRight: ({ className, ...props }) => (
-
+ Root: ({ className, rootRef, ...props }) => {
+ return (
+
+ );
+ },
+ Chevron: ({ className, orientation, ...props }) => {
+ if (orientation === "left") {
+ return (
+
+ );
+ }
+
+ if (orientation === "right") {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+ },
+ DayButton: ({ ...props }) => (
+
),
+ WeekNumber: ({ children, ...props }) => {
+ return (
+
+
+ {children}
+
+
+ );
+ },
+ ...components,
}}
{...props}
/>
);
}
-Calendar.displayName = "Calendar";
-export { Calendar };
+function CalendarDayButton({
+ className,
+ day,
+ modifiers,
+ locale,
+ ...props
+}: React.ComponentProps & { locale?: Partial }) {
+ const defaultClassNames = getDefaultClassNames();
+
+ const ref = React.useRef(null);
+ React.useEffect(() => {
+ if (modifiers.focused) ref.current?.focus();
+ }, [modifiers.focused]);
+
+ return (
+ span]:text-xs [&>span]:opacity-70",
+ defaultClassNames.day,
+ className,
+ )}
+ {...props}
+ />
+ );
+}
+
+export { Calendar, CalendarDayButton };
diff --git a/apps/dokploy/components/ui/card.tsx b/apps/dokploy/components/ui/card.tsx
index 9ebb4023f..92abd7d96 100644
--- a/apps/dokploy/components/ui/card.tsx
+++ b/apps/dokploy/components/ui/card.tsx
@@ -1,86 +1,100 @@
-import * as React from "react";
+import type * as React from "react";
import { cn } from "@/lib/utils";
-const Card = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-Card.displayName = "Card";
+function Card({
+ className,
+ size = "default",
+ ...props
+}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
+ return (
+
+ );
+}
-const CardHeader = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardHeader.displayName = "CardHeader";
+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
-const CardTitle = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardTitle.displayName = "CardTitle";
+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
-const CardDescription = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardDescription.displayName = "CardDescription";
+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
-const CardContent = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardContent.displayName = "CardContent";
+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
-const CardFooter = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes
->(({ className, ...props }, ref) => (
-
-));
-CardFooter.displayName = "CardFooter";
+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
+ CardAction,
CardDescription,
CardContent,
};
diff --git a/apps/dokploy/components/ui/chart.tsx b/apps/dokploy/components/ui/chart.tsx
index 07359187e..6f04835d3 100644
--- a/apps/dokploy/components/ui/chart.tsx
+++ b/apps/dokploy/components/ui/chart.tsx
@@ -1,4 +1,5 @@
import * as React from "react";
+import type { TooltipValueType } from "recharts";
import * as RechartsPrimitive from "recharts";
import { cn } from "@/lib/utils";
@@ -6,15 +7,19 @@ import { cn } from "@/lib/utils";
// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const;
-export type ChartConfig = {
- [k in string]: {
+const INITIAL_DIMENSION = { width: 320, height: 200 } as const;
+type TooltipNameType = number | string;
+
+export type ChartConfig = Record<
+ string,
+ {
label?: React.ReactNode;
icon?: React.ComponentType;
} & (
| { color?: string; theme?: never }
| { color?: never; theme: Record }
- );
-};
+ )
+>;
type ChartContextProps = {
config: ChartConfig;
@@ -32,42 +37,51 @@ function useChart() {
return context;
}
-const ChartContainer = React.forwardRef<
- HTMLDivElement,
- React.ComponentProps<"div"> & {
- config: ChartConfig;
- children: React.ComponentProps<
- typeof RechartsPrimitive.ResponsiveContainer
- >["children"];
- }
->(({ id, className, children, config, ...props }, ref) => {
+function ChartContainer({
+ id,
+ className,
+ children,
+ config,
+ initialDimension = INITIAL_DIMENSION,
+ ...props
+}: React.ComponentProps<"div"> & {
+ config: ChartConfig;
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"];
+ initialDimension?: {
+ width: number;
+ height: number;
+ };
+}) {
const uniqueId = React.useId();
- const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
+ const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`;
return (
-
+
{children}
);
-});
-ChartContainer.displayName = "Chart";
+}
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter(
- ([, config]) => config.theme || config.color,
+ ([, config]) => config.theme ?? config.color,
);
if (!colorConfig.length) {
@@ -84,7 +98,7 @@ ${prefix} [data-chart=${id}] {
${colorConfig
.map(([key, itemConfig]) => {
const color =
- itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ??
itemConfig.color;
return color ? ` --color-${key}: ${color};` : null;
})
@@ -100,97 +114,97 @@ ${colorConfig
const ChartTooltip = RechartsPrimitive.Tooltip;
-const ChartTooltipContent = React.forwardRef<
- HTMLDivElement,
- React.ComponentProps &
- React.ComponentProps<"div"> & {
- hideLabel?: boolean;
- hideIndicator?: boolean;
- indicator?: "line" | "dot" | "dashed";
- nameKey?: string;
- labelKey?: string;
- }
->(
- (
- {
- active,
- payload,
- className,
- indicator = "dot",
- hideLabel = false,
- hideIndicator = false,
- label,
- labelFormatter,
- labelClassName,
- formatter,
- color,
- nameKey,
- labelKey,
- },
- ref,
- ) => {
- const { config } = useChart();
+function ChartTooltipContent({
+ active,
+ payload,
+ className,
+ indicator = "dot",
+ hideLabel = false,
+ hideIndicator = false,
+ label,
+ labelFormatter,
+ labelClassName,
+ formatter,
+ color,
+ nameKey,
+ labelKey,
+}: React.ComponentProps &
+ React.ComponentProps<"div"> & {
+ hideLabel?: boolean;
+ hideIndicator?: boolean;
+ indicator?: "line" | "dot" | "dashed";
+ nameKey?: string;
+ labelKey?: string;
+ } & Omit<
+ RechartsPrimitive.DefaultTooltipContentProps<
+ TooltipValueType,
+ TooltipNameType
+ >,
+ "accessibilityLayer"
+ >) {
+ const { config } = useChart();
- const tooltipLabel = React.useMemo(() => {
- if (hideLabel || !payload?.length) {
- return null;
- }
-
- const [item] = payload;
- const key = `${labelKey || item?.dataKey || item?.name || "value"}`;
- const itemConfig = getPayloadConfigFromPayload(config, item, key);
- const value =
- !labelKey && typeof label === "string"
- ? config[label as keyof typeof config]?.label || label
- : itemConfig?.label;
-
- if (labelFormatter) {
- return (
-
- {labelFormatter(value, payload)}
-
- );
- }
-
- if (!value) {
- return null;
- }
-
- return {value}
;
- }, [
- label,
- labelFormatter,
- payload,
- hideLabel,
- labelClassName,
- config,
- labelKey,
- ]);
-
- if (!active || !payload?.length) {
+ const tooltipLabel = React.useMemo(() => {
+ if (hideLabel || !payload?.length) {
return null;
}
- const nestLabel = payload.length === 1 && indicator !== "dot";
+ const [item] = payload;
+ const key = `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}`;
+ const itemConfig = getPayloadConfigFromPayload(config, item, key);
+ const value =
+ !labelKey && typeof label === "string"
+ ? (config[label]?.label ?? label)
+ : itemConfig?.label;
- return (
-
- {!nestLabel ? tooltipLabel : null}
-
- {payload.map((item, index) => {
- const key = `${nameKey || item.name || item.dataKey || "value"}`;
+ if (labelFormatter) {
+ return (
+
+ {labelFormatter(value, payload)}
+
+ );
+ }
+
+ if (!value) {
+ return null;
+ }
+
+ return
{value}
;
+ }, [
+ label,
+ labelFormatter,
+ payload,
+ hideLabel,
+ labelClassName,
+ config,
+ labelKey,
+ ]);
+
+ if (!active || !payload?.length) {
+ return null;
+ }
+
+ const nestLabel = payload.length === 1 && indicator !== "dot";
+
+ return (
+
+ {!nestLabel ? tooltipLabel : null}
+
+ {payload
+ .filter((item) => item.type !== "none")
+ .map((item, index) => {
+ const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key);
- const indicatorColor = color || item.payload.fill || item.color;
+ const indicatorColor = color ?? item.payload?.fill ?? item.color;
return (
svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
indicator === "dot" && "items-center",
@@ -206,7 +220,7 @@ const ChartTooltipContent = React.forwardRef<
!hideIndicator && (
{nestLabel ? tooltipLabel : null}
- {itemConfig?.label || item.name}
+ {itemConfig?.label ?? item.name}
- {item.value && (
-
- {item.value.toLocaleString()}
+ {item.value != null && (
+
+ {typeof item.value === "number"
+ ? item.value.toLocaleString()
+ : String(item.value)}
)}
@@ -247,49 +263,46 @@ const ChartTooltipContent = React.forwardRef<
);
})}
-
- );
- },
-);
-ChartTooltipContent.displayName = "ChartTooltip";
+
+ );
+}
const ChartLegend = RechartsPrimitive.Legend;
-const ChartLegendContent = React.forwardRef<
- HTMLDivElement,
- React.ComponentProps<"div"> &
- Pick & {
- hideIcon?: boolean;
- nameKey?: string;
- }
->(
- (
- { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
- ref,
- ) => {
- const { config } = useChart();
+function ChartLegendContent({
+ className,
+ hideIcon = false,
+ payload,
+ verticalAlign = "bottom",
+ nameKey,
+}: React.ComponentProps<"div"> & {
+ hideIcon?: boolean;
+ nameKey?: string;
+} & RechartsPrimitive.DefaultLegendContentProps) {
+ const { config } = useChart();
- if (!payload?.length) {
- return null;
- }
+ if (!payload?.length) {
+ return null;
+ }
- return (
-
- {payload.map((item) => {
- const key = `${nameKey || item.dataKey || "value"}`;
+ return (
+
+ {payload
+ .filter((item) => item.type !== "none")
+ .map((item, index) => {
+ const key = `${nameKey ?? item.dataKey ?? "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key);
return (
svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground",
)}
@@ -308,13 +321,10 @@ const ChartLegendContent = React.forwardRef<
);
})}
-
- );
- },
-);
-ChartLegendContent.displayName = "ChartLegend";
+
+ );
+}
-// Helper to extract item config from a payload.
function getPayloadConfigFromPayload(
config: ChartConfig,
payload: unknown,
@@ -348,9 +358,7 @@ function getPayloadConfigFromPayload(
] as string;
}
- return configLabelKey in config
- ? config[configLabelKey]
- : config[key as keyof typeof config];
+ return configLabelKey in config ? config[configLabelKey] : config[key];
}
export {
diff --git a/apps/dokploy/components/ui/checkbox.tsx b/apps/dokploy/components/ui/checkbox.tsx
index 54cdffe7a..00aba0ada 100644
--- a/apps/dokploy/components/ui/checkbox.tsx
+++ b/apps/dokploy/components/ui/checkbox.tsx
@@ -1,28 +1,31 @@
-import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
-import { Check } from "lucide-react";
-import * as React from "react";
+"use client";
+import { CheckIcon } from "lucide-react";
+import { Checkbox as CheckboxPrimitive } from "radix-ui";
+import type * as React from "react";
import { cn } from "@/lib/utils";
-const Checkbox = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
- ) {
+ return (
+
-
-
-
-));
-Checkbox.displayName = CheckboxPrimitive.Root.displayName;
+
+
+
+
+ );
+}
export { Checkbox };
diff --git a/apps/dokploy/components/ui/collapsible.tsx b/apps/dokploy/components/ui/collapsible.tsx
index 5c28cbcc3..439b44d3d 100644
--- a/apps/dokploy/components/ui/collapsible.tsx
+++ b/apps/dokploy/components/ui/collapsible.tsx
@@ -1,9 +1,31 @@
-import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
+import { Collapsible as CollapsiblePrimitive } from "radix-ui";
-const Collapsible = CollapsiblePrimitive.Root;
+function Collapsible({
+ ...props
+}: React.ComponentProps) {
+ return ;
+}
-const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
+function CollapsibleTrigger({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
-const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
+function CollapsibleContent({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
diff --git a/apps/dokploy/components/ui/command.tsx b/apps/dokploy/components/ui/command.tsx
index 12d5b0ddd..634e4c1aa 100644
--- a/apps/dokploy/components/ui/command.tsx
+++ b/apps/dokploy/components/ui/command.tsx
@@ -1,144 +1,184 @@
-import type { DialogProps } from "@radix-ui/react-dialog";
-import { Command as CommandPrimitive } from "cmdk";
-import { Search } from "lucide-react";
-import * as React from "react";
+"use client";
-import { Dialog, DialogContent } from "@/components/ui/dialog";
+import { Command as CommandPrimitive } from "cmdk";
+import { CheckIcon, SearchIcon } from "lucide-react";
+import type * as React from "react";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog";
+import { InputGroup, InputGroupAddon } from "@/components/ui/input-group";
import { cn } from "@/lib/utils";
-const Command = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-Command.displayName = CommandPrimitive.displayName;
+function Command({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ );
+}
-interface CommandDialogProps extends DialogProps {}
-
-const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
+function CommandDialog({
+ title = "Command Palette",
+ description = "Search for a command to run...",
+ children,
+ className,
+ showCloseButton = false,
+ ...props
+}: React.ComponentProps & {
+ title?: string;
+ description?: string;
+ className?: string;
+ showCloseButton?: boolean;
+}) {
return (
-
-
- {children}
-
+
+
+ {title}
+ {description}
+
+ {children}
);
-};
+}
-const CommandInput = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-
-
-
-));
-
-CommandInput.displayName = CommandPrimitive.Input.displayName;
-
-const CommandList = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-
-CommandList.displayName = CommandPrimitive.List.displayName;
-
-const CommandEmpty = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->((props, ref) => (
-
-));
-
-CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
-
-const CommandGroup = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-
-CommandGroup.displayName = CommandPrimitive.Group.displayName;
-
-const CommandSeparator = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
-
-const CommandItem = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-));
-
-CommandItem.displayName = CommandPrimitive.Item.displayName;
-
-const CommandShortcut = ({
+function CommandInput({
className,
...props
-}: React.HTMLAttributes) => {
+}: React.ComponentProps) {
return (
-
+
+
+
+
+
+
+
+ );
+}
+
+function CommandList({
+ className,
+ ...props
+}: React.ComponentProps