mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-18 07:52:23 +01:00
stock
This commit is contained in:
parent
f21a68593a
commit
3a0248ed98
@ -242,7 +242,7 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
|
|||||||
<p className={stockTab ? 'is-active' : ''} onClick={() => setStockTab(true)}>{t('app.admin.store.product_form.stock_management')}</p>
|
<p className={stockTab ? 'is-active' : ''} onClick={() => setStockTab(true)}>{t('app.admin.store.product_form.stock_management')}</p>
|
||||||
</div>
|
</div>
|
||||||
{stockTab
|
{stockTab
|
||||||
? <ProductStockForm product={product} control={control} onError={onError} onSuccess={onSuccess} />
|
? <ProductStockForm product={product} register={register} control={control} id="stock" onError={onError} onSuccess={onSuccess} />
|
||||||
: <section>
|
: <section>
|
||||||
<div className="subgrid">
|
<div className="subgrid">
|
||||||
<FormInput id="name"
|
<FormInput id="name"
|
||||||
@ -277,13 +277,12 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
|
|||||||
|
|
||||||
<div className="price-data">
|
<div className="price-data">
|
||||||
<div className="header-switch">
|
<div className="header-switch">
|
||||||
<h4 className='span-7'>{t('app.admin.store.product_form.price_and_rule_of_selling_product')}</h4>
|
<h4>{t('app.admin.store.product_form.price_and_rule_of_selling_product')}</h4>
|
||||||
<FormSwitch control={control}
|
<FormSwitch control={control}
|
||||||
id="is_active_price"
|
id="is_active_price"
|
||||||
label={t('app.admin.store.product_form.is_active_price')}
|
label={t('app.admin.store.product_form.is_active_price')}
|
||||||
defaultValue={isActivePrice}
|
defaultValue={isActivePrice}
|
||||||
onChange={toggleIsActivePrice}
|
onChange={toggleIsActivePrice} />
|
||||||
className='span-3' />
|
|
||||||
</div>
|
</div>
|
||||||
{isActivePrice && <div className="price-data-content">
|
{isActivePrice && <div className="price-data-content">
|
||||||
<FormInput id="amount"
|
<FormInput id="amount"
|
||||||
|
@ -1,44 +1,208 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Product } from '../../models/product';
|
import { Product } from '../../models/product';
|
||||||
|
import { UseFormRegister } from 'react-hook-form';
|
||||||
|
import { Control, FormState } from 'react-hook-form/dist/types/form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Control } from 'react-hook-form';
|
import { HtmlTranslate } from '../base/html-translate';
|
||||||
import { FormSwitch } from '../form/form-switch';
|
import { FormSwitch } from '../form/form-switch';
|
||||||
|
import { FormInput } from '../form/form-input';
|
||||||
|
import Select from 'react-select';
|
||||||
|
import { FabAlert } from '../base/fab-alert';
|
||||||
|
import { FabButton } from '../base/fab-button';
|
||||||
|
import { PencilSimple } from 'phosphor-react';
|
||||||
|
import { FabModal, ModalSize } from '../base/fab-modal';
|
||||||
|
import { ProductStockModal } from './product-stock-modal';
|
||||||
|
|
||||||
interface ProductStockFormProps {
|
interface ProductStockFormProps<TFieldValues, TContext extends object> {
|
||||||
product: Product,
|
product: Product,
|
||||||
control: Control,
|
register: UseFormRegister<TFieldValues>,
|
||||||
|
control: Control<TFieldValues, TContext>,
|
||||||
|
formState: FormState<TFieldValues>,
|
||||||
onSuccess: (product: Product) => void,
|
onSuccess: (product: Product) => void,
|
||||||
onError: (message: string) => 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<ProductStockFormProps> = ({ product, control, onError, onSuccess }) => {
|
export const ProductStockForm = <TFieldValues, TContext extends object> ({ product, register, control, formState, onError, onSuccess }: ProductStockFormProps<TFieldValues, TContext>) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
|
const [activeThreshold, setActiveThreshold] = useState<boolean>(false);
|
||||||
|
// is the modal open?
|
||||||
|
const [isOpen, setIsOpen] = useState<boolean>(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<selectOption> => {
|
||||||
|
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<selectOption> => {
|
||||||
|
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
|
* Toggle stock threshold
|
||||||
*/
|
*/
|
||||||
const toggleStockThreshold = (checked: boolean) => {
|
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 (
|
return (
|
||||||
<section>
|
<section className='product-stock-form'>
|
||||||
<h4>Stock à jour <span>00/00/0000 - 00H30</span></h4>
|
<h4>Stock à jour <span>00/00/0000 - 00H30</span></h4>
|
||||||
<div></div>
|
<div></div>
|
||||||
|
<div className="stock-item">
|
||||||
|
<p className='title'>Product name</p>
|
||||||
|
<div className="group">
|
||||||
|
<span>{t('app.admin.store.product_stock_form.internal')}</span>
|
||||||
|
<p>00</p>
|
||||||
|
</div>
|
||||||
|
<div className="group">
|
||||||
|
<span>{t('app.admin.store.product_stock_form.external')}</span>
|
||||||
|
<p>000</p>
|
||||||
|
</div>
|
||||||
|
<FabButton onClick={toggleModal} icon={<PencilSimple size={20} weight="fill" />} className="is-black">Modifier</FabButton>
|
||||||
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
|
<div className="threshold-data">
|
||||||
<div className="header-switch">
|
<div className="header-switch">
|
||||||
<h4 className='span-7'>{t('app.admin.store.product_stock_form.low_stock_threshold')}</h4>
|
<h4>{t('app.admin.store.product_stock_form.low_stock_threshold')}</h4>
|
||||||
<FormSwitch control={control}
|
<FormSwitch control={control}
|
||||||
id="is_active_threshold"
|
id="is_active_threshold"
|
||||||
label={t('app.admin.store.product_stock_form.toggle_stock_threshold')}
|
label={t('app.admin.store.product_stock_form.stock_threshold_toggle')}
|
||||||
defaultValue={false}
|
defaultValue={activeThreshold}
|
||||||
onChange={toggleStockThreshold}
|
onChange={toggleStockThreshold} />
|
||||||
className='span-3' />
|
|
||||||
</div>
|
</div>
|
||||||
|
<FabAlert level="warning">
|
||||||
|
<HtmlTranslate trKey="app.admin.store.product_stock_form.stock_threshold_information" />
|
||||||
|
</FabAlert>
|
||||||
|
{activeThreshold && <>
|
||||||
|
<span className='stock-label'>{t('app.admin.store.product_stock_form.low_stock')}</span>
|
||||||
|
<div className="threshold-data-content">
|
||||||
|
<FormInput id="threshold"
|
||||||
|
type="number"
|
||||||
|
register={register}
|
||||||
|
rules={{ required: true, min: 1 }}
|
||||||
|
step={1}
|
||||||
|
formState={formState}
|
||||||
|
label={t('app.admin.store.product_stock_form.threshold_level')} />
|
||||||
|
<FormSwitch control={control}
|
||||||
|
id="threshold_alert"
|
||||||
|
formState={formState}
|
||||||
|
label={t('app.admin.store.product_stock_form.threshold_alert')}
|
||||||
|
defaultValue={activeThreshold}
|
||||||
|
onChange={toggleStockThresholdAlert} />
|
||||||
|
</div>
|
||||||
|
</>}
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div className="store-list">
|
||||||
|
<h4>{t('app.admin.store.product_stock_form.events_history')}</h4>
|
||||||
|
<div className="store-list-header">
|
||||||
|
<div className='sort-events'>
|
||||||
|
<p>{t('app.admin.store.product_stock_form.event_type')}</p>
|
||||||
|
<Select
|
||||||
|
options={buildEventsOptions()}
|
||||||
|
onChange={evt => eventsOptionsChange(evt)}
|
||||||
|
styles={customStyles}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='sort-stocks'>
|
||||||
|
<p>{t('app.admin.store.product_stock_form.stocks')}</p>
|
||||||
|
<Select
|
||||||
|
options={buildStocksOptions()}
|
||||||
|
onChange={evt => stocksOptionsChange(evt)}
|
||||||
|
styles={customStyles}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="stock-history">
|
||||||
|
<div className="stock-item">
|
||||||
|
<p className='title'>Product name</p>
|
||||||
|
<p>00/00/0000</p>
|
||||||
|
<div className="group">
|
||||||
|
<span>[stock type]</span>
|
||||||
|
<p>00</p>
|
||||||
|
</div>
|
||||||
|
<div className="group">
|
||||||
|
<span>{t('app.admin.store.product_stock_form.event_type')}</span>
|
||||||
|
<p>[event type]</p>
|
||||||
|
</div>
|
||||||
|
<div className="group">
|
||||||
|
<span>{t('app.admin.store.product_stock_form.stock_level')}</span>
|
||||||
|
<p>000</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FabModal title={t('app.admin.store.product_stock_form.modal_title')}
|
||||||
|
className="fab-modal-lg"
|
||||||
|
width={ModalSize.large}
|
||||||
|
isOpen={isOpen}
|
||||||
|
toggleModal={toggleModal}
|
||||||
|
closeButton>
|
||||||
|
<ProductStockModal product={product} register={register} control={control} id="stock-modal" onError={onError} onSuccess={onSuccess} />
|
||||||
|
</FabModal>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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<TFieldValues, TContext extends object> {
|
||||||
|
product: Product,
|
||||||
|
register: UseFormRegister<TFieldValues>,
|
||||||
|
control: Control<TFieldValues, TContext>,
|
||||||
|
formState: FormState<TFieldValues>,
|
||||||
|
onSuccess: (product: Product) => void,
|
||||||
|
onError: (message: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form to manage a product's stock movement and quantity
|
||||||
|
*/
|
||||||
|
export const ProductStockModal = <TFieldValues, TContext extends object> ({ product, register, control, formState, onError, onSuccess }: ProductStockModalProps<TFieldValues, TContext>) => {
|
||||||
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
|
const [movement, setMovement] = useState<'in' | 'out'>('in');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle between adding or removing product from stock
|
||||||
|
*/
|
||||||
|
const toggleMovementType = (evt: React.MouseEvent<HTMLButtonElement, MouseEvent>, type: 'in' | 'out') => {
|
||||||
|
evt.preventDefault();
|
||||||
|
setMovement(type);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates sorting options to the react-select format
|
||||||
|
*/
|
||||||
|
const buildEventsOptions = (): Array<selectOption> => {
|
||||||
|
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<selectOption> => {
|
||||||
|
return [
|
||||||
|
{ value: 0, label: t('app.admin.store.product_stock_modal.internal') },
|
||||||
|
{ value: 1, label: t('app.admin.store.product_stock_modal.external') }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form className='product-stock-modal'>
|
||||||
|
<p className='subtitle'>{t('app.admin.store.product_stock_modal.new_event')}</p>
|
||||||
|
<div className="movement">
|
||||||
|
<button onClick={(evt) => toggleMovementType(evt, 'in')} className={movement === 'in' ? 'is-active' : ''}>
|
||||||
|
{t('app.admin.store.product_stock_modal.addition')}
|
||||||
|
</button>
|
||||||
|
<button onClick={(evt) => toggleMovementType(evt, 'out')} className={movement === 'out' ? 'is-active' : ''}>
|
||||||
|
{t('app.admin.store.product_stock_modal.withdrawal')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<FormSelect options={buildStocksOptions()}
|
||||||
|
control={control}
|
||||||
|
id="updated_stock_type"
|
||||||
|
formState={formState}
|
||||||
|
label={t('app.admin.store.product_stock_modal.stocks')} />
|
||||||
|
<FormInput id="updated_stock_quantity"
|
||||||
|
type="number"
|
||||||
|
register={register}
|
||||||
|
rules={{ required: true, min: 1 }}
|
||||||
|
step={1}
|
||||||
|
formState={formState}
|
||||||
|
label={t('app.admin.store.product_stock_modal.quantity')} />
|
||||||
|
<FormSelect options={buildEventsOptions()}
|
||||||
|
control={control}
|
||||||
|
id="updated_stock_event"
|
||||||
|
formState={formState}
|
||||||
|
label={t('app.admin.store.product_stock_modal.event_type')} />
|
||||||
|
<FabButton type='submit'>{t('app.admin.store.product_stock_modal.update_stock')} </FabButton>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
@ -84,7 +84,7 @@ export const StoreProductItem: React.FC<StoreProductItemProps> = ({ product, car
|
|||||||
<span>/ {t('app.public.store_product_item.unit')}</span>
|
<span>/ {t('app.public.store_product_item.unit')}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div className="stock">
|
<div className="stock-label">
|
||||||
{productStockStatus(product)}
|
{productStockStatus(product)}
|
||||||
</div>
|
</div>
|
||||||
{product.stock.external > 0 &&
|
{product.stock.external > 0 &&
|
||||||
|
@ -160,7 +160,7 @@ export const StoreProduct: React.FC<StoreProductProps> = ({ productSlug, onError
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside>
|
<aside>
|
||||||
<div className="stock">
|
<div className="stock-label">
|
||||||
{productStockStatus(product)}
|
{productStockStatus(product)}
|
||||||
</div>
|
</div>
|
||||||
<div className='price'>
|
<div className='price'>
|
||||||
|
@ -95,6 +95,8 @@
|
|||||||
@import "modules/store/orders";
|
@import "modules/store/orders";
|
||||||
@import "modules/store/product-categories";
|
@import "modules/store/product-categories";
|
||||||
@import "modules/store/product-form";
|
@import "modules/store/product-form";
|
||||||
|
@import "modules/store/product-stock-form";
|
||||||
|
@import "modules/store/product-stock-modal";
|
||||||
@import "modules/store/products-grid";
|
@import "modules/store/products-grid";
|
||||||
@import "modules/store/products-list";
|
@import "modules/store/products-list";
|
||||||
@import "modules/store/products";
|
@import "modules/store/products";
|
||||||
|
@ -68,6 +68,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stock-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
@include text-sm;
|
||||||
|
color: var(--status-color);
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
margin-right: 0.8rem;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
background-color: var(--status-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Custom scrollbar
|
// Custom scrollbar
|
||||||
.u-scrollbar {
|
.u-scrollbar {
|
||||||
&::-webkit-scrollbar-track
|
&::-webkit-scrollbar-track
|
||||||
|
@ -52,9 +52,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header-switch {
|
.header-switch {
|
||||||
@include grid-col(10);
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
gap: 3.2rem;
|
gap: 3.2rem;
|
||||||
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
label { flex: 0 1 fit-content; }
|
||||||
}
|
}
|
||||||
.price-data-content {
|
.price-data-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
.product-stock-form {
|
||||||
|
h4 span { @include text-sm; }
|
||||||
|
|
||||||
|
.store-list {
|
||||||
|
h4 { margin: 0; }
|
||||||
|
}
|
||||||
|
.store-list-header {
|
||||||
|
& > *:not(:first-child) {
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
margin: 0 2rem;
|
||||||
|
width: 1px;
|
||||||
|
height: 2rem;
|
||||||
|
background-color: var(--gray-hard-darkest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sort-events {
|
||||||
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.sort-stocks {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.threshold-data-content {
|
||||||
|
margin-top: 1.6rem;
|
||||||
|
padding: 1.6rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 3.2rem;
|
||||||
|
border: 1px solid var(--gray-soft);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
label { flex: 0 1 fit-content; }
|
||||||
|
|
||||||
|
}
|
||||||
|
.stock-label {
|
||||||
|
--status-color: var(--alert-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-item {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
gap: 4.8rem;
|
||||||
|
justify-items: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1.6rem;
|
||||||
|
border: 1px solid var(--gray-soft-dark);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
background-color: var(--gray-soft-lightest);
|
||||||
|
|
||||||
|
& > * { flex: 1 1 45%; }
|
||||||
|
button { flex: 0;}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
@include text-base;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
@include text-base(600);
|
||||||
|
flex: 1 1 100%;
|
||||||
|
}
|
||||||
|
.group {
|
||||||
|
span {
|
||||||
|
@include text-xs;
|
||||||
|
color: var(--gray-hard-light);
|
||||||
|
}
|
||||||
|
p { @include text-base(600); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
.product-stock-modal {
|
||||||
|
.movement {
|
||||||
|
margin-bottom: 3.2rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 1.6rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--gray-soft-lightest);
|
||||||
|
border: 1px solid var(--gray-soft-dark);
|
||||||
|
color: var(--gray-soft-darkest);
|
||||||
|
@include text-base;
|
||||||
|
&.is-active {
|
||||||
|
border: 1px solid var(--gray-soft-darkest);
|
||||||
|
background-color: var(--gray-hard-darkest);
|
||||||
|
color: var(--gray-soft-lightest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button:first-of-type {
|
||||||
|
border-radius: var(--border-radius) 0 0 var(--border-radius);
|
||||||
|
}
|
||||||
|
button:last-of-type {
|
||||||
|
border-radius: 0 var(--border-radius) var(--border-radius) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -49,21 +49,7 @@
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.stock {
|
.stock-label { grid-area: stock; }
|
||||||
grid-area: stock;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
@include text-sm;
|
|
||||||
color: var(--status-color);
|
|
||||||
&::before {
|
|
||||||
content: "";
|
|
||||||
margin-right: 0.8rem;
|
|
||||||
width: 1rem;
|
|
||||||
height: 1rem;
|
|
||||||
background-color: var(--status-color);
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
button {
|
button {
|
||||||
grid-area: btn;
|
grid-area: btn;
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
|
@ -22,9 +22,13 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
& > *:not(:first-child) {
|
& > *:not(:first-child) {
|
||||||
margin-left: 2rem;
|
&::before {
|
||||||
padding-left: 2rem;
|
content: "";
|
||||||
border-left: 1px solid var(--gray-hard-darkest);
|
margin: 0 2rem;
|
||||||
|
width: 1px;
|
||||||
|
height: 2rem;
|
||||||
|
background-color: var(--gray-hard-darkest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort {
|
.sort {
|
||||||
|
@ -142,20 +142,6 @@
|
|||||||
background-color: var(--gray-soft-light);
|
background-color: var(--gray-soft-light);
|
||||||
border-radius: var(--border-radius-sm);
|
border-radius: var(--border-radius-sm);
|
||||||
|
|
||||||
.stock {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
@include text-sm;
|
|
||||||
color: var(--status-color);
|
|
||||||
&::before {
|
|
||||||
content: "";
|
|
||||||
margin-right: 0.8rem;
|
|
||||||
width: 1rem;
|
|
||||||
height: 1rem;
|
|
||||||
background-color: var(--status-color);
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.price {
|
.price {
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -1991,7 +1991,43 @@ en:
|
|||||||
save: "Save"
|
save: "Save"
|
||||||
product_stock_form:
|
product_stock_form:
|
||||||
low_stock_threshold: "Define a low stock threshold"
|
low_stock_threshold: "Define a low stock threshold"
|
||||||
toggle_stock_threshold: "Activate stock threshold"
|
stock_threshold_toggle: "Activate stock threshold"
|
||||||
|
stock_threshold_information: "<strong>Information</strong></br>Define a low stock threshold and receive a notification when it's reached.<br>Above the threshold, the product is available in the store. When the threshold is reached, the product quantity is labeled as low."
|
||||||
|
low_stock: "Low stock"
|
||||||
|
threshold_level: "Minimum threshold level"
|
||||||
|
threshold_alert: "Notify me when the threshold is reached"
|
||||||
|
event_type: "Events:"
|
||||||
|
stocks: "Stocks:"
|
||||||
|
internal: "Private stock"
|
||||||
|
external: "Public stock"
|
||||||
|
all: "All types"
|
||||||
|
stock_level: "Stock level"
|
||||||
|
events:
|
||||||
|
inward_stock: "Inward stock"
|
||||||
|
returned: "Returned by client"
|
||||||
|
canceled: "Canceled by client"
|
||||||
|
sold: "Sold"
|
||||||
|
missing: "Missing in stock"
|
||||||
|
damaged: "Damaged product"
|
||||||
|
events_history: "Events history"
|
||||||
|
modal_title: "Manage stock"
|
||||||
|
product_stock_modal:
|
||||||
|
internal: "Private stock"
|
||||||
|
external: "Public stock"
|
||||||
|
new_event: "New stock event"
|
||||||
|
addition: "Addition"
|
||||||
|
withdrawal: "Withdrawal"
|
||||||
|
update_stock: "Update stock"
|
||||||
|
event_type: "Events:"
|
||||||
|
stocks: "Stocks:"
|
||||||
|
quantity: "Quantity"
|
||||||
|
events:
|
||||||
|
inward_stock: "Inward stock"
|
||||||
|
returned: "Returned by client"
|
||||||
|
canceled: "Canceled by client"
|
||||||
|
sold: "Sold"
|
||||||
|
missing: "Missing in stock"
|
||||||
|
damaged: "Damaged product"
|
||||||
orders:
|
orders:
|
||||||
heading: "Orders"
|
heading: "Orders"
|
||||||
create_order: "Create an order"
|
create_order: "Create an order"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user