This commit is contained in:
Benjamin Toby 2026-03-10 13:42:50 +00:00
parent 2363f9dc1f
commit aeff7c6ff0
5 changed files with 137 additions and 67 deletions

View File

@ -13,8 +13,13 @@ import {
ParsedDeploymentServiceConfig, ParsedDeploymentServiceConfig,
} from "@/src/types"; } from "@/src/types";
import Select, { TWUISelectOptionObject } from "@/twui/components/form/Select"; import Select, { TWUISelectOptionObject } from "@/twui/components/form/Select";
import Input from "@/twui/components/form/Input";
import AceEditor from "@/twui/components/editors/AceEditor"; import AceEditor from "@/twui/components/editors/AceEditor";
import Row from "@/twui/components/layout/Row";
import Button from "@/twui/components/layout/Button";
import Modal from "@/twui/components/elements/Modal";
import H3 from "@/twui/components/layout/H3";
import Span from "@/twui/components/layout/Span";
import useStatus from "@/twui/components/hooks/useStatus";
type Props = { type Props = {
service: ParsedDeploymentServiceConfig; service: ParsedDeploymentServiceConfig;
@ -36,24 +41,23 @@ export default function ServiceClusterServerLogSelector({
const [log, setLog] = useState(log_strings?.[0]); const [log, setLog] = useState(log_strings?.[0]);
const [isCustomLog, setIsCustomLog] = useState(false); const [isCustomLog, setIsCustomLog] = useState(false);
const { open, setOpen } = useStatus();
const customLogRef = useRef(""); const customLogRef = useRef("");
useEffect(() => {
externalSetLog(log);
}, [log]);
return ( return (
<Stack className="w-full gap-2"> <Stack className="w-full gap-2 p-4">
<Row className="flex-nowrap">
<Select <Select
options={[ options={[
...(log_strings?.map( ...(log_strings?.map(
(l) => (l) =>
({ value: l, title: l }) as TWUISelectOptionObject, ({
value: l,
title: l,
}) as TWUISelectOptionObject,
) || []), ) || []),
{
value: "custom",
title: "--Custom--",
},
]} ]}
changeHandler={(v) => { changeHandler={(v) => {
if (v == "custom") { if (v == "custom") {
@ -64,14 +68,36 @@ export default function ServiceClusterServerLogSelector({
} }
}} }}
/> />
{isCustomLog ? ( <Modal
target={
<Button title="Enter custom command" size="small">
Custom
</Button>
}
setOpen={setOpen}
open={open}
>
<Stack>
<Stack className="gap-1">
<H3 className="admin-h3">Enter a custom Command</H3>
<Span variant="faded">
Enter a command to run a custom log
</Span>
</Stack>
<AceEditor <AceEditor
placeholder="Enter custom command" placeholder="Enter custom command"
onChange={(v) => { onChange={(v) => {
customLogRef.current = v; customLogRef.current = v;
}} }}
/> />
) : null} <Button title="Set Custom Command" onClick={()=>{
}}>
Set Custom Command
</Button>
</Stack>
</Modal>
</Row>
</Stack> </Stack>
); );
} }

View File

@ -1,5 +1,5 @@
import Stack from "@/twui/components/layout/Stack"; import Stack from "@/twui/components/layout/Stack";
import { useContext, useRef, useState } from "react"; import { ComponentProps, useContext, useRef, useState } from "react";
import { AppContext } from "@/src/pages/_app"; import { AppContext } from "@/src/pages/_app";
import { import {
NormalizedServerObject, NormalizedServerObject,
@ -13,13 +13,19 @@ import ServiceClusterServerViews from "./cluster-server-views";
import Row from "@/twui/components/layout/Row"; import Row from "@/twui/components/layout/Row";
import Button from "@/twui/components/layout/Button"; import Button from "@/twui/components/layout/Button";
import ServiceClusterServerLogSelector from "./cluster-server-log-selector"; import ServiceClusterServerLogSelector from "./cluster-server-log-selector";
import { twMerge } from "tailwind-merge";
type Props = { type Props = {
service: ParsedDeploymentServiceConfig; service: ParsedDeploymentServiceConfig;
server: NormalizedServerObject; server: NormalizedServerObject;
wrapperProps?: ComponentProps<typeof Stack>;
}; };
export default function ServiceClusterServer({ service, server }: Props) { export default function ServiceClusterServer({
service,
server,
wrapperProps,
}: Props) {
const { pageProps } = useContext(AppContext); const { pageProps } = useContext(AppContext);
const elementRef = useRef<HTMLDivElement>(undefined); const elementRef = useRef<HTMLDivElement>(undefined);
@ -31,12 +37,16 @@ export default function ServiceClusterServer({ service, server }: Props) {
const [log, setLog] = useState<string>(); const [log, setLog] = useState<string>();
return ( return (
<Stack className="gap-0"> <Stack
<Row className="w-full justify-between"> {...wrapperProps}
{/* <Row> className={twMerge("gap-0", wrapperProps?.className)}
<code>{server.private_ip}</code> >
</Row> */} <Row className="w-full justify-between p-4 -mb-6">
<Row> <Row>
<code>{server.private_ip}</code>
</Row>
<Row className="">
{ServerTerminalTargets.map((targ, index) => { {ServerTerminalTargets.map((targ, index) => {
const is_active = targ.name == target; const is_active = targ.name == target;

View File

@ -25,7 +25,20 @@ export default function ServiceCluster({ service }: Props) {
return ( return (
<Stack className="w-full grid-cell col-span-1 gap-0"> <Stack className="w-full grid-cell col-span-1 gap-0">
<Row className="p-4"> <Row className="nested-grid-frame xl:grid-cols-2">
{cluster_servers?.map((server, index) => {
return (
<ServiceClusterServer
server={server}
service={service}
key={index}
wrapperProps={{ className: "grid-cell" }}
/>
);
})}
</Row>
{/* <Row className="p-4">
{cluster_servers?.map((server, index) => { {cluster_servers?.map((server, index) => {
const is_active = const is_active =
server?.private_ip == targetServer?.private_ip; server?.private_ip == targetServer?.private_ip;
@ -55,7 +68,7 @@ export default function ServiceCluster({ service }: Props) {
{targetServer ? ( {targetServer ? (
<ServiceClusterServer server={targetServer} service={service} /> <ServiceClusterServer server={targetServer} service={service} />
) : undefined} ) : undefined} */}
</Stack> </Stack>
); );
} }

View File

@ -11,6 +11,7 @@ import Row from "@/twui/components/layout/Row";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import Select from "@/twui/components/form/Select"; import Select from "@/twui/components/form/Select";
import ServiceClusterServer from "../(partials)/cluster-server"; import ServiceClusterServer from "../(partials)/cluster-server";
import H2 from "@/twui/components/layout/H2";
export default function ServiceClusters() { export default function ServiceClusters() {
const { pageProps } = useContext(AppContext); const { pageProps } = useContext(AppContext);
@ -36,6 +37,11 @@ export default function ServiceClusters() {
<Stack className="w-full nested-grid-frame grid-cols-1"> <Stack className="w-full nested-grid-frame grid-cols-1">
<Stack className="grid-cell gap-0"> <Stack className="grid-cell gap-0">
<Row className="p-4 flex-nowrap"> <Row className="p-4 flex-nowrap">
<Row className="w-full justify-between">
<Row>
<H2>Cluster #{clusterIndex}</H2>
</Row>
<Row>
<Select <Select
options={all_services.map((srv) => ({ options={all_services.map((srv) => ({
value: srv?.service_name!, value: srv?.service_name!,
@ -45,15 +51,18 @@ export default function ServiceClusters() {
className: `max-w-[250px]`, className: `max-w-[250px]`,
}} }}
changeHandler={(v) => { changeHandler={(v) => {
setTargetCluster(undefined);
setTimeout(() => {
setTargetCluster( setTargetCluster(
all_services.find( all_services.find(
(s) => s?.service_name == v, (s) => s?.service_name == v,
) || undefined, ) || undefined,
); );
}, 200);
}} }}
/> />
{cluster_servers?.[0] ? ( {/* {cluster_servers?.[0] ? (
<Select <Select
options={cluster_servers.map((server) => ({ options={cluster_servers.map((server) => ({
value: server?.private_ip!, value: server?.private_ip!,
@ -63,14 +72,17 @@ export default function ServiceClusters() {
className: `max-w-[250px]`, className: `max-w-[250px]`,
}} }}
changeHandler={(v) => { changeHandler={(v) => {
setTargetServer(undefined);
setTimeout(() => {
setTargetServer( setTargetServer(
cluster_servers.find( cluster_servers.find(
(s) => s?.private_ip == v, (s) => s?.private_ip == v,
) || undefined, ) || undefined,
); );
}, 200);
}} }}
/> />
) : undefined} ) : undefined} */}
{/* {all_services.map((srv, index) => { {/* {all_services.map((srv, index) => {
const is_active = const is_active =
srv?.service_name == targetCluster?.service_name; srv?.service_name == targetCluster?.service_name;
@ -94,16 +106,20 @@ export default function ServiceClusters() {
); );
})} */} })} */}
</Row> </Row>
</Row>
</Row>
<hr /> <hr />
{targetCluster ? ( {targetCluster ? (
<ServiceCluster service={targetCluster} /> <ServiceCluster service={targetCluster} />
) : undefined} ) : undefined}
{targetServer && service ? (
{/* {targetCluster && targetServer && service ? (
<ServiceClusterServer <ServiceClusterServer
server={targetServer} server={targetServer}
service={service} service={service}
/> />
) : undefined} ) : undefined} */}
</Stack> </Stack>
</Stack> </Stack>
); );

View File

@ -12,7 +12,7 @@
--color-dark: #000000; --color-dark: #000000;
--color-primary: #3ecf8e; --color-primary: #76e4ad;
--color-primary-hover: #3ecf8e; --color-primary-hover: #3ecf8e;
--color-primary-outline: #3ecf8e; --color-primary-outline: #3ecf8e;
--color-primary-text: #000000; --color-primary-text: #000000;
@ -120,6 +120,11 @@ code {
@apply text-xl font-semibold; @apply text-xl font-semibold;
} }
#admin-main h3,
.admin-h3 {
@apply text-lg font-semibold;
}
hr { hr {
@apply border-foreground-light/10 dark:border-foreground-dark/10; @apply border-foreground-light/10 dark:border-foreground-dark/10;
} }