Upgrade to latest cargo packages
Refactor code to use the person table
This commit is contained in:
parent
c9dd17ae14
commit
1589ebfd37
File diff suppressed because it is too large
Load Diff
|
|
@ -7,10 +7,10 @@ edition = "2021"
|
|||
|
||||
# Update all dependencies with `cargo upgrade -i allow && cargo update`
|
||||
[dependencies]
|
||||
axum = { version = "0.7.6" }
|
||||
axum_session = { version = "0.14.2" }
|
||||
axum-server = { version = "0.7.1" }
|
||||
axum-extra = { version = "0.9.4", features = ["cookie-private", "typed-header"] }
|
||||
axum = { version = "0.8.1" }
|
||||
axum_session = { version = "0.16.0" }
|
||||
axum-server = { version = "0.7.2" }
|
||||
axum-extra = { version = "0.10.0", features = ["cookie-private", "typed-header"] }
|
||||
askama = "0.12.0"
|
||||
askama_axum = "0.4.0"
|
||||
headers = "0.4"
|
||||
|
|
|
|||
|
|
@ -19,8 +19,12 @@ ADD
|
|||
ALTER TABLE if exists users
|
||||
ADD COLUMN person_id uuid REFERENCES people (id) ON DELETE SET NULL;
|
||||
|
||||
-- Copy data
|
||||
-- Copy accounts(users) to profiles(people)
|
||||
insert into people (created_by, updated_by, email, name, family_name, given_name)
|
||||
select created_by, updated_by, email, name, family_name, given_name from users;
|
||||
|
||||
update users u set person_id = p.id from people p where p.email = u.email;
|
||||
-- Link accounts to profiles
|
||||
update users u set person_id = p.id from people p where p.email = u.email;
|
||||
|
||||
-- Move wishlist items from accounts to profiles
|
||||
update wishlist_items wi set user_id = p.person_id from users p where p.id = wi.user_id;
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
use askama::Template;
|
||||
use askama_axum::{IntoResponse, Response};
|
||||
use axum::{
|
||||
body::Body, extract::{Path, Query, Request, State}, response::{Html, Redirect}, Extension
|
||||
body::Body, extract::{Path, Query, Request, State}, response::{Html, IntoResponse, Redirect, Response}, Extension
|
||||
};
|
||||
use http::StatusCode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
// GOOGLE_CLIENT_SECRET=yyy
|
||||
|
||||
use axum::{
|
||||
extract::{Extension, Host, Query, State}, response::{IntoResponse, Redirect}
|
||||
extract::{Extension, Query, State}, response::{IntoResponse, Redirect}
|
||||
};
|
||||
use axum_extra::TypedHeader;
|
||||
use axum_extra::{extract::Host, TypedHeader};
|
||||
use dotenvy::var;
|
||||
use headers::Cookie;
|
||||
use http::{HeaderMap, HeaderValue};
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ use calendar::{calendar, get_events};
|
|||
use error_handling::AppError;
|
||||
use google_oauth::{google_auth_return, login, logout};
|
||||
use middlewares::inject_user_data;
|
||||
use routes::{about, contact, dashboard, index, profile, user_profile, useradmin};
|
||||
use user::{add_user_role, delete_user_role, UserData};
|
||||
use routes::{about, contact, dashboard, index, profile, user_profile, user_profile_account, useradmin};
|
||||
use user::{add_user_role, delete_user_role, user_account, UserData};
|
||||
use wishlist::{
|
||||
user_wishlist, user_wishlist_add, user_wishlist_add_item, user_wishlist_bought_item,
|
||||
user_wishlist_delete_item, user_wishlist_edit_item, user_wishlist_received_item,
|
||||
|
|
@ -77,46 +77,47 @@ async fn main() {
|
|||
// User
|
||||
.route("/profile", get(profile))
|
||||
.route("/useradmin", get(useradmin))
|
||||
.route("/users/:user_id", get(user_profile))
|
||||
.route("/roles/:user_id/:role_id/add", get(add_user_role))
|
||||
.route("/user/{user_id}", get(user_profile))
|
||||
.route("/user/{user_id}/{account_id}", get(user_profile_account))
|
||||
.route("/roles/{user_id}/{role_id}/add", get(add_user_role))
|
||||
.route(
|
||||
"/roles/:user_id/:user_role_id/delete",
|
||||
"/roles/{user_id}/{user_role_id}/delete",
|
||||
get(delete_user_role),
|
||||
)
|
||||
// Calendar
|
||||
.route("/calendar", get(calendar))
|
||||
.route("/getevents/:calendar", get(get_events))
|
||||
.route("/getevents/{calendar}", get(get_events))
|
||||
// Wishlist
|
||||
.route("/wishlists", get(wishlists))
|
||||
.route("/userwishlist/:user_id", get(user_wishlist))
|
||||
.route("/userwishlist/{user_id}", get(user_wishlist))
|
||||
.route(
|
||||
"/userwishlist/add/:user_id",
|
||||
"/userwishlist/add/{user_id}",
|
||||
get(user_wishlist_add).post(user_wishlist_add_item),
|
||||
)
|
||||
.route(
|
||||
"/userwishlist/edit/:item_id",
|
||||
"/userwishlist/edit/{item_id}",
|
||||
get(user_wishlist_edit_item).post(user_wishlist_save_item),
|
||||
)
|
||||
.route(
|
||||
"/userwishlist/bought/:user_id",
|
||||
"/userwishlist/bought/{user_id}",
|
||||
get(user_wishlist_bought_item),
|
||||
)
|
||||
.route(
|
||||
"/userwishlist/received/:user_id",
|
||||
"/userwishlist/received/{user_id}",
|
||||
get(user_wishlist_received_item),
|
||||
)
|
||||
.route(
|
||||
"/userwishlist/delete/:item_id",
|
||||
"/userwishlist/delete/{item_id}",
|
||||
get(user_wishlist_delete_item),
|
||||
)
|
||||
.route(
|
||||
"/userwishlist/returned/:item_id",
|
||||
"/userwishlist/returned/{item_id}",
|
||||
get(user_wishlist_returned_item),
|
||||
)
|
||||
// Secret Gift Exchange - Not ready for public use yet
|
||||
.route("/giftexchanges", get(giftexchanges))
|
||||
.route(
|
||||
"/giftexchange/:giftexchange_id",
|
||||
"/giftexchange/{giftexchange_id}",
|
||||
get(giftexchange).post(giftexchange_save),
|
||||
)
|
||||
.nest_service(
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
use askama_axum::{Response, Template};
|
||||
use askama_axum::Template;
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{Html, IntoResponse, Redirect},
|
||||
Extension,
|
||||
};
|
||||
use http::StatusCode;
|
||||
use reqwest::redirect;
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
middlewares::is_authorized,
|
||||
user::{get_other_roles_display, get_user_roles_display},
|
||||
user::{get_other_roles_display, get_user_roles_display, AccountData},
|
||||
UserData,
|
||||
};
|
||||
|
||||
|
|
@ -28,7 +30,8 @@ struct UserProfileTemplate {
|
|||
user: UserData,
|
||||
user_roles: Vec<crate::user::UserRolesDisplay>,
|
||||
profile: UserData,
|
||||
profile_accounts: Vec<UserData>,
|
||||
profile_accounts: Vec<AccountData>,
|
||||
account: AccountData,
|
||||
profile_roles: Vec<crate::user::UserRolesDisplay>,
|
||||
non_profile_roles: Vec<crate::user::UserRolesDisplay>,
|
||||
}
|
||||
|
|
@ -39,7 +42,7 @@ impl<T> IntoResponse for HtmlTemplate<T>
|
|||
where
|
||||
T: Template,
|
||||
{
|
||||
fn into_response(self) -> Response {
|
||||
fn into_response(self) -> http::Response<axum::body::Body> {
|
||||
match self.0.render() {
|
||||
Ok(html) => Html(html).into_response(),
|
||||
Err(err) => (
|
||||
|
|
@ -172,7 +175,45 @@ pub async fn user_profile(
|
|||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
|
||||
// Extract the user data.
|
||||
let profile = sqlx::query_as( "SELECT * FROM users WHERE id = $1")
|
||||
let profile: UserData = sqlx::query_as( "SELECT * FROM people WHERE id = $1")
|
||||
.bind(user_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if is_authorized("/users", user_data, db_pool.clone()).await {
|
||||
// Get first user account
|
||||
let profile_account: Uuid = sqlx::query_scalar( "SELECT id FROM users WHERE person_id = $1 LIMIT 1")
|
||||
.bind(user_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let redirect_url = format!("/user/{user_id}/{}", profile_account);
|
||||
Redirect::to(&redirect_url).into_response()
|
||||
} else {
|
||||
Redirect::to("/").into_response()
|
||||
}
|
||||
} else {
|
||||
Redirect::to("/").into_response()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn user_profile_account(
|
||||
Path((user_id, account_id)): Path<(uuid::Uuid, uuid::Uuid)>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
let logged_in = user_data.is_some();
|
||||
|
||||
if logged_in {
|
||||
// Extract the user data.
|
||||
let user = user_data.as_ref().unwrap().clone();
|
||||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
|
||||
// Extract the user data.
|
||||
let profile = sqlx::query_as( "SELECT * FROM people WHERE id = $1")
|
||||
.bind(user_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
|
|
@ -189,21 +230,29 @@ pub async fn user_profile(
|
|||
created_by,
|
||||
updated_at,
|
||||
updated_by,
|
||||
person_id,
|
||||
email,
|
||||
name,
|
||||
family_name,
|
||||
given_name
|
||||
FROM users WHERE person_id = $1"#)
|
||||
FROM users WHERE person_id = $1 order by name"#)
|
||||
.bind(user_id)
|
||||
.fetch_all(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Get user account
|
||||
let account = sqlx::query_as( "SELECT * FROM users WHERE id = $1")
|
||||
.bind(account_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Get user roles
|
||||
let profile_roles = get_user_roles_display(user_id, &db_pool.clone()).await;
|
||||
let profile_roles = get_user_roles_display(account_id, &db_pool.clone()).await;
|
||||
|
||||
// Get roles user does not have
|
||||
let non_profile_roles = get_other_roles_display(user_id, &db_pool.clone()).await;
|
||||
let non_profile_roles = get_other_roles_display(account_id, &db_pool.clone()).await;
|
||||
|
||||
// Create the profile template.
|
||||
let template = UserProfileTemplate {
|
||||
|
|
@ -212,6 +261,7 @@ pub async fn user_profile(
|
|||
user_roles,
|
||||
profile,
|
||||
profile_accounts,
|
||||
account,
|
||||
profile_roles,
|
||||
non_profile_roles,
|
||||
};
|
||||
|
|
@ -245,7 +295,7 @@ pub async fn useradmin(
|
|||
let user = user_data.as_ref().unwrap().clone();
|
||||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
|
||||
let users = sqlx::query_as::<_, UserData>("SELECT * FROM users")
|
||||
let users = sqlx::query_as::<_, UserData>("SELECT * FROM people order by name")
|
||||
.fetch_all(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
use askama::Template;
|
||||
use askama_axum::{IntoResponse, Response};
|
||||
use axum::{
|
||||
body::{self, Body},
|
||||
extract::{FromRequest, Path, Request, State},
|
||||
response::Redirect,
|
||||
response::{Html, IntoResponse, Redirect, Response},
|
||||
Extension, Form, Json, RequestExt,
|
||||
};
|
||||
use axum_extra::response::Html;
|
||||
use http::{header::CONTENT_TYPE, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, PgPool};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use askama_axum::IntoResponse;
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::Redirect,
|
||||
response::{IntoResponse, Redirect},
|
||||
Extension,
|
||||
};
|
||||
|
||||
///User related structs and functions
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::middlewares::is_authorized;
|
||||
|
||||
|
|
@ -24,6 +24,20 @@ pub struct UserData {
|
|||
pub given_name: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, FromRow)]
|
||||
pub struct AccountData {
|
||||
pub id: uuid::Uuid,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub created_by: uuid::Uuid,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
pub updated_by: uuid::Uuid,
|
||||
pub person_id: uuid::Uuid,
|
||||
pub email: String,
|
||||
pub name: String,
|
||||
pub family_name: String,
|
||||
pub given_name: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RoleData {
|
||||
pub id: uuid::Uuid,
|
||||
|
|
@ -87,10 +101,13 @@ pub async fn get_user_roles(user_id: i64, db_pool: &PgPool) -> Vec<UserRoles> {
|
|||
user_roles
|
||||
} */
|
||||
|
||||
pub async fn get_user_roles_display(user_id: uuid::Uuid, db_pool: &PgPool) -> Vec<UserRolesDisplay> {
|
||||
pub async fn get_user_roles_display(
|
||||
user_id: uuid::Uuid,
|
||||
db_pool: &PgPool,
|
||||
) -> Vec<UserRolesDisplay> {
|
||||
// Get user roles
|
||||
let user_roles = sqlx::query_as(
|
||||
r#"select ur.id, u.id as user_id, u.name as user_name, r.id as role_id, r.name as role_name, r.created_at, r.created_by, r.updated_at, r.updated_by from roles r join user_roles ur on ur.role_id = r.id join users u on u.id = ur.user_id WHERE ur.user_id = $1"#
|
||||
r#"select ur.id, u.id as user_id, u.name as user_name, r.id as role_id, r.name as role_name, r.created_at, r.created_by, r.updated_at, r.updated_by from roles r join user_roles ur on ur.role_id = r.id join users u on u.id = ur.user_id WHERE ur.user_id = $1 order by r.name"#
|
||||
)
|
||||
.bind(user_id)
|
||||
.fetch_all(db_pool)
|
||||
|
|
@ -100,10 +117,13 @@ pub async fn get_user_roles_display(user_id: uuid::Uuid, db_pool: &PgPool) -> Ve
|
|||
user_roles
|
||||
}
|
||||
|
||||
pub async fn get_other_roles_display(user_id: uuid::Uuid, db_pool: &PgPool) -> Vec<UserRolesDisplay> {
|
||||
pub async fn get_other_roles_display(
|
||||
user_id: uuid::Uuid,
|
||||
db_pool: &PgPool,
|
||||
) -> Vec<UserRolesDisplay> {
|
||||
// Get roles user does not have
|
||||
let user_roles = sqlx::query_as(
|
||||
r#"select r.id as id, r.created_at, r.created_by, r.updated_at, r.updated_by, $1 as user_id, '' as user_name, r.id as role_id, r.name as role_name from roles r where r.id not in (select ur.role_id from user_roles ur where ur.user_id = $2)"#
|
||||
r#"select r.id as id, r.created_at, r.created_by, r.updated_at, r.updated_by, $1 as user_id, '' as user_name, r.id as role_id, r.name as role_name from roles r where r.id not in (select ur.role_id from user_roles ur where ur.user_id = $2) order by r.name"#
|
||||
)
|
||||
.bind(user_id.clone())
|
||||
.bind(user_id)
|
||||
|
|
@ -115,7 +135,7 @@ pub async fn get_other_roles_display(user_id: uuid::Uuid, db_pool: &PgPool) -> V
|
|||
}
|
||||
|
||||
pub async fn add_user_role(
|
||||
Path((user_id, role_id)): Path<(uuid::Uuid, uuid::Uuid)>,
|
||||
Path((account_id, role_id)): Path<(uuid::Uuid, uuid::Uuid)>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
|
|
@ -123,15 +143,20 @@ pub async fn add_user_role(
|
|||
sqlx::query("INSERT INTO user_roles (created_by, updated_by, user_id, role_id) VALUES ($1, $2, $3, $4)")
|
||||
.bind(user_data.as_ref().unwrap().id)// Created by current user
|
||||
.bind(user_data.as_ref().unwrap().id) // Updated by current user
|
||||
.bind(user_id)
|
||||
.bind(account_id)
|
||||
.bind(role_id)
|
||||
.execute(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// TODO - send email to user regarding role changes
|
||||
let profile_id: Uuid = sqlx::query_scalar("SELECT person_id FROM users WHERE id = $1")
|
||||
.bind(account_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let redirect_url = format!("/users/{user_id}");
|
||||
let redirect_url = format!("/user/{profile_id}/{account_id}");
|
||||
Redirect::to(&redirect_url).into_response()
|
||||
} else {
|
||||
Redirect::to("/").into_response()
|
||||
|
|
@ -139,7 +164,7 @@ pub async fn add_user_role(
|
|||
}
|
||||
|
||||
pub async fn delete_user_role(
|
||||
Path((user_id, user_role_id)): Path<(uuid::Uuid, uuid::Uuid)>,
|
||||
Path((account_id, user_role_id)): Path<(uuid::Uuid, uuid::Uuid)>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
|
|
@ -150,14 +175,23 @@ pub async fn delete_user_role(
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let redirect_url = format!("/users/{user_id}");
|
||||
let profile_id: Uuid = sqlx::query_scalar("SELECT person_id FROM users WHERE id = $1")
|
||||
.bind(account_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let redirect_url = format!("/user/{profile_id}/{account_id}");
|
||||
Redirect::to(&redirect_url).into_response()
|
||||
} else {
|
||||
Redirect::to("/").into_response()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_user_wishlist_item_by_id(item_id: uuid::Uuid, db_pool: &PgPool) -> UserWishlistItem {
|
||||
pub async fn get_user_wishlist_item_by_id(
|
||||
item_id: uuid::Uuid,
|
||||
db_pool: &PgPool,
|
||||
) -> UserWishlistItem {
|
||||
// Get wish list items for the user
|
||||
let user_wishlist_item = sqlx::query_as(
|
||||
r#"select id, created_at, created_by, updated_at, updated_by, user_id, item, item_url, purchased_by, received_at
|
||||
|
|
@ -171,7 +205,10 @@ pub async fn get_user_wishlist_item_by_id(item_id: uuid::Uuid, db_pool: &PgPool)
|
|||
user_wishlist_item
|
||||
}
|
||||
|
||||
pub async fn get_user_wishlist_items(user_id: uuid::Uuid, db_pool: &PgPool) -> Vec<UserWishlistItem> {
|
||||
pub async fn get_user_wishlist_items(
|
||||
user_id: uuid::Uuid,
|
||||
db_pool: &PgPool,
|
||||
) -> Vec<UserWishlistItem> {
|
||||
// Get wish list items for the user
|
||||
let user_wishlist_items = sqlx::query_as(
|
||||
r#"select id, created_at, created_by, updated_at, updated_by, user_id, item, item_url, purchased_by, received_at
|
||||
|
|
@ -193,4 +230,24 @@ pub async fn get_useremails_by_role(role_name: String, db_pool: &PgPool) -> Stri
|
|||
.unwrap();
|
||||
|
||||
useremails
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn user_account(
|
||||
Path(account_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
let logged_in = user_data.is_some();
|
||||
|
||||
if logged_in {
|
||||
// Extract the user data.
|
||||
if is_authorized("/users", user_data.clone(), db_pool.clone()).await {
|
||||
Redirect::to("/").into_response()
|
||||
} else {
|
||||
Redirect::to("/").into_response()
|
||||
}
|
||||
} else {
|
||||
Redirect::to("/").into_response()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
use askama_axum::{IntoResponse, Response, Template};
|
||||
use askama_axum::Template;
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::Redirect,
|
||||
response::{Html, IntoResponse, Redirect},
|
||||
Extension, Form,
|
||||
};
|
||||
use axum_extra::response::Html;
|
||||
use chrono::Utc;
|
||||
use http::StatusCode;
|
||||
use serde::Deserialize;
|
||||
|
|
@ -25,7 +24,7 @@ impl<T> IntoResponse for HtmlTemplate<T>
|
|||
where
|
||||
T: Template,
|
||||
{
|
||||
fn into_response(self) -> Response {
|
||||
fn into_response(self) -> http::Response<axum::body::Body> {
|
||||
match self.0.render() {
|
||||
Ok(html) => Html(html).into_response(),
|
||||
Err(err) => (
|
||||
|
|
@ -58,7 +57,7 @@ pub async fn wishlists(
|
|||
let user = user_data.as_ref().unwrap().clone();
|
||||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
|
||||
let users = sqlx::query_as::<_, UserData>("SELECT * FROM users")
|
||||
let users = sqlx::query_as::<_, UserData>("SELECT * FROM people order by name")
|
||||
.fetch_all(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -107,7 +106,7 @@ pub async fn user_wishlist(
|
|||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
|
||||
// Extract the user data.
|
||||
let person = sqlx::query_as("SELECT * FROM users WHERE id = $1")
|
||||
let person = sqlx::query_as("SELECT * FROM people WHERE id = $1")
|
||||
.bind(user_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
Full name: {{ profile.name }}<br/>
|
||||
Given name: {{ profile.given_name }}<br/>
|
||||
Family name: {{ profile.family_name }}<br/>
|
||||
Your email address: {{ profile.email }}<br/>
|
||||
Logged in email address: {{ profile.email }}<br/>
|
||||
<br/>
|
||||
<h2>Accounts</h2>
|
||||
<button type="button" class="btn btn-primary">Merge</button>
|
||||
|
|
@ -13,19 +13,21 @@ Your email address: {{ profile.email }}<br/>
|
|||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Email</th>
|
||||
<th scope="col">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for account in profile_accounts %}
|
||||
{% for user_account in profile_accounts %}
|
||||
<tr>
|
||||
<td><a href="/accounts/{{ account.id }}">{{ account.name }}</a></td>
|
||||
<td><a href="/accounts/{{ account.id }}/delete">Delete</a></td>
|
||||
<td><a href="/user/{{profile.id}}/{{ user_account.id }}">{{ account.name }}</a></td>
|
||||
<td>{{ user_account.email }}</td>
|
||||
<td><a href="/account/{{ user_account.id }}/delete">Delete</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>User Roles</h2>
|
||||
<h2>Account Roles ({{ account.email }})</h2>
|
||||
<button type="button" class="btn btn-primary">Edit</button>
|
||||
<button type="button" class="btn btn-primary">Add</button>
|
||||
<table class="table table-striped table-bordered">
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td><a href="/users/{{ user.id }}">{{ user.name }}</a></td>
|
||||
<td><a href="/user/{{ user.id }}">{{ user.name }}</a></td>
|
||||
<td>{{ user.email }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
|||
Loading…
Reference in New Issue