Set up the user interface state model and set up the admin user onboarding #283

Merged
savanni merged 12 commits from visions-admin into main 2024-12-18 14:18:16 +00:00
4 changed files with 94 additions and 4 deletions
Showing only changes of commit 5e89b8257d - Show all commits

View File

@ -8,6 +8,7 @@ import { WebsocketProvider } from './components/WebsocketProvider';
import { PlayerView } from './views/PlayerView/PlayerView'; import { PlayerView } from './views/PlayerView/PlayerView';
import { Admin } from './views/Admin/Admin'; import { Admin } from './views/Admin/Admin';
import Candela from './plugins/Candela'; import Candela from './plugins/Candela';
import { Authentication } from './views/Authentication/Authentication';
const TEST_CHARSHEET_UUID = "12df9c09-1f2f-4147-8eda-a97bd2a7a803"; const TEST_CHARSHEET_UUID = "12df9c09-1f2f-4147-8eda-a97bd2a7a803";
@ -35,6 +36,10 @@ const App = ({ client }: AppProps) => {
let router = let router =
createBrowserRouter([ createBrowserRouter([
{
path: "/",
element: websocketUrl ? <WebsocketProvider websocketUrl={websocketUrl}> <Authentication client={client}> <PlayerView client={client} /> </Authentication> </WebsocketProvider> : <div> </div>
},
{ {
path: "/gm", path: "/gm",
element: websocketUrl ? <WebsocketProvider websocketUrl={websocketUrl}> <GmView client={client} /> </WebsocketProvider> : <div> </div> element: websocketUrl ? <WebsocketProvider websocketUrl={websocketUrl}> <GmView client={client} /> </WebsocketProvider> : <div> </div>
@ -43,10 +48,6 @@ const App = ({ client }: AppProps) => {
path: "/admin", path: "/admin",
element: <Admin client={client} /> element: <Admin client={client} />
}, },
{
path: "/",
element: websocketUrl ? <WebsocketProvider websocketUrl={websocketUrl}> <PlayerView client={client} /> </WebsocketProvider> : <div> </div>
},
{ {
path: "/candela", path: "/candela",
element: <CandelaCharsheet client={client} /> element: <CandelaCharsheet client={client} />

15
visions/ui/src/design.css Normal file
View File

@ -0,0 +1,15 @@
:root {
--border-standard: 2px solid black;
--border-radius-standard: 4px;
--border-shadow-shallow: 1px 1px 2px black;
--padding-m: 8px;
--margin-s: 4px;
}
.card {
border: var(--border-standard);
border-radius: var(--border-radius-standard);
box-shadow: var(--border-shadow-shallow);
padding: var(--padding-m);
}

View File

@ -0,0 +1,24 @@
@import '../../design.css';
.auth {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100vh;
}
.auth > div {
border: var(--border-standard);
border-radius: var(--border-radius-standard);
padding: var(--padding-m);
}
.auth__input-line {
display: flex;
justify-content: space-between;
}
.auth__input-line > * {
margin: var(--margin-s);
}

View File

@ -0,0 +1,50 @@
import React, { PropsWithChildren, ReactNode, useContext, useEffect, useState } from 'react';
import { Client } from '../../client';
import { assertNever } from '../../plugins/Candela';
import './Authentication.css';
interface AuthenticationProps {
client: Client;
}
type AuthState = "NoAdmin" | "Unauthed" | "Authed";
export const Authentication = ({ client, children }: PropsWithChildren<AuthenticationProps>) => {
// No admin password set: prompt for the admin password
// Password set, nobody logged in: prompt for login
// User logged in: show the children
let [state, setState] = useState<AuthState>("Unauthed");
switch (state) {
case "NoAdmin": {
return <div className="auth">
<div className="card">
<h1> Welcome to your new Visions VTT Instance </h1>
<p> Set your admin password: </p>
<input type="password" placeholder="Password" />
<input type="submit" value="Submit" />
</div>
</div>;
}
case "Unauthed": {
return <div className="auth card">
<div className="card">
<h1> Welcome to Visions VTT </h1>
<div className="auth__input-line">
<input type="text" placeholder="Username" />
<input type="password" placeholder="Password" />
<input type="submit" value="Sign in" />
</div>
</div>
</div>;
}
case "Authed": {
return <div> {children} </div>;
}
default: {
assertNever(state);
return <div></div>;
}
}
}