Checkout page
This commit is contained in:
parent
66eb6395ac
commit
074c3a9900
@ -100,45 +100,38 @@ pub struct AddItem {
|
||||
cart_async_handler!(AddItem, add_item, ShoppingCartItem);
|
||||
|
||||
async fn add_item(msg: AddItem, db: actix::Addr<Database>) -> Result<ShoppingCartItem> {
|
||||
match db
|
||||
.send(database_manager::EnsureActiveShoppingCart {
|
||||
let _cart = query_db!(
|
||||
db,
|
||||
database_manager::EnsureActiveShoppingCart {
|
||||
buyer_id: msg.buyer_id,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(_)) => {}
|
||||
_ => return Err(Error::ShoppingCartFailed),
|
||||
};
|
||||
let cart = match db
|
||||
.send(database_manager::AccountShoppingCarts {
|
||||
},
|
||||
Error::ShoppingCartFailed
|
||||
);
|
||||
let mut carts: Vec<model::ShoppingCart> = query_db!(
|
||||
db,
|
||||
database_manager::AccountShoppingCarts {
|
||||
account_id: msg.buyer_id,
|
||||
state: Some(ShoppingCartState::Active),
|
||||
})
|
||||
.await
|
||||
.map(|res| match res {
|
||||
Ok(mut v) if !v.is_empty() => Ok(v.remove(0)),
|
||||
Err(e) => Err(Error::Db(e)),
|
||||
_ => Err(Error::CartNotAvailable),
|
||||
}) {
|
||||
Ok(Ok(cart)) => cart,
|
||||
Ok(Err(e)) => {
|
||||
log::error!("{e:?}");
|
||||
return Err(e);
|
||||
}
|
||||
_ => return Err(Error::CartNotAvailable),
|
||||
},
|
||||
passthrough Error::Db,
|
||||
Error::CartNotAvailable
|
||||
);
|
||||
let cart = if carts.is_empty() {
|
||||
return Err(Error::CartNotAvailable);
|
||||
} else {
|
||||
carts.remove(0)
|
||||
};
|
||||
match db
|
||||
.send(database_manager::CreateShoppingCartItem {
|
||||
Ok(query_db!(
|
||||
db,
|
||||
database_manager::CreateShoppingCartItem {
|
||||
product_id: msg.product_id,
|
||||
shopping_cart_id: cart.id,
|
||||
quantity: msg.quantity,
|
||||
quantity_unit: msg.quantity_unit,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(res) => res.map_err(Into::into),
|
||||
_ => Err(Error::CantAddItem),
|
||||
}
|
||||
},
|
||||
passthrough Error::Db,
|
||||
Error::CantAddItem
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(actix::Message)]
|
||||
|
@ -9,7 +9,7 @@ use model::api::Config;
|
||||
#[macro_export]
|
||||
macro_rules! public_send_db {
|
||||
($db: expr, $msg: expr) => {{
|
||||
use crate::routes::PublicError;
|
||||
use $crate::routes::PublicError;
|
||||
|
||||
return match $db.send($msg).await {
|
||||
Ok(Ok(res)) => Ok(HttpResponse::Ok().json(res)),
|
||||
@ -59,8 +59,8 @@ async fn landing() -> HttpResponse {
|
||||
HttpResponse::NotImplemented().body("")
|
||||
}
|
||||
|
||||
#[get("/config.json")]
|
||||
async fn client_config(config: Data<SharedAppConfig>) -> Json<model::api::Config> {
|
||||
#[get("/config")]
|
||||
async fn client_config(config: Data<SharedAppConfig>) -> Json<Config> {
|
||||
let (optional_payment, currency) = {
|
||||
let lock = config.lock();
|
||||
let p = lock.payment();
|
||||
@ -76,6 +76,8 @@ async fn client_config(config: Data<SharedAppConfig>) -> Json<model::api::Config
|
||||
false => vec![model::PaymentMethod::PayU],
|
||||
},
|
||||
currency,
|
||||
shipping: false,
|
||||
shipping_methods: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@ -113,8 +115,8 @@ async fn svg(path: Path<String>) -> HttpResponse {
|
||||
|
||||
pub fn configure(config: &mut ServiceConfig) {
|
||||
config
|
||||
.service(client_config)
|
||||
.service(landing)
|
||||
.service(svg)
|
||||
.service(client_config)
|
||||
.configure(api_v1::configure);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ pub struct Config {
|
||||
pub coupons: bool,
|
||||
pub currency: String,
|
||||
pub shipping: bool,
|
||||
pub shipping_methods: Vec<String>,
|
||||
pub shipping_methods: Vec<ShippingMethod>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
|
@ -591,7 +591,7 @@ where
|
||||
{
|
||||
fn decode(
|
||||
value: <sqlx::Postgres as ::sqlx::database::HasValueRef<'r>>::ValueRef,
|
||||
) -> Result<Self, Box<dyn Error + 'static + Send + Sync>> {
|
||||
) -> Result<Self, Box<dyn ::std::error::Error + 'static + Send + Sync>> {
|
||||
let value = <i32 as ::sqlx::decode::Decode<'r, sqlx::Postgres>>::decode(value)?;
|
||||
Ok(Days(
|
||||
(0..7)
|
||||
@ -1099,10 +1099,12 @@ pub struct ProductPhoto {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShippingMethod {
|
||||
/// Build-in InPost shipping
|
||||
InPost,
|
||||
Custom(String),
|
||||
/// Shop owner will ship product manually
|
||||
Manual,
|
||||
}
|
||||
|
@ -12,3 +12,7 @@ backend = "http://localhost:8080/files"
|
||||
[[proxy]]
|
||||
rewrite = "/svg"
|
||||
backend = "http://localhost:8080/svg"
|
||||
|
||||
[[proxy]]
|
||||
rewrite = "/config"
|
||||
backend = "http://localhost:8080/config"
|
||||
|
@ -4,7 +4,7 @@ use seed::fetch::{Header, Method, Request};
|
||||
use crate::api::perform;
|
||||
|
||||
pub async fn config() -> super::NetRes<model::api::Config> {
|
||||
perform(Request::new("/config.json").method(Method::Get)).await
|
||||
perform(Request::new("/config").method(Method::Get)).await
|
||||
}
|
||||
|
||||
pub async fn fetch_products() -> super::NetRes<model::api::Products> {
|
||||
|
@ -157,33 +157,38 @@ fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||
shared::update(msg, model, orders);
|
||||
}
|
||||
Msg::UrlChanged(subs::UrlChanged(url)) => model.page.page_changed(url, orders),
|
||||
Msg::Public(pages::public::Msg::Listing(msg)) => {
|
||||
Msg::Public(pages::public::PublicMsg::Listing(msg)) => {
|
||||
let page = fetch_page!(public model, Listing);
|
||||
pages::public::listing::update(msg, page, &mut orders.proxy(Into::into));
|
||||
}
|
||||
Msg::Public(pages::public::Msg::Product(msg)) => {
|
||||
Msg::Public(pages::public::PublicMsg::Product(msg)) => {
|
||||
let page = fetch_page!(public model, Product);
|
||||
pages::public::product::update(msg, page, &mut orders.proxy(Into::into))
|
||||
}
|
||||
Msg::Public(pages::public::Msg::SignIn(msg)) => {
|
||||
Msg::Public(pages::public::PublicMsg::SignIn(msg)) => {
|
||||
let page = fetch_page!(public model, SignIn);
|
||||
pages::public::sign_in::update(msg, page, &mut orders.proxy(Into::into))
|
||||
}
|
||||
Msg::Public(pages::public::Msg::SignUp(
|
||||
Msg::Public(pages::public::PublicMsg::SignUp(
|
||||
pages::public::sign_up::RegisterMsg::AccountCreated(res),
|
||||
)) => {
|
||||
orders
|
||||
.skip()
|
||||
.send_msg(Msg::Session(SessionMsg::TokenRefreshed(res)));
|
||||
}
|
||||
Msg::Public(pages::public::Msg::SignUp(msg)) => {
|
||||
Msg::Public(pages::public::PublicMsg::SignUp(msg)) => {
|
||||
let page = fetch_page!(public model, SignUp);
|
||||
pages::public::sign_up::update(msg, page, &mut orders.proxy(Into::into))
|
||||
}
|
||||
Msg::Public(pages::public::Msg::ShoppingCart(msg)) => {
|
||||
Msg::Public(pages::public::PublicMsg::ShoppingCart(msg)) => {
|
||||
let page = fetch_page!(public model, ShoppingCart);
|
||||
pages::public::shopping_cart::update(msg, page, &mut orders.proxy(Into::into))
|
||||
}
|
||||
Msg::Public(pages::public::PublicMsg::Checkout(msg)) => {
|
||||
let page = fetch_page!(public model, Checkout);
|
||||
pages::public::checkout::update(msg, page, &mut orders.proxy(Into::into))
|
||||
}
|
||||
// Admin
|
||||
Msg::Admin(pages::admin::Msg::Landing(msg)) => {
|
||||
let page = fetch_page!(admin model, Landing);
|
||||
pages::admin::landing::update(msg, page, &mut orders.proxy(Into::into))
|
||||
@ -210,6 +215,7 @@ fn view(model: &Model) -> Node<Msg> {
|
||||
Page::Public(PublicPage::ShoppingCart(page)) => {
|
||||
pages::public::shopping_cart::view(model, page)
|
||||
}
|
||||
Page::Public(PublicPage::Checkout(page)) => pages::public::checkout::view(model, page),
|
||||
Page::Admin(AdminPage::Landing(page)) => pages::admin::landing::view(model, page),
|
||||
_ => empty![],
|
||||
};
|
||||
|
@ -24,6 +24,8 @@ pub struct Config {
|
||||
pub pay_methods: Vec<::model::PaymentMethod>,
|
||||
pub coupons: bool,
|
||||
pub currency: &'static rusty_money::iso::Currency,
|
||||
pub shipping: bool,
|
||||
pub shipping_methods: Vec<model::ShippingMethod>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -32,6 +34,8 @@ impl Default for Config {
|
||||
pay_methods: vec![],
|
||||
coupons: false,
|
||||
currency: rusty_money::iso::PLN,
|
||||
shipping: false,
|
||||
shipping_methods: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -42,12 +46,16 @@ impl From<::model::api::Config> for Config {
|
||||
pay_methods,
|
||||
coupons,
|
||||
currency,
|
||||
shipping,
|
||||
shipping_methods,
|
||||
}: model::api::Config,
|
||||
) -> Self {
|
||||
Self {
|
||||
pay_methods,
|
||||
coupons,
|
||||
currency: rusty_money::iso::find(¤cy).unwrap_or(rusty_money::iso::PLN),
|
||||
shipping,
|
||||
shipping_methods,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,40 @@ use seed::{struct_urls, Url};
|
||||
|
||||
use crate::{shared, NetRes};
|
||||
|
||||
/// Implement msg conversion for
|
||||
///
|
||||
/// Examples
|
||||
///
|
||||
/// ```
|
||||
/// use crate::pages::*;
|
||||
/// use crate::pages::public::*;
|
||||
/// use crate::pages::public::checkout::*;
|
||||
///
|
||||
/// impl_into_msg!(ShoppingCartMsg > PublicMsg as ShoppingCart > Public);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! impl_into_msg {
|
||||
($msg: ty > $scoped_msg: ty as $scope_name: ident > $scope: ident) => {
|
||||
impl From<$msg> for $crate::Msg {
|
||||
fn from(msg: $msg) -> $crate::Msg {
|
||||
$crate::Msg::$scope(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$msg> for $scoped_msg {
|
||||
fn from(msg: $msg) -> $scoped_msg {
|
||||
<$scoped_msg>::$scope_name(msg)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Msg {
|
||||
#[cfg(debug_assertions)]
|
||||
NoOp,
|
||||
Config(NetRes<::model::api::Config>),
|
||||
Public(public::Msg),
|
||||
Public(public::PublicMsg),
|
||||
Admin(admin::Msg),
|
||||
UrlChanged(subs::UrlChanged),
|
||||
Shared(shared::SharedMsg),
|
||||
@ -36,7 +64,7 @@ pub enum PublicPage {
|
||||
SignIn(public::sign_in::SignInPage),
|
||||
SignUp(public::sign_up::SignUpPage),
|
||||
ShoppingCart(public::shopping_cart::ShoppingCartPage),
|
||||
Checkout,
|
||||
Checkout(public::checkout::CheckoutPage),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -63,6 +91,10 @@ impl Page {
|
||||
["shopping-cart", _rest @ ..] => Self::Public(PublicPage::ShoppingCart(
|
||||
public::shopping_cart::init(url, &mut orders.proxy(Into::into)),
|
||||
)),
|
||||
["checkout", _rest @ ..] => Self::Public(PublicPage::Checkout(public::checkout::init(
|
||||
url,
|
||||
&mut orders.proxy(Into::into),
|
||||
))),
|
||||
["sign-in", _rest @ ..] => Self::Public(PublicPage::SignIn(public::sign_in::init(
|
||||
url,
|
||||
&mut orders.proxy(Into::into),
|
||||
@ -101,6 +133,10 @@ impl Page {
|
||||
crate::fetch_page!(public page self, ShoppingCart, Page::init(url, orders));
|
||||
public::shopping_cart::page_changed(url, page);
|
||||
}
|
||||
["checkout", _rest @ ..] => {
|
||||
let page = crate::fetch_page!(public page self, Checkout, Page::init(url, orders));
|
||||
public::checkout::page_changed(url, page);
|
||||
}
|
||||
["sign-in", ..] => {
|
||||
let page = crate::fetch_page!(public page self, SignIn, Page::init(url, orders));
|
||||
public::sign_in::page_changed(url, page);
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::impl_into_msg;
|
||||
|
||||
pub mod checkout;
|
||||
pub mod listing;
|
||||
pub mod product;
|
||||
@ -6,79 +8,21 @@ pub mod sign_in;
|
||||
pub mod sign_up;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Msg {
|
||||
pub enum PublicMsg {
|
||||
Listing(listing::ListingMsg),
|
||||
Product(product::ProductMsg),
|
||||
SignIn(sign_in::LogInMsg),
|
||||
SignUp(sign_up::RegisterMsg),
|
||||
ShoppingCart(shopping_cart::ShoppingCartMsg),
|
||||
Checkout(checkout::CheckoutMsg),
|
||||
}
|
||||
|
||||
impl From<listing::ListingMsg> for Msg {
|
||||
fn from(msg: listing::ListingMsg) -> Self {
|
||||
Self::Listing(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<product::ProductMsg> for Msg {
|
||||
fn from(msg: product::ProductMsg) -> Self {
|
||||
Self::Product(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sign_in::LogInMsg> for Msg {
|
||||
fn from(msg: sign_in::LogInMsg) -> Self {
|
||||
Self::SignIn(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sign_up::RegisterMsg> for Msg {
|
||||
fn from(msg: sign_up::RegisterMsg) -> Self {
|
||||
Self::SignUp(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<shopping_cart::ShoppingCartMsg> for Msg {
|
||||
fn from(msg: shopping_cart::ShoppingCartMsg) -> Self {
|
||||
Self::ShoppingCart(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<listing::ListingMsg> for crate::Msg {
|
||||
fn from(msg: listing::ListingMsg) -> Self {
|
||||
crate::Msg::Public(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<product::ProductMsg> for crate::Msg {
|
||||
fn from(msg: product::ProductMsg) -> Self {
|
||||
crate::Msg::Public(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sign_in::LogInMsg> for crate::Msg {
|
||||
fn from(msg: sign_in::LogInMsg) -> Self {
|
||||
crate::Msg::Public(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sign_up::RegisterMsg> for crate::Msg {
|
||||
fn from(msg: sign_up::RegisterMsg) -> Self {
|
||||
crate::Msg::Public(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<shopping_cart::ShoppingCartMsg> for crate::Msg {
|
||||
fn from(msg: shopping_cart::ShoppingCartMsg) -> Self {
|
||||
Self::Public(msg.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Msg> for crate::Msg {
|
||||
fn from(msg: Msg) -> Self {
|
||||
crate::Msg::Public(msg)
|
||||
}
|
||||
}
|
||||
impl_into_msg!(listing::ListingMsg > PublicMsg as Listing > Public);
|
||||
impl_into_msg!(product::ProductMsg > PublicMsg as Product > Public);
|
||||
impl_into_msg!(sign_in::LogInMsg > PublicMsg as SignIn > Public);
|
||||
impl_into_msg!(sign_up::RegisterMsg > PublicMsg as SignUp > Public);
|
||||
impl_into_msg!(shopping_cart::ShoppingCartMsg > PublicMsg as ShoppingCart > Public);
|
||||
impl_into_msg!(checkout::CheckoutMsg > PublicMsg as Checkout > Public);
|
||||
|
||||
pub mod layout {
|
||||
use seed::prelude::*;
|
||||
|
@ -0,0 +1,414 @@
|
||||
use seed::prelude::*;
|
||||
use seed::*;
|
||||
|
||||
use crate::model::Products;
|
||||
use crate::NetRes;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CheckoutMsg {
|
||||
ProductsFetched(NetRes<model::api::Products>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CheckoutPage {
|
||||
pub products: Products,
|
||||
}
|
||||
|
||||
pub fn init(_url: Url, orders: &mut impl Orders<crate::Msg>) -> CheckoutPage {
|
||||
orders.perform_cmd(async move {
|
||||
crate::Msg::from(CheckoutMsg::ProductsFetched(
|
||||
crate::api::public::fetch_products().await,
|
||||
))
|
||||
});
|
||||
CheckoutPage {
|
||||
products: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn page_changed(_url: Url, _model: &mut CheckoutPage) {}
|
||||
|
||||
pub fn update(msg: CheckoutMsg, model: &mut CheckoutPage, _orders: &mut impl Orders<crate::Msg>) {
|
||||
match msg {
|
||||
CheckoutMsg::ProductsFetched(NetRes::Success(products)) => {
|
||||
model.products.update(products.0);
|
||||
}
|
||||
CheckoutMsg::ProductsFetched(NetRes::Error(e)) => {
|
||||
seed::error!("fetch product error", e);
|
||||
}
|
||||
CheckoutMsg::ProductsFetched(NetRes::Http(e)) => {
|
||||
seed::error!("fetch product http", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view(model: &crate::Model, page: &CheckoutPage) -> Node<crate::Msg> {
|
||||
let content = div![
|
||||
C!["flex justify-center my-6"],
|
||||
div![
|
||||
C!["flex flex-col w-full p-8 text-gray-800 bg-white shadow-lg pin-r pin-y md:w-4/5 lg:w-4/5"],
|
||||
div![C!["-mx-3 md:flex items-start"], left_side::view(model, page), right_side::view(model, page)]
|
||||
]
|
||||
];
|
||||
|
||||
div![
|
||||
crate::shared::view::public_navbar::view(model, &page.products),
|
||||
super::layout::view(model, content, None)
|
||||
]
|
||||
}
|
||||
|
||||
mod left_side {
|
||||
use rusty_money::Money;
|
||||
use seed::prelude::*;
|
||||
use seed::*;
|
||||
|
||||
use crate::pages::public::checkout::CheckoutPage;
|
||||
use crate::shopping_cart::Item;
|
||||
use crate::Msg;
|
||||
|
||||
pub fn view(model: &crate::Model, page: &CheckoutPage) -> Node<Msg> {
|
||||
div![
|
||||
C!["px-3 md:w-7/12 lg:pr-10"],
|
||||
products(model, page),
|
||||
total(model, page)
|
||||
]
|
||||
}
|
||||
|
||||
fn products(model: &crate::Model, page: &CheckoutPage) -> Node<Msg> {
|
||||
let products = model
|
||||
.cart
|
||||
.items
|
||||
.values()
|
||||
.filter_map(|item: &Item| {
|
||||
page.products
|
||||
.products
|
||||
.get(&item.product_id)
|
||||
.map(|product| (item, product))
|
||||
})
|
||||
.map(|(item, product)| product_view(model, product, 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> {
|
||||
let img = product
|
||||
.photos
|
||||
.first()
|
||||
.as_ref()
|
||||
.map(|photo| photo.url.as_str())
|
||||
.unwrap_or_default();
|
||||
let price = Money::from_minor(
|
||||
**(product.price * item.quantity) as i64,
|
||||
model.config.currency,
|
||||
)
|
||||
.to_string();
|
||||
let split = if price.contains(',') { ',' } else { '.' };
|
||||
let mut price_parts = price.split(split);
|
||||
|
||||
div![
|
||||
C!["w-full flex items-center"],
|
||||
div![
|
||||
C!["overflow-hidden rounded-lg w-16 h-16 bg-gray-50 border border-gray-200"],
|
||||
img![attrs![At::Src => img]]
|
||||
],
|
||||
div![
|
||||
C!["flex-grow pl-3"],
|
||||
h6![
|
||||
C!["font-semibold uppercase text-gray-600"],
|
||||
product.name.as_str()
|
||||
],
|
||||
p![C!["text-gray-400"], "x ", **item.quantity]
|
||||
],
|
||||
div![
|
||||
span![
|
||||
C!["font-semibold text-gray-600 text-xl"],
|
||||
price_parts.next()
|
||||
],
|
||||
span![
|
||||
C!["font-semibold text-gray-600 text-sm"],
|
||||
split.to_string(),
|
||||
price_parts.next().unwrap_or("00")
|
||||
],
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
fn total(model: &crate::Model, page: &CheckoutPage) -> Node<Msg> {
|
||||
let subtotal_value = model
|
||||
.cart
|
||||
.items
|
||||
.values()
|
||||
.filter_map(|item: &crate::shopping_cart::Item| {
|
||||
page.products
|
||||
.products
|
||||
.get(&item.product_id)
|
||||
.map(|product| (item, product))
|
||||
})
|
||||
.map(
|
||||
|(item, product): (&crate::shopping_cart::Item, &model::api::Product)| {
|
||||
**(product.price * item.quantity)
|
||||
},
|
||||
)
|
||||
.sum::<i32>() as i64;
|
||||
|
||||
// TODO: Shipping
|
||||
let shipping_cost = 0;
|
||||
|
||||
let total = subtotal_value + shipping_cost;
|
||||
|
||||
div![
|
||||
C!["mb-6 pb-6 border-b border-gray-200 md:border-none text-gray-800 text-xl"],
|
||||
div![
|
||||
C!["w-full flex items-center"],
|
||||
div![
|
||||
C!["flex-grow"],
|
||||
span![C!["text-gray-600"], model.i18n.t("Total")]
|
||||
],
|
||||
div![
|
||||
C!["pl-3"],
|
||||
span![
|
||||
C!["font-semibold"],
|
||||
Money::from_minor(total, model.config.currency).to_string()
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
mod right_side {
|
||||
use model::PaymentMethod;
|
||||
use seed::prelude::*;
|
||||
use seed::*;
|
||||
|
||||
use crate::pages::public::checkout::CheckoutPage;
|
||||
use crate::Msg;
|
||||
|
||||
static SELECTED_PAYMENT_METHOD: &str = "payment-selected-method";
|
||||
|
||||
pub fn view(model: &crate::Model, page: &CheckoutPage) -> Node<Msg> {
|
||||
let pay_methods = model.config.pay_methods.iter().map(|ty| match ty {
|
||||
PaymentMethod::PayU => pay_u(model),
|
||||
PaymentMethod::PaymentOnTheSpot => pay_on_spot(model),
|
||||
});
|
||||
div![
|
||||
C!["px-3 md:w-5/12"],
|
||||
contact(model, page),
|
||||
div![
|
||||
C!["w-full mx-auto rounded-lg bg-white border border-gray-200 text-gray-800 font-light mb-6"],
|
||||
pay_methods
|
||||
],
|
||||
pay_now(model)
|
||||
]
|
||||
}
|
||||
|
||||
fn contact(model: &crate::Model, _page: &CheckoutPage) -> Node<Msg> {
|
||||
if model.shared.me.is_some() {
|
||||
// TODO: Display user addresses
|
||||
return empty![];
|
||||
}
|
||||
div![
|
||||
C!["w-full mx-auto rounded-lg bg-white border border-gray-200 p-3 text-gray-800 font-light mb-6"],
|
||||
form![
|
||||
div![
|
||||
C!["mb-3"],
|
||||
div![C!["text-gray-600 font-semibold text-sm mb-2 ml-1"], model.i18n.t("Contact")],
|
||||
],
|
||||
div![
|
||||
C!["mb-3"],
|
||||
div![
|
||||
input![
|
||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
||||
attrs![At::Id => "client-name", At::Placeholder => model.i18n.t("Name")]
|
||||
]
|
||||
],
|
||||
],
|
||||
div![
|
||||
C!["mb-3"],
|
||||
div![
|
||||
input![
|
||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
||||
attrs![At::Id => "client-email", At::Type => "email", At::Placeholder => model.i18n.t("E-Mail")]
|
||||
]
|
||||
],
|
||||
],
|
||||
div![
|
||||
C!["mb-3"],
|
||||
div![C!["text-gray-600 font-semibold text-sm mb-2 ml-1"], model.i18n.t("Address")],
|
||||
],
|
||||
div![
|
||||
C!["mb-3"],
|
||||
input![
|
||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
||||
attrs![At::Id => "client-street", At::Placeholder => model.i18n.t("Street")]
|
||||
]
|
||||
],
|
||||
div![
|
||||
C!["mb-3"],
|
||||
input![
|
||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
||||
attrs![At::Id => "client-city", At::Placeholder => model.i18n.t("City")]
|
||||
]
|
||||
],
|
||||
div![
|
||||
C!["mb-3 inline-block w-1/2 pr-1"],
|
||||
input![
|
||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
||||
attrs![At::Id => "client-country", At::Placeholder => model.i18n.t("Country")]
|
||||
]
|
||||
],
|
||||
div![
|
||||
C!["mb-3 inline-block -mx-1 pl-1 w-1/2"],
|
||||
input![
|
||||
C!["w-full px-3 py-2 mb-1 border border-gray-200 rounded-md focus:outline-none focus:border-indigo-500 transition-colors"],
|
||||
attrs![At::Id => "client-zip", At::Placeholder => model.i18n.t("Zip")]
|
||||
]
|
||||
],
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
fn pay_now(model: &crate::Model) -> Node<Msg> {
|
||||
div![
|
||||
button![
|
||||
C!["w-full max-w-xs mx-auto bg-indigo-500 hover:bg-indigo-700 focus:bg-indigo-700 text-white rounded-lg px-3 py-2 font-semibold text-center flex justify-center"],
|
||||
padlock_icon(),
|
||||
model.i18n.t("PAY NOW"),
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
fn padlock_icon() -> Node<Msg> {
|
||||
svg![
|
||||
attrs![At::Xmlns => "http://www.w3.org/2000/svg", At::ViewBox => "0 0 32 32"],
|
||||
C!["fill-white h-6 mr-3"],
|
||||
path![
|
||||
attrs![At::D => "M25 13H7a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V15a2 2 0 0 0-2-2zM7 28V15h18v13zm2-17a1 1 0 0 0 1-1V8a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2a1 1 0 0 0 2 0V8a6 6 0 0 0-6-6h-4a6 6 0 0 0-6 6v2a1 1 0 0 0 1 1z"]
|
||||
],
|
||||
path![attrs![At::D => "M16 18a1 1 0 0 0-1 1v6a1 1 0 0 0 2 0v-6a1 1 0 0 0-1-1Z"]],
|
||||
]
|
||||
}
|
||||
|
||||
fn pay_u(model: &crate::Model) -> Node<Msg> {
|
||||
div![
|
||||
C!["w-full p-3 border-b border-gray-200"],
|
||||
label![
|
||||
C!["flex items-center cursor-pointer"],
|
||||
attrs![At::For => "pay_u"],
|
||||
input![
|
||||
C!["form-radio h-5 w-5 text-indigo-500"],
|
||||
attrs![At::Id => "pay_u", At::Name => SELECTED_PAYMENT_METHOD, At::Type => "radio", At::Value => "pay_u"],
|
||||
],
|
||||
span![
|
||||
C!["flex items-center"],
|
||||
pay_u_icon(),
|
||||
span![C!["ml-3"], model.i18n.t("PayU")]
|
||||
]
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
fn pay_u_icon() -> Node<Msg> {
|
||||
svg![
|
||||
C!["ml-3"],
|
||||
attrs![
|
||||
At::Xmlns => "http://www.w3.org/2000/svg",
|
||||
At::ViewBox => "0 0 120 76",
|
||||
At::Style => "height: 21px; width: 33px;",
|
||||
],
|
||||
g![
|
||||
attrs![
|
||||
At::Fill => "none",
|
||||
At::FillRule => "evenodd"
|
||||
],
|
||||
path![attrs![
|
||||
At::Fill => "#1AAF5D",
|
||||
At::D => "M111.999 0H8C3.582 0 0 3.59 0 8.008v59.984C0 72.415 3.591 76 8.001 76H112c4.419 0 8.001-3.59 8.001-8.008V8.008C120 3.585 116.409 0 111.999 0Z"
|
||||
]],
|
||||
path![attrs![
|
||||
At::D => "M89.794 27.902H88.57a2.243 2.243 0 0 0-2.242 2.243V44.22s-.116 2.447-4.181 2.447c-4.066 0-4.181-2.447-4.181-2.447V30.145a2.243 2.243 0 0 0-2.242-2.243h-2.451a2.243 2.243 0 0 0-2.242 2.242V43.61c0 4.841 3.927 8.768 8.77 8.768h4.691a8.77 8.77 0 0 0 8.77-8.768v-10.2h2.653a1.02 1.02 0 0 0 1.019-1.019v-5.1a1.02 1.02 0 0 0-1.019-1.02h-5.1a1.02 1.02 0 0 0-1.02 1.02v.612zm0 0h1.226a2.244 2.244 0 0 1 2.242 2.242v3.265h-2.449a1.02 1.02 0 0 1-1.019-1.019v-4.488zm6.935-6.118c0-.45.365-.817.817-.817h3.67c.45 0 .816.365.816.817v3.67a.816.816 0 0 1-.817.816h-3.669a.816.816 0 0 1-.817-.817v-3.669zm-3.467-3.674c0-.337.276-.61.61-.61h2.45c.338 0 .611.276.611.61v2.45c0 .338-.276.611-.61.611h-2.45a.612.612 0 0 1-.611-.61v-2.45zM61.089 52.382a36.48 36.48 0 0 1-.463.073c-3.95.513-4.733-2.421-5.419-4.66L51.767 35c-.175-.654.229-1.183.907-1.183h.815c.676 0 1.366.528 1.542 1.182l3.443 12.809c.373.945.85 1.82 1.883 1.82 1.035 0 1.577-.214 2.05-1.977l2.986-12.643c.156-.658.83-1.191 1.507-1.191h.802c.677 0 1.095.534.935 1.19l-3.948 16.17c-1.817 7.135-4.153 8.173-7.927 8.323-1.152-.15-1.745-2.488-.805-2.866 0 0 1.902.013 3.002-.784 1.188-.862 1.693-2.24 1.968-3.026.016-.003.143-.397.162-.442zM47.37 41.363h-6.218a5.406 5.406 0 1 0 0 10.81h4.075a5.406 5.406 0 0 0 5.407-5.405v-7.852c0-5.507-5.92-5.507-7.547-5.507-2.104 0-4.697.318-4.697.318a.93.93 0 0 0-.81.904v1.227c0 .45.363.776.81.729 0 0 2.844-.322 4.539-.322 1.62 0 4.441 0 4.441 2.65v2.448zm-8.362 5.303a2.85 2.85 0 0 1 2.854-2.855h5.508v2.855a2.85 2.85 0 0 1-2.854 2.856h-2.654a2.857 2.857 0 0 1-2.854-2.856zM18 31.37a3.467 3.467 0 0 1 3.469-3.468h7.545a6.73 6.73 0 0 1 6.73 6.729v2.247a6.73 6.73 0 0 1-6.73 6.73h-7.547v7.545a1.02 1.02 0 0 1-1.021 1.02h-1.424A1.021 1.021 0 0 1 18 51.153V31.37zm3.467.795c0-.552.446-1 1.002-1h5.421a4.59 4.59 0 1 1 0 9.179h-6.423v-8.179z",
|
||||
At::Fill => "#FFF"
|
||||
]]
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
fn pay_on_spot(model: &crate::Model) -> Node<Msg> {
|
||||
div![
|
||||
C!["w-full p-3 border-b border-gray-200"],
|
||||
label![
|
||||
C!["flex items-center cursor-pointer"],
|
||||
attrs![At::For => "pay_on_spot"],
|
||||
input![
|
||||
C!["form-radio h-5 w-5 text-indigo-500"],
|
||||
attrs![At::Id => "pay_on_spot", At::Name => SELECTED_PAYMENT_METHOD, At::Type => "radio", At::Value => "pay_on_spot"],
|
||||
],
|
||||
span![
|
||||
C!["flex items-center"],
|
||||
pay_in_spot_icon(),
|
||||
span![C!["ml-3"], model.i18n.t("Pay on spot")]
|
||||
]
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
fn pay_in_spot_icon() -> Node<Msg> {
|
||||
svg![
|
||||
attrs![
|
||||
At::Style => "height: 21px; width: 33px;",
|
||||
At::ViewBox => "0 0 31 17",
|
||||
At::Xmlns => "http://www.w3.org/2000/svg"
|
||||
],
|
||||
C!["ml-3"],
|
||||
g![
|
||||
attrs!["transform" => "translate(120.5 -1344.862)"],
|
||||
rect![attrs![
|
||||
At::Width => "31" ,
|
||||
At::Height => "17",
|
||||
At::X => "-120.5",
|
||||
At::Y => "1344.862",
|
||||
At::Fill => "#2dbca4",
|
||||
At::Rx => "1.5",
|
||||
At::Ry => "1.5"
|
||||
]],
|
||||
path![attrs![
|
||||
At::Fill => "#15a78f",
|
||||
At::D => "M-115.5 1346.862a3 3 0 0 1-3 3v7a3 3 0 0 1 3 3h21a3 3 0 0 1 3-3v-7a3 3 0 0 1-3-3z"
|
||||
]],
|
||||
circle![attrs![
|
||||
At::Cx => "-115",
|
||||
At::Cy => "1353.362",
|
||||
At::R => ".5",
|
||||
At::Fill => "#2dbca4",
|
||||
At::Stroke => "#0c5286",
|
||||
At::StrokeLinecap => "round",
|
||||
"stroke-miterlimit" => "3.7"
|
||||
]],
|
||||
circle![attrs![
|
||||
At::Cx => "-95",
|
||||
At::Cy => "1353.362",
|
||||
At::R => ".5",
|
||||
At::Fill => "#2dbca4",
|
||||
At::Stroke => "#0c5286",
|
||||
At::StrokeLinecap => "round",
|
||||
"stroke-miterlimit" => "3.7"
|
||||
]],
|
||||
path![attrs![
|
||||
At::Fill => "#2dbca4",
|
||||
At::D => "M-99.5 1353.362c0 3.036-2.474 5.5-5.516 5.5a5.484 5.484 0 0 1-5.484-5.5c0-3.035 2.44-5.5 5.484-5.5 3.042 0 5.516 2.465 5.516 5.5z"
|
||||
]],
|
||||
path![attrs![
|
||||
At::Fill => "#0c5286",
|
||||
At::Style => "line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;isolation:auto;mix-blend-mode:normal",
|
||||
At::D => "M-105.49 327.998v1.191a2.476 2.476 0 0 0-1.065.442c-.701.495-1.072 1.398-.812 2.256.297 1.033 1.312 1.631 2.31 1.613h.121a.5.5 0 0 0 .012 0c.571-.011 1.188.372 1.336.893a.5.5 0 0 0 .002.011c.122.398-.075.908-.432 1.157a.5.5 0 0 0-.015.01c-.6.448-1.647.327-2.082-.28a1.051 1.051 0 0 1-.201-.607h-1c-.001.433.142.85.39 1.195a2.36 2.36 0 0 0 1.436.92V338h1v-1.183a2.476 2.476 0 0 0 1.047-.436c.703-.494 1.074-1.397.814-2.256-.297-1.034-1.316-1.631-2.314-1.613h-.12a.5.5 0 0 0-.011 0c-.571.011-1.186-.372-1.334-.893a.5.5 0 0 0-.002-.011c-.122-.398.075-.908.432-1.157a.5.5 0 0 0 .013-.011c.6-.449 1.646-.326 2.08.281a.5.5 0 0 0 .002 0c.129.177.202.397.201.607h1a2.03 2.03 0 0 0-.388-1.195 2.36 2.36 0 0 0-1.42-.916v-1.219z",
|
||||
At::Color => "#000",
|
||||
At::FontFamily => "sans-serif",
|
||||
At::FontWeight => "400",
|
||||
At::Overflow => "visible",
|
||||
At::Transform => "translate(0 1020.362)",
|
||||
]],
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
@ -70,12 +70,18 @@ pub fn view(model: &crate::Model, page: &ShoppingCartPage) -> Node<crate::Msg> {
|
||||
fn summary(model: &crate::Model, page: &ShoppingCartPage) -> Node<crate::Msg> {
|
||||
div![
|
||||
C!["my-4 mt-6 -mx-2 lg:flex"],
|
||||
summary_left(model, page),
|
||||
summary_left::view(model, page),
|
||||
summary_right::view(model, page)
|
||||
]
|
||||
}
|
||||
|
||||
fn summary_left(model: &crate::Model, page: &ShoppingCartPage) -> Node<crate::Msg> {
|
||||
mod summary_left {
|
||||
use seed::prelude::*;
|
||||
use seed::*;
|
||||
|
||||
use crate::pages::public::shopping_cart::ShoppingCartPage;
|
||||
|
||||
pub fn view(model: &crate::Model, page: &ShoppingCartPage) -> Node<crate::Msg> {
|
||||
div![
|
||||
C!["lg:px-2 lg:w-1/2"],
|
||||
IF![model.config.coupons => div![C!["p-4 bg-gray-100 rounded-full"], h1![C!["ml-2 font-bold uppercase"], model.i18n.t("Coupon Code")]]],
|
||||
@ -90,9 +96,9 @@ fn summary_left(model: &crate::Model, page: &ShoppingCartPage) -> Node<crate::Ms
|
||||
div![C!["p-4"], p![C!["mb-4 italic"], model.i18n.t("If you have some information for the seller you can leave them in the box below")]],
|
||||
textarea![C!["w-full h-24 p-2 bg-gray-100 rounded border-none"]]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn coupon_form(model: &crate::Model, _page: &ShoppingCartPage) -> Node<crate::Msg> {
|
||||
fn coupon_form(model: &crate::Model, _page: &ShoppingCartPage) -> Node<crate::Msg> {
|
||||
div![
|
||||
C!["p-4"],
|
||||
p![
|
||||
@ -116,16 +122,16 @@ fn coupon_form(model: &crate::Model, _page: &ShoppingCartPage) -> Node<crate::Ms
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn gift_icon() -> Node<crate::Msg> {
|
||||
fn gift_icon() -> Node<crate::Msg> {
|
||||
svg![
|
||||
attrs![
|
||||
"aria-hidden" => "true",
|
||||
"data-prefix" => "fas",
|
||||
"data-icon" => "gift",
|
||||
"class" => "w-8",
|
||||
"xmlns" => "http://www.w3.org/2000/svg",
|
||||
At::Xmlns => "http://www.w3.org/2000/svg",
|
||||
"viewBox" => "0 0 512 512"
|
||||
],
|
||||
path![attrs![
|
||||
@ -133,6 +139,7 @@ fn gift_icon() -> Node<crate::Msg> {
|
||||
"d" => "M32 448c0 17.7 14.3 32 32 32h160V320H32v128zm256 32h160c17.7 0 32-14.3 32-32V320H288v160zm192-320h-42.1c6.2-12.1 10.1-25.5 10.1-40 0-48.5-39.5-88-88-88-41.6 0-68.5 21.3-103 68.3-34.5-47-61.4-68.3-103-68.3-48.5 0-88 39.5-88 88 0 14.5 3.8 27.9 10.1 40H32c-17.7 0-32 14.3-32 32v80c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-80c0-17.7-14.3-32-32-32zm-326.1 0c-22.1 0-40-17.9-40-40s17.9-40 40-40c19.9 0 34.6 3.3 86.1 80h-86.1zm206.1 0h-86.1c51.4-76.5 65.7-80 86.1-80 22.1 0 40 17.9 40 40s-17.9 40-40 40z"
|
||||
]]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
mod summary_right {
|
||||
@ -307,13 +314,13 @@ mod summary_right {
|
||||
"aria-hidden" => "true",
|
||||
"data-prefix" => "far",
|
||||
"data-icon" => "trash-alt",
|
||||
"class" => "w-4 text-red-600 hover:text-red-800",
|
||||
"xmlns" => "http://www.w3.org/2000/svg",
|
||||
"viewBox" => "0 0 448 512"
|
||||
At::Xmlns => "http://www.w3.org/2000/svg",
|
||||
At::ViewBox => "0 0 448 512"
|
||||
],
|
||||
C!["w-4 text-red-600 hover:text-red-800"],
|
||||
path![attrs![
|
||||
"fill" => "currentColor",
|
||||
"d" => "M268 416h24a12 12 0 0012-12V188a12 12 0 00-12-12h-24a12 12 0 00-12 12v216a12 12 0 0012 12zM432 80h-82.41l-34-56.7A48 48 0 00274.41 0H173.59a48 48 0 00-41.16 23.3L98.41 80H16A16 16 0 000 96v16a16 16 0 0016 16h16v336a48 48 0 0048 48h288a48 48 0 0048-48V128h16a16 16 0 0016-16V96a16 16 0 00-16-16zM171.84 50.91A6 6 0 01177 48h94a6 6 0 015.15 2.91L293.61 80H154.39zM368 464H80V128h288zm-212-48h24a12 12 0 0012-12V188a12 12 0 00-12-12h-24a12 12 0 00-12 12v216a12 12 0 0012 12z"
|
||||
At::Fill => "currentColor",
|
||||
At::D => "M268 416h24a12 12 0 0012-12V188a12 12 0 00-12-12h-24a12 12 0 00-12 12v216a12 12 0 0012 12zM432 80h-82.41l-34-56.7A48 48 0 00274.41 0H173.59a48 48 0 00-41.16 23.3L98.41 80H16A16 16 0 000 96v16a16 16 0 0016 16h16v336a48 48 0 0048 48h288a48 48 0 0048-48V128h16a16 16 0 0016-16V96a16 16 0 00-16-16zM171.84 50.91A6 6 0 01177 48h94a6 6 0 015.15 2.91L293.61 80H154.39zM368 464H80V128h288zm-212-48h24a12 12 0 0012-12V188a12 12 0 00-12-12h-24a12 12 0 00-12 12v216a12 12 0 0012 12z"
|
||||
]]
|
||||
]
|
||||
}
|
||||
@ -324,13 +331,13 @@ mod summary_right {
|
||||
"aria-hidden" => "true",
|
||||
"data-prefix" => "far",
|
||||
"data-icon" => "credit-card",
|
||||
"class" => "w-8",
|
||||
"xmlns" => "http://www.w3.org/2000/svg",
|
||||
"viewBox" => "0 0 576 512"
|
||||
At::Xmlns => "http://www.w3.org/2000/svg",
|
||||
At::ViewBox => "0 0 576 512"
|
||||
],
|
||||
C!["w-8"],
|
||||
path![attrs![
|
||||
"fill" => "currentColor",
|
||||
"d" => "M527.9 32H48.1C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48.1 48h479.8c26.6 0 48.1-21.5 48.1-48V80c0-26.5-21.5-48-48.1-48zM54.1 80h467.8c3.3 0 6 2.7 6 6v42H48.1V86c0-3.3 2.7-6 6-6zm467.8 352H54.1c-3.3 0-6-2.7-6-6V256h479.8v170c0 3.3-2.7 6-6 6zM192 332v40c0 6.6-5.4 12-12 12h-72c-6.6 0-12-5.4-12-12v-40c0-6.6 5.4-12 12-12h72c6.6 0 12 5.4 12 12zm192 0v40c0 6.6-5.4 12-12 12H236c-6.6 0-12-5.4-12-12v-40c0-6.6 5.4-12 12-12h136c6.6 0 12 5.4 12 12z"
|
||||
At::Fill => "currentColor",
|
||||
At::D => "M527.9 32H48.1C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48.1 48h479.8c26.6 0 48.1-21.5 48.1-48V80c0-26.5-21.5-48-48.1-48zM54.1 80h467.8c3.3 0 6 2.7 6 6v42H48.1V86c0-3.3 2.7-6 6-6zm467.8 352H54.1c-3.3 0-6-2.7-6-6V256h479.8v170c0 3.3-2.7 6-6 6zM192 332v40c0 6.6-5.4 12-12 12h-72c-6.6 0-12-5.4-12-12v-40c0-6.6 5.4-12 12-12h72c6.6 0 12 5.4 12 12zm192 0v40c0 6.6-5.4 12-12 12H236c-6.6 0-12-5.4-12-12v-40c0-6.6 5.4-12 12-12h136c6.6 0 12 5.4 12 12z"
|
||||
]]
|
||||
]
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ pub fn redirect_on_session(model: &Model, orders: &mut impl Orders<Msg>) {
|
||||
PublicPage::SignIn(_) => {}
|
||||
PublicPage::SignUp(_) => {}
|
||||
PublicPage::ShoppingCart(_) => {}
|
||||
PublicPage::Checkout => {}
|
||||
PublicPage::Checkout(_) => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ pub fn message(id: uuid::Uuid, message: &str, icon: Type) -> Node<Msg> {
|
||||
fn icon(color: &str, d: &str) -> Node<Msg> {
|
||||
svg![
|
||||
attrs![
|
||||
"xmlns"=>"http://www.w3.org/2000/svg",
|
||||
At::Xmlns=>"http://www.w3.org/2000/svg",
|
||||
"class" => "w-8 h-8",
|
||||
"viewBox" => "0 0 20 20",
|
||||
"fill" => "currentColor"
|
||||
@ -218,7 +218,7 @@ fn close_icon() -> Node<Msg> {
|
||||
C!["inline-flex items-center cursor-pointer"],
|
||||
svg![
|
||||
attrs![
|
||||
"xmlns" => "http://www.w3.org/2000/svg",
|
||||
At::Xmlns => "http://www.w3.org/2000/svg",
|
||||
"class" => "w-4 h-4 text-gray-600",
|
||||
"fill" => "none",
|
||||
"viewBox" => "0 0 24 24",
|
||||
|
@ -76,7 +76,7 @@ pub mod public_navbar {
|
||||
"width" => "32px",
|
||||
"height" => "32px",
|
||||
At::ViewBox => "0 0 32 32",
|
||||
"xmlns" => "http://www.w3.org/2000/svg",
|
||||
At::Xmlns => "http://www.w3.org/2000/svg",
|
||||
At::Class => "w-6 h-6",
|
||||
At::Fill => "none",
|
||||
At::Stroke => "currentColor",
|
||||
@ -91,7 +91,7 @@ pub mod public_navbar {
|
||||
fn account() -> Node<Msg> {
|
||||
svg![
|
||||
attrs![
|
||||
"xmlns" => "http://www.w3.org/2000/svg",
|
||||
At::Xmlns => "http://www.w3.org/2000/svg",
|
||||
At::Class => "w-6 h-6",
|
||||
At::Fill => "none",
|
||||
At::ViewBox => "0 0 24 24",
|
||||
@ -180,7 +180,7 @@ pub mod cart_dropdown {
|
||||
fn trash_icon() -> Node<Msg> {
|
||||
svg![
|
||||
attrs![
|
||||
"xmlns" => Namespace::Svg.as_str(),
|
||||
At::Xmlns => Namespace::Svg.as_str(),
|
||||
"width" => "100%",
|
||||
"height" => "100%",
|
||||
"fill" => "none",
|
||||
|
Loading…
Reference in New Issue
Block a user