1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-12-01 12:24:28 +01:00

(quality) refactor selectOption

This commit is contained in:
Sylvain 2022-10-25 17:33:14 +02:00
parent 47d6f2e165
commit 9f3cbc46a3
24 changed files with 86 additions and 212 deletions

View File

@ -9,6 +9,7 @@ import { FabPagination } from '../../base/fab-pagination';
import OrderAPI from '../../../api/order';
import { Order, OrderSortOption } from '../../../models/order';
import { User } from '../../../models/user';
import { SelectOption } from '../../../models/select';
declare const Application: IApplication;
@ -16,11 +17,6 @@ interface OrdersDashboardProps {
currentUser: User,
onError: (message: string) => void
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: OrderSortOption, label: string };
/**
* This component shows a list of all orders from the store for the current user
@ -44,7 +40,7 @@ export const OrdersDashboard: React.FC<OrdersDashboardProps> = ({ currentUser, o
/**
* Creates sorting options to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
const buildOptions = (): Array<SelectOption<OrderSortOption>> => {
return [
{ value: 'created_at-desc', label: t('app.public.orders_dashboard.sort.newest') },
{ value: 'created_at-asc', label: t('app.public.orders_dashboard.sort.oldest') }
@ -53,7 +49,7 @@ export const OrdersDashboard: React.FC<OrdersDashboardProps> = ({ currentUser, o
/**
* Display option: sorting
*/
const handleSorting = (option: selectOption) => {
const handleSorting = (option: SelectOption<OrderSortOption>) => {
OrderAPI.index({ page: 1, sort: option.value }).then(res => {
setCurrentPage(1);
setOrders(res.data);

View File

@ -7,6 +7,7 @@ import { Event } from '../../models/event';
import { EventTheme } from '../../models/event-theme';
import { IApplication } from '../../models/application';
import EventThemeAPI from '../../api/event-theme';
import { SelectOption } from '../../models/select';
declare const Application: IApplication;
@ -15,12 +16,6 @@ interface EventThemesProps {
onChange: (themes: Array<EventTheme>) => void
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, label: string };
/**
* This component shows a select input to edit the themes associated with the event
*/
@ -43,7 +38,7 @@ export const EventThemes: React.FC<EventThemesProps> = ({ event, onChange }) =>
/**
* Return the current theme(s) for the given event, formatted to match the react-select format
*/
const defaultValues = (): Array<selectOption> => {
const defaultValues = (): Array<SelectOption<number>> => {
const res = [];
themes.forEach(t => {
if (event.event_theme_ids && event.event_theme_ids.indexOf(t.id) > -1) {
@ -57,7 +52,7 @@ export const EventThemes: React.FC<EventThemesProps> = ({ event, onChange }) =>
* Callback triggered when the selection has changed.
* Convert the react-select specific format to an array of EventTheme, and call the provided callback.
*/
const handleChange = (selectedOptions: Array<selectOption>): void => {
const handleChange = (selectedOptions: Array<SelectOption<number>>): void => {
const res = [];
selectedOptions.forEach(opt => {
res.push(themes.find(t => t.id === opt.value));
@ -68,7 +63,7 @@ export const EventThemes: React.FC<EventThemesProps> = ({ event, onChange }) =>
/**
* Convert all themes to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
const buildOptions = (): Array<SelectOption<number>> => {
return themes.map(t => {
return { value: t.id, label: t.name };
});

View File

@ -7,11 +7,7 @@ import { UnpackNestedValue } from 'react-hook-form/dist/types';
import { FormControlledComponent } from '../../models/form-component';
import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
import { FabButton } from '../base/fab-button';
/**
* Checklist Option format
*/
export type ChecklistOption<TOptionValue> = { value: TOptionValue, label: string };
import { ChecklistOption } from '../../models/select';
interface FormChecklistProps<TFieldValues, TOptionValue, TContext extends object> extends FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
defaultValue?: Array<TOptionValue>,

View File

@ -7,9 +7,10 @@ import { FieldPath } from 'react-hook-form/dist/types/path';
import { FieldPathValue, UnpackNestedValue } from 'react-hook-form/dist/types';
import { FormControlledComponent } from '../../models/form-component';
import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
import { SelectOption } from '../../models/select';
interface FormSelectProps<TFieldValues, TContext extends object, TOptionValue> extends FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
options: Array<selectOption<TOptionValue>>,
interface FormSelectProps<TFieldValues, TContext extends object, TOptionValue, TOptionLabel> extends FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
options: Array<SelectOption<TOptionValue, TOptionLabel>>,
valueDefault?: TOptionValue,
onChange?: (value: TOptionValue) => void,
placeholder?: string,
@ -17,16 +18,10 @@ interface FormSelectProps<TFieldValues, TContext extends object, TOptionValue> e
creatable?: boolean,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption<TOptionValue> = { value: TOptionValue, label: string };
/**
* This component is a wrapper for react-select to use with react-hook-form
*/
export const FormSelect = <TFieldValues extends FieldValues, TContext extends object, TOptionValue>({ id, label, tooltip, className, control, placeholder, options, valueDefault, error, warning, rules, disabled = false, onChange, clearable = false, formState, creatable = false }: FormSelectProps<TFieldValues, TContext, TOptionValue>) => {
export const FormSelect = <TFieldValues extends FieldValues, TContext extends object, TOptionValue, TOptionLabel>({ id, label, tooltip, className, control, placeholder, options, valueDefault, error, warning, rules, disabled = false, onChange, clearable = false, formState, creatable = false }: FormSelectProps<TFieldValues, TContext, TOptionValue, TOptionLabel>) => {
const [isDisabled, setIsDisabled] = React.useState<boolean>(false);
useEffect(() => {

View File

@ -12,6 +12,7 @@ import { FormSelect } from '../form/form-select';
import MemberAPI from '../../api/member';
import SettingAPI from '../../api/setting';
import UserLib from '../../lib/user';
import { SelectOption } from '../../models/select';
declare const Application: IApplication;
@ -23,12 +24,6 @@ interface ChangeGroupProps {
className?: string,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, label: string };
/**
* Component to display the group of the provided user, and allow him to change his group.
*/
@ -72,7 +67,7 @@ export const ChangeGroup: React.FC<ChangeGroupProps> = ({ user, onSuccess, onErr
/**
* Convert the provided array of items to the react-select format
*/
const buildGroupsOptions = (): Array<selectOption> => {
const buildGroupsOptions = (): Array<SelectOption<number>> => {
return groups?.map(t => {
return { value: t.id, label: t.name };
});

View File

@ -1,17 +1,12 @@
import React from 'react';
import Select from 'react-select';
import { useTranslation } from 'react-i18next';
import { SelectOption } from '../../models/select';
interface MachinesFiltersProps {
onStatusSelected: (enabled: boolean) => void,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: boolean, label: string };
/**
* Allows filtering on machines list
*/
@ -23,7 +18,7 @@ export const MachinesFilters: React.FC<MachinesFiltersProps> = ({ onStatusSelect
/**
* Provides boolean options in the react-select format (yes/no/all)
*/
const buildBooleanOptions = (): Array<selectOption> => {
const buildBooleanOptions = (): Array<SelectOption<boolean>> => {
return [
defaultValue,
{ value: false, label: t('app.public.machines_filters.status_disabled') },
@ -34,7 +29,7 @@ export const MachinesFilters: React.FC<MachinesFiltersProps> = ({ onStatusSelect
/**
* Callback triggered when the user selects a machine status in the dropdown list
*/
const handleStatusSelected = (option: selectOption): void => {
const handleStatusSelected = (option: SelectOption<boolean>): void => {
onStatusSelected(option.value);
};

View File

@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import { FabModal } from '../base/fab-modal';
import { PaymentMethod, PaymentSchedule } from '../../models/payment-schedule';
import PaymentScheduleAPI from '../../api/payment-schedule';
import { SelectOption } from '../../models/select';
interface UpdatePaymentMeanModalProps {
isOpen: boolean,
@ -13,12 +14,6 @@ interface UpdatePaymentMeanModalProps {
paymentSchedule: PaymentSchedule
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: PaymentMethod, label: string };
/**
* Component to allow the member to change his payment mean for the given payment schedule (e.g. from card to transfer)
*/
@ -30,7 +25,7 @@ export const UpdatePaymentMeanModal: React.FC<UpdatePaymentMeanModalProps> = ({
/**
* Convert all payment means to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
const buildOptions = (): Array<SelectOption<PaymentMethod>> => {
return Object.keys(PaymentMethod).filter(pm => PaymentMethod[pm] !== PaymentMethod.Card).map(pm => {
return { value: PaymentMethod[pm], label: t(`app.admin.update_payment_mean_modal.method_${pm}`) };
});
@ -39,7 +34,7 @@ export const UpdatePaymentMeanModal: React.FC<UpdatePaymentMeanModalProps> = ({
/**
* When the payment mean is changed in the select, update the state
*/
const handleMeanSelected = (option: selectOption): void => {
const handleMeanSelected = (option: SelectOption<PaymentMethod>): void => {
setPaymentMean(option.value);
};

View File

@ -9,16 +9,11 @@ import { CardPaymentModal } from '../card-payment-modal';
import { PaymentSchedule } from '../../../models/payment-schedule';
import { HtmlTranslate } from '../../base/html-translate';
import CheckoutAPI from '../../../api/checkout';
import { SelectOption } from '../../../models/select';
const ALL_SCHEDULE_METHODS = ['card', 'check', 'transfer'] as const;
type scheduleMethod = typeof ALL_SCHEDULE_METHODS[number];
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: scheduleMethod, label: string };
/**
* A form component to ask for confirmation before cashing a payment directly at the FabLab's reception.
* This is intended for use by privileged users.
@ -44,14 +39,14 @@ export const LocalPaymentForm: React.FC<GatewayFormProps> = ({ onSubmit, onSucce
/**
* Convert all payement methods for schedules to the react-select format
*/
const buildMethodOptions = (): Array<selectOption> => {
const buildMethodOptions = (): Array<SelectOption<scheduleMethod>> => {
return ALL_SCHEDULE_METHODS.map(i => methodToOption(i));
};
/**
* Convert the given payment-method to the react-select format
*/
const methodToOption = (value: scheduleMethod): selectOption => {
const methodToOption = (value: scheduleMethod): SelectOption<scheduleMethod> => {
if (!value) return { value, label: '' };
return { value, label: t(`app.admin.local_payment_form.method_${value}`) };
@ -60,7 +55,7 @@ export const LocalPaymentForm: React.FC<GatewayFormProps> = ({ onSubmit, onSucce
/**
* Callback triggered when the user selects a payment method for the current payment schedule.
*/
const handleUpdateMethod = (option: selectOption) => {
const handleUpdateMethod = (option: SelectOption<scheduleMethod>) => {
updateCart(Object.assign({}, cart, { payment_method: option.value }));
setMethod(option.value);
};

View File

@ -5,6 +5,7 @@ import { Group } from '../../models/group';
import { User } from '../../models/user';
import PlanAPI from '../../api/plan';
import { PlansDuration } from '../../models/plan';
import { SelectOption } from '../../models/select';
interface PlansFilterProps {
user?: User,
@ -14,12 +15,6 @@ interface PlansFilterProps {
onDurationSelected: (plansIds: Array<number>) => void,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, label: string };
/**
* Allows filtering on plans list
*/
@ -38,7 +33,7 @@ export const PlansFilter: React.FC<PlansFilterProps> = ({ user, groups, onGroupS
/**
* Convert all groups to the react-select format
*/
const buildGroupOptions = (): Array<selectOption> => {
const buildGroupOptions = (): Array<SelectOption<number>> => {
return groups.filter(g => !g.disabled && g.slug !== 'admins').map(g => {
return { value: g.id, label: g.name };
});
@ -47,7 +42,7 @@ export const PlansFilter: React.FC<PlansFilterProps> = ({ user, groups, onGroupS
/**
* Convert all durations to the react-select format
*/
const buildDurationOptions = (): Array<selectOption> => {
const buildDurationOptions = (): Array<SelectOption<number>> => {
const options = durations.map((d, index) => {
return { value: index, label: d.name };
});
@ -58,14 +53,14 @@ export const PlansFilter: React.FC<PlansFilterProps> = ({ user, groups, onGroupS
/**
* Callback triggered when the user selects a group in the dropdown list
*/
const handleGroupSelected = (option: selectOption): void => {
const handleGroupSelected = (option: SelectOption<number>): void => {
onGroupSelected(option.value);
};
/**
* Callback triggered when the user selects a duration in the dropdown list
*/
const handleDurationSelected = (option: selectOption): void => {
const handleDurationSelected = (option: SelectOption<number>): void => {
onDurationSelected(durations[option.value]?.plans_ids);
};

View File

@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
import { useImmer } from 'use-immer';
import { FabInput } from '../../base/fab-input';
import { IFablab } from '../../../models/fablab';
import { SelectOption } from '../../../models/select';
declare let Fablab: IFablab;
@ -18,12 +19,6 @@ interface PackFormProps {
const ALL_INTERVALS = ['day', 'week', 'month', 'year'] as const;
type interval = typeof ALL_INTERVALS[number];
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: interval, label: string };
/**
* A form component to create/edit a PrepaidPack.
* The form validation must be created elsewhere, using the attribute form={formId}.
@ -36,14 +31,14 @@ export const PackForm: React.FC<PackFormProps> = ({ formId, onSubmit, pack }) =>
/**
* Convert all validity-intervals to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
const buildOptions = (): Array<SelectOption<interval>> => {
return ALL_INTERVALS.map(i => intervalToOption(i));
};
/**
* Convert the given validity-interval to the react-select format
*/
const intervalToOption = (value: interval): selectOption => {
const intervalToOption = (value: interval): SelectOption<interval> => {
if (!value) return { value, label: '' };
return { value, label: t(`app.admin.pack_form.intervals.${value}`, { COUNT: packData.validity_count || 0 }) };
@ -87,7 +82,7 @@ export const PackForm: React.FC<PackFormProps> = ({ formId, onSubmit, pack }) =>
/**
* Callback triggered when the user selects a type of interval for the current pack.
*/
const handleUpdateValidityInterval = (option: selectOption) => {
const handleUpdateValidityInterval = (option: SelectOption<interval>) => {
updatePackData(draft => {
draft.validity_interval = option.value as interval;
});

View File

@ -8,6 +8,7 @@ import { ProductCategory } from '../../../models/product-category';
import { FabButton } from '../../base/fab-button';
import ProductCategoryAPI from '../../../api/product-category';
import { HtmlTranslate } from '../../base/html-translate';
import { SelectOption } from '../../../models/select';
interface ProductCategoryFormProps {
action: 'create' | 'update' | 'delete',
@ -17,12 +18,6 @@ interface ProductCategoryFormProps {
onError: (message: string) => void,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, label: string };
/**
* Form to create/edit/delete a product category
*/
@ -40,7 +35,7 @@ export const ProductCategoryForm: React.FC<ProductCategoryFormProps> = ({ action
/**
* Convert all parents to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
const buildOptions = (): Array<SelectOption<number>> => {
const options = parents.map(t => {
return { value: t.id, label: t.name };
});

View File

@ -7,6 +7,7 @@ import { FormSelect } from '../../form/form-select';
import { FormInput } from '../../form/form-input';
import { useForm } from 'react-hook-form';
import _ from 'lodash';
import { SelectOption } from '../../../models/select';
interface StockFilterProps {
onApplyFilters: (filters: ProductIndexFilter) => void,
@ -14,12 +15,6 @@ interface StockFilterProps {
openDefault?: boolean
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: StockType, label: string };
/**
* Component to filter the products list by stock
*/
@ -51,7 +46,7 @@ export const StockFilter: React.FC<StockFilterProps> = ({ onApplyFilters, curren
};
/** Creates sorting options to the react-select format */
const buildStockOptions = (): Array<selectOption> => {
const buildStockOptions = (): Array<SelectOption<StockType>> => {
return [
{ value: 'internal', label: t('app.admin.store.stock_filter.stock_internal') },
{ value: 'external', label: t('app.admin.store.stock_filter.stock_external') }

View File

@ -6,6 +6,7 @@ import OrderAPI from '../../api/order';
import { Order } from '../../models/order';
import FabTextEditor from '../base/text-editor/fab-text-editor';
import { HtmlTranslate } from '../base/html-translate';
import { SelectOption } from '../../models/select';
interface OrderActionsProps {
order: Order,
@ -13,18 +14,12 @@ interface OrderActionsProps {
onError: (message: string) => void,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: string, label: string };
/**
* Actions for an order
*/
export const OrderActions: React.FC<OrderActionsProps> = ({ order, onSuccess, onError }) => {
const { t } = useTranslation('shared');
const [currentAction, setCurrentAction] = useState<selectOption>();
const [currentAction, setCurrentAction] = useState<SelectOption<string>>();
const [modalIsOpen, setModalIsOpen] = useState<boolean>(false);
const [readyNote, setReadyNote] = useState<string>('');
@ -51,7 +46,7 @@ export const OrderActions: React.FC<OrderActionsProps> = ({ order, onSuccess, on
/**
* Creates sorting options to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
const buildOptions = (): Array<SelectOption<string>> => {
let actions = [];
switch (order.state) {
case 'paid':
@ -80,7 +75,7 @@ export const OrderActions: React.FC<OrderActionsProps> = ({ order, onSuccess, on
/**
* Callback after selecting an action
*/
const handleAction = (action: selectOption) => {
const handleAction = (action: SelectOption<string>) => {
setCurrentAction(action);
setModalIsOpen(true);
};

View File

@ -16,6 +16,7 @@ import OrderAPI from '../../api/order';
import { Order, OrderIndexFilter, OrderSortOption } from '../../models/order';
import { FabPagination } from '../base/fab-pagination';
import { CaretDoubleUp, X } from 'phosphor-react';
import { ChecklistOption, SelectOption } from '../../models/select';
declare const Application: IApplication;
@ -23,16 +24,6 @@ interface OrdersProps {
currentUser?: User,
onError: (message: string) => void,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: OrderSortOption, label: string };
/**
* Option format, expected by checklist
*/
type checklistOption = { value: string, label: string };
const initFilters: OrderIndexFilter = {
reference: '',
@ -72,14 +63,7 @@ const Orders: React.FC<OrdersProps> = ({ currentUser, onError }) => {
}).catch(onError);
}, [filters]);
/**
* Create a new order
*/
const newOrder = () => {
console.log('Create new order');
};
const statusOptions: checklistOption[] = [
const statusOptions: ChecklistOption<string>[] = [
{ value: 'cart', label: t('app.admin.store.orders.state.cart') },
{ value: 'paid', label: t('app.admin.store.orders.state.paid') },
{ value: 'payment_failed', label: t('app.admin.store.orders.state.payment_failed') },
@ -174,7 +158,7 @@ const Orders: React.FC<OrdersProps> = ({ currentUser, onError }) => {
/**
* Creates sorting options to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
const buildOptions = (): Array<SelectOption<OrderSortOption>> => {
return [
{ value: 'created_at-desc', label: t('app.admin.store.orders.sort.newest') },
{ value: 'created_at-asc', label: t('app.admin.store.orders.sort.oldest') }
@ -184,7 +168,7 @@ const Orders: React.FC<OrdersProps> = ({ currentUser, onError }) => {
/**
* Display option: sorting
*/
const handleSorting = (option: selectOption) => {
const handleSorting = (option: SelectOption<OrderSortOption>) => {
setFilters(draft => {
draft.sort = option.value;
});
@ -200,7 +184,7 @@ const Orders: React.FC<OrdersProps> = ({ currentUser, onError }) => {
/**
* Filter: by status
*/
const handleSelectStatus = (s: checklistOption, checked: boolean) => {
const handleSelectStatus = (s: ChecklistOption<string>, checked: boolean) => {
const list = [...states];
checked
? list.push(s.value)
@ -250,11 +234,6 @@ const Orders: React.FC<OrdersProps> = ({ currentUser, onError }) => {
<div className='orders'>
<header>
<h2>{t('app.admin.store.orders.heading')}</h2>
{false &&
<div className='grpBtn'>
<FabButton className="main-action-btn" onClick={newOrder}>{t('app.admin.store.orders.create_order')}</FabButton>
</div>
}
</header>
<aside className={`store-filters ${filtersPanel ? '' : 'collapsed'}`}>

View File

@ -23,6 +23,7 @@ import { CloneProductModal } from './clone-product-modal';
import ProductLib from '../../lib/product';
import { UnsavedFormAlert } from '../form/unsaved-form-alert';
import { UIRouter } from '@uirouter/angularjs';
import { SelectOption, ChecklistOption } from '../../models/select';
interface ProductFormProps {
product: Product,
@ -32,17 +33,6 @@ interface ProductFormProps {
uiRouter: UIRouter
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, label: string | JSX.Element };
/**
* Option format, expected by checklist
*/
type checklistOption = { value: number, label: string };
/**
* Form component to create or update a product
*/
@ -52,8 +42,8 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
const { handleSubmit, register, control, formState, setValue, reset } = useForm<Product>({ defaultValues: { ...product } });
const output = useWatch<Product>({ control });
const [isActivePrice, setIsActivePrice] = useState<boolean>(product.id && _.isFinite(product.amount));
const [productCategories, setProductCategories] = useState<selectOption[]>([]);
const [machines, setMachines] = useState<checklistOption[]>([]);
const [productCategories, setProductCategories] = useState<SelectOption<number, string | JSX.Element>[]>([]);
const [machines, setMachines] = useState<ChecklistOption<number>[]>([]);
const [stockTab, setStockTab] = useState<boolean>(false);
const [openCloneModal, setOpenCloneModal] = useState<boolean>(false);
const [saving, setSaving] = useState<boolean>(false);
@ -70,7 +60,7 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
/**
* Convert the provided array of items to the react-select format
*/
const buildSelectOptions = (items: Array<{ id?: number, name: string, parent_id?: number }>): Array<selectOption> => {
const buildSelectOptions = (items: Array<{ id?: number, name: string, parent_id?: number }>): Array<SelectOption<number, string | JSX.Element>> => {
return items.map(t => {
return {
value: t.id,
@ -84,7 +74,7 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
/**
* Convert the provided array of items to the checklist format
*/
const buildChecklistOptions = (items: Array<{ id?: number, name: string }>): Array<checklistOption> => {
const buildChecklistOptions = (items: Array<{ id?: number, name: string }>): Array<ChecklistOption<number>> => {
return items.map(t => {
return { value: t.id, label: t.name };
});

View File

@ -28,6 +28,7 @@ import { ActiveFiltersTags } from './filters/active-filters-tags';
import SettingAPI from '../../api/setting';
import { UIRouter } from '@uirouter/angularjs';
import { CaretDoubleUp } from 'phosphor-react';
import { SelectOption } from '../../models/select';
declare const Application: IApplication;
@ -36,11 +37,6 @@ interface ProductsProps {
onError: (message: string) => void,
uiRouter: UIRouter,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: ProductSortOption, label: string };
/** This component shows the admin view of the store */
const Products: React.FC<ProductsProps> = ({ onSuccess, onError, uiRouter }) => {
@ -171,7 +167,7 @@ const Products: React.FC<ProductsProps> = ({ onSuccess, onError, uiRouter }) =>
};
/** Display option: sorting */
const handleSorting = (option: selectOption) => {
const handleSorting = (option: SelectOption<ProductSortOption>) => {
ProductLib.updateFilter(setResources, 'sort', option.value);
};
@ -183,7 +179,7 @@ const Products: React.FC<ProductsProps> = ({ onSuccess, onError, uiRouter }) =>
};
/** Creates sorting options to the react-select format */
const buildSortOptions = (): Array<selectOption> => {
const buildSortOptions = (): Array<SelectOption<ProductSortOption>> => {
return [
{ value: 'name-asc', label: t('app.admin.store.products.sort.name_az') },
{ value: 'name-desc', label: t('app.admin.store.products.sort.name_za') },

View File

@ -3,21 +3,17 @@ import { useTranslation } from 'react-i18next';
import Select from 'react-select';
import Switch from 'react-switch';
import { SortOption } from '../../models/api';
import { SelectOption } from '../../models/select';
interface StoreListHeaderProps {
productsCount: number,
selectOptions: selectOption[],
onSelectOptionsChange: (option: selectOption) => void,
selectOptions: SelectOption<SortOption>[],
onSelectOptionsChange: (option: SelectOption<SortOption>) => void,
selectValue?: SortOption,
switchLabel?: string,
switchChecked?: boolean,
onSwitch?: (boolean) => void
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: SortOption, label: string };
/**
* Renders an accordion item

View File

@ -28,6 +28,7 @@ import { ActiveFiltersTags } from './filters/active-filters-tags';
import ProductLib from '../../lib/product';
import { UIRouter } from '@uirouter/angularjs';
import SettingAPI from '../../api/setting';
import { SelectOption } from '../../models/select';
declare const Application: IApplication;
@ -50,11 +51,6 @@ interface StoreProps {
currentUser: User,
uiRouter: UIRouter,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: ProductSortOption, label: string };
/**
* This component shows public store
@ -165,7 +161,7 @@ const Store: React.FC<StoreProps> = ({ onError, onSuccess, currentUser, uiRouter
/**
* Creates sorting options to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
const buildOptions = (): Array<SelectOption<ProductSortOption>> => {
return [
{ value: 'name-asc', label: t('app.public.store.products.sort.name_az') },
{ value: 'name-desc', label: t('app.public.store.products.sort.name_za') },
@ -176,7 +172,7 @@ const Store: React.FC<StoreProps> = ({ onError, onSuccess, currentUser, uiRouter
/**
* Display option: sorting
*/
const handleSorting = (option: selectOption) => {
const handleSorting = (option: SelectOption<ProductSortOption>) => {
ProductLib.updateFilter(setResources, 'sort', option.value);
};

View File

@ -18,6 +18,7 @@ import { PaymentScheduleSummary } from '../payment-schedule/payment-schedule-sum
import { PaymentSchedule } from '../../models/payment-schedule';
import PriceAPI from '../../api/price';
import { LocalPaymentModal } from '../payment/local-payment/local-payment-modal';
import { SelectOption } from '../../models/select';
declare const Application: IApplication;
@ -30,12 +31,6 @@ interface SubscribeModalProps {
onError: (message: string) => void,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, label: string };
/**
* Modal dialog shown to create a subscription for the given customer
*/
@ -91,7 +86,7 @@ export const SubscribeModal: React.FC<SubscribeModalProps> = ({ isOpen, toggleMo
/**
* Callback triggered when the user selects a group in the dropdown list
*/
const handlePlanSelect = (option: selectOption): void => {
const handlePlanSelect = (option: SelectOption<number>): void => {
const plan = allPlans.find(p => p.id === option.value);
setSelectedPlan(plan);
};
@ -116,7 +111,7 @@ export const SubscribeModal: React.FC<SubscribeModalProps> = ({ isOpen, toggleMo
/**
* Convert all groups to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
const buildOptions = (): Array<SelectOption<number>> => {
if (!allPlans) return [];
return allPlans.filter(p => !p.disabled && p.group_id === customer.group_id).map(p => {

View File

@ -4,6 +4,7 @@ import Select from 'react-select';
import { FabInput } from '../base/fab-input';
import { ProofOfIdentityType } from '../../models/proof-of-identity-type';
import { Group } from '../../models/group';
import { SelectOption } from '../../models/select';
interface SupportingDocumentsTypeFormProps {
groups: Array<Group>,
@ -11,12 +12,6 @@ interface SupportingDocumentsTypeFormProps {
onChange: (field: string, value: string | Array<number>) => void,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, label: string };
/**
* Form to set create/edit supporting documents type
*/
@ -26,7 +21,7 @@ export const SupportingDocumentsTypeForm: React.FC<SupportingDocumentsTypeFormPr
/**
* Convert all groups to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
const buildOptions = (): Array<SelectOption<number>> => {
return groups.map(t => {
return { value: t.id, label: t.name };
});
@ -35,7 +30,7 @@ export const SupportingDocumentsTypeForm: React.FC<SupportingDocumentsTypeFormPr
/**
* Return the group(s) associated with the current type, formatted to match the react-select format
*/
const groupsValues = (): Array<selectOption> => {
const groupsValues = (): Array<SelectOption<number>> => {
const res = [];
const groupIds = proofOfIdentityType?.group_ids || [];
if (groupIds.length > 0) {
@ -51,7 +46,7 @@ export const SupportingDocumentsTypeForm: React.FC<SupportingDocumentsTypeFormPr
/**
* Callback triggered when the selection of group has changed.
*/
const handleGroupsChange = (selectedOptions: Array<selectOption>): void => {
const handleGroupsChange = (selectedOptions: Array<SelectOption<number>>): void => {
onChange('group_ids', selectedOptions.map(o => o.value));
};

View File

@ -3,6 +3,7 @@ import AsyncSelect from 'react-select/async';
import { useTranslation } from 'react-i18next';
import MemberAPI from '../../api/member';
import { User } from '../../models/user';
import { SelectOption } from '../../models/select';
interface MemberSelectProps {
defaultUser?: User,
@ -12,18 +13,12 @@ interface MemberSelectProps {
hasError?: boolean
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, label: string };
/**
* This component renders the member select for manager.
*/
export const MemberSelect: React.FC<MemberSelectProps> = ({ defaultUser, value, onSelected, noHeader, hasError }) => {
const { t } = useTranslation('public');
const [option, setOption] = useState<selectOption>();
const [option, setOption] = useState<SelectOption<number>>();
useEffect(() => {
if (defaultUser) {
@ -49,7 +44,7 @@ export const MemberSelect: React.FC<MemberSelectProps> = ({ defaultUser, value,
/**
* search members by name
*/
const loadMembers = async (inputValue: string): Promise<Array<selectOption>> => {
const loadMembers = async (inputValue: string): Promise<Array<SelectOption<number>>> => {
if (!inputValue) {
return [];
}
@ -62,7 +57,7 @@ export const MemberSelect: React.FC<MemberSelectProps> = ({ defaultUser, value,
/**
* callback for handle select changed
*/
const onChange = (v: selectOption) => {
const onChange = (v: SelectOption<number>) => {
setOption(v);
onSelected({ id: v.value, name: v.label });
};

View File

@ -30,6 +30,7 @@ import ProfileCustomFieldAPI from '../../api/profile-custom-field';
import { ProfileCustomField } from '../../models/profile-custom-field';
import { SettingName } from '../../models/setting';
import SettingAPI from '../../api/setting';
import { SelectOption } from '../../models/select';
declare const Application: IApplication;
@ -46,12 +47,6 @@ interface UserProfileFormProps {
showTagsInput?: boolean,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, label: string };
/**
* Form component to create or update a user
*/
@ -67,7 +62,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
const [isOrganization, setIsOrganization] = useState<boolean>(!_isNil(user.invoicing_profile_attributes.organization_attributes));
const [isLocalDatabaseProvider, setIsLocalDatabaseProvider] = useState<boolean>(false);
const [groups, setGroups] = useState<selectOption[]>([]);
const [groups, setGroups] = useState<SelectOption<number>[]>([]);
const [termsAndConditions, setTermsAndConditions] = useState<CustomAsset>(null);
const [profileCustomFields, setProfileCustomFields] = useState<ProfileCustomField[]>([]);
const [requiredFieldsSettings, setRequiredFieldsSettings] = useState<Map<SettingName, string>>(new Map());
@ -107,7 +102,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
/**
* Convert the provided array of items to the react-select format
*/
const buildOptions = (items: Array<{ id?: number, name: string }>): Array<selectOption> => {
const buildOptions = (items: Array<{ id?: number, name: string }>): Array<SelectOption<number>> => {
return items.map(t => {
return { value: t.id, label: t.name };
});
@ -116,7 +111,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
/**
* Asynchronously load the full list of enabled trainings to display in the drop-down select field
*/
const loadTrainings = (inputValue: string, callback: (options: Array<selectOption>) => void): void => {
const loadTrainings = (inputValue: string, callback: (options: Array<SelectOption<number>>) => void): void => {
TrainingAPI.index({ disabled: false }).then(data => {
callback(buildOptions(data));
}).catch(error => onError(error));
@ -125,7 +120,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
/**
* Asynchronously load the full list of tags to display in the drop-down select field
*/
const loadTags = (inputValue: string, callback: (options: Array<selectOption>) => void): void => {
const loadTags = (inputValue: string, callback: (options: Array<SelectOption<number>>) => void): void => {
TagAPI.index().then(data => {
callback(buildOptions(data));
}).catch(error => onError(error));

View File

@ -80,10 +80,6 @@ export default class ProductLib {
let list = [...currentSelection];
const children = allCategories
.filter(el => el.parent_id === category.id);
/*
const siblings = allCategories
.filter(el => el.parent_id === category.parent_id && el.parent_id !== null);
*/
if (operation === 'add') {
list.push(category);
@ -91,12 +87,6 @@ export default class ProductLib {
// if a parent category is selected, we automatically select all its children
list = [...Array.from(new Set([...list, ...children]))];
}
/*
if (siblings.length && siblings.every(el => list.includes(el))) {
// if a child category is selected, with every sibling of it, we automatically select its parent
list.push(allCategories.find(p => p.id === siblings[0].parent_id));
}
*/
} else {
list.splice(list.indexOf(category), 1);
const parent = allCategories.find(p => p.id === category.parent_id);

View File

@ -0,0 +1,10 @@
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
export type SelectOption<TOptionValue, TOptionLabel = string> = { value: TOptionValue, label: TOptionLabel }
/**
* Checklist Option format
*/
export type ChecklistOption<TOptionValue> = { value: TOptionValue, label: string };