diff --git a/web/package.json b/web/package.json index a623e351ff..2ac897d936 100644 --- a/web/package.json +++ b/web/package.json @@ -121,9 +121,13 @@ "webpack-merge": "5.8.0" }, "dependencies": { + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", "@grafana/data": "^8.5.16", "@maji/react-prism": "^1.0.1", "@monaco-editor/react": "^4.4.5", + "@mui/icons-material": "^5.14.14", + "@mui/material": "^5.14.14", "@storybook/addon-storysource": "^6.5.16", "accounting": "^0.4.1", "apexcharts": "^3.24.0", @@ -142,6 +146,7 @@ "js-yaml": "3.14.0", "lodash": "4.17.21", "markdown-it": "^12.3.2", + "material-react-table": "^1.15.1", "monaco-editor": "^0.33.0", "monaco-editor-webpack-plugin": "^7.0.1", "node-polyfill-webpack-plugin": "^1.1.4", diff --git a/web/src/Root.tsx b/web/src/Root.tsx index 2e7930a438..2013369166 100644 --- a/web/src/Root.tsx +++ b/web/src/Root.tsx @@ -58,6 +58,7 @@ import SnapshotDetails from "@components/snapshots/SnapshotDetails"; import SnapshotRestore from "@components/snapshots/SnapshotRestore"; import AppSnapshots from "@components/snapshots/AppSnapshots"; import AppSnapshotRestore from "@components/snapshots/AppSnapshotRestore"; +import HelmVMViewNode from "@components/apps/HelmVMViewNode"; // react-query client const queryClient = new QueryClient(); @@ -100,6 +101,8 @@ type State = { selectedAppName: string | null; snapshotInProgressApps: string[]; themeState: ThemeState; + isKurl: boolean | null; + isHelmVM: boolean | null; }; let interval: ReturnType | undefined; @@ -131,6 +134,8 @@ const Root = () => { navbarLogo: null, }, app: null, + isKurl: null, + isHelmVM: null, } ); @@ -302,6 +307,8 @@ const Root = () => { adminConsoleMetadata: data.adminConsoleMetadata, featureFlags: data.consoleFeatureFlags, fetchingMetadata: false, + isKurl: data.isKurl, + isHelmVM: data.isHelmVM, }); }) .catch((err) => { @@ -531,6 +538,8 @@ const Root = () => { appSlugFromMetadata={state.appSlugFromMetadata || ""} fetchingMetadata={state.fetchingMetadata} onUploadSuccess={getAppsList} + isKurl={!!state.isKurl} + isHelmVM={!!state.isHelmVM} /> } /> @@ -573,16 +582,36 @@ const Root = () => { } /> } /> + {/* {state.adminConsoleMetadata?.isHelmVM && ( */} + + } + /> + {/* )} */} + {/* {(state.adminConsoleMetadata?.isKurl || + state.adminConsoleMetadata?.isHelmVM) && ( */} + ) : ( - + ) } /> + {/* )} */} + {/* {state.adminConsoleMetadata?.isHelmVM && ( */} + } + /> + {/* )} */} } @@ -761,12 +790,7 @@ const Root = () => { } /> - } + element={} /> {/* WHERE IS SELECTEDAPP */} {state.app?.isAppIdentityServiceSupported && ( diff --git a/web/src/components/UploadLicenseFile.tsx b/web/src/components/UploadLicenseFile.tsx index 19bc1cf95b..2fe1ed8a12 100644 --- a/web/src/components/UploadLicenseFile.tsx +++ b/web/src/components/UploadLicenseFile.tsx @@ -1,23 +1,23 @@ import React, { useEffect, useReducer } from "react"; -import { useNavigate } from "react-router-dom"; -import { Link } from "react-router-dom"; -import { KotsPageTitle } from "@components/Head"; -// TODO: upgrade this dependency -// @ts-ignore -import Dropzone from "react-dropzone"; +import { Link, useNavigate } from "react-router-dom"; import yaml from "js-yaml"; -import size from "lodash/size"; import isEmpty from "lodash/isEmpty"; import keyBy from "lodash/keyBy"; +import size from "lodash/size"; +// TODO: upgrade this dependency +// @ts-ignore +import Dropzone from "react-dropzone"; import Modal from "react-modal"; import Select from "react-select"; + +import { KotsPageTitle } from "@components/Head"; import { getFileContent } from "../utilities/utilities"; -import CodeSnippet from "./shared/CodeSnippet"; +import Icon from "./Icon"; import LicenseUploadProgress from "./LicenseUploadProgress"; +import CodeSnippet from "./shared/CodeSnippet"; import "../scss/components/troubleshoot/UploadSupportBundleModal.scss"; import "../scss/components/UploadLicenseFile.scss"; -import Icon from "./Icon"; type LicenseYaml = { spec: { @@ -26,17 +26,6 @@ type LicenseYaml = { }; }; -type Props = { - appsListLength: number; - appName: string; - appSlugFromMetadata: string; - fetchingMetadata: boolean; - isBackupRestore?: boolean; - onUploadSuccess: () => Promise; - logo: string | null; - snapshot?: { name: string }; -}; - type SelectedAppToInstall = { label: string; value: string; @@ -68,6 +57,20 @@ type UploadLicenseResponse = { slug: string; success?: boolean; }; + +type Props = { + appsListLength: number; + appName: string; + appSlugFromMetadata: string; + fetchingMetadata: boolean; + isBackupRestore?: boolean; + onUploadSuccess: () => Promise; + logo: string | null; + snapshot?: { name: string }; + isHelmVM: boolean; + isKurl: boolean; +}; + const UploadLicenseFile = (props: Props) => { const [state, setState] = useReducer( (currentState: State, newState: Partial) => ({ @@ -264,6 +267,11 @@ const UploadLicenseFile = (props: Props) => { return; } + if (props.isHelmVM && !props.isKurl) { + navigate(`/${data.slug}/cluster/manage`, { replace: true }); + return; + } + if (data.hasPreflight) { navigate(`/${data.slug}/preflight`, { replace: true }); return; diff --git a/web/src/components/apps/HelmVMClusterManagement.jsx b/web/src/components/apps/HelmVMClusterManagement.jsx deleted file mode 100644 index 4d1712f258..0000000000 --- a/web/src/components/apps/HelmVMClusterManagement.jsx +++ /dev/null @@ -1,544 +0,0 @@ -import React, { Component, Fragment } from "react"; -import classNames from "classnames"; -import dayjs from "dayjs"; -import { KotsPageTitle } from "@components/Head"; -import CodeSnippet from "../shared/CodeSnippet"; -import HelmVMNodeRow from "./HelmVMNodeRow"; -import Loader from "../shared/Loader"; -import { rbacRoles } from "../../constants/rbac"; -import { Utilities } from "../../utilities/utilities"; -import { Repeater } from "../../utilities/repeater"; -import ErrorModal from "../modals/ErrorModal"; -import Modal from "react-modal"; - -import "@src/scss/components/apps/HelmVMClusterManagement.scss"; -import Icon from "../Icon"; - -export class HelmVMClusterManagement extends Component { - state = { - generating: false, - command: "", - expiry: null, - displayAddNode: false, - selectedNodeType: "primary", - generateCommandErrMsg: "", - helmvm: null, - getNodeStatusJob: new Repeater(), - deletNodeError: "", - confirmDeleteNode: "", - showConfirmDrainModal: false, - nodeNameToDrain: "", - drainingNodeName: null, - drainNodeSuccessful: false, - }; - - componentDidMount() { - this.getNodeStatus(); - this.state.getNodeStatusJob.start(this.getNodeStatus, 1000); - } - - componentWillUnmount() { - this.state.getNodeStatusJob.stop(); - } - - getNodeStatus = async () => { - try { - const res = await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, { - headers: { - Accept: "application/json", - }, - credentials: "include", - method: "GET", - }); - if (!res.ok) { - if (res.status === 401) { - Utilities.logoutUser(); - return; - } - console.log( - "failed to get node status list, unexpected status code", - res.status - ); - return; - } - const response = await res.json(); - this.setState({ - helmvm: response, - // if cluster doesn't support ha, then primary will be disabled. Force into secondary - selectedNodeType: !response.ha - ? "secondary" - : this.state.selectedNodeType, - }); - return response; - } catch (err) { - console.log(err); - throw err; - } - }; - - deleteNode = (name) => { - this.setState({ - confirmDeleteNode: name, - }); - }; - - cancelDeleteNode = () => { - this.setState({ - confirmDeleteNode: "", - }); - }; - - reallyDeleteNode = () => { - const name = this.state.confirmDeleteNode; - this.cancelDeleteNode(); - - fetch(`${process.env.API_ENDPOINT}/helmvm/nodes/${name}`, { - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - method: "DELETE", - }) - .then(async (res) => { - if (!res.ok) { - if (res.status === 401) { - Utilities.logoutUser(); - return; - } - this.setState({ - deleteNodeError: `Delete failed with status ${res.status}`, - }); - } - }) - .catch((err) => { - console.log(err); - }); - }; - - generateWorkerAddNodeCommand = async () => { - this.setState({ - generating: true, - command: "", - expiry: null, - generateCommandErrMsg: "", - }); - - fetch( - `${process.env.API_ENDPOINT}/helmvm/generate-node-join-command-secondary`, - { - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - method: "POST", - } - ) - .then(async (res) => { - if (!res.ok) { - this.setState({ - generating: false, - generateCommandErrMsg: `Failed to generate command with status ${res.status}`, - }); - } else { - const data = await res.json(); - this.setState({ - generating: false, - command: data.command, - expiry: data.expiry, - }); - } - }) - .catch((err) => { - console.log(err); - this.setState({ - generating: false, - generateCommandErrMsg: err ? err.message : "Something went wrong", - }); - }); - }; - - onDrainNodeClick = (name) => { - this.setState({ - showConfirmDrainModal: true, - nodeNameToDrain: name, - }); - }; - - drainNode = async (name) => { - this.setState({ showConfirmDrainModal: false, drainingNodeName: name }); - fetch(`${process.env.API_ENDPOINT}/helmvm/nodes/${name}/drain`, { - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - method: "POST", - }) - .then(async (res) => { - this.setState({ drainNodeSuccessful: true }); - setTimeout(() => { - this.setState({ - drainingNodeName: null, - drainNodeSuccessful: false, - }); - }, 3000); - }) - .catch((err) => { - console.log(err); - this.setState({ - drainingNodeName: null, - drainNodeSuccessful: false, - }); - }); - }; - - generatePrimaryAddNodeCommand = async () => { - this.setState({ - generating: true, - command: "", - expiry: null, - generateCommandErrMsg: "", - }); - - fetch( - `${process.env.API_ENDPOINT}/helmvm/generate-node-join-command-primary`, - { - headers: { - "Content-Type": "application/json", - Accept: "application/json", - }, - credentials: "include", - method: "POST", - } - ) - .then(async (res) => { - if (!res.ok) { - this.setState({ - generating: false, - generateCommandErrMsg: `Failed to generate command with status ${res.status}`, - }); - } else { - const data = await res.json(); - this.setState({ - generating: false, - command: data.command, - expiry: data.expiry, - }); - } - }) - .catch((err) => { - console.log(err); - this.setState({ - generating: false, - generateCommandErrMsg: err ? err.message : "Something went wrong", - }); - }); - }; - - onAddNodeClick = () => { - this.setState( - { - displayAddNode: true, - }, - async () => { - await this.generateWorkerAddNodeCommand(); - } - ); - }; - - onSelectNodeType = (event) => { - const value = event.currentTarget.value; - this.setState( - { - selectedNodeType: value, - }, - async () => { - if (this.state.selectedNodeType === "secondary") { - await this.generateWorkerAddNodeCommand(); - } else { - await this.generatePrimaryAddNodeCommand(); - } - } - ); - }; - - ackDeleteNodeError = () => { - this.setState({ deleteNodeError: "" }); - }; - - render() { - const { helmvm } = this.state; - const { displayAddNode, generateCommandErrMsg } = this.state; - - if (!helmvm) { - return ( -
- -
- ); - } - - return ( -
- -
-
-
-

- Your nodes -

-
- {helmvm?.nodes && - helmvm?.nodes.map((node, i) => ( - - ))} -
-
- {helmvm?.isHelmVMEnabled && - Utilities.sessionRolesHasOneOf([rbacRoles.CLUSTER_ADMIN]) ? ( - !displayAddNode ? ( -
- -
- ) : ( -
-
-

- Add a Node -

-
-
-
- - -
-
- - -
-
- {this.state.generating && ( -
- -
- )} - {!this.state.generating && this.state.command?.length > 0 ? ( - -

- Run this command on the node you wish to join the - cluster -

- - Command has been copied to your clipboard - - } - > - {[this.state.command.join(" \\\n ")]} - - {this.state.expiry && ( - - {`Expires on ${dayjs(this.state.expiry).format( - "MMM Do YYYY, h:mm:ss a z" - )} UTC${(-1 * new Date().getTimezoneOffset()) / 60}`} - - )} -
- ) : ( - - {generateCommandErrMsg && ( -
- - {generateCommandErrMsg} - -
- )} -
- )} -
- ) - ) : null} -
-
- {this.state.deleteNodeError && ( - - )} - -
-

