ezidam: user settings: show personal info
This commit is contained in:
parent
71643f6e37
commit
29fbcb6428
8 changed files with 181 additions and 9 deletions
|
|
@ -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>"#,
|
"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>"#,
|
"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>"#,
|
"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::Sun,
|
||||||
Icon::ArrowLeft,
|
Icon::ArrowLeft,
|
||||||
Icon::AlertTriangleLarge,
|
Icon::AlertTriangleLarge,
|
||||||
|
Icon::IdBadge2,
|
||||||
|
Icon::User,
|
||||||
|
Icon::At,
|
||||||
];
|
];
|
||||||
|
|
||||||
// For each icon, it will output: ("icons/name", "<svg>...</svg>")
|
// For each icon, it will output: ("icons/name", "<svg>...</svg>")
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,39 @@
|
||||||
use crate::icons::Icon;
|
use crate::icons::Icon;
|
||||||
use crate::menu::MainItem;
|
use crate::menu::{MainItem, SubItem};
|
||||||
use crate::routes;
|
use crate::routes;
|
||||||
use rocket::uri;
|
use rocket::uri;
|
||||||
|
|
||||||
pub enum UserMenu {
|
pub enum UserMenu {
|
||||||
Home,
|
Home,
|
||||||
|
Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserMenu {
|
impl UserMenu {
|
||||||
pub fn id(&self) -> &'static str {
|
pub fn id(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
UserMenu::Home => "home",
|
UserMenu::Home => "home",
|
||||||
|
UserMenu::Settings => "settings",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn list() -> Vec<MainItem> {
|
pub fn list() -> Vec<MainItem> {
|
||||||
vec![MainItem {
|
vec![
|
||||||
id: UserMenu::Home.id(),
|
MainItem {
|
||||||
label: "Home",
|
id: UserMenu::Home.id(),
|
||||||
link: uri!(routes::root::home::homepage).to_string(),
|
label: "Home",
|
||||||
icon: Icon::Home.svg,
|
link: uri!(routes::root::home::homepage).to_string(),
|
||||||
sub: None,
|
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(),
|
||||||
|
}]),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ pub enum Page {
|
||||||
AdminAppsList(AdminAppsList),
|
AdminAppsList(AdminAppsList),
|
||||||
AdminAppsNew(AdminAppsNew),
|
AdminAppsNew(AdminAppsNew),
|
||||||
AdminAppsView(AdminAppsView),
|
AdminAppsView(AdminAppsView),
|
||||||
|
UserPersonalSettings(UserPersonalSettings),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
|
|
@ -38,6 +39,7 @@ impl Page {
|
||||||
Page::AdminAppsList(_) => "pages/admin/apps/list",
|
Page::AdminAppsList(_) => "pages/admin/apps/list",
|
||||||
Page::AdminAppsNew(_) => "pages/admin/apps/new",
|
Page::AdminAppsNew(_) => "pages/admin/apps/new",
|
||||||
Page::AdminAppsView(_) => "pages/admin/apps/view",
|
Page::AdminAppsView(_) => "pages/admin/apps/view",
|
||||||
|
Page::UserPersonalSettings(_) => "pages/settings/personal",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +57,7 @@ impl Page {
|
||||||
Page::AdminAppsList(_) => "Applications",
|
Page::AdminAppsList(_) => "Applications",
|
||||||
Page::AdminAppsNew(_) => "New application",
|
Page::AdminAppsNew(_) => "New application",
|
||||||
Page::AdminAppsView(_) => "Application info",
|
Page::AdminAppsView(_) => "Application info",
|
||||||
|
Page::UserPersonalSettings(_) => "Personal settings",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,6 +77,7 @@ impl Page {
|
||||||
Page::AdminAppsList(_) => Some(AdminMenu::Apps.into()),
|
Page::AdminAppsList(_) => Some(AdminMenu::Apps.into()),
|
||||||
Page::AdminAppsNew(_) => Some(AdminMenu::Apps.into()),
|
Page::AdminAppsNew(_) => Some(AdminMenu::Apps.into()),
|
||||||
Page::AdminAppsView(_) => 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::AdminAppsList(list) => Box::new(list),
|
||||||
Page::AdminAppsNew(new) => Box::new(new),
|
Page::AdminAppsNew(new) => Box::new(new),
|
||||||
Page::AdminAppsView(view) => Box::new(view),
|
Page::AdminAppsView(view) => Box::new(view),
|
||||||
|
Page::UserPersonalSettings(personal) => Box::new(personal),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,4 @@ pub use crate::error::content::*;
|
||||||
pub use crate::routes::admin::content::*;
|
pub use crate::routes::admin::content::*;
|
||||||
pub use crate::routes::oauth::content::*;
|
pub use crate::routes::oauth::content::*;
|
||||||
pub use crate::routes::root::content::*;
|
pub use crate::routes::root::content::*;
|
||||||
|
pub use crate::routes::settings::content::*;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use rocket::{Build, Rocket};
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod oauth;
|
pub mod oauth;
|
||||||
pub mod root;
|
pub mod root;
|
||||||
|
pub mod settings;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
pub mod well_known;
|
pub mod well_known;
|
||||||
|
|
||||||
|
|
@ -42,4 +43,6 @@ pub fn routes(rocket_builder: Rocket<Build>) -> Rocket<Build> {
|
||||||
.mount("/", well_known::routes())
|
.mount("/", well_known::routes())
|
||||||
// Admin
|
// Admin
|
||||||
.mount("/", admin::routes())
|
.mount("/", admin::routes())
|
||||||
|
// Settings
|
||||||
|
.mount("/", settings::routes())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
crates/ezidam/src/routes/settings.rs
Normal file
29
crates/ezidam/src/routes/settings.rs
Normal 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>,
|
||||||
|
}
|
||||||
|
}
|
||||||
25
crates/ezidam/src/routes/settings/personal.rs
Normal file
25
crates/ezidam/src/routes/settings/personal.rs
Normal 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()))
|
||||||
|
}
|
||||||
89
crates/ezidam/templates/pages/settings/personal.html.tera
Normal file
89
crates/ezidam/templates/pages/settings/personal.html.tera
Normal 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 %}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue