Add webapp skeletons

This commit is contained in:
2025-11-04 19:43:51 +01:00
parent 20a42f3c55
commit 9ed711777c
11 changed files with 218 additions and 4 deletions

View File

@@ -1,3 +1,61 @@
export function App(): React.ReactElement {
return <>hello world</>;
import React from "react";
import {
createBrowserRouter,
createRoutesFromElements,
Route,
RouterProvider,
} from "react-router";
import { AuthApi } from "./api/AuthApi";
import { ServerApi } from "./api/ServerApi";
import { LoginRoute } from "./routes/auth/LoginRoute";
import { OIDCCbRoute } from "./routes/auth/OIDCCbRoute";
import { HomeRoute } from "./routes/HomeRoute";
import { NotFoundRoute } from "./routes/NotFoundRoute";
import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
import { BaseLoginPage } from "./widgets/auth/BaseLoginPage";
interface AuthContext {
signedIn: boolean;
setSignedIn: (signedIn: boolean) => void;
}
const AuthContextK = React.createContext<AuthContext | null>(null);
export function App(): React.ReactElement {
const [signedIn, setSignedIn] = React.useState(AuthApi.SignedIn);
const context: AuthContext = {
signedIn: signedIn,
setSignedIn: (s) => {
setSignedIn(s);
location.reload();
},
};
const router = createBrowserRouter(
createRoutesFromElements(
signedIn || ServerApi.Config.auth_disabled ? (
<Route path="*" element={<BaseAuthenticatedPage />}>
<Route path="" element={<HomeRoute />} />
<Route path="*" element={<NotFoundRoute />} />
</Route>
) : (
<Route path="*" element={<BaseLoginPage />}>
<Route path="" element={<LoginRoute />} />
<Route path="oidc_cb" element={<OIDCCbRoute />} />
<Route path="*" element={<NotFoundRoute />} />
</Route>
)
)
);
return (
<AuthContextK value={context}>
<RouterProvider router={router} />
</AuthContextK>
);
}
export function useAuth(): AuthContext {
return React.use(AuthContextK)!;
}

View File

@@ -0,0 +1,3 @@
export function HomeRoute(): React.ReactElement {
return <p>Todo home route</p>;
}

View File

@@ -0,0 +1,23 @@
import { Button } from "@mui/material";
import { RouterLink } from "../widgets/RouterLink";
export function NotFoundRoute(): React.ReactElement {
return (
<div
style={{
textAlign: "center",
flex: "1",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<h1>404 Not found</h1>
<p>The page you requested was not found!</p>
<RouterLink to="/">
<Button>Go back home</Button>
</RouterLink>
</div>
);
}

View File

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

View File

@@ -0,0 +1,53 @@
import { CircularProgress } from "@mui/material";
import { useEffect, useRef, useState } from "react";
import { useNavigate, useSearchParams } from "react-router";
import { AuthApi } from "../../api/AuthApi";
import { useAuth } from "../../App";
import { AuthSingleMessage } from "../../widgets/auth/AuthSingleMessage";
/**
* OpenID login callback route
*/
export function OIDCCbRoute(): React.ReactElement {
const auth = useAuth();
const navigate = useNavigate();
const [error, setError] = useState(false);
const [searchParams] = useSearchParams();
const code = searchParams.get("code");
const state = searchParams.get("state");
const count = useRef("");
useEffect(() => {
const load = async () => {
try {
if (count.current === code) {
return;
}
count.current = code!;
await AuthApi.FinishOpenIDLogin(code!, state!);
navigate("/");
auth.setSignedIn(true);
} catch (e) {
console.error(e);
setError(true);
}
};
load();
});
if (error)
return (
<AuthSingleMessage message="Failed to finalize OpenID authentication!" />
);
return (
<>
<CircularProgress />
</>
);
}

View File

@@ -0,0 +1,3 @@
export function BaseAuthenticatedPage(): React.ReactElement {
return <p>todo authenticated</p>;
}

View File

@@ -0,0 +1,16 @@
import { type PropsWithChildren } from "react";
import { Link } from "react-router";
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>
);
}

View File

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

View File

@@ -0,0 +1,3 @@
export function BaseLoginPage(): React.ReactElement {
return <p>Todo login page route</p>;
}