new deployment / repo from server page

This commit is contained in:
mbecker20
2024-06-01 20:33:38 -07:00
parent d9d44ceee1
commit 693f24763f
10 changed files with 80 additions and 43 deletions

View File

@@ -105,6 +105,7 @@ export const BuildConfig = ({
selected={id}
onSelect={(builder_id) => set({ builder_id })}
disabled={disabled}
align="end"
/>
</ConfigItem>
),

View File

@@ -17,6 +17,7 @@ import { Card, CardHeader } from "@ui/card";
import { cn } from "@lib/utils";
import { useState } from "react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/tabs";
import { ResourceComponents } from "..";
const useBuild = (id?: string) =>
useRead("ListBuilds", {}).data?.find((d) => d.id === id);
@@ -33,6 +34,20 @@ const ConfigOrDeployments = ({ id }: { id: string }) => {
(deployment) => deployment.info.build_id === id
);
const deploymentsDisabled = (deployments?.length || 0) === 0;
const titleOther = (
<TabsList className="justify-start w-fit">
<TabsTrigger value="Config" className="w-[110px]">
Config
</TabsTrigger>
<TabsTrigger
value="Deployments"
className="w-[110px]"
disabled={deploymentsDisabled}
>
Deployments
</TabsTrigger>
</TabsList>
);
return (
<Tabs
value={deploymentsDisabled ? "Config" : view}
@@ -40,40 +55,12 @@ const ConfigOrDeployments = ({ id }: { id: string }) => {
className="grid gap-4"
>
<TabsContent value="Config">
<BuildConfig
id={id}
titleOther={
<TabsList className="justify-start w-fit">
<TabsTrigger value="Config" className="w-[110px]">
Config
</TabsTrigger>
<TabsTrigger
value="Deployments"
className="w-[110px]"
disabled={deploymentsDisabled}
>
Deployments
</TabsTrigger>
</TabsList>
}
/>
<BuildConfig id={id} titleOther={titleOther} />
</TabsContent>
<TabsContent value="Deployments">
<Section
titleOther={
<TabsList className="justify-start w-fit">
<TabsTrigger value="Config" className="w-[110px]">
Config
</TabsTrigger>
<TabsTrigger
value="Deployments"
className="w-[110px]"
disabled={deploymentsDisabled}
>
Deployments
</TabsTrigger>
</TabsList>
}
titleOther={titleOther}
actions={<ResourceComponents.Deployment.New build_id={id} />}
>
<DeploymentTable deployments={deployments} />
</Section>

View File

@@ -108,7 +108,7 @@ export const ResourceSelector = ({
{!disabled && <ChevronsUpDown className="w-3 h-3" />}
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] max-h-[200px] p-0" align={align}>
<PopoverContent className="w-[300px] max-h-[300px] p-0" align={align}>
<Command>
<CommandInput
placeholder={`Search ${type}s`}
@@ -228,17 +228,34 @@ export const CopyResource = ({
);
};
export const NewResource = ({ type }: { type: UsableResource }) => {
export const NewResource = ({
type,
server_id,
build_id,
}: {
type: UsableResource;
server_id?: string;
build_id?: string;
}) => {
const nav = useNavigate();
const { mutateAsync } = useWrite(`Create${type}`);
const [name, setName] = useState("");
const type_display =
type === "ServerTemplate" ? "server-template" : type.toLowerCase();
const config =
type === "Deployment"
? {
server_id,
image: build_id ?? { type: "Build", params: { build_id } },
}
: type === "Repo"
? { server_id }
: {};
return (
<NewLayout
entityType={type}
onSuccess={async () => {
const id = (await mutateAsync({ name, config: {} }))._id?.$oid!;
const id = (await mutateAsync({ name, config }))._id?.$oid!;
nav(`/${usableResourcePath(type)}/${id}`);
}}
enabled={!!name}

View File

@@ -103,7 +103,9 @@ export const DeploymentComponents: RequiredResourceComponents = {
Dashboard: DeploymentsChart,
New: () => <NewResource type="Deployment" />,
New: ({ server_id, build_id }) => (
<NewResource type="Deployment" server_id={server_id} build_id={build_id} />
),
Table: ({ search }) => {
const deployments = useRead("ListDeployments", {}).data;

View File

@@ -174,12 +174,12 @@ const Log = ({
useEffect(scroll, [_log]);
return (
<>
<div ref={ref} className="h-[80vh] overflow-y-auto">
<div ref={ref} className="h-[75vh] overflow-y-auto">
<pre
dangerouslySetInnerHTML={{
__html: _log ? logToHtml(_log) : `no ${stream} logs`,
}}
className="-scroll-mt-24"
className="-scroll-mt-24 pb-[20vh]"
/>
</div>
<Button variant="secondary" className="absolute top-4 right-4" onClick={scroll}>

View File

@@ -32,7 +32,7 @@ export const RepoComponents: RequiredResourceComponents = {
Dashboard: RepoDashboard,
New: () => <NewResource type="Repo" />,
New: ({ server_id }) => <NewResource type="Repo" server_id={server_id} />,
Table: ({ search }) => {
const repos = useRead("ListRepos", {}).data;

View File

@@ -31,6 +31,7 @@ import { Button } from "@ui/button";
import { useState } from "react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/tabs";
import { RepoTable } from "../repo/table";
import { ResourceComponents } from "..";
export const useServer = (id?: string) =>
useRead("ListServers", {}, { refetchInterval: 5000 }).data?.find(
@@ -88,13 +89,19 @@ const ConfigOrChildResources = ({ id }: { id: string }) => {
</TabsContent>
<TabsContent value="Deployments">
<Section titleOther={tabsList}>
<Section
titleOther={tabsList}
actions={<ResourceComponents.Deployment.New server_id={id} />}
>
<DeploymentTable deployments={deployments} />
</Section>
</TabsContent>
<TabsContent value="Repos">
<Section titleOther={tabsList}>
<Section
titleOther={tabsList}
actions={<ResourceComponents.Repo.New server_id={id} />}
>
<RepoTable repos={repos} />
</Section>
</TabsContent>

View File

@@ -17,7 +17,7 @@ import {
import { UsableResource } from "@types";
import { useToast } from "@ui/use-toast";
import { atom, useAtom } from "jotai";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
// ============== RESOLVER ==============
@@ -291,3 +291,25 @@ export const useFilterResources = <Info>(
) ?? []
);
};
export type LocalStorageSetter<T> = (state: T) => T;
export const useLocalStorage = <T>(
key: string,
init: T
): [T, (state: T | LocalStorageSetter<T>) => void] => {
const stored = localStorage.getItem(key);
const parsed = stored ? (JSON.parse(stored) as T) : undefined;
const [state, inner_set] = useState<T>(parsed ?? init);
const set = (state: T | LocalStorageSetter<T>) => {
inner_set((prev_state) => {
const new_val =
typeof state === "function"
? (state as LocalStorageSetter<T>)(prev_state)
: state;
localStorage.setItem(key, JSON.stringify(new_val));
return new_val;
});
};
return [state, set];
};

View File

@@ -50,7 +50,8 @@ export const Tree = () => {
const Server = ({ id }: { id: string }) => {
const [search] = useAtom(searchAtom);
const [open, setOpen] = useState(true);
// const [open, setOpen] = useLocalStorage(`server-tree-open-${id}`, false);
const [open, setOpen] = useState(false);
const server = useRead("ListServers", {}).data?.find(
(server) => server.id === id
);

View File

@@ -12,7 +12,7 @@ export interface RequiredResourceComponents {
Dashboard: React.FC;
/** New resource button / dialog */
New: React.FC;
New: React.FC<{ server_id?: string; build_id?: string }>;
/** A table component to view resource list */
Table: React.FC<{ search?: string }>;