new config who dis

This commit is contained in:
karamvir
2023-08-04 00:00:15 -07:00
parent 1abaff6625
commit 24437e7b10
2 changed files with 510 additions and 118 deletions

View File

@@ -53,6 +53,7 @@ function findDeepDisabled<T extends Record<string, unknown>>(
export function Config<T extends Record<string, unknown>>({
config,
layout,
update,
set,
overrides,
@@ -61,6 +62,7 @@ export function Config<T extends Record<string, unknown>>({
disabled,
}: {
config: T;
layout?: { [key: string]: Array<keyof T> };
update: Partial<T>;
set: ConfigSetter<T>;
overrides?: Overides<T>;
@@ -87,100 +89,210 @@ export function Config<T extends Record<string, unknown>>({
))}
</div>
<div className="w-full h-fit">
{Object.entries(config).map(([field, value]) => {
if (show !== field) return null;
const val = update[field] ?? value;
const overide = overrides?.[field];
if (overide && typeof overide === "function") {
return overide(update[field] ?? (value as T[string]), (updt) =>
set((curr) => ({
...curr,
[field]: updt(
update[field] ?? (config[field] as Partial<T[string]>)
),
}))
) as ReactNode;
}
if (typeof val === "string") {
return (
<StringConfig
key={field}
field={field}
val={val}
set={(u) =>
{layout &&
Object.entries(layout).map(([field, config_keys]) => {
if (show !== field) return null;
config_keys.map((field) => {
const value = config[field];
const val = update[field] ?? value;
const overide = overrides?.[field];
if (overide && typeof overide === "function") {
return overide(update[field] ?? (value as T[string]), (updt) =>
set((curr) => ({
...curr,
[field]: u(val),
[field]: updt(
update[field] ?? (config[field] as Partial<T[string]>)
),
}))
}
description={descriptions?.[field] as string | undefined}
disabled={findDeepDisabled(field, disabled) as boolean}
/>
);
}
if (typeof val === "boolean") {
return (
<BooleanConfig
key={field}
field={field}
val={val}
set={(u) => set((curr) => ({ ...curr, [field]: u(val) }))}
description={descriptions?.[field] as string | undefined}
disabled={findDeepDisabled(field, disabled) as boolean}
/>
);
}
if (typeof val === "number") {
return (
<NumberConfig
key={field}
field={field}
val={val}
set={(u) => set((curr) => ({ ...curr, [field]: u(val) }))}
description={descriptions?.[field] as string | undefined}
disabled={findDeepDisabled(field, disabled) as boolean}
/>
);
}
if (Array.isArray(value)) {
const val = (update[field] ? update[field] : value) as unknown[];
return (
<ArrayConfig
key={field}
field={field}
val={val}
set={set}
defaultNew={arrayDefaults?.[field] ?? ""}
disabled={findDeepDisabled(field, disabled) as boolean}
/>
);
}
return (
<Card key={field}>
<CardHeader>
<CardTitle>{field.replaceAll("_", " ")}</CardTitle>
</CardHeader>
<CardContent>
<Config
config={config[field] as Record<string, unknown>}
update={update[field] ?? {}}
set={(update) => {
) as ReactNode;
}
if (typeof val === "string") {
return (
<StringConfig
key={field as string}
field={field as string}
val={val}
set={(u) =>
set((curr) => ({
...curr,
[field]: u(val),
}))
}
description={descriptions?.[field] as string | undefined}
disabled={
findDeepDisabled(field as string, disabled) as boolean
}
/>
);
}
if (typeof val === "boolean") {
return (
<BooleanConfig
key={field as string}
field={field as string}
val={val}
set={(u) => set((curr) => ({ ...curr, [field]: u(val) }))}
description={descriptions?.[field] as string | undefined}
disabled={
findDeepDisabled(field as string, disabled) as boolean
}
/>
);
}
if (typeof val === "number") {
return (
<NumberConfig
key={field as string}
field={field as string}
val={val}
set={(u) => set((curr) => ({ ...curr, [field]: u(val) }))}
description={descriptions?.[field] as string | undefined}
disabled={
findDeepDisabled(field as string, disabled) as boolean
}
/>
);
}
if (Array.isArray(value)) {
const val = (
update[field] ? update[field] : value
) as unknown[];
return (
<ArrayConfig
key={field as string}
field={field as string}
val={val}
set={set}
defaultNew={arrayDefaults?.[field] ?? ""}
disabled={
findDeepDisabled(field as string, disabled) as boolean
}
/>
);
}
return (
<Card key={field as string}>
<CardHeader>
<CardTitle>
{(field as string).replaceAll("_", " ")}
</CardTitle>
</CardHeader>
<CardContent>
<Config
config={config[field] as Record<string, unknown>}
update={update[field] ?? {}}
set={(update) => {
set((curr) => ({
...curr,
[field]: update(
curr[field] ?? (config[field] as Partial<T[string]>)
),
}));
}}
overrides={
overrides?.[field] as Overides<Record<string, unknown>>
}
disabled={findDeepDisabled(field as string, disabled)}
/>
</CardContent>
</Card>
);
});
})}
{!layout &&
Object.entries(config).map(([field, value]) => {
const val = update[field] ?? value;
const overide = overrides?.[field];
if (overide && typeof overide === "function") {
return overide(update[field] ?? (value as T[string]), (updt) =>
set((curr) => ({
...curr,
[field]: updt(
update[field] ?? (config[field] as Partial<T[string]>)
),
}))
) as ReactNode;
}
if (typeof val === "string") {
return (
<StringConfig
key={field}
field={field}
val={val}
set={(u) =>
set((curr) => ({
...curr,
[field]: update(
curr[field] ?? (config[field] as Partial<T[string]>)
),
}));
}}
overrides={
overrides?.[field] as Overides<Record<string, unknown>>
[field]: u(val),
}))
}
disabled={findDeepDisabled(field, disabled)}
description={descriptions?.[field] as string | undefined}
disabled={findDeepDisabled(field, disabled) as boolean}
/>
</CardContent>
</Card>
);
})}
);
}
if (typeof val === "boolean") {
return (
<BooleanConfig
key={field}
field={field}
val={val}
set={(u) => set((curr) => ({ ...curr, [field]: u(val) }))}
description={descriptions?.[field] as string | undefined}
disabled={findDeepDisabled(field, disabled) as boolean}
/>
);
}
if (typeof val === "number") {
return (
<NumberConfig
key={field}
field={field}
val={val}
set={(u) => set((curr) => ({ ...curr, [field]: u(val) }))}
description={descriptions?.[field] as string | undefined}
disabled={findDeepDisabled(field, disabled) as boolean}
/>
);
}
if (Array.isArray(value)) {
const val = (update[field] ? update[field] : value) as unknown[];
return (
<ArrayConfig
key={field}
field={field}
val={val}
set={set}
defaultNew={arrayDefaults?.[field] ?? ""}
disabled={findDeepDisabled(field, disabled) as boolean}
/>
);
}
return (
<Card key={field}>
<CardHeader>
<CardTitle>{field.replaceAll("_", " ")}</CardTitle>
</CardHeader>
<CardContent>
<Config
config={config[field] as Record<string, unknown>}
update={update[field] ?? {}}
set={(update) => {
set((curr) => ({
...curr,
[field]: update(
curr[field] ?? (config[field] as Partial<T[string]>)
),
}));
}}
overrides={
overrides?.[field] as Overides<Record<string, unknown>>
}
disabled={findDeepDisabled(field, disabled)}
/>
</CardContent>
</Card>
);
})}
</div>
</div>
);

View File

@@ -1,43 +1,323 @@
import { Config } from "@components/config/Config";
import { useRead, useWrite } from "@hooks";
import { Section } from "@layouts/page";
import { Types } from "@monitor/client";
import { Button } from "@ui/button";
import { Settings, Save, History } from "lucide-react";
import { useState } from "react";
import { Card, CardHeader, CardTitle, CardContent } from "@ui/card";
import { Input } from "@ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@ui/select";
import { Switch } from "@ui/switch";
import { Settings, Save, History, PlusCircle } from "lucide-react";
import { ReactNode, useState } from "react";
import { useParams } from "react-router-dom";
const ServersSelector = ({
selected,
onSelect,
}: {
selected: string | undefined;
onSelect: (serverId: string) => void;
}) => {
const servers = useRead("ListServers", {}).data;
return (
<Select value={selected} onValueChange={onSelect}>
<SelectTrigger className="w-[400px]">
<SelectValue placeholder="Select A Server" />
</SelectTrigger>
<SelectContent>
{servers?.map((s) => (
<SelectItem key={s.id} value={s.id}>
{s.name}
</SelectItem>
))}
</SelectContent>
</Select>
);
};
export const DeploymentConfig = () => {
const id = useParams().deploymentId;
const deployment = useRead("GetDeployment", { id }).data;
const [update, set] = useState<Partial<Types.DeploymentConfig>>({});
const { mutate } = useWrite("UpdateDeployment");
const { mutate, isLoading } = useWrite("UpdateDeployment");
if (id && deployment?.config) {
return (
<Section
title="Config"
icon={<Settings className="w-4 h-4" />}
actions={
<div className="flex gap-4">
<Button variant="outline" intent="warning" onClick={() => set({})}>
<History className="w-4 h-4" />
</Button>
<Button
variant="outline"
intent="success"
onClick={() => mutate({ config: update, id })}
>
<Save className="w-4 h-4" />
</Button>
</div>
}
>
<Config config={deployment?.config as any} update={update} set={set} />
</Section>
);
} else {
// loading
return null;
}
console.log(deployment?.config);
if (!id || !deployment?.config) return null;
return (
<Section
title="Config"
icon={<Settings className="w-4 h-4" />}
actions={
<div className="flex gap-4">
<Button variant="outline" intent="warning" onClick={() => set({})}>
<History className="w-4 h-4" />
</Button>
<Button
variant="outline"
intent="success"
onClick={() => mutate({ config: update, id })}
>
<Save className="w-4 h-4" />
</Button>
</div>
}
>
<Configuration
config={deployment.config}
loading={isLoading}
update={update}
set={(input) => set((update) => ({ ...update, ...input }))}
layout={{
general: ["server_id", "image", "restart"],
networking: ["network", "ports"],
environment: ["environment", "skip_secret_interp"],
}}
overrides={{
server_id: (value, set) => (
<div className="flex items-center justify-between border-b pb-4">
Server
<ServersSelector
selected={value}
onSelect={(server_id) => set({ server_id })}
/>
</div>
),
image: () => <div>Image </div>,
environment: (vars, set) => (
<div className="flex flex-col gap-4 border-b pb-4">
{vars?.map((variable, i) => (
<div className="flex justify-between gap-4">
<Input
value={variable.variable}
placeholder="Variable Name"
onChange={(e) => {
vars[i].variable = e.target.value;
set({ environment: [...vars] });
}}
/>
=
<Input
value={variable.value}
placeholder="Variable Value"
onChange={(e) => {
vars[i].value = e.target.value;
set({ environment: [...vars] });
}}
/>
</div>
))}
<Button
variant="outline"
intent="success"
className="flex items-center gap-2"
onClick={() =>
set({
environment: [...(vars ?? []), { variable: "", value: "" }],
})
}
>
<PlusCircle className="w-4 h-4" />
Add
</Button>
</div>
),
}}
/>
</Section>
);
};
const fmt_field = (s: string) => s.split("_").join(" ");
const Configuration = <T extends Partial<Record<keyof T, unknown>>>({
config,
loading,
update,
layout,
overrides,
set,
}: {
config: T;
loading: boolean;
update: Partial<T>;
layout?: { [key: string]: Array<keyof T> };
overrides?: Partial<{
[P in keyof T]: (
value: T[P],
set: (input: Partial<T>) => void
) => ReactNode;
}>;
set: (input: Partial<T>) => void;
}) => {
const keys = Object.keys(layout ?? {});
const [show, setShow] = useState(keys[0]);
return (
<div className="flex gap-4">
{layout && (
<div className="flex flex-col gap-4 w-[300px]">
{Object.keys(layout).map((key) => (
<Button
key={key}
onClick={() => setShow(key)}
variant={key === show ? "secondary" : "outline"}
// disabled={config === show}
className="capitalize justify-start"
>
{fmt_field(key)}
</Button>
))}
</div>
)}
<Card className="w-full min-h-[50vh]">
<CardHeader className="border-b">
<CardTitle className="capitalize">{show}</CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-6 mt-6">
{layout?.[show].map((field) => {
if (typeof field !== "string") return null;
const val = update[field] ?? config[field];
const override = overrides?.[field];
if (!!override) return override(val, set);
if (typeof val === "string") {
return (
<div
key={field}
className="flex justify-between items-center border-b pb-4"
>
<div className="capitalize"> {fmt_field(field)} </div>
<Input
className="max-w-[400px]"
value={val}
onChange={(e) =>
set({ [field]: e.target.value } as Partial<T>)
}
disabled={loading}
/>
</div>
);
}
if (typeof val === "number") {
return (
<div
key={field}
className="flex justify-between items-center border-b pb-4"
>
<div className="capitalize"> {fmt_field(field)} </div>
<Input
className="max-w-[400px]"
type="number"
value={val}
onChange={(e) =>
set({ [field]: e.target.value } as Partial<T>)
}
disabled={loading}
/>
</div>
);
}
if (typeof val === "boolean") {
return (
<div
key={field}
className="flex justify-between items-center border-b pb-4"
>
<div className="capitalize"> {fmt_field(field)} </div>
<Switch
checked={val}
onCheckedChange={(e) => set({ [field]: e } as Partial<T>)}
disabled={loading}
/>
</div>
);
}
return (
<Configuration
config={config[field] as T}
update={update[field] as Partial<T>}
loading={loading}
set={set}
/>
);
})}
{!layout &&
Object.entries(config).map(([field, value]) => {
if (typeof field !== "string") return null;
const val = update[field as keyof T] ?? value;
const override = overrides?.[field as keyof T];
if (!!override) return override(val as T[keyof T], set);
if (typeof val === "string") {
return (
<div
key={field}
className="flex justify-between items-center border-b pb-4"
>
<div className="capitalize text-md">
{" "}
{fmt_field(field)}{" "}
</div>
<Input
className="max-w-[400px]"
value={val}
onChange={(e) =>
set({ [field]: e.target.value } as Partial<T>)
}
disabled={loading}
/>
</div>
);
}
if (typeof val === "number") {
return (
<div
key={field}
className="flex justify-between items-center border-b pb-4"
>
<div className="capitalize text-md">
{" "}
{fmt_field(field)}{" "}
</div>
<Input
className="max-w-[400px]"
type="number"
value={val}
onChange={(e) =>
set({ [field]: e.target.value } as Partial<T>)
}
disabled={loading}
/>
</div>
);
}
if (typeof val === "boolean") {
return (
<div key={field} className="flex flex-col gap-2">
<div className="capitalize"> {fmt_field(field)} </div>
<Switch
checked={val}
onCheckedChange={(e) => set({ [field]: e } as Partial<T>)}
disabled={loading}
/>
</div>
);
}
return (
<Configuration
config={config[field as keyof T] as T}
update={update[field as keyof T] as Partial<T>}
loading={loading}
set={set}
/>
);
})}
</CardContent>
</Card>
</div>
);
};