From 94a821d6573a906d1684e0f89db04094be85e712 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 26 Jan 2025 21:30:05 -0500 Subject: [PATCH] Improve the user profile, create a Modal, and provide a way to create a user --- visions/server/src/routes.rs | 8 +++- visions/ui/src/App.tsx | 4 +- visions/ui/src/client.ts | 11 +++++ visions/ui/src/components/Modal/Modal.css | 24 ++++++++++ visions/ui/src/components/Modal/Modal.tsx | 33 ++++++++++++++ visions/ui/src/components/Profile/Profile.css | 8 ---- visions/ui/src/components/Profile/Profile.tsx | 7 +-- .../UserManagement/UserManagement.tsx | 10 +++-- visions/ui/src/components/index.ts | 3 +- .../providers/StateProvider/StateProvider.tsx | 11 +++-- visions/ui/src/views/Admin/Admin.tsx | 2 +- visions/ui/src/views/Main/Main.tsx | 3 +- visions/ui/src/views/Profile/Profile.css | 9 ++++ visions/ui/src/views/Profile/Profile.tsx | 45 +++++++++++++++++++ visions/ui/src/views/index.ts | 3 +- 15 files changed, 153 insertions(+), 28 deletions(-) create mode 100644 visions/ui/src/components/Modal/Modal.css create mode 100644 visions/ui/src/components/Modal/Modal.tsx create mode 100644 visions/ui/src/views/Profile/Profile.css create mode 100644 visions/ui/src/views/Profile/Profile.tsx diff --git a/visions/server/src/routes.rs b/visions/server/src/routes.rs index 10146c8..6d62583 100644 --- a/visions/server/src/routes.rs +++ b/visions/server/src/routes.rs @@ -64,7 +64,13 @@ pub fn routes(core: Core) -> Router { let Json(req) = req; wrap_handler(|| create_user(core, headers, req)) } - }), + }) + .layer( + CorsLayer::new() + .allow_methods([Method::PUT]) + .allow_headers([AUTHORIZATION, CONTENT_TYPE]) + .allow_origin(Any), + ), ) .route( "/api/v1/users", diff --git a/visions/ui/src/App.tsx b/visions/ui/src/App.tsx index 1f792ba..b95ca76 100644 --- a/visions/ui/src/App.tsx +++ b/visions/ui/src/App.tsx @@ -3,11 +3,11 @@ import './App.css' import { Client } from './client' import { createBrowserRouter, RouterProvider } from 'react-router-dom' import { DesignPage } from './views/Design/Design' -import { Admin } from './views/Admin/Admin' import Candela from './plugins/Candela' import { Authentication } from './views/Authentication/Authentication' import { StateContext, StateProvider } from './providers/StateProvider/StateProvider' import { MainView } from './views' +import { AdminView } from './views/Admin/Admin' const TEST_CHARSHEET_UUID = "12df9c09-1f2f-4147-8eda-a97bd2a7a803" @@ -62,7 +62,7 @@ const App = ({ client }: AppProps) => { }, { path: "/admin", - element: + element: }, { path: "/candela", diff --git a/visions/ui/src/client.ts b/visions/ui/src/client.ts index 96e56a2..db5e015 100644 --- a/visions/ui/src/client.ts +++ b/visions/ui/src/client.ts @@ -63,6 +63,17 @@ export class Client { return fetch(url).then((response) => response.json()); } + async createUser(sessionId: string, username: string) { + const url = new URL(this.base); + url.pathname = '/api/v1/user'; + return fetch(url, { + method: 'PUT', + headers: [['Authorization', `Bearer: ${sessionId}`], + ['Content-Type', 'application/json']], + body: JSON.stringify({ username }), + }).then((response) => response.json()) + } + async setPassword(password_1: string, password_2: string) { const url = new URL(this.base); url.pathname = `/api/v1/user/password`; diff --git a/visions/ui/src/components/Modal/Modal.css b/visions/ui/src/components/Modal/Modal.css new file mode 100644 index 0000000..e3c0874 --- /dev/null +++ b/visions/ui/src/components/Modal/Modal.css @@ -0,0 +1,24 @@ +.modal { + z-index: 1; + position: absolute; + width: 100%; + height: 100%; + background-color: rgba(128, 128, 128, 0.8); + display: flex; + justify-content: center; + align-items: center; +} + +.modal_window { + border: 1px solid black; + border-radius: 5px; + padding: 1em; +} + +.modal_title { + background-color: skyblue; +} + +.modal_cta { + margin-top: var(--margin-s); +} diff --git a/visions/ui/src/components/Modal/Modal.tsx b/visions/ui/src/components/Modal/Modal.tsx new file mode 100644 index 0000000..c2ffd8b --- /dev/null +++ b/visions/ui/src/components/Modal/Modal.tsx @@ -0,0 +1,33 @@ +import { PropsWithChildren } from "react"; +import "./Modal.css"; + +export type Action = { + name: string + action: () => void +} + +export interface ModalProps { + title: string + + onCancel: Action + onPrimary: Action + onSecondary?: Action +} + +export const Modal = ({ title, children, onCancel, onPrimary, onSecondary }: PropsWithChildren) => ( +
+
+

{title}

+
+ {children} +
+
+ + {onSecondary ? : <>} + +
+
+
+) + + diff --git a/visions/ui/src/components/Profile/Profile.css b/visions/ui/src/components/Profile/Profile.css index 03fd3a6..6720cf8 100644 --- a/visions/ui/src/components/Profile/Profile.css +++ b/visions/ui/src/components/Profile/Profile.css @@ -2,11 +2,3 @@ margin: var(--margin-s); } -.profile_columns { - display: flex; - justify-content: space-between; -} - -.profile_columns > div { - width: 45%; -} diff --git a/visions/ui/src/components/Profile/Profile.tsx b/visions/ui/src/components/Profile/Profile.tsx index a6262cc..3bf4074 100644 --- a/visions/ui/src/components/Profile/Profile.tsx +++ b/visions/ui/src/components/Profile/Profile.tsx @@ -4,15 +4,12 @@ import './Profile.css'; interface ProfileProps { profile: UserOverview, - users: UserOverview[], games: GameOverview[], } -export const ProfileElement = ({ profile, users, games }: ProfileProps) => { +export const ProfileElement = ({ profile, games }: ProfileProps) => { const adminNote = profile.isAdmin ?
Note: this user is an admin
: <>; - const userList = profile.isAdmin && ; - return (
Games: {games.map((game) => { @@ -20,7 +17,5 @@ export const ProfileElement = ({ profile, users, games }: ProfileProps) => { }) }
{adminNote}
- - {userList}
) } diff --git a/visions/ui/src/components/UserManagement/UserManagement.tsx b/visions/ui/src/components/UserManagement/UserManagement.tsx index b81e02d..8a5da94 100644 --- a/visions/ui/src/components/UserManagement/UserManagement.tsx +++ b/visions/ui/src/components/UserManagement/UserManagement.tsx @@ -1,12 +1,15 @@ +import { PropsWithChildren, useState } from "react" import { AccountState, UserOverview } from "visions-types" -import { CardElement } from ".." +import { CardElement} from ".." interface UserManagementProps { users: UserOverview[] + onShowCreateUser: () => void } -export const UserManagementElement = ({ users }: UserManagementProps) => { +export const UserManagementElement = ({ users, onShowCreateUser }: UserManagementProps) => { return ( + <> @@ -18,8 +21,9 @@ export const UserManagementElement = ({ users }: UserManagementProps) => { )}
- +
+ ) } diff --git a/visions/ui/src/components/index.ts b/visions/ui/src/components/index.ts index e9b3455..8cc0b77 100644 --- a/visions/ui/src/components/index.ts +++ b/visions/ui/src/components/index.ts @@ -1,9 +1,10 @@ import { CardElement } from './Card/Card' import { GameOverviewElement } from './GameOverview/GameOverview' +import { Modal, ModalProps } from './Modal/Modal' import { ProfileElement } from './Profile/Profile' import { SimpleGuage } from './Guages/SimpleGuage' import { ThumbnailElement } from './Thumbnail/Thumbnail' import { TabletopElement } from './Tabletop/Tabletop' import { UserManagementElement } from './UserManagement/UserManagement' -export { CardElement, GameOverviewElement, UserManagementElement, ProfileElement, ThumbnailElement, TabletopElement, SimpleGuage } +export { CardElement, GameOverviewElement, Modal, type ModalProps, UserManagementElement, ProfileElement, ThumbnailElement, TabletopElement, SimpleGuage } diff --git a/visions/ui/src/providers/StateProvider/StateProvider.tsx b/visions/ui/src/providers/StateProvider/StateProvider.tsx index 0abfead..bdb0dec 100644 --- a/visions/ui/src/providers/StateProvider/StateProvider.tsx +++ b/visions/ui/src/providers/StateProvider/StateProvider.tsx @@ -97,12 +97,15 @@ class StateManager { break; } } - /* + } + + async createUser(username: string) { + if (!this.client || !this.dispatch) return; + + const sessionId = window.localStorage.getItem("sessionId"); if (sessionId) { - window.localStorage.setItem("sessionId", sessionId); - this.dispatch({ type: "SetAuthState", content: { type: "Authed", sessionId } }); + let createUserResponse = await this.client.createUser(sessionId, username); } - */ } } diff --git a/visions/ui/src/views/Admin/Admin.tsx b/visions/ui/src/views/Admin/Admin.tsx index 721c17f..462af60 100644 --- a/visions/ui/src/views/Admin/Admin.tsx +++ b/visions/ui/src/views/Admin/Admin.tsx @@ -45,7 +45,7 @@ interface AdminProps { client: Client, } -export const Admin = ({ client }: AdminProps) => { +export const AdminView = ({ client }: AdminProps) => { const [users, setUsers] = useState>([]); useEffect(() => { diff --git a/visions/ui/src/views/Main/Main.tsx b/visions/ui/src/views/Main/Main.tsx index fb05a60..ba43234 100644 --- a/visions/ui/src/views/Main/Main.tsx +++ b/visions/ui/src/views/Main/Main.tsx @@ -3,6 +3,7 @@ import { UserOverview } from 'visions-types'; import { Client } from '../../client'; import { ProfileElement } from '../../components'; import { getSessionId, StateContext, StateProvider } from '../../providers/StateProvider/StateProvider'; +import { ProfileView } from '../Profile/Profile'; interface MainProps { client: Client @@ -26,7 +27,7 @@ export const MainView = ({ client }: MainProps) => { return (
- {profile && } + {profile && }
) } diff --git a/visions/ui/src/views/Profile/Profile.css b/visions/ui/src/views/Profile/Profile.css new file mode 100644 index 0000000..789838c --- /dev/null +++ b/visions/ui/src/views/Profile/Profile.css @@ -0,0 +1,9 @@ +.profile-view_columns { + display: flex; + justify-content: space-between; +} + +.profile-view_columns > div { + width: 45%; +} + diff --git a/visions/ui/src/views/Profile/Profile.tsx b/visions/ui/src/views/Profile/Profile.tsx new file mode 100644 index 0000000..2b0a6b6 --- /dev/null +++ b/visions/ui/src/views/Profile/Profile.tsx @@ -0,0 +1,45 @@ +import { useContext, useState } from "react" +import { GameOverview, UserOverview } from "visions-types" +import { Modal, ModalProps, ProfileElement, UserManagementElement } from "../../components" +import { StateContext } from "../../providers/StateProvider/StateProvider" +import "./Profile.css" + +interface ProfileProps { + profile: UserOverview, + users: UserOverview[], + games: GameOverview[], +} + +interface CreateUserModalProps { + onCancel: () => void + onCreateUser: (name: string) => void +} + +const CreateUserModal = ({ onCancel, onCreateUser }: CreateUserModalProps) => { + const [userName, setUserName] = useState(""); + + return onCreateUser(userName) }}> + { + setUserName(evt.target.value); + }} /> + +} + +export const ProfileView = ({ profile, users, games, }: ProfileProps) => { + const [_state, manager] = useContext(StateContext) + const [showUser, setShowUser] = useState(false) + + const userList = profile.isAdmin && setShowUser(true)} /> + + return ( +
+ {showUser && setShowUser(false)} onCreateUser={(username) => manager.createUser(username)} />} +
+ + + {userList} +
+
+ ) +} diff --git a/visions/ui/src/views/index.ts b/visions/ui/src/views/index.ts index 0719403..7a000d1 100644 --- a/visions/ui/src/views/index.ts +++ b/visions/ui/src/views/index.ts @@ -1,3 +1,4 @@ import { MainView } from './Main/Main' +import { ProfileView } from './Profile/Profile' -export { MainView } +export { MainView, ProfileView }