From 3a0248ed987b561bf7511280e42b7212f21f614d Mon Sep 17 00:00:00 2001 From: vincent Date: Tue, 6 Sep 2022 12:08:19 +0200 Subject: [PATCH] stock --- .../components/store/product-form.tsx | 7 +- .../components/store/product-stock-form.tsx | 196 ++++++++++++++++-- .../components/store/product-stock-modal.tsx | 96 +++++++++ .../components/store/store-product-item.tsx | 2 +- .../components/store/store-product.tsx | 2 +- app/frontend/src/stylesheets/application.scss | 2 + .../stylesheets/modules/store/_utilities.scss | 15 ++ .../modules/store/product-form.scss | 5 +- .../modules/store/product-stock-form.scss | 74 +++++++ .../modules/store/product-stock-modal.scss | 30 +++ .../modules/store/products-grid.scss | 16 +- .../modules/store/store-list-header.scss | 10 +- .../src/stylesheets/modules/store/store.scss | 14 -- config/locales/app.admin.en.yml | 38 +++- 14 files changed, 451 insertions(+), 56 deletions(-) create mode 100644 app/frontend/src/javascript/components/store/product-stock-modal.tsx create mode 100644 app/frontend/src/stylesheets/modules/store/product-stock-form.scss create mode 100644 app/frontend/src/stylesheets/modules/store/product-stock-modal.scss diff --git a/app/frontend/src/javascript/components/store/product-form.tsx b/app/frontend/src/javascript/components/store/product-form.tsx index 62b25e874..7839ed8b2 100644 --- a/app/frontend/src/javascript/components/store/product-form.tsx +++ b/app/frontend/src/javascript/components/store/product-form.tsx @@ -242,7 +242,7 @@ export const ProductForm: React.FC = ({ product, title, onSucc

setStockTab(true)}>{t('app.admin.store.product_form.stock_management')}

{stockTab - ? + ? :
= ({ product, title, onSucc
-

{t('app.admin.store.product_form.price_and_rule_of_selling_product')}

+

{t('app.admin.store.product_form.price_and_rule_of_selling_product')}

+ onChange={toggleIsActivePrice} />
{isActivePrice &&
{ product: Product, - control: Control, + register: UseFormRegister, + control: Control, + formState: FormState, onSuccess: (product: Product) => void, onError: (message: string) => void, } /** - * Form tab to manage product's stock + * Form tab to manage a product's stock */ -export const ProductStockForm: React.FC = ({ product, control, onError, onSuccess }) => { +export const ProductStockForm = ({ product, register, control, formState, onError, onSuccess }: ProductStockFormProps) => { const { t } = useTranslation('admin'); + const [activeThreshold, setActiveThreshold] = useState(false); + // is the modal open? + const [isOpen, setIsOpen] = useState(false); + + // Styles the React-select component + const customStyles = { + control: base => ({ + ...base, + width: '20ch', + border: 'none', + backgroundColor: 'transparent' + }), + indicatorSeparator: () => ({ + display: 'none' + }) + }; + + type selectOption = { value: number, label: string }; + /** + * Creates sorting options to the react-select format + */ + const buildEventsOptions = (): Array => { + return [ + { value: 0, label: t('app.admin.store.product_stock_form.events.inward_stock') }, + { value: 1, label: t('app.admin.store.product_stock_form.events.returned') }, + { value: 2, label: t('app.admin.store.product_stock_form.events.canceled') }, + { value: 3, label: t('app.admin.store.product_stock_form.events.sold') }, + { value: 4, label: t('app.admin.store.product_stock_form.events.missing') }, + { value: 5, label: t('app.admin.store.product_stock_form.events.damaged') } + ]; + }; + /** + * Creates sorting options to the react-select format + */ + const buildStocksOptions = (): Array => { + return [ + { value: 0, label: t('app.admin.store.product_stock_form.internal') }, + { value: 1, label: t('app.admin.store.product_stock_form.external') }, + { value: 2, label: t('app.admin.store.product_stock_form.all') } + ]; + }; + + /** + * On events option change + */ + const eventsOptionsChange = (evt: selectOption) => { + console.log('Event option:', evt); + }; + /** + * On stocks option change + */ + const stocksOptionsChange = (evt: selectOption) => { + console.log('Stocks option:', evt); + }; + /** * Toggle stock threshold */ const toggleStockThreshold = (checked: boolean) => { - console.log('Stock threshold:', checked); + setActiveThreshold(checked); + }; + + /** + * Opens/closes the product category modal + */ + const toggleModal = (): void => { + setIsOpen(!isOpen); + }; + + /** + * Toggle stock threshold alert + */ + const toggleStockThresholdAlert = (checked: boolean) => { + console.log('Low stock notification:', checked); }; return ( -
+

Stock à jour 00/00/0000 - 00H30

+
+

Product name

+
+ {t('app.admin.store.product_stock_form.internal')} +

00

+
+
+ {t('app.admin.store.product_stock_form.external')} +

000

+
+ } className="is-black">Modifier +

-
-

{t('app.admin.store.product_stock_form.low_stock_threshold')}

- +
+
+

{t('app.admin.store.product_stock_form.low_stock_threshold')}

+ +
+ + + + {activeThreshold && <> + {t('app.admin.store.product_stock_form.low_stock')} +
+ + +
+ }
+
+ +
+

{t('app.admin.store.product_stock_form.events_history')}

+
+
+

{t('app.admin.store.product_stock_form.event_type')}

+ stocksOptionsChange(evt)} + styles={customStyles} + /> +
+
+
+
+

Product name

+

00/00/0000

+
+ [stock type] +

00

+
+
+ {t('app.admin.store.product_stock_form.event_type')} +

[event type]

+
+
+ {t('app.admin.store.product_stock_form.stock_level')} +

000

+
+
+
+
+ + + +
); }; diff --git a/app/frontend/src/javascript/components/store/product-stock-modal.tsx b/app/frontend/src/javascript/components/store/product-stock-modal.tsx new file mode 100644 index 000000000..5c730403b --- /dev/null +++ b/app/frontend/src/javascript/components/store/product-stock-modal.tsx @@ -0,0 +1,96 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Product } from '../../models/product'; +import { UseFormRegister } from 'react-hook-form'; +import { Control, FormState } from 'react-hook-form/dist/types/form'; +import { FormSelect } from '../form/form-select'; +import { FormInput } from '../form/form-input'; +import { FabButton } from '../base/fab-button'; + +type selectOption = { value: number, label: string }; + +interface ProductStockModalProps { + product: Product, + register: UseFormRegister, + control: Control, + formState: FormState, + onSuccess: (product: Product) => void, + onError: (message: string) => void +} + +/** + * Form to manage a product's stock movement and quantity + */ +export const ProductStockModal = ({ product, register, control, formState, onError, onSuccess }: ProductStockModalProps) => { + const { t } = useTranslation('admin'); + + const [movement, setMovement] = useState<'in' | 'out'>('in'); + + /** + * Toggle between adding or removing product from stock + */ + const toggleMovementType = (evt: React.MouseEvent, type: 'in' | 'out') => { + evt.preventDefault(); + setMovement(type); + }; + + /** + * Creates sorting options to the react-select format + */ + const buildEventsOptions = (): Array => { + let options = []; + movement === 'in' + ? options = [ + { value: 0, label: t('app.admin.store.product_stock_modal.events.inward_stock') }, + { value: 1, label: t('app.admin.store.product_stock_modal.events.returned') }, + { value: 2, label: t('app.admin.store.product_stock_modal.events.canceled') } + ] + : options = [ + { value: 0, label: t('app.admin.store.product_stock_modal.events.sold') }, + { value: 1, label: t('app.admin.store.product_stock_modal.events.missing') }, + { value: 2, label: t('app.admin.store.product_stock_modal.events.damaged') } + ]; + return options; + }; + /** + * Creates sorting options to the react-select format + */ + const buildStocksOptions = (): Array => { + return [ + { value: 0, label: t('app.admin.store.product_stock_modal.internal') }, + { value: 1, label: t('app.admin.store.product_stock_modal.external') } + ]; + }; + + return ( +
+

{t('app.admin.store.product_stock_modal.new_event')}

+
+ + +
+ + + + {t('app.admin.store.product_stock_modal.update_stock')} + + ); +}; diff --git a/app/frontend/src/javascript/components/store/store-product-item.tsx b/app/frontend/src/javascript/components/store/store-product-item.tsx index 5b46bc006..79edaf837 100644 --- a/app/frontend/src/javascript/components/store/store-product-item.tsx +++ b/app/frontend/src/javascript/components/store/store-product-item.tsx @@ -84,7 +84,7 @@ export const StoreProductItem: React.FC = ({ product, car / {t('app.public.store_product_item.unit')}
} -
+
{productStockStatus(product)}
{product.stock.external > 0 && diff --git a/app/frontend/src/javascript/components/store/store-product.tsx b/app/frontend/src/javascript/components/store/store-product.tsx index fe55e5a94..72c221440 100644 --- a/app/frontend/src/javascript/components/store/store-product.tsx +++ b/app/frontend/src/javascript/components/store/store-product.tsx @@ -160,7 +160,7 @@ export const StoreProduct: React.FC = ({ productSlug, onError