1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-30 19:52:20 +01:00

cart button

This commit is contained in:
Du Peng 2022-08-20 18:47:15 +02:00
parent ab800a519f
commit cfd21adb60
11 changed files with 119 additions and 15 deletions

View File

@ -0,0 +1,48 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { react2angular } from 'react2angular';
import { Loader } from '../base/loader';
import { IApplication } from '../../models/application';
import { Order } from '../../models/order';
import { useCustomEventListener } from 'react-custom-events';
declare const Application: IApplication;
/**
* This component shows my cart button
*/
const CartButton: React.FC = () => {
const { t } = useTranslation('public');
const [cart, setCart] = useState<Order>();
useCustomEventListener<Order>('CartUpdate', (data) => {
setCart(data);
});
/**
* Goto cart page
*/
const showCart = () => {
window.location.href = '/#!/cart';
};
if (cart) {
return (
<div className="cart-button" onClick={showCart}>
<i className="fas fa-cart-arrow-down" />
<span>{cart.order_items_attributes.length}</span>
<div>{t('app.public.cart_button.my_cart')}</div>
</div>
);
}
return null;
};
const CartButtonWrapper: React.FC = () => {
return (
<Loader>
<CartButton />
</Loader>
);
};
Application.Components.component('cartButton', react2angular(CartButtonWrapper));

View File

