WIP update

This commit is contained in:
Pierre HUBERT 2025-03-28 11:06:30 +01:00
parent e9e3103938
commit e5753d2b26
18 changed files with 455 additions and 19054 deletions

File diff suppressed because it is too large Load Diff

View File

@ -9,17 +9,17 @@
"@fontsource/roboto": "^5.2.5", "@fontsource/roboto": "^5.2.5",
"@mdi/js": "^7.2.96", "@mdi/js": "^7.2.96",
"@mdi/react": "^1.6.1", "@mdi/react": "^1.6.1",
"@mui/icons-material": "^6.4.8", "@mui/icons-material": "^7.0.0",
"@mui/material": "^6.4.8", "@mui/material": "^7.0.0",
"@mui/x-charts": "^7.28.0", "@mui/x-charts": "^7.28.0",
"@mui/x-data-grid": "^7.28.0", "@mui/x-data-grid": "^7.28.1",
"@testing-library/jest-dom": "^6.6.3", "@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0", "@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^14.5.2", "@testing-library/user-event": "^14.5.2",
"@types/humanize-duration": "^3.27.1", "@types/humanize-duration": "^3.27.1",
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
"@types/react": "^18.3.12", "@types/react": "^19.0.12",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^19.0.4",
"@types/react-syntax-highlighter": "^15.5.13", "@types/react-syntax-highlighter": "^15.5.13",
"@types/uuid": "^10.0.0", "@types/uuid": "^10.0.0",
"@vitejs/plugin-react": "^4.3.3", "@vitejs/plugin-react": "^4.3.3",
@ -27,16 +27,15 @@
"filesize": "^10.1.6", "filesize": "^10.1.6",
"humanize-duration": "^3.29.0", "humanize-duration": "^3.29.0",
"mui-file-input": "^7.0.0", "mui-file-input": "^7.0.0",
"react": "^18.2.0", "react": "^19.0.0",
"react-dom": "^18.2.0", "react-dom": "^19.0.0",
"react-router-dom": "^7.4.0", "react-router-dom": "^7.4.0",
"react-syntax-highlighter": "^15.6.1", "react-syntax-highlighter": "^15.6.1",
"react-vnc": "^2.0.3", "react-vnc": "^3.0.7",
"typescript": "^4.9.5", "typescript": "^5.8.2",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"vite": "^6.2.2", "vite": "^6.2.3",
"vite-tsconfig-paths": "^5.1.3", "vite-tsconfig-paths": "^5.1.3",
"web-vitals": "^3.5.2",
"xml-formatter": "^3.6.0" "xml-formatter": "^3.6.0"
}, },
"scripts": { "scripts": {

View File

@ -7,7 +7,6 @@ import React from "react";
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client";
import { App } from "./App"; import { App } from "./App";
import "./index.css"; import "./index.css";
import reportWebVitals from "./reportWebVitals";
import { LoadServerConfig } from "./widgets/LoadServerConfig"; import { LoadServerConfig } from "./widgets/LoadServerConfig";
import { ThemeProvider, createTheme } from "@mui/material"; import { ThemeProvider, createTheme } from "@mui/material";
import { LoadingMessageProvider } from "./hooks/providers/LoadingMessageProvider"; import { LoadingMessageProvider } from "./hooks/providers/LoadingMessageProvider";
@ -41,8 +40,3 @@ root.render(
</ThemeProvider> </ThemeProvider>
</React.StrictMode> </React.StrictMode>
); );
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@ -1,15 +0,0 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -16,7 +16,7 @@ import {
TableRow, TableRow,
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid";
import { PieChart } from "@mui/x-charts"; import { PieChart } from "@mui/x-charts";
import { filesize } from "filesize"; import { filesize } from "filesize";
import humanizeDuration from "humanize-duration"; import humanizeDuration from "humanize-duration";

View File

@ -1,17 +1,16 @@
import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import FullscreenIcon from "@mui/icons-material/Fullscreen"; import FullscreenIcon from "@mui/icons-material/Fullscreen";
import FullscreenExitIcon from "@mui/icons-material/FullscreenExit"; import FullscreenExitIcon from "@mui/icons-material/FullscreenExit";
import KeyboardAltIcon from "@mui/icons-material/KeyboardAlt";
import { IconButton, Tooltip } from "@mui/material"; import { IconButton, Tooltip } from "@mui/material";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { VncScreen } from "react-vnc"; import { VncScreen, VncScreenHandle } from "react-vnc";
import { ServerApi } from "../api/ServerApi"; import { ServerApi } from "../api/ServerApi";
import { VMApi, VMInfo } from "../api/VMApi"; import { VMApi, VMInfo } from "../api/VMApi";
import { useSnackbar } from "../hooks/providers/SnackbarProvider"; import { useSnackbar } from "../hooks/providers/SnackbarProvider";
import { time } from "../utils/DateUtils"; import { time } from "../utils/DateUtils";
import { AsyncWidget } from "../widgets/AsyncWidget"; import { AsyncWidget } from "../widgets/AsyncWidget";
import RFB from "react-vnc/dist/types/noVNC/core/rfb";
import KeyboardAltIcon from "@mui/icons-material/KeyboardAlt";
interface VNCTokenInfo { interface VNCTokenInfo {
url: string; url: string;
@ -43,9 +42,10 @@ function VNCInner(p: { vm: VMInfo }): React.ReactElement {
const [token, setToken] = React.useState<VNCTokenInfo | undefined>(); const [token, setToken] = React.useState<VNCTokenInfo | undefined>();
const [counter, setCounter] = React.useState(1); const [counter, setCounter] = React.useState(1);
const [rfb, setRFB] = React.useState<RFB | undefined>(); const [connected, setConnected] = React.useState(false);
const vncRef = React.createRef<HTMLDivElement>(); const vncRef = React.createRef<HTMLDivElement>();
const vncScreenRef = React.createRef<VncScreenHandle>();
const connect = async (force: boolean) => { const connect = async (force: boolean) => {
try { try {
@ -71,7 +71,7 @@ function VNCInner(p: { vm: VMInfo }): React.ReactElement {
}; };
const disconnected = () => { const disconnected = () => {
setRFB(undefined); setConnected(false);
connect(true); connect(true);
}; };
@ -118,9 +118,9 @@ function VNCInner(p: { vm: VMInfo }): React.ReactElement {
)} )}
{/* Keystrokes */} {/* Keystrokes */}
{rfb && ( {connected && (
<Tooltip title="Send Ctrl+Alt+Del"> <Tooltip title="Send Ctrl+Alt+Del">
<IconButton onClick={() => rfb?.sendCtrlAltDel()}> <IconButton onClick={() => vncScreenRef.current?.sendCtrlAltDel()}>
<KeyboardAltIcon /> <KeyboardAltIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
@ -137,12 +137,13 @@ function VNCInner(p: { vm: VMInfo }): React.ReactElement {
}} }}
> >
<VncScreen <VncScreen
ref={vncScreenRef}
url={token.url} url={token.url}
onDisconnect={() => { onDisconnect={() => {
console.info("VNC disconnected " + token?.url); console.info("VNC disconnected " + token?.url);
disconnected(); disconnected();
}} }}
onConnect={(rfb) => setRFB(rfb)} onConnect={() => setConnected(true)}
/> />
</div> </div>
</div> </div>

View File

@ -1,4 +1,5 @@
import { Visibility, VisibilityOff } from "@mui/icons-material"; import VisibilityIcon from '@mui/icons-material/Visibility';
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import { import {
Alert, Alert,
CircularProgress, CircularProgress,
@ -130,7 +131,7 @@ export function LoginRoute(): React.ReactElement {
onMouseDown={handleMouseDownPassword} onMouseDown={handleMouseDownPassword}
edge="end" edge="end"
> >
{showPassword ? <VisibilityOff /> : <Visibility />} {showPassword ? <VisibilityOffIcon /> : <VisibilityIcon />}
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</InputAdornment> </InputAdornment>

View File

@ -3,7 +3,7 @@ import Icon from "@mdi/react";
import Avatar from "@mui/material/Avatar"; import Avatar from "@mui/material/Avatar";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import CssBaseline from "@mui/material/CssBaseline"; import CssBaseline from "@mui/material/CssBaseline";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { Link, Outlet } from "react-router-dom"; import { Link, Outlet } from "react-router-dom";

View File

@ -1,6 +1,6 @@
import { Paper, Typography } from "@mui/material"; import { Paper, Typography } from "@mui/material";
import React, { PropsWithChildren } from "react"; import React, { PropsWithChildren } from "react";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid";
export function EditSection( export function EditSection(
p: { p: {

View File

@ -18,7 +18,7 @@ import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
import { IPInput } from "./IPInput"; import { IPInput } from "./IPInput";
import { MACInput } from "./MACInput"; import { MACInput } from "./MACInput";
import { TextInput } from "./TextInput"; import { TextInput } from "./TextInput";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid";
export function NetDHCPHostReservations(p: { export function NetDHCPHostReservations(p: {
editable: boolean; editable: boolean;

View File

@ -9,7 +9,7 @@ import {
Tooltip, Tooltip,
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid";
import React, { PropsWithChildren } from "react"; import React, { PropsWithChildren } from "react";
import { NatEntry } from "../../api/NetworksApi"; import { NatEntry } from "../../api/NetworksApi";
import { ServerApi } from "../../api/ServerApi"; import { ServerApi } from "../../api/ServerApi";

View File

@ -10,7 +10,7 @@ import {
ListItemText, ListItemText,
Tooltip, Tooltip,
} from "@mui/material"; } from "@mui/material";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid";
import { NWFilter } from "../../api/NWFilterApi"; import { NWFilter } from "../../api/NWFilterApi";
import { NetworkInfo } from "../../api/NetworksApi"; import { NetworkInfo } from "../../api/NetworksApi";
import { ServerApi } from "../../api/ServerApi"; import { ServerApi } from "../../api/ServerApi";

View File

@ -1,5 +1,5 @@
import { Button, Checkbox } from "@mui/material"; import { Button, Checkbox } from "@mui/material";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid";
import React from "react"; import React from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { IpConfig, NetworkApi, NetworkInfo } from "../../api/NetworksApi"; import { IpConfig, NetworkApi, NetworkInfo } from "../../api/NetworksApi";

View File

@ -1,5 +1,5 @@
import { Button } from "@mui/material"; import { Button } from "@mui/material";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid";
import React, { ReactElement } from "react"; import React, { ReactElement } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { import {

View File

@ -1,5 +1,5 @@
import { Button } from "@mui/material"; import { Button } from "@mui/material";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid";
import React from "react"; import React from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { GroupApi } from "../../api/GroupApi"; import { GroupApi } from "../../api/GroupApi";

View File

@ -1,7 +1,7 @@
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
import ListIcon from "@mui/icons-material/List"; import ListIcon from "@mui/icons-material/List";
import { Button, IconButton, Tooltip } from "@mui/material"; import { Button, IconButton, Tooltip } from "@mui/material";
import Grid from "@mui/material/Grid2"; import Grid from "@mui/material/Grid";
import React from "react"; import React from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { validate as validateUUID } from "uuid"; import { validate as validateUUID } from "uuid";

View File

@ -9,7 +9,7 @@ export function VMScreenshot(p: { vm: VMInfo }): React.ReactElement {
string | undefined string | undefined
>(); >();
const int = React.useRef<number | undefined>(); const int = React.useRef<number | undefined>(undefined);
React.useEffect(() => { React.useEffect(() => {
const refresh = async () => { const refresh = async () => {

View File

@ -15,7 +15,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"noEmit": true, "noEmit": true,