profile name surname and save profile data
This commit is contained in:
@@ -23,6 +23,8 @@ use crate::{
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ProfileForm {
|
||||
first_name: Option<String>,
|
||||
last_name: Option<String>,
|
||||
company_name: Option<String>,
|
||||
company_id: Option<String>,
|
||||
tax_id: Option<String>,
|
||||
@@ -39,6 +41,24 @@ fn trimmed(value: Option<&str>) -> Option<String> {
|
||||
value.map(str::trim).filter(|v| !v.is_empty()).map(String::from)
|
||||
}
|
||||
|
||||
/// Split a stored full name into (first name, surname). The surname is
|
||||
/// everything after the first whitespace, so multi-word surnames round-trip.
|
||||
fn split_name(name: &str) -> (String, String) {
|
||||
match name.trim().split_once(char::is_whitespace) {
|
||||
Some((first, rest)) => (first.to_string(), rest.trim().to_string()),
|
||||
None => (name.trim().to_string(), String::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Recombine the two name fields into the single stored `name`. Returns `None`
|
||||
/// when the result is too short to be a valid name (the user can't blank it out).
|
||||
fn full_name_from_form(form: &ProfileForm) -> Option<String> {
|
||||
let first = form.first_name.as_deref().unwrap_or("").trim();
|
||||
let last = form.last_name.as_deref().unwrap_or("").trim();
|
||||
let full = format!("{first} {last}").trim().to_string();
|
||||
(full.chars().count() >= 2).then_some(full)
|
||||
}
|
||||
|
||||
/// Build the persisted fields from the submitted form. Company identifiers are
|
||||
/// only kept for company accounts (a personal account can never carry them).
|
||||
fn fields_from_form(form: &ProfileForm, is_company: bool) -> ProfileFields {
|
||||
@@ -92,6 +112,7 @@ fn profile_view(
|
||||
saved: bool,
|
||||
error: bool,
|
||||
) -> Result<Response> {
|
||||
let (first_name, last_name) = split_name(&user.name);
|
||||
format::view(
|
||||
v,
|
||||
"account/profile.html",
|
||||
@@ -101,6 +122,8 @@ fn profile_view(
|
||||
"saved": saved,
|
||||
"error": error,
|
||||
"name": user.name,
|
||||
"first_name": first_name,
|
||||
"last_name": last_name,
|
||||
"email": user.email,
|
||||
"account_type": user.account_type,
|
||||
"company_name": fields.company_name,
|
||||
@@ -147,12 +170,25 @@ async fn save_profile(
|
||||
if guard::is_admin(&ctx, &user) {
|
||||
return format::redirect("/admin/dashboard");
|
||||
}
|
||||
// Apply the edited name to a working copy so it's reflected on both the
|
||||
// success and re-rendered-error views. A blank/too-short name is ignored —
|
||||
// the field can't be cleared.
|
||||
let mut user = user;
|
||||
let new_name = full_name_from_form(&form).filter(|n| *n != user.name);
|
||||
if let Some(name) = new_name.clone() {
|
||||
user.name = name;
|
||||
}
|
||||
let fields = fields_from_form(&form, user.is_company());
|
||||
// A company account's profile is rejected (and re-shown with the entered
|
||||
// values) until it carries its required identifiers.
|
||||
if user.is_company() && company_fields_missing(&fields) {
|
||||
return profile_view(&v, &jar, &user, &fields, false, true);
|
||||
}
|
||||
if let Some(name) = new_name {
|
||||
let mut active = user.clone().into_active_model();
|
||||
active.name = ActiveValue::set(name);
|
||||
active.update(&ctx.db).await?;
|
||||
}
|
||||
customer_profiles::Model::upsert(&ctx.db, user.id, fields.clone()).await?;
|
||||
profile_view(&v, &jar, &user, &fields, true, false)
|
||||
}
|
||||
|
||||
@@ -113,6 +113,12 @@ async fn checkout_page(
|
||||
let p = |get: fn(&customer_profiles::Model) -> Option<String>| {
|
||||
profile.as_ref().and_then(get)
|
||||
};
|
||||
// Whether the customer already has a shipping address on file. When they do,
|
||||
// the "save this address to my profile" opt-in is pointless (the profile was
|
||||
// filled in advance), so it's hidden and the existing profile is left alone.
|
||||
let profile_filled = profile
|
||||
.as_ref()
|
||||
.is_some_and(|pr| pr.address.is_some() && pr.city.is_some() && pr.zip.is_some());
|
||||
|
||||
format::view(
|
||||
&v,
|
||||
@@ -126,6 +132,7 @@ async fn checkout_page(
|
||||
"packeta_api_key": settings::get(&ctx, "packeta_api_key").unwrap_or(""),
|
||||
"logged_in_admin": is_admin,
|
||||
"logged_in_customer": is_customer,
|
||||
"profile_filled": profile_filled,
|
||||
// A logged-in customer's account type is fixed; only guests pick it
|
||||
// and may opt to create an account from the order.
|
||||
"account_fixed": is_customer,
|
||||
|
||||
Reference in New Issue
Block a user