From f59c3544b43312746781e4454b0fa756df256b74 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Fri, 3 Jan 2025 16:50:56 -0500 Subject: [PATCH] Set up the landing page, which shows the user their profile --- visions/server/src/routes.rs | 20 ++++++++++++----- visions/ui/src/client.ts | 22 +++++++++++++++++-- visions/ui/src/components/Profile/Profile.tsx | 16 ++++++++------ visions/ui/src/components/index.ts | 5 +++-- visions/ui/src/views/Main/Main.tsx | 21 ++++++++++++++---- 5 files changed, 64 insertions(+), 20 deletions(-) diff --git a/visions/server/src/routes.rs b/visions/server/src/routes.rs index bc51f23..7b23280 100644 --- a/visions/server/src/routes.rs +++ b/visions/server/src/routes.rs @@ -1,6 +1,6 @@ use axum::{ extract::Path, - http::{header::CONTENT_TYPE, HeaderMap, Method, StatusCode}, + http::{header::{AUTHORIZATION, CONTENT_TYPE}, HeaderMap, Method}, routing::{get, post, put}, Json, Router, }; @@ -8,7 +8,7 @@ use tower_http::cors::{Any, CorsLayer}; use crate::{ core::Core, - database::{SessionId, UserId}, + database::UserId, handlers::{ check_password, create_game, create_user, get_user, healthcheck, set_password, wrap_handler, AuthRequest, CreateGameRequest, CreateUserRequest, SetPasswordRequest, @@ -34,9 +34,13 @@ pub fn routes(core: Core) -> Router { post({ let core = core.clone(); move |req: Json| wrap_handler(|| check_password(core, req)) - }).layer( - CorsLayer::new().allow_methods([Method::POST]).allow_headers([CONTENT_TYPE]).allow_origin(Any), - ), + }) + .layer( + CorsLayer::new() + .allow_methods([Method::POST]) + .allow_headers([CONTENT_TYPE]) + .allow_origin(Any), + ), ) .route( // By default, just get the self user. @@ -45,6 +49,12 @@ pub fn routes(core: Core) -> Router { let core = core.clone(); move |headers: HeaderMap| wrap_handler(|| get_user(core, headers, None)) }) + .layer( + CorsLayer::new() + .allow_methods([Method::GET]) + .allow_headers([AUTHORIZATION]) + .allow_origin(Any), + ) .put({ let core = core.clone(); move |headers: HeaderMap, req: Json| { diff --git a/visions/ui/src/client.ts b/visions/ui/src/client.ts index 5521429..af8815c 100644 --- a/visions/ui/src/client.ts +++ b/visions/ui/src/client.ts @@ -1,4 +1,4 @@ -import { SessionId } from "visions-types"; +import { SessionId, UserId, UserProfile } from "visions-types"; export type PlayingField = { backgroundImage: string; @@ -70,11 +70,29 @@ export class Client { async auth(username: string, password: string): Promise { const url = new URL(this.base); url.pathname = `/api/v1/auth` - const response = await fetch(url, { method: 'POST', headers: [['Content-Type', 'application/json']], body: JSON.stringify({ 'username': username, 'password': password }) }); + const response = await fetch(url, { + method: 'POST', + headers: [['Content-Type', 'application/json']], + body: JSON.stringify({ 'username': username, 'password': password }) + }); const session_id: SessionId = await response.json(); return session_id; } + async profile(sessionId: SessionId, userId: UserId | undefined): Promise { + const url = new URL(this.base); + if (userId) { + url.pathname = `/api/v1/user${userId}` + } else { + url.pathname = `/api/v1/user` + } + const response = await fetch(url, { + method: 'GET', + headers: [['Authorization', `Bearer ${sessionId}`]], + }); + return await response.json() + } + async health() { const url = new URL(this.base); url.pathname = `/api/v1/health`; diff --git a/visions/ui/src/components/Profile/Profile.tsx b/visions/ui/src/components/Profile/Profile.tsx index 9c38a15..dde4545 100644 --- a/visions/ui/src/components/Profile/Profile.tsx +++ b/visions/ui/src/components/Profile/Profile.tsx @@ -1,10 +1,12 @@ -import React from 'react'; -import { Client } from '../../client'; +import { UserProfile } from 'visions-types'; -interface ProfileProps { - client: Client -} +export const ProfileElement = ({ name, games, is_admin }: UserProfile) => { + const adminNote = is_admin ?
Note: this user is an admin
: <>; -export const ProfileElement = ({ client }: ProfileProps) => { - return
+ return ( +
+

{name}

+
Games: {games.map((game) => <>{game.game_name} ({game.game_type})).join(', ')}
+ {adminNote} +
) } diff --git a/visions/ui/src/components/index.ts b/visions/ui/src/components/index.ts index 64551ca..95a33e5 100644 --- a/visions/ui/src/components/index.ts +++ b/visions/ui/src/components/index.ts @@ -1,5 +1,6 @@ +import { ProfileElement } from './Profile/Profile' +import { SimpleGuage } from './Guages/SimpleGuage' import { ThumbnailElement } from './Thumbnail/Thumbnail' import { TabletopElement } from './Tabletop/Tabletop' -import { SimpleGuage } from './Guages/SimpleGuage' -export default { ThumbnailElement, TabletopElement, SimpleGuage } +export { ProfileElement, ThumbnailElement, TabletopElement, SimpleGuage } diff --git a/visions/ui/src/views/Main/Main.tsx b/visions/ui/src/views/Main/Main.tsx index 27ded81..89d69ec 100644 --- a/visions/ui/src/views/Main/Main.tsx +++ b/visions/ui/src/views/Main/Main.tsx @@ -1,5 +1,7 @@ -import React, { useContext } from 'react'; +import { useContext, useEffect, useState } from 'react'; +import { UserProfile } from 'visions-types'; import { Client } from '../../client'; +import { ProfileElement } from '../../components'; import { getSessionId, StateContext, StateProvider } from '../../providers/StateProvider/StateProvider'; interface MainProps { @@ -7,10 +9,21 @@ interface MainProps { } export const MainView = ({ client }: MainProps) => { - const [state, manager] = useContext(StateContext) + const [state, _manager] = useContext(StateContext) + const [profile, setProfile] = useState(undefined) - const sessionId = getSessionId(state); + const sessionId = getSessionId(state) + useEffect(() => { + if (sessionId) { + client.profile(sessionId, undefined).then((profile) => setProfile(profile)) + } + }, [sessionId, client]) - return
Profile: {sessionId}
+ return ( +
+
Session ID: {sessionId}
+ {profile && } +
+ ) }