Set up the landing page, which shows the user their profile

This commit is contained in:
Savanni D'Gerinel 2025-01-03 16:50:56 -05:00
parent 208083d39e
commit f59c3544b4
5 changed files with 64 additions and 20 deletions

View File

@ -1,6 +1,6 @@
use axum::{ use axum::{
extract::Path, extract::Path,
http::{header::CONTENT_TYPE, HeaderMap, Method, StatusCode}, http::{header::{AUTHORIZATION, CONTENT_TYPE}, HeaderMap, Method},
routing::{get, post, put}, routing::{get, post, put},
Json, Router, Json, Router,
}; };
@ -8,7 +8,7 @@ use tower_http::cors::{Any, CorsLayer};
use crate::{ use crate::{
core::Core, core::Core,
database::{SessionId, UserId}, database::UserId,
handlers::{ handlers::{
check_password, create_game, create_user, get_user, healthcheck, set_password, check_password, create_game, create_user, get_user, healthcheck, set_password,
wrap_handler, AuthRequest, CreateGameRequest, CreateUserRequest, SetPasswordRequest, wrap_handler, AuthRequest, CreateGameRequest, CreateUserRequest, SetPasswordRequest,
@ -34,8 +34,12 @@ pub fn routes(core: Core) -> Router {
post({ post({
let core = core.clone(); let core = core.clone();
move |req: Json<AuthRequest>| wrap_handler(|| check_password(core, req)) move |req: Json<AuthRequest>| 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( .route(
@ -45,6 +49,12 @@ pub fn routes(core: Core) -> Router {
let core = core.clone(); let core = core.clone();
move |headers: HeaderMap| wrap_handler(|| get_user(core, headers, None)) move |headers: HeaderMap| wrap_handler(|| get_user(core, headers, None))
}) })
.layer(
CorsLayer::new()
.allow_methods([Method::GET])
.allow_headers([AUTHORIZATION])
.allow_origin(Any),
)
.put({ .put({
let core = core.clone(); let core = core.clone();
move |headers: HeaderMap, req: Json<CreateUserRequest>| { move |headers: HeaderMap, req: Json<CreateUserRequest>| {

View File

@ -1,4 +1,4 @@
import { SessionId } from "visions-types"; import { SessionId, UserId, UserProfile } from "visions-types";
export type PlayingField = { export type PlayingField = {
backgroundImage: string; backgroundImage: string;
@ -70,11 +70,29 @@ export class Client {
async auth(username: string, password: string): Promise<SessionId | undefined> { async auth(username: string, password: string): Promise<SessionId | undefined> {
const url = new URL(this.base); const url = new URL(this.base);
url.pathname = `/api/v1/auth` 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(); const session_id: SessionId = await response.json();
return session_id; return session_id;
} }
async profile(sessionId: SessionId, userId: UserId | undefined): Promise<UserProfile | undefined> {
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() { async health() {
const url = new URL(this.base); const url = new URL(this.base);
url.pathname = `/api/v1/health`; url.pathname = `/api/v1/health`;

View File

@ -1,10 +1,12 @@
import React from 'react'; import { UserProfile } from 'visions-types';
import { Client } from '../../client';
interface ProfileProps { export const ProfileElement = ({ name, games, is_admin }: UserProfile) => {
client: Client const adminNote = is_admin ? <div> <i>Note: this user is an admin</i> </div> : <></>;
}
export const ProfileElement = ({ client }: ProfileProps) => { return (
return <div></div> <div className="card">
<h1>{name}</h1>
<div>Games: {games.map((game) => <>{game.game_name} ({game.game_type})</>).join(', ')}</div>
{adminNote}
</div>)
} }

View File

@ -1,5 +1,6 @@
import { ProfileElement } from './Profile/Profile'
import { SimpleGuage } from './Guages/SimpleGuage'
import { ThumbnailElement } from './Thumbnail/Thumbnail' import { ThumbnailElement } from './Thumbnail/Thumbnail'
import { TabletopElement } from './Tabletop/Tabletop' import { TabletopElement } from './Tabletop/Tabletop'
import { SimpleGuage } from './Guages/SimpleGuage'
export default { ThumbnailElement, TabletopElement, SimpleGuage } export { ProfileElement, ThumbnailElement, TabletopElement, SimpleGuage }

View File

@ -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 { Client } from '../../client';
import { ProfileElement } from '../../components';
import { getSessionId, StateContext, StateProvider } from '../../providers/StateProvider/StateProvider'; import { getSessionId, StateContext, StateProvider } from '../../providers/StateProvider/StateProvider';
interface MainProps { interface MainProps {
@ -7,10 +9,21 @@ interface MainProps {
} }
export const MainView = ({ client }: MainProps) => { export const MainView = ({ client }: MainProps) => {
const [state, manager] = useContext(StateContext) const [state, _manager] = useContext(StateContext)
const [profile, setProfile] = useState<UserProfile | undefined>(undefined)
const sessionId = getSessionId(state); const sessionId = getSessionId(state)
useEffect(() => {
if (sessionId) {
client.profile(sessionId, undefined).then((profile) => setProfile(profile))
}
}, [sessionId, client])
return <div>Profile: {sessionId}</div> return (
<div>
<div>Session ID: {sessionId}</div>
{profile && <ProfileElement {...profile} />}
</div>
)
} }