ezidam: user settings: show personal info

This commit is contained in:
Philippe Loctaux 2023-04-05 19:59:04 +02:00
parent 71643f6e37
commit 29fbcb6428
8 changed files with 181 additions and 9 deletions

View file

@ -36,7 +36,10 @@ impl Icon {
"moon", Moon, r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-moon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path></svg>"#,
"sun", Sun, r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-sun" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0"></path><path d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7"></path></svg>"#,
"arrow-left", ArrowLeft, r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-left" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M5 12l14 0"></path><path d="M5 12l6 6"></path><path d="M5 12l6 -6"></path></svg>"#,
"alert-triangle-large", AlertTriangleLarge, r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-lg icon-tabler icon-tabler-alert-triangle" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M10.24 3.957l-8.422 14.06a1.989 1.989 0 0 0 1.7 2.983h16.845a1.989 1.989 0 0 0 1.7 -2.983l-8.423 -14.06a1.989 1.989 0 0 0 -3.4 0z"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path></svg>"#
"alert-triangle-large", AlertTriangleLarge, r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-lg icon-tabler icon-tabler-alert-triangle" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M10.24 3.957l-8.422 14.06a1.989 1.989 0 0 0 1.7 2.983h16.845a1.989 1.989 0 0 0 1.7 -2.983l-8.423 -14.06a1.989 1.989 0 0 0 -3.4 0z"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path></svg>"#,
"id-badge-2", IdBadge2, r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-id-badge-2" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 12h3v4h-3z"></path><path d="M10 6h-6a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h16a1 1 0 0 0 1 -1v-12a1 1 0 0 0 -1 -1h-6"></path><path d="M10 3m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v3a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path><path d="M14 16h2"></path><path d="M14 12h4"></path></svg>"#,
"user", User, r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-user" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0"></path><path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"></path></svg>"#,
"at", At, r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-at" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0"></path><path d="M16 12v1.5a2.5 2.5 0 0 0 5 0v-1.5a9 9 0 1 0 -5.5 8.28"></path></svg>"#
}
}
@ -54,6 +57,9 @@ pub fn icons_to_templates(tera: &mut Tera) {
Icon::Sun,
Icon::ArrowLeft,
Icon::AlertTriangleLarge,
Icon::IdBadge2,
Icon::User,
Icon::At,
];
// For each icon, it will output: ("icons/name", "<svg>...</svg>")

View file

@ -1,25 +1,39 @@
use crate::icons::Icon;
use crate::menu::MainItem;
use crate::menu::{MainItem, SubItem};
use crate::routes;
use rocket::uri;
pub enum UserMenu {
Home,
Settings,
}
impl UserMenu {
pub fn id(&self) -> &'static str {
match self {
UserMenu::Home => "home",
UserMenu::Settings => "settings",
}
}
pub fn list() -> Vec<MainItem> {
vec![MainItem {
id: UserMenu::Home.id(),
label: "Home",
link: uri!(routes::root::home::homepage).to_string(),
icon: Icon::Home.svg,
sub: None,
}]
vec![
MainItem {
id: UserMenu::Home.id(),
label: "Home",
link: uri!(routes::root::home::homepage).to_string(),
icon: Icon::Home.svg,
sub: None,
},
MainItem {
id: UserMenu::Settings.id(),
label: "Settings",
link: uri!(routes::settings::user_settings).to_string(),
icon: Icon::Settings.svg,
sub: Some(vec![SubItem {
label: "Personal",
link: uri!(routes::settings::user_settings_personal).to_string(),
}]),
},
]
}
}

View file

@ -21,6 +21,7 @@ pub enum Page {
AdminAppsList(AdminAppsList),
AdminAppsNew(AdminAppsNew),
AdminAppsView(AdminAppsView),
UserPersonalSettings(UserPersonalSettings),
}
impl Page {
@ -38,6 +39,7 @@ impl Page {
Page::AdminAppsList(_) => "pages/admin/apps/list",
Page::AdminAppsNew(_) => "pages/admin/apps/new",
Page::AdminAppsView(_) => "pages/admin/apps/view",
Page::UserPersonalSettings(_) => "pages/settings/personal",
}
}
@ -55,6 +57,7 @@ impl Page {
Page::AdminAppsList(_) => "Applications",
Page::AdminAppsNew(_) => "New application",
Page::AdminAppsView(_) => "Application info",
Page::UserPersonalSettings(_) => "Personal settings",
}
}
@ -74,6 +77,7 @@ impl Page {
Page::AdminAppsList(_) => Some(AdminMenu::Apps.into()),
Page::AdminAppsNew(_) => Some(AdminMenu::Apps.into()),
Page::AdminAppsView(_) => Some(AdminMenu::Apps.into()),
Page::UserPersonalSettings(_) => Some(UserMenu::Settings.into()),
}
}
@ -91,6 +95,7 @@ impl Page {
Page::AdminAppsList(list) => Box::new(list),
Page::AdminAppsNew(new) => Box::new(new),
Page::AdminAppsView(view) => Box::new(view),
Page::UserPersonalSettings(personal) => Box::new(personal),
}
}
}

