This commit is contained in:
parent
84c41dda4d
commit
2e1fbd00be
3 changed files with 90 additions and 2 deletions
5
migrations/each_user/0011_phone_numbers.sql
Normal file
5
migrations/each_user/0011_phone_numbers.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
create table if not exists phone_numbers (
|
||||||
|
contact_id integer not null references contacts(id) on delete cascade,
|
||||||
|
label text,
|
||||||
|
phone_number text not null
|
||||||
|
);
|
||||||
|
|
@ -97,7 +97,7 @@ impl HydratedContact {
|
||||||
let raw = sqlx::query_as!(
|
let raw = sqlx::query_as!(
|
||||||
RawHydratedContact,
|
RawHydratedContact,
|
||||||
r#"select id, birthday, lives_with, manually_freshened_at as "manually_freshened_at: String", (
|
r#"select id, birthday, lives_with, manually_freshened_at as "manually_freshened_at: String", (
|
||||||
select string_agg(name,'\x1c' order by sort)
|
select string_agg(name,x'1c' order by sort)
|
||||||
from names where contact_id = c.id
|
from names where contact_id = c.id
|
||||||
) as names, (
|
) as names, (
|
||||||
select jes.date from journal_entries jes
|
select jes.date from journal_entries jes
|
||||||
|
|
@ -124,7 +124,7 @@ impl HydratedContact {
|
||||||
let contacts = sqlx::query_as!(
|
let contacts = sqlx::query_as!(
|
||||||
RawHydratedContact,
|
RawHydratedContact,
|
||||||
r#"select id, birthday, lives_with, manually_freshened_at as "manually_freshened_at: String", (
|
r#"select id, birthday, lives_with, manually_freshened_at as "manually_freshened_at: String", (
|
||||||
select string_agg(name,'\x1c' order by sort)
|
select string_agg(name,x'1c' order by sort)
|
||||||
from names where contact_id = c.id
|
from names where contact_id = c.id
|
||||||
) as names, (
|
) as names, (
|
||||||
select jes.date from journal_entries jes
|
select jes.date from journal_entries jes
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,13 @@ pub struct Group {
|
||||||
pub slug: String,
|
pub slug: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, Debug)]
|
||||||
|
pub struct PhoneNumber {
|
||||||
|
pub contact_id: DbId,
|
||||||
|
pub label: Option<String>,
|
||||||
|
pub phone_number: String,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn router() -> Router<AppState> {
|
pub fn router() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/contact/new", post(self::post::contact))
|
.route("/contact/new", post(self::post::contact))
|
||||||
|
|
@ -93,6 +100,12 @@ mod get {
|
||||||
.fetch_all(pool)
|
.fetch_all(pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let phone_numbers: Vec<PhoneNumber> = sqlx::query_as!(
|
||||||
|
PhoneNumber,
|
||||||
|
"select * from phone_numbers where contact_id = $1",
|
||||||
|
contact_id
|
||||||
|
).fetch_all(pool).await?;
|
||||||
|
|
||||||
let lives_with = if contact.lives_with.len() > 1 {
|
let lives_with = if contact.lives_with.len() > 1 {
|
||||||
let mention_host = MentionHost {
|
let mention_host = MentionHost {
|
||||||
entity_id: contact_id,
|
entity_id: contact_id,
|
||||||
|
|
@ -162,6 +175,19 @@ mod get {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if phone_numbers.len() > 0 {
|
||||||
|
label { "phone" }
|
||||||
|
#phone_numbers {
|
||||||
|
@for phone_number in phone_numbers {
|
||||||
|
@let lbl = phone_number.label.unwrap_or(String::new());
|
||||||
|
.label data-is-empty=(lbl.len() == 0) { (lbl) }
|
||||||
|
.phone_nunber {
|
||||||
|
a href=(format!("tel:{}", phone_number.phone_number)) { (phone_number.phone_number) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@if let Some(lives_with) = lives_with {
|
@if let Some(lives_with) = lives_with {
|
||||||
label { "lives with" }
|
label { "lives with" }
|
||||||
div { (lives_with) }
|
div { (lives_with) }
|
||||||
|
|
@ -223,6 +249,12 @@ mod get {
|
||||||
let pool = &state.db(&auth_session.user.unwrap()).pool;
|
let pool = &state.db(&auth_session.user.unwrap()).pool;
|
||||||
let contact = HydratedContact::load(contact_id, pool).await?;
|
let contact = HydratedContact::load(contact_id, pool).await?;
|
||||||
|
|
||||||
|
let phone_numbers: Vec<PhoneNumber> = sqlx::query_as!(
|
||||||
|
PhoneNumber,
|
||||||
|
"select * from phone_numbers where contact_id = $1",
|
||||||
|
contact_id
|
||||||
|
).fetch_all(pool).await?;
|
||||||
|
|
||||||
let addresses: Vec<Address> = sqlx::query_as!(
|
let addresses: Vec<Address> = sqlx::query_as!(
|
||||||
Address,
|
Address,
|
||||||
"select * from addresses where contact_id = $1",
|
"select * from addresses where contact_id = $1",
|
||||||
|
|
@ -290,6 +322,20 @@ mod get {
|
||||||
span x-text="date.length ? date.split('T')[0] : '(never)'" {}
|
span x-text="date.length ? date.split('T')[0] : '(never)'" {}
|
||||||
input type="button" value="Mark fresh now" x-on:click="date = new Date().toISOString()";
|
input type="button" value="Mark fresh now" x-on:click="date = new Date().toISOString()";
|
||||||
}
|
}
|
||||||
|
label { "phone" }
|
||||||
|
#phone_numbers x-data=(json!({ "phones": phone_numbers, "new_label": "", "new_number": "" })) {
|
||||||
|
template x-for="(phone, index) in phones" x-bind:key="index" {
|
||||||
|
.phone_input {
|
||||||
|
input name="phone_label" x-model="phone.label" placeholder="home/work/mobile";
|
||||||
|
input name="phone_number" x-model="phone.phone_number" placeholder="number";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.phone_input {
|
||||||
|
input name="phone_label" x-model="new_label" placeholder="home/work/mobile";
|
||||||
|
input name="phone_number" x-model="new_number" placeholder="number";
|
||||||
|
}
|
||||||
|
input type="button" value="Add" x-on:click="phones.push({ label: new_label, phone_number: new_number }); new_label=''; new_number = ''";
|
||||||
|
}
|
||||||
label { "lives with" }
|
label { "lives with" }
|
||||||
div {
|
div {
|
||||||
input name="lives_with" value=(contact.lives_with);
|
input name="lives_with" value=(contact.lives_with);
|
||||||
|
|
@ -367,6 +413,8 @@ mod put {
|
||||||
birthday: String,
|
birthday: String,
|
||||||
manually_freshened_at: String,
|
manually_freshened_at: String,
|
||||||
lives_with: String,
|
lives_with: String,
|
||||||
|
phone_label: Option<Vec<String>>,
|
||||||
|
phone_number: Option<Vec<String>>,
|
||||||
address_label: Option<Vec<String>>,
|
address_label: Option<Vec<String>>,
|
||||||
address_value: Option<Vec<String>>,
|
address_value: Option<Vec<String>>,
|
||||||
group: Option<Vec<String>>,
|
group: Option<Vec<String>>,
|
||||||
|
|
@ -473,6 +521,41 @@ mod put {
|
||||||
// these blocks are not in functions because payload gets progressively
|
// these blocks are not in functions because payload gets progressively
|
||||||
// partially moved as we handle each field and i don't want to deal with it
|
// partially moved as we handle each field and i don't want to deal with it
|
||||||
|
|
||||||
|
{
|
||||||
|
// update phone numbers
|
||||||
|
let new_numbers = payload.phone_number.clone().map_or(vec![], |numbers| {
|
||||||
|
let labels: Vec<String> = payload.phone_label.clone().unwrap();
|
||||||
|
|
||||||
|
// TODO sanitize down to linkable on input
|
||||||
|
labels
|
||||||
|
.into_iter()
|
||||||
|
.zip(numbers)
|
||||||
|
.filter(|(_, val)| val.len() > 0)
|
||||||
|
.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?;
|
||||||
|
|
||||||
|
if new_numbers != old_numbers {
|
||||||
|
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);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// update addresses
|
// update addresses
|
||||||
let new_addresses = payload.address_value.clone().map(|values| {
|
let new_addresses = payload.address_value.clone().map(|values| {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue