diff --git a/db-seed/src/products.rs b/db-seed/src/products.rs index 90f03bd..5bc1b23 100644 --- a/db-seed/src/products.rs +++ b/db-seed/src/products.rs @@ -12,6 +12,8 @@ async fn create_product( seed: SharedState, name: &'static str, category: &'static str, + short_desc: Option<&'static str>, + long_desc: Option<&'static str>, ) -> crate::Result<()> { let seed = seed.clone(); let db = db.clone(); @@ -19,8 +21,12 @@ async fn create_product( db, database_manager::CreateProduct { name: ProductName::from(String::from(name)), - short_description: Faker.fake(), - long_description: Faker.fake(), + short_description: short_desc + .map(|s| model::ProductShortDesc::new(s)) + .unwrap_or_else(|| Faker.fake()), + long_description: long_desc + .map(|s| model::ProductLongDesc::new(s)) + .unwrap_or_else(|| Faker.fake()), category: Some(ProductCategory::new(category)), price: Faker.fake(), deliver_days_flag: Faker.fake(), @@ -59,72 +65,96 @@ pub(crate) async fn create_products( seed.clone(), "Nikon", model::Category::CAMERAS_NAME, + None, + None ), create_product( db.clone(), seed.clone(), "Bonoid CBD", model::Category::DRUGSTORE_NAME, + None, + None ), create_product( db.clone(), seed.clone(), "Casio Speaker", model::Category::SPEAKERS_NAME, + None, + None ), create_product( db.clone(), seed.clone(), "Eprism Studio", model::Category::DRUGSTORE_NAME, + None, + None ), create_product( db.clone(), seed.clone(), "Best Phones 2022", model::Category::PHONES_NAME, + None, + None ), create_product( db.clone(), seed.clone(), "Sweet cake", model::Category::SWEETS_NAME, + None, + Some("The packaging of a product can do so much to the brand awareness campaigns of any brand. With a good quality logo along with the color scheme of a brand added on those packaging, it can perfectly represent such brand wherever it may reach. Do you need a mockup to test your paper lock box packaging? If you do then this freebie will make you feel lucky. It features 2 paper lock boxes with the one top of the other. Apparently, each of the boxes comes with smart object layer for inserting your own branding. You can use this for bread, donuts and similar products.") ), create_product( db.clone(), seed.clone(), "Lexal 128G", model::Category::MEMORY_NAME, + None, + None ), create_product( db.clone(), seed.clone(), "Fujifilm X-T10", model::Category::CAMERAS_NAME, + None, + None ), create_product( db.clone(), seed.clone(), "Sweet Tower", model::Category::SWEETS_NAME, + None, + Some("If you’re currently designing a label for your product packaging using pouch, you can opt for this Plastic Pouch Product Mockup Free PSD. Before you finalize your designs, it would be best to have a product mockup to help you see the flaws and polish it well. This mockup will aid you with your label designs for soy sauce, tomato sauce, coffee or any pouch products. Apparently, this mockup lets you add a design for the surface of the pouch via smart object layer; change the color of the pouch to represent the kind of flavor of the product inside and the background color to match with your designs.") ), create_product( db.clone(), seed.clone(), "Nikon Lenses", model::Category::CAMERAS_NAME, + None, + None ), create_product( db.clone(), seed.clone(), "Venus HD Professional", model::Category::DRUGSTORE_NAME, + None, + Some("Cosmetic products have always been hot and in demand. That’s why many entrepreneurs in this line of business always seek for the best cosmetic products to meet the demands. Hence, cosmetic businesses need to craft their labels with keenness to for effective branding. If you’re a designer working with cosmetic products packaging, this free mockup is useful to you. It features a cosmetic pump bottle and a cream pot to showcase your logo, branding and marketing designs. You can easily add your own graphics into the scene via the smart object layer. With this mockup, you can impress a client with a photorealistic product packaging in no time!") ), create_product( db.clone(), seed.clone(), "Fancy Plate", model::Category::PLATES_NAME, + None, + None ) ); results.0.unwrap(); diff --git a/shared/model/src/dummy.rs b/shared/model/src/dummy.rs index 3342eb2..852182c 100644 --- a/shared/model/src/dummy.rs +++ b/shared/model/src/dummy.rs @@ -26,7 +26,13 @@ impl fake::Dummy for ProductShortDesc { impl fake::Dummy for ProductLongDesc { fn dummy_with_rng(_config: &T, _rng: &mut R) -> Self { use fake::faker::lorem::en::Paragraphs; - let words: Vec = Paragraphs(10..20).fake(); + let words: Vec = (3..5) + .into_iter() + .map(|_| { + let words: Vec = Paragraphs(4..8).fake(); + words.join(" ").to_string() + }) + .collect(); Self(words.join("\n")) } } diff --git a/shared/model/src/lib.rs b/shared/model/src/lib.rs index b7383d2..3c4792e 100644 --- a/shared/model/src/lib.rs +++ b/shared/model/src/lib.rs @@ -681,12 +681,24 @@ pub struct ProductName(String); #[serde(transparent)] pub struct ProductShortDesc(String); +impl ProductShortDesc { + pub fn new>(s: S) -> Self { + Self(s.into()) + } +} + #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Clone, Hash, Deref, Display, From)] #[serde(transparent)] pub struct ProductLongDesc(String); +impl ProductLongDesc { + pub fn new>(s: S) -> Self { + Self(s.into()) + } +} + #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] diff --git a/web/src/i18n/pl.rs b/web/src/i18n/pl.rs index a0d108d..b2a9725 100644 --- a/web/src/i18n/pl.rs +++ b/web/src/i18n/pl.rs @@ -20,5 +20,15 @@ pub fn define(i18n: &mut I18n) { .define("Memory", "Pamięć") .define("Pants", "Spodnie") .define("Clothes", "Ubrania") - .define("Plates", "Talerze"); + .define("Plates", "Talerze") + .define("Price per", "Cena za") + .define("g", "g") + .define("dkg", "dkg") + .define("kg", "kg") + .define("piece", "szt") + .define("Gram", "Gram") + .define("Decagram", "Decagram") + .define("Kilogram", "Kilogram") + .define("Piece", "Sztukę") + .define("Out of stock", "Brak na stanie"); } diff --git a/web/src/pages/public/product.rs b/web/src/pages/public/product.rs index 6d1d0b2..b53f99d 100644 --- a/web/src/pages/public/product.rs +++ b/web/src/pages/public/product.rs @@ -95,11 +95,29 @@ pub fn view(model: &crate::Model, page: &ProductPage) -> Node { C!["mb-2 leading-tight tracking-tight font-bold text-gray-800 text-2xl md:text-3xl"], product.name.as_str() ], - div![model.i18n.t("Price per"), ": ", model.i18n.t(product.quantity_unit.short_name())], + div![model.i18n.t("Price per"), " ", model.i18n.t(product.quantity_unit.name()).to_lowercase()], div![ delivery ], - div![ + action_section(product, model) + ], + ], + div![ + C!["-mx-4"], + attrs!["id" => "product-header"], + div![description] + ] + ].map_msg(map_to_global); + + div![ + crate::shared::view::public_navbar(model), + super::layout::view(model, content, None) + ] +} + +fn action_section(product: &model::api::Product, model: &crate::Model) -> Node { + if product.available { + div![ C!["flex py-4 space-x-4"], div![ C!["relative"], @@ -120,19 +138,9 @@ pub fn view(model: &crate::Model, page: &ProductPage) -> Node { }) ] ] - ], - ], - div![ - C!["-mx-4"], - attrs!["id" => "product-header"], - div![description] - ] - ].map_msg(map_to_global); - - div![ - crate::shared::view::public_navbar(model), - super::layout::view(model, content, None) - ] + } else { + div![C!["text-sm text-gray-400 "], model.i18n.t("Out of stock")] + } } fn delivery_available(product: &model::api::Product, model: &crate::Model) -> Node { @@ -161,7 +169,7 @@ fn small_image(idx: usize, selected: usize, img: &model::api::Photo) -> Node C!["ring-2"]], img![ - C!["h-24 md:h-32"], + C!["h-24 md:h-31"], attrs!["src" => img.url.as_str()] ], ev("click", move |ev| {