mirror of
https://github.com/moghtech/komodo.git
synced 2026-08-01 18:59:59 -05:00
add single deployment page etc
This commit is contained in:
@@ -2,13 +2,14 @@ import { ThemeToggle } from "@components/util";
|
||||
|
||||
import { Button } from "@ui/button";
|
||||
import { ChevronRight, Circle, LogOut } from "lucide-react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import { Link, useLocation, useParams } from "react-router-dom";
|
||||
|
||||
import { useUser } from "@hooks";
|
||||
import { ServerName } from "@pages/server";
|
||||
|
||||
export const Paths = () => {
|
||||
const location = useLocation();
|
||||
const path = location.pathname.split("/")[1];
|
||||
const path = useLocation().pathname.split("/")[1];
|
||||
const { serverId } = useParams();
|
||||
|
||||
return (
|
||||
<div className="hidden md:flex items-center gap-2">
|
||||
@@ -25,6 +26,14 @@ export const Paths = () => {
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
{serverId && (
|
||||
<>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
<Link to={`/servers/${serverId}`}>
|
||||
<ServerName serverId={serverId} />
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
import { Button } from "@ui/button";
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from "@ui/sheet";
|
||||
import { Update } from "@monitor/client/dist/types";
|
||||
import {
|
||||
readableDuration,
|
||||
readableVersion,
|
||||
version_to_string,
|
||||
} from "@util/helpers";
|
||||
import { Calendar, Clock, Milestone, Search, User } from "lucide-react";
|
||||
// import { UpdateUser } from ".";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@ui/card";
|
||||
import { useRead } from "@hooks";
|
||||
|
||||
export const UpdateUser = ({ userId }: { userId: string }) => {
|
||||
// const { data } = useUpdateUser(userId);
|
||||
if (userId === "github") return <>GitHub</>;
|
||||
if (userId === "auto redeploy") return <>Auto Redeploy</>;
|
||||
return <>{userId}</>;
|
||||
};
|
||||
|
||||
export const UpdateDetails = ({ update }: { update: Update }) => {
|
||||
return (
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="flex items-center gap-2 w-full max-w-fit"
|
||||
>
|
||||
<Search className="w-4 h-4" />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent position="right" size="lg">
|
||||
<SheetHeader className="mb-4">
|
||||
<SheetTitle>
|
||||
{update.operation
|
||||
.split("_")
|
||||
.map((s) => s[0].toUpperCase() + s.slice(1))
|
||||
.join(" ")}{" "}
|
||||
{version_to_string(update.version)}
|
||||
</SheetTitle>
|
||||
<SheetDescription className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="w-4 h-4" />
|
||||
{new Date(update.start_ts).toLocaleString()}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4" />
|
||||
{update.end_ts
|
||||
? readableDuration(update.start_ts, update.end_ts)
|
||||
: "ongoing"}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<User className="w-4 h-4" />
|
||||
<UpdateUser userId={update.operator} />
|
||||
</div>
|
||||
{update.version && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Milestone className="w-4 h-4" />
|
||||
{readableVersion(update.version)}
|
||||
</div>
|
||||
)}
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
<div className="max-h-[80vh] overflow-y-auto grid gap-2">
|
||||
{update.logs.map((log, i) => (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{log.stage}</CardTitle>
|
||||
<CardDescription className="flex gap-2">
|
||||
<span>
|
||||
Stage {i + 1} of {update.logs.length}
|
||||
</span>
|
||||
<span>|</span>
|
||||
<span className="flex items-center gap-2">
|
||||
<Clock className="w-4 h-4" />
|
||||
{readableDuration(log.start_ts, log.end_ts)}
|
||||
</span>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-2">
|
||||
{log.command && (
|
||||
<div>
|
||||
<CardDescription>command</CardDescription>
|
||||
<pre className="max-h-[500px] overflow-y-auto">
|
||||
{log.command}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
{log.stdout && (
|
||||
<div>
|
||||
<CardDescription>stdout</CardDescription>
|
||||
<pre className="max-h-[500px] overflow-y-auto">
|
||||
{log.stdout}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
{log.stderr && (
|
||||
<div>
|
||||
<CardDescription>stdout</CardDescription>
|
||||
<pre className="max-h-[500px] overflow-y-auto">
|
||||
{log.stderr}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@ui/card";
|
||||
import { Types } from "@monitor/client";
|
||||
import { cn, version_to_string } from "@util/helpers";
|
||||
import { Calendar, User } from "lucide-react";
|
||||
import { UpdateDetails, UpdateUser } from "./update";
|
||||
|
||||
export const Updates = ({
|
||||
updates,
|
||||
className,
|
||||
}: {
|
||||
updates?: Types.Update[];
|
||||
className?: string;
|
||||
}) => (
|
||||
<Card className={cn("w-full h-fit", className)}>
|
||||
<CardHeader className="flex-row justify-between">
|
||||
<CardTitle className="text-xl">Updates</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-2 max-h-[50vh] overflow-y-auto">
|
||||
{updates?.map((update) => (
|
||||
<Card key={update._id?.$oid}>
|
||||
<CardHeader className="flex flex-row items-end md:items-center justify-between gap-4">
|
||||
<div
|
||||
className="flex flex-col md:flex-row justify-between items-center w-full"
|
||||
style={{ placeItems: "center start" }}
|
||||
>
|
||||
<CardTitle className="whitespace-nowrap w-full">
|
||||
{update.operation
|
||||
.split("_")
|
||||
.map((s) => s[0].toUpperCase() + s.slice(1))
|
||||
.join(" ")}{" "}
|
||||
{version_to_string(update.version)}
|
||||
</CardTitle>
|
||||
<div className="flex flex-col-reverse md:flex-row md:gap-4 w-full">
|
||||
<div className="flex gap-2 items-center md:w-[200px]">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<CardDescription className="text-xs md:text-sm">
|
||||
{update.end_ts
|
||||
? new Date(update.end_ts).toLocaleString()
|
||||
: "ongoing"}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex gap-2 items-center">
|
||||
<User className="w-4 h-4" />
|
||||
<CardDescription>
|
||||
<UpdateUser userId={update.operator} />
|
||||
</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<UpdateDetails update={update} />
|
||||
</CardHeader>
|
||||
</Card>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
@@ -1,13 +1,38 @@
|
||||
import { Types } from "@monitor/client";
|
||||
import { client } from "./main";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { useAtomValue, useSetAtom } from "jotai";
|
||||
import { atomWithStorage } from "jotai/utils";
|
||||
|
||||
export const useRead = <T extends Types.ReadRequest>(req: T) =>
|
||||
useQuery([req], () => client.read(req));
|
||||
|
||||
export const useWrite = <T extends Types.WriteRequest>() =>
|
||||
useMutation((req: T) => client.write(req));
|
||||
|
||||
export const useExecute = <T extends Types.ExecuteRequest>() =>
|
||||
useMutation((req: T) => client.execute(req));
|
||||
|
||||
export const useUser = () => useRead({ type: "GetUser", params: {} });
|
||||
|
||||
export const useLogin = () =>
|
||||
useMutation(client.login, {
|
||||
onSuccess: (jwt) => localStorage.setItem("monitor-auth-token", jwt ?? ""),
|
||||
});
|
||||
|
||||
// const recents_atom = atomWithStorage<
|
||||
// { type: "Deployment" | "Build" | "Server"; id: string }[]
|
||||
// >("recents", []);
|
||||
|
||||
const recents_atom = atomWithStorage<
|
||||
{ type: "Deployment" | "Build" | "Server"; id: string }[]
|
||||
>("recently-viewed", []);
|
||||
|
||||
export const useGetRecentlyViewed = () => useAtomValue(recents_atom);
|
||||
|
||||
export const useSetRecentlyViewed = () => {
|
||||
const set = useSetAtom(recents_atom);
|
||||
const push = (type: "Deployment" | "Build" | "Server", id: string) =>
|
||||
set((res) => [{ type, id }, ...res.filter((r) => r.id !== id)].slice(0, 5));
|
||||
return push;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useGetRecentlyViewed } from "@hooks";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@ui/card";
|
||||
import { DeploymentCard, ServerCard } from "..";
|
||||
|
||||
export const RecentlyViewed = () => {
|
||||
const recents = useGetRecentlyViewed();
|
||||
return (
|
||||
<Card className="w-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Recently Viewed</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4">
|
||||
{recents.map(({ type, id }) => {
|
||||
if (type === "Deployment") return <DeploymentCard key={id} id={id} />;
|
||||
if (type === "Build") return <div></div>;
|
||||
if (type === "Server") return <ServerCard key={id} id={id} />;
|
||||
})}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
@@ -1,12 +1,129 @@
|
||||
import { useRead, useUser } from "@hooks";
|
||||
import { useRead, useUser, useWrite } from "@hooks";
|
||||
import { Card, CardDescription, CardHeader, CardTitle } from "@ui/card";
|
||||
import { version_to_string } from "@util/helpers";
|
||||
import { ServersChart } from "./components/servers-chart";
|
||||
import { DeploymentsChart } from "./components/deployments-chart";
|
||||
import { Input } from "@ui/input";
|
||||
import { Button } from "@ui/button";
|
||||
import { PlusCircle } from "lucide-react";
|
||||
import { ChevronDown, PlusCircle } from "lucide-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@ui/dropdown";
|
||||
import { Button } from "@ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@ui/dialog";
|
||||
import { useState } from "react";
|
||||
import { RecentlyViewed } from "./components/recents";
|
||||
|
||||
const NewDeployment = ({
|
||||
open,
|
||||
set,
|
||||
}: {
|
||||
open: boolean;
|
||||
set: (b: boolean) => void;
|
||||
}) => {
|
||||
const { mutate } = useWrite();
|
||||
const [name, setName] = useState("");
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={set}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>New Deployment</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>Deployment Name</div>
|
||||
<Input
|
||||
className="max-w-[50%]"
|
||||
placeholder="Deployment Name"
|
||||
name={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
intent="success"
|
||||
onClick={() => {
|
||||
mutate({
|
||||
type: "CreateDeployment",
|
||||
params: { name, config: {} },
|
||||
});
|
||||
set(false);
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
const NewButton = () => {
|
||||
const [open, set] = useState<"deployment" | "server" | boolean>(false);
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Button
|
||||
className="w-[200px] flex items-center justify-between"
|
||||
variant="outline"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<PlusCircle className="w-4 h-4 text-green-500" />
|
||||
Add New
|
||||
</div>
|
||||
<ChevronDown className="w-4 h-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-[200px]">
|
||||
<DropdownMenuLabel className="text-xs">
|
||||
Resource Type
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem onClick={() => set("deployment")}>
|
||||
Deployment
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem> Build </DropdownMenuItem>
|
||||
<DropdownMenuItem> Server </DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<NewDeployment open={open === "deployment"} set={set} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const DeploymentCard = ({ id }: { id: string }) => {
|
||||
const deployments = useRead({ type: "ListDeployments", params: {} }).data;
|
||||
const deployment = deployments?.find((d) => d.id === id);
|
||||
if (!deployment) return null;
|
||||
return (
|
||||
<Link to={`/deployments/${deployment.id}`}>
|
||||
<Card className="hover:bg-accent">
|
||||
<CardHeader>
|
||||
<CardTitle>{deployment.name}</CardTitle>
|
||||
<CardDescription>
|
||||
{deployment.status ?? "not deployed"}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const DeploymentsList = () => {
|
||||
const deployments = useRead({ type: "ListDeployments", params: {} }).data;
|
||||
@@ -14,33 +131,38 @@ const DeploymentsList = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-2 w-full border-r pr-4">
|
||||
<h2 className="text-lg">Deployments</h2>
|
||||
{deployments?.map((deployment) => (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{deployment.name}</CardTitle>
|
||||
<CardDescription>{deployment.version}</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
{deployments?.map(({ id }) => (
|
||||
<DeploymentCard key={id} id={id} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ServerCard = ({ id }: { id: string }) => {
|
||||
const servers = useRead({ type: "ListServers", params: {} }).data;
|
||||
const server = servers?.find((server) => server.id === id);
|
||||
if (!server) return null;
|
||||
|
||||
return (
|
||||
<Link to={`/servers/${server.id}`} key={server.id}>
|
||||
<Card className="hover:bg-accent">
|
||||
<CardHeader>
|
||||
<CardTitle>{server.name}</CardTitle>
|
||||
<CardDescription>{server.status}</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const ServersList = () => {
|
||||
const servers = useRead({ type: "ListServers", params: {} }).data;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 w-full border-r pr-4">
|
||||
<h2 className="text-lg">Servers</h2>
|
||||
<h2 className="text-lg">Deployments</h2>
|
||||
{servers?.map((server) => (
|
||||
<Link to={`/servers/${server.id}`} key={server.id}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{server.name}</CardTitle>
|
||||
<CardDescription>{server.status}</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
<ServerCard key={server.id} id={server.id} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
@@ -75,21 +197,21 @@ export const Dashboard = () => {
|
||||
<h1 className="text-xl"> Hello, {user?.username}.</h1>
|
||||
<div className="flex gap-4">
|
||||
<Input className="w-[300px]" placeholder="Search" />
|
||||
<Button className="w-[120px]" variant="outline" intent="success">
|
||||
<PlusCircle className="w-4 h-4 mr-2 text-green-500" />
|
||||
Add New
|
||||
</Button>
|
||||
<NewButton />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<DeploymentsChart />
|
||||
<ServersChart />
|
||||
<div className="flex gap-24">
|
||||
<div className="flex gap-4 w-full h-fit">
|
||||
<DeploymentsChart />
|
||||
<ServersChart />
|
||||
</div>
|
||||
<RecentlyViewed />
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
{/* <div className="flex gap-4">
|
||||
<DeploymentsList />
|
||||
<ServersList />
|
||||
<BuildsList />
|
||||
</div>
|
||||
</div> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Button } from "@ui/button";
|
||||
import { Card, CardHeader, CardContent } from "@ui/card";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@ui/tabs";
|
||||
// import { useDeploymentLog } from "@hooks/deployments";
|
||||
import { TabsContent } from "@radix-ui/react-tabs";
|
||||
import { AlertOctagon, ChevronDown } from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
import { useRead } from "@hooks";
|
||||
|
||||
const scroll_to_bottom = (id: string) => () =>
|
||||
document
|
||||
.getElementById(id)
|
||||
?.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
|
||||
|
||||
export const DeploymentLogs = ({ deploymentId }: { deploymentId: string }) => {
|
||||
const { data, refetch } = useRead({
|
||||
type: "GetLog",
|
||||
params: { deployment_id: deploymentId, tail: 200 },
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const handle = setInterval(() => refetch(), 30000);
|
||||
return () => clearInterval(handle);
|
||||
}, [refetch]);
|
||||
|
||||
useEffect(() => {
|
||||
scroll_to_bottom("stdout")();
|
||||
scroll_to_bottom("stderr")();
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<Tabs defaultValue="stdout">
|
||||
<Card>
|
||||
<CardHeader className="flex-row justify-end">
|
||||
<TabsList>
|
||||
<TabsTrigger value="stdout" onClick={scroll_to_bottom("stdout")}>
|
||||
Out
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="stderr" onClick={scroll_to_bottom("stderr")}>
|
||||
Err
|
||||
{data?.stderr && (
|
||||
<AlertOctagon className="w-4 h-4 ml-2 stroke-red-500" />
|
||||
)}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</CardHeader>
|
||||
{["stdout", "stderr"].map((t) => (
|
||||
<TabsContent key={t} className="h-full relative" value={t}>
|
||||
<CardContent className="h-[50vh] overflow-y-scroll">
|
||||
<pre id={t}>
|
||||
{data?.[t as keyof typeof data] || `no ${t} logs`}
|
||||
</pre>
|
||||
</CardContent>
|
||||
<Button
|
||||
className="absolute bottom-8 right-12"
|
||||
onClick={scroll_to_bottom(t)}
|
||||
>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</Button>
|
||||
</TabsContent>
|
||||
))}
|
||||
</Card>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
// import { DeploymentLogs } from "@pages/resource/deployment/components/logs";
|
||||
import { Resource } from "@layouts/resource";
|
||||
// import { Updates } from "@components/updates";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { CardDescription } from "@ui/card";
|
||||
// import { DeploymentConfig } from "@pages/resource/deployment/components/config";
|
||||
// import {
|
||||
// RedeployContainer,
|
||||
// StartOrStopContainer,
|
||||
// RemoveContainer,
|
||||
// } from "@pages/resource/deployment/components/actions";
|
||||
// import { DeleteDeployment } from "@pages/resource/deployment/components/delete";
|
||||
import { useRead, useSetRecentlyViewed } from "@hooks";
|
||||
import { Circle } from "lucide-react";
|
||||
import { cn } from "@util/helpers";
|
||||
import { DeploymentLogs } from "./components/deployment-logs";
|
||||
import { Updates } from "@components/updates/updates";
|
||||
|
||||
export const DeploymentName = ({
|
||||
deploymentId,
|
||||
}: {
|
||||
deploymentId: string | undefined;
|
||||
}) => {
|
||||
const deployments = useRead({ type: "ListDeployments", params: {} }).data;
|
||||
const deployment = deployments?.find((d) => d.id === deploymentId);
|
||||
return <>{deployment?.name ?? "..."}</>;
|
||||
};
|
||||
|
||||
export const DeploymentStatus = ({
|
||||
deploymentId,
|
||||
}: {
|
||||
deploymentId: string | undefined;
|
||||
}) => {
|
||||
const deployments = useRead({ type: "ListDeployments", params: {} }).data;
|
||||
const deployment = deployments?.find((d) => d.id === deploymentId);
|
||||
return <>{deployments ? deployment?.status ?? "not deployed" : "..."}</>;
|
||||
};
|
||||
|
||||
export const DeploymentStatusIcon = ({
|
||||
deploymentId,
|
||||
}: {
|
||||
deploymentId: string | undefined;
|
||||
}) => {
|
||||
const deployments = useRead({ type: "ListDeployments", params: {} }).data;
|
||||
const deployment = deployments?.find((d) => d.id === deploymentId);
|
||||
return (
|
||||
<Circle
|
||||
className={cn(
|
||||
"w-4 h-4 stroke-none",
|
||||
deployment?.status === "running" && "fill-green-500",
|
||||
deployment?.status === "exited" && "fill-red-500",
|
||||
deployment?.status === "not_deployed" && "fill-blue-500"
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// export const DeploymentInfo = ({ deploymentId }: { deploymentId: string }) => {
|
||||
// const deployments = useRead({ type: "ListDeployments", params: {} }).data;
|
||||
// const deployment = deployments?.find((d) => d.id === deploymentId);
|
||||
|
||||
// return (
|
||||
// <div className="flex flex-col gap-2 md:flex-row md:gap-4">
|
||||
// <Link to={`/builds/${deployment?.deployment.build_id}`}>
|
||||
// <CardDescription className="flex items-center">
|
||||
// <HardDrive className="w-4 h-4 mr-2" />
|
||||
// {data ? deployment?.container?.image ?? "no image" : "..."}
|
||||
// </CardDescription>
|
||||
// </Link>
|
||||
// <Link to={`/servers/${deployment?.deployment.server_id}`}>
|
||||
// <CardDescription className="flex items-center gap-2">
|
||||
// <Server className="w-4 h-4" />
|
||||
// <ServerName serverId={deployment?.deployment.server_id} />
|
||||
// </CardDescription>
|
||||
// </Link>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
export const Deployment = () => {
|
||||
const { deploymentId } = useParams();
|
||||
const push = useSetRecentlyViewed();
|
||||
|
||||
if (!deploymentId) return null;
|
||||
push("Deployment", deploymentId);
|
||||
|
||||
return (
|
||||
<Resource
|
||||
title={<DeploymentName deploymentId={deploymentId} />}
|
||||
info={
|
||||
<div className="flex flex-col gap-2 md:flex-row md:items-center md:gap-4">
|
||||
<div>deployment info</div>
|
||||
{/* <DeploymentInfo deploymentId={deploymentId} /> */}
|
||||
<CardDescription className="hidden md:block">|</CardDescription>
|
||||
<CardDescription className="flex items-center gap-2">
|
||||
<DeploymentStatusIcon deploymentId={deploymentId} />
|
||||
<DeploymentStatus deploymentId={deploymentId} />
|
||||
</CardDescription>
|
||||
<CardDescription className="hidden md:block">|</CardDescription>
|
||||
{/* <DeleteDeployment deploymentId={deploymentId} /> */}
|
||||
</div>
|
||||
}
|
||||
actions={
|
||||
<>
|
||||
{/* <RedeployContainer deploymentId={deploymentId} />
|
||||
<StartOrStopContainer deploymentId={deploymentId} />
|
||||
<RemoveContainer deploymentId={deploymentId} /> */}
|
||||
</>
|
||||
}
|
||||
tabs={[
|
||||
{
|
||||
title: "Logs",
|
||||
component: <DeploymentLogs deploymentId={deploymentId} />,
|
||||
},
|
||||
{
|
||||
title: "Config",
|
||||
component: <>Config</>,
|
||||
},
|
||||
{
|
||||
title: "Updates",
|
||||
component: <>Updates</>,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useRead } from "@hooks";
|
||||
import { useRead, useSetRecentlyViewed } from "@hooks";
|
||||
import { Resource } from "@layouts/resource";
|
||||
import { ServerStatus } from "@monitor/client/dist/types";
|
||||
import { CardDescription } from "@ui/card";
|
||||
@@ -83,7 +83,12 @@ export const ServerStatusIcon = ({
|
||||
|
||||
export const Server = () => {
|
||||
const { serverId } = useParams();
|
||||
// const { data } = useRead({ type: "GetServer", params: { id: serverId! } });
|
||||
const push = useSetRecentlyViewed();
|
||||
|
||||
// if (!serverId) return null;
|
||||
// push("Server", serverId!);
|
||||
if (!serverId) return null;
|
||||
push("Server", serverId);
|
||||
|
||||
return (
|
||||
<Resource
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Login } from "@pages/auth/login";
|
||||
import { Signup } from "@pages/auth/signup";
|
||||
import { Dashboard } from "@pages/dashboard";
|
||||
import { Server } from "@pages/server";
|
||||
import { Deployment } from "@pages/deployment";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@@ -18,13 +19,13 @@ const router = createBrowserRouter([
|
||||
{ path: "login", element: <Login /> },
|
||||
{ path: "signup", element: <Signup /> },
|
||||
|
||||
// {
|
||||
// path: "deployments",
|
||||
// children: [
|
||||
// { path: "", element: <Deployments /> },
|
||||
// { path: ":deploymentId", element: <Deployment /> },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
path: "deployments",
|
||||
children: [
|
||||
{ path: "", element: "deploymenys" },
|
||||
{ path: ":deploymentId", element: <Deployment /> },
|
||||
],
|
||||
},
|
||||
// {
|
||||
// path: "builds",
|
||||
// children: [
|
||||
|
||||
@@ -62,7 +62,7 @@ export function readableMonitorTimestamp(rfc3339_ts: string) {
|
||||
} ${pm ? "PM" : "AM"}`;
|
||||
}
|
||||
|
||||
export function readableDuration(start_ts: string, end_ts: string) {
|
||||
export function readableDuration(start_ts: number, end_ts: number) {
|
||||
const start = new Date(start_ts);
|
||||
const end = new Date(end_ts);
|
||||
const durr = end.getTime() - start.getTime();
|
||||
@@ -146,7 +146,9 @@ export function copyToClipboard(text: string) {
|
||||
navigator.clipboard.writeText(text);
|
||||
}
|
||||
|
||||
export function parseEnvVarseToDotEnv(envVars: Types.EnvironmentVar[] | undefined) {
|
||||
export function parseEnvVarseToDotEnv(
|
||||
envVars: Types.EnvironmentVar[] | undefined
|
||||
) {
|
||||
return envVars?.reduce(
|
||||
(prev, { variable, value }) =>
|
||||
prev + (prev ? "\n" : "") + `${variable}=${value}`,
|
||||
|
||||
@@ -5,9 +5,9 @@ import tsconfigPaths from "vite-tsconfig-paths";
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tsconfigPaths()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@monitor/client": "../client/ts"
|
||||
}
|
||||
}
|
||||
// resolve: {
|
||||
// alias: {
|
||||
// "@monitor/client": "../client/ts"
|
||||
// }
|
||||
// }
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user