mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-30 19:52:20 +01:00
(bug) show error validation message
This commit is contained in:
parent
5658e0aeed
commit
4dde127203
@ -24,9 +24,10 @@ export const AdvancedAccountingForm = <TFieldValues extends FieldValues>({ regis
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
|
<p>toto</p>
|
||||||
{isEnabled && <>
|
{isEnabled && <>
|
||||||
<header>
|
<header>
|
||||||
<p className="title">{t('app.admin.advanced_accounting_form.title')}</p>
|
<p className="title" role="heading">{t('app.admin.advanced_accounting_form.title')}</p>
|
||||||
</header>
|
</header>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<FormInput register={register}
|
<FormInput register={register}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { UseFormRegister } from 'react-hook-form';
|
import { UseFormRegister, FormState } from 'react-hook-form';
|
||||||
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FormInput } from '../form/form-input';
|
import { FormInput } from '../form/form-input';
|
||||||
@ -6,12 +6,13 @@ import { FormInput } from '../form/form-input';
|
|||||||
export interface BooleanMappingFormProps<TFieldValues> {
|
export interface BooleanMappingFormProps<TFieldValues> {
|
||||||
register: UseFormRegister<TFieldValues>,
|
register: UseFormRegister<TFieldValues>,
|
||||||
fieldMappingId: number,
|
fieldMappingId: number,
|
||||||
|
formState: FormState<TFieldValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial form to map an internal boolean field to an external API providing a string value.
|
* Partial form to map an internal boolean field to an external API providing a string value.
|
||||||
*/
|
*/
|
||||||
export const BooleanMappingForm = <TFieldValues extends FieldValues>({ register, fieldMappingId }: BooleanMappingFormProps<TFieldValues>) => {
|
export const BooleanMappingForm = <TFieldValues extends FieldValues>({ register, fieldMappingId, formState }: BooleanMappingFormProps<TFieldValues>) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -20,10 +21,12 @@ export const BooleanMappingForm = <TFieldValues extends FieldValues>({ register,
|
|||||||
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.true_value`}
|
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.true_value`}
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.authentication.boolean_mapping_form.true_value')} />
|
label={t('app.admin.authentication.boolean_mapping_form.true_value')} />
|
||||||
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.false_value`}
|
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.false_value`}
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.authentication.boolean_mapping_form.false_value')} />
|
label={t('app.admin.authentication.boolean_mapping_form.false_value')} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -3,7 +3,7 @@ import { UseFormRegister, useFieldArray, ArrayPath, useWatch, Path, FieldPathVal
|
|||||||
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
import AuthProviderAPI from '../../api/auth-provider';
|
import AuthProviderAPI from '../../api/auth-provider';
|
||||||
import { AuthenticationProviderMapping, MappingFields, mappingType, ProvidableType } from '../../models/authentication-provider';
|
import { AuthenticationProviderMapping, MappingFields, mappingType, ProvidableType } from '../../models/authentication-provider';
|
||||||
import { Control, UnpackNestedValue, UseFormSetValue } from 'react-hook-form/dist/types/form';
|
import { Control, UnpackNestedValue, UseFormSetValue, FormState } from 'react-hook-form/dist/types/form';
|
||||||
import { FormSelect } from '../form/form-select';
|
import { FormSelect } from '../form/form-select';
|
||||||
import { FormInput } from '../form/form-input';
|
import { FormInput } from '../form/form-input';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -19,6 +19,7 @@ export interface DataMappingFormProps<TFieldValues, TContext extends object> {
|
|||||||
providerType: ProvidableType,
|
providerType: ProvidableType,
|
||||||
setValue: UseFormSetValue<TFieldValues>,
|
setValue: UseFormSetValue<TFieldValues>,
|
||||||
currentFormValues: Array<AuthenticationProviderMapping>,
|
currentFormValues: Array<AuthenticationProviderMapping>,
|
||||||
|
formState: FormState<TFieldValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
type selectModelFieldOption = { value: string, label: string };
|
type selectModelFieldOption = { value: string, label: string };
|
||||||
@ -26,7 +27,7 @@ type selectModelFieldOption = { value: string, label: string };
|
|||||||
/**
|
/**
|
||||||
* Partial form to define the mapping of the data between the API of the authentication provider and the application internals.
|
* Partial form to define the mapping of the data between the API of the authentication provider and the application internals.
|
||||||
*/
|
*/
|
||||||
export const DataMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, providerType, setValue, currentFormValues }: DataMappingFormProps<TFieldValues, TContext>) => {
|
export const DataMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, providerType, setValue, currentFormValues, formState }: DataMappingFormProps<TFieldValues, TContext>) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
const [dataMapping, setDataMapping] = useState<MappingFields>(null);
|
const [dataMapping, setDataMapping] = useState<MappingFields>(null);
|
||||||
const [isOpenTypeMappingModal, updateIsOpenTypeMappingModal] = useImmer<Map<number, boolean>>(new Map());
|
const [isOpenTypeMappingModal, updateIsOpenTypeMappingModal] = useImmer<Map<number, boolean>>(new Map());
|
||||||
@ -144,20 +145,24 @@ export const DataMappingForm = <TFieldValues extends FieldValues, TContext exten
|
|||||||
<FormInput id={`auth_provider_mappings_attributes.${index}.id`} register={register} type="hidden" />
|
<FormInput id={`auth_provider_mappings_attributes.${index}.id`} register={register} type="hidden" />
|
||||||
<div className="local-data">
|
<div className="local-data">
|
||||||
<FormSelect id={`auth_provider_mappings_attributes.${index}.local_model`}
|
<FormSelect id={`auth_provider_mappings_attributes.${index}.local_model`}
|
||||||
control={control} rules={{ required: true }}
|
control={control}
|
||||||
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
options={buildModelOptions()}
|
options={buildModelOptions()}
|
||||||
label={t('app.admin.authentication.data_mapping_form.model')}/>
|
label={t('app.admin.authentication.data_mapping_form.model')}/>
|
||||||
<FormSelect id={`auth_provider_mappings_attributes.${index}.local_field`}
|
<FormSelect id={`auth_provider_mappings_attributes.${index}.local_field`}
|
||||||
options={buildFieldOptions(output, index)}
|
options={buildFieldOptions(output, index)}
|
||||||
control={control}
|
control={control}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.authentication.data_mapping_form.field')} />
|
label={t('app.admin.authentication.data_mapping_form.field')} />
|
||||||
</div>
|
</div>
|
||||||
<div className="remote-data">
|
<div className="remote-data">
|
||||||
{providerType === 'OAuth2Provider' && <Oauth2DataMappingForm register={register} control={control} index={index} />}
|
{providerType === 'OAuth2Provider' && <Oauth2DataMappingForm register={register} control={control} index={index} formState={formState} />}
|
||||||
{providerType === 'OpenIdConnectProvider' && <OpenidConnectDataMappingForm register={register}
|
{providerType === 'OpenIdConnectProvider' && <OpenidConnectDataMappingForm register={register}
|
||||||
index={index}
|
index={index}
|
||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
|
formState={formState}
|
||||||
currentFormValues={currentFormValues} />}
|
currentFormValues={currentFormValues} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -172,7 +177,9 @@ export const DataMappingForm = <TFieldValues extends FieldValues, TContext exten
|
|||||||
type={getDataType(output, index)}
|
type={getDataType(output, index)}
|
||||||
isOpen={isOpenTypeMappingModal.get(index)}
|
isOpen={isOpenTypeMappingModal.get(index)}
|
||||||
toggleModal={toggleTypeMappingModal(index)}
|
toggleModal={toggleTypeMappingModal(index)}
|
||||||
control={control} register={register}
|
control={control}
|
||||||
|
register={register}
|
||||||
|
formState={formState}
|
||||||
fieldMappingId={index} />
|
fieldMappingId={index} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FormSelect } from '../form/form-select';
|
import { FormSelect } from '../form/form-select';
|
||||||
import { Control } from 'react-hook-form/dist/types/form';
|
import { Control, FormState } from 'react-hook-form/dist/types/form';
|
||||||
|
|
||||||
export interface DateMappingFormProps<TFieldValues, TContext extends object> {
|
export interface DateMappingFormProps<TFieldValues, TContext extends object> {
|
||||||
control: Control<TFieldValues, TContext>,
|
control: Control<TFieldValues, TContext>,
|
||||||
fieldMappingId: number,
|
fieldMappingId: number,
|
||||||
|
formState: FormState<TFieldValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial form for mapping an internal date field to an external API.
|
* Partial form for mapping an internal date field to an external API.
|
||||||
*/
|
*/
|
||||||
export const DateMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ control, fieldMappingId }: DateMappingFormProps<TFieldValues, TContext>) => {
|
export const DateMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ control, fieldMappingId, formState }: DateMappingFormProps<TFieldValues, TContext>) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
// available date formats
|
// available date formats
|
||||||
@ -44,6 +45,7 @@ export const DateMappingForm = <TFieldValues extends FieldValues, TContext exten
|
|||||||
<FormSelect id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.format`}
|
<FormSelect id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.format`}
|
||||||
control={control}
|
control={control}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
options={dateFormats}
|
options={dateFormats}
|
||||||
label={t('app.admin.authentication.date_mapping_form.date_format')} />
|
label={t('app.admin.authentication.date_mapping_form.date_format')} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ArrayPath, useFieldArray, UseFormRegister } from 'react-hook-form';
|
import { ArrayPath, useFieldArray, UseFormRegister } from 'react-hook-form';
|
||||||
import { Control } from 'react-hook-form/dist/types/form';
|
import { Control, FormState } from 'react-hook-form/dist/types/form';
|
||||||
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FabButton } from '../base/fab-button';
|
import { FabButton } from '../base/fab-button';
|
||||||
@ -9,12 +9,13 @@ export interface IntegerMappingFormProps<TFieldValues, TContext extends object>
|
|||||||
register: UseFormRegister<TFieldValues>,
|
register: UseFormRegister<TFieldValues>,
|
||||||
control: Control<TFieldValues, TContext>,
|
control: Control<TFieldValues, TContext>,
|
||||||
fieldMappingId: number,
|
fieldMappingId: number,
|
||||||
|
formState: FormState<TFieldValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial for to map an internal integer field to an external API providing a string value.
|
* Partial for to map an internal integer field to an external API providing a string value.
|
||||||
*/
|
*/
|
||||||
export const IntegerMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, fieldMappingId }: IntegerMappingFormProps<TFieldValues, TContext>) => {
|
export const IntegerMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, fieldMappingId, formState }: IntegerMappingFormProps<TFieldValues, TContext>) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
const { fields, append, remove } = useFieldArray({ control, name: 'auth_provider_mappings_attributes_transformation_mapping' as ArrayPath<TFieldValues> });
|
const { fields, append, remove } = useFieldArray({ control, name: 'auth_provider_mappings_attributes_transformation_mapping' as ArrayPath<TFieldValues> });
|
||||||
@ -33,11 +34,13 @@ export const IntegerMappingForm = <TFieldValues extends FieldValues, TContext ex
|
|||||||
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.mapping.${index}.from`}
|
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.mapping.${index}.from`}
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.authentication.integer_mapping_form.mapping_from')} />
|
label={t('app.admin.authentication.integer_mapping_form.mapping_from')} />
|
||||||
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.mapping.${index}.to`}
|
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.mapping.${index}.to`}
|
||||||
register={register}
|
register={register}
|
||||||
type="number"
|
type="number"
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.authentication.integer_mapping_form.mapping_to')} />
|
label={t('app.admin.authentication.integer_mapping_form.mapping_to')} />
|
||||||
</div>
|
</div>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { UseFormRegister } from 'react-hook-form';
|
import { UseFormRegister } from 'react-hook-form';
|
||||||
import { Control } from 'react-hook-form/dist/types/form';
|
import { Control, FormState } from 'react-hook-form/dist/types/form';
|
||||||
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
import { FormInput } from '../form/form-input';
|
import { FormInput } from '../form/form-input';
|
||||||
import { FormSelect } from '../form/form-select';
|
import { FormSelect } from '../form/form-select';
|
||||||
@ -10,13 +10,14 @@ interface Oauth2DataMappingFormProps<TFieldValues, TContext extends object> {
|
|||||||
register: UseFormRegister<TFieldValues>,
|
register: UseFormRegister<TFieldValues>,
|
||||||
control: Control<TFieldValues, TContext>,
|
control: Control<TFieldValues, TContext>,
|
||||||
index: number,
|
index: number,
|
||||||
|
formState: FormState<TFieldValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial form to set the data mapping for an OAuth 2.0 provider.
|
* Partial form to set the data mapping for an OAuth 2.0 provider.
|
||||||
* The data mapping is the way to bind data from the authentication provider API to the Fab-manager's database
|
* The data mapping is the way to bind data from the authentication provider API to the Fab-manager's database
|
||||||
*/
|
*/
|
||||||
export const Oauth2DataMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, index }: Oauth2DataMappingFormProps<TFieldValues, TContext>) => {
|
export const Oauth2DataMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, index, formState }: Oauth2DataMappingFormProps<TFieldValues, TContext>) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -24,15 +25,19 @@ export const Oauth2DataMappingForm = <TFieldValues extends FieldValues, TContext
|
|||||||
<FormInput id={`auth_provider_mappings_attributes.${index}.api_endpoint`}
|
<FormInput id={`auth_provider_mappings_attributes.${index}.api_endpoint`}
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
placeholder="/api/resource..."
|
placeholder="/api/resource..."
|
||||||
label={t('app.admin.authentication.oauth2_data_mapping_form.api_endpoint_url')} />
|
label={t('app.admin.authentication.oauth2_data_mapping_form.api_endpoint_url')} />
|
||||||
<FormSelect id={`auth_provider_mappings_attributes.${index}.api_data_type`}
|
<FormSelect id={`auth_provider_mappings_attributes.${index}.api_data_type`}
|
||||||
options={[{ label: 'JSON', value: 'json' }]}
|
options={[{ label: 'JSON', value: 'json' }]}
|
||||||
control={control} rules={{ required: true }}
|
control={control}
|
||||||
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.authentication.oauth2_data_mapping_form.api_type')} />
|
label={t('app.admin.authentication.oauth2_data_mapping_form.api_type')} />
|
||||||
<FormInput id={`auth_provider_mappings_attributes.${index}.api_field`}
|
<FormInput id={`auth_provider_mappings_attributes.${index}.api_field`}
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
placeholder="field_name..."
|
placeholder="field_name..."
|
||||||
tooltip={<HtmlTranslate trKey="app.admin.authentication.oauth2_data_mapping_form.api_field_help_html" />}
|
tooltip={<HtmlTranslate trKey="app.admin.authentication.oauth2_data_mapping_form.api_field_help_html" />}
|
||||||
label={t('app.admin.authentication.oauth2_data_mapping_form.api_field')} />
|
label={t('app.admin.authentication.oauth2_data_mapping_form.api_field')} />
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
import { FormInput } from '../form/form-input';
|
import { FormInput } from '../form/form-input';
|
||||||
import { UseFormRegister } from 'react-hook-form';
|
import { UseFormRegister, FormState } from 'react-hook-form';
|
||||||
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FabOutputCopy } from '../base/fab-output-copy';
|
import { FabOutputCopy } from '../base/fab-output-copy';
|
||||||
|
|
||||||
interface Oauth2FormProps<TFieldValues> {
|
interface Oauth2FormProps<TFieldValues> {
|
||||||
register: UseFormRegister<TFieldValues>,
|
register: UseFormRegister<TFieldValues>,
|
||||||
|
formState: FormState<TFieldValues>,
|
||||||
strategyName?: string,
|
strategyName?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial form to fill the OAuth2 settings for a new/existing authentication provider.
|
* Partial form to fill the OAuth2 settings for a new/existing authentication provider.
|
||||||
*/
|
*/
|
||||||
export const Oauth2Form = <TFieldValues extends FieldValues>({ register, strategyName }: Oauth2FormProps<TFieldValues>) => {
|
export const Oauth2Form = <TFieldValues extends FieldValues>({ register, strategyName, formState }: Oauth2FormProps<TFieldValues>) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
// regular expression to validate the input fields
|
// regular expression to validate the input fields
|
||||||
@ -34,31 +35,37 @@ export const Oauth2Form = <TFieldValues extends FieldValues>({ register, strateg
|
|||||||
register={register}
|
register={register}
|
||||||
placeholder="https://sso.example.net..."
|
placeholder="https://sso.example.net..."
|
||||||
label={t('app.admin.authentication.oauth2_form.common_url')}
|
label={t('app.admin.authentication.oauth2_form.common_url')}
|
||||||
rules={{ required: true, pattern: urlRegex }} />
|
rules={{ required: true, pattern: urlRegex }}
|
||||||
|
formState={formState} />
|
||||||
<FormInput id="providable_attributes.authorization_endpoint"
|
<FormInput id="providable_attributes.authorization_endpoint"
|
||||||
register={register}
|
register={register}
|
||||||
placeholder="/oauth2/auth..."
|
placeholder="/oauth2/auth..."
|
||||||
label={t('app.admin.authentication.oauth2_form.authorization_endpoint')}
|
label={t('app.admin.authentication.oauth2_form.authorization_endpoint')}
|
||||||
rules={{ required: true, pattern: endpointRegex }} />
|
rules={{ required: true, pattern: endpointRegex }}
|
||||||
|
formState={formState} />
|
||||||
<FormInput id="providable_attributes.token_endpoint"
|
<FormInput id="providable_attributes.token_endpoint"
|
||||||
register={register}
|
register={register}
|
||||||
placeholder="/oauth2/token..."
|
placeholder="/oauth2/token..."
|
||||||
label={t('app.admin.authentication.oauth2_form.token_acquisition_endpoint')}
|
label={t('app.admin.authentication.oauth2_form.token_acquisition_endpoint')}
|
||||||
rules={{ required: true, pattern: endpointRegex }} />
|
rules={{ required: true, pattern: endpointRegex }}
|
||||||
|
formState={formState} />
|
||||||
<FormInput id="providable_attributes.profile_url"
|
<FormInput id="providable_attributes.profile_url"
|
||||||
register={register}
|
register={register}
|
||||||
placeholder="https://exemple.net/user..."
|
placeholder="https://exemple.net/user..."
|
||||||
label={t('app.admin.authentication.oauth2_form.profile_edition_url')}
|
label={t('app.admin.authentication.oauth2_form.profile_edition_url')}
|
||||||
tooltip={t('app.admin.authentication.oauth2_form.profile_edition_url_help')}
|
tooltip={t('app.admin.authentication.oauth2_form.profile_edition_url_help')}
|
||||||
rules={{ required: true, pattern: urlRegex }} />
|
rules={{ required: true, pattern: urlRegex }}
|
||||||
|
formState={formState} />
|
||||||
<FormInput id="providable_attributes.client_id"
|
<FormInput id="providable_attributes.client_id"
|
||||||
register={register}
|
register={register}
|
||||||
label={t('app.admin.authentication.oauth2_form.client_identifier')}
|
label={t('app.admin.authentication.oauth2_form.client_identifier')}
|
||||||
rules={{ required: true }} />
|
rules={{ required: true }}
|
||||||
|
formState={formState} />
|
||||||
<FormInput id="providable_attributes.client_secret"
|
<FormInput id="providable_attributes.client_secret"
|
||||||
register={register}
|
register={register}
|
||||||
label={t('app.admin.authentication.oauth2_form.client_secret')}
|
label={t('app.admin.authentication.oauth2_form.client_secret')}
|
||||||
rules={{ required: true }} />
|
rules={{ required: true }}
|
||||||
|
formState={formState} />
|
||||||
<FormInput id="providable_attributes.scopes" register={register}
|
<FormInput id="providable_attributes.scopes" register={register}
|
||||||
placeholder="profile,email..."
|
placeholder="profile,email..."
|
||||||
label={t('app.admin.authentication.oauth2_form.scopes')} />
|
label={t('app.admin.authentication.oauth2_form.scopes')} />
|
||||||
|
@ -3,7 +3,7 @@ import { FieldValues } from 'react-hook-form/dist/types/fields';
|
|||||||
import { FormInput } from '../form/form-input';
|
import { FormInput } from '../form/form-input';
|
||||||
import { HtmlTranslate } from '../base/html-translate';
|
import { HtmlTranslate } from '../base/html-translate';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { UnpackNestedValue, UseFormSetValue } from 'react-hook-form/dist/types/form';
|
import { UnpackNestedValue, UseFormSetValue, FormState } from 'react-hook-form/dist/types/form';
|
||||||
import { FabButton } from '../base/fab-button';
|
import { FabButton } from '../base/fab-button';
|
||||||
import { FieldPathValue } from 'react-hook-form/dist/types/path';
|
import { FieldPathValue } from 'react-hook-form/dist/types/path';
|
||||||
import { AuthenticationProviderMapping } from '../../models/authentication-provider';
|
import { AuthenticationProviderMapping } from '../../models/authentication-provider';
|
||||||
@ -13,13 +13,14 @@ interface OpenidConnectDataMappingFormProps<TFieldValues> {
|
|||||||
setValue: UseFormSetValue<TFieldValues>,
|
setValue: UseFormSetValue<TFieldValues>,
|
||||||
currentFormValues: Array<AuthenticationProviderMapping>,
|
currentFormValues: Array<AuthenticationProviderMapping>,
|
||||||
index: number,
|
index: number,
|
||||||
|
formState: FormState<TFieldValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial form to set the data mapping for an OpenID Connect provider.
|
* Partial form to set the data mapping for an OpenID Connect provider.
|
||||||
* The data mapping is the way to bind data from the OIDC claims to the Fab-manager's database
|
* The data mapping is the way to bind data from the OIDC claims to the Fab-manager's database
|
||||||
*/
|
*/
|
||||||
export const OpenidConnectDataMappingForm = <TFieldValues extends FieldValues>({ register, setValue, currentFormValues, index }: OpenidConnectDataMappingFormProps<TFieldValues>) => {
|
export const OpenidConnectDataMappingForm = <TFieldValues extends FieldValues>({ register, setValue, currentFormValues, index, formState }: OpenidConnectDataMappingFormProps<TFieldValues>) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
const standardConfiguration = {
|
const standardConfiguration = {
|
||||||
@ -65,15 +66,18 @@ export const OpenidConnectDataMappingForm = <TFieldValues extends FieldValues>({
|
|||||||
type="hidden"
|
type="hidden"
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
defaultValue="user_info" />
|
defaultValue="user_info" />
|
||||||
<FormInput id={`auth_provider_mappings_attributes.${index}.api_data_type`}
|
<FormInput id={`auth_provider_mappings_attributes.${index}.api_data_type`}
|
||||||
type="hidden"
|
type="hidden"
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
defaultValue="json" />
|
defaultValue="json" />
|
||||||
<FormInput id={`auth_provider_mappings_attributes.${index}.api_field`}
|
<FormInput id={`auth_provider_mappings_attributes.${index}.api_field`}
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
placeholder="claim..."
|
placeholder="claim..."
|
||||||
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_data_mapping_form.api_field_help_html" />}
|
tooltip={<HtmlTranslate trKey="app.admin.authentication.openid_connect_data_mapping_form.api_field_help_html" />}
|
||||||
label={t('app.admin.authentication.openid_connect_data_mapping_form.api_field')} />
|
label={t('app.admin.authentication.openid_connect_data_mapping_form.api_field')} />
|
||||||
|
@ -161,41 +161,49 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
|
|||||||
placeholder="https://sso.exemple.com/my-account"
|
placeholder="https://sso.exemple.com/my-account"
|
||||||
label={t('app.admin.authentication.openid_connect_form.profile_edition_url')}
|
label={t('app.admin.authentication.openid_connect_form.profile_edition_url')}
|
||||||
tooltip={t('app.admin.authentication.openid_connect_form.profile_edition_url_help')}
|
tooltip={t('app.admin.authentication.openid_connect_form.profile_edition_url_help')}
|
||||||
rules={{ required: false, pattern: urlRegex }} />
|
rules={{ required: false, pattern: urlRegex }}
|
||||||
|
formState={formState} />
|
||||||
<h4>{t('app.admin.authentication.openid_connect_form.client_options')}</h4>
|
<h4>{t('app.admin.authentication.openid_connect_form.client_options')}</h4>
|
||||||
<FormInput id="providable_attributes.client__identifier"
|
<FormInput id="providable_attributes.client__identifier"
|
||||||
label={t('app.admin.authentication.openid_connect_form.client__identifier')}
|
label={t('app.admin.authentication.openid_connect_form.client__identifier')}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
register={register} />
|
register={register} />
|
||||||
<FormInput id="providable_attributes.client__secret"
|
<FormInput id="providable_attributes.client__secret"
|
||||||
label={t('app.admin.authentication.openid_connect_form.client__secret')}
|
label={t('app.admin.authentication.openid_connect_form.client__secret')}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
register={register} />
|
register={register} />
|
||||||
{!currentFormValues?.discovery && <div className="client-options-without-discovery">
|
{!currentFormValues?.discovery && <div className="client-options-without-discovery">
|
||||||
<FormInput id="providable_attributes.client__authorization_endpoint"
|
<FormInput id="providable_attributes.client__authorization_endpoint"
|
||||||
label={t('app.admin.authentication.openid_connect_form.client__authorization_endpoint')}
|
label={t('app.admin.authentication.openid_connect_form.client__authorization_endpoint')}
|
||||||
placeholder="/authorize"
|
placeholder="/authorize"
|
||||||
rules={{ required: !currentFormValues?.discovery, pattern: endpointRegex }}
|
rules={{ required: !currentFormValues?.discovery, pattern: endpointRegex }}
|
||||||
|
formState={formState}
|
||||||
register={register} />
|
register={register} />
|
||||||
<FormInput id="providable_attributes.client__token_endpoint"
|
<FormInput id="providable_attributes.client__token_endpoint"
|
||||||
label={t('app.admin.authentication.openid_connect_form.client__token_endpoint')}
|
label={t('app.admin.authentication.openid_connect_form.client__token_endpoint')}
|
||||||
placeholder="/token"
|
placeholder="/token"
|
||||||
rules={{ required: !currentFormValues?.discovery, pattern: endpointRegex }}
|
rules={{ required: !currentFormValues?.discovery, pattern: endpointRegex }}
|
||||||
|
formState={formState}
|
||||||
register={register} />
|
register={register} />
|
||||||
<FormInput id="providable_attributes.client__userinfo_endpoint"
|
<FormInput id="providable_attributes.client__userinfo_endpoint"
|
||||||
label={t('app.admin.authentication.openid_connect_form.client__userinfo_endpoint')}
|
label={t('app.admin.authentication.openid_connect_form.client__userinfo_endpoint')}
|
||||||
placeholder="/userinfo"
|
placeholder="/userinfo"
|
||||||
rules={{ required: !currentFormValues?.discovery, pattern: endpointRegex }}
|
rules={{ required: !currentFormValues?.discovery, pattern: endpointRegex }}
|
||||||
|
formState={formState}
|
||||||
register={register} />
|
register={register} />
|
||||||
{currentFormValues?.client_auth_method === 'jwks' && <FormInput id="providable_attributes.client__jwks_uri"
|
{currentFormValues?.client_auth_method === 'jwks' && <FormInput id="providable_attributes.client__jwks_uri"
|
||||||
label={t('app.admin.authentication.openid_connect_form.client__jwks_uri')}
|
label={t('app.admin.authentication.openid_connect_form.client__jwks_uri')}
|
||||||
rules={{ required: currentFormValues.client_auth_method === 'jwks', pattern: endpointRegex }}
|
rules={{ required: currentFormValues.client_auth_method === 'jwks', pattern: endpointRegex }}
|
||||||
|
formState={formState}
|
||||||
placeholder="/jwk"
|
placeholder="/jwk"
|
||||||
register={register} />}
|
register={register} />}
|
||||||
<FormInput id="providable_attributes.client__end_session_endpoint"
|
<FormInput id="providable_attributes.client__end_session_endpoint"
|
||||||
label={t('app.admin.authentication.openid_connect_form.client__end_session_endpoint')}
|
label={t('app.admin.authentication.openid_connect_form.client__end_session_endpoint')}
|
||||||
tooltip={t('app.admin.authentication.openid_connect_form.client__end_session_endpoint_help')}
|
tooltip={t('app.admin.authentication.openid_connect_form.client__end_session_endpoint_help')}
|
||||||
rules={{ pattern: endpointRegex }}
|
rules={{ pattern: endpointRegex }}
|
||||||
|
formState={formState}
|
||||||
register={register} />
|
register={register} />
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
|
@ -99,6 +99,7 @@ export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, on
|
|||||||
register={register}
|
register={register}
|
||||||
disabled={action === 'update'}
|
disabled={action === 'update'}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.authentication.provider_form.name')} />
|
label={t('app.admin.authentication.provider_form.name')} />
|
||||||
<FormSelect id="providable_type"
|
<FormSelect id="providable_type"
|
||||||
control={control}
|
control={control}
|
||||||
@ -106,9 +107,10 @@ export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, on
|
|||||||
label={t('app.admin.authentication.provider_form.authentication_type')}
|
label={t('app.admin.authentication.provider_form.authentication_type')}
|
||||||
onChange={onProvidableTypeChange}
|
onChange={onProvidableTypeChange}
|
||||||
disabled={action === 'update'}
|
disabled={action === 'update'}
|
||||||
rules={{ required: true }} />
|
rules={{ required: true }}
|
||||||
|
formState={formState} />
|
||||||
{providableType === 'DatabaseProvider' && <DatabaseForm register={register} />}
|
{providableType === 'DatabaseProvider' && <DatabaseForm register={register} />}
|
||||||
{providableType === 'OAuth2Provider' && <Oauth2Form register={register} strategyName={strategyName} />}
|
{providableType === 'OAuth2Provider' && <Oauth2Form register={register} strategyName={strategyName} formState={formState} />}
|
||||||
{providableType === 'OpenIdConnectProvider' && <OpenidConnectForm register={register}
|
{providableType === 'OpenIdConnectProvider' && <OpenidConnectForm register={register}
|
||||||
control={control}
|
control={control}
|
||||||
currentFormValues={output.providable_attributes as OpenIdConnectProvider}
|
currentFormValues={output.providable_attributes as OpenIdConnectProvider}
|
||||||
@ -116,6 +118,7 @@ export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, on
|
|||||||
setValue={setValue} />}
|
setValue={setValue} />}
|
||||||
{providableType && providableType !== 'DatabaseProvider' && <DataMappingForm register={register}
|
{providableType && providableType !== 'DatabaseProvider' && <DataMappingForm register={register}
|
||||||
control={control}
|
control={control}
|
||||||
|
formState={formState}
|
||||||
providerType={providableType}
|
providerType={providableType}
|
||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
currentFormValues={output.auth_provider_mappings_attributes as Array<AuthenticationProviderMapping>} />}
|
currentFormValues={output.auth_provider_mappings_attributes as Array<AuthenticationProviderMapping>} />}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ArrayPath, useFieldArray, UseFormRegister } from 'react-hook-form';
|
import { ArrayPath, useFieldArray, UseFormRegister } from 'react-hook-form';
|
||||||
import { Control } from 'react-hook-form/dist/types/form';
|
import { Control, FormState } from 'react-hook-form/dist/types/form';
|
||||||
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FabButton } from '../base/fab-button';
|
import { FabButton } from '../base/fab-button';
|
||||||
@ -9,12 +9,13 @@ export interface StringMappingFormProps<TFieldValues, TContext extends object> {
|
|||||||
register: UseFormRegister<TFieldValues>,
|
register: UseFormRegister<TFieldValues>,
|
||||||
control: Control<TFieldValues, TContext>,
|
control: Control<TFieldValues, TContext>,
|
||||||
fieldMappingId: number,
|
fieldMappingId: number,
|
||||||
|
formState: FormState<TFieldValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial form to map an internal string field to an external API.
|
* Partial form to map an internal string field to an external API.
|
||||||
*/
|
*/
|
||||||
export const StringMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, fieldMappingId }: StringMappingFormProps<TFieldValues, TContext>) => {
|
export const StringMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control, fieldMappingId, formState }: StringMappingFormProps<TFieldValues, TContext>) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
const { fields, append, remove } = useFieldArray({ control, name: 'auth_provider_mappings_attributes_transformation_mapping' as ArrayPath<TFieldValues> });
|
const { fields, append, remove } = useFieldArray({ control, name: 'auth_provider_mappings_attributes_transformation_mapping' as ArrayPath<TFieldValues> });
|
||||||
@ -33,10 +34,12 @@ export const StringMappingForm = <TFieldValues extends FieldValues, TContext ext
|
|||||||
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.mapping.${index}.from`}
|
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.mapping.${index}.from`}
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.authentication.string_mapping_form.mapping_from')} />
|
label={t('app.admin.authentication.string_mapping_form.mapping_from')} />
|
||||||
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.mapping.${index}.to`}
|
<FormInput id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.mapping.${index}.to`}
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.authentication.string_mapping_form.mapping_to')} />
|
label={t('app.admin.authentication.string_mapping_form.mapping_to')} />
|
||||||
</div>
|
</div>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
|
@ -2,7 +2,7 @@ import { FabModal } from '../base/fab-modal';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { IntegerMappingForm } from './integer-mapping-form';
|
import { IntegerMappingForm } from './integer-mapping-form';
|
||||||
import { UseFormRegister } from 'react-hook-form';
|
import { UseFormRegister } from 'react-hook-form';
|
||||||
import { Control } from 'react-hook-form/dist/types/form';
|
import { Control, FormState } from 'react-hook-form/dist/types/form';
|
||||||
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
import { mappingType } from '../../models/authentication-provider';
|
import { mappingType } from '../../models/authentication-provider';
|
||||||
import { BooleanMappingForm } from './boolean-mapping-form';
|
import { BooleanMappingForm } from './boolean-mapping-form';
|
||||||
@ -19,6 +19,7 @@ interface TypeMappingModalProps<TFieldValues, TContext extends object> {
|
|||||||
register: UseFormRegister<TFieldValues>,
|
register: UseFormRegister<TFieldValues>,
|
||||||
control: Control<TFieldValues, TContext>,
|
control: Control<TFieldValues, TContext>,
|
||||||
fieldMappingId: number,
|
fieldMappingId: number,
|
||||||
|
formState: FormState<TFieldValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,7 +28,7 @@ interface TypeMappingModalProps<TFieldValues, TContext extends object> {
|
|||||||
*
|
*
|
||||||
* This component is intended to be used in a react-hook-form context.
|
* This component is intended to be used in a react-hook-form context.
|
||||||
*/
|
*/
|
||||||
export const TypeMappingModal = <TFieldValues extends FieldValues, TContext extends object>({ model, field, type, isOpen, toggleModal, register, control, fieldMappingId }:TypeMappingModalProps<TFieldValues, TContext>) => {
|
export const TypeMappingModal = <TFieldValues extends FieldValues, TContext extends object>({ model, field, type, isOpen, toggleModal, register, control, fieldMappingId, formState }:TypeMappingModalProps<TFieldValues, TContext>) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -42,10 +43,10 @@ export const TypeMappingModal = <TFieldValues extends FieldValues, TContext exte
|
|||||||
id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.type`}
|
id={`auth_provider_mappings_attributes.${fieldMappingId}.transformation.type`}
|
||||||
type="hidden"
|
type="hidden"
|
||||||
defaultValue={type} />
|
defaultValue={type} />
|
||||||
{type === 'integer' && <IntegerMappingForm register={register} control={control} fieldMappingId={fieldMappingId} />}
|
{type === 'integer' && <IntegerMappingForm register={register} control={control} fieldMappingId={fieldMappingId} formState={formState} />}
|
||||||
{type === 'boolean' && <BooleanMappingForm register={register} fieldMappingId={fieldMappingId} />}
|
{type === 'boolean' && <BooleanMappingForm register={register} fieldMappingId={fieldMappingId} formState={formState} />}
|
||||||
{type === 'date' && <DateMappingForm control={control} fieldMappingId={fieldMappingId} />}
|
{type === 'date' && <DateMappingForm control={control} fieldMappingId={fieldMappingId} formState={formState} />}
|
||||||
{type === 'string' && <StringMappingForm register={register} control={control} fieldMappingId={fieldMappingId} />}
|
{type === 'string' && <StringMappingForm register={register} control={control} fieldMappingId={fieldMappingId} formState={formState} />}
|
||||||
</FabModal>
|
</FabModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -33,7 +33,7 @@ const PrepaidPacksPanel: React.FC<PrepaidPacksPanelProps> = ({ user, onError })
|
|||||||
const [selectedMachine, setSelectedMachine] = useState<Machine>(null);
|
const [selectedMachine, setSelectedMachine] = useState<Machine>(null);
|
||||||
const [packsModal, setPacksModal] = useState<boolean>(false);
|
const [packsModal, setPacksModal] = useState<boolean>(false);
|
||||||
|
|
||||||
const { handleSubmit, control } = useForm<{ machine_id: number }>();
|
const { handleSubmit, control, formState } = useForm<{ machine_id: number }>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
UserPackAPI.index({ user_id: user.id })
|
UserPackAPI.index({ user_id: user.id })
|
||||||
@ -125,7 +125,7 @@ const PrepaidPacksPanel: React.FC<PrepaidPacksPanelProps> = ({ user, onError })
|
|||||||
<div className='prepaid-packs-cta'>
|
<div className='prepaid-packs-cta'>
|
||||||
<p>{t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.cta_info')}</p>
|
<p>{t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.cta_info')}</p>
|
||||||
<form onSubmit={handleSubmit(onBuyPack)}>
|
<form onSubmit={handleSubmit(onBuyPack)}>
|
||||||
<FormSelect options={buildMachinesOptions(machines)} control={control} id="machine_id" rules={{ required: true }} label={t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.select_machine')} />
|
<FormSelect options={buildMachinesOptions(machines)} control={control} id="machine_id" rules={{ required: true }} formState={formState} label={t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.select_machine')} />
|
||||||
<FabButton className='is-black' type="submit">
|
<FabButton className='is-black' type="submit">
|
||||||
{t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.cta_button')}
|
{t('app.logged.dashboard.reservations_dashboard.prepaid_packs_panel.cta_button')}
|
||||||
</FabButton>
|
</FabButton>
|
||||||
|
@ -186,6 +186,7 @@ export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, on
|
|||||||
<FormRichText control={control}
|
<FormRichText control={control}
|
||||||
id="description"
|
id="description"
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.event_form.description')}
|
label={t('app.admin.event_form.description')}
|
||||||
limit={null}
|
limit={null}
|
||||||
heading bulletList blockquote link video image />
|
heading bulletList blockquote link video image />
|
||||||
@ -291,11 +292,13 @@ export const EventForm: React.FC<EventFormProps> = ({ action, event, onError, on
|
|||||||
control={control}
|
control={control}
|
||||||
id={`event_price_categories_attributes.${index}.price_category_id`}
|
id={`event_price_categories_attributes.${index}.price_category_id`}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.event_form.fare_class')} />
|
label={t('app.admin.event_form.fare_class')} />
|
||||||
<FormInput id={`event_price_categories_attributes.${index}.amount`}
|
<FormInput id={`event_price_categories_attributes.${index}.amount`}
|
||||||
register={register}
|
register={register}
|
||||||
type="number"
|
type="number"
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.event_form.price')}
|
label={t('app.admin.event_form.price')}
|
||||||
addOn={FormatLib.currencySymbol()} />
|
addOn={FormatLib.currencySymbol()} />
|
||||||
<FabButton className="remove-price is-main" onClick={() => handlePriceRemove(price, index)} icon={<Trash size={20} />} />
|
<FabButton className="remove-price is-main" onClick={() => handlePriceRemove(price, index)} icon={<Trash size={20} />} />
|
||||||
|
@ -4,7 +4,7 @@ import { AbstractFormComponent } from '../../models/form-component';
|
|||||||
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
import { get as _get } from 'lodash';
|
import { get as _get } from 'lodash';
|
||||||
|
|
||||||
export interface AbstractFormItemProps<TFieldValues> extends PropsWithChildren<AbstractFormComponent<TFieldValues>> {
|
export type AbstractFormItemProps<TFieldValues> = PropsWithChildren<AbstractFormComponent<TFieldValues>> & {
|
||||||
id: string,
|
id: string,
|
||||||
label?: string|ReactNode,
|
label?: string|ReactNode,
|
||||||
tooltip?: ReactNode,
|
tooltip?: ReactNode,
|
||||||
@ -21,7 +21,7 @@ export interface AbstractFormItemProps<TFieldValues> extends PropsWithChildren<A
|
|||||||
*/
|
*/
|
||||||
export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label, tooltip, className, disabled, error, warning, rules, formState, onLabelClick, inLine, containerType, children }: AbstractFormItemProps<TFieldValues>) => {
|
export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label, tooltip, className, disabled, error, warning, rules, formState, onLabelClick, inLine, containerType, children }: AbstractFormItemProps<TFieldValues>) => {
|
||||||
const [isDirty, setIsDirty] = useState<boolean>(false);
|
const [isDirty, setIsDirty] = useState<boolean>(false);
|
||||||
const [fieldError, setFieldError] = useState<{ message: string }>(error);
|
const [fieldError, setFieldError] = useState<{ message: string }>(null);
|
||||||
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
const [isDisabled, setIsDisabled] = useState<boolean>(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -29,10 +29,6 @@ export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label,
|
|||||||
setFieldError(_get(formState?.errors, id));
|
setFieldError(_get(formState?.errors, id));
|
||||||
}, [formState]);
|
}, [formState]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setFieldError(error);
|
|
||||||
}, [error]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof disabled === 'function') {
|
if (typeof disabled === 'function') {
|
||||||
setIsDisabled(disabled(id));
|
setIsDisabled(disabled(id));
|
||||||
@ -44,7 +40,7 @@ export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label,
|
|||||||
// Compose classnames from props
|
// Compose classnames from props
|
||||||
const classNames = [
|
const classNames = [
|
||||||
`${className || ''}`,
|
`${className || ''}`,
|
||||||
`${isDirty && fieldError ? 'is-incorrect' : ''}`,
|
`${(isDirty && error) || fieldError ? 'is-incorrect' : ''}`,
|
||||||
`${isDirty && warning ? 'is-warned' : ''}`,
|
`${isDirty && warning ? 'is-warned' : ''}`,
|
||||||
`${rules && rules.required ? 'is-required' : ''}`,
|
`${rules && rules.required ? 'is-required' : ''}`,
|
||||||
`${isDisabled ? 'is-disabled' : ''}`
|
`${isDisabled ? 'is-disabled' : ''}`
|
||||||
@ -79,7 +75,8 @@ export const AbstractFormItem = <TFieldValues extends FieldValues>({ id, label,
|
|||||||
</div>}
|
</div>}
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
{(isDirty && fieldError) && <div className="form-item-error">{fieldError.message}</div> }
|
{ fieldError && <div className="form-item-error">{fieldError.message}</div> }
|
||||||
|
{(isDirty && error) && <div className="form-item-error">{error.message}</div> }
|
||||||
{(isDirty && warning) && <div className="form-item-warning">{warning.message}</div> }
|
{(isDirty && warning) && <div className="form-item-warning">{warning.message}</div> }
|
||||||
</>
|
</>
|
||||||
));
|
));
|
||||||
|
@ -9,7 +9,7 @@ import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
|
|||||||
import { FabButton } from '../base/fab-button';
|
import { FabButton } from '../base/fab-button';
|
||||||
import { ChecklistOption } from '../../models/select';
|
import { ChecklistOption } from '../../models/select';
|
||||||
|
|
||||||
interface FormChecklistProps<TFieldValues, TOptionValue, TContext extends object> extends FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
type FormChecklistProps<TFieldValues, TOptionValue, TContext extends object> = FormControlledComponent<TFieldValues, TContext> & AbstractFormItemProps<TFieldValues> & {
|
||||||
defaultValue?: Array<TOptionValue>,
|
defaultValue?: Array<TOptionValue>,
|
||||||
options: Array<ChecklistOption<TOptionValue>>,
|
options: Array<ChecklistOption<TOptionValue>>,
|
||||||
onChange?: (values: Array<TOptionValue>) => void,
|
onChange?: (values: Array<TOptionValue>) => void,
|
||||||
|
@ -13,7 +13,7 @@ import { FilePdf, Trash } from 'phosphor-react';
|
|||||||
import { FileType } from '../../models/file';
|
import { FileType } from '../../models/file';
|
||||||
import FileUploadLib from '../../lib/file-upload';
|
import FileUploadLib from '../../lib/file-upload';
|
||||||
|
|
||||||
interface FormFileUploadProps<TFieldValues> extends FormComponent<TFieldValues>, AbstractFormItemProps<TFieldValues> {
|
type FormFileUploadProps<TFieldValues> = FormComponent<TFieldValues> & AbstractFormItemProps<TFieldValues> & {
|
||||||
setValue: UseFormSetValue<TFieldValues>,
|
setValue: UseFormSetValue<TFieldValues>,
|
||||||
defaultFile?: FileType,
|
defaultFile?: FileType,
|
||||||
accept?: string,
|
accept?: string,
|
||||||
|
@ -14,7 +14,7 @@ import { Trash } from 'phosphor-react';
|
|||||||
import { ImageType } from '../../models/file';
|
import { ImageType } from '../../models/file';
|
||||||
import FileUploadLib from '../../lib/file-upload';
|
import FileUploadLib from '../../lib/file-upload';
|
||||||
|
|
||||||
interface FormImageUploadProps<TFieldValues, TContext extends object> extends FormComponent<TFieldValues>, FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
type FormImageUploadProps<TFieldValues, TContext extends object> = FormComponent<TFieldValues> & FormControlledComponent<TFieldValues, TContext> & AbstractFormItemProps<TFieldValues> & {
|
||||||
setValue: UseFormSetValue<TFieldValues>,
|
setValue: UseFormSetValue<TFieldValues>,
|
||||||
defaultImage?: ImageType,
|
defaultImage?: ImageType,
|
||||||
accept?: string,
|
accept?: string,
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { ReactNode, useCallback, useState } from 'react';
|
import { ReactNode, useCallback, useState, useEffect } from 'react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { FieldPathValue } from 'react-hook-form';
|
import { FieldPathValue, UseFormGetValues } from 'react-hook-form';
|
||||||
import { debounce as _debounce } from 'lodash';
|
import { debounce as _debounce } from 'lodash';
|
||||||
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
import { FieldValues } from 'react-hook-form/dist/types/fields';
|
||||||
import { FieldPath } from 'react-hook-form/dist/types/path';
|
import { FieldPath } from 'react-hook-form/dist/types/path';
|
||||||
import { FormComponent } from '../../models/form-component';
|
import { FormComponent } from '../../models/form-component';
|
||||||
import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
|
import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
|
||||||
|
|
||||||
interface FormInputProps<TFieldValues, TInputType> extends FormComponent<TFieldValues>, AbstractFormItemProps<TFieldValues> {
|
type FormInputProps<TFieldValues, TInputType> = FormComponent<TFieldValues> & AbstractFormItemProps<TFieldValues> & {
|
||||||
icon?: ReactNode,
|
icon?: ReactNode,
|
||||||
addOn?: ReactNode,
|
addOn?: ReactNode,
|
||||||
addOnAction?: (event: React.MouseEvent<HTMLButtonElement>) => void,
|
addOnAction?: (event: React.MouseEvent<HTMLButtonElement>) => void,
|
||||||
@ -22,13 +22,14 @@ interface FormInputProps<TFieldValues, TInputType> extends FormComponent<TFieldV
|
|||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void,
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void,
|
||||||
nullable?: boolean,
|
nullable?: boolean,
|
||||||
ariaLabel?: string,
|
ariaLabel?: string,
|
||||||
maxLength?: number
|
maxLength?: number,
|
||||||
|
getValues?: UseFormGetValues<FieldValues>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component is a template for an input component to use within React Hook Form
|
* This component is a template for an input component to use within React Hook Form
|
||||||
*/
|
*/
|
||||||
export const FormInput = <TFieldValues extends FieldValues, TInputType>({ id, register, label, tooltip, defaultValue, icon, className, rules, disabled, type, addOn, addOnAction, addOnClassName, addOnAriaLabel, placeholder, error, warning, formState, step, onChange, debounce, accept, nullable = false, ariaLabel, maxLength }: FormInputProps<TFieldValues, TInputType>) => {
|
export const FormInput = <TFieldValues extends FieldValues, TInputType>({ id, register, getValues, label, tooltip, defaultValue, icon, className, rules, disabled, type, addOn, addOnAction, addOnClassName, addOnAriaLabel, placeholder, error, warning, formState, step, onChange, debounce, accept, nullable = false, ariaLabel, maxLength }: FormInputProps<TFieldValues, TInputType>) => {
|
||||||
const [characterCount, setCharacterCount] = useState(0);
|
const [characterCount, setCharacterCount] = useState(0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,6 +69,13 @@ export const FormInput = <TFieldValues extends FieldValues, TInputType>({ id, re
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If maxLength and getValues is provided, uses input ref to initiate the countdown of characters
|
||||||
|
useEffect(() => {
|
||||||
|
if (getValues && maxLength) {
|
||||||
|
setCharacterCount(getValues(id).length);
|
||||||
|
}
|
||||||
|
}, [maxLength, getValues]);
|
||||||
|
|
||||||
// Compose classnames from props
|
// Compose classnames from props
|
||||||
const classNames = [
|
const classNames = [
|
||||||
`${className || ''}`,
|
`${className || ''}`,
|
||||||
|
@ -11,7 +11,7 @@ import { FileType } from '../../models/file';
|
|||||||
import { UnpackNestedValue } from 'react-hook-form/dist/types';
|
import { UnpackNestedValue } from 'react-hook-form/dist/types';
|
||||||
import { FieldPathValue } from 'react-hook-form/dist/types/path';
|
import { FieldPathValue } from 'react-hook-form/dist/types/path';
|
||||||
|
|
||||||
interface FormMultiFileUploadProps<TFieldValues, TContext extends object> extends FormComponent<TFieldValues>, FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
type FormMultiFileUploadProps<TFieldValues, TContext extends object> = FormComponent<TFieldValues> & FormControlledComponent<TFieldValues, TContext> & AbstractFormItemProps<TFieldValues> & {
|
||||||
setValue: UseFormSetValue<TFieldValues>,
|
setValue: UseFormSetValue<TFieldValues>,
|
||||||
addButtonLabel: ReactNode,
|
addButtonLabel: ReactNode,
|
||||||
accept: string
|
accept: string
|
||||||
|
@ -11,7 +11,7 @@ import { ImageType } from '../../models/file';
|
|||||||
import { UnpackNestedValue } from 'react-hook-form/dist/types';
|
import { UnpackNestedValue } from 'react-hook-form/dist/types';
|
||||||
import { FieldPathValue } from 'react-hook-form/dist/types/path';
|
import { FieldPathValue } from 'react-hook-form/dist/types/path';
|
||||||
|
|
||||||
interface FormMultiImageUploadProps<TFieldValues, TContext extends object> extends FormComponent<TFieldValues>, FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
type FormMultiImageUploadProps<TFieldValues, TContext extends object> = FormComponent<TFieldValues> & FormControlledComponent<TFieldValues, TContext> & AbstractFormItemProps<TFieldValues> & {
|
||||||
setValue: UseFormSetValue<TFieldValues>,
|
setValue: UseFormSetValue<TFieldValues>,
|
||||||
addButtonLabel: ReactNode
|
addButtonLabel: ReactNode
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Controller, FieldPathValue, Path } from 'react-hook-form';
|
import { Controller, FieldPathValue, Path } from 'react-hook-form';
|
||||||
import { UnpackNestedValue } from 'react-hook-form/dist/types/form';
|
import { UnpackNestedValue } from 'react-hook-form/dist/types/form';
|
||||||
|
|
||||||
interface CommonProps<TFieldValues, TContext extends object, TOptionValue> extends FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
type CommonProps<TFieldValues, TContext extends object, TOptionValue> = FormControlledComponent<TFieldValues, TContext> & AbstractFormItemProps<TFieldValues> & {
|
||||||
valuesDefault?: Array<TOptionValue>,
|
valuesDefault?: Array<TOptionValue>,
|
||||||
onChange?: (values: Array<TOptionValue>) => void,
|
onChange?: (values: Array<TOptionValue>) => void,
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
|
@ -8,7 +8,7 @@ import { Controller, Path } from 'react-hook-form';
|
|||||||
import { FieldPath } from 'react-hook-form/dist/types/path';
|
import { FieldPath } from 'react-hook-form/dist/types/path';
|
||||||
import { FieldPathValue, UnpackNestedValue } from 'react-hook-form/dist/types';
|
import { FieldPathValue, UnpackNestedValue } from 'react-hook-form/dist/types';
|
||||||
|
|
||||||
interface FormRichTextProps<TFieldValues, TContext extends object> extends FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
type FormRichTextProps<TFieldValues, TContext extends object> = FormControlledComponent<TFieldValues, TContext> & AbstractFormItemProps<TFieldValues> & {
|
||||||
valueDefault?: string,
|
valueDefault?: string,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
heading?: boolean,
|
heading?: boolean,
|
||||||
|
@ -9,7 +9,7 @@ import { FormControlledComponent } from '../../models/form-component';
|
|||||||
import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
|
import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
|
||||||
import { SelectOption } from '../../models/select';
|
import { SelectOption } from '../../models/select';
|
||||||
|
|
||||||
interface FormSelectProps<TFieldValues, TContext extends object, TOptionValue, TOptionLabel> extends FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
type FormSelectProps<TFieldValues, TContext extends object, TOptionValue, TOptionLabel> = FormControlledComponent<TFieldValues, TContext> & AbstractFormItemProps<TFieldValues> & {
|
||||||
options: Array<SelectOption<TOptionValue, TOptionLabel>>,
|
options: Array<SelectOption<TOptionValue, TOptionLabel>>,
|
||||||
valueDefault?: TOptionValue,
|
valueDefault?: TOptionValue,
|
||||||
onChange?: (value: TOptionValue) => void,
|
onChange?: (value: TOptionValue) => void,
|
||||||
|
@ -5,7 +5,7 @@ import { Controller, Path } from 'react-hook-form';
|
|||||||
import Switch from 'react-switch';
|
import Switch from 'react-switch';
|
||||||
import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
|
import { AbstractFormItem, AbstractFormItemProps } from './abstract-form-item';
|
||||||
|
|
||||||
interface FormSwitchProps<TFieldValues, TContext extends object> extends FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
type FormSwitchProps<TFieldValues, TContext extends object> = FormControlledComponent<TFieldValues, TContext> & AbstractFormItemProps<TFieldValues> & {
|
||||||
defaultValue?: boolean,
|
defaultValue?: boolean,
|
||||||
onChange?: (value: boolean) => void,
|
onChange?: (value: boolean) => void,
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ enableMapSet();
|
|||||||
export const VatSettingsModal: React.FC<VatSettingsModalProps> = ({ isOpen, toggleModal, onError, onSuccess }) => {
|
export const VatSettingsModal: React.FC<VatSettingsModalProps> = ({ isOpen, toggleModal, onError, onSuccess }) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
const { handleSubmit, reset, control, register } = useForm<Record<SettingName, SettingValue>>();
|
const { handleSubmit, reset, control, register, formState } = useForm<Record<SettingName, SettingValue>>();
|
||||||
const isActive = useWatch({ control, name: 'invoice_VAT-active' });
|
const isActive = useWatch({ control, name: 'invoice_VAT-active' });
|
||||||
const generalRate = useWatch({ control, name: 'invoice_VAT-rate' });
|
const generalRate = useWatch({ control, name: 'invoice_VAT-rate' });
|
||||||
|
|
||||||
@ -108,11 +108,13 @@ export const VatSettingsModal: React.FC<VatSettingsModalProps> = ({ isOpen, togg
|
|||||||
<FormInput register={register}
|
<FormInput register={register}
|
||||||
id="invoice_VAT-name"
|
id="invoice_VAT-name"
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
tooltip={t('app.admin.vat_settings_modal.VAT_name_help')}
|
tooltip={t('app.admin.vat_settings_modal.VAT_name_help')}
|
||||||
label={t('app.admin.vat_settings_modal.VAT_name')} />
|
label={t('app.admin.vat_settings_modal.VAT_name')} />
|
||||||
<FormInput register={register}
|
<FormInput register={register}
|
||||||
id="invoice_VAT-rate"
|
id="invoice_VAT-rate"
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
tooltip={t('app.admin.vat_settings_modal.VAT_rate_help')}
|
tooltip={t('app.admin.vat_settings_modal.VAT_rate_help')}
|
||||||
type='number'
|
type='number'
|
||||||
step={0.001}
|
step={0.001}
|
||||||
|
@ -127,12 +127,14 @@ export const MachineForm: React.FC<MachineFormProps> = ({ action, machine, onErr
|
|||||||
<FormRichText control={control}
|
<FormRichText control={control}
|
||||||
id="description"
|
id="description"
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.machine_form.description')}
|
label={t('app.admin.machine_form.description')}
|
||||||
limit={null}
|
limit={null}
|
||||||
heading bulletList blockquote link image video />
|
heading bulletList blockquote link image video />
|
||||||
<FormRichText control={control}
|
<FormRichText control={control}
|
||||||
id="spec"
|
id="spec"
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.machine_form.technical_specifications')}
|
label={t('app.admin.machine_form.technical_specifications')}
|
||||||
limit={null}
|
limit={null}
|
||||||
heading bulletList link />
|
heading bulletList link />
|
||||||
|
@ -11,6 +11,9 @@ import { MachineCard } from './machine-card';
|
|||||||
import { MachinesFilters } from './machines-filters';
|
import { MachinesFilters } from './machines-filters';
|
||||||
import { User } from '../../models/user';
|
import { User } from '../../models/user';
|
||||||
import { EditorialBlock } from '../editorial-block/editorial-block';
|
import { EditorialBlock } from '../editorial-block/editorial-block';
|
||||||
|
import SettingAPI from '../../api/setting';
|
||||||
|
import SettingLib from '../../lib/setting';
|
||||||
|
import { SettingValue, machineBannerSettings } from '../../models/setting';
|
||||||
|
|
||||||
declare const Application: IApplication;
|
declare const Application: IApplication;
|
||||||
|
|
||||||
@ -41,6 +44,17 @@ export const MachinesList: React.FC<MachinesListProps> = ({ onError, onSuccess,
|
|||||||
category: null
|
category: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [banner, setBanner] = useState<Record<string, SettingValue>>({});
|
||||||
|
|
||||||
|
// fetch Banner text and button from API
|
||||||
|
const fetchBanner = async () => {
|
||||||
|
SettingAPI.query(machineBannerSettings)
|
||||||
|
.then(settings => {
|
||||||
|
setBanner({ ...SettingLib.bulkMapToObject(settings) });
|
||||||
|
})
|
||||||
|
.catch(onError);
|
||||||
|
};
|
||||||
|
|
||||||
// retrieve the full list of machines on component mount
|
// retrieve the full list of machines on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
MachineAPI.index()
|
MachineAPI.index()
|
||||||
@ -49,6 +63,7 @@ export const MachinesList: React.FC<MachinesListProps> = ({ onError, onSuccess,
|
|||||||
MachineCategoryAPI.index()
|
MachineCategoryAPI.index()
|
||||||
.then(data => setMachineCategories(data))
|
.then(data => setMachineCategories(data))
|
||||||
.catch(e => onError(e));
|
.catch(e => onError(e));
|
||||||
|
fetchBanner();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// filter the machines shown when the full list was retrieved
|
// filter the machines shown when the full list was retrieved
|
||||||
@ -96,12 +111,11 @@ export const MachinesList: React.FC<MachinesListProps> = ({ onError, onSuccess,
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="machines-list">
|
<div className="machines-list">
|
||||||
{/* TODO: Condition to display editorial block */}
|
{banner.machines_banner_active &&
|
||||||
{false &&
|
|
||||||
<EditorialBlock
|
<EditorialBlock
|
||||||
text={'<h3>Lorem ipsum dolor sit amet</h3><p>Consectetur adipiscing elit. In eget eros sed odio tristique cursus. Quisque pretium tortor vel lorem tempor, eu egestas lorem laoreet. Pellentesque arcu lectus, rutrum eu volutpat nec, luctus eget sapien. Sed ligula tortor, blandit eget purus sit sed.</p>'}
|
text={banner.machines_banner_text}
|
||||||
cta={'Pif paf pouf'}
|
cta={banner.machines_banner_cta_active && banner.machines_banner_cta_label}
|
||||||
url={'https://www.plop.io'} />
|
url={banner.machines_banner_cta_active && banner.machines_banner_cta_url} />
|
||||||
}
|
}
|
||||||
<MachinesFilters onFilterChangedBy={handleFilterChangedBy} machineCategories={machineCategories}/>
|
<MachinesFilters onFilterChangedBy={handleFilterChangedBy} machineCategories={machineCategories}/>
|
||||||
<div className="all-machines">
|
<div className="all-machines">
|
||||||
|
@ -22,7 +22,7 @@ interface PlanCategoryFormProps {
|
|||||||
const PlanCategoryForm: React.FC<PlanCategoryFormProps> = ({ action, category, onSuccess, onError }) => {
|
const PlanCategoryForm: React.FC<PlanCategoryFormProps> = ({ action, category, onSuccess, onError }) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
const { register, control, handleSubmit } = useForm<PlanCategory>({ defaultValues: { ...category } });
|
const { register, control, handleSubmit, formState } = useForm<PlanCategory>({ defaultValues: { ...category } });
|
||||||
/**
|
/**
|
||||||
* The action has been confirmed by the user.
|
* The action has been confirmed by the user.
|
||||||
* Push the created/updated plan-category to the API.
|
* Push the created/updated plan-category to the API.
|
||||||
@ -48,7 +48,7 @@ const PlanCategoryForm: React.FC<PlanCategoryFormProps> = ({ action, category, o
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<FormInput id='name' register={register} rules={{ required: 'true' }} label={t('app.admin.plan_category_form.name')} />
|
<FormInput id='name' register={register} rules={{ required: 'true' }} formState={formState} label={t('app.admin.plan_category_form.name')} />
|
||||||
|
|
||||||
<FormRichText control={control} id="description" label={t('app.admin.plan_category_form.description')} limit={100} />
|
<FormRichText control={control} id="description" label={t('app.admin.plan_category_form.description')} limit={100} />
|
||||||
|
|
||||||
|
@ -94,6 +94,7 @@ export const SpaceForm: React.FC<SpaceFormProps> = ({ action, space, onError, on
|
|||||||
<FormRichText control={control}
|
<FormRichText control={control}
|
||||||
id="description"
|
id="description"
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.space_form.description')}
|
label={t('app.admin.space_form.description')}
|
||||||
limit={null}
|
limit={null}
|
||||||
heading bulletList blockquote link video image />
|
heading bulletList blockquote link video image />
|
||||||
|
@ -254,7 +254,7 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
|
|||||||
|
|
||||||
<section>
|
<section>
|
||||||
<header>
|
<header>
|
||||||
<p className="title">{t('app.admin.store.product_form.assigning_machines')}</p>
|
<p className="title" role="heading">{t('app.admin.store.product_form.assigning_machines')}</p>
|
||||||
<p className="description">{t('app.admin.store.product_form.assigning_machines_info')}</p>
|
<p className="description">{t('app.admin.store.product_form.assigning_machines_info')}</p>
|
||||||
</header>
|
</header>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
|
@ -45,7 +45,7 @@ export const StoreProduct: React.FC<StoreProductProps> = ({ productSlug, current
|
|||||||
setProduct(data);
|
setProduct(data);
|
||||||
const productImage = _.find(data.product_images_attributes, { is_main: true });
|
const productImage = _.find(data.product_images_attributes, { is_main: true });
|
||||||
if (productImage) {
|
if (productImage) {
|
||||||
setShowImage(productImage.id);
|
setShowImage(productImage.id as number);
|
||||||
}
|
}
|
||||||
setToCartCount(data.quantity_min ? data.quantity_min : 1);
|
setToCartCount(data.quantity_min ? data.quantity_min : 1);
|
||||||
setDisplayToggle(descContainer.current.offsetHeight < descContainer.current.scrollHeight);
|
setDisplayToggle(descContainer.current.offsetHeight < descContainer.current.scrollHeight);
|
||||||
@ -132,7 +132,7 @@ export const StoreProduct: React.FC<StoreProductProps> = ({ productSlug, current
|
|||||||
<div className='thumbnails'>
|
<div className='thumbnails'>
|
||||||
{product.product_images_attributes.map(i => (
|
{product.product_images_attributes.map(i => (
|
||||||
<div key={i.id} className={`picture ${i.id === showImage ? 'is-active' : ''}`}>
|
<div key={i.id} className={`picture ${i.id === showImage ? 'is-active' : ''}`}>
|
||||||
<img alt='' onClick={() => setShowImage(i.id)} src={i.thumb_attachment_url} />
|
<img alt='' onClick={() => setShowImage(i.id as number)} src={i.thumb_attachment_url} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,6 +31,7 @@ export const StoreSettings: React.FC<StoreSettingsProps> = ({ onError, onSuccess
|
|||||||
.then(settings => {
|
.then(settings => {
|
||||||
const data = SettingLib.bulkMapToObject(settings);
|
const data = SettingLib.bulkMapToObject(settings);
|
||||||
reset(data);
|
reset(data);
|
||||||
|
console.log(data);
|
||||||
})
|
})
|
||||||
.catch(onError);
|
.catch(onError);
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -113,6 +113,7 @@ export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, on
|
|||||||
<FormRichText control={control}
|
<FormRichText control={control}
|
||||||
id="description"
|
id="description"
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.admin.training_form.description')}
|
label={t('app.admin.training_form.description')}
|
||||||
limit={null}
|
limit={null}
|
||||||
heading bulletList blockquote link />
|
heading bulletList blockquote link />
|
||||||
|
@ -33,7 +33,7 @@ export const ChangePassword = <TFieldValues extends FieldValues>({ register, onE
|
|||||||
const [isConfirmedPassword, setIsConfirmedPassword] = React.useState<boolean>(false);
|
const [isConfirmedPassword, setIsConfirmedPassword] = React.useState<boolean>(false);
|
||||||
const [isPrivileged, setIsPrivileged] = React.useState<boolean>(false);
|
const [isPrivileged, setIsPrivileged] = React.useState<boolean>(false);
|
||||||
|
|
||||||
const { handleSubmit, register: passwordRegister } = useForm<{ password: string }>();
|
const { handleSubmit, register: passwordRegister, formState: passwordFormState } = useForm<{ password: string }>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
MemberAPI.current().then(operator => {
|
MemberAPI.current().then(operator => {
|
||||||
@ -106,6 +106,7 @@ export const ChangePassword = <TFieldValues extends FieldValues>({ register, onE
|
|||||||
type="password"
|
type="password"
|
||||||
register={passwordRegister}
|
register={passwordRegister}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={passwordFormState}
|
||||||
label={t('app.shared.change_password.confirm_current')} />
|
label={t('app.shared.change_password.confirm_current')} />
|
||||||
<FabButton type="submit">
|
<FabButton type="submit">
|
||||||
{t('app.shared.change_password.confirm')}
|
{t('app.shared.change_password.confirm')}
|
||||||
|
@ -40,7 +40,7 @@ type selectGroupOption = { value: number, label: string };
|
|||||||
*/
|
*/
|
||||||
export const ChangeRoleModal: React.FC<ChangeRoleModalProps> = ({ isOpen, toggleModal, user, onSuccess, onError }) => {
|
export const ChangeRoleModal: React.FC<ChangeRoleModalProps> = ({ isOpen, toggleModal, user, onSuccess, onError }) => {
|
||||||
const { t } = useTranslation('admin');
|
const { t } = useTranslation('admin');
|
||||||
const { control, handleSubmit } = useForm<RoleFormData>({ defaultValues: { role: user.role, groupId: user.group_id } });
|
const { control, handleSubmit, formState } = useForm<RoleFormData>({ defaultValues: { role: user.role, groupId: user.group_id } });
|
||||||
|
|
||||||
const [groups, setGroups] = useState<Array<Group>>([]);
|
const [groups, setGroups] = useState<Array<Group>>([]);
|
||||||
|
|
||||||
@ -104,14 +104,16 @@ export const ChangeRoleModal: React.FC<ChangeRoleModalProps> = ({ isOpen, toggle
|
|||||||
control={control}
|
control={control}
|
||||||
id="role"
|
id="role"
|
||||||
label={t('app.admin.change_role_modal.new_role')}
|
label={t('app.admin.change_role_modal.new_role')}
|
||||||
rules={{ required: true }} />
|
rules={{ required: true }}
|
||||||
|
formState={formState} />
|
||||||
<FormSelect options={buildGroupsOptions()}
|
<FormSelect options={buildGroupsOptions()}
|
||||||
control={control}
|
control={control}
|
||||||
disabled={!canChangeGroup()}
|
disabled={!canChangeGroup()}
|
||||||
id="groupId"
|
id="groupId"
|
||||||
label={t('app.admin.change_role_modal.new_group')}
|
label={t('app.admin.change_role_modal.new_group')}
|
||||||
tooltip={t('app.admin.change_role_modal.new_group_help')}
|
tooltip={t('app.admin.change_role_modal.new_group_help')}
|
||||||
rules={{ required: true }} />
|
rules={{ required: true }}
|
||||||
|
formState={formState} />
|
||||||
</form>
|
</form>
|
||||||
</FabModal>
|
</FabModal>
|
||||||
);
|
);
|
||||||
|
@ -208,6 +208,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
|
|||||||
label={t('app.shared.user_profile_form.date_of_birth')}
|
label={t('app.shared.user_profile_form.date_of_birth')}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
|
formState={formState}
|
||||||
type="date" />
|
type="date" />
|
||||||
<FormInput id="profile_attributes.phone"
|
<FormInput id="profile_attributes.phone"
|
||||||
register={register}
|
register={register}
|
||||||
@ -230,6 +231,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
|
|||||||
register={register}
|
register={register}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
rules={{ required: fieldsSettings.get('address_required') === 'true' }}
|
rules={{ required: fieldsSettings.get('address_required') === 'true' }}
|
||||||
|
formState={formState}
|
||||||
label={t('app.shared.user_profile_form.address')} />
|
label={t('app.shared.user_profile_form.address')} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@ import { UseFormRegister, Validate } from 'react-hook-form';
|
|||||||
import { Control, FormState } from 'react-hook-form/dist/types/form';
|
import { Control, FormState } from 'react-hook-form/dist/types/form';
|
||||||
|
|
||||||
export type ruleTypes = {
|
export type ruleTypes = {
|
||||||
required?: boolean | string,
|
required?: boolean | string | { value: boolean, message: string },
|
||||||
pattern?: RegExp | { value: RegExp, message: string },
|
pattern?: RegExp | { value: RegExp, message: string },
|
||||||
minLength?: number | { value: number, message: string },
|
minLength?: number | { value: number, message: string },
|
||||||
maxLength?: number | { value: number, message: string },
|
maxLength?: number | { value: number, message: string },
|
||||||
@ -16,17 +16,21 @@ export type ruleTypes = {
|
|||||||
* Automatic error handling is done through the `formState` prop.
|
* Automatic error handling is done through the `formState` prop.
|
||||||
* Even for manual error/warning, the `formState` prop is required, because it is used to determine if the field is dirty.
|
* Even for manual error/warning, the `formState` prop is required, because it is used to determine if the field is dirty.
|
||||||
*/
|
*/
|
||||||
export interface AbstractFormComponent<TFieldValues> {
|
interface AbstractFormComponentCommon {
|
||||||
error?: { message: string },
|
error?: { message: string },
|
||||||
warning?: { message: string },
|
warning?: { message: string }
|
||||||
rules?: ruleTypes,
|
|
||||||
formState?: FormState<TFieldValues>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormComponent<TFieldValues> extends AbstractFormComponent<TFieldValues> {
|
type AbstractFormComponentRules<TFieldValues> =
|
||||||
|
{ rules: ruleTypes, formState: FormState<TFieldValues> } |
|
||||||
|
{ rules?: never, formState?: FormState<TFieldValues> };
|
||||||
|
|
||||||
|
export type AbstractFormComponent<TFieldValues> = AbstractFormComponentCommon & AbstractFormComponentRules<TFieldValues>;
|
||||||
|
|
||||||
|
export type FormComponent<TFieldValues> = AbstractFormComponent<TFieldValues> & {
|
||||||
register: UseFormRegister<TFieldValues>,
|
register: UseFormRegister<TFieldValues>,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormControlledComponent<TFieldValues, TContext extends object> extends AbstractFormComponent<TFieldValues> {
|
export type FormControlledComponent<TFieldValues, TContext extends object> = AbstractFormComponent<TFieldValues> & {
|
||||||
control: Control<TFieldValues, TContext>
|
control: Control<TFieldValues, TContext>
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user