refactor: standardize code formatting and improve component structure across dashboard components

- Updated component props formatting for consistency.
- Refactored API query hooks and mutation calls for better readability.
- Enhanced tooltip descriptions for clarity in user actions.
- Maintained functionality for deploying, reloading, starting, and stopping applications, composes, and Postgres instances.
This commit is contained in:
Mauricio Siu
2025-03-16 19:50:04 -06:00
parent e97c8f42b3
commit 2405e5a93a
3 changed files with 694 additions and 703 deletions

View File

@@ -24,290 +24,287 @@ import { useRouter } from "next/router";
import { toast } from "sonner"; import { toast } from "sonner";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
interface Props { interface Props {
applicationId: string; applicationId: string;
} }
export const ShowGeneralApplication = ({ applicationId }: Props) => { export const ShowGeneralApplication = ({ applicationId }: Props) => {
const router = useRouter(); const router = useRouter();
const { data, refetch } = api.application.one.useQuery( const { data, refetch } = api.application.one.useQuery(
{ {
applicationId, applicationId,
}, },
{ enabled: !!applicationId } { enabled: !!applicationId },
); );
const { mutateAsync: update } = api.application.update.useMutation(); const { mutateAsync: update } = api.application.update.useMutation();
const { mutateAsync: start, isLoading: isStarting } = const { mutateAsync: start, isLoading: isStarting } =
api.application.start.useMutation(); api.application.start.useMutation();
const { mutateAsync: stop, isLoading: isStopping } = const { mutateAsync: stop, isLoading: isStopping } =
api.application.stop.useMutation(); api.application.stop.useMutation();
const { mutateAsync: deploy } = api.application.deploy.useMutation(); const { mutateAsync: deploy } = api.application.deploy.useMutation();
const { mutateAsync: reload, isLoading: isReloading } = const { mutateAsync: reload, isLoading: isReloading } =
api.application.reload.useMutation(); api.application.reload.useMutation();
const { mutateAsync: redeploy } = api.application.redeploy.useMutation(); const { mutateAsync: redeploy } = api.application.redeploy.useMutation();
return ( return (
<> <>
<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} disableHoverableContent={false}> <TooltipProvider delayDuration={0} disableHoverableContent={false}>
<DialogAction <DialogAction
title="Deploy Application" title="Deploy Application"
description="Are you sure you want to deploy this application?" description="Are you sure you want to deploy this application?"
type="default" type="default"
onClick={async () => { onClick={async () => {
await deploy({ await deploy({
applicationId: applicationId, applicationId: applicationId,
}) })
.then(() => { .then(() => {
toast.success("Application deployed successfully"); toast.success("Application deployed successfully");
refetch(); refetch();
router.push( router.push(
`/dashboard/project/${data?.projectId}/services/application/${applicationId}?tab=deployments` `/dashboard/project/${data?.projectId}/services/application/${applicationId}?tab=deployments`,
); );
}) })
.catch(() => { .catch(() => {
toast.error("Error deploying application"); toast.error("Error deploying application");
}); });
}} }}
> >
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant="default" variant="default"
isLoading={data?.applicationStatus === "running"} isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
> >
<Rocket className="size-4 mr-1" /> <Rocket className="size-4 mr-1" />
Deploy Deploy
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipPrimitive.Portal> <TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipContent sideOffset={5} className="z-[60]">
<p> <p>
Downloads the source code and performs a complete build Downloads the source code and performs a complete build
</p> </p>
</TooltipContent> </TooltipContent>
</TooltipPrimitive.Portal> </TooltipPrimitive.Portal>
</Tooltip> </Tooltip>
</DialogAction> </DialogAction>
<DialogAction <DialogAction
title="Reload Application" title="Reload Application"
description="Are you sure you want to reload this application?" description="Are you sure you want to reload this application?"
type="default" type="default"
onClick={async () => { onClick={async () => {
await reload({ await reload({
applicationId: applicationId, applicationId: applicationId,
appName: data?.appName || "", appName: data?.appName || "",
}) })
.then(() => { .then(() => {
toast.success("Application reloaded successfully"); toast.success("Application reloaded successfully");
refetch(); refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error reloading application"); toast.error("Error reloading application");
}); });
}} }}
> >
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant="secondary" variant="secondary"
isLoading={isReloading} isLoading={isReloading}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
> >
<RefreshCcw className="size-4 mr-1" /> <RefreshCcw className="size-4 mr-1" />
Reload Reload
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipPrimitive.Portal> <TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipContent sideOffset={5} className="z-[60]">
<p> <p>Reload the application without rebuilding it</p>
Reload the application when you change configuration or </TooltipContent>
environment variables </TooltipPrimitive.Portal>
</p> </Tooltip>
</TooltipContent> </DialogAction>
</TooltipPrimitive.Portal> <DialogAction
</Tooltip> title="Rebuild Application"
</DialogAction> description="Are you sure you want to rebuild this application?"
<DialogAction type="default"
title="Rebuild Application" onClick={async () => {
description="Are you sure you want to rebuild this application?" await redeploy({
type="default" applicationId: applicationId,
onClick={async () => { })
await redeploy({ .then(() => {
applicationId: applicationId, toast.success("Application rebuilt successfully");
}) refetch();
.then(() => { })
toast.success("Application rebuilt successfully"); .catch(() => {
refetch(); toast.error("Error rebuilding application");
}) });
.catch(() => { }}
toast.error("Error rebuilding application"); >
}); <Tooltip>
}} <TooltipTrigger asChild>
> <Button
<Tooltip> variant="secondary"
<TooltipTrigger asChild> isLoading={data?.applicationStatus === "running"}
<Button className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
variant="secondary" >
isLoading={data?.applicationStatus === "running"} <Hammer className="size-4 mr-1" />
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" Rebuild
> </Button>
<Hammer className="size-4 mr-1" /> </TooltipTrigger>
Rebuild <TooltipPrimitive.Portal>
</Button> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipTrigger> <p>
<TooltipPrimitive.Portal> Only rebuilds the application without downloading new code
<TooltipContent sideOffset={5} className="z-[60]"> </p>
<p> </TooltipContent>
Only rebuilds the application without downloading new code </TooltipPrimitive.Portal>
</p> </Tooltip>
</TooltipContent> </DialogAction>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
{data?.applicationStatus === "idle" ? ( {data?.applicationStatus === "idle" ? (
<DialogAction <DialogAction
title="Start Application" title="Start Application"
description="Are you sure you want to start this application?" description="Are you sure you want to start this application?"
type="default" type="default"
onClick={async () => { onClick={async () => {
await start({ await start({
applicationId: applicationId, applicationId: applicationId,
}) })
.then(() => { .then(() => {
toast.success("Application started successfully"); toast.success("Application started successfully");
refetch(); refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error starting application"); toast.error("Error starting application");
}); });
}} }}
> >
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant="secondary" variant="secondary"
isLoading={isStarting} isLoading={isStarting}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
> >
<CheckCircle2 className="size-4 mr-1" /> <CheckCircle2 className="size-4 mr-1" />
Start Start
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipPrimitive.Portal> <TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipContent sideOffset={5} className="z-[60]">
<p> <p>
Start the application (requires a previous successful Start the application (requires a previous successful
build) build)
</p> </p>
</TooltipContent> </TooltipContent>
</TooltipPrimitive.Portal> </TooltipPrimitive.Portal>
</Tooltip> </Tooltip>
</DialogAction> </DialogAction>
) : ( ) : (
<DialogAction <DialogAction
title="Stop Application" title="Stop Application"
description="Are you sure you want to stop this application?" description="Are you sure you want to stop this application?"
onClick={async () => { onClick={async () => {
await stop({ await stop({
applicationId: applicationId, applicationId: applicationId,
}) })
.then(() => { .then(() => {
toast.success("Application stopped successfully"); toast.success("Application stopped successfully");
refetch(); refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error stopping application"); toast.error("Error stopping application");
}); });
}} }}
> >
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant="destructive" variant="destructive"
isLoading={isStopping} isLoading={isStopping}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
> >
<Ban className="size-4 mr-1" /> <Ban className="size-4 mr-1" />
Stop Stop
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipPrimitive.Portal> <TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running application</p> <p>Stop the currently running application</p>
</TooltipContent> </TooltipContent>
</TooltipPrimitive.Portal> </TooltipPrimitive.Portal>
</Tooltip> </Tooltip>
</DialogAction> </DialogAction>
)} )}
</TooltipProvider> </TooltipProvider>
<DockerTerminalModal <DockerTerminalModal
appName={data?.appName || ""} appName={data?.appName || ""}
serverId={data?.serverId || ""} serverId={data?.serverId || ""}
> >
<Button <Button
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" /> <Terminal className="size-4 mr-1" />
Open Terminal Open Terminal
</Button> </Button>
</DockerTerminalModal> </DockerTerminalModal>
<div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border"> <div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border">
<span className="text-sm font-medium">Autodeploy</span> <span className="text-sm font-medium">Autodeploy</span>
<Switch <Switch
aria-label="Toggle autodeploy" aria-label="Toggle autodeploy"
checked={data?.autoDeploy || false} checked={data?.autoDeploy || false}
onCheckedChange={async (enabled) => { onCheckedChange={async (enabled) => {
await update({ await update({
applicationId, applicationId,
autoDeploy: enabled, autoDeploy: enabled,
}) })
.then(async () => { .then(async () => {
toast.success("Auto Deploy Updated"); toast.success("Auto Deploy Updated");
await refetch(); await refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error updating Auto Deploy"); toast.error("Error updating Auto Deploy");
}); });
}} }}
className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary" className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary"
/> />
</div> </div>
<div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border"> <div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border">
<span className="text-sm font-medium">Clean Cache</span> <span className="text-sm font-medium">Clean Cache</span>
<Switch <Switch
aria-label="Toggle clean cache" aria-label="Toggle clean cache"
checked={data?.cleanCache || false} checked={data?.cleanCache || false}
onCheckedChange={async (enabled) => { onCheckedChange={async (enabled) => {
await update({ await update({
applicationId, applicationId,
cleanCache: enabled, cleanCache: enabled,
}) })
.then(async () => { .then(async () => {
toast.success("Clean Cache Updated"); toast.success("Clean Cache Updated");
await refetch(); await refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error updating Clean Cache"); toast.error("Error updating Clean Cache");
}); });
}} }}
className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary" className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary"
/> />
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
<ShowProviderForm applicationId={applicationId} /> <ShowProviderForm applicationId={applicationId} />
<ShowBuildChooseForm applicationId={applicationId} /> <ShowBuildChooseForm applicationId={applicationId} />
</> </>
); );
}; };