View file

@ -2,3 +2,4 @@ pub use crate::error::content::*;
pub use crate::routes::admin::content::*;
pub use crate::routes::oauth::content::*;
pub use crate::routes::root::content::*;
pub use crate::routes::settings::content::*;

View file

@ -3,6 +3,7 @@ use rocket::{Build, Rocket};
pub mod admin;
pub mod oauth;
pub mod root;
pub mod settings;
pub mod setup;
pub mod well_known;
@ -42,4 +43,6 @@ pub fn routes(rocket_builder: Rocket<Build>) -> Rocket<Build> {
.mount("/", well_known::routes())
// Admin
.mount("/", admin::routes())
// Settings
.mount("/", settings::routes())
}

View file

@ -0,0 +1,29 @@
use super::prelude::*;
pub use personal::*;
use rocket::get;
pub mod personal;
pub fn routes() -> Vec<Route> {
routes![user_settings, user_settings_personal]
}
#[get("/settings")]
pub async fn user_settings(_user: JwtUser) -> Redirect {
Redirect::to(uri!(user_settings_personal))
}
pub mod content {
use jwt::JwtClaims;
use rocket::serde::Serialize;
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
#[derive(Clone)]
pub struct UserPersonalSettings {
pub user: JwtClaims,
pub username: String,
pub name: Option<String>,
pub email: Option<String>,
}
}

View file

@ -0,0 +1,25 @@
use crate::routes::prelude::*;
use rocket::get;
use users::User;
#[get("/settings/personal")]
pub async fn user_settings_personal(
mut db: Connection<Database>,
jwt_user: JwtUser,
flash: Option<FlashMessage<'_>>,
) -> Result<Template> {
let user = User::get_by_login(&mut *db, &jwt_user.0.subject)
.await?
.ok_or_else(|| Error::not_found(jwt_user.0.subject.to_string()))?;
let page = Page::UserPersonalSettings(super::content::UserPersonalSettings {
user: jwt_user.0,
username: user.username().into(),
name: user.name().map(String::from),
email: user.email().map(String::from),
});
Ok(flash
.map(|flash| Page::with_flash(page.clone(), flash))
.unwrap_or_else(|| page.into()))
}

View file

@ -0,0 +1,89 @@
{% extends "shell" %}
{% block content %}
<!-- Page header -->
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<h2 class="page-title">
Settings
</h2>
</div>
</div>
</div>
</div>
<!-- Page body -->
<div class="page-body">
<div class="container-xl">
{% if flash %}
<div class="alert alert-{{flash.0}}" role="alert">
<h4 class="alert-title">{{ flash.1 | safe }}</h4>
</div>
{% endif %}
<div class="card">
<div class="row g-0">
<div class="col-3 d-none d-md-block border-end">
<div class="card-body">
<div class="list-group list-group-transparent">
<a href="./personal"
class="list-group-item list-group-item-action d-flex align-items-center active">Personal</a>
</div>
</div>
</div>
<div class="col d-flex flex-column">
<form action="" method="post" >
<div class="card-header">
<h3 class="card-title">My Profile</h3>
</div>
<div class="card-body">
<!-- Username -->
<label class="form-label required" for="username">Username</label>
<div class="input-icon mb-3">
<span class="input-icon-addon">
{% include "icons/id-badge-2" %}
</span>
<input name="username" id="username" value="{{ username }}" type="text"
placeholder="Enter a username"
class="form-control"
required>
</div>
<!-- Name -->
<label class="form-label" for="name">Full Name</label>
<div class="input-icon mb-3">
<span class="input-icon-addon">
{% include "icons/user" %}
</span>
<input name="name" id="name" value="{{ name }}" type="text"
placeholder="Napoleon Bonaparte"
class="form-control">
</div>
<!-- Email -->
<label class="form-label" for="email">Email address</label>
<div class="input-icon mb-3">
<span class="input-icon-addon">
{% include "icons/at" %}
</span>
<input name="email" id="email" value="{{ email }}" type="email"
placeholder="napoleon@bonaparte.fr"
class="form-control">
</div>
</div>
<div class="card-footer text-end">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}