diff --git a/src/components/FilterMenu/index.jsx b/src/components/FilterMenu/index.tsx similarity index 62% rename from src/components/FilterMenu/index.jsx rename to src/components/FilterMenu/index.tsx index 6a4a538..8c86df5 100644 --- a/src/components/FilterMenu/index.jsx +++ b/src/components/FilterMenu/index.tsx @@ -1,19 +1,29 @@ import TextField from "@mui/material/TextField"; import Autocomplete from '@mui/material/Autocomplete'; +import type { FilterOptionsState } from "@mui/material/useAutocomplete"; -export default function FilterMenu({type, value, setter, optionsHook, width}) { +interface FilterMenuProps { + type: string; + value: string; + params: Record; + optionsHook: Function; +} + + +export default function FilterMenu({type, value, params, optionsHook}: FilterMenuProps) { const query = optionsHook(); if (query.isError) console.error(query.error); const style = {textTransform: "capitalize"}; const label = type.replaceAll("_", " "); - if ( width ) style.width = width; - const onChange = (_, newValue) => { - setter({[type]: newValue}) + const onChange = (_: any, newValue: string | null) => { + // setter({[type]: newValue}) + // const newUrl = getUrl(context.urlPathname, updater(columnFilters), pagination); + // navigate(newUrl.pathname + newUrl.search); }; - const filterOptions = (options, { inputValue }) => { + const filterOptions = (options: string[], { inputValue }: FilterOptionsState) => { if (!inputValue) return options; - let result = []; + let result: string[] = []; result.push(...options.filter((item) => item === inputValue)); result.push( ...options.filter((item) => { @@ -40,8 +50,6 @@ export default function FilterMenu({type, value, setter, optionsHook, width}) { onChange={onChange} options={query.data || []} renderInput={(params) => } - className={classes.filterMenu} - style={style} size="small" /> ); diff --git a/src/components/MachineTypeSelector/index.tsx b/src/components/MachineTypeSelector/index.tsx new file mode 100644 index 0000000..c89122e --- /dev/null +++ b/src/components/MachineTypeSelector/index.tsx @@ -0,0 +1,35 @@ +import { usePageContext } from 'vike-react/usePageContext' +import { navigate } from 'vike/client/router' + +const machineTypes = [ + "smithi", + "mira", +] + +export default function MachineTypeSelector() { + const context = usePageContext(); + const selected = context.urlParsed.search.machine_type; + + const getUrl = (machine_type: string) => { + const newUrl = new URL(context.urlPathname, window.location.origin); + newUrl.searchParams.set("machine_type", machine_type); + return newUrl; + }; + const onChange = (value: string) => { + const newUrl = getUrl(value); + navigate(newUrl.pathname + newUrl.search); + + }; + return ( + <> + Machine Type + + + ) +} diff --git a/src/components/NodeList/index.tsx b/src/components/NodeList/index.tsx index 7636189..8d8ba48 100644 --- a/src/components/NodeList/index.tsx +++ b/src/components/NodeList/index.tsx @@ -1,4 +1,3 @@ -import type { UseQueryResult } from "@tanstack/react-query"; import { useMaterialReactTable, MaterialReactTable, @@ -6,8 +5,8 @@ import { } from 'material-react-table'; import Link from '@mui/material/Link'; -import type { Node } from "../../lib/paddles.d"; -import { formatDate } from "../../lib/utils"; +import type { Node } from "#src/lib/paddles.d"; +import { formatDate } from "#src/lib/utils"; import useDefaultTableOptions from "../../lib/table"; @@ -43,7 +42,7 @@ export const columns: MRT_ColumnDef[] = [ header: "locked since", filterVariant: 'date', sortingFn: "datetime", - accessorFn: (row: Node) => formatDate(row.locked_since), + accessorFn: (row: Node) => row.locked_since? formatDate(row.locked_since): "", size: 55, enableColumnFilter: false, }, @@ -78,16 +77,11 @@ export const columns: MRT_ColumnDef[] = [ }, ]; -interface NodeListProps { - query: UseQueryResult; -} - -export default function NodeList({ query }: NodeListProps) { +export default function NodeList({nodes}: {nodes: Node[]}) { const options = useDefaultTableOptions(); options.state = {}; options.state.columnVisibility = {}; - const data = query.data || []; - if ( data.length <= 1 ) { + if ( nodes.length <= 1 ) { options.enableFilters = false; options.enablePagination = false; options.enableTableFooter = false; @@ -97,17 +91,17 @@ export default function NodeList({ query }: NodeListProps) { name: false, }; } - if ( new Set(data.map(node => node.machine_type)).size === 1 ) { + if ( new Set(nodes.map(node => node.machine_type)).size === 1 ) { options.state.columnVisibility.machine_type = false; } - if ( new Set(data.map(node => node.arch)).size === 1 ) { + if ( new Set(nodes.map(node => node.arch)).size === 1 ) { options.state.columnVisibility.arch = false; } const table = useMaterialReactTable({ ...options, columns, - data: data, - rowCount: data.length, + data: nodes, + rowCount: nodes.length, enableFacetedValues: true, initialState: { ...options.initialState, @@ -128,7 +122,6 @@ export default function NodeList({ query }: NodeListProps) { }, state: { ...options.state, - isLoading: query.isLoading || query.isFetching, }, muiTableBodyRowProps: ({row}) => { let className = "info"; diff --git a/src/pages/Node/index.tsx b/src/pages/Node/index.tsx deleted file mode 100644 index a781710..0000000 --- a/src/pages/Node/index.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useParams } from "react-router-dom"; -import { useSearchParams } from "react-router-dom"; -import Typography from "@mui/material/Typography"; -import { Helmet } from "react-helmet"; - -import JobList from "../../components/JobList"; -import NodeList from "../../components/NodeList"; -import type { NodeParams } from "../../lib/paddles.d"; - -import { useNode, useNodeJobs } from "../../lib/paddles"; - -export default function Node() { - const [params, _] = useSearchParams({ - page: "", - pageSize: "", - }); - const { name } = useParams(); - const detailsQuery = useNode(name === undefined ? "" : name); - const jobsQuery = useNodeJobs(name === undefined ? "" : name, params); - - if (detailsQuery === null) return 404; - if (detailsQuery.isError) return null; - - if (jobsQuery === null) return 404; - if (jobsQuery.isError) return null; - - return ( -
- - Node - Pulpito - - - Node: {name} - - -
-
- - -
-
-
- ); -} diff --git a/src/pages/Nodes/index.tsx b/src/pages/Nodes/index.tsx deleted file mode 100644 index a72646c..0000000 --- a/src/pages/Nodes/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { useSearchParams } from "react-router-dom"; -import Typography from "@mui/material/Typography"; -import { Helmet } from "react-helmet"; - -import FilterMenu from "../../components/FilterMenu"; -import NodeList from "../../components/NodeList"; - -import { useMachineTypes } from "../../lib/paddles"; -import { useNodes } from "../../lib/paddles"; - - -export default function Nodes() { - const [params, setParams] = useSearchParams({ - machine_type: "", - }); - const machine_type = params.get("machine_type"); - const query = useNodes(machine_type || ""); - if (query.isError) return null; - - - return ( -
- - Nodes - Pulpito - - - Nodes - -
-
-
- - Filter by: - -
- -
-
- -
- ); -} diff --git a/src/pages/nodes/+Page.tsx b/src/pages/nodes/+Page.tsx new file mode 100644 index 0000000..1ee8cb5 --- /dev/null +++ b/src/pages/nodes/+Page.tsx @@ -0,0 +1,33 @@ +import { useData } from 'vike-react/useData' +import { Config } from 'vike-react/Config' + +import Typography from "@mui/material/Typography"; + +import MachineTypeSelector from "#src/components/MachineTypeSelector"; +import NodeList from "../../components/NodeList"; + +import type { NodesResponse } from "./+data" + + +export default function Nodes() { + const data = useData(); + return ( +
+ + + Nodes + +
+
+
+ + Filter by: + +
+ +
+
+ +
+ ); +} diff --git a/src/pages/nodes/+data.tsx b/src/pages/nodes/+data.tsx new file mode 100644 index 0000000..8d35d75 --- /dev/null +++ b/src/pages/nodes/+data.tsx @@ -0,0 +1,16 @@ +import type { PageContext } from 'vike/types' + +import { getURL } from "#src/lib/paddles"; +import type { Node } from "#src/lib/paddles.d"; + +export type NodesResponse = { + nodes: Node[]; +} + +export default async function data(pageContext: PageContext): Promise { + const url = getURL('/nodes/', pageContext.urlParsed.search); + const response = await fetch(url); + if ( response.ok ) return {nodes: await response.json()}; + return {nodes: []}; +} + diff --git a/src/pages/nodes/@name/+Page.tsx b/src/pages/nodes/@name/+Page.tsx new file mode 100644 index 0000000..89b4d30 --- /dev/null +++ b/src/pages/nodes/@name/+Page.tsx @@ -0,0 +1,31 @@ +import { useData } from 'vike-react/useData' +import { usePageContext } from 'vike-react/usePageContext' +import { Config } from 'vike-react/Config' + +import Typography from "@mui/material/Typography"; + +import JobList from "#src/components/JobList"; +import NodeList from "#src/components/NodeList"; + +import { type NodeResponse } from "./+data" + +export default function Node() { + const data = useData(); + const context = usePageContext(); + const name = context.routeParams.name; + return ( +
+ + + Node: {name} + + +
+
+ + +
+
+
+ ); +} diff --git a/src/pages/nodes/@name/+data.tsx b/src/pages/nodes/@name/+data.tsx new file mode 100644 index 0000000..9dc62aa --- /dev/null +++ b/src/pages/nodes/@name/+data.tsx @@ -0,0 +1,25 @@ +import type { PageContext } from 'vike/types' +import { render } from 'vike/abort' + +import { getURL } from "#src/lib/paddles"; +import type { Job, Node } from "#src/lib/paddles.d"; + +export type NodeResponse = { + jobs: Job[]; + nodes: Node[]; +} + +export default async function data(pageContext: PageContext): Promise { + const nodeUrl = getURL(`/nodes/${pageContext.routeParams.name}`, pageContext.urlParsed.search); + const jobsUrl = getURL(`/nodes/${pageContext.routeParams.name}/jobs`, pageContext.urlParsed.search); + const [nodeResponse, jobsResponse] = await Promise.all([fetch(nodeUrl), fetch(jobsUrl)]); + const result: NodeResponse = {jobs: [], nodes: []}; + if ( nodeResponse.status === 404 ) throw render( + nodeResponse.status, + `Node "${pageContext.routeParams.name}" does not exist` + ); + else if ( nodeResponse.ok ) result.nodes = [await nodeResponse.json()]; + if ( jobsResponse.ok ) result.jobs = await jobsResponse.json(); + return result; +} +