Fixing web

This commit is contained in:
Adrian Woźniak 2022-11-23 16:31:11 +01:00
parent f98d1e6903
commit ee5bc43962
10 changed files with 120 additions and 71 deletions

View File

@ -57,7 +57,7 @@ pub async fn modify_item(msg: modify_item::Input, db: Database) -> modify_item::
}; };
let dbm = ActiveCartItemByProduct { let dbm = ActiveCartItemByProduct {
product_id: msg.product_id, product_variant_id: msg.product_variant_id,
}; };
let item: Option<model::ShoppingCartItem> = match dbm.run(&mut t).await { let item: Option<model::ShoppingCartItem> = match dbm.run(&mut t).await {
Ok(res) => res, Ok(res) => res,
@ -83,7 +83,7 @@ pub async fn modify_item(msg: modify_item::Input, db: Database) -> modify_item::
Some(item) => { Some(item) => {
let dbm = UpdateShoppingCartItem { let dbm = UpdateShoppingCartItem {
id: item.id, id: item.id,
product_id: msg.product_id, product_variant_id: msg.product_variant_id,
shopping_cart_id: cart.id, shopping_cart_id: cart.id,
quantity: msg.quantity, quantity: msg.quantity,
quantity_unit: msg.quantity_unit, quantity_unit: msg.quantity_unit,
@ -99,7 +99,7 @@ pub async fn modify_item(msg: modify_item::Input, db: Database) -> modify_item::
} }
None => { None => {
let dbm = CreateShoppingCartItem { let dbm = CreateShoppingCartItem {
product_id: msg.product_id, product_variant_id: msg.product_variant_id,
shopping_cart_id: cart.id, shopping_cart_id: cart.id,
quantity: msg.quantity, quantity: msg.quantity,
quantity_unit: msg.quantity_unit, quantity_unit: msg.quantity_unit,
@ -127,7 +127,7 @@ pub async fn remove_product(
let dbm = RemoveCartItem { let dbm = RemoveCartItem {
shopping_cart_id: msg.shopping_cart_id, shopping_cart_id: msg.shopping_cart_id,
shopping_cart_item_id: Some(msg.shopping_cart_item_id), shopping_cart_item_id: Some(msg.shopping_cart_item_id),
product_id: None, product_variant_id: None,
}; };
let mut t = begin_t!(db); let mut t = begin_t!(db);
@ -185,7 +185,7 @@ pub async fn modify_cart(
msg.items msg.items
.iter() .iter()
.fold(HashSet::with_capacity(msg.items.len()), |mut agg, item| { .fold(HashSet::with_capacity(msg.items.len()), |mut agg, item| {
agg.insert(item.product_id); agg.insert(item.product_variant_id);
agg agg
}); });
@ -202,12 +202,12 @@ pub async fn modify_cart(
for item in items for item in items
.into_iter() .into_iter()
.filter(|item| !existing.contains(&item.product_id)) .filter(|item| !existing.contains(&item.product_variant_id))
{ {
let dbm = RemoveCartItem { let dbm = RemoveCartItem {
shopping_cart_id: cart.id, shopping_cart_id: cart.id,
shopping_cart_item_id: Some(item.id), shopping_cart_item_id: Some(item.id),
product_id: None, product_variant_id: None,
}; };
match dbm.run(&mut t).await { match dbm.run(&mut t).await {
Ok(_) => {} Ok(_) => {}

View File

@ -40,12 +40,14 @@ pub mod remove_product {
} }
pub mod modify_item { pub mod modify_item {
use model::v2::ProductVariantId;
use super::Error; use super::Error;
#[derive(Debug, serde::Serialize, serde::Deserialize)] #[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Input { pub struct Input {
pub buyer_id: model::AccountId, pub buyer_id: model::AccountId,
pub product_id: model::ProductId, pub product_variant_id: ProductVariantId,
pub quantity: model::Quantity, pub quantity: model::Quantity,
pub quantity_unit: model::QuantityUnit, pub quantity_unit: model::QuantityUnit,
} }

View File

@ -290,6 +290,25 @@ pub struct Product {
pub photos: Vec<Photo>, pub photos: Vec<Photo>,
} }
pub mod v2 {
use crate::api::{Category, Photo};
#[derive(Debug, Hash, serde::Serialize, serde::Deserialize)]
pub struct ProductVariant {
pub id: crate::ProductVariantId,
pub product_id: crate::ProductId,
pub name: crate::ProductName,
pub short_description: crate::ProductShortDesc,
pub long_description: crate::ProductLongDesc,
pub category: Option<Category>,
pub price: crate::Price,
pub available: bool,
pub quantity_unit: crate::QuantityUnit,
pub deliver_days_flag: crate::Days,
pub photos: Vec<Photo>,
}
}
impl<'path> impl<'path>
From<( From<(
crate::Product, crate::Product,
@ -452,7 +471,7 @@ pub struct DeleteItemOutput {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct UpdateItemInput { pub struct UpdateItemInput {
pub product_id: ProductId, pub product_variant_id: ProductVariantId,
pub quantity: Quantity, pub quantity: Quantity,
pub quantity_unit: QuantityUnit, pub quantity_unit: QuantityUnit,
} }

View File

@ -1,4 +1,5 @@
use model::api::OrderAddressInput; use model::api::OrderAddressInput;
use model::v2::ProductVariantId;
use model::{AccessTokenString, AddressId, PaymentMethod, RefreshTokenString}; use model::{AccessTokenString, AddressId, PaymentMethod, RefreshTokenString};
use seed::fetch::{Header, Method, Request}; use seed::fetch::{Header, Method, Request};
@ -66,12 +67,12 @@ pub async fn sign_up(input: model::api::CreateAccountInput) -> NetRes<model::api
pub async fn update_cart_item( pub async fn update_cart_item(
access_token: &AccessTokenString, access_token: &AccessTokenString,
product_id: model::ProductId, product_variant_id: ProductVariantId,
quantity: model::Quantity, quantity: model::Quantity,
quantity_unit: model::QuantityUnit, quantity_unit: model::QuantityUnit,
) -> NetRes<model::api::UpdateItemOutput> { ) -> NetRes<model::api::UpdateItemOutput> {
let input = model::api::UpdateItemInput { let input = model::api::UpdateItemInput {
product_id, product_variant_id,
quantity, quantity,
quantity_unit, quantity_unit,
}; };
@ -97,11 +98,11 @@ pub async fn update_cart(
.into_iter() .into_iter()
.map( .map(
|crate::shopping_cart::Item { |crate::shopping_cart::Item {
product_id, product_variant_id,
quantity, quantity,
quantity_unit, quantity_unit,
}| model::api::UpdateItemInput { }| model::api::UpdateItemInput {
product_id, product_variant_id,
quantity, quantity,
quantity_unit, quantity_unit,
}, },

View File

@ -1,5 +1,7 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use model::v2::ProductVariantId;
use model::ProductId;
use seed::Url; use seed::Url;
use crate::{shopping_cart, I18n, Page}; use crate::{shopping_cart, I18n, Page};
@ -72,8 +74,10 @@ impl From<::model::api::Config> for Config {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Products { pub struct Products {
pub categories: Vec<model::api::Category>, pub categories: Vec<model::api::Category>,
pub product_ids: Vec<model::ProductId>, pub product_ids: Vec<ProductId>,
pub products: HashMap<model::ProductId, model::api::Product>, pub products: HashMap<ProductId, model::api::Product>,
pub product_variant_ids: Vec<ProductVariantId>,
pub product_variants: HashMap<ProductVariantId, model::api::v2::ProductVariant>,
} }
impl Products { impl Products {
@ -102,13 +106,18 @@ impl Products {
}; };
} }
pub fn filter_product_ids<F>(&self, filter: F) -> Vec<model::ProductId> pub fn filter_product_variant_ids<F>(&self, filter: F) -> Vec<ProductVariantId>
where where
F: Fn(&model::api::Product) -> bool, F: Fn(&model::api::v2::ProductVariant) -> bool,
{ {
self.product_ids self.product_variant_ids
.iter() .iter()
.filter_map(|id| self.products.get(id).filter(|&p| filter(p)).map(|p| p.id)) .filter_map(|id| {
self.product_variants
.get(id)
.filter(|&p| filter(p))
.map(|p| p.id)
})
.collect() .collect()
} }
} }

View File

@ -177,18 +177,22 @@ mod left_side {
.values() .values()
.filter_map(|item: &Item| { .filter_map(|item: &Item| {
page.products page.products
.products .product_variants
.get(&item.product_id) .get(&item.product_variant_id)
.map(|product| (item, product)) .map(|product| (item, product))
}) })
.map(|(item, product)| product_view(model, product, item)); .map(|(item, variant)| product_view(model, variant, item));
div![ div![
C!["w-full mx-auto text-gray-800 font-light mb-6 border-b border-gray-200 pb-6"], C!["w-full mx-auto text-gray-800 font-light mb-6 border-b border-gray-200 pb-6"],
products products
] ]
} }
fn product_view(model: &crate::Model, product: &model::api::Product, item: &Item) -> Node<Msg> { fn product_view(
model: &crate::Model,
product: &model::api::v2::ProductVariant,
item: &Item,
) -> Node<Msg> {
let img = product let img = product
.photos .photos
.first() .first()
@ -236,14 +240,14 @@ mod left_side {
.cart .cart
.items .items
.values() .values()
.filter_map(|item: &crate::shopping_cart::Item| { .filter_map(|item: &Item| {
page.products page.products
.products .product_variants
.get(&item.product_id) .get(&item.product_variant_id)
.map(|product| (item, product)) .map(|product| (item, product))
}) })
.map( .map(
|(item, product): (&crate::shopping_cart::Item, &model::api::Product)| { |(item, product): (&Item, &model::api::v2::ProductVariant)| {
**(product.price * item.quantity) **(product.price * item.quantity)
}, },
) )

View File

@ -1,5 +1,6 @@
use std::collections::HashSet; use std::collections::HashSet;
use model::v2::ProductVariantId;
use model::Quantity; use model::Quantity;
use seed::app::Orders; use seed::app::Orders;
use seed::prelude::*; use seed::prelude::*;
@ -14,7 +15,7 @@ use crate::shopping_cart::CartMsg;
pub struct ListingPage { pub struct ListingPage {
pub errors: Vec<String>, pub errors: Vec<String>,
pub filters: HashSet<String>, pub filters: HashSet<String>,
pub visible_products: Vec<model::ProductId>, pub visible_product_variants: Vec<ProductVariantId>,
pub products: Products, pub products: Products,
} }
@ -31,7 +32,7 @@ pub fn init(url: Url, orders: &mut impl Orders<ListingMsg>) -> ListingPage {
products: Products::default(), products: Products::default(),
filters: url_to_filters(url), filters: url_to_filters(url),
errors: vec![], errors: vec![],
visible_products: vec![], visible_product_variants: vec![],
} }
} }
@ -54,12 +55,12 @@ pub fn page_changed(url: Url, model: &mut ListingPage) {
let ids = { let ids = {
model model
.products .products
.filter_product_ids(|product| filter_product(&*model, product)) .filter_product_variant_ids(|product| filter_product(&*model, product))
}; };
model.visible_products = ids; model.visible_product_variants = ids;
} }
fn filter_product(model: &ListingPage, product: &model::api::Product) -> bool { fn filter_product(model: &ListingPage, product: &model::api::v2::ProductVariant) -> bool {
product product
.category .category
.as_ref() .as_ref()
@ -83,8 +84,8 @@ pub fn update(msg: ListingMsg, model: &mut ListingPage, orders: &mut impl Orders
model.products.update(products.0); model.products.update(products.0);
let ids = model let ids = model
.products .products
.filter_product_ids(|product| filter_product(model, product)); .filter_product_variant_ids(|product| filter_product(model, product));
model.visible_products = ids; model.visible_product_variants = ids;
} }
ListingMsg::ProductsFetched(NetRes::Error(_)) ListingMsg::ProductsFetched(NetRes::Error(_))
| ListingMsg::ProductsFetched(NetRes::Http(_)) => { | ListingMsg::ProductsFetched(NetRes::Http(_)) => {
@ -94,17 +95,17 @@ pub fn update(msg: ListingMsg, model: &mut ListingPage, orders: &mut impl Orders
} }
pub fn view(model: &crate::Model, page: &ListingPage) -> Node<crate::Msg> { pub fn view(model: &crate::Model, page: &ListingPage) -> Node<crate::Msg> {
let products: Vec<Node<crate::Msg>> = if page.visible_products.is_empty() { let products: Vec<Node<crate::Msg>> = if page.visible_product_variants.is_empty() {
page.products page.products
.product_ids .product_variant_ids
.iter() .iter()
.filter_map(|id| page.products.products.get(id)) .filter_map(|id| page.products.product_variants.get(id))
.map(|p| product(model, p)) .map(|p| product(model, p))
.collect() .collect()
} else { } else {
page.visible_products page.visible_product_variants
.iter() .iter()
.filter_map(|id| page.products.products.get(id)) .filter_map(|id| page.products.product_variants.get(id))
.map(|p| product(model, p)) .map(|p| product(model, p))
.collect() .collect()
}; };
@ -121,7 +122,7 @@ pub fn view(model: &crate::Model, page: &ListingPage) -> Node<crate::Msg> {
] ]
} }
fn product(model: &crate::Model, product: &model::api::Product) -> Node<crate::Msg> { fn product(model: &crate::Model, product: &model::api::v2::ProductVariant) -> Node<crate::Msg> {
use rusty_money::Money; use rusty_money::Money;
let price = Money::from_minor(**product.price as i64, model.config.currency).to_string(); let price = Money::from_minor(**product.price as i64, model.config.currency).to_string();
@ -138,7 +139,7 @@ fn product(model: &crate::Model, product: &model::api::Product) -> Node<crate::M
.add_path_part((*product.id as i32).to_string()); .add_path_part((*product.id as i32).to_string());
let quantity_unit = product.quantity_unit; let quantity_unit = product.quantity_unit;
let product_id = product.id; let product_variant_id = product.id;
div![ div![
C!["w-full px-4 lg:px-0"], C!["w-full px-4 lg:px-0"],
@ -174,7 +175,7 @@ fn product(model: &crate::Model, product: &model::api::Product) -> Node<crate::M
ev.prevent_default(); ev.prevent_default();
ev.stop_propagation(); ev.stop_propagation();
crate::Msg::from(CartMsg::AddItem { crate::Msg::from(CartMsg::AddItem {
product_id, product_variant_id,
quantity_unit, quantity_unit,
quantity: Quantity::try_from(1).unwrap_or_default() quantity: Quantity::try_from(1).unwrap_or_default()
}) })

View File

@ -167,14 +167,15 @@ mod summary_right {
.values() .values()
.filter_map(|item: &crate::shopping_cart::Item| { .filter_map(|item: &crate::shopping_cart::Item| {
page.products page.products
.products .product_variants
.get(&item.product_id) .get(&item.product_variant_id)
.map(|product| (item, product)) .map(|product| (item, product))
}) })
.map( .map(
|(item, product): (&crate::shopping_cart::Item, &model::api::Product)| { |(item, product): (
**(product.price * item.quantity) &crate::shopping_cart::Item,
}, &model::api::v2::ProductVariant,
)| { **(product.price * item.quantity) },
) )
.sum::<i32>() as i64; .sum::<i32>() as i64;
div![ div![
@ -391,12 +392,12 @@ fn products_body(model: &crate::Model, page: &ShoppingCartPage) -> Node<crate::M
.values() .values()
.filter_map(|item: &crate::shopping_cart::Item| { .filter_map(|item: &crate::shopping_cart::Item| {
page.products page.products
.products .product_variants
.get(&item.product_id) .get(&item.product_variant_id)
.map(|product| (item, product)) .map(|product| (item, product))
}) })
.map( .map(
|(item, product): (&crate::shopping_cart::Item, &model::api::Product)| { |(item, product): (&crate::shopping_cart::Item, &model::api::v2::ProductVariant)| {
item_view(model, item, product) item_view(model, item, product)
}, },
); );
@ -406,7 +407,7 @@ fn products_body(model: &crate::Model, page: &ShoppingCartPage) -> Node<crate::M
fn item_view( fn item_view(
model: &crate::Model, model: &crate::Model,
item: &crate::shopping_cart::Item, item: &crate::shopping_cart::Item,
product: &model::api::Product, product: &model::api::v2::ProductVariant,
) -> Node<crate::Msg> { ) -> Node<crate::Msg> {
use rusty_money::Money; use rusty_money::Money;
@ -416,7 +417,7 @@ fn item_view(
.map(|photo| photo.url.as_str()) .map(|photo| photo.url.as_str())
.unwrap_or_default(); .unwrap_or_default();
let product_id = product.id; let product_variant_id = product.id;
let quantity_unit = product.quantity_unit; let quantity_unit = product.quantity_unit;
let product_url = Urls::new(&model.url) let product_url = Urls::new(&model.url)
.product() .product()
@ -441,7 +442,7 @@ fn item_view(
ev(Ev::Submit, move |ev| { ev(Ev::Submit, move |ev| {
ev.prevent_default(); ev.prevent_default();
ev.stop_propagation(); ev.stop_propagation();
crate::Msg::from(CartMsg::Remove(product_id)) crate::Msg::from(CartMsg::Remove(product_variant_id))
}), }),
button![ button![
attrs![At::Type => "submit", At::Class => "text-gray-700 md:ml-4 text-red-600"], attrs![At::Type => "submit", At::Class => "text-gray-700 md:ml-4 text-red-600"],
@ -449,7 +450,7 @@ fn item_view(
ev(Ev::Click, move |ev| { ev(Ev::Click, move |ev| {
ev.prevent_default(); ev.prevent_default();
ev.stop_propagation(); ev.stop_propagation();
crate::Msg::from(CartMsg::Remove(product_id)) crate::Msg::from(CartMsg::Remove(product_variant_id))
}) })
] ]
] ]
@ -474,7 +475,7 @@ fn item_view(
let quantity = Quantity::from_u32(value); let quantity = Quantity::from_u32(value);
Some(crate::Msg::from(CartMsg::ModifyItem { Some(crate::Msg::from(CartMsg::ModifyItem {
product_id, product_variant_id,
quantity_unit, quantity_unit,
quantity, quantity,
})) }))

View File

@ -125,8 +125,19 @@ pub mod cart_dropdown {
}; };
} }
macro_rules! filter_product_variants {
($model: expr, $products: expr) => {
$model
.cart
.items
.values()
.filter_map(|item: &Item| filter_product(item, $products, item.product_variant_id))
};
}
pub fn view(model: &Model, products: &crate::model::Products) -> Node<Msg> { pub fn view(model: &Model, products: &crate::model::Products) -> Node<Msg> {
let items = filter_products!(model, products).map(|(it, product)| item(model, it, product)); let items =
filter_product_variants!(model, products).map(|(it, product)| item(model, it, product));
div![ div![
C![ C![
"absolute w-full rounded-b border-t-0 z-10", "absolute w-full rounded-b border-t-0 z-10",
@ -201,7 +212,7 @@ pub mod cart_dropdown {
} }
fn checkout(model: &Model, products: &crate::model::Products) -> Node<Msg> { fn checkout(model: &Model, products: &crate::model::Products) -> Node<Msg> {
let sum: i32 = filter_products!(model, products) let sum: i32 = filter_product_variants!(model, products)
.map(|(item, product): (&Item, &model::api::Product)| **item.quantity * **product.price) .map(|(item, product): (&Item, &model::api::Product)| **item.quantity * **product.price)
.sum(); .sum();
let sum = rusty_money::Money::from_minor(sum as i64, model.config.currency); let sum = rusty_money::Money::from_minor(sum as i64, model.config.currency);

View File

@ -1,3 +1,4 @@
use model::v2::ProductVariantId;
use model::{PaymentMethod, ProductId, Quantity, QuantityUnit}; use model::{PaymentMethod, ProductId, Quantity, QuantityUnit};
use seed::prelude::*; use seed::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -10,14 +11,14 @@ pub enum CartMsg {
AddItem { AddItem {
quantity: Quantity, quantity: Quantity,
quantity_unit: QuantityUnit, quantity_unit: QuantityUnit,
product_id: ProductId, product_variant_id: ProductVariantId,
}, },
ModifyItem { ModifyItem {
quantity: Quantity, quantity: Quantity,
quantity_unit: QuantityUnit, quantity_unit: QuantityUnit,
product_id: ProductId, product_variant_id: ProductVariantId,
}, },
Remove(ProductId), Remove(ProductVariantId),
Hover, Hover,
Leave, Leave,
/// Send current non-empty cart to server /// Send current non-empty cart to server
@ -38,7 +39,7 @@ pub type Items = indexmap::IndexMap<ProductId, Item>;
#[derive(Debug, Copy, Clone, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct Item { pub struct Item {
#[serde(rename = "i")] #[serde(rename = "i")]
pub product_id: ProductId, pub product_variant_id: ProductVariantId,
#[serde(rename = "q")] #[serde(rename = "q")]
pub quantity: Quantity, pub quantity: Quantity,
#[serde(rename = "u")] #[serde(rename = "u")]
@ -68,14 +69,14 @@ pub fn update(msg: CartMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
CartMsg::AddItem { CartMsg::AddItem {
quantity, quantity,
quantity_unit, quantity_unit,
product_id, product_variant_id,
} => { } => {
{ {
let items: &mut Items = &mut model.cart.items; let items: &mut Items = &mut model.cart.items;
let entry: &mut Item = items.entry(product_id).or_insert_with(|| Item { let entry: &mut Item = items.entry(product_variant_id).or_insert_with(|| Item {
quantity: Quantity::from_u32(0), quantity: Quantity::from_u32(0),
quantity_unit, quantity_unit,
product_id, product_variant_id,
}); });
entry.quantity = entry.quantity + quantity; entry.quantity = entry.quantity + quantity;
entry.quantity_unit = quantity_unit; entry.quantity_unit = quantity_unit;
@ -84,18 +85,18 @@ pub fn update(msg: CartMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
sync_cart(model, orders); sync_cart(model, orders);
} }
CartMsg::ModifyItem { CartMsg::ModifyItem {
product_id, product_variant_id,
quantity_unit, quantity_unit,
quantity, quantity,
} => { } => {
if **quantity == 0 { if **quantity == 0 {
model.cart.items.remove(&product_id); model.cart.items.remove(&product_variant_id);
} else { } else {
let items: &mut Items = &mut model.cart.items; let items: &mut Items = &mut model.cart.items;
let entry: &mut Item = items.entry(product_id).or_insert_with(|| Item { let entry: &mut Item = items.entry(product_variant_id).or_insert_with(|| Item {
quantity, quantity,
quantity_unit, quantity_unit,
product_id, product_variant_id,
}); });
entry.quantity = quantity; entry.quantity = quantity;
entry.quantity_unit = quantity_unit; entry.quantity_unit = quantity_unit;
@ -125,15 +126,15 @@ pub fn update(msg: CartMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|mut set, |mut set,
model::api::ShoppingCartItem { model::api::ShoppingCartItem {
id: _, id: _,
product_id, product_variant_id,
shopping_cart_id: _, shopping_cart_id: _,
quantity, quantity,
quantity_unit, quantity_unit,
}| { }| {
set.insert( set.insert(
product_id, product_variant_id,
Item { Item {
product_id, product_variant_id,
quantity, quantity,
quantity_unit, quantity_unit,
}, },