107 lines
3.6 KiB
Rust
107 lines
3.6 KiB
Rust
use axum::RequestPartsExt;
|
|
use axum::extract::FromRequestParts;
|
|
use cache_bust::asset;
|
|
use http::request::Parts;
|
|
use maud::{DOCTYPE, Markup, html};
|
|
|
|
use super::models::user::{AuthSession, User};
|
|
use super::{AppError, AppState};
|
|
use crate::db::DbId;
|
|
|
|
pub mod auth;
|
|
pub mod contact;
|
|
pub mod group;
|
|
pub mod home;
|
|
pub mod ics;
|
|
pub mod journal;
|
|
pub mod settings;
|
|
|
|
#[derive(Debug)]
|
|
struct ContactLink {
|
|
name: String,
|
|
contact_id: DbId,
|
|
}
|
|
pub struct Layout {
|
|
contact_links: Vec<ContactLink>,
|
|
user: User,
|
|
}
|
|
|
|
impl FromRequestParts<AppState> for Layout {
|
|
type Rejection = AppError;
|
|
|
|
async fn from_request_parts(
|
|
parts: &mut Parts,
|
|
state: &AppState,
|
|
) -> Result<Self, Self::Rejection> {
|
|
let auth_session = parts
|
|
.extract::<AuthSession>()
|
|
.await
|
|
.map_err(|_| anyhow::Error::msg("could not get session"))?;
|
|
let user = auth_session.user.unwrap();
|
|
|
|
let contact_links = sqlx::query_as!(
|
|
ContactLink,
|
|
"select c.id as contact_id,
|
|
coalesce(n.name, '(unnamed)') as name
|
|
from contacts c
|
|
left join names n on c.id = n.contact_id
|
|
where n.sort is null or n.sort = 0
|
|
order by name asc",
|
|
)
|
|
.fetch_all(&state.db(&user).pool)
|
|
.await?;
|
|
|
|
Ok(Layout {
|
|
contact_links,
|
|
user,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Layout {
|
|
pub fn render(&self, css: Option<Vec<&str>>, content: Markup) -> Markup {
|
|
html! {
|
|
(DOCTYPE)
|
|
html {
|
|
head {
|
|
link rel="stylesheet" type="text/css" href=(format!("/static/{}", asset!("index.css")));
|
|
meta name="viewport" content="width=device-width";
|
|
script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js" {}
|
|
script src="https://cdn.jsdelivr.net/npm/htmx-ext-response-targets@2.0.4" integrity="sha384-T41oglUPvXLGBVyRdZsVRxNWnOOqCynaPubjUVjxhsjFTKrFJGEMm3/0KGmNQ+Pg" crossorigin="anonymous" {}
|
|
script src="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/dist/cdn.min.js" defer {}
|
|
@if let Some(hrefs) = css {
|
|
@for href in hrefs {
|
|
link rel="stylesheet" type="text/css" href=(format!("/static/{}", href));
|
|
}
|
|
}
|
|
}
|
|
body x-data="{ sidebar: false }" {
|
|
header {
|
|
input #sidebar-show-hide type="button" x-on:click="sidebar = !sidebar" value="☰";
|
|
h1 { a href="/" { "Mascarpone" } }
|
|
span { (self.user.username) }
|
|
a href="/settings" { "Settings" }
|
|
a href="/logout" { "Logout" }
|
|
}
|
|
section #content {
|
|
nav #contacts-sidebar x-bind:class="sidebar ? 'show' : 'hide'" {
|
|
ul {
|
|
li { button hx-post="/contact/new" { "+ Add Contact" } }
|
|
@for link in &self.contact_links {
|
|
li {
|
|
a href=(format!("/contact/{}", link.contact_id)) {
|
|
(link.name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
main {
|
|
(content)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|