Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ helm-charts
.editorconfig
.idea
coverage*
# adopted from https://bun.sh/guides/ecosystem/docker
# adopted from https://bun.sh/guides/ecosystem/docker
.env.development
1 change: 1 addition & 0 deletions .env.development.ignore
22 changes: 11 additions & 11 deletions .github/workflows/ts-format-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ jobs:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v1
- name: Setup Bun
uses: oven-sh/setup-bun@v1

- name: Install Prettier
run: bun install
- name: Install Prettier
run: bun install

- name: Check for formatting errors
run: bun run format-check
- name: Check for TS errors
run: bun run tsc
- name: Check for formatting errors
run: bun run format-check

- name: Check for TS errors
run: bun run tsc --noEmit
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ yarn-error.log*

#
.env.dev
.env.development

# ide
.vscode

# npm package-lock.json (we use Bun)
package-lock.json
package-lock.json
Binary file modified bun.lockb
Binary file not shown.
76 changes: 39 additions & 37 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,70 +9,72 @@
"dev": "vite --port 3000",
"build": "vite build",
"format": "prettier --write .",
"format-check": "prettier --check ."
"format-check": "prettier --check .",
"check": "prettier --check . && tsc --noEmit"
},
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@iconify/react": "^4.1.1",
"@kthcloud/go-deploy-types": "^1.0.24",
"@mui/icons-material": "^5.15.20",
"@mui/lab": "^5.0.0-alpha.170",
"@mui/material": "^5.15.20",
"@kthcloud/go-deploy-types": "^1.0.25",
"@mui/icons-material": "^5.18.0",
"@mui/lab": "^5.0.0-alpha.177",
"@mui/material": "^5.18.0",
"@mui/material-next": "^6.0.0-alpha.126",
"@mui/x-tree-view": "^7.7.0",
"@mui/x-tree-view": "^7.29.10",
"@react-keycloak/web": "^3.4.0",
"@react-three/fiber": "^8.16.8",
"@react-three/fiber": "^8.18.0",
"@sanity/eventsource": "^5.0.2",
"@types/crypto-js": "^4.2.2",
"@types/lodash": "^4.17.23",
"@types/numeral": "^2.0.5",
"@types/punycode": "^2.1.4",
"@types/three": "^0.164.1",
"apexcharts": "^3.49.1",
"bun": "^1.2.22",
"apexcharts": "^3.54.1",
"bun": "^1.3.9",
"change-case": "^5.4.4",
"crypto-js": "^4.2.0",
"http-status-codes": "^2.3.0",
"i18next": "^23.11.5",
"i18next-browser-languagedetector": "^7.2.1",
"js-base64": "^3.7.7",
"i18next": "^23.16.8",
"i18next-browser-languagedetector": "^7.2.2",
"js-base64": "^3.7.8",
"keycloak-js": "^24.0.5",
"lodash": "^4.17.21",
"lodash": "^4.17.23",
"million": "latest",
"notistack": "^3.0.1",
"notistack": "^3.0.2",
"numeral": "^2.0.6",
"punycode": "^2.3.1",
"react": "^18.3.1",
"react-apexcharts": "^1.4.1",
"react-cookie": "^7.1.4",
"react-apexcharts": "^1.9.0",
"react-cookie": "^7.2.2",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.3.1",
"react-helmet-async": "^2.0.5",
"react-i18next": "^14.1.2",
"react-router-dom": "^6.23.1",
"simplebar": "^6.2.7",
"simplebar-react": "^3.2.6",
"react-i18next": "^14.1.3",
"react-router-dom": "^6.30.3",
"simplebar": "^6.3.3",
"simplebar-react": "^3.3.2",
"three": "^0.164.1",
"three-stdlib": "^2.30.3",
"yaml": "^2.4.5"
"three-stdlib": "^2.36.1",
"yaml": "^2.8.2"
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@types/bun": "^1.1.17",
"@types/react": "^18.3.3",
"@types/bun": "^1.3.9",
"@types/react": "^18.3.28",
"@types/react-copy-to-clipboard": "^5.0.7",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.13.0",
"@typescript-eslint/parser": "^7.13.0",
"@vitejs/plugin-react": "^4.3.1",
"@vitejs/plugin-react-swc": "^3.7.0",
"eslint": "^9.35.0",
"eslint-plugin-react": "^7.34.2",
"@types/react-dom": "^18.3.7",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"@vitejs/plugin-react": "^4.7.0",
"@vitejs/plugin-react-swc": "^3.11.0",
"eslint": "^9.39.2",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"prettier": "^3.6.2",
"eslint-plugin-react-refresh": "^0.4.26",
"prettier": "^3.8.1",
"prettier-plugin-nginx": "^1.0.3",
"typescript": "^5.4.5",
"vite": "^7.1.6"
"typescript": "^5.9.3",
"vite": "^7.3.1"
}
}
73 changes: 73 additions & 0 deletions src/api/deploy/gpuClaims.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { GpuClaimCreate } from "../../temporaryTypesRemoveMe";
import { Jwt } from "../../types";

