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:
parent
ab800a519f
commit
cfd21adb60
48
app/frontend/src/javascript/components/cart/cart-button.tsx
Normal file
48
app/frontend/src/javascript/components/cart/cart-button.tsx
Normal 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));
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user