1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-02 22:52:21 +01:00

232 lines
7.5 KiB
TypeScript
Raw Normal View History

2022-08-25 16:23:00 +02:00
import React, { useState } from 'react';
2022-08-19 19:59:13 +02:00
import { useTranslation } from 'react-i18next';
import { react2angular } from 'react2angular';
import { Loader } from '../base/loader';
import { IApplication } from '../../models/application';
import { FabButton } from '../base/fab-button';
import useCart from '../../hooks/use-cart';
import FormatLib from '../../lib/format';
import CartAPI from '../../api/cart';
import { User } from '../../models/user';
2022-08-25 08:52:17 +02:00
import { PaymentModal } from '../payment/stripe/payment-modal';
import { PaymentMethod } from '../../models/payment';
import { Order } from '../../models/order';
import { MemberSelect } from '../user/member-select';
2022-08-25 20:50:15 +02:00
import { CouponInput } from '../coupon/coupon-input';
2022-08-26 20:10:21 +02:00
import { Coupon } from '../../models/coupon';
import { computePriceWithCoupon } from '../../lib/coupon';
2022-08-29 16:32:35 +02:00
import noImage from '../../../../images/no_image.png';
2022-08-19 19:59:13 +02:00
declare const Application: IApplication;
interface StoreCartProps {
2022-08-29 16:32:35 +02:00
onSuccess: (message: string) => void,
2022-08-19 19:59:13 +02:00
onError: (message: string) => void,
userLogin: () => void,
currentUser?: User
2022-08-19 19:59:13 +02:00
}
/**
* This component shows user's cart
*/
2022-08-29 16:32:35 +02:00
const StoreCart: React.FC<StoreCartProps> = ({ onSuccess, onError, currentUser, userLogin }) => {
2022-08-19 19:59:13 +02:00
const { t } = useTranslation('public');
2022-08-25 16:23:00 +02:00
const { cart, setCart } = useCart(currentUser);
2022-08-29 16:32:35 +02:00
console.log('cart: ', cart);
2022-08-25 08:52:17 +02:00
const [paymentModal, setPaymentModal] = useState<boolean>(false);
2022-08-19 19:59:13 +02:00
/**
* Remove the product from cart
*/
const removeProductFromCart = (item) => {
return (e: React.BaseSyntheticEvent) => {
e.preventDefault();
e.stopPropagation();
CartAPI.removeItem(cart, item.orderable_id).then(data => {
setCart(data);
2022-08-26 11:57:53 +02:00
}).catch(onError);
2022-08-19 19:59:13 +02:00
};
};
2022-08-20 18:47:15 +02:00
/**
* Change product quantity
*/
const changeProductQuantity = (item) => {
return (e: React.BaseSyntheticEvent) => {
CartAPI.setQuantity(cart, item.orderable_id, e.target.value).then(data => {
setCart(data);
2022-08-26 11:57:53 +02:00
}).catch(onError);
2022-08-20 18:47:15 +02:00
};
};
/**
* Checkout cart
*/
const checkout = () => {
if (!currentUser) {
userLogin();
} else {
setPaymentModal(true);
}
2022-08-25 08:52:17 +02:00
};
/**
* Open/closes the payment modal
*/
const togglePaymentModal = (): void => {
setPaymentModal(!paymentModal);
};
/**
2022-08-29 16:32:35 +02:00
* Handle payment
2022-08-25 08:52:17 +02:00
*/
const handlePaymentSuccess = (data: Order): void => {
if (data.payment_state === 'paid') {
setPaymentModal(false);
window.location.href = '/#!/store';
2022-08-29 16:32:35 +02:00
onSuccess(t('app.public.store_cart.checkout_success'));
} else {
2022-08-29 16:32:35 +02:00
onError(t('app.public.store_cart.checkout_error'));
}
2022-08-25 08:52:17 +02:00
};
/**
* Change cart's customer by admin/manger
*/
const handleChangeMember = (userId: number): void => {
setCart({ ...cart, user: { id: userId, role: 'member' } });
2022-08-25 08:52:17 +02:00
};
/**
* Check if the current operator has administrative rights or is a normal member
*/
const isPrivileged = (): boolean => {
return (currentUser?.role === 'admin' || currentUser?.role === 'manager');
2022-08-20 18:47:15 +02:00
};
2022-08-25 16:23:00 +02:00
/**
* Check if the current cart is empty ?
*/
const cartIsEmpty = (): boolean => {
return cart && cart.order_items_attributes.length === 0;
};
2022-08-26 20:10:21 +02:00
/**
* Apply coupon to current cart
*/
const applyCoupon = (coupon?: Coupon): void => {
if (coupon !== cart.coupon) {
setCart({ ...cart, coupon });
}
};
2022-08-29 16:32:35 +02:00
/**
* Get the offered item total
*/
const offeredAmount = (): number => {
return cart.order_items_attributes
.filter(i => i.is_offered)
.map(i => i.amount)
.reduce((acc, curr) => acc + curr, 0);
};
2022-08-19 19:59:13 +02:00
return (
2022-08-29 16:32:35 +02:00
<div className='store-cart'>
<div className="store-cart-list">
{cart && cartIsEmpty() && <p>{t('app.public.store_cart.cart_is_empty')}</p>}
{cart && cart.order_items_attributes.map(item => (
<article key={item.id} className='store-cart-list-item'>
<div className='picture'>
<img alt=''src={noImage} />
</div>
<div className="ref">
<span>ref: </span>
<p>{item.orderable_name}</p>
</div>
<div className="actions">
<div className='price'>
<p>{FormatLib.price(item.amount)}</p>
<span>/ {t('app.public.store_cart.unit')}</span>
</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 className='total'>
<span>{t('app.public.store_cart.total')}</span>
<p>{FormatLib.price(item.quantity * item.amount)}</p>
</div>
<FabButton className="main-action-btn" onClick={removeProductFromCart(item)}>
<i className="fa fa-trash" />
</FabButton>
</div>
</article>
))}
</div>
<div className="group">
<div className='store-cart-info'>
<h3>{t('app.public.store_cart.pickup')}</h3>
<p>[TODO: texte venant des paramètres de la boutique]</p>
2022-08-19 19:59:13 +02:00
</div>
2022-08-29 16:32:35 +02:00
{cart && !cartIsEmpty() && cart.user &&
<div className='store-cart-coupon'>
<CouponInput user={cart.user as User} amount={cart.total} onChange={applyCoupon} />
</div>
}
</div>
<aside>
{cart && !cartIsEmpty() && isPrivileged() &&
<div> <MemberSelect onSelected={handleChangeMember} /></div>
}
{cart && !cartIsEmpty() && <>
<div className="checkout">
<h3>{t('app.public.store_cart.checkout_header')}</h3>
<span>{t('app.public.store_cart.checkout_products_COUNT', { COUNT: cart?.order_items_attributes.length })}</span>
<div className="list">
<p>{t('app.public.store_cart.checkout_products_total')} <span>{FormatLib.price(cart.total)}</span></p>
{offeredAmount() > 0 &&
<p className='gift'>{t('app.public.store_cart.checkout_gift_total')} <span>-{FormatLib.price(offeredAmount())}</span></p>
}
{cart.coupon && computePriceWithCoupon(cart.total, cart.coupon) !== cart.total &&
<p>{t('app.public.store_cart.checkout_coupon')} <span>{FormatLib.price(-(cart.total - computePriceWithCoupon(cart.total, cart.coupon)))}</span></p>
}
</div>
<p className='total'>{t('app.public.store_cart.checkout_total')} <span>{FormatLib.price(computePriceWithCoupon(cart.total, cart.coupon))}</span></p>
</div>
<FabButton className='checkout-btn' onClick={checkout} disabled={!cart.user || cart.order_items_attributes.length === 0}>
{t('app.public.store_cart.checkout')}
</FabButton>
</>}
</aside>
2022-08-25 16:23:00 +02:00
{cart && !cartIsEmpty() && cart.user && <div>
2022-08-25 08:52:17 +02:00
<PaymentModal isOpen={paymentModal}
toggleModal={togglePaymentModal}
afterSuccess={handlePaymentSuccess}
onError={onError}
2022-08-25 16:23:00 +02:00
cart={{ customer_id: cart.user.id, items: [], payment_method: PaymentMethod.Card }}
2022-08-25 08:52:17 +02:00
order={cart}
operator={currentUser}
customer={cart.user as User}
2022-08-25 16:23:00 +02:00
updateCart={() => 'dont need update shopping cart'} />
2022-08-25 08:52:17 +02:00
</div>}
2022-08-19 19:59:13 +02:00
</div>
);
};
const StoreCartWrapper: React.FC<StoreCartProps> = (props) => {
2022-08-19 19:59:13 +02:00
return (
<Loader>
<StoreCart {...props} />
2022-08-19 19:59:13 +02:00
</Loader>
);
};
2022-08-29 16:32:35 +02:00
Application.Components.component('storeCart', react2angular(StoreCartWrapper, ['onSuccess', 'onError', 'currentUser', 'userLogin']));