refactor: improve button structure and tooltip integration across dashboard components

- Refactored button components in the dashboard to enhance structure and readability.
- Integrated tooltips directly within button elements for better user experience.
- Updated tooltip descriptions for clarity across various database actions (Deploy, Reload, Start, Stop) for Redis, MySQL, PostgreSQL, and MariaDB.
- Ensured consistent formatting and improved code maintainability.
This commit is contained in:
Mauricio Siu
2025-03-16 20:52:57 -06:00
parent c6a288781f
commit 7c17cfb5c7
6 changed files with 988 additions and 914 deletions

View File

@@ -76,25 +76,27 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="default"
<Button isLoading={data?.applicationStatus === "running"}
variant="default" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={data?.applicationStatus === "running"} >
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<Rocket className="size-4 mr-1" /> <div className="flex items-center">
Deploy <Rocket className="size-4 mr-1" />
</Button> Deploy
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p> <TooltipContent sideOffset={5} className="z-[60]">
Downloads the source code and performs a complete build <p>
</p> Downloads the source code and performs a complete build
</TooltipContent> </p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
<DialogAction <DialogAction
title="Reload Application" title="Reload Application"
@@ -114,23 +116,25 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="secondary"
<Button isLoading={isReloading}
variant="secondary" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={isReloading} >
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<RefreshCcw className="size-4 mr-1" /> <div className="flex items-center">
Reload <RefreshCcw className="size-4 mr-1" />
</Button> Reload
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Reload the application without rebuilding it</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Reload the application without rebuilding it</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
<DialogAction <DialogAction
title="Rebuild Application" title="Rebuild Application"
@@ -149,25 +153,28 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="secondary"
<Button isLoading={data?.applicationStatus === "running"}
variant="secondary" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={data?.applicationStatus === "running"} >
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<Hammer className="size-4 mr-1" /> <div className="flex items-center">
Rebuild <Hammer className="size-4 mr-1" />
</Button> Rebuild
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p> <TooltipContent sideOffset={5} className="z-[60]">
Only rebuilds the application without downloading new code <p>
</p> Only rebuilds the application without downloading new
</TooltipContent> code
</TooltipPrimitive.Portal> </p>
</Tooltip> </TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
{data?.applicationStatus === "idle" ? ( {data?.applicationStatus === "idle" ? (
@@ -188,26 +195,28 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="secondary"
<Button isLoading={isStarting}
variant="secondary" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={isStarting} >
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<CheckCircle2 className="size-4 mr-1" /> <div className="flex items-center">
Start <CheckCircle2 className="size-4 mr-1" />
</Button> Start
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p> <TooltipContent sideOffset={5} className="z-[60]">
Start the application (requires a previous successful <p>
build) Start the application (requires a previous successful
</p> build)
</TooltipContent> </p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
) : ( ) : (
<DialogAction <DialogAction
@@ -226,23 +235,25 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="destructive"
<Button isLoading={isStopping}
variant="destructive" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={isStopping} >
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<Ban className="size-4 mr-1" /> <div className="flex items-center">
Stop <Ban className="size-4 mr-1" />
</Button> Stop
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Stop the currently running application</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Stop the currently running application</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
)} )}
</TooltipProvider> </TooltipProvider>

View File