@ -20,7 +20,7 @@ interface StoreCartProps {
const StoreCart: React.FC<StoreCartProps> = ({ onError }) => {
const { t } = useTranslation('public');
const { loading, cart, setCart } = useCart();
const { cart, setCart } = useCart();
/**
* Remove the product from cart
@ -35,21 +35,46 @@ const StoreCart: React.FC<StoreCartProps> = ({ onError }) => {
};
};
/**
* Change product quantity
*/
const changeProductQuantity = (item) => {
return (e: React.BaseSyntheticEvent) => {
CartAPI.setQuantity(cart, item.orderable_id, e.target.value).then(data => {
setCart(data);
});
};
};
/**
* Checkout cart
*/
const checkout = () => {
console.log('checkout .....');
};
return (
<div className="store-cart">
{loading && <p>loading</p>}
{cart && cart.order_items_attributes.map(item => (
<div key={item.id}>
<div>{item.orderable_name}</div>
<div>{FormatLib.price(item.amount)}</div>
<div>{item.quantity}</div>
<select value={item.quantity} onChange={changeProductQuantity(item)}>
{Array.from({ length: 100 }, (_, i) => i + 1).map(v => (
<option key={v} value={v}>{v}</option>
))}
</select>
<div>{FormatLib.price(item.quantity * item.amount)}</div>
<FabButton className="delete-btn" onClick={removeProductFromCart(item)}>
<i className="fa fa-trash" /> {t('app.public.store_cart.remove_item')}
<i className="fa fa-trash" />
</FabButton>
</div>
))}
{cart && <p>{cart.amount}</p>}
{cart && <p>Totale: {FormatLib.price(cart.amount)}</p>}
<FabButton className="checkout-btn" onClick={checkout}>
{t('app.public.store_cart.checkout')}
</FabButton>
</div>
);
};

View File

@ -10,12 +10,13 @@ import CartAPI from '../../api/cart';
interface StoreProductItemProps {
product: Product,
cart: Order,
onSuccessAddProductToCart: (cart: Order) => void
}
/**
* This component shows a product item in store
*/
export const StoreProductItem: React.FC<StoreProductItemProps> = ({ product, cart }) => {
export const StoreProductItem: React.FC<StoreProductItemProps> = ({ product, cart, onSuccessAddProductToCart }) => {
const { t } = useTranslation('public');
/**
@ -33,6 +34,9 @@ export const StoreProductItem: React.FC<StoreProductItemProps> = ({ product, car
* Return product's stock status
*/
const productStockStatus = (product: Product) => {
if (product.stock.external === 0) {
return <span>{t('app.public.store_product_item.out_of_stock')}</span>;
}
if (product.low_stock_threshold && product.stock.external < product.low_stock_threshold) {
return <span>{t('app.public.store_product_item.limited_stock')}</span>;
}
@ -45,7 +49,7 @@ export const StoreProductItem: React.FC<StoreProductItemProps> = ({ product, car
const addProductToCart = (e: React.BaseSyntheticEvent) => {
e.preventDefault();
e.stopPropagation();
CartAPI.addItem(cart, product.id, 1);
CartAPI.addItem(cart, product.id, 1).then(onSuccessAddProductToCart);
};
/**
@ -66,9 +70,11 @@ export const StoreProductItem: React.FC<StoreProductItemProps> = ({ product, car
<div>{FormatLib.price(product.amount)}</div>
{productStockStatus(product)}
</span>
<FabButton className="edit-btn" onClick={addProductToCart}>
<i className="fas fa-cart-arrow-down" /> {t('app.public.store_product_item.add')}
</FabButton>
{product.stock.external > 0 &&
<FabButton className="edit-btn" onClick={addProductToCart}>
<i className="fas fa-cart-arrow-down" /> {t('app.public.store_product_item.add')}
</FabButton>
}
</div>
</div>
);

View File

@ -8,6 +8,7 @@ import { Product } from '../../models/product';
import ProductAPI from '../../api/product';
import { StoreProductItem } from './store-product-item';
import useCart from '../../hooks/use-cart';
import { emitCustomEvent } from 'react-custom-events';
declare const Application: IApplication;
@ -21,7 +22,7 @@ interface StoreProps {
const Store: React.FC<StoreProps> = ({ onError }) => {
const { t } = useTranslation('public');
const { cart } = useCart();
const { cart, setCart } = useCart();
const [products, setProducts] = useState<Array<Product>>([]);
@ -33,6 +34,10 @@ const Store: React.FC<StoreProps> = ({ onError }) => {
});
}, []);
useEffect(() => {
emitCustomEvent('CartUpdate', cart);
}, [cart]);
return (
<div className="store">
<div className='layout'>
@ -71,7 +76,7 @@ const Store: React.FC<StoreProps> = ({ onError }) => {
<div className="products">
{products.map((product) => (
<StoreProductItem key={product.id} product={product} cart={cart}/>
<StoreProductItem key={product.id} product={product} cart={cart} onSuccessAddProductToCart={setCart} />
))}
</div>
</div>

View File

@ -13,6 +13,7 @@
<div class="col-xs-12 col-sm-12 col-md-3 b-t hide-b-md">
<section class="heading-actions wrapper">
<cart-button />
</section>
</div>
</div>

View File

@ -3,6 +3,9 @@
# Category is a first-level filter, used to categorize Products.
# It is mandatory to choose a Category when creating a Product.
class ProductCategory < ApplicationRecord
extend FriendlyId
friendly_id :name, use: :slugged
validates :name, :slug, presence: true
belongs_to :parent, class_name: 'ProductCategory'

View File

@ -5,16 +5,16 @@ class Cart::SetQuantityService
def call(order, orderable, quantity = nil)
return order if quantity.to_i.zero?
raise Cart::OutStockError if quantity > orderable.stock['external']
raise Cart::OutStockError if quantity.to_i > orderable.stock['external']
item = order.order_items.find_by(orderable: orderable)
raise ActiveRecord::RecordNotFound if item.nil?
different_quantity = item.quantity - quantiy.to_i
different_quantity = quantity.to_i - item.quantity
order.amount += (orderable.amount * different_quantity)
ActiveRecord::Base.transaction do
item.update(quantity: quantity)
item.update(quantity: quantity.to_i)
order.save
end
order.reload

View File

@ -381,11 +381,16 @@ en:
store_product_item:
available: "Available"
limited_stock: "Limited stock"
out_of_stock: "Out of stock"
add: "Add"
store_product:
unexpected_error_occurred: "An unexpected error occurred. Please try again later."
cart:
my_cart: "My Cart"
cart_button:
my_cart: "My Cart"
store_cart:
checkout: "Checkout"
tour:
conclusion:
title: "Thank you for your attention"

View File

@ -381,11 +381,16 @@ fr:
store_product_item:
available: "Disponible"
limited_stock: "Stock limité"
out_of_stock: "Épuisé"
add: "Ajouter"
store_product:
unexpected_error_occurred: "An unexpected error occurred. Please try again later."
unexpected_error_occurred: "Une erreur inattendue s'est produite. Veuillez réessayer ultérieurement."
cart:
my_cart: "Mon Panier"
cart_button:
my_cart: "Mon Panier"
store_cart:
checkout: "Valider mon panier"
tour:
conclusion:
title: "Merci de votre attention"

View File

@ -138,6 +138,7 @@
"rails-erb-loader": "^5.5.2",
"react": "^17.0.2",
"react-cool-onclickoutside": "^1.7.0",
"react-custom-events": "^1.1.1",
"react-dom": "^17.0.2",
"react-hook-form": "^7.30.0",
"react-i18next": "^11.15.6",

View File

@ -6588,6 +6588,11 @@ react-cool-onclickoutside@^1.7.0:
resolved "https://registry.yarnpkg.com/react-cool-onclickoutside/-/react-cool-onclickoutside-1.7.0.tgz#abc844e14852220fe15f81d7ef44976d15cd9980"
integrity sha512-HVZK2155Unee+enpoHKyYP2UdQK69thw90XAOUCjvJBcgRSgfRPgWWt/W1dYzoGp3+nleAa8SJxF1d4FMA4Qmw==
react-custom-events@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/react-custom-events/-/react-custom-events-1.1.1.tgz#792f126e897043a14b9f27a4c5ab7072ff235ceb"
integrity sha512-71iEu3zHsBn3uvF+Sq4Fu5imtRt+cLZO6nG2zqUhdqGVIpZIfeLcl6yieqPghrE+18KFrS5BaHD0NBPP/EZJNw==
react-dom@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"