diff --git a/visions/ui/src/App.tsx b/visions/ui/src/App.tsx
index 98626e8..635f326 100644
--- a/visions/ui/src/App.tsx
+++ b/visions/ui/src/App.tsx
@@ -8,6 +8,7 @@ import { WebsocketProvider } from './components/WebsocketProvider';
import { PlayerView } from './views/PlayerView/PlayerView';
import { Admin } from './views/Admin/Admin';
import Candela from './plugins/Candela';
+import { Authentication } from './views/Authentication/Authentication';
const TEST_CHARSHEET_UUID = "12df9c09-1f2f-4147-8eda-a97bd2a7a803";
@@ -35,6 +36,10 @@ const App = ({ client }: AppProps) => {
let router =
createBrowserRouter([
+ {
+ path: "/",
+ element: websocketUrl ? :
+ },
{
path: "/gm",
element: websocketUrl ? :
@@ -43,10 +48,6 @@ const App = ({ client }: AppProps) => {
path: "/admin",
element:
},
- {
- path: "/",
- element: websocketUrl ? :
- },
{
path: "/candela",
element:
diff --git a/visions/ui/src/design.css b/visions/ui/src/design.css
new file mode 100644
index 0000000..7e227a4
--- /dev/null
+++ b/visions/ui/src/design.css
@@ -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);
+}
+
diff --git a/visions/ui/src/views/Authentication/Authentication.css b/visions/ui/src/views/Authentication/Authentication.css
new file mode 100644
index 0000000..ca86baf
--- /dev/null
+++ b/visions/ui/src/views/Authentication/Authentication.css
@@ -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);
+}
diff --git a/visions/ui/src/views/Authentication/Authentication.tsx b/visions/ui/src/views/Authentication/Authentication.tsx
new file mode 100644
index 0000000..6997fe2
--- /dev/null
+++ b/visions/ui/src/views/Authentication/Authentication.tsx
@@ -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) => {
+ // 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("Unauthed");
+
+ switch (state) {
+ case "NoAdmin": {
+ return
+
+
Welcome to your new Visions VTT Instance
+
Set your admin password:
+
+
+
+
;
+ }
+ case "Unauthed": {
+ return ;
+ }
+ case "Authed": {
+ return {children}
;
+ }
+ default: {
+ assertNever(state);
+ return ;
+ }
+ }
+}