refactor: break up web/contact
This commit is contained in:
parent
2e1fbd00be
commit
57177612ec
8 changed files with 166 additions and 121 deletions
131
src/web/contact/fields.rs
Normal file
131
src/web/contact/fields.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
use maud::{Markup, html};
|
||||
use serde_json::json;
|
||||
use sqlx::sqlite::SqlitePool;
|
||||
|
||||
use crate::AppError;
|
||||
use crate::db::DbId;
|
||||
|
||||
pub mod addresses {
|
||||
use super::*;
|
||||
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub struct Address {
|
||||
pub id: DbId,
|
||||
pub contact_id: DbId,
|
||||
pub label: Option<String>,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
async fn all(pool: &SqlitePool, contact_id: DbId) -> Result<Vec<Address>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
Address,
|
||||
"select * from addresses where contact_id = $1",
|
||||
contact_id
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get(pool: &SqlitePool, contact_id: DbId) -> Result<Markup, AppError> {
|
||||
let addresses: Vec<Address> = addresses::all(pool, contact_id).await?;
|
||||
|
||||
Ok(html! {
|
||||
@if addresses.len() == 1 {
|
||||
label { "address" }
|
||||
#addresses {
|
||||
.label {}
|
||||
.value { (addresses[0].value) }
|
||||
}
|
||||
} @else if addresses.len() > 0 {
|
||||
label { "addresses" }
|
||||
#addresses {
|
||||
@for address in addresses {
|
||||
@let lbl = address.label.unwrap_or(String::new());
|
||||
.label data-is-empty=(lbl.len() == 0) {
|
||||
(lbl)
|
||||
}
|
||||
.value { (address.value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn edit(pool: &SqlitePool, contact_id: DbId) -> Result<Markup, AppError> {
|
||||
let addresses: Vec<Address> = addresses::all(pool, contact_id).await?;
|
||||
|
||||
Ok(html! {
|
||||
label { "addresses" }
|
||||
div x-data=(json!({ "addresses": addresses, "new_label": "", "new_address": "" })) {
|
||||
template x-for="(address, index) in addresses" x-bind:key="index" {
|
||||
.address-input {
|
||||
input name="address_label" x-show="addresses.length" x-model="address.label" placeholder="label";
|
||||
.grow-wrap x-bind:data-replicated-value="address.value" {
|
||||
textarea name="address_value" x-model="address.value" placeholder="address" {}
|
||||
}
|
||||
}
|
||||
}
|
||||
.address-input {
|
||||
input x-show="addresses.length" name="address_label" x-model="new_label" placeholder="label";
|
||||
.grow-wrap x-bind:data-replicated-value="new_address" {
|
||||
textarea name="address_value" x-model="new_address" placeholder="new address" {}
|
||||
}
|
||||
}
|
||||
input type="button" value="Add" x-on:click="addresses.push({ label: new_label, value: new_address }); new_label = ''; new_address = ''";
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub mod groups {
|
||||
use super::*;
|
||||
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub struct Group {
|
||||
pub contact_id: DbId,
|
||||
pub name: String,
|
||||
pub slug: String,
|
||||
}
|
||||
|
||||
async fn all(pool: &SqlitePool, contact_id: DbId) -> Result<Vec<Group>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
Group,
|
||||
"select * from groups where contact_id = $1",
|
||||
contact_id
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get(pool: &SqlitePool, contact_id: DbId) -> Result<Markup, AppError> {
|
||||
let groups: Vec<Group> = groups::all(pool, contact_id).await?;
|
||||
|
||||
Ok(html! {
|
||||
@if groups.len() > 0 {
|
||||
label { "in groups" }
|
||||
#groups {
|
||||
@for group in groups {
|
||||
a .group href=(format!("/group/{}", group.slug)) {
|
||||
(group.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn edit(pool: &SqlitePool, contact_id: DbId) -> Result<Markup, AppError> {
|
||||
let groups: Vec<Group> = groups::all(pool, contact_id).await?;
|
||||
|
||||
Ok(html! {
|
||||
label { "groups" }
|
||||
#groups x-data=(json!({ "groups": groups, "new_group": "" })) {
|
||||
template x-for="(group, index) in groups" x-bind:key="index" {
|
||||
input name="group" x-model="group.name" placeholder="group name";
|
||||
}
|
||||
input name="group" x-model="new_group" placeholder="group name";
|
||||
input type="button" value="Add" x-on:click="groups.push({ name: new_group }); new_group = ''";
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -22,20 +22,7 @@ use crate::models::{HydratedContact, JournalEntry};
|
|||
use crate::switchboard::{MentionHost, MentionHostType, insert_mentions};
|
||||
use crate::{AppError, AppState};
|
||||
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub struct Address {
|
||||
pub id: DbId,
|
||||
pub contact_id: DbId,
|
||||
pub label: Option<String>,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub struct Group {
|
||||
pub contact_id: DbId,
|
||||
pub name: String,
|
||||
pub slug: String,
|
||||
}
|
||||
pub mod fields;
|
||||
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub struct PhoneNumber {
|
||||
|
|
@ -104,7 +91,9 @@ mod get {
|
|||
PhoneNumber,
|
||||
"select * from phone_numbers where contact_id = $1",
|
||||
contact_id
|
||||
).fetch_all(pool).await?;
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
let lives_with = if contact.lives_with.len() > 1 {
|
||||
let mention_host = MentionHost {
|
||||
|
|
@ -117,22 +106,6 @@ mod get {
|
|||
None
|
||||
};
|
||||
|
||||
let addresses: Vec<Address> = sqlx::query_as!(
|
||||
Address,
|
||||
"select * from addresses where contact_id = $1",
|
||||
contact_id
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
let groups: Vec<Group> = sqlx::query_as!(
|
||||
Group,
|
||||
"select * from groups where contact_id = $1",
|
||||
contact_id
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
let text_body: Option<String> =
|
||||
sqlx::query!("select text_body from contacts where id = $1", contact_id)
|
||||
.fetch_one(pool)
|
||||
|
|
@ -193,35 +166,8 @@ mod get {
|
|||
div { (lives_with) }
|
||||
}
|
||||
|
||||
@if addresses.len() == 1 {
|
||||
label { "address" }
|
||||
#addresses {
|
||||
.label {}
|
||||
.value { (addresses[0].value) }
|
||||
}
|
||||
} @else if addresses.len() > 0 {
|
||||
label { "addresses" }
|
||||
#addresses {
|
||||
@for address in addresses {
|
||||
@let lbl = address.label.unwrap_or(String::new());
|
||||
.label data-is-empty=(lbl.len() == 0) {
|
||||
(lbl)
|
||||
}
|
||||
.value { (address.value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@if groups.len() > 0 {
|
||||
label { "in groups" }
|
||||
#groups {
|
||||
@for group in groups {
|
||||
a .group href=(format!("/group/{}", group.slug)) {
|
||||
(group.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(fields::addresses::get(pool, contact_id).await?)
|
||||
(fields::groups::get(pool, contact_id).await?)
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -253,12 +199,6 @@ mod get {
|
|||
PhoneNumber,
|
||||
"select * from phone_numbers where contact_id = $1",
|
||||
contact_id
|
||||
).fetch_all(pool).await?;
|
||||
|
||||
let addresses: Vec<Address> = sqlx::query_as!(
|
||||
Address,
|
||||
"select * from addresses where contact_id = $1",
|
||||
contact_id
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
|
@ -269,17 +209,6 @@ mod get {
|
|||
.clone()
|
||||
.map_or("".to_string(), |m| m.to_rfc3339());
|
||||
|
||||
let groups: Vec<String> = sqlx::query_as!(
|
||||
Group,
|
||||
"select * from groups where contact_id = $1",
|
||||
contact_id
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|group| group.name)
|
||||
.collect();
|
||||
|
||||
let text_body: String =
|
||||
sqlx::query!("select text_body from contacts where id = $1", contact_id)
|
||||
.fetch_one(pool)
|
||||
|
|
@ -340,32 +269,8 @@ mod get {
|
|||
div {
|
||||
input name="lives_with" value=(contact.lives_with);
|
||||
}
|
||||
label { "addresses" }
|
||||
div x-data=(json!({ "addresses": addresses, "new_label": "", "new_address": "" })) {
|
||||
template x-for="(address, index) in addresses" x-bind:key="index" {
|
||||
.address-input {
|
||||
input name="address_label" x-show="addresses.length" x-model="address.label" placeholder="label";
|
||||
.grow-wrap x-bind:data-replicated-value="address.value" {
|
||||
textarea name="address_value" x-model="address.value" placeholder="address" {}
|
||||
}
|
||||
}
|
||||
}
|
||||
.address-input {
|
||||
input x-show="addresses.length" name="address_label" x-model="new_label" placeholder="label";
|
||||
.grow-wrap x-bind:data-replicated-value="new_address" {
|
||||
textarea name="address_value" x-model="new_address" placeholder="new address" {}
|
||||
}
|
||||
}
|
||||
input type="button" value="Add" x-on:click="addresses.push({ label: new_label, value: new_address }); new_label = ''; new_address = ''";
|
||||
}
|
||||
label { "groups" }
|
||||
#groups x-data=(json!({ "groups": groups, "new_group": "" })) {
|
||||
template x-for="(group, index) in groups" x-bind:key="index" {
|
||||
input name="group" x-model="group" placeholder="group name";
|
||||
}
|
||||
input name="group" x-model="new_group" placeholder="group name";
|
||||
input type="button" value="Add" x-on:click="groups.push(new_group); new_group = ''";
|
||||
}
|
||||
(fields::addresses::edit(pool, contact_id).await?)
|
||||
(fields::groups::edit(pool, contact_id).await?)
|
||||
}
|
||||
div #text_body {
|
||||
div { "Free text (supports markdown)" }
|
||||
|
|
@ -390,7 +295,7 @@ mod post {
|
|||
let user = auth_session.user.unwrap();
|
||||
let pool = &state.db(&user).pool;
|
||||
|
||||
let contact_id: (u32,) =
|
||||
let contact_id: (DbId,) =
|
||||
sqlx::query_as("insert into contacts (birthday) values (null) returning id")
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
|
@ -534,21 +439,27 @@ mod put {
|
|||
.collect::<Vec<(String, String)>>()
|
||||
});
|
||||
|
||||
let old_numbers: Vec<(String, String)> =
|
||||
sqlx::query_as("select label, phone_number from phone_numbers where contact_id = $1")
|
||||
.bind(contact_id)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
let old_numbers: Vec<(String, String)> = sqlx::query_as(
|
||||
"select label, phone_number from phone_numbers where contact_id = $1",
|
||||
)
|
||||
.bind(contact_id)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
if new_numbers != old_numbers {
|
||||
sqlx::query!("delete from phone_numbers where contact_id = $1", contact_id)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"delete from phone_numbers where contact_id = $1",
|
||||
contact_id
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
// trailing space in query intentional
|
||||
QueryBuilder::new("insert into phone_numbers (contact_id, label, phone_number) ")
|
||||
.push_values(new_numbers, |mut b, (label, phone_number)| {
|
||||
b.push_bind(contact_id).push_bind(label).push_bind(phone_number);
|
||||
b.push_bind(contact_id)
|
||||
.push_bind(label)
|
||||
.push_bind(phone_number);
|
||||
})
|
||||
.build()
|
||||
.execute(pool)
|
||||
|
|
@ -743,7 +654,7 @@ mod delete {
|
|||
pub async fn contact(
|
||||
auth_session: AuthSession,
|
||||
State(state): State<AppState>,
|
||||
Path(contact_id): Path<u32>,
|
||||
Path(contact_id): Path<DbId>,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
let user = auth_session.user.unwrap();
|
||||
let pool = &state.db(&user).pool;
|
||||
Loading…
Add table
Add a link
Reference in a new issue