View File

@@ -15,211 +15,208 @@ import { toast } from "sonner";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
interface Props { interface Props {
composeId: string; composeId: string;
} }
export const ComposeActions = ({ composeId }: Props) => { export const ComposeActions = ({ composeId }: Props) => {
const router = useRouter(); const router = useRouter();
const { data, refetch } = api.compose.one.useQuery( const { data, refetch } = api.compose.one.useQuery(
{ {
composeId, composeId,
}, },
{ enabled: !!composeId } { enabled: !!composeId },
); );
const { mutateAsync: update } = api.compose.update.useMutation(); const { mutateAsync: update } = api.compose.update.useMutation();
const { mutateAsync: deploy } = api.compose.deploy.useMutation(); const { mutateAsync: deploy } = api.compose.deploy.useMutation();
const { mutateAsync: redeploy } = api.compose.redeploy.useMutation(); const { mutateAsync: redeploy } = api.compose.redeploy.useMutation();
const { mutateAsync: start, isLoading: isStarting } = const { mutateAsync: start, isLoading: isStarting } =
api.compose.start.useMutation(); api.compose.start.useMutation();
const { mutateAsync: stop, isLoading: isStopping } = const { mutateAsync: stop, isLoading: isStopping } =
api.compose.stop.useMutation(); api.compose.stop.useMutation();
return ( return (
<div className="flex flex-row gap-4 w-full flex-wrap "> <div className="flex flex-row gap-4 w-full flex-wrap ">
<TooltipProvider delayDuration={0} disableHoverableContent={false}> <TooltipProvider delayDuration={0} disableHoverableContent={false}>
<DialogAction <DialogAction
title="Deploy Compose" title="Deploy Compose"
description="Are you sure you want to deploy this compose?" description="Are you sure you want to deploy this compose?"
type="default" type="default"
onClick={async () => { onClick={async () => {
await deploy({ await deploy({
composeId: composeId, composeId: composeId,
}) })
.then(() => { .then(() => {
toast.success("Compose deployed successfully"); toast.success("Compose deployed successfully");
refetch(); refetch();
router.push( router.push(
`/dashboard/project/${data?.project.projectId}/services/compose/${composeId}?tab=deployments` `/dashboard/project/${data?.project.projectId}/services/compose/${composeId}?tab=deployments`,
); );
}) })
.catch(() => { .catch(() => {
toast.error("Error deploying compose"); toast.error("Error deploying compose");
}); });
}} }}
> >
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant="default" variant="default"
isLoading={data?.composeStatus === "running"} isLoading={data?.composeStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
> >
<Rocket className="size-4 mr-1" /> <Rocket className="size-4 mr-1" />
Deploy Deploy
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipPrimitive.Portal> <TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads the source code and performs a complete build</p> <p>Downloads the source code and performs a complete build</p>
</TooltipContent> </TooltipContent>
</TooltipPrimitive.Portal> </TooltipPrimitive.Portal>
</Tooltip> </Tooltip>
</DialogAction> </DialogAction>
<DialogAction <DialogAction
title="Reload Compose" title="Reload Compose"
description="Are you sure you want to reload this compose?" description="Are you sure you want to reload this compose?"
type="default" type="default"
onClick={async () => { onClick={async () => {
await redeploy({ await redeploy({
composeId: composeId, composeId: composeId,
}) })
.then(() => { .then(() => {
toast.success("Compose reloaded successfully"); toast.success("Compose reloaded successfully");
refetch(); refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error reloading compose"); toast.error("Error reloading compose");
}); });
}} }}
> >
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant="secondary" variant="secondary"
isLoading={data?.composeStatus === "running"} isLoading={data?.composeStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
> >
<RefreshCcw className="size-4 mr-1" /> <RefreshCcw className="size-4 mr-1" />
Reload Reload
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipPrimitive.Portal> <TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipContent sideOffset={5} className="z-[60]">
<p> <p>Reload the compose without rebuilding it</p>
Reload the compose when you change configuration or </TooltipContent>
environment variables </TooltipPrimitive.Portal>
</p> </Tooltip>
</TooltipContent> </DialogAction>
</TooltipPrimitive.Portal> {data?.composeType === "docker-compose" &&
</Tooltip> data?.composeStatus === "idle" ? (
</DialogAction> <DialogAction
{data?.composeType === "docker-compose" && title="Start Compose"
data?.composeStatus === "idle" ? ( description="Are you sure you want to start this compose?"
<DialogAction type="default"
title="Start Compose" onClick={async () => {
description="Are you sure you want to start this compose?" await start({
type="default" composeId: composeId,
onClick={async () => { })
await start({ .then(() => {
composeId: composeId, toast.success("Compose started successfully");
}) refetch();
.then(() => { })
toast.success("Compose started successfully"); .catch(() => {
refetch(); toast.error("Error starting compose");
}) });
.catch(() => { }}
toast.error("Error starting compose"); >
}); <Tooltip>
}} <TooltipTrigger asChild>
> <Button
<Tooltip> variant="secondary"
<TooltipTrigger asChild> isLoading={isStarting}
<Button className="flex items-center gap-1.5 group 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 group focus-visible:ring-2 focus-visible:ring-offset-2" Start
> </Button>
<CheckCircle2 className="size-4 mr-1" /> </TooltipTrigger>
Start <TooltipPrimitive.Portal>
</Button> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipTrigger> <p>
<TooltipPrimitive.Portal> Start the compose (requires a previous successful build)
<TooltipContent sideOffset={5} className="z-[60]"> </p>
<p> </TooltipContent>
Start the compose (requires a previous successful build) </TooltipPrimitive.Portal>
</p> </Tooltip>
</TooltipContent> </DialogAction>
</TooltipPrimitive.Portal> ) : (
</Tooltip> <DialogAction
</DialogAction> title="Stop Compose"
) : ( description="Are you sure you want to stop this compose?"
<DialogAction onClick={async () => {
title="Stop Compose" await stop({
description="Are you sure you want to stop this compose?" composeId: composeId,
onClick={async () => { })
await stop({ .then(() => {
composeId: composeId, toast.success("Compose stopped successfully");
}) refetch();
.then(() => { })
toast.success("Compose stopped successfully"); .catch(() => {
refetch(); toast.error("Error stopping compose");
}) });
.catch(() => { }}
toast.error("Error stopping compose"); >
}); <Tooltip>
}} <TooltipTrigger asChild>
> <Button
<Tooltip> variant="destructive"
<TooltipTrigger asChild> isLoading={isStopping}
<Button className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
variant="destructive" >
isLoading={isStopping} <Ban className="size-4 mr-1" />
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2" Stop
> </Button>
<Ban className="size-4 mr-1" /> </TooltipTrigger>
Stop <TooltipPrimitive.Portal>
</Button> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipTrigger> <p>Stop the currently running compose</p>
<TooltipPrimitive.Portal> </TooltipContent>
<TooltipContent sideOffset={5} className="z-[60]"> </TooltipPrimitive.Portal>
<p>Stop the currently running compose</p> </Tooltip>
</TooltipContent> </DialogAction>
</TooltipPrimitive.Portal> )}
</Tooltip> </TooltipProvider>
</DialogAction> <DockerTerminalModal
)} appName={data?.appName || ""}
</TooltipProvider> serverId={data?.serverId || ""}
<DockerTerminalModal >
appName={data?.appName || ""} <Button
serverId={data?.serverId || ""} variant="outline"
> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
<Button >
variant="outline" <Terminal className="size-4 mr-1" />
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" Open Terminal
> </Button>
<Terminal className="size-4 mr-1" /> </DockerTerminalModal>
Open Terminal <div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border">
</Button> <span className="text-sm font-medium">Autodeploy</span>
</DockerTerminalModal> <Switch
<div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border"> aria-label="Toggle autodeploy"
<span className="text-sm font-medium">Autodeploy</span> checked={data?.autoDeploy || false}
<Switch onCheckedChange={async (enabled) => {
aria-label="Toggle autodeploy" await update({
checked={data?.autoDeploy || false} composeId,
onCheckedChange={async (enabled) => { autoDeploy: enabled,
await update({ })
composeId, .then(async () => {
autoDeploy: enabled, toast.success("Auto Deploy Updated");
}) await refetch();
.then(async () => { })
toast.success("Auto Deploy Updated"); .catch(() => {
await refetch(); toast.error("Error updating Auto Deploy");
}) });
.catch(() => { }}
toast.error("Error updating Auto Deploy"); className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary"
}); />
}} </div>
className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary" </div>
/> );
</div>
</div>
);
}; };

View File

@@ -3,10 +3,10 @@ import { DrawerLogs } from "@/components/shared/drawer-logs";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
TooltipProvider, TooltipProvider,
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import * as TooltipPrimitive from "@radix-ui/react-tooltip";
@@ -17,230 +17,227 @@ 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 {
postgresId: string; postgresId: string;
} }
export const ShowGeneralPostgres = ({ postgresId }: Props) => { export const ShowGeneralPostgres = ({ postgresId }: Props) => {
const { data, refetch } = api.postgres.one.useQuery( const { data, refetch } = api.postgres.one.useQuery(
{ {
postgresId: postgresId, postgresId: postgresId,
}, },
{ enabled: !!postgresId } { enabled: !!postgresId },
); );
const { mutateAsync: reload, isLoading: isReloading } = const { mutateAsync: reload, isLoading: isReloading } =
api.postgres.reload.useMutation(); api.postgres.reload.useMutation();
const { mutateAsync: stop, isLoading: isStopping } = const { mutateAsync: stop, isLoading: isStopping } =
api.postgres.stop.useMutation(); api.postgres.stop.useMutation();
const { mutateAsync: start, isLoading: isStarting } = const { mutateAsync: start, isLoading: isStarting } =
api.postgres.start.useMutation(); api.postgres.start.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.postgres.deployWithLogs.useSubscription( api.postgres.deployWithLogs.useSubscription(
{ {
postgresId: postgresId, postgresId: postgresId,
}, },
{ {
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 disableHoverableContent={false}> <TooltipProvider disableHoverableContent={false}>
<DialogAction <DialogAction
title="Deploy Postgres" title="Deploy Postgres"
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 () => {
setIsDeploying(true); setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000));
refetch(); refetch();
}} }}
> >
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant="default" variant="default"
isLoading={data?.applicationStatus === "running"} isLoading={data?.applicationStatus === "running"}
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"
> >
<Rocket className="size-4 mr-1" /> <Rocket className="size-4 mr-1" />
Deploy Deploy
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipPrimitive.Portal> <TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the PostgreSQL database</p> <p>Downloads and sets up the PostgreSQL database</p>
</TooltipContent> </TooltipContent>
</TooltipPrimitive.Portal> </TooltipPrimitive.Portal>
</Tooltip> </Tooltip>
</DialogAction> </DialogAction>
<DialogAction <DialogAction
title="Reload Postgres" title="Reload Postgres"
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 () => {
await reload({ await reload({
postgresId: postgresId, postgresId: postgresId,
appName: data?.appName || "", appName: data?.appName || "",
}) })
.then(() => { .then(() => {
toast.success("Postgres reloaded successfully"); toast.success("Postgres reloaded successfully");
refetch(); refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error reloading Postgres"); toast.error("Error reloading Postgres");
}); });
}} }}
> >
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <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" /> <RefreshCcw className="size-4 mr-1" />
Reload Reload
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipPrimitive.Portal> <TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]"> <TooltipContent sideOffset={5} className="z-[60]">
<p> <p>Reload the PostgreSQL without rebuilding it</p>
Reload the PostgreSQL when you change configuration or </TooltipContent>
environment variables </TooltipPrimitive.Portal>
</p> </Tooltip>
</TooltipContent> </DialogAction>
</TooltipPrimitive.Portal> {data?.applicationStatus === "idle" ? (
</Tooltip> <DialogAction
</DialogAction> title="Start Postgres"
{data?.applicationStatus === "idle" ? ( description="Are you sure you want to start this postgres?"
<DialogAction type="default"
title="Start Postgres" onClick={async () => {
description="Are you sure you want to start this postgres?" await start({
type="default" postgresId: postgresId,
onClick={async () => { })
await start({ .then(() => {
postgresId: postgresId, toast.success("Postgres started successfully");
}) refetch();
.then(() => { })
toast.success("Postgres started successfully"); .catch(() => {
refetch(); toast.error("Error starting Postgres");
}) });
.catch(() => { }}
toast.error("Error starting Postgres"); >
}); <Tooltip>
}} <TooltipTrigger asChild>
> <Button
<Tooltip> variant="secondary"
<TooltipTrigger asChild> 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>
<CheckCircle2 className="size-4 mr-1" /> </TooltipTrigger>
Start <TooltipPrimitive.Portal>
</Button> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipTrigger> <p>
<TooltipPrimitive.Portal> Start the PostgreSQL database (requires a previous
<TooltipContent sideOffset={5} className="z-[60]"> successful setup)
<p> </p>
Start the PostgreSQL database (requires a previous </TooltipContent>
successful setup) </TooltipPrimitive.Portal>
</p> </Tooltip>
</TooltipContent> </DialogAction>
</TooltipPrimitive.Portal> ) : (
</Tooltip> <DialogAction
</DialogAction> title="Stop Postgres"
) : ( description="Are you sure you want to stop this postgres?"
<DialogAction onClick={async () => {
title="Stop Postgres" await stop({
description="Are you sure you want to stop this postgres?" postgresId: postgresId,
onClick={async () => { })
await stop({ .then(() => {
postgresId: postgresId, toast.success("Postgres stopped successfully");
}) refetch();
.then(() => { })
toast.success("Postgres stopped successfully"); .catch(() => {
refetch(); toast.error("Error stopping Postgres");
}) });
.catch(() => { }}
toast.error("Error stopping Postgres"); >
}); <Tooltip>
}} <TooltipTrigger asChild>
> <Button
<Tooltip> variant="destructive"
<TooltipTrigger asChild> isLoading={isStopping}
<Button className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
variant="destructive" >
isLoading={isStopping} <Ban className="size-4 mr-1" />
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" Stop
> </Button>
<Ban className="size-4 mr-1" /> </TooltipTrigger>
Stop <TooltipPrimitive.Portal>
</Button> <TooltipContent sideOffset={5} className="z-[60]">
</TooltipTrigger> <p>Stop the currently running PostgreSQL database</p>
<TooltipPrimitive.Portal> </TooltipContent>
<TooltipContent sideOffset={5} className="z-[60]"> </TooltipPrimitive.Portal>
<p>Stop the currently running PostgreSQL database</p> </Tooltip>
</TooltipContent> </DialogAction>
</TooltipPrimitive.Portal> )}
</Tooltip> </TooltipProvider>
</DialogAction> <DockerTerminalModal
)} appName={data?.appName || ""}
</TooltipProvider> serverId={data?.serverId || ""}
<DockerTerminalModal >
appName={data?.appName || ""} <Button
serverId={data?.serverId || ""} variant="outline"
> className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
<Button >
variant="outline" <Terminal className="size-4 mr-1" />
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" Open Terminal
> </Button>
<Terminal className="size-4 mr-1" /> </DockerTerminalModal>
Open Terminal </CardContent>
</Button> </Card>
</DockerTerminalModal> <DrawerLogs
</CardContent> isOpen={isDrawerOpen}
</Card> onClose={() => {
<DrawerLogs setIsDrawerOpen(false);
isOpen={isDrawerOpen} setFilteredLogs([]);
onClose={() => { setIsDeploying(false);
setIsDrawerOpen(false); refetch();
setFilteredLogs([]); }}
setIsDeploying(false); filteredLogs={filteredLogs}
refetch(); />
}} </div>
filteredLogs={filteredLogs} </>
/> );
</div>
</>
);
}; };