Build base web

This commit is contained in:
2023-09-04 14:11:56 +02:00
parent 83bd87c6f8
commit 8defc104c6
35 changed files with 31630 additions and 0 deletions

View File

@ -0,0 +1,92 @@
import { Alert, Box, Button, CircularProgress } from "@mui/material";
import { useEffect, useRef, useState } from "react";
enum State {
Loading,
Ready,
Error,
}
export function AsyncWidget(p: {
loadKey: any;
load: () => Promise<void>;
errMsg: string;
build: () => React.ReactElement;
ready?: boolean;
errAdditionalElement?: () => React.ReactElement;
}): React.ReactElement {
const [state, setState] = useState(State.Loading);
const counter = useRef<any | null>(null);
const load = async () => {
try {
setState(State.Loading);
await p.load();
setState(State.Ready);
} catch (e) {
console.error(e);
setState(State.Error);
}
};
useEffect(() => {
if (counter.current === p.loadKey) return;
counter.current = p.loadKey;
load();
});
if (state === State.Error)
return (
<Box
component="div"
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100%",
flex: "1",
flexDirection: "column",
backgroundColor: (theme) =>
theme.palette.mode === "light"
? theme.palette.grey[100]
: theme.palette.grey[900],
}}
>
<Alert
variant="outlined"
severity="error"
style={{ margin: "0px 15px 15px 15px" }}
>
{p.errMsg}
</Alert>
<Button onClick={load}>Try again</Button>
{p.errAdditionalElement && p.errAdditionalElement()}
</Box>
);
if (state === State.Loading || p.ready === false)
return (
<Box
component="div"
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100%",
flex: "1",
backgroundColor: (theme) =>
theme.palette.mode === "light"
? theme.palette.grey[100]
: theme.palette.grey[900],
}}
>
<CircularProgress />
</Box>
);
return p.build();
}

View File

@ -0,0 +1,13 @@
import { Button } from "@mui/material";
import { Link } from "react-router-dom";
export function AuthSingleMessage(p: { message: string }): React.ReactElement {
return (
<>
<p style={{ textAlign: "center" }}>{p.message}</p>
<Link to={"/"}>
<Button>Retour à l'accueil</Button>
</Link>
</>
);
}

View File

@ -0,0 +1,3 @@
export function BaseAuthenticatedPage(): React.ReactElement {
return <>ready with login</>;
}

View File

@ -0,0 +1,90 @@
import { mdiServer } from "@mdi/js";
import Icon from "@mdi/react";
import Avatar from "@mui/material/Avatar";
import Box from "@mui/material/Box";
import CssBaseline from "@mui/material/CssBaseline";
import Grid from "@mui/material/Grid";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import { Link, Outlet } from "react-router-dom";
function Copyright(props: any) {
return (
<Typography
variant="body2"
color="text.secondary"
align="center"
style={{ marginTop: "20px" }}
{...props}
>
{"Copyright © "}
<a
color="inherit"
href="https://0ph.fr/"
target="_blank"
rel="noreferrer"
style={{ color: "inherit" }}
>
Pierre HUBERT
</a>{" "}
{new Date().getFullYear()}
{"."}
</Typography>
);
}
export function BaseLoginPage() {
return (
<Grid container component="main" sx={{ height: "100vh" }}>
<CssBaseline />
<Grid
item
xs={false}
sm={4}
md={7}
sx={{
backgroundImage: "url(/login_splash.jpg)",
backgroundRepeat: "no-repeat",
backgroundColor: (t) =>
t.palette.mode === "light"
? t.palette.grey[50]
: t.palette.grey[900],
backgroundSize: "cover",
backgroundPosition: "center",
}}
/>
<Grid item xs={12} sm={8} md={5} component={Paper} elevation={6} square>
<Box
sx={{
my: 8,
mx: 4,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<Avatar sx={{ m: 1, bgcolor: "secondary.main" }}>
<Icon path={mdiServer} size={1} />
</Avatar>
<Link to="/" style={{ color: "inherit", textDecoration: "none" }}>
<Typography component="h1" variant="h5">
VirtWeb
</Typography>
</Link>
<Typography
component="h1"
variant="h6"
style={{ margin: "10px 0px 30px 0px" }}
>
Virtual Machines Management
</Typography>
{/* inner page */}
<Outlet />
<Copyright sx={{ mt: 5 }} />
</Box>
</Grid>
</Grid>
);
}

View File

@ -0,0 +1,18 @@
import { PropsWithChildren } from "react";
import { AsyncWidget } from "./AsyncWidget";
import { ServerApi } from "../api/ServerApi";
export function LoadServerConfig(p: PropsWithChildren): React.ReactElement {
const load = async () => {
await ServerApi.LoadConfig();
};
return (
<AsyncWidget
loadKey={0}
errMsg="Failed to load server config!"
load={load}
build={() => <>{p.children}</>}
/>
);
}

View File

@ -0,0 +1,16 @@
import { PropsWithChildren } from "react";
import { Link } from "react-router-dom";
export function RouterLink(
p: PropsWithChildren<{ to: string; target?: React.HTMLAttributeAnchorTarget }>
): React.ReactElement {
return (
<Link
to={p.to}
target={p.target}
style={{ color: "inherit", textDecoration: "inherit" }}
>
{p.children}
</Link>
);
}