@@ -55,23 +55,25 @@ export const ComposeActions = ({ composeId }: Props) => {
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="default"
<Button isLoading={data?.composeStatus === "running"}
variant="default" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={data?.composeStatus === "running"} >
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<Rocket className="size-4 mr-1" /> <div className="flex items-center">
Deploy <Rocket className="size-4 mr-1" />
</Button> Deploy
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Downloads the source code and performs a complete build</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Downloads the source code and performs a complete build</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
<DialogAction <DialogAction
title="Reload Compose" title="Reload Compose"
@@ -90,23 +92,25 @@ export const ComposeActions = ({ composeId }: Props) => {
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="secondary"
<Button isLoading={data?.composeStatus === "running"}
variant="secondary" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={data?.composeStatus === "running"} >
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<RefreshCcw className="size-4 mr-1" /> <div className="flex items-center">
Reload <RefreshCcw className="size-4 mr-1" />
</Button> Reload
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Reload the compose without rebuilding it</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Reload the compose without rebuilding it</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
{data?.composeType === "docker-compose" && {data?.composeType === "docker-compose" &&
data?.composeStatus === "idle" ? ( data?.composeStatus === "idle" ? (
@@ -127,25 +131,27 @@ export const ComposeActions = ({ composeId }: Props) => {
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="secondary"
<Button isLoading={isStarting}
variant="secondary" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={isStarting} >
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<CheckCircle2 className="size-4 mr-1" /> <div className="flex items-center">
Start <CheckCircle2 className="size-4 mr-1" />
</Button> Start
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p> <TooltipContent sideOffset={5} className="z-[60]">
Start the compose (requires a previous successful build) <p>
</p> Start the compose (requires a previous successful build)
</TooltipContent> </p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
) : ( ) : (
<DialogAction <DialogAction
@@ -164,23 +170,25 @@ export const ComposeActions = ({ composeId }: Props) => {
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="destructive"
<Button isLoading={isStopping}
variant="destructive" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={isStopping} >
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<Ban className="size-4 mr-1" /> <div className="flex items-center">
Stop <Ban className="size-4 mr-1" />
</Button> Stop
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Stop the currently running compose</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Stop the currently running compose</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
)} )}
</TooltipProvider> </TooltipProvider>

View File

@@ -17,236 +17,252 @@ import { type LogLine, parseLogs } from "../../docker/logs/utils";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
interface Props { interface Props {
mariadbId: string; mariadbId: string;
} }
export const ShowGeneralMariadb = ({ mariadbId }: Props) => { export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
const { data, refetch } = api.mariadb.one.useQuery( const { data, refetch } = api.mariadb.one.useQuery(
{ {
mariadbId, mariadbId,
}, },
{ enabled: !!mariadbId } { enabled: !!mariadbId },
); );
const { mutateAsync: reload, isLoading: isReloading } = const { mutateAsync: reload, isLoading: isReloading } =
api.mariadb.reload.useMutation(); api.mariadb.reload.useMutation();
const { mutateAsync: start, isLoading: isStarting } = const { mutateAsync: start, isLoading: isStarting } =
api.mariadb.start.useMutation(); api.mariadb.start.useMutation();
const { mutateAsync: stop, isLoading: isStopping } = const { mutateAsync: stop, isLoading: isStopping } =
api.mariadb.stop.useMutation(); api.mariadb.stop.useMutation();
const [isDrawerOpen, setIsDrawerOpen] = useState(false); const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]); const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false); const [isDeploying, setIsDeploying] = useState(false);
api.mariadb.deployWithLogs.useSubscription( api.mariadb.deployWithLogs.useSubscription(
{ {
mariadbId: mariadbId, mariadbId: mariadbId,
}, },
{ {
enabled: isDeploying, enabled: isDeploying,
onData(log) { onData(log) {
if (!isDrawerOpen) { if (!isDrawerOpen) {
setIsDrawerOpen(true); setIsDrawerOpen(true);
} }
if (log === "Deployment completed successfully!") { if (log === "Deployment completed successfully!") {
setIsDeploying(false); setIsDeploying(false);
} }
const parsedLogs = parseLogs(log); const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]); setFilteredLogs((prev) => [...prev, ...parsedLogs]);
}, },
onError(error) { onError(error) {
console.error("Deployment logs error:", error); console.error("Deployment logs error:", error);
setIsDeploying(false); setIsDeploying(false);
}, },
} },
); );
return ( return (
<> <>
<div className="flex w-full flex-col gap-5 "> <div className="flex w-full flex-col gap-5 ">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader> <CardHeader>
<CardTitle className="text-xl">Deploy Settings</CardTitle> <CardTitle className="text-xl">Deploy Settings</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="flex flex-row gap-4 flex-wrap"> <CardContent className="flex flex-row gap-4 flex-wrap">
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>
<DialogAction <DialogAction
title="Deploy Mariadb" title="Deploy Mariadb"
description="Are you sure you want to deploy this mariadb?" description="Are you sure you want to deploy this mariadb?"
type="default" type="default"
onClick={async () => { onClick={async () => {
setIsDeploying(true); setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000));
refetch(); refetch();
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="default"
<Button isLoading={data?.applicationStatus === "running"}
variant="default" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={data?.applicationStatus === "running"} >
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<Rocket className="size-4 mr-1" /> <div className="flex items-center">
Deploy <Rocket className="size-4 mr-1" />
</Button> Deploy
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Downloads and sets up the MariaDB database</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Downloads and sets up the MariaDB database</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</DialogAction> </Tooltip>
<DialogAction </Button>
title="Reload Mariadb" </DialogAction>
description="Are you sure you want to reload this mariadb?" </TooltipProvider>
type="default" <TooltipProvider delayDuration={0}>
onClick={async () => { <DialogAction
await reload({ title="Reload Mariadb"
mariadbId: mariadbId, description="Are you sure you want to reload this mariadb?"
appName: data?.appName || "", type="default"
}) onClick={async () => {
.then(() => { await reload({
toast.success("Mariadb reloaded successfully"); mariadbId: mariadbId,
refetch(); appName: data?.appName || "",
}) })
.catch(() => { .then(() => {
toast.error("Error reloading Mariadb"); toast.success("Mariadb reloaded successfully");
}); refetch();
}} })
> .catch(() => {
<Tooltip> toast.error("Error reloading Mariadb");
<TooltipTrigger asChild> });
<Button }}
variant="secondary" >
isLoading={isReloading} <Button
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" variant="secondary"
> isLoading={isReloading}
<RefreshCcw className="size-4 mr-1" /> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
Reload >
</Button> <Tooltip>
</TooltipTrigger> <TooltipTrigger asChild>
<TooltipPrimitive.Portal> <div className="flex items-center">
<TooltipContent sideOffset={5} className="z-[60]"> <RefreshCcw className="size-4 mr-1" />
<p>Restart the MariaDB service without rebuilding</p> Reload
</TooltipContent> </div>
</TooltipPrimitive.Portal> </TooltipTrigger>
</Tooltip> <TooltipPrimitive.Portal>
</DialogAction> <TooltipContent sideOffset={5} className="z-[60]">
{data?.applicationStatus === "idle" ? ( <p>Restart the MariaDB service without rebuilding</p>
<DialogAction </TooltipContent>
title="Start Mariadb" </TooltipPrimitive.Portal>
description="Are you sure you want to start this mariadb?" </Tooltip>
type="default" </Button>
onClick={async () => { </DialogAction>
await start({ </TooltipProvider>
mariadbId: mariadbId, {data?.applicationStatus === "idle" ? (
}) <TooltipProvider delayDuration={0}>
.then(() => { <DialogAction
toast.success("Mariadb started successfully"); title="Start Mariadb"
refetch(); description="Are you sure you want to start this mariadb?"
}) type="default"
.catch(() => { onClick={async () => {
toast.error("Error starting Mariadb"); await start({
}); mariadbId: mariadbId,
}} })
> .then(() => {
<Tooltip> toast.success("Mariadb started successfully");
<TooltipTrigger asChild> refetch();
<Button })
variant="secondary" .catch(() => {
isLoading={isStarting} toast.error("Error starting Mariadb");
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" });
> }}
<CheckCircle2 className="size-4 mr-1" /> >
Start <Button
</Button> variant="secondary"
</TooltipTrigger> isLoading={isStarting}
<TooltipPrimitive.Portal> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
<TooltipContent sideOffset={5} className="z-[60]"> >
<p> <Tooltip>
Start the MariaDB database (requires a previous <TooltipTrigger asChild>
successful setup) <div className="flex items-center">
</p> <CheckCircle2 className="size-4 mr-1" />
</TooltipContent> Start
</TooltipPrimitive.Portal> </div>
</Tooltip> </TooltipTrigger>
</DialogAction> <TooltipPrimitive.Portal>
) : ( <TooltipContent sideOffset={5} className="z-[60]">
<DialogAction <p>
title="Stop Mariadb" Start the MariaDB database (requires a previous
description="Are you sure you want to stop this mariadb?" successful setup)
onClick={async () => { </p>
await stop({ </TooltipContent>
mariadbId: mariadbId, </TooltipPrimitive.Portal>
}) </Tooltip>
.then(() => { </Button>
toast.success("Mariadb stopped successfully"); </DialogAction>
refetch(); </TooltipProvider>
}) ) : (
.catch(() => { <TooltipProvider delayDuration={0}>
toast.error("Error stopping Mariadb"); <DialogAction
}); title="Stop Mariadb"
}} description="Are you sure you want to stop this mariadb?"
> onClick={async () => {
<Tooltip> await stop({
<TooltipTrigger asChild> mariadbId: mariadbId,
<Button })
variant="destructive" .then(() => {
isLoading={isStopping} toast.success("Mariadb stopped successfully");
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" refetch();
> })
<Ban className="size-4 mr-1" /> .catch(() => {
Stop toast.error("Error stopping Mariadb");
</Button> });
</TooltipTrigger> }}
<TooltipPrimitive.Portal> >
<TooltipContent sideOffset={5} className="z-[60]"> <Button
<p>Stop the currently running MariaDB database</p> variant="destructive"
</TooltipContent> isLoading={isStopping}
</TooltipPrimitive.Portal> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
</Tooltip> >
</DialogAction> <Tooltip>
)} <TooltipTrigger asChild>
</TooltipProvider> <div className="flex items-center">
<DockerTerminalModal <Ban className="size-4 mr-1" />
appName={data?.appName || ""} Stop
serverId={data?.serverId || ""} </div>
> </TooltipTrigger>
<Tooltip> <TooltipPrimitive.Portal>
<TooltipTrigger asChild> <TooltipContent sideOffset={5} className="z-[60]">
<Button <p>Stop the currently running MariaDB database</p>
variant="outline" </TooltipContent>
className="flex items-center gap-2 focus-visible:ring-2 focus-visible:ring-offset-2" </TooltipPrimitive.Portal>
> </Tooltip>
<Terminal className="size-4" /> </Button>
Open Terminal </DialogAction>
</Button> </TooltipProvider>
</TooltipTrigger> )}
<TooltipPrimitive.Portal> <DockerTerminalModal
<TooltipContent sideOffset={5} className="z-[60]"> appName={data?.appName || ""}
<p>Open a terminal to the MariaDB container</p> serverId={data?.serverId || ""}
</TooltipContent> >
</TooltipPrimitive.Portal> <Button
</Tooltip> variant="outline"
</DockerTerminalModal> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
</CardContent> >
</Card> <Tooltip>
<DrawerLogs <TooltipTrigger asChild>
isOpen={isDrawerOpen} <div className="flex items-center">
onClose={() => { <Terminal className="size-4 mr-1" />
setIsDrawerOpen(false); Open Terminal
setFilteredLogs([]); </div>
setIsDeploying(false); </TooltipTrigger>
refetch(); <TooltipPrimitive.Portal>
}} <TooltipContent sideOffset={5} className="z-[60]">
filteredLogs={filteredLogs} <p>Open a terminal to the MariaDB container</p>
/> </TooltipContent>
</div> </TooltipPrimitive.Portal>
</> </Tooltip>
); </Button>
</DockerTerminalModal>
</CardContent>
</Card>
<DrawerLogs
isOpen={isDrawerOpen}
onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
}; };

View File

@@ -16,234 +16,244 @@ import { toast } from "sonner";
import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { type LogLine, parseLogs } from "../../docker/logs/utils";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
interface Props { interface Props {
mysqlId: string; mysqlId: string;
} }
export const ShowGeneralMysql = ({ mysqlId }: Props) => { export const ShowGeneralMysql = ({ mysqlId }: Props) => {
const { data, refetch } = api.mysql.one.useQuery( const { data, refetch } = api.mysql.one.useQuery(
{ {
mysqlId, mysqlId,
}, },
{ enabled: !!mysqlId } { enabled: !!mysqlId },
); );
const { mutateAsync: reload, isLoading: isReloading } = const { mutateAsync: reload, isLoading: isReloading } =
api.mysql.reload.useMutation(); api.mysql.reload.useMutation();
const { mutateAsync: start, isLoading: isStarting } = const { mutateAsync: start, isLoading: isStarting } =
api.mysql.start.useMutation(); api.mysql.start.useMutation();
const { mutateAsync: stop, isLoading: isStopping } = const { mutateAsync: stop, isLoading: isStopping } =
api.mysql.stop.useMutation(); api.mysql.stop.useMutation();
const [isDrawerOpen, setIsDrawerOpen] = useState(false); const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]); const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false); const [isDeploying, setIsDeploying] = useState(false);
api.mysql.deployWithLogs.useSubscription( api.mysql.deployWithLogs.useSubscription(
{ {
mysqlId: mysqlId, mysqlId: mysqlId,
}, },
{ {
enabled: isDeploying, enabled: isDeploying,
onData(log) { onData(log) {
if (!isDrawerOpen) { if (!isDrawerOpen) {
setIsDrawerOpen(true); setIsDrawerOpen(true);
} }
if (log === "Deployment completed successfully!") { if (log === "Deployment completed successfully!") {
setIsDeploying(false); setIsDeploying(false);
} }
const parsedLogs = parseLogs(log); const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]); setFilteredLogs((prev) => [...prev, ...parsedLogs]);
}, },
onError(error) { onError(error) {
console.error("Deployment logs error:", error); console.error("Deployment logs error:", error);
setIsDeploying(false); setIsDeploying(false);
}, },
} },
); );
return ( return (
<> <>
<div className="flex w-full flex-col gap-5 "> <div className="flex w-full flex-col gap-5 ">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader> <CardHeader>
<CardTitle className="text-xl">Deploy Settings</CardTitle> <CardTitle className="text-xl">Deploy Settings</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="flex flex-row gap-4 flex-wrap"> <CardContent className="flex flex-row gap-4 flex-wrap">
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>
<DialogAction <DialogAction
title="Deploy Mysql" title="Deploy MySQL"
description="Are you sure you want to deploy this mysql?" description="Are you sure you want to deploy this mysql?"
type="default" type="default"
onClick={async () => { onClick={async () => {
setIsDeploying(true); setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000));
refetch(); refetch();
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="default"
<Button isLoading={data?.applicationStatus === "running"}
variant="default" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={data?.applicationStatus === "running"} >
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<Rocket className="size-4 mr-1" /> <div className="flex items-center">
Deploy <Rocket className="size-4 mr-1" />
</Button> Deploy
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Downloads and sets up the MySQL database</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Downloads and sets up the MySQL database</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</DialogAction> </Tooltip>
<DialogAction </Button>
title="Reload Mysql" </DialogAction>
description="Are you sure you want to reload this mysql?" <DialogAction
type="default" title="Reload MySQL"
onClick={async () => { description="Are you sure you want to reload this mysql?"
await reload({ type="default"
mysqlId: mysqlId, onClick={async () => {
appName: data?.appName || "", await reload({
}) mysqlId: mysqlId,
.then(() => { appName: data?.appName || "",
toast.success("Mysql reloaded successfully"); })
refetch(); .then(() => {
}) toast.success("MySQL reloaded successfully");
.catch(() => { refetch();
toast.error("Error reloading Mysql"); })
}); .catch(() => {
}} toast.error("Error reloading MySQL");
> });
<Tooltip> }}
<TooltipTrigger asChild> >
<Button <Button
variant="secondary" variant="secondary"
isLoading={isReloading} isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
> >
<RefreshCcw className="size-4 mr-1" /> <Tooltip>
Reload <TooltipTrigger asChild>
</Button> <div className="flex items-center">
</TooltipTrigger> <RefreshCcw className="size-4 mr-1" />
<TooltipPrimitive.Portal> Reload
<TooltipContent sideOffset={5} className="z-[60]"> </div>
<p>Restart the MySQL service without rebuilding</p> </TooltipTrigger>
</TooltipContent> <TooltipPrimitive.Portal>
</TooltipPrimitive.Portal> <TooltipContent sideOffset={5} className="z-[60]">
</Tooltip> <p>Restart the MySQL service without rebuilding</p>
</DialogAction> </TooltipContent>
{data?.applicationStatus === "idle" ? ( </TooltipPrimitive.Portal>
<DialogAction </Tooltip>
title="Start Mysql" </Button>
description="Are you sure you want to start this mysql?" </DialogAction>
type="default" {data?.applicationStatus === "idle" ? (
onClick={async () => { <DialogAction
await start({ title="Start MySQL"
mysqlId: mysqlId, description="Are you sure you want to start this mysql?"
}) type="default"
.then(() => { onClick={async () => {
toast.success("Mysql started successfully"); await start({
refetch(); mysqlId: mysqlId,
}) })
.catch(() => { .then(() => {
toast.error("Error starting Mysql"); toast.success("MySQL started successfully");
}); refetch();
}} })
> .catch(() => {
<Tooltip> toast.error("Error starting MySQL");
<TooltipTrigger asChild> });
<Button }}
variant="secondary" >
isLoading={isStarting} <Button
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" variant="secondary"
> isLoading={isStarting}
<CheckCircle2 className="size-4 mr-1" /> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
Start >
</Button> <Tooltip>
</TooltipTrigger> <TooltipTrigger asChild>
<TooltipPrimitive.Portal> <div className="flex items-center">
<TooltipContent sideOffset={5} className="z-[60]"> <CheckCircle2 className="size-4 mr-1" />
<p> Start
Start the MySQL database (requires a previous </div>
successful setup) </TooltipTrigger>
</p> <TooltipPrimitive.Portal>
</TooltipContent> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipPrimitive.Portal> <p>
</Tooltip> Start the MySQL database (requires a previous
</DialogAction> successful setup)
) : ( </p>
<DialogAction </TooltipContent>
title="Stop Mysql" </TooltipPrimitive.Portal>
description="Are you sure you want to stop this mysql?" </Tooltip>
onClick={async () => { </Button>
await stop({ </DialogAction>
mysqlId: mysqlId, ) : (
}) <DialogAction
.then(() => { title="Stop MySQL"
toast.success("Mysql stopped successfully"); description="Are you sure you want to stop this mysql?"
refetch(); onClick={async () => {
}) await stop({
.catch(() => { mysqlId: mysqlId,
toast.error("Error stopping Mysql"); })
}); .then(() => {
}} toast.success("MySQL stopped successfully");
> refetch();
<Tooltip> })
<TooltipTrigger asChild> .catch(() => {
<Button toast.error("Error stopping MySQL");
variant="destructive" });
isLoading={isStopping} }}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" >
> <Button
<Ban className="size-4 mr-1" /> variant="destructive"
Stop isLoading={isStopping}
</Button> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
</TooltipTrigger> >
<TooltipPrimitive.Portal> <Tooltip>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipTrigger asChild>
<p>Stop the currently running MySQL database</p> <div className="flex items-center">
</TooltipContent> <Ban className="size-4 mr-1" />
</TooltipPrimitive.Portal> Stop
</Tooltip> </div>
</DialogAction> </TooltipTrigger>
)} <TooltipPrimitive.Portal>
</TooltipProvider> <TooltipContent sideOffset={5} className="z-[60]">
<DockerTerminalModal <p>Stop the currently running MySQL database</p>
appName={data?.appName || ""} </TooltipContent>
serverId={data?.serverId || ""} </TooltipPrimitive.Portal>
> </Tooltip>
<Tooltip> </Button>
<TooltipTrigger asChild> </DialogAction>
<Button )}
variant="outline" </TooltipProvider>
className="flex items-center gap-2 focus-visible:ring-2 focus-visible:ring-offset-2" <DockerTerminalModal
> appName={data?.appName || ""}
<Terminal className="size-4" /> serverId={data?.serverId || ""}
Open Terminal >
</Button> <Button
</TooltipTrigger> variant="outline"
<TooltipPrimitive.Portal> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
<TooltipContent sideOffset={5} className="z-[60]"> >
<p>Open a terminal to the MySQL container</p> <Tooltip>
</TooltipContent> <TooltipTrigger asChild>
</TooltipPrimitive.Portal> <div className="flex items-center">
</Tooltip> <Terminal className="size-4 mr-1" />
</DockerTerminalModal> Open Terminal
</CardContent> </div>
</Card> </TooltipTrigger>
<DrawerLogs <TooltipPrimitive.Portal>
isOpen={isDrawerOpen} <TooltipContent sideOffset={5} className="z-[60]">
onClose={() => { <p>Open a terminal to the MySQL container</p>
setIsDrawerOpen(false); </TooltipContent>
setFilteredLogs([]); </TooltipPrimitive.Portal>
setIsDeploying(false); </Tooltip>
refetch(); </Button>
}} </DockerTerminalModal>
filteredLogs={filteredLogs} </CardContent>
/> </Card>
</div> <DrawerLogs
</> isOpen={isDrawerOpen}
); onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
}; };

View File

@@ -74,7 +74,7 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
<CardContent className="flex flex-row gap-4 flex-wrap"> <CardContent className="flex flex-row gap-4 flex-wrap">
<TooltipProvider disableHoverableContent={false}> <TooltipProvider disableHoverableContent={false}>
<DialogAction <DialogAction
title="Deploy Postgres" title="Deploy PostgreSQL"
description="Are you sure you want to deploy this postgres?" description="Are you sure you want to deploy this postgres?"
type="default" type="default"
onClick={async () => { onClick={async () => {
@@ -83,26 +83,28 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
refetch(); refetch();
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="default"
<Button isLoading={data?.applicationStatus === "running"}
variant="default" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={data?.applicationStatus === "running"} >
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<Rocket className="size-4 mr-1" /> <div className="flex items-center">
Deploy <Rocket className="size-4 mr-1" />
</Button> Deploy
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Downloads and sets up the PostgreSQL database</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Downloads and sets up the PostgreSQL database</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
<DialogAction <DialogAction
title="Reload Postgres" title="Reload PostgreSQL"
description="Are you sure you want to reload this postgres?" description="Are you sure you want to reload this postgres?"
type="default" type="default"
onClick={async () => { onClick={async () => {
@@ -111,35 +113,37 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
appName: data?.appName || "", appName: data?.appName || "",
}) })
.then(() => { .then(() => {
toast.success("Postgres reloaded successfully"); toast.success("PostgreSQL reloaded successfully");
refetch(); refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error reloading Postgres"); toast.error("Error reloading PostgreSQL");
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="secondary"
<Button isLoading={isReloading}
variant="secondary" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={isReloading} >
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<RefreshCcw className="size-4 mr-1" /> <div className="flex items-center">
Reload <RefreshCcw className="size-4 mr-1" />
</Button> Reload
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Reload the PostgreSQL without rebuilding it</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Restart the PostgreSQL service without rebuilding</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
{data?.applicationStatus === "idle" ? ( {data?.applicationStatus === "idle" ? (
<DialogAction <DialogAction
title="Start Postgres" title="Start PostgreSQL"
description="Are you sure you want to start this postgres?" description="Are you sure you want to start this postgres?"
type="default" type="default"
onClick={async () => { onClick={async () => {
@@ -147,69 +151,73 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
postgresId: postgresId, postgresId: postgresId,
}) })
.then(() => { .then(() => {
toast.success("Postgres started successfully"); toast.success("PostgreSQL started successfully");
refetch(); refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error starting Postgres"); toast.error("Error starting PostgreSQL");
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="secondary"
<Button isLoading={isStarting}
variant="secondary" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={isStarting} >
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<CheckCircle2 className="size-4 mr-1" /> <div className="flex items-center">
Start <CheckCircle2 className="size-4 mr-1" />
</Button> Start
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p> <TooltipContent sideOffset={5} className="z-[60]">
Start the PostgreSQL database (requires a previous <p>
successful setup) Start the PostgreSQL database (requires a previous
</p> successful setup)
</TooltipContent> </p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
) : ( ) : (
<DialogAction <DialogAction
title="Stop Postgres" title="Stop PostgreSQL"
description="Are you sure you want to stop this postgres?" description="Are you sure you want to stop this postgres?"
onClick={async () => { onClick={async () => {
await stop({ await stop({
postgresId: postgresId, postgresId: postgresId,
}) })
.then(() => { .then(() => {
toast.success("Postgres stopped successfully"); toast.success("PostgreSQL stopped successfully");
refetch(); refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error stopping Postgres"); toast.error("Error stopping PostgreSQL");
}); });
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="destructive"
<Button isLoading={isStopping}
variant="destructive" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={isStopping} >
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<Ban className="size-4 mr-1" /> <div className="flex items-center">
Stop <Ban className="size-4 mr-1" />
</Button> Stop
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Stop the currently running PostgreSQL database</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Stop the currently running PostgreSQL database</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction> </DialogAction>
)} )}
</TooltipProvider> </TooltipProvider>
@@ -221,8 +229,19 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
variant="outline" variant="outline"
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
> >
<Terminal className="size-4 mr-1" /> <Tooltip>
Open Terminal <TooltipTrigger asChild>
<div className="flex items-center">
<Terminal className="size-4 mr-1" />
Open Terminal
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Open a terminal to the PostgreSQL container</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button> </Button>
</DockerTerminalModal> </DockerTerminalModal>
</CardContent> </CardContent>

View File

@@ -17,235 +17,245 @@ import { type LogLine, parseLogs } from "../../docker/logs/utils";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
interface Props { interface Props {
redisId: string; redisId: string;
} }
export const ShowGeneralRedis = ({ redisId }: Props) => { export const ShowGeneralRedis = ({ redisId }: Props) => {
const { data, refetch } = api.redis.one.useQuery( const { data, refetch } = api.redis.one.useQuery(
{ {
redisId, redisId,
}, },
{ enabled: !!redisId } { enabled: !!redisId },
); );
const { mutateAsync: reload, isLoading: isReloading } = const { mutateAsync: reload, isLoading: isReloading } =
api.redis.reload.useMutation(); api.redis.reload.useMutation();
const { mutateAsync: start, isLoading: isStarting } = const { mutateAsync: start, isLoading: isStarting } =
api.redis.start.useMutation(); api.redis.start.useMutation();
const { mutateAsync: stop, isLoading: isStopping } = const { mutateAsync: stop, isLoading: isStopping } =
api.redis.stop.useMutation(); api.redis.stop.useMutation();
const [isDrawerOpen, setIsDrawerOpen] = useState(false); const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]); const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false); const [isDeploying, setIsDeploying] = useState(false);
api.redis.deployWithLogs.useSubscription( api.redis.deployWithLogs.useSubscription(
{ {
redisId: redisId, redisId: redisId,
}, },
{ {
enabled: isDeploying, enabled: isDeploying,
onData(log) { onData(log) {
if (!isDrawerOpen) { if (!isDrawerOpen) {
setIsDrawerOpen(true); setIsDrawerOpen(true);
} }
if (log === "Deployment completed successfully!") { if (log === "Deployment completed successfully!") {
setIsDeploying(false); setIsDeploying(false);
} }
const parsedLogs = parseLogs(log); const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]); setFilteredLogs((prev) => [...prev, ...parsedLogs]);
}, },
onError(error) { onError(error) {
console.error("Deployment logs error:", error); console.error("Deployment logs error:", error);
setIsDeploying(false); setIsDeploying(false);
}, },
} },
); );
return ( return (
<> <>
<div className="flex w-full flex-col gap-5 "> <div className="flex w-full flex-col gap-5 ">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader> <CardHeader>
<CardTitle className="text-xl">Deploy Settings</CardTitle> <CardTitle className="text-xl">Deploy Settings</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="flex flex-row gap-4 flex-wrap"> <CardContent className="flex flex-row gap-4 flex-wrap">
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>
<DialogAction <DialogAction
title="Deploy Redis" title="Deploy Redis"
description="Are you sure you want to deploy this redis?" description="Are you sure you want to deploy this redis?"
type="default" type="default"
onClick={async () => { onClick={async () => {
setIsDeploying(true); setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000));
refetch(); refetch();
}} }}
> >
<Tooltip> <Button
<TooltipTrigger asChild> variant="default"
<Button isLoading={data?.applicationStatus === "running"}
variant="default" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
isLoading={data?.applicationStatus === "running"} >
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" <Tooltip>
> <TooltipTrigger asChild>
<Rocket className="size-4 mr-1" /> <div className="flex items-center">
Deploy <Rocket className="size-4 mr-1" />
</Button> Deploy
</TooltipTrigger> </div>
<TooltipPrimitive.Portal> </TooltipTrigger>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipPrimitive.Portal>
<p>Downloads and sets up the Redis database</p> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipContent> <p>Downloads and sets up the Redis database</p>
</TooltipPrimitive.Portal> </TooltipContent>
</Tooltip> </TooltipPrimitive.Portal>
</DialogAction> </Tooltip>
<DialogAction </Button>
title="Reload Redis" </DialogAction>
description="Are you sure you want to reload this redis?" <DialogAction
type="default" title="Reload Redis"
onClick={async () => { description="Are you sure you want to reload this redis?"
await reload({ type="default"
redisId: redisId, onClick={async () => {
appName: data?.appName || "", await reload({
}) redisId: redisId,
.then(() => { appName: data?.appName || "",
toast.success("Redis reloaded successfully"); })
refetch(); .then(() => {
}) toast.success("Redis reloaded successfully");
.catch(() => { refetch();
toast.error("Error reloading Redis"); })
}); .catch(() => {
}} toast.error("Error reloading Redis");
> });
<Tooltip> }}
<TooltipTrigger asChild> >
<Button <Button
variant="secondary" variant="secondary"
isLoading={isReloading} isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
> >
<RefreshCcw className="size-4 mr-1" /> <Tooltip>
Reload <TooltipTrigger asChild>
</Button> <div className="flex items-center">
</TooltipTrigger> <RefreshCcw className="size-4 mr-1" />
<TooltipPrimitive.Portal> Reload
<TooltipContent sideOffset={5} className="z-[60]"> </div>
<p>Restart the Redis service without rebuilding</p> </TooltipTrigger>
</TooltipContent> <TooltipPrimitive.Portal>
</TooltipPrimitive.Portal> <TooltipContent sideOffset={5} className="z-[60]">
</Tooltip> <p>Restart the Redis service without rebuilding</p>
</DialogAction> </TooltipContent>
{data?.applicationStatus === "idle" ? ( </TooltipPrimitive.Portal>
<DialogAction </Tooltip>
title="Start Redis" </Button>
description="Are you sure you want to start this redis?" </DialogAction>
type="default" {data?.applicationStatus === "idle" ? (
onClick={async () => { <DialogAction
await start({ title="Start Redis"
redisId: redisId, description="Are you sure you want to start this redis?"
}) type="default"
.then(() => { onClick={async () => {
toast.success("Redis started successfully"); await start({
refetch(); redisId: redisId,
}) })
.catch(() => { .then(() => {
toast.error("Error starting Redis"); toast.success("Redis started successfully");
}); refetch();
}} })
> .catch(() => {
<Tooltip> toast.error("Error starting Redis");
<TooltipTrigger asChild> });
<Button }}
variant="secondary" >
isLoading={isStarting} <Button
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" variant="secondary"
> isLoading={isStarting}
<CheckCircle2 className="size-4 mr-1" /> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
Start >
</Button> <Tooltip>
</TooltipTrigger> <TooltipTrigger asChild>
<TooltipPrimitive.Portal> <div className="flex items-center">
<TooltipContent sideOffset={5} className="z-[60]"> <CheckCircle2 className="size-4 mr-1" />
<p> Start
Start the Redis database (requires a previous </div>
successful setup) </TooltipTrigger>
</p> <TooltipPrimitive.Portal>
</TooltipContent> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipPrimitive.Portal> <p>
</Tooltip> Start the Redis database (requires a previous
</DialogAction> successful setup)
) : ( </p>
<DialogAction </TooltipContent>
title="Stop Redis" </TooltipPrimitive.Portal>
description="Are you sure you want to stop this redis?" </Tooltip>
onClick={async () => { </Button>
await stop({ </DialogAction>
redisId: redisId, ) : (
}) <DialogAction
.then(() => { title="Stop Redis"
toast.success("Redis stopped successfully"); description="Are you sure you want to stop this redis?"
refetch(); onClick={async () => {
}) await stop({
.catch(() => { redisId: redisId,
toast.error("Error stopping Redis"); })
}); .then(() => {
}} toast.success("Redis stopped successfully");
> refetch();
<Tooltip> })
<TooltipTrigger asChild> .catch(() => {
<Button toast.error("Error stopping Redis");
variant="destructive" });
isLoading={isStopping} }}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" >
> <Button
<Ban className="size-4 mr-1" /> variant="destructive"
Stop isLoading={isStopping}
</Button> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
</TooltipTrigger> >
<TooltipPrimitive.Portal> <Tooltip>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipTrigger asChild>
<p>Stop the currently running Redis database</p> <div className="flex items-center">
</TooltipContent> <Ban className="size-4 mr-1" />
</TooltipPrimitive.Portal> Stop
</Tooltip> </div>
</DialogAction> </TooltipTrigger>
)} <TooltipPrimitive.Portal>
</TooltipProvider> <TooltipContent sideOffset={5} className="z-[60]">
<DockerTerminalModal <p>Stop the currently running Redis database</p>
appName={data?.appName || ""} </TooltipContent>
serverId={data?.serverId || ""} </TooltipPrimitive.Portal>
> </Tooltip>
<Tooltip> </Button>
<TooltipTrigger asChild> </DialogAction>
<Button )}
variant="outline" </TooltipProvider>
className="flex items-center gap-2 focus-visible:ring-2 focus-visible:ring-offset-2" <DockerTerminalModal
> appName={data?.appName || ""}
<Terminal className="size-4" /> serverId={data?.serverId || ""}
Open Terminal >
</Button> <Button
</TooltipTrigger> variant="outline"
<TooltipPrimitive.Portal> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
<TooltipContent sideOffset={5} className="z-[60]"> >
<p>Open a terminal to the Redis container</p> <Tooltip>
</TooltipContent> <TooltipTrigger asChild>
</TooltipPrimitive.Portal> <div className="flex items-center">
</Tooltip> <Terminal className="size-4 mr-1" />
</DockerTerminalModal> Open Terminal
</CardContent> </div>
</Card> </TooltipTrigger>
<DrawerLogs <TooltipPrimitive.Portal>
isOpen={isDrawerOpen} <TooltipContent sideOffset={5} className="z-[60]">
onClose={() => { <p>Open a terminal to the Redis container</p>
setIsDrawerOpen(false); </TooltipContent>
setFilteredLogs([]); </TooltipPrimitive.Portal>
setIsDeploying(false); </Tooltip>
refetch(); </Button>
}} </DockerTerminalModal>
filteredLogs={filteredLogs} </CardContent>
/> </Card>
</div> <DrawerLogs
</> isOpen={isDrawerOpen}
); onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
}; };