- Deleting this node may cause data loss. Are you sure you want to - proceed? -

-
- - -
-
-
- {this.state.showConfirmDrainModal && ( - - this.setState({ - showConfirmDrainModal: false, - nodeNameToDrain: "", - }) - } - shouldReturnFocusAfterClose={false} - contentLabel="Confirm Drain Node" - ariaHideApp={false} - className="Modal MediumSize" - > -
-

- Are you sure you want to drain {this.state.nodeNameToDrain}? -

-

- Draining this node may cause data loss. If you want to delete{" "} - {this.state.nodeNameToDrain} you must disconnect it after it has - been drained. -

-
- - -
-
-
- )} -
- ); - } -} - -export default HelmVMClusterManagement; diff --git a/web/src/components/apps/HelmVMClusterManagement.tsx b/web/src/components/apps/HelmVMClusterManagement.tsx new file mode 100644 index 0000000000..ed0fcfa3f2 --- /dev/null +++ b/web/src/components/apps/HelmVMClusterManagement.tsx @@ -0,0 +1,577 @@ +import classNames from "classnames"; +import MaterialReactTable from "material-react-table"; +import React, { ChangeEvent, useMemo, useReducer, useState } from "react"; +import Modal from "react-modal"; +import { useQuery } from "react-query"; +import { Link, useParams } from "react-router-dom"; + +import { KotsPageTitle } from "@components/Head"; +import { useApps } from "@features/App"; +import { rbacRoles } from "../../constants/rbac"; +import { Utilities } from "../../utilities/utilities"; +import Icon from "../Icon"; +import CodeSnippet from "../shared/CodeSnippet"; + +import "@src/scss/components/apps/HelmVMClusterManagement.scss"; + +const testData = { + isHelmVMEnabled: true, + ha: false, + nodes: [ + { + name: "test-helmvm-node", + isConnected: true, + isReady: true, + isPrimaryNode: true, + canDelete: false, + kubeletVersion: "v1.28.2", + cpu: { + capacity: 8, + available: 7.466876775, + }, + memory: { + capacity: 31.33294677734375, + available: 24.23790740966797, + }, + pods: { + capacity: 110, + available: 77, + }, + labels: [ + "beta.kubernetes.io/arch:amd64", + "beta.kubernetes.io/os:linux", + "node-role.kubernetes.io/master:", + "node.kubernetes.io/exclude-from-external-load-balancers:", + "kubernetes.io/arch:amd64", + "kubernetes.io/hostname:laverya-kurl", + "kubernetes.io/os:linux", + "node-role.kubernetes.io/control-plane:", + ], + conditions: { + memoryPressure: false, + diskPressure: false, + pidPressure: false, + ready: true, + }, + }, + { + name: "test-helmvm-worker", + isConnected: true, + isReady: true, + isPrimaryNode: false, + canDelete: false, + kubeletVersion: "v1.28.2", + cpu: { + capacity: 4, + available: 3.761070507, + }, + memory: { + capacity: 15.50936508178711, + available: 11.742542266845703, + }, + pods: { + capacity: 110, + available: 94, + }, + labels: [ + "beta.kubernetes.io/arch:amd64", + "beta.kubernetes.io/os:linux", + "kubernetes.io/arch:amd64", + "kubernetes.io/os:linux", + "kurl.sh/cluster:true", + ], + conditions: { + memoryPressure: false, + diskPressure: false, + pidPressure: false, + ready: true, + }, + }, + ], +}; + +type State = { + displayAddNode: boolean; + confirmDeleteNode: string; + deleteNodeError: string; + showConfirmDrainModal: boolean; + nodeNameToDrain: string; + drainingNodeName: string | null; + drainNodeSuccessful: boolean; +}; + +const HelmVMClusterManagement = ({ + fromLicenseFlow = false, + appName, +}: { + fromLicenseFlow?: boolean; + appName?: string; +}) => { + const [state, setState] = useReducer( + (prevState: State, newState: Partial) => ({ + ...prevState, + ...newState, + }), + { + displayAddNode: false, + confirmDeleteNode: "", + deleteNodeError: "", + showConfirmDrainModal: false, + nodeNameToDrain: "", + drainingNodeName: null, + drainNodeSuccessful: false, + } + ); + const [selectedNodeTypes, setSelectedNodeTypes] = useState([]); + + const { data: appsData } = useApps(); + const app = appsData?.apps?.find((a) => a.name === appName); + const { slug } = useParams(); + + // #region queries + type NodesResponse = { + ha: boolean; + isHelmVMEnabled: boolean; + nodes: { + name: string; + isConnected: boolean; + isReady: boolean; + isPrimaryNode: boolean; + canDelete: boolean; + kubeletVersion: string; + cpu: { + capacity: number; + available: number; + }; + memory: { + capacity: number; + available: number; + }; + pods: { + capacity: number; + available: number; + }; + labels: string[]; + conditions: { + memoryPressure: boolean; + diskPressure: boolean; + pidPressure: boolean; + ready: boolean; + }; + }[]; + }; + + const { + data: nodesData, + isInitialLoading: nodesLoading, + error: nodesError, + } = useQuery({ + queryKey: "helmVmNodes", + queryFn: async () => { + const res = await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, { + headers: { + Accept: "application/json", + }, + credentials: "include", + method: "GET", + }); + if (!res.ok) { + if (res.status === 401) { + Utilities.logoutUser(); + } + console.log( + "failed to get node status list, unexpected status code", + res.status + ); + try { + const error = await res.json(); + throw new Error( + error?.error?.message || error?.error || error?.message + ); + } catch (err) { + throw new Error("Unable to fetch nodes, please try again later."); + } + } + return res.json(); + }, + refetchInterval: (data) => (data ? 1000 : 0), + retry: false, + }); + + type AddNodeCommandResponse = { + command: string; + expiry: string; + }; + + const { + data: generateAddNodeCommand, + isLoading: generateAddNodeCommandLoading, + error: generateAddNodeCommandError, + } = useQuery({ + queryKey: ["generateAddNodeCommand", selectedNodeTypes], + queryFn: async ({ queryKey }) => { + const [, nodeTypes] = queryKey; + const res = await fetch( + `${process.env.API_ENDPOINT}/helmvm/generate-node-join-command`, + { + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + credentials: "include", + method: "POST", + body: JSON.stringify({ + roles: nodeTypes, + }), + } + ); + if (!res.ok) { + if (res.status === 401) { + Utilities.logoutUser(); + } + console.log( + "failed to get generate node command, unexpected status code", + res.status + ); + try { + const error = await res.json(); + throw new Error( + error?.error?.message || error?.error || error?.message + ); + } catch (err) { + throw new Error( + "Unable to generate node join command, please try again later." + ); + } + } + return res.json(); + }, + enabled: selectedNodeTypes.length > 0, + }); + + // TODO: import useMutation + // const { + // mutate: addNodeType, + // isLoading: addNodeTypeLoading, + // error: addNodeTypeError, + // } = useMutation({ + // mutationFn: async () => { + // return ( + // await fetch(`${process.env.API_ENDPOINT}/helmvm/nodes`, { + // headers: { + // "Content-Type": "application/json", + // Accept: "application/json", + // }, + // credentials: "include", + // method: "POST", + // }) + // ).json(); + // }, + // }); + // #endregion + + const onAddNodeClick = () => { + setState({ + displayAddNode: true, + }); + }; + + // #region node type logic + const NODE_TYPES = ["controller"]; + + const determineDisabledState = () => { + // if (nodeType === "controller") { + // const numControllers = testData.nodes.reduce((acc, node) => { + // if (node.labels.includes("controller")) { + // acc += 1; + // } + // return acc; + // }, 0); + // return numControllers === 3; + // } + return false; + }; + + const handleSelectNodeType = (e: ChangeEvent) => { + let nodeType = e.currentTarget.value; + let types = selectedNodeTypes; + + if (selectedNodeTypes.includes(nodeType)) { + setSelectedNodeTypes(types.filter((type) => type !== nodeType)); + } else { + setSelectedNodeTypes([...types, nodeType]); + } + }; + // #endregion + + const columns = useMemo( + () => [ + { + accessorKey: "name", + header: "Name", + enableHiding: false, + enableColumnDragging: false, + size: 150, + }, + { + accessorKey: "roles", + header: "Role(s)", + size: 404, + }, + { + accessorKey: "status", + header: "Status", + size: 150, + }, + { + accessorKey: "disk", + header: "Disk", + size: 150, + }, + { + accessorKey: "cpu", + header: "CPU", + size: 150, + }, + { + accessorKey: "memory", + header: "Memory", + size: 150, + }, + { + accessorKey: "pause", + header: "Pause", + size: 100, + }, + { + accessorKey: "delete", + header: "Delete", + size: 100, + }, + ], + [] + ); + + const mappedNodes = useMemo(() => { + return (nodesData?.nodes || testData.nodes).map((n) => ({ + name: slug ? ( + + ) : ( + n.name + ), + roles: ( +
+ {n.labels.map((l) => ( + + {l} + + ))} +
+ ), + status: n.isReady ? "Ready" : "Not Ready", + disk: n.conditions.diskPressure ? "Disk Pressure" : "No Disk Pressure", + cpu: n.conditions.pidPressure ? "CPU Pressure" : "No CPU Pressure", + memory: n.conditions.memoryPressure + ? "Memory Pressure" + : "No Memory Pressure", + pause: ( + <> + + + ), + delete: ( + <> + + + ), + })); + }, [nodesData?.nodes?.toString()]); + // #endregion + + return ( +
+ +
+
+

