mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor(ui): enhance update server button and sidebar layout
- Improve UpdateServer component with flexible rendering and tooltip support - Modify sidebar layout to integrate update server button more cleanly - Add conditional rendering and styling for update availability - Introduce more consistent button and tooltip interactions
This commit is contained in:
parent
8ba3a42c1e
commit
c89f957133
@ -5,6 +5,12 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import type { IUpdateData } from "@dokploy/server/index";
|
import type { IUpdateData } from "@dokploy/server/index";
|
||||||
import {
|
import {
|
||||||
@ -24,9 +30,17 @@ import { UpdateWebServer } from "./update-webserver";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
updateData?: IUpdateData;
|
updateData?: IUpdateData;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
isOpen?: boolean;
|
||||||
|
onOpenChange?: (open: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UpdateServer = ({ updateData }: Props) => {
|
export const UpdateServer = ({
|
||||||
|
updateData,
|
||||||
|
children,
|
||||||
|
isOpen: isOpenProp,
|
||||||
|
onOpenChange: onOpenChangeProp,
|
||||||
|
}: Props) => {
|
||||||
const [hasCheckedUpdate, setHasCheckedUpdate] = useState(!!updateData);
|
const [hasCheckedUpdate, setHasCheckedUpdate] = useState(!!updateData);
|
||||||
const [isUpdateAvailable, setIsUpdateAvailable] = useState(
|
const [isUpdateAvailable, setIsUpdateAvailable] = useState(
|
||||||
!!updateData?.updateAvailable,
|
!!updateData?.updateAvailable,
|
||||||
@ -35,10 +49,10 @@ export const UpdateServer = ({ updateData }: Props) => {
|
|||||||
api.settings.getUpdateData.useMutation();
|
api.settings.getUpdateData.useMutation();
|
||||||
const { data: dokployVersion } = api.settings.getDokployVersion.useQuery();
|
const { data: dokployVersion } = api.settings.getDokployVersion.useQuery();
|
||||||
const { data: releaseTag } = api.settings.getReleaseTag.useQuery();
|
const { data: releaseTag } = api.settings.getReleaseTag.useQuery();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [latestVersion, setLatestVersion] = useState(
|
const [latestVersion, setLatestVersion] = useState(
|
||||||
updateData?.latestVersion ?? "",
|
updateData?.latestVersion ?? "",
|
||||||
);
|
);
|
||||||
|
const [isOpenInternal, setIsOpenInternal] = useState(false);
|
||||||
|
|
||||||
const handleCheckUpdates = async () => {
|
const handleCheckUpdates = async () => {
|
||||||
try {
|
try {
|
||||||
@ -65,28 +79,52 @@ export const UpdateServer = ({ updateData }: Props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isOpen = isOpenInternal || isOpenProp;
|
||||||
|
const onOpenChange = (open: boolean) => {
|
||||||
|
setIsOpenInternal(open);
|
||||||
|
onOpenChangeProp?.(open);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button
|
{children ? (
|
||||||
variant={updateData ? "outline" : "secondary"}
|
children
|
||||||
className="gap-2"
|
) : (
|
||||||
>
|
<TooltipProvider delayDuration={0}>
|
||||||
{updateData ? (
|
<Tooltip>
|
||||||
<>
|
<TooltipTrigger asChild>
|
||||||
<span className="flex h-2 w-2">
|
<Button
|
||||||
<span className="animate-ping absolute inline-flex h-2 w-2 rounded-full bg-emerald-400 opacity-75" />
|
variant={updateData ? "outline" : "secondary"}
|
||||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-500" />
|
size="sm"
|
||||||
</span>
|
onClick={() => onOpenChange?.(true)}
|
||||||
Update available
|
>
|
||||||
</>
|
<Download className="h-4 w-4 flex-shrink-0" />
|
||||||
) : (
|
{updateData ? (
|
||||||
<>
|
<span className="font-medium truncate group-data-[collapsible=icon]:hidden">
|
||||||
<Sparkles className="h-4 w-4" />
|
Update Available
|
||||||
Updates
|
</span>
|
||||||
</>
|
) : (
|
||||||
)}
|
<span className="font-medium truncate group-data-[collapsible=icon]:hidden">
|
||||||
</Button>
|
Check for updates
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{updateData && (
|
||||||
|
<span className="absolute right-2 flex h-2 w-2 group-data-[collapsible=icon]:hidden">
|
||||||
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" />
|
||||||
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-500" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
{updateData && (
|
||||||
|
<TooltipContent side="right" sideOffset={10}>
|
||||||
|
<p>Update Available</p>
|
||||||
|
</TooltipContent>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
)}
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-w-lg p-6">
|
<DialogContent className="max-w-lg p-6">
|
||||||
<div className="flex items-center justify-between mb-8">
|
<div className="flex items-center justify-between mb-8">
|
||||||
@ -217,7 +255,7 @@ export const UpdateServer = ({ updateData }: Props) => {
|
|||||||
|
|
||||||
<div className="space-y-4 flex items-center justify-end">
|
<div className="space-y-4 flex items-center justify-end">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button variant="outline" onClick={() => setIsOpen(false)}>
|
<Button variant="outline" onClick={() => onOpenChange?.(false)}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
{isUpdateAvailable ? (
|
{isUpdateAvailable ? (
|
||||||
|
@ -37,8 +37,6 @@ import {
|
|||||||
BreadcrumbItem,
|
BreadcrumbItem,
|
||||||
BreadcrumbLink,
|
BreadcrumbLink,
|
||||||
BreadcrumbList,
|
BreadcrumbList,
|
||||||
BreadcrumbPage,
|
|
||||||
BreadcrumbSeparator,
|
|
||||||
} from "@/components/ui/breadcrumb";
|
} from "@/components/ui/breadcrumb";
|
||||||
import {
|
import {
|
||||||
Collapsible,
|
Collapsible,
|
||||||
@ -1017,18 +1015,16 @@ export default function Page({ children }: Props) {
|
|||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
))}
|
))}
|
||||||
{!isCloud && auth?.role === "owner" && (
|
|
||||||
<SidebarMenuItem>
|
|
||||||
<SidebarMenuButton asChild>
|
|
||||||
<UpdateServerButton />
|
|
||||||
</SidebarMenuButton>
|
|
||||||
</SidebarMenuItem>
|
|
||||||
)}
|
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<SidebarFooter>
|
<SidebarFooter>
|
||||||
<SidebarMenu>
|
<SidebarMenu className="flex flex-col gap-2">
|
||||||
|
{!isCloud && auth?.role === "owner" && (
|
||||||
|
<SidebarMenuItem>
|
||||||
|
<UpdateServerButton />
|
||||||
|
</SidebarMenuItem>
|
||||||
|
)}
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<UserNav />
|
<UserNav />
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
|
@ -3,7 +3,14 @@ import type { IUpdateData } from "@dokploy/server/index";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import UpdateServer from "../dashboard/settings/web-server/update-server";
|
import UpdateServer from "../dashboard/settings/web-server/update-server";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import { Download } from "lucide-react";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "../ui/tooltip";
|
||||||
const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 7;
|
const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 7;
|
||||||
|
|
||||||
export const UpdateServerButton = () => {
|
export const UpdateServerButton = () => {
|
||||||
@ -15,6 +22,7 @@ export const UpdateServerButton = () => {
|
|||||||
const { data: isCloud } = api.settings.isCloud.useQuery();
|
const { data: isCloud } = api.settings.isCloud.useQuery();
|
||||||
const { mutateAsync: getUpdateData } =
|
const { mutateAsync: getUpdateData } =
|
||||||
api.settings.getUpdateData.useMutation();
|
api.settings.getUpdateData.useMutation();
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const checkUpdatesIntervalRef = useRef<null | NodeJS.Timeout>(null);
|
const checkUpdatesIntervalRef = useRef<null | NodeJS.Timeout>(null);
|
||||||
|
|
||||||
@ -69,11 +77,47 @@ export const UpdateServerButton = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return updateData.updateAvailable ? (
|
||||||
updateData.updateAvailable && (
|
<div className="border-t pt-4">
|
||||||
<div>
|
<UpdateServer
|
||||||
<UpdateServer updateData={updateData} />
|
updateData={updateData}
|
||||||
</div>
|
isOpen={isOpen}
|
||||||
)
|
onOpenChange={setIsOpen}
|
||||||
);
|
>
|
||||||
|
<TooltipProvider delayDuration={0}>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant={updateData ? "outline" : "secondary"}
|
||||||
|
className="w-full"
|
||||||
|
onClick={() => setIsOpen(true)}
|
||||||
|
>
|
||||||
|
<Download className="h-4 w-4 flex-shrink-0" />
|
||||||
|
{updateData ? (
|
||||||
|
<span className="font-medium truncate group-data-[collapsible=icon]:hidden">
|
||||||
|
Update Available
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="font-medium truncate group-data-[collapsible=icon]:hidden">
|
||||||
|
Check for updates
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{updateData && (
|
||||||
|
<span className="absolute right-2 flex h-2 w-2 group-data-[collapsible=icon]:hidden">
|
||||||
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75" />
|
||||||
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-500" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
{updateData && (
|
||||||
|
<TooltipContent side="right" sideOffset={10}>
|
||||||
|
<p>Update Available</p>
|
||||||
|
</TooltipContent>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</UpdateServer>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user