1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-20 14:54:15 +01:00

(quality) refactored categories sorting + fix ts issues

This commit is contained in:
Sylvain 2022-09-07 17:28:41 +02:00
parent 3f08d831b9
commit 45bac88b26
11 changed files with 43 additions and 54 deletions

View File

@ -42,7 +42,7 @@ export const FabModal: React.FC<FabModalProps> = ({ title, isOpen, toggleModal,
return (
<Modal isOpen={isOpen}
className={`fab-modal fab-modal-${width} ${className}`}
className={`fab-modal fab-modal-${width} ${className || ''}`}
overlayClassName="fab-modal-overlay"
onRequestClose={toggleModal}>
{closeButton && <FabButton className="modal-btn--close" onClick={toggleModal}>{t('app.shared.fab_modal.close')}</FabButton>}

View File

@ -95,7 +95,7 @@ export const StripeCardUpdate: React.FC<StripeCardUpdateProps> = ({ onSubmit, on
};
return (
<form onSubmit={handleSubmit} id="stripe-card" className={`stripe-card-update ${className}`}>
<form onSubmit={handleSubmit} id="stripe-card" className={`stripe-card-update ${className || ''}`}>
<CardElement options={cardOptions} />
{children}
</form>

View File

@ -69,7 +69,6 @@ export const ManageProductCategory: React.FC<ManageProductCategoryProps> = ({ pr
<div className='manage-product-category'>
{ toggleBtn() }
<FabModal title={t(`app.admin.store.manage_product_category.${action}`)}
className="fab-modal-lg"
width={ModalSize.large}
isOpen={isOpen}
toggleModal={toggleModal}

View File

@ -10,6 +10,7 @@ import { HtmlTranslate } from '../../base/html-translate';
import { IApplication } from '../../../models/application';
import { Loader } from '../../base/loader';
import { react2angular } from 'react2angular';
import ProductLib from '../../../lib/product';
declare const Application: IApplication;
@ -54,18 +55,7 @@ const ProductCategories: React.FC<ProductCategoriesProps> = ({ onSuccess, onErro
*/
const refreshCategories = () => {
ProductCategoryAPI.index().then(data => {
// Map product categories by position
const sortedCategories = data
.filter(c => !c.parent_id)
.sort((a, b) => a.position - b.position);
const childrenCategories = data
.filter(c => typeof c.parent_id === 'number')
.sort((a, b) => b.position - a.position);
childrenCategories.forEach(c => {
const parentIndex = sortedCategories.findIndex(i => i.id === c.parent_id);
sortedCategories.splice(parentIndex + 1, 0, c);
});
setProductCategories(sortedCategories);
setProductCategories(new ProductLib().sortCategories(data));
}).catch((error) => onError(error));
};

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { SubmitHandler, useForm, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import slugify from 'slugify';
import _ from 'lodash';
@ -19,6 +19,7 @@ import MachineAPI from '../../api/machine';
import ProductAPI from '../../api/product';
import { Plus } from 'phosphor-react';
import { ProductStockForm } from './product-stock-form';
import ProductLib from '../../lib/product';
interface ProductFormProps {
product: Product,
@ -53,18 +54,7 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
useEffect(() => {
ProductCategoryAPI.index().then(data => {
// Map product categories by position
const sortedCategories = data
.filter(c => !c.parent_id)
.sort((a, b) => a.position - b.position);
const childrenCategories = data
.filter(c => typeof c.parent_id === 'number')
.sort((a, b) => b.position - a.position);
childrenCategories.forEach(c => {
const parentIndex = sortedCategories.findIndex(i => i.id === c.parent_id);
sortedCategories.splice(parentIndex + 1, 0, c);
});
setProductCategories(buildSelectOptions(sortedCategories));
setProductCategories(buildSelectOptions(new ProductLib().sortCategories(data)));
}).catch(onError);
MachineAPI.index({ disabled: false }).then(data => {
setMachines(buildChecklistOptions(data));
@ -111,10 +101,8 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
/**
* Callback triggered when the form is submitted: process with the product creation or update.
*/
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
return handleSubmit((data: Product) => {
saveProduct(data);
})(event);
const onSubmit: SubmitHandler<Product> = (data: Product) => {
saveProduct(data);
};
/**
@ -236,13 +224,13 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
<FabButton className="main-action-btn" onClick={handleSubmit(saveProduct)}>{t('app.admin.store.product_form.save')}</FabButton>
</div>
</header>
<form className="product-form" onSubmit={onSubmit}>
<form className="product-form" onSubmit={handleSubmit(onSubmit)}>
<div className='tabs'>
<p className={!stockTab ? 'is-active' : ''} onClick={() => setStockTab(false)}>{t('app.admin.store.product_form.product_parameters')}</p>
<p className={stockTab ? 'is-active' : ''} onClick={() => setStockTab(true)}>{t('app.admin.store.product_form.stock_management')}</p>
</div>
{stockTab
? <ProductStockForm product={product} register={register} control={control} id="stock" onError={onError} onSuccess={onSuccess} />
? <ProductStockForm product={product} register={register} control={control} formState={formState} onError={onError} onSuccess={onSuccess} />
: <section>
<div className="subgrid">
<FormInput id="name"

View File

@ -12,6 +12,7 @@ import { FabButton } from '../base/fab-button';
import { PencilSimple } from 'phosphor-react';
import { FabModal, ModalSize } from '../base/fab-modal';
import { ProductStockModal } from './product-stock-modal';
import { FieldValues } from 'react-hook-form/dist/types/fields';
import { FabStateLabel } from '../base/fab-state-label';
interface ProductStockFormProps<TFieldValues, TContext extends object> {
@ -26,7 +27,7 @@ interface ProductStockFormProps<TFieldValues, TContext extends object> {
/**
* Form tab to manage a product's stock
*/
export const ProductStockForm = <TFieldValues, TContext extends object> ({ product, register, control, formState, onError, onSuccess }: ProductStockFormProps<TFieldValues, TContext>) => {
export const ProductStockForm = <TFieldValues extends FieldValues, TContext extends object> ({ product, register, control, formState, onError, onSuccess }: ProductStockFormProps<TFieldValues, TContext>) => {
const { t } = useTranslation('admin');
const [activeThreshold, setActiveThreshold] = useState<boolean>(false);
@ -197,12 +198,11 @@ export const ProductStockForm = <TFieldValues, TContext extends object> ({ produ
</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} />
<ProductStockModal product={product} register={register} control={control} formState={formState} onError={onError} onSuccess={onSuccess} />
</FabModal>
</section>
);

View File

@ -6,6 +6,7 @@ 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';
import { FieldValues } from 'react-hook-form/dist/types/fields';
type selectOption = { value: number, label: string };
@ -23,7 +24,7 @@ interface ProductStockModalProps<TFieldValues, TContext extends object> {
*/
// TODO: delete next eslint disable
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const ProductStockModal = <TFieldValues, TContext extends object> ({ product, register, control, formState, onError, onSuccess }: ProductStockModalProps<TFieldValues, TContext>) => {
export const ProductStockModal = <TFieldValues extends FieldValues, TContext extends object> ({ product, register, control, formState, onError, onSuccess }: ProductStockModalProps<TFieldValues, TContext>) => {
const { t } = useTranslation('admin');
const [movement, setMovement] = useState<'in' | 'out'>('in');

View File

@ -14,6 +14,7 @@ import MachineAPI from '../../api/machine';
import { AccordionItem } from './accordion-item';
import { X } from 'phosphor-react';
import { StoreListHeader } from './store-list-header';
import ProductLib from '../../lib/product';
declare const Application: IApplication;
@ -52,18 +53,7 @@ const Products: React.FC<ProductsProps> = ({ onSuccess, onError }) => {
});
ProductCategoryAPI.index().then(data => {
// Map product categories by position
const sortedCategories = data
.filter(c => !c.parent_id)
.sort((a, b) => a.position - b.position);
const childrenCategories = data
.filter(c => typeof c.parent_id === 'number')
.sort((a, b) => b.position - a.position);
childrenCategories.forEach(c => {
const parentIndex = sortedCategories.findIndex(i => i.id === c.parent_id);
sortedCategories.splice(parentIndex + 1, 0, c);
});
setProductCategories(sortedCategories);
setProductCategories(new ProductLib().sortCategories(data));
}).catch(onError);
MachineAPI.index({ disabled: false }).then(data => {

View File

@ -179,7 +179,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
const userNetworks = new UserLib(user).getUserSocialNetworks();
return (
<form className={`user-profile-form user-profile-form--${size} ${className}`} onSubmit={onSubmit}>
<form className={`user-profile-form user-profile-form--${size} ${className || ''}`} onSubmit={onSubmit}>
<div className="avatar-group">
<AvatarInput currentAvatar={output.profile_attributes?.user_avatar_attributes?.attachment_url}
userName={`${output.profile_attributes?.first_name} ${output.profile_attributes?.last_name}`}

View File

@ -0,0 +1,21 @@
import { ProductCategory } from '../models/product-category';
export default class ProductLib {
/**
* Map product categories by position
* @param categories unsorted categories, as returned by the API
*/
sortCategories = (categories: Array<ProductCategory>): Array<ProductCategory> => {
const sortedCategories = categories
.filter(c => !c.parent_id)
.sort((a, b) => a.position - b.position);
const childrenCategories = categories
.filter(c => typeof c.parent_id === 'number')
.sort((a, b) => b.position - a.position);
childrenCategories.forEach(c => {
const parentIndex = sortedCategories.findIndex(i => i.id === c.parent_id);
sortedCategories.splice(parentIndex + 1, 0, c);
});
return sortedCategories;
};
}

View File

@ -16,11 +16,11 @@ export interface Stock {
}
export interface Product {
id: number,
id?: number,
name: string,
slug: string,
sku: string,
description: string,
sku?: string,
description?: string,
is_active: boolean,
product_category_id?: number,
amount?: number,