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

View File

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

View File

@ -290,6 +290,25 @@ pub struct Product {
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>
From<(
crate::Product,
@ -452,7 +471,7 @@ pub struct DeleteItemOutput {
#[derive(Serialize, Deserialize, Debug)]
pub struct UpdateItemInput {
pub product_id: ProductId,
pub product_variant_id: ProductVariantId,
pub quantity: Quantity,
pub quantity_unit: QuantityUnit,
}

View File

@ -1,4 +1,5 @@
use model::api::OrderAddressInput;
use model::v2::ProductVariantId;
use model::{AccessTokenString, AddressId, PaymentMethod, RefreshTokenString};
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(
access_token: &AccessTokenString,
product_id: model::ProductId,
product_variant_id: ProductVariantId,
quantity: model::Quantity,
quantity_unit: model::QuantityUnit,
) -> NetRes<model::api::UpdateItemOutput> {
let input = model::api::UpdateItemInput {
product_id,
product_variant_id,
quantity,
quantity_unit,
};
@ -97,11 +98,11 @@ pub async fn update_cart(
.into_iter()
.map(
|crate::shopping_cart::Item {
product_id,
product_variant_id,
quantity,
quantity_unit,
}| model::api::UpdateItemInput {
product_id,
product_variant_id,
quantity,
quantity_unit,
},

View File

@ -1,5 +1,7 @@
use std::collections::{HashMap, HashSet};
use model::v2::ProductVariantId;
use model::ProductId;
use seed::Url;
use crate::{shopping_cart, I18n, Page};
@ -72,8 +74,10 @@ impl From<::model::api::Config> for Config {
#[derive(Debug, Default)]
pub struct Products {
pub categories: Vec<model::api::Category>,
pub product_ids: Vec<model::ProductId>,
pub products: HashMap<model::ProductId, model::api::Product>,
pub product_ids: Vec<ProductId>,
pub products: HashMap<ProductId, model::api::Product>,
pub product_variant_ids: Vec<ProductVariantId>,
pub product_variants: HashMap<ProductVariantId, model::api::v2::ProductVariant>,
}
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
F: Fn(&model::api::Product) -> bool,
F: Fn(&model::api::v2::ProductVariant) -> bool,
{
self.product_ids
self.product_variant_ids
.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()
}
}

View File

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

View File

@ -1,5 +1,6 @@
use std::collections::HashSet;
use model::v2::ProductVariantId;
use model::Quantity;
use seed::app::Orders;
use seed::prelude::*;
@ -14,7 +15,7 @@ use crate::shopping_cart::CartMsg;
pub struct ListingPage {
pub errors: Vec<String>,
pub filters: HashSet<String>,
pub visible_products: Vec<model::ProductId>,
pub visible_product_variants: Vec<ProductVariantId>,
pub products: Products,
}
@ -31,7 +32,7 @@ pub fn init(url: Url, orders: &mut impl Orders<ListingMsg>) -> ListingPage {
products: Products::default(),
filters: url_to_filters(url),
errors: vec![],
visible_products: vec![],
visible_product_variants: vec![],
}
}
@ -54,12 +55,12 @@ pub fn page_changed(url: Url, model: &mut ListingPage) {
let ids = {
model
.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
.category
.as_ref()
@ -83,8 +84,8 @@ pub fn update(msg: ListingMsg, model: &mut ListingPage, orders: &mut impl Orders
model.products.update(products.0);
let ids = model
.products
.filter_product_ids(|product| filter_product(model, product));
model.visible_products = ids;
.filter_product_variant_ids(|product| filter_product(model, product));
model.visible_product_variants = ids;
}
ListingMsg::ProductsFetched(NetRes::Error(_))
| 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> {
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
.product_ids
.product_variant_ids
.iter()
.filter_map(|id| page.products.products.get(id))
.filter_map(|id| page.products.product_variants.get(id))
.map(|p| product(model, p))
.collect()
} else {
page.visible_products
page.visible_product_variants
.iter()
.filter_map(|id| page.products.products.get(id))
.filter_map(|id| page.products.product_variants.get(id))
.map(|p| product(model, p))
.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;
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());
let quantity_unit = product.quantity_unit;
let product_id = product.id;
let product_variant_id = product.id;
div![
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.stop_propagation();
crate::Msg::from(CartMsg::AddItem {
product_id,
product_variant_id,
quantity_unit,
quantity: Quantity::try_from(1).unwrap_or_default()
})

View File

@ -167,14 +167,15 @@ mod summary_right {
.values()
.filter_map(|item: &crate::shopping_cart::Item| {
page.products
.products
.get(&item.product_id)
.product_variants
.get(&item.product_variant_id)
.map(|product| (item, product))
})
.map(
|(item, product): (&crate::shopping_cart::Item, &model::api::Product)| {
**(product.price * item.quantity)
},
|(item, product): (
&crate::shopping_cart::Item,
&model::api::v2::ProductVariant,
)| { **(product.price * item.quantity) },
)
.sum::<i32>() as i64;
div![
@ -391,12 +392,12 @@ fn products_body(model: &crate::Model, page: &ShoppingCartPage) -> Node<crate::M
.values()
.filter_map(|item: &crate::shopping_cart::Item| {
page.products
.products
.get(&item.product_id)
.product_variants
.get(&item.product_variant_id)
.map(|product| (item, product))
})
.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)
},
);
@ -406,7 +407,7 @@ fn products_body(model: &crate::Model, page: &ShoppingCartPage) -> Node<crate::M
fn item_view(
model: &crate::Model,
item: &crate::shopping_cart::Item,
product: &model::api::Product,
product: &model::api::v2::ProductVariant,
) -> Node<crate::Msg> {
use rusty_money::Money;
@ -416,7 +417,7 @@ fn item_view(
.map(|photo| photo.url.as_str())
.unwrap_or_default();
let product_id = product.id;
let product_variant_id = product.id;
let quantity_unit = product.quantity_unit;
let product_url = Urls::new(&model.url)
.product()
@ -441,7 +442,7 @@ fn item_view(
ev(Ev::Submit, move |ev| {
ev.prevent_default();
ev.stop_propagation();
crate::Msg::from(CartMsg::Remove(product_id))
crate::Msg::from(CartMsg::Remove(product_variant_id))
}),
button![
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.prevent_default();
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);
Some(crate::Msg::from(CartMsg::ModifyItem {
product_id,
product_variant_id,
quantity_unit,
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> {
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![
C![
"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> {
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)
.sum();
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 seed::prelude::*;
use serde::{Deserialize, Serialize};
@ -10,14 +11,14 @@ pub enum CartMsg {
AddItem {
quantity: Quantity,
quantity_unit: QuantityUnit,
product_id: ProductId,
product_variant_id: ProductVariantId,
},
ModifyItem {
quantity: Quantity,
quantity_unit: QuantityUnit,
product_id: ProductId,
product_variant_id: ProductVariantId,
},
Remove(ProductId),
Remove(ProductVariantId),
Hover,
Leave,
/// Send current non-empty cart to server
@ -38,7 +39,7 @@ pub type Items = indexmap::IndexMap<ProductId, Item>;
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct Item {
#[serde(rename = "i")]
pub product_id: ProductId,
pub product_variant_id: ProductVariantId,
#[serde(rename = "q")]
pub quantity: Quantity,
#[serde(rename = "u")]
@ -68,14 +69,14 @@ pub fn update(msg: CartMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
CartMsg::AddItem {
quantity,
quantity_unit,
product_id,
product_variant_id,
} => {
{
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_unit,
product_id,
product_variant_id,
});
entry.quantity = entry.quantity + quantity;
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);
}
CartMsg::ModifyItem {
product_id,
product_variant_id,
quantity_unit,
quantity,
} => {
if **quantity == 0 {
model.cart.items.remove(&product_id);
model.cart.items.remove(&product_variant_id);
} else {
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_unit,
product_id,
product_variant_id,
});
entry.quantity = quantity;
entry.quantity_unit = quantity_unit;
@ -125,15 +126,15 @@ pub fn update(msg: CartMsg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|mut set,
model::api::ShoppingCartItem {
id: _,
product_id,
product_variant_id,
shopping_cart_id: _,
quantity,
quantity_unit,
}| {
set.insert(
product_id,
product_variant_id,
Item {
product_id,
product_variant_id,
quantity,
quantity_unit,
},