+ Cluster Nodes +

+
+

+ This page lists the nodes that are configured and shows the + status/health of each. +

+ {Utilities.sessionRolesHasOneOf([rbacRoles.CLUSTER_ADMIN]) && ( + + )} +
+
+ {nodesLoading && ( +

+ Loading nodes... +

+ )} + {!nodesData && nodesError && ( +

+ {nodesError?.message} +

+ )} + {(nodesData?.nodes || testData?.nodes) && ( + + )} +
+ {fromLicenseFlow && ( + + Continue + + )} +
+
+ {/* MODALS */} + setState({ displayAddNode: false })} + contentLabel="Add Node" + className="Modal" + ariaHideApp={false} + > +
+
+

+ Add a Node +

+ setState({ displayAddNode: false })} + /> +
+

+ To add a node to this cluster, select the type of node you'd like to + add. Once you've selected a node type, we will generate a node join + command for you to use in the CLI. When the node successfully joins + the cluster, you will see it appear in the list of nodes on this + page. +

+
+ {NODE_TYPES.map((nodeType) => ( +
+ + +
+ ))} +
+
+ {generateAddNodeCommandLoading && ( +

+ Generating command... +

+ )} + {!generateAddNodeCommand && generateAddNodeCommandError && ( +

+ {generateAddNodeCommandError?.message} +

+ )} + {!generateAddNodeCommandLoading && generateAddNodeCommand?.command && ( + Copied! + } + > + {generateAddNodeCommand?.command || ""} + + )} +
+ {/* buttons */} +
+ +
+
+
+
+ ); +}; + +export default HelmVMClusterManagement; diff --git a/web/src/components/apps/HelmVMNodeRow.jsx b/web/src/components/apps/HelmVMNodeRow.jsx deleted file mode 100644 index 93f2c5489c..0000000000 --- a/web/src/components/apps/HelmVMNodeRow.jsx +++ /dev/null @@ -1,278 +0,0 @@ -import React from "react"; -import classNames from "classnames"; -import Loader from "../shared/Loader"; -import { rbacRoles } from "../../constants/rbac"; -import { getPercentageStatus, Utilities } from "../../utilities/utilities"; -import Icon from "../Icon"; - -export default function HelmVMNodeRow(props) { - const { node } = props; - - const DrainDeleteNode = () => { - const { drainNode, drainNodeSuccessful, drainingNodeName } = props; - if (drainNode && Utilities.sessionRolesHasOneOf(rbacRoles.DRAIN_NODE)) { - if ( - !drainNodeSuccessful && - drainingNodeName && - drainingNodeName === node?.name - ) { - return ( -
- - - - - Draining Node - -
- ); - } else if (drainNodeSuccessful && drainingNodeName === node?.name) { - return ( -
- - - Node successfully drained - -
- ); - } else { - return ( -
- -
- ); - } - } - }; - - return ( -
-
-
-

- {node?.name} -

- {node?.isPrimaryNode && ( - - Primary node - - )} -
-
-
-

- - {node?.isConnected ? "Connected" : "Disconnected"} -

-

-   -

-
-
-

- - {node?.pods?.available === -1 - ? `${node?.pods?.capacity} pods` - : `${ - node?.pods?.available === 0 - ? "0" - : node?.pods?.capacity - node?.pods?.available - } pods used`} -

- {node?.pods?.available !== -1 && ( -

- of {node?.pods?.capacity} pods total -

- )} -
-
-

- - {node?.cpu?.available === -1 - ? `${node?.cpu?.capacity} ${ - node?.cpu?.available === "1" ? "core" : "cores" - }` - : `${ - node?.cpu?.available === 0 - ? "0" - : (node?.cpu?.capacity - node?.cpu?.available).toFixed(1) - } ${ - node?.cpu?.available === "1" ? "core used" : "cores used" - }`} -

- {node?.pods?.available !== -1 && ( -

- of {node?.cpu?.capacity}{" "} - {node?.cpu?.available === "1" ? "core total" : "cores total"} -

- )} -
-
-

- - {node?.memory?.available === -1 - ? `${node?.memory?.capacity?.toFixed(1)} GB` - : `${ - node?.memory?.available === 0 - ? "0" - : ( - node?.memory?.capacity - node?.memory?.available - ).toFixed(1) - } GB used`} -

- {node?.pods?.available !== -1 && ( -

- of {node?.memory?.capacity?.toFixed(1)} GB total -

- )} -
-
-
-
-

- - {node?.kubeletVersion} -

-
-
-

- - {node?.conditions?.diskPressure - ? "No Space on Device" - : "No Disk Pressure"} -

-
-
-

- - {node?.conditions?.pidPressure - ? "Pressure on CPU" - : "No CPU Pressure"} -

-
-
-

- - {node?.conditions?.memoryPressure - ? "No Space on Memory" - : "No Memory Pressure"} -

-
-
- {/* LABELS */} -
- {node?.labels.length > 0 - ? node.labels.sort().map((label, i) => { - let labelToShow = label.replace(":", "="); - return ( -
- {labelToShow} -
- ); - }) - : null} -
-
-

- For more details run{" "} - - kubectl describe node {node?.name} - -

-
-
- -
- ); -} diff --git a/web/src/components/apps/HelmVMViewNode.jsx b/web/src/components/apps/HelmVMViewNode.jsx new file mode 100644 index 0000000000..a9b8bae245 --- /dev/null +++ b/web/src/components/apps/HelmVMViewNode.jsx @@ -0,0 +1,462 @@ +import { MaterialReactTable } from "material-react-table"; +import React, { useMemo } from "react"; +import { useQuery } from "react-query"; +import { Link, useParams } from "react-router-dom"; + +const testData = { + isHelmVMEnabled: true, + ha: false, + nodes: [ + { + name: "test-helmvm-node", + isConnected: true, + isReady: true, + isPrimaryNode: true, + canDelete: false, + kubeletVersion: "v1.28.2", + cpu: { + capacity: 8, + available: 7.466876775, + }, + memory: { + capacity: 31.33294677734375, + available: 24.23790740966797, + }, + pods: { + capacity: 110, + available: 77, + }, + labels: [ + "beta.kubernetes.io/arch:amd64", + "beta.kubernetes.io/os:linux", + "node-role.kubernetes.io/master:", + "node.kubernetes.io/exclude-from-external-load-balancers:", + "kubernetes.io/arch:amd64", + "kubernetes.io/hostname:laverya-kurl", + "kubernetes.io/os:linux", + "node-role.kubernetes.io/control-plane:", + ], + conditions: { + memoryPressure: false, + diskPressure: false, + pidPressure: false, + ready: true, + }, + podList: [ + { + metadata: { + name: "example-es-85fc9df74-g9jbn", + generateName: "example-es-85fc9df74-", + namespace: "helmvm", + uid: "1caba3fb-bd52-430a-9cff-0eb0939317fa", + resourceVersion: "40284", + creationTimestamp: "2023-10-17T16:22:37Z", + labels: { + app: "example", + component: "es", + "kots.io/app-slug": "laverya-minimal-kots", + "kots.io/backup": "velero", + "pod-template-hash": "85fc9df74", + }, + annotations: { + "cni.projectcalico.org/containerID": + "c3fa12aad2ed6f726ecda31f7f94d1224c9f50a805a9efc67aaf4959e464434c", + "cni.projectcalico.org/podIP": "10.244.45.141/32", + "cni.projectcalico.org/podIPs": "10.244.45.141/32", + "kots.io/app-slug": "laverya-minimal-kots", + }, + ownerReferences: [ + { + apiVersion: "apps/v1", + kind: "ReplicaSet", + name: "example-es-85fc9df74", + uid: "b5008bca-1ad0-4107-8603-397fc3be74f8", + controller: true, + blockOwnerDeletion: true, + }, + ], + }, + spec: { + volumes: [ + { + name: "kube-api-access-fhfc4", + projected: { + sources: [ + { + serviceAccountToken: { + expirationSeconds: 3607, + path: "token", + }, + }, + { + configMap: { + name: "kube-root-ca.crt", + items: [{ key: "ca.crt", path: "ca.crt" }], + }, + }, + { + downwardAPI: { + items: [ + { + path: "namespace", + fieldRef: { + apiVersion: "v1", + fieldPath: "metadata.namespace", + }, + }, + ], + }, + }, + ], + defaultMode: 420, + }, + }, + ], + containers: [ + { + name: "es", + image: + "docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.21", + envFrom: [{ configMapRef: { name: "example-config" } }], + resources: { + limits: { cpu: "500m", memory: "256Mi" }, + requests: { cpu: "50m", memory: "16Mi" }, + }, + volumeMounts: [ + { + name: "kube-api-access-fhfc4", + readOnly: true, + mountPath: "/var/run/secrets/kubernetes.io/serviceaccount", + }, + ], + terminationMessagePath: "/dev/termination-log", + terminationMessagePolicy: "File", + imagePullPolicy: "IfNotPresent", + }, + ], + restartPolicy: "Always", + terminationGracePeriodSeconds: 30, + dnsPolicy: "ClusterFirst", + serviceAccountName: "default", + serviceAccount: "default", + nodeName: "laverya-helmvm", + securityContext: {}, + imagePullSecrets: [{ name: "laverya-minimal-kots-registry" }], + schedulerName: "default-scheduler", + tolerations: [ + { + key: "node.kubernetes.io/not-ready", + operator: "Exists", + effect: "NoExecute", + tolerationSeconds: 300, + }, + { + key: "node.kubernetes.io/unreachable", + operator: "Exists", + effect: "NoExecute", + tolerationSeconds: 300, + }, + ], + priority: 0, + enableServiceLinks: true, + preemptionPolicy: "PreemptLowerPriority", + }, + status: { + phase: "Running", + conditions: [ + { + type: "Initialized", + status: "True", + lastProbeTime: null, + lastTransitionTime: "2023-10-17T16:22:37Z", + }, + { + type: "Ready", + status: "False", + lastProbeTime: null, + lastTransitionTime: "2023-10-17T19:55:16Z", + reason: "ContainersNotReady", + message: "containers with unready status: [es]", + }, + { + type: "ContainersReady", + status: "False", + lastProbeTime: null, + lastTransitionTime: "2023-10-17T19:55:16Z", + reason: "ContainersNotReady", + message: "containers with unready status: [es]", + }, + { + type: "PodScheduled", + status: "True", + lastProbeTime: null, + lastTransitionTime: "2023-10-17T16:22:37Z", + }, + ], + hostIP: "10.128.0.44", + podIP: "10.244.45.141", + podIPs: [{ ip: "10.244.45.141" }], + startTime: "2023-10-17T16:22:37Z", + containerStatuses: [ + { + name: "es", + state: { + waiting: { + reason: "CrashLoopBackOff", + message: + "back-off 5m0s restarting failed container=es pod=example-es-85fc9df74-g9jbn_helmvm(1caba3fb-bd52-430a-9cff-0eb0939317fa)", + }, + }, + lastState: { + terminated: { + exitCode: 137, + reason: "OOMKilled", + startedAt: "2023-10-17T19:55:11Z", + finishedAt: "2023-10-17T19:55:13Z", + containerID: + "containerd://9cce5c792b7ad61d040f7b8aca042d13a714100c75ebc40e71eb5444bbb65e83", + }, + }, + ready: false, + restartCount: 46, + image: + "docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.21", + imageID: + "docker.elastic.co/elasticsearch/elasticsearch-oss@sha256:86e7750c4d896d41bd638b6e510e0610b98fd9fa48f8caeeed8ccd8424b1dc9f", + containerID: + "containerd://9cce5c792b7ad61d040f7b8aca042d13a714100c75ebc40e71eb5444bbb65e83", + started: false, + }, + ], + qosClass: "Burstable", + }, + }, + ], + }, + { + name: "test-helmvm-worker", + isConnected: true, + isReady: true, + isPrimaryNode: false, + canDelete: false, + kubeletVersion: "v1.28.2", + cpu: { + capacity: 4, + available: 3.761070507, + }, + memory: { + capacity: 15.50936508178711, + available: 11.742542266845703, + }, + pods: { + capacity: 110, + available: 94, + }, + labels: [ + "beta.kubernetes.io/arch:amd64", + "beta.kubernetes.io/os:linux", + "kubernetes.io/arch:amd64", + "kubernetes.io/os:linux", + "kurl.sh/cluster:true", + ], + conditions: { + memoryPressure: false, + diskPressure: false, + pidPressure: false, + ready: true, + }, + }, + ], +}; + +const HelmVMViewNode = () => { + const { slug, nodeName } = useParams(); + const { data: nodeData } = useQuery({ + queryKey: ["helmVmNode", nodeName], + queryFn: async ({ queryKey }) => { + const [, nodeName] = queryKey; + return ( + await fetch(`${process.env.API_ENDPOINT}/helmvm/node/${nodeName}`, { + headers: { + Accept: "application/json", + }, + credentials: "include", + method: "GET", + }) + ).json(); + }, + onError: (err) => { + if (err.status === 401) { + Utilities.logoutUser(); + return; + } + console.log( + "failed to get node status list, unexpected status code", + err.status + ); + }, + onSuccess: (data) => { + setState({ + // if cluster doesn't support ha, then primary will be disabled. Force into secondary + selectedNodeType: !data.ha ? "secondary" : state.selectedNodeType, + }); + }, + config: { + retry: false, + }, + }); + + const node = nodeData || testData.nodes[0]; + + // #region table data + const columns = useMemo( + () => [ + { + accessorKey: "name", + header: "Name", + enableHiding: false, + enableColumnDragging: false, + size: 150, + }, + { + accessorKey: "status", + header: "Status", + size: 150, + }, + { + accessorKey: "disk", + header: "Disk", + size: 150, + }, + { + accessorKey: "cpu", + header: "CPU", + size: 150, + }, + { + accessorKey: "memory", + header: "Memory", + size: 150, + }, + { + accessorKey: "canDelete", + header: "Delete Pod", + size: 150, + }, + ], + [] + ); + + const mappedPods = useMemo(() => { + return node?.podList?.map((p) => ({ + name: p.metadata.name, + status: p.status.phase, + disk: null, + cpu: null, + memory: null, + canDelete: ( + <> + + + ), + })); + }, [node?.podList?.toString()]); + // #endregion + + return ( +
+ {/* Breadcrumbs */} +

+ + Cluster Nodes + {" "} + / {node?.name} +

+ {/* Node Info */} +
+

+ Node Info +

+
+

Name

+

{node?.name}

+
+
+ {/* Pods table */} +
+

Pods

+ +
+ {/* Troubleshooting */} +
+

+ Troubleshooting +

+
+ {/* Danger Zone */} +
+

+ Danger Zone +

+ +
+
+ ); +}; + +export default HelmVMViewNode; diff --git a/web/tailwind.config.js b/web/tailwind.config.js index 1ea5c980de..d89f3af2a5 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -13,17 +13,60 @@ module.exports = { "teal-muted-dark": "#577981", "teal-medium": "#097992", gray: { - 100: "#dfdfdf", + 100: "#dedede", 200: "#c4c8ca", 300: "#b3b3b3", + 410: "#9b9b9b", 400: "#959595", 500: "#717171", 600: "#585858", 700: "#4f4f4f", - 800: "#323232" + 800: "#323232", + 900: "#2c2c2c", + }, + blue: { + 50: "#ecf4fe", + 75: "#b3d2fc", + 200: "#65a4f8", + 300: "#4591f7", + 400: "#3066ad", + }, + green: { + 50: "#e7f7f3", + 75: "#9cdfcf", + 100: "#73d2bb", + 200: "#37bf9e", + 300: "#0eb28a", + 400: "#0a7d61", + 500: "#096d54", + }, + indigo: { + 100: "#f0f1ff", + 200: "#c2c7fd", + 300: "#a9b0fd", + 400: "#838efc", + 500: "#6a77fb", + 600: "#4a53b0", + 700: "#414999", }, neutral: { - 700: "#4A4A4A" + 700: "#4A4A4A", + }, + teal: { + 300: "#4db9c0", + 400: "#38a3a8", + }, + pink: { + 50: "#fff0f3", + 100: "#ffc1cf", + 200: "#fea7bc", + 300: "#fe819f", + 400: "#fe678b", + 500: "#b24861", + 600: "#9b3f55", + }, + purple: { + 400: "#7242b0", }, error: "#bc4752", "error-xlight": "#fbedeb", @@ -34,26 +77,26 @@ module.exports = { "warning-bright": "#ec8f39", "info-bright": "#76bbca", "disabled-teal": "#76a6cf", - "dark-neon-green": "#38cc97" + "dark-neon-green": "#38cc97", }, extend: { borderRadius: { xs: "0.125rem", sm: "0.187rem", - variants: ["first", "last"] + variants: ["first", "last"], }, fontFamily: { - sans: ["Open Sans", ...defaultTheme.fontFamily.sans] - } - } + sans: ["Open Sans", ...defaultTheme.fontFamily.sans], + }, + }, }, corePlugins: { - preflight: false + preflight: false, }, plugins: [ plugin(function ({ addVariant }) { addVariant("is-enabled", "&:not([disabled])"); addVariant("is-disabled", "&[disabled]"); - }) - ] + }), + ], }; diff --git a/web/yarn.lock b/web/yarn.lock index 55cbb3f645..bcabb4abd6 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -350,6 +350,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-module-imports@^7.16.7": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + "@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.20.2", "@babel/helper-module-transforms@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.0.tgz#89a8f86ad748870e3d024e470b2e8405e869db67" @@ -447,11 +454,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" @@ -1390,6 +1407,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.23.1", "@babel/runtime@^7.8.7": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" + integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.3.1": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" @@ -1535,6 +1559,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" @@ -1614,6 +1647,23 @@ resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@emotion/babel-plugin@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" + integrity sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/serialize" "^1.1.2" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.2.0" + "@emotion/babel-utils@^0.6.4": version "0.6.10" resolved "https://registry.npmjs.org/@emotion/babel-utils/-/babel-utils-0.6.10.tgz" @@ -1626,11 +1676,27 @@ find-root "^1.1.0" source-map "^0.7.2" +"@emotion/cache@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff" + integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ== + dependencies: + "@emotion/memoize" "^0.8.1" + "@emotion/sheet" "^1.2.2" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" + stylis "4.2.0" + "@emotion/hash@^0.6.2", "@emotion/hash@^0.6.6": version "0.6.6" resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.6.6.tgz" integrity sha512-ojhgxzUHZ7am3D2jHkMzPpsBAiB005GF5YU4ea+8DNPybMk01JJUM9V9YRlF/GE95tcOm8DxQvWA2jq19bGalQ== +"@emotion/hash@^0.9.1": + version "0.9.1" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" + integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== + "@emotion/is-prop-valid@^1.1.0": version "1.1.3" resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.3.tgz" @@ -1638,6 +1704,13 @@ dependencies: "@emotion/memoize" "^0.7.4" +"@emotion/is-prop-valid@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc" + integrity sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw== + dependencies: + "@emotion/memoize" "^0.8.1" + "@emotion/memoize@^0.6.1", "@emotion/memoize@^0.6.6": version "0.6.6" resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz" @@ -1648,6 +1721,25 @@ resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz" integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ== +"@emotion/memoize@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" + integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== + +"@emotion/react@^11.11.1": + version "11.11.1" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.1.tgz#b2c36afac95b184f73b08da8c214fdf861fa4157" + integrity sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/cache" "^11.11.0" + "@emotion/serialize" "^1.1.2" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" + hoist-non-react-statics "^3.3.1" + "@emotion/serialize@^0.9.1": version "0.9.1" resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.9.1.tgz" @@ -1658,6 +1750,34 @@ "@emotion/unitless" "^0.6.7" "@emotion/utils" "^0.8.2" +"@emotion/serialize@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.2.tgz#017a6e4c9b8a803bd576ff3d52a0ea6fa5a62b51" + integrity sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA== + dependencies: + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/unitless" "^0.8.1" + "@emotion/utils" "^1.2.1" + csstype "^3.0.2" + +"@emotion/sheet@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" + integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== + +"@emotion/styled@^11.11.0": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.0.tgz#26b75e1b5a1b7a629d7c0a8b708fbf5a9cdce346" + integrity sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/is-prop-valid" "^1.2.1" + "@emotion/serialize" "^1.1.2" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" + "@emotion/stylis@^0.7.0": version "0.7.1" resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.7.1.tgz" @@ -1678,11 +1798,31 @@ resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== +"@emotion/unitless@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" + integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== + +"@emotion/use-insertion-effect-with-fallbacks@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963" + integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== + "@emotion/utils@^0.8.2": version "0.8.2" resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.8.2.tgz" integrity sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw== +"@emotion/utils@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4" + integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== + +"@emotion/weak-memoize@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" + integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== + "@eslint/eslintrc@^1.3.0": version "1.3.0" resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz" @@ -1698,6 +1838,33 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@floating-ui/core@^1.4.2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.0.tgz#5c05c60d5ae2d05101c3021c1a2a350ddc027f8c" + integrity sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg== + dependencies: + "@floating-ui/utils" "^0.1.3" + +"@floating-ui/dom@^1.5.1": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa" + integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA== + dependencies: + "@floating-ui/core" "^1.4.2" + "@floating-ui/utils" "^0.1.3" + +"@floating-ui/react-dom@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.2.tgz#fab244d64db08e6bed7be4b5fcce65315ef44d20" + integrity sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ== + dependencies: + "@floating-ui/dom" "^1.5.1" + +"@floating-ui/utils@^0.1.3": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9" + integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A== + "@gar/promisify@^1.0.1": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -2204,6 +2371,97 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@mui/base@5.0.0-beta.20": + version "5.0.0-beta.20" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.20.tgz#14fcdfe0350f2aad06ab6c37c4c91dacaab8f600" + integrity sha512-CS2pUuqxST7ch9VNDCklRYDbJ3rru20Tx7na92QvVVKfu3RL4z/QLuVIc8jYGsdCnauMaeUSlFNLAJNb0yXe6w== + dependencies: + "@babel/runtime" "^7.23.1" + "@floating-ui/react-dom" "^2.0.2" + "@mui/types" "^7.2.6" + "@mui/utils" "^5.14.13" + "@popperjs/core" "^2.11.8" + clsx "^2.0.0" + prop-types "^15.8.1" + +"@mui/core-downloads-tracker@^5.14.14": + version "5.14.14" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.14.tgz#a54894e9b4dc908ab2d59eac543219d9018448e6" + integrity sha512-Rw/xKiTOUgXD8hdKqj60aC6QcGprMipG7ne2giK6Mz7b4PlhL/xog9xLeclY3BxsRLkZQ05egFnIEY1CSibTbw== + +"@mui/icons-material@^5.14.14": + version "5.14.14" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.14.14.tgz#02d33f51f0b9de238d5c47b0a31ff330144393c4" + integrity sha512-vwuaMsKvI7AWTeYqR8wYbpXijuU8PzMAJWRAq2DDIuOZPxjKyHlr8WQ25+azZYkIXtJ7AqnVb1ZmHdEyB4/kug== + dependencies: + "@babel/runtime" "^7.23.1" + +"@mui/material@^5.14.14": + version "5.14.14" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.14.tgz#e47f3992b609002cd57a71f70e829dc2d286028c" + integrity sha512-cAmCwAHFQXxb44kWbVFkhKATN8tACgMsFwrXo8ro6WzYW73U/qsR5AcCiJIhCyYYg+gcftfkmNcpRaV3JjhHCg== + dependencies: + "@babel/runtime" "^7.23.1" + "@mui/base" "5.0.0-beta.20" + "@mui/core-downloads-tracker" "^5.14.14" + "@mui/system" "^5.14.14" + "@mui/types" "^7.2.6" + "@mui/utils" "^5.14.13" + "@types/react-transition-group" "^4.4.7" + clsx "^2.0.0" + csstype "^3.1.2" + prop-types "^15.8.1" + react-is "^18.2.0" + react-transition-group "^4.4.5" + +"@mui/private-theming@^5.14.14": + version "5.14.14" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.14.14.tgz#035dde1eb30c896c69a12b7dee1dce3a323c66e9" + integrity sha512-n77au3CQj9uu16hak2Y+rvbGSBaJKxziG/gEbOLVGrAuqZ+ycVSkorCfN6Y/4XgYOpG/xvmuiY3JwhAEOzY3iA== + dependencies: + "@babel/runtime" "^7.23.1" + "@mui/utils" "^5.14.13" + prop-types "^15.8.1" + +"@mui/styled-engine@^5.14.13": + version "5.14.14" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.14.14.tgz#b0ededf531fff1ef110f7b263c2d3d95a0b8ec9a" + integrity sha512-sF3DS2PVG+cFWvkVHQQaGFpL1h6gSwOW3L91pdxPLQDHDZ5mZ/X0SlXU5XA+WjypoysG4urdAQC7CH/BRvUiqg== + dependencies: + "@babel/runtime" "^7.23.1" + "@emotion/cache" "^11.11.0" + csstype "^3.1.2" + prop-types "^15.8.1" + +"@mui/system@^5.14.14": + version "5.14.14" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.14.14.tgz#f33327e74230523169107ace960e8bb51cbdbab7" + integrity sha512-y4InFmCgGGWXnz+iK4jRTWVikY0HgYnABjz4wgiUgEa2W1H8M4ow+27BegExUWPkj4TWthQ2qG9FOGSMtI+PKA== + dependencies: + "@babel/runtime" "^7.23.1" + "@mui/private-theming" "^5.14.14" + "@mui/styled-engine" "^5.14.13" + "@mui/types" "^7.2.6" + "@mui/utils" "^5.14.13" + clsx "^2.0.0" + csstype "^3.1.2" + prop-types "^15.8.1" + +"@mui/types@^7.2.6": + version "7.2.6" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.6.tgz#d72b9e9eb0032e107e76033932d65c3f731d2608" + integrity sha512-7sjLQrUmBwufm/M7jw/quNiPK/oor2+pGUQP2CULRcFCArYTq78oJ3D5esTaL0UMkXKJvDqXn6Ike69yAOBQng== + +"@mui/utils@^5.14.13": + version "5.14.14" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.14.tgz#7b2a0bcfb44c3376fc81f85500f9bd01706682ac" + integrity sha512-3AKp8uksje5sRfVrtgG9Q/2TBsHWVBUtA0NaXliZqGcXo8J+A+Agp0qUW2rJ+ivgPWTCCubz9FZVT2IQZ3bGsw== + dependencies: + "@babel/runtime" "^7.23.1" + "@types/prop-types" "^15.7.7" + prop-types "^15.8.1" + react-is "^18.2.0" + "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": version "2.1.8-no-fsevents.3" resolved "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz" @@ -2345,6 +2603,11 @@ resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== +"@popperjs/core@^2.11.8": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + "@remix-run/router@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.5.0.tgz#57618e57942a5f0131374a9fdb0167e25a117fdc" @@ -3661,6 +3924,37 @@ regenerator-runtime "^0.13.7" resolve-from "^5.0.0" +"@tanstack/match-sorter-utils@8.8.4": + version "8.8.4" + resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.8.4.tgz#0b2864d8b7bac06a9f84cb903d405852cc40a457" + integrity sha512-rKH8LjZiszWEvmi01NR72QWZ8m4xmXre0OOwlRGnjU01Eqz/QnN+cqpty2PJ0efHblq09+KilvyR7lsbzmXVEw== + dependencies: + remove-accents "0.4.2" + +"@tanstack/react-table@8.10.7": + version "8.10.7" + resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.10.7.tgz#733f4bee8cf5aa19582f944dd0fd3224b21e8c94" + integrity sha512-bXhjA7xsTcsW8JPTTYlUg/FuBpn8MNjiEPhkNhIGCUR6iRQM2+WEco4OBpvDeVcR9SE+bmWLzdfiY7bCbCSVuA== + dependencies: + "@tanstack/table-core" "8.10.7" + +"@tanstack/react-virtual@3.0.0-beta.65": + version "3.0.0-beta.65" + resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.0.0-beta.65.tgz#a29a10c761afd00c8000dc38adf60088656e0e62" + integrity sha512-Q21cUoE0C8Oyzy3RAMV+u4BuB+RwIf2/oQRCWksmIBp1PqLEtvXhAldh7v/wUt7WKEkislKDICZAvbYYs7EAyQ== + dependencies: + "@tanstack/virtual-core" "3.0.0-beta.65" + +"@tanstack/table-core@8.10.7": + version "8.10.7" + resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.10.7.tgz#577e8a635048875de4c9d6d6a3c21d26ff9f9d08" + integrity sha512-KQk5OMg5OH6rmbHZxuNROvdI+hKDIUxANaHlV+dPlNN7ED3qYQ/WkpY2qlXww1SIdeMlkIhpN/2L00rof0fXFw== + +"@tanstack/virtual-core@3.0.0-beta.65": + version "3.0.0-beta.65" + resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.0.0-beta.65.tgz#fac199321db7787db9463082903dca23c0850c5c" + integrity sha512-ObP2pvXBdbivinr7BWDbGqYt4TK8wNzYsOWio+qBkDx5AJFuvqcdJxcCCYnv4dzVTe5ELA1MT4tkt8NB/tnEdA== + "@testing-library/dom@^8.3.0": version "8.19.0" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.19.0.tgz#bd3f83c217ebac16694329e413d9ad5fdcfd785f" @@ -4154,6 +4448,11 @@ resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/prop-types@^15.7.7": + version "15.7.8" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.8.tgz#805eae6e8f41bd19e88917d2ea200dc992f405d3" + integrity sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ== + "@types/q@1.0.7": version "1.0.7" resolved "https://registry.npmjs.org/@types/q/-/q-1.0.7.tgz" @@ -4223,6 +4522,13 @@ dependencies: "@types/react" "*" +"@types/react-transition-group@^4.4.7": + version "4.4.7" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.7.tgz#bf69f269d74aa78b99097673ca6dd6824a68ef1c" + integrity sha512-ICCyBl5mvyqYp8Qeq9B5G/fyBSRC0zx3XM3sCC6KkcMsNeAHqXBKkmat4GqdJET5jtYUpZXrxI5flve5qhi2Eg== + dependencies: + "@types/react" "*" + "@types/react@*": version "18.0.20" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.20.tgz#e4c36be3a55eb5b456ecf501bd4a00fd4fd0c9ab" @@ -5628,7 +5934,7 @@ babel-plugin-macros@^2.0.0: cosmiconfig "^6.0.0" resolve "^1.12.0" -babel-plugin-macros@^3.0.1: +babel-plugin-macros@^3.0.1, babel-plugin-macros@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== @@ -6786,6 +7092,11 @@ clsx@^1.0.4: resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== +clsx@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b" + integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== + co@^4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" @@ -7339,6 +7650,11 @@ csstype@^3.0.2: resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz" integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw== +csstype@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" + integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -7794,6 +8110,14 @@ dom-helpers@^3.4.0: dependencies: "@babel/runtime" "^7.1.2" +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" @@ -9746,6 +10070,11 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +highlight-words@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/highlight-words/-/highlight-words-1.2.2.tgz#9875b75d11814d7356b24f23feeb7d77761fa867" + integrity sha512-Mf4xfPXYm8Ay1wTibCrHpNWeR2nUMynMVFkXCi4mbl+TEgmNOe+I4hV7W3OCZcSvzGL6kupaqpfHOemliMTGxQ== + highlight.js@^10.4.1, highlight.js@~10.7.0: version "10.7.3" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" @@ -9777,7 +10106,7 @@ hoek@4.2.1: resolved "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz" integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA== -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -11742,6 +12071,16 @@ match-sorter@^6.0.2: "@babel/runtime" "^7.12.5" remove-accents "0.4.2" +material-react-table@^1.15.1: + version "1.15.1" + resolved "https://registry.yarnpkg.com/material-react-table/-/material-react-table-1.15.1.tgz#c2bdfdd9c9636acbb2e8ffd5553a82395a2d9f4a" + integrity sha512-TXidRV7lGtCV5G/ON9Y38TztRcmpKFodFmyTCjvlKXCl5/9X+KY4waP8U0l16FFslg1f7HGWhfkqV5OfUfEIoA== + dependencies: + "@tanstack/match-sorter-utils" "8.8.4" + "@tanstack/react-table" "8.10.7" + "@tanstack/react-virtual" "3.0.0-beta.65" + highlight-words "1.2.2" + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -13872,7 +14211,7 @@ react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^18.0.0: +react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== @@ -14009,6 +14348,16 @@ react-transition-group@^2.2.1: prop-types "^15.6.2" react-lifecycles-compat "^3.0.4" +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-vis@^1.11.7: version "1.11.7" resolved "https://registry.npmjs.org/react-vis/-/react-vis-1.11.7.tgz" @@ -14205,6 +14554,11 @@ regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.2, regenerator-runtime@^ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== + regenerator-transform@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" @@ -15497,6 +15851,11 @@ stylis-rule-sheet@^0.0.10: resolved "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz" integrity sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw== +stylis@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" + integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== + stylis@^3.5.0: version "3.5.4" resolved "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz"