basics of the account page
This commit is contained in:
parent
c40f8aed4a
commit
1a152e7acb
11 changed files with 143 additions and 15 deletions
|
@ -23,9 +23,7 @@ a profile is
|
|||
- privacy policy and GDPR notice
|
||||
- p2: saving register form as it gets filled / re-display it with partial values
|
||||
- account
|
||||
- basic username / password thingy
|
||||
- p2: optional email for forgotten password i guess
|
||||
- "my registered tags" page
|
||||
- ability to delete a tag (remove all data except the ID to lock the ID)
|
||||
- ability to delete the whole account (incl. all registered tags)
|
||||
- deleting a tag keeping the account allows to re-use the tag later on. no one else can use it still
|
||||
|
|
|
@ -130,6 +130,7 @@ main {
|
|||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
section {
|
||||
|
@ -198,6 +199,11 @@ div.dual-fields {
|
|||
margin-left: 1em;
|
||||
}
|
||||
|
||||
p.subnav {
|
||||
display: flex;
|
||||
gap: 10pt;
|
||||
}
|
||||
|
||||
p.heading {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
@ -295,6 +301,28 @@ input#ident {
|
|||
color: var(--clr-primary-a50);
|
||||
}
|
||||
|
||||
.profile>div.header {
|
||||
padding-bottom: .5em;
|
||||
}
|
||||
|
||||
.profile .subnav {
|
||||
margin-top: .5em;
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
.profile .subnav a:not(:first-of-type) {
|
||||
margin-left: 4pt;
|
||||
}
|
||||
|
||||
.profile:not(:first-of-type) {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
.label {
|
||||
text-transform: uppercase;
|
||||
color: var(--clr-primary-a50);
|
||||
}
|
||||
|
||||
.id {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 1.1em;
|
||||
|
@ -326,6 +354,10 @@ input#ident {
|
|||
gap: 4pt;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
p.subnav {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
use crate::db::schema::DollProfile;
|
||||
use sqlx::{pool::PoolConnection, Postgres};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::schema::{CreateDollProfile, DollTagsDb};
|
||||
|
||||
pub async fn list(mut db: DollTagsDb, from: &Uuid) -> sqlx::Result<Vec<DollProfile>> {
|
||||
sqlx::query_as!(
|
||||
DollProfile,
|
||||
"select * from doll_profiles where bound_to_id = $1",
|
||||
from
|
||||
)
|
||||
.fetch_all(&mut **db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
mut db: DollTagsDb,
|
||||
ident: i32,
|
||||
|
|
|
@ -76,7 +76,8 @@ pub struct CreateDollProfile<'a> {
|
|||
///
|
||||
/// If you only need to check the login status without any security impact
|
||||
/// (e.g. for page template rendering reasons), use [`Session`][crate::auth::session::Session] instead.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct User {
|
||||
pub id: Uuid,
|
||||
pub created_at: chrono::DateTime<Utc>,
|
||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -8,7 +8,7 @@ use rocket::fairing::AdHoc;
|
|||
use rocket::fs::{relative, FileServer};
|
||||
use rocket_db_pools::Database;
|
||||
use routes::form::accounts;
|
||||
use routes::{error_handlers, form, public};
|
||||
use routes::{account, error_handlers, form, public};
|
||||
|
||||
pub mod auth;
|
||||
pub mod db;
|
||||
|
@ -27,13 +27,21 @@ fn rocket() -> _ {
|
|||
catchers![error_handlers::not_found, session::unauthorized],
|
||||
)
|
||||
.mount("/assets", FileServer::from(relative!("/assets")))
|
||||
.mount(
|
||||
"/account",
|
||||
routes![
|
||||
account::index,
|
||||
account::show_settings,
|
||||
form::register_tag::show_register,
|
||||
form::register_tag::handle_register,
|
||||
],
|
||||
)
|
||||
.mount(
|
||||
"/",
|
||||
routes![
|
||||
public::index,
|
||||
public::short_url,
|
||||
public::show_profile,
|
||||
form::register_tag::show_register,
|
||||
form::register_tag::handle_register,
|
||||
accounts::show_register,
|
||||
accounts::handle_register,
|
||||
accounts::show_login,
|
||||
|
|
31
src/routes/account.rs
Normal file
31
src/routes/account.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use rocket_dyn_templates::{context, Template};
|
||||
|
||||
use crate::{
|
||||
db::{
|
||||
doll,
|
||||
schema::{DollTagsDb, User},
|
||||
},
|
||||
pages::CommonTemplateState,
|
||||
};
|
||||
|
||||
use super::error_handlers::PageResult;
|
||||
|
||||
#[get("/")]
|
||||
pub async fn index(db: DollTagsDb, user: User, meta: CommonTemplateState) -> PageResult {
|
||||
let tags = doll::list(db, &user.id).await?;
|
||||
|
||||
Ok(Template::render(
|
||||
"account/index",
|
||||
context! {
|
||||
meta,
|
||||
user,
|
||||
tags,
|
||||
},
|
||||
)
|
||||
.into())
|
||||
}
|
||||
|
||||
#[get("/settings")]
|
||||
pub fn show_settings(user: User, meta: CommonTemplateState) -> Template {
|
||||
todo!("woof");
|
||||
}
|
|
@ -3,20 +3,19 @@ use rocket::{
|
|||
response::Redirect,
|
||||
};
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use uuid::uuid;
|
||||
|
||||
use crate::{
|
||||
db::{
|
||||
doll,
|
||||
schema::{CreateDollProfile, DollTagsDb},
|
||||
schema::{CreateDollProfile, DollTagsDb, User},
|
||||
},
|
||||
ids::{id_public_to_db, pick_ids},
|
||||
pages::CommonTemplateState,
|
||||
routes::{error_handlers::PageResult, public},
|
||||
};
|
||||
|
||||
#[get("/register_tag")]
|
||||
pub async fn show_register(db: DollTagsDb, meta: CommonTemplateState) -> PageResult {
|
||||
#[get("/new_tag")]
|
||||
pub async fn show_register(db: DollTagsDb, _user: User, meta: CommonTemplateState) -> PageResult {
|
||||
let ids = pick_ids(db).await?;
|
||||
|
||||
Ok(Template::render(
|
||||
|
@ -92,10 +91,11 @@ fn validate_chassis<'v>(a: &str, b: &str, c: &str, field: &str) -> form::Result<
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[post("/register_tag", data = "<tag>")]
|
||||
#[post("/new_tag", data = "<tag>")]
|
||||
pub async fn handle_register(
|
||||
mut db: DollTagsDb,
|
||||
tag: Form<Contextual<'_, TagForm<'_>>>,
|
||||
user: User,
|
||||
meta: CommonTemplateState,
|
||||
) -> PageResult {
|
||||
let tag = match tag.value {
|
||||
|
@ -132,7 +132,7 @@ pub async fn handle_register(
|
|||
let microchip_id = normalize_opt(&normalized_microchip_id);
|
||||
|
||||
if doll::id_exists(&mut *db, id, microchip_id.unwrap_or("")).await? {
|
||||
return Ok(Redirect::found(uri!(show_register)).into());
|
||||
return Ok(Redirect::found(uri!("/account", show_register)).into());
|
||||
}
|
||||
|
||||
doll::create(
|
||||
|
@ -153,7 +153,7 @@ pub async fn handle_register(
|
|||
chassis_type: normalize_opt(tag.chassis_type),
|
||||
chassis_id: normalize_opt(tag.chassis_id),
|
||||
chassis_color: normalize_opt(tag.chassis_color),
|
||||
bound_to_id: &uuid!("00000000-0000-0000-0000-000000000000"), // TODO: remove once accounts exist
|
||||
bound_to_id: &user.id,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod account;
|
||||
pub mod error_handlers;
|
||||
pub mod form;
|
||||
pub mod public;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use rocket::response::Redirect;
|
||||
use rocket_dyn_templates::{context, Template};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
db::{doll, schema::DollTagsDb},
|
||||
|
@ -27,6 +28,11 @@ pub fn index(
|
|||
)
|
||||
}
|
||||
|
||||
#[get("/profile/<id>")]
|
||||
pub fn short_url(id: &str) -> Redirect {
|
||||
Redirect::to(uri!(show_profile(Some(id), Some(""))))
|
||||
}
|
||||
|
||||
#[get("/profile?<ident>&<microchip_id>")]
|
||||
pub async fn show_profile(
|
||||
db: DollTagsDb,
|
||||
|
|
41
templates/account/index.html.tera
Normal file
41
templates/account/index.html.tera
Normal file
|
@ -0,0 +1,41 @@
|
|||
{% extends "base" %}
|
||||
{% block title %}Log in - {% endblock title %}
|
||||
{% block main %}
|
||||
<aside>
|
||||
<p class="subnav">
|
||||
<a href="/account/new_tag">New tag</a>
|
||||
<a href="/account/settings">Account settings</a>
|
||||
<a href="/logout">Log out</a>
|
||||
</p>
|
||||
</aside>
|
||||
<section>
|
||||
<h2>Your {{tags | length}} tags</h2>
|
||||
|
||||
{% for profile in tags %}
|
||||
<article class="profile">
|
||||
<div class="header">
|
||||
<p class="ident">
|
||||
Tag ID <span class="id">{{profile.id | pretty_id}}</span>,
|
||||
{% if profile.microchip_id %}
|
||||
Microchip ID <span class="id">{{profile.microchip_id}}</span>
|
||||
{% else %}
|
||||
Not microchipped
|
||||
{% endif %}
|
||||
</p>
|
||||
<p class="pronouns">
|
||||
<span class="label">Pronouns</span>
|
||||
<span class="pronoun_fragment">{{profile.pronoun_subject}}</span> /
|
||||
<span class="pronoun_fragment">{{profile.pronoun_object}}</span> /
|
||||
<span class="pronoun_fragment">{{profile.pronoun_possessive}}</span>
|
||||
</p>
|
||||
</div>
|
||||
<h3>{{profile.name}}</h3>
|
||||
<div class="subnav">
|
||||
<a href="/profile/{{profile.id}}">Public page</a>
|
||||
<a href="/account/edit/{{profile.id}}">Edit</a>
|
||||
<a href="/account/delete/{{profile.id}}">Delete</a>
|
||||
</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endblock main %}
|
|
@ -13,8 +13,7 @@
|
|||
|
||||
<nav>
|
||||
{% if meta.logged_in %}
|
||||
<a class="btn" href="/register_tag">New tag</a>
|
||||
<a href="/logout" class="btn">Log out</a>
|
||||
<a class="btn" href="/account">Account</a>
|
||||
{% else %}
|
||||
<a href="/login" class="btn">Log in</a>
|
||||
{% endif %}
|
||||
|
|
Loading…
Add table
Reference in a new issue