export const listGpuClaims = async (token: Jwt, detailed?: boolean) => {
const detailedQuery = detailed
? `?detailed=${encodeURIComponent(detailed)}`
: "";
const url = `${import.meta.env.VITE_DEPLOY_API_URL}/gpuClaims${detailedQuery}`;
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});

const result = await response.json();

if (!Array.isArray(result)) {
throw new Error("Error listing GPU claims, response was not an array");
}

result.sort((a: any, b: any) => {
return a.id < b.id ? -1 : 1;
});

return result;
};

export const getGpuClaim = async (token: Jwt, gpuClaimId: string) => {
const url = `${import.meta.env.VITE_DEPLOY_API_URL}/gpuClaims/${gpuClaimId}`;
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const result = await response.json();
if (typeof result !== "object") {
throw new Error("Error getting GPU claim, response was not an object");
}
return result;
};

export const deleteGpuClaim = async (token: Jwt, gpuClaimId: string) => {
const url = `${import.meta.env.VITE_DEPLOY_API_URL}/gpuClaims/${gpuClaimId}`;
const response = await fetch(url, {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
},
});
const result = await response.json();
if (typeof result !== "object") {
throw new Error("Error deleting GPU claim, response was not an object");
}
return result;
};

export const createGpuClaim = async (token: Jwt, gpuClaim: GpuClaimCreate) => {
const url = `${import.meta.env.VITE_DEPLOY_API_URL}/gpuClaims`;
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(gpuClaim),
});
const result = await response.json();
if (typeof result !== "object") {
throw new Error("Error creating GPU claim, response was not an object");
}
return result;
};
80 changes: 80 additions & 0 deletions src/components/admin/ClusterOverviewTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
Paper,
Stack,
Typography,
Card,
CardContent,
Chip,
Divider,
} from "@mui/material";
import useResource from "../../hooks/useResource";
import { useTranslation } from "react-i18next";
import DRAConfigPanel from "./DRAConfigPanel";
import { useEffect, useState } from "react";
import { discover } from "../../api/deploy/discover";

export default function ClusterOverviewTab() {
const { t } = useTranslation();
const { zones } = useResource();
const [roles, setRoles] = useState<string[]>([]);

useEffect(() => {
discover().then((resp) => {
setRoles([...new Set([...resp.roles.map((r) => r.name), "admin"])]);
});
}, []);

return (
<Paper sx={{ p: 3 }}>
<Stack spacing={3}>
<Typography variant="h6">{t("clusters-overview")}</Typography>

{zones?.map((zone) => {
const hasDRA = zone.capabilities?.includes("dra");

return (
<Card key={zone.name} variant="outlined">
<CardContent>
<Stack spacing={2}>
{/* Header */}
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Typography variant="subtitle1" fontWeight={600}>
{zone.name}
</Typography>
<Typography variant="subtitle1" fontWeight={600}>
{zone.description}
</Typography>
</Stack>

{/* Capabilities */}
<Stack direction="row" spacing={1} flexWrap="wrap">
{zone.capabilities.map((cap) => (
<Chip
key={cap}
label={cap.toUpperCase()}
size="small"
color={cap === "dra" ? "primary" : "default"}
/>
))}
</Stack>

{/* Optional DRA section */}
{hasDRA && (
<>
<Divider />
<DRAConfigPanel zone={zone} roles={roles} />
</>
)}
</Stack>
</CardContent>
</Card>
);
})}
</Stack>
</Paper>
);
}
Loading