Let the admin see a list of users and the state of each one
This commit is contained in:
parent
90224a6841
commit
dcd5514433
@ -140,6 +140,7 @@ impl Core {
|
|||||||
.map(|user| UserOverview {
|
.map(|user| UserOverview {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
|
state: user.state,
|
||||||
is_admin: user.admin,
|
is_admin: user.admin,
|
||||||
})
|
})
|
||||||
.collect()),
|
.collect()),
|
||||||
@ -152,15 +153,10 @@ impl Core {
|
|||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
) -> ResultExt<Option<UserOverview>, AppError, FatalError> {
|
) -> ResultExt<Option<UserOverview>, AppError, FatalError> {
|
||||||
let users = return_error!(self.list_users().await);
|
let users = return_error!(self.list_users().await);
|
||||||
let user = match users.into_iter().find(|user| user.id == user_id) {
|
match users.into_iter().find(|user| user.id == user_id) {
|
||||||
Some(user) => user,
|
Some(user) => ok(Some(user)),
|
||||||
None => return ok(None),
|
None => return ok(None),
|
||||||
};
|
}
|
||||||
ok(Some(UserOverview {
|
|
||||||
id: user.id.clone(),
|
|
||||||
name: user.name,
|
|
||||||
is_admin: user.is_admin,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_user(&self, username: &str) -> ResultExt<UserId, AppError, FatalError> {
|
pub async fn create_user(&self, username: &str) -> ResultExt<UserId, AppError, FatalError> {
|
||||||
|
@ -52,14 +52,14 @@ impl From<crate::types::User> for User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct UserOverview {
|
pub struct UserOverview {
|
||||||
pub id: UserId,
|
pub id: UserId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub is_admin: bool,
|
pub is_admin: bool,
|
||||||
|
pub state: AccountState,
|
||||||
pub games: Vec<crate::types::GameOverview>,
|
pub games: Vec<crate::types::GameOverview>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,8 +76,8 @@ impl From<crate::types::UserOverview> for UserOverview {
|
|||||||
id: input.id,
|
id: input.id,
|
||||||
name: input.name,
|
name: input.name,
|
||||||
is_admin: input.is_admin,
|
is_admin: input.is_admin,
|
||||||
|
state: AccountState::from(input.state),
|
||||||
games: vec![],
|
games: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
@ -10,6 +10,8 @@ use crate::{
|
|||||||
types::{AppError, FatalError, User},
|
types::{AppError, FatalError, User},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::UserOverview;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct AuthRequest {
|
pub struct AuthRequest {
|
||||||
@ -106,21 +108,23 @@ pub async fn get_user(
|
|||||||
core: Core,
|
core: Core,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
user_id: Option<UserId>,
|
user_id: Option<UserId>,
|
||||||
) -> ResultExt<Option<crate::types::UserOverview>, AppError, FatalError> {
|
) -> ResultExt<Option<UserOverview>, AppError, FatalError> {
|
||||||
auth_required(core.clone(), headers, |user| async move {
|
auth_required(core.clone(), headers, |user| async move {
|
||||||
match user_id {
|
match user_id {
|
||||||
Some(user_id) => core.user(user_id).await,
|
Some(user_id) => core.user(user_id).await,
|
||||||
None => core.user(user.id).await,
|
None => core.user(user.id).await,
|
||||||
}
|
}
|
||||||
}).await
|
.map(|maybe_user| maybe_user.map(|user| UserOverview::from(user)))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_users(
|
pub async fn get_users(
|
||||||
core: Core,
|
core: Core,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
) -> ResultExt<Vec<crate::types::UserOverview>, AppError, FatalError> {
|
) -> ResultExt<Vec<UserOverview>, AppError, FatalError> {
|
||||||
auth_required(core.clone(), headers, |_user| async move {
|
auth_required(core.clone(), headers, |_user| async move {
|
||||||
core.list_users().await
|
core.list_users().await.map(|users| users.into_iter().map(|user| UserOverview::from(user)).collect::<Vec<UserOverview>>())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -167,16 +167,15 @@ pub enum Message {
|
|||||||
UpdateTabletop(Tabletop),
|
UpdateTabletop(Tabletop),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
#[typeshare]
|
|
||||||
pub struct UserOverview {
|
pub struct UserOverview {
|
||||||
pub id: UserId,
|
pub id: UserId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub is_admin: bool,
|
pub is_admin: bool,
|
||||||
|
pub state: AccountState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct GameOverview {
|
pub struct GameOverview {
|
||||||
|
@ -50,7 +50,7 @@ export class Client {
|
|||||||
|
|
||||||
async users(sessionId: string) {
|
async users(sessionId: string) {
|
||||||
const url = new URL(this.base);
|
const url = new URL(this.base);
|
||||||
url.pathname = '/api/v1/users/';
|
url.pathname = '/api/v1/users';
|
||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: [['Authorization', `Bearer ${sessionId}`]]
|
headers: [['Authorization', `Bearer ${sessionId}`]]
|
||||||
|
@ -4,18 +4,23 @@ import './Profile.css';
|
|||||||
|
|
||||||
interface ProfileProps {
|
interface ProfileProps {
|
||||||
profile: UserOverview,
|
profile: UserOverview,
|
||||||
|
users: UserOverview[],
|
||||||
games: GameOverview[],
|
games: GameOverview[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProfileElement = ({ profile, games }: ProfileProps) => {
|
export const ProfileElement = ({ profile, users, games }: ProfileProps) => {
|
||||||
const adminNote = profile.isAdmin ? <div> <i>Note: this user is an admin</i> </div> : <></>;
|
const adminNote = profile.isAdmin ? <div> <i>Note: this user is an admin</i> </div> : <></>;
|
||||||
|
|
||||||
return (<div className="profile">
|
const userList = profile.isAdmin && <UserManagementElement users={users} />;
|
||||||
|
|
||||||
|
return (<div className="profile profile_columns">
|
||||||
<CardElement name={profile.name}>
|
<CardElement name={profile.name}>
|
||||||
<div>Games: {games.map((game) => {
|
<div>Games: {games.map((game) => {
|
||||||
return <span key={game.id}>{game.name} ({game.type})</span>;
|
return <span key={game.id}>{game.name} ({game.type})</span>;
|
||||||
}) }</div>
|
}) }</div>
|
||||||
{adminNote}
|
{adminNote}
|
||||||
</CardElement>
|
</CardElement>
|
||||||
|
|
||||||
|
{userList}
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { UserOverview } from "visions-types"
|
import { AccountState, UserOverview } from "visions-types"
|
||||||
import { CardElement } from ".."
|
import { CardElement } from ".."
|
||||||
|
|
||||||
interface UserManagementProps {
|
interface UserManagementProps {
|
||||||
@ -7,10 +7,26 @@ interface UserManagementProps {
|
|||||||
|
|
||||||
export const UserManagementElement = ({ users }: UserManagementProps) => {
|
export const UserManagementElement = ({ users }: UserManagementProps) => {
|
||||||
return (
|
return (
|
||||||
<CardElement>
|
<CardElement name="Users">
|
||||||
<ul>
|
<table>
|
||||||
{users.map((user) => <li key={user.id}>{user.name}</li>)}
|
<thead>
|
||||||
</ul>
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{users.map((user) => <tr key={user.id}>
|
||||||
|
<td> {user.name} </td>
|
||||||
|
<td> {formatAccountState(user.state)} </td>
|
||||||
|
</tr>)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button>Create User</button>
|
||||||
</CardElement>
|
</CardElement>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formatAccountState = (state: AccountState): string => {
|
||||||
|
switch (state.type) {
|
||||||
|
case "Normal": return "";
|
||||||
|
case "PasswordReset": return "password reset";
|
||||||
|
case "Locked": return "locked";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,13 +17,16 @@ export const MainView = ({ client }: MainProps) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
client.profile(sessionId, undefined).then((profile) => setProfile(profile))
|
client.profile(sessionId, undefined).then((profile) => setProfile(profile))
|
||||||
client.users(sessionId).then((users) => setUsers(users))
|
client.users(sessionId).then((users) => {
|
||||||
|
console.log(users);
|
||||||
|
setUsers(users);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}, [sessionId, client])
|
}, [sessionId, client])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{profile && <ProfileElement profile={profile} games={[]} />}
|
{profile && <ProfileElement profile={profile} users={users} games={[]} />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user