mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-26 20:54:21 +01:00
(wip) cart items components
This commit is contained in:
parent
c24673fefa
commit
05a6f517cd
@ -0,0 +1,79 @@
|
||||
import * as React from 'react';
|
||||
import noImage from '../../../../images/no_image.png';
|
||||
import FormatLib from '../../lib/format';
|
||||
import OrderLib from '../../lib/order';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
import Switch from 'react-switch';
|
||||
import type { OrderItem } from '../../models/order';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface AbstractItemProps {
|
||||
item: OrderItem,
|
||||
hasError: boolean,
|
||||
className?: string,
|
||||
removeItemFromCart: (item: OrderItem) => void,
|
||||
toggleItemOffer: (item: OrderItem, checked: boolean) => void,
|
||||
privilegedOperator: boolean,
|
||||
actions?: ReactNode
|
||||
}
|
||||
|
||||
/**
|
||||
* This component shares the common code for items in the cart (product, cart-item, etc)
|
||||
*/
|
||||
export const AbstractItem: React.FC<AbstractItemProps> = ({ item, hasError, className, removeItemFromCart, toggleItemOffer, privilegedOperator, actions, children }) => {
|
||||
const { t } = useTranslation('public');
|
||||
|
||||
/**
|
||||
* Return the callback triggered when then user remove the given item from the cart
|
||||
*/
|
||||
const handleRemoveItem = (item: OrderItem) => {
|
||||
return (e: React.BaseSyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
removeItemFromCart(item);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the callback triggered when the privileged user enable/disable the offered attribute for the given item
|
||||
*/
|
||||
const handleToggleOffer = (item: OrderItem) => {
|
||||
return (checked: boolean) => toggleItemOffer(item, checked);
|
||||
};
|
||||
|
||||
return (
|
||||
<article className={`item ${className || ''} ${hasError ? 'error' : ''}`}>
|
||||
<div className='picture'>
|
||||
<img alt='' src={item.orderable_main_image_url || noImage} />
|
||||
</div>
|
||||
{children}
|
||||
<div className="actions">
|
||||
{actions}
|
||||
<div className='total'>
|
||||
<span>{t('app.public.abstract_item.total')}</span>
|
||||
<p>{FormatLib.price(OrderLib.itemAmount(item))}</p>
|
||||
</div>
|
||||
<FabButton className="main-action-btn" onClick={handleRemoveItem(item)}>
|
||||
<i className="fa fa-trash" />
|
||||
</FabButton>
|
||||
</div>
|
||||
{privilegedOperator &&
|
||||
<div className='offer'>
|
||||
<label>
|
||||
<span>{t('app.public.abstract_item.offer_product')}</span>
|
||||
<Switch
|
||||
checked={item.is_offered || false}
|
||||
onChange={handleToggleOffer(item)}
|
||||
width={40}
|
||||
height={19}
|
||||
uncheckedIcon={false}
|
||||
checkedIcon={false}
|
||||
handleDiameter={15} />
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
</article>
|
||||
);
|
||||
};
|
@ -0,0 +1,141 @@
|
||||
import * as React from 'react';
|
||||
import FormatLib from '../../lib/format';
|
||||
import { CaretDown, CaretUp } from 'phosphor-react';
|
||||
import type { OrderProduct, OrderErrors, Order } from '../../models/order';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import _ from 'lodash';
|
||||
import CartAPI from '../../api/cart';
|
||||
import { AbstractItem } from './abstract-item';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface CartOrderProductProps {
|
||||
item: OrderProduct,
|
||||
cartErrors: OrderErrors,
|
||||
className?: string,
|
||||
cart: Order,
|
||||
setCart: (cart: Order) => void,
|
||||
onError: (message: string) => void,
|
||||
removeProductFromCart: (item: OrderProduct) => void,
|
||||
toggleProductOffer: (item: OrderProduct, checked: boolean) => void,
|
||||
privilegedOperator: boolean,
|
||||
}
|
||||
|
||||
/**
|
||||
* This component shows a product in the cart
|
||||
*/
|
||||
export const CartOrderProduct: React.FC<CartOrderProductProps> = ({ item, cartErrors, className, cart, setCart, onError, removeProductFromCart, toggleProductOffer, privilegedOperator }) => {
|
||||
const { t } = useTranslation('public');
|
||||
|
||||
/**
|
||||
* Get the given item's errors
|
||||
*/
|
||||
const getItemErrors = (item: OrderProduct) => {
|
||||
if (!cartErrors) return [];
|
||||
const errors = _.find(cartErrors.details, (e) => e.item_id === item.id);
|
||||
return errors?.errors || [{ error: 'not_found' }];
|
||||
};
|
||||
|
||||
/**
|
||||
* Show an human-readable styled error for the given item's error
|
||||
*/
|
||||
const itemError = (item: OrderProduct, error) => {
|
||||
if (error.error === 'is_active' || error.error === 'not_found') {
|
||||
return <div className='error'><p>{t('app.public.cart_order_product.errors.product_not_found')}</p></div>;
|
||||
}
|
||||
if (error.error === 'stock' && error.value === 0) {
|
||||
return <div className='error'><p>{t('app.public.cart_order_product.errors.out_of_stock')}</p></div>;
|
||||
}
|
||||
if (error.error === 'stock' && error.value > 0) {
|
||||
return <div className='error'><p>{t('app.public.cart_order_product.errors.stock_limit_QUANTITY', { QUANTITY: error.value })}</p></div>;
|
||||
}
|
||||
if (error.error === 'quantity_min') {
|
||||
return <div className='error'><p>{t('app.public.cart_order_product.errors.quantity_min_QUANTITY', { QUANTITY: error.value })}</p></div>;
|
||||
}
|
||||
if (error.error === 'amount') {
|
||||
return <div className='error'>
|
||||
<p>{t('app.public.cart_order_product.errors.price_changed_PRICE', { PRICE: `${FormatLib.price(error.value)} / ${t('app.public.cart_order_product.unit')}` })}</p>
|
||||
<span className='refresh-btn' onClick={refreshItem(item)}>{t('app.public.cart_order_product.update_item')}</span>
|
||||
</div>;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh product amount
|
||||
*/
|
||||
const refreshItem = (item: OrderProduct) => {
|
||||
return (e: React.BaseSyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
CartAPI.refreshItem(cart, item.orderable_id, item.orderable_type).then(data => {
|
||||
setCart(data);
|
||||
}).catch(onError);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Change product quantity
|
||||
*/
|
||||
const changeProductQuantity = (e: React.BaseSyntheticEvent, item: OrderProduct) => {
|
||||
CartAPI.setQuantity(cart, item.orderable_id, item.orderable_type, e.target.value)
|
||||
.then(data => {
|
||||
setCart(data);
|
||||
})
|
||||
.catch(() => onError(t('app.public.cart_order_product.stock_limit')));
|
||||
};
|
||||
|
||||
/**
|
||||
* Increment/decrement product quantity
|
||||
*/
|
||||
const increaseOrDecreaseProductQuantity = (item: OrderProduct, direction: 'up' | 'down') => {
|
||||
CartAPI.setQuantity(cart, item.orderable_id, item.orderable_type, direction === 'up' ? item.quantity + 1 : item.quantity - 1)
|
||||
.then(data => {
|
||||
setCart(data);
|
||||
})
|
||||
.catch(() => onError(t('app.public.cart_order_product.stock_limit')));
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the components in the "actions" section of the item
|
||||
*/
|
||||
const buildActions = (): ReactNode => {
|
||||
return (
|
||||
<>
|
||||
<div className='price'>
|
||||
<p>{FormatLib.price(item.amount)}</p>
|
||||
<span>/ {t('app.public.cart_order_product.unit')}</span>
|
||||
</div>
|
||||
<div className='quantity'>
|
||||
<input type='number'
|
||||
onChange={e => changeProductQuantity(e, item)}
|
||||
min={item.quantity_min}
|
||||
max={item.orderable_external_stock}
|
||||
value={item.quantity}
|
||||
/>
|
||||
<button onClick={() => increaseOrDecreaseProductQuantity(item, 'up')}><CaretUp size={12} weight="fill" /></button>
|
||||
<button onClick={() => increaseOrDecreaseProductQuantity(item, 'down')}><CaretDown size={12} weight="fill" /></button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<AbstractItem className={`cart-order-product ${className || ''}`}
|
||||
hasError={getItemErrors(item).length > 0}
|
||||
item={item}
|
||||
removeItemFromCart={removeProductFromCart}
|
||||
privilegedOperator={privilegedOperator}
|
||||
toggleItemOffer={toggleProductOffer}
|
||||
actions={buildActions()}>
|
||||
<div className="ref">
|
||||
<span>{t('app.public.cart_order_product.reference_short')} {item.orderable_ref || ''}</span>
|
||||
<p><a className="text-black" href={`/#!/store/p/${item.orderable_slug}`}>{item.orderable_name}</a></p>
|
||||
{item.quantity_min > 1 &&
|
||||
<span className='min'>{t('app.public.cart_order_product.minimum_purchase')}{item.quantity_min}</span>
|
||||
}
|
||||
{getItemErrors(item).map(e => {
|
||||
return itemError(item, e);
|
||||
})}
|
||||
</div>
|
||||
</AbstractItem>
|
||||
);
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
import * as React from 'react';
|
||||
import type { OrderErrors, Order } from '../../models/order';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import _ from 'lodash';
|
||||
import { AbstractItem } from './abstract-item';
|
||||
import { OrderCartItem } from '../../models/order';
|
||||
|
||||
interface CartOrderReservationProps {
|
||||
item: OrderCartItem,
|
||||
cartErrors: OrderErrors,
|
||||
className?: string,
|
||||
cart: Order,
|
||||
setCart: (cart: Order) => void,
|
||||
onError: (message: string) => void,
|
||||
removeProductFromCart: (item: OrderCartItem) => void,
|
||||
toggleProductOffer: (item: OrderCartItem, checked: boolean) => void,
|
||||
privilegedOperator: boolean,
|
||||
}
|
||||
|
||||
/**
|
||||
* This component shows a product in the cart
|
||||
*/
|
||||
export const CartOrderReservation: React.FC<CartOrderReservationProps> = ({ item, cartErrors, className, cart, setCart, onError, removeProductFromCart, toggleProductOffer, privilegedOperator }) => {
|
||||
const { t } = useTranslation('public');
|
||||
|
||||
/**
|
||||
* Get the given item's errors
|
||||
*/
|
||||
const getItemErrors = (item: OrderCartItem) => {
|
||||
if (!cartErrors) return [];
|
||||
const errors = _.find(cartErrors.details, (e) => e.item_id === item.id);
|
||||
return errors?.errors || [{ error: 'not_found' }];
|
||||
};
|
||||
|
||||
return (
|
||||
<AbstractItem className={`cart-order-reservation ${className || ''}`}
|
||||
hasError={getItemErrors(item).length > 0}
|
||||
item={item}
|
||||
removeItemFromCart={removeProductFromCart}
|
||||
privilegedOperator={privilegedOperator}
|
||||
toggleItemOffer={toggleProductOffer}>
|
||||
<div className="ref">
|
||||
<p>Réservation {item.orderable_name}</p>
|
||||
{getItemErrors(item)}
|
||||
</div>
|
||||
</AbstractItem>
|
||||
);
|
||||
};
|
@ -3,24 +3,23 @@ import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { Loader } from '../base/loader';
|
||||
import { IApplication } from '../../models/application';
|
||||
import type { 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';
|
||||
import type { User } from '../../models/user';
|
||||
import { PaymentModal } from '../payment/stripe/payment-modal';
|
||||
import { PaymentMethod } from '../../models/payment';
|
||||
import { Order, OrderErrors } from '../../models/order';
|
||||
import type { Order, OrderCartItem, OrderErrors, OrderItem, OrderProduct } from '../../models/order';
|
||||
import { MemberSelect } from '../user/member-select';
|
||||
import { CouponInput } from '../coupon/coupon-input';
|
||||
import { Coupon } from '../../models/coupon';
|
||||
import noImage from '../../../../images/no_image.png';
|
||||
import Switch from 'react-switch';
|
||||
import type { Coupon } from '../../models/coupon';
|
||||
import OrderLib from '../../lib/order';
|
||||
import { CaretDown, CaretUp } from 'phosphor-react';
|
||||
import _ from 'lodash';
|
||||
import OrderAPI from '../../api/order';
|
||||
import { CartOrderProduct } from './cart-order-product';
|
||||
import { CartOrderReservation } from './cart-order-reservation';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
@ -58,9 +57,6 @@ const StoreCart: React.FC<StoreCartProps> = ({ onSuccess, onError, currentUser,
|
||||
* Remove the product from cart
|
||||
*/
|
||||
const removeProductFromCart = (item) => {
|
||||
return (e: React.BaseSyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const errors = getItemErrors(item);
|
||||
if (errors.length === 1 && errors[0].error === 'not_found') {
|
||||
reloadCart().catch(onError);
|
||||
@ -70,42 +66,6 @@ const StoreCart: React.FC<StoreCartProps> = ({ onSuccess, onError, currentUser,
|
||||
}).catch(onError);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Change product quantity
|
||||
*/
|
||||
const changeProductQuantity = (e: React.BaseSyntheticEvent, item) => {
|
||||
CartAPI.setQuantity(cart, item.orderable_id, item.orderable_type, e.target.value)
|
||||
.then(data => {
|
||||
setCart(data);
|
||||
})
|
||||
.catch(() => onError(t('app.public.store_cart.stock_limit')));
|
||||
};
|
||||
|
||||
/**
|
||||
* Increment/decrement product quantity
|
||||
*/
|
||||
const increaseOrDecreaseProductQuantity = (item, direction: 'up' | 'down') => {
|
||||
CartAPI.setQuantity(cart, item.orderable_id, item.orderable_type, direction === 'up' ? item.quantity + 1 : item.quantity - 1)
|
||||
.then(data => {
|
||||
setCart(data);
|
||||
})
|
||||
.catch(() => onError(t('app.public.store_cart.stock_limit')));
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh product amount
|
||||
*/
|
||||
const refreshItem = (item) => {
|
||||
return (e: React.BaseSyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
CartAPI.refreshItem(cart, item.orderable_id, item.orderable_type).then(data => {
|
||||
setCart(data);
|
||||
}).catch(onError);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Check the current cart's items (available, price, stock, quantity_min)
|
||||
@ -202,8 +162,7 @@ const StoreCart: React.FC<StoreCartProps> = ({ onSuccess, onError, currentUser,
|
||||
/**
|
||||
* Toggle product offer
|
||||
*/
|
||||
const toggleProductOffer = (item) => {
|
||||
return (checked: boolean) => {
|
||||
const toggleProductOffer = (item: OrderItem, checked: boolean) => {
|
||||
CartAPI.setOffer(cart, item.orderable_id, item.orderable_type, checked).then(data => {
|
||||
setCart(data);
|
||||
}).catch(e => {
|
||||
@ -214,7 +173,6 @@ const StoreCart: React.FC<StoreCartProps> = ({ onSuccess, onError, currentUser,
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply coupon to current cart
|
||||
@ -225,90 +183,39 @@ const StoreCart: React.FC<StoreCartProps> = ({ onSuccess, onError, currentUser,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show item error
|
||||
*/
|
||||
const itemError = (item, error) => {
|
||||
if (error.error === 'is_active' || error.error === 'not_found') {
|
||||
return <div className='error'><p>{t('app.public.store_cart.errors.product_not_found')}</p></div>;
|
||||
}
|
||||
if (error.error === 'stock' && error.value === 0) {
|
||||
return <div className='error'><p>{t('app.public.store_cart.errors.out_of_stock')}</p></div>;
|
||||
}
|
||||
if (error.error === 'stock' && error.value > 0) {
|
||||
return <div className='error'><p>{t('app.public.store_cart.errors.stock_limit_QUANTITY', { QUANTITY: error.value })}</p></div>;
|
||||
}
|
||||
if (error.error === 'quantity_min') {
|
||||
return <div className='error'><p>{t('app.public.store_cart.errors.quantity_min_QUANTITY', { QUANTITY: error.value })}</p></div>;
|
||||
}
|
||||
if (error.error === 'amount') {
|
||||
return <div className='error'>
|
||||
<p>{t('app.public.store_cart.errors.price_changed_PRICE', { PRICE: `${FormatLib.price(error.value)} / ${t('app.public.store_cart.unit')}` })}</p>
|
||||
<span className='refresh-btn' onClick={refreshItem(item)}>{t('app.public.store_cart.update_item')}</span>
|
||||
</div>;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<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 ${getItemErrors(item).length > 0 ? 'error' : ''}`}>
|
||||
<div className='picture'>
|
||||
<img alt='' src={item.orderable_main_image_url || noImage} />
|
||||
</div>
|
||||
<div className="ref">
|
||||
<span>{t('app.public.store_cart.reference_short')} {item.orderable_ref || ''}</span>
|
||||
<p><a className="text-black" href={`/#!/store/p/${item.orderable_slug}`}>{item.orderable_name}</a></p>
|
||||
{item.quantity_min > 1 &&
|
||||
<span className='min'>{t('app.public.store_cart.minimum_purchase')}{item.quantity_min}</span>
|
||||
{cart && cart.order_items_attributes.map(item => {
|
||||
if (item.orderable_type === 'Product') {
|
||||
return (
|
||||
<CartOrderProduct item={item as OrderProduct}
|
||||
key={item.id}
|
||||
className="store-cart-list-item"
|
||||
cartErrors={cartErrors}
|
||||
cart={cart}
|
||||
setCart={setCart}
|
||||
onError={onError}
|
||||
removeProductFromCart={removeProductFromCart}
|
||||
toggleProductOffer={toggleProductOffer}
|
||||
privilegedOperator={isPrivileged()} />
|
||||
);
|
||||
}
|
||||
{getItemErrors(item).map(e => {
|
||||
return itemError(item, e);
|
||||
return (
|
||||
<CartOrderReservation item={item as OrderCartItem}
|
||||
key={item.id}
|
||||
className="store-cart-list-item"
|
||||
cartErrors={cartErrors}
|
||||
cart={cart}
|
||||
setCart={setCart}
|
||||
onError={onError}
|
||||
removeProductFromCart={removeProductFromCart}
|
||||
toggleProductOffer={toggleProductOffer}
|
||||
privilegedOperator={isPrivileged()} />
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="actions">
|
||||
<div className='price'>
|
||||
<p>{FormatLib.price(item.amount)}</p>
|
||||
<span>/ {t('app.public.store_cart.unit')}</span>
|
||||
</div>
|
||||
<div className='quantity'>
|
||||
<input type='number'
|
||||
onChange={e => changeProductQuantity(e, item)}
|
||||
min={item.quantity_min}
|
||||
max={item.orderable_external_stock}
|
||||
value={item.quantity}
|
||||
/>
|
||||
<button onClick={() => increaseOrDecreaseProductQuantity(item, 'up')}><CaretUp size={12} weight="fill" /></button>
|
||||
<button onClick={() => increaseOrDecreaseProductQuantity(item, 'down')}><CaretDown size={12} weight="fill" /></button>
|
||||
</div>
|
||||
<div className='total'>
|
||||
<span>{t('app.public.store_cart.total')}</span>
|
||||
<p>{FormatLib.price(OrderLib.itemAmount(item))}</p>
|
||||
</div>
|
||||
<FabButton className="main-action-btn" onClick={removeProductFromCart(item)}>
|
||||
<i className="fa fa-trash" />
|
||||
</FabButton>
|
||||
</div>
|
||||
{isPrivileged() &&
|
||||
<div className='offer'>
|
||||
<label>
|
||||
<span>{t('app.public.store_cart.offer_product')}</span>
|
||||
<Switch
|
||||
checked={item.is_offered || false}
|
||||
onChange={toggleProductOffer(item)}
|
||||
width={40}
|
||||
height={19}
|
||||
uncheckedIcon={false}
|
||||
checkedIcon={false}
|
||||
handleDiameter={15} />
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="group">
|
||||
{cart && !cartIsEmpty() &&
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { computePriceWithCoupon } from './coupon';
|
||||
import { Order } from '../models/order';
|
||||
import { Order, OrderItem } from '../models/order';
|
||||
|
||||
export default class OrderLib {
|
||||
/**
|
||||
* Get the order item total
|
||||
*/
|
||||
static itemAmount = (item): number => {
|
||||
static itemAmount = (item: OrderItem): number => {
|
||||
return item.quantity * Math.round(item.amount * 100) / 100;
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,29 @@ import { CartItemType } from './cart_item';
|
||||
|
||||
export type OrderableType = 'Product' | CartItemType;
|
||||
|
||||
export interface OrderItem {
|
||||
id: number,
|
||||
orderable_type: OrderableType,
|
||||
orderable_id: number,
|
||||
orderable_name: string,
|
||||
orderable_main_image_url?: string;
|
||||
quantity: number,
|
||||
amount: number,
|
||||
is_offered: boolean
|
||||
}
|
||||
|
||||
export interface OrderProduct extends OrderItem {
|
||||
orderable_type: 'Product',
|
||||
orderable_slug: string,
|
||||
orderable_ref?: string,
|
||||
orderable_external_stock: number,
|
||||
quantity_min: number
|
||||
}
|
||||
|
||||
export interface OrderCartItem extends OrderItem {
|
||||
orderable_type: CartItemType
|
||||
}
|
||||
|
||||
export interface Order {
|
||||
id: number,
|
||||
token: string,
|
||||
@ -29,20 +52,7 @@ export interface Order {
|
||||
payment_date?: TDateISO,
|
||||
wallet_amount?: number,
|
||||
paid_total?: number,
|
||||
order_items_attributes: Array<{
|
||||
id: number,
|
||||
orderable_type: OrderableType,
|
||||
orderable_id: number,
|
||||
orderable_name: string,
|
||||
orderable_slug: string,
|
||||
orderable_ref?: string,
|
||||
orderable_main_image_url?: string,
|
||||
orderable_external_stock: number,
|
||||
quantity: number,
|
||||
quantity_min: number,
|
||||
amount: number,
|
||||
is_offered: boolean
|
||||
}>,
|
||||
order_items_attributes: Array<OrderItem>,
|
||||
}
|
||||
|
||||
export interface OrderPayment {
|
||||
|
@ -434,12 +434,6 @@ en:
|
||||
checkout: "Checkout"
|
||||
cart_is_empty: "Your cart is empty"
|
||||
pickup: "Pickup your products"
|
||||
reference_short: "ref:"
|
||||
minimum_purchase: "Minimum purchase: "
|
||||
stock_limit: "You have reached the current stock limit"
|
||||
unit: "Unit"
|
||||
total: "Total"
|
||||
offer_product: "Offer the product"
|
||||
checkout_header: "Total amount for your cart"
|
||||
checkout_products_COUNT: "Your cart contains {COUNT} {COUNT, plural, =1{product} other{products}}"
|
||||
checkout_products_total: "Products total"
|
||||
@ -449,6 +443,14 @@ en:
|
||||
checkout_error: "An unexpected error occurred. Please contact the administrator."
|
||||
checkout_success: "Purchase confirmed. Thanks!"
|
||||
select_user: "Please select a user before continuing."
|
||||
abstract_item:
|
||||
offer_product: "Offer the product"
|
||||
total: "Total"
|
||||
cart_order_product:
|
||||
reference_short: "ref:"
|
||||
minimum_purchase: "Minimum purchase: "
|
||||
stock_limit: "You have reached the current stock limit"
|
||||
unit: "Unit"
|
||||
update_item: "Update"
|
||||
errors:
|
||||
product_not_found: "This product is no longer available, please remove it from your cart."
|
||||
|
Loading…
x
Reference in New Issue
Block a user