1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-11-29 10:24:20 +01:00

(wip)(ui) type mapping ui

This commit is contained in:
Sylvain 2022-04-06 17:14:23 +02:00
parent 294c47b796
commit e51e2d63cb
8 changed files with 157 additions and 8 deletions

View File

@ -1,17 +1,85 @@
import React, { useEffect } from 'react';
import { UseFormRegister } from 'react-hook-form';
import React, { useEffect, useState } from 'react';
import { UseFormRegister, useFieldArray, ArrayPath, useWatch, Path } from 'react-hook-form';
import { FieldValues } from 'react-hook-form/dist/types/fields';
import AuthProviderAPI from '../../api/auth-provider';
import { MappingFields } from '../../models/authentication-provider';
import { Control } from 'react-hook-form/dist/types/form';
import { FormSelect } from '../form/form-select';
import { FormInput } from '../form/form-input';
import { useTranslation } from 'react-i18next';
import { FabButton } from '../base/fab-button';
import { TypeMappingModal } from './type-mapping-modal';
export interface DataMappingFormProps<TFieldValues> {
export interface DataMappingFormProps<TFieldValues, TContext extends object> {
register: UseFormRegister<TFieldValues>,
control: Control<TFieldValues, TContext>,
}
export const DataMappingForm = <TFieldValues extends FieldValues>({ register }: DataMappingFormProps<TFieldValues>) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [dataMapping, setDataMapping] = React.useState<MappingFields>(null);
type selectModelFieldOption = { value: string, label: string };
export const DataMappingForm = <TFieldValues extends FieldValues, TContext extends object>({ register, control }: DataMappingFormProps<TFieldValues, TContext>) => {
const { t } = useTranslation('shared');
const [dataMapping, setDataMapping] = useState<MappingFields>(null);
const [isOpenTypeMappingModal, setIsOpenTypeMappingModal] = useState<boolean>(false);
const { fields, append, remove } = useFieldArray({ control, name: 'auth_provider_mappings_attributes' as ArrayPath<TFieldValues> });
const output = useWatch({ name: 'auth_provider_mappings_attributes' as Path<TFieldValues>, control });
/**
* Build the list of available models for the data mapping
*/
const buildModelOptions = (): Array<selectModelFieldOption> => {
return Object.keys(dataMapping).map(model => {
return {
label: model,
value: model
};
}) || [];
};
/**
* Build the list of fields of the current model for the data mapping
*/
const buildFieldOptions = (formData: Array<TFieldValues>, index: number): Array<selectModelFieldOption> => {
return dataMapping[getModel(formData, index)]?.map(field => {
return {
label: field[0],
value: field[0]
};
}) || [];
};
/**
* Return the name of the modal for the given index, in the current data-mapping form
*/
const getModel = (formData: Array<TFieldValues>, index: number): string => {
return formData ? formData[index]?.local_model : undefined;
};
/**
* Return the name of the field for the given index, in the current data-mapping form
*/
const getField = (formData: Array<TFieldValues>, index: number): string => {
return formData ? formData[index]?.local_field : undefined;
};
/**
* Return the type of data expected for the given index, in the current data-mapping form
*/
const getDataType = (formData: Array<TFieldValues>, index: number): string => {
const model = getModel(formData, index);
const field = getField(formData, index);
if (model && field) {
return dataMapping[model]?.find(f => f[0] === field)?.[1];
}
};
/**
* Open/closes the "edit type mapping" modal dialog
*/
const toggleTypeMappingModal = (): void => {
setIsOpenTypeMappingModal(!isOpenTypeMappingModal);
};
// fetch the mapping data from the API on mount
useEffect(() => {
@ -22,7 +90,33 @@ export const DataMappingForm = <TFieldValues extends FieldValues>({ register }:
return (
<div className="data-mapping-form">
<FormInput id="local_model" register={register} />
<h4>{t('app.shared.oauth2.define_the_fields_mapping')}</h4>
<FabButton
icon={<i className="fa fa-plus"/>}
onClick={() => append({})}>
{t('app.shared.oauth2.add_a_match')}
</FabButton>
{fields.map((item, index) => (
<div key={item.id} className="data-mapping-item">
<div className="inputs">
<FormInput id={`auth_provider_mappings_attributes.${index}.id`} register={register} type="hidden" />
<div className="local-data">
<FormSelect id={`auth_provider_mappings_attributes.${index}.local_model`} control={control} rules={{ required: true }} options={buildModelOptions()} label={t('app.shared.oauth2.model')}/>
<FormSelect id={`auth_provider_mappings_attributes.${index}.local_field`} options={buildFieldOptions(output, index)} control={control} rules={{ required: true }} label={t('app.shared.oauth2.field')} />
</div>
<div className="remote-data">
<FormInput id={`auth_provider_mappings_attributes.${index}.api_endpoint`} register={register} rules={{ required: true }} placeholder="/api/resource..." label={t('app.shared.oauth2.api_endpoint_url')} />
<FormSelect id={`auth_provider_mappings_attributes.${index}.api_data_type`} options={[{ label: 'JSON', value: 'json' }]} control={control} rules={{ required: true }} label={t('app.shared.oauth2.api_type')} />
<FormInput id={`auth_provider_mappings_attributes.${index}.api_field`} register={register} rules={{ required: true }} placeholder="field_name..." label={t('app.shared.oauth2.api_fields')} />
</div>
</div>
<div className="actions">
<FabButton icon={<i className="fa fa-random" />} onClick={toggleTypeMappingModal} />
<FabButton icon={<i className="fa fa-trash" />} onClick={() => remove(index)} className="delete-button" />
<TypeMappingModal model={getModel(output, index)} field={getField(output, index)} type={getDataType(output, index)} isOpen={isOpenTypeMappingModal} toggleModal={toggleTypeMappingModal} />
</div>
</div>
))}
</div>
);
};

View File

@ -8,6 +8,7 @@ import { FormInput } from '../form/form-input';
import { useTranslation } from 'react-i18next';
import { FormSelect } from '../form/form-select';
import { Oauth2Form } from './oauth2-form';
import { DataMappingForm } from './data-mapping-form';
declare const Application: IApplication;
@ -77,6 +78,7 @@ export const ProviderForm: React.FC<ProviderFormProps> = ({ action, provider, on
onChange={onProvidableTypeChange}
rules={{ required: true }} />
{providableType === 'OAuth2Provider' && <Oauth2Form register={register} />}
{providableType && providableType !== 'DatabaseProvider' && <DataMappingForm register={register} control={control} />}
<input type={'submit'} />
</form>
);

View File

@ -0,0 +1,20 @@
import React from 'react';
import { FabModal } from '../base/fab-modal';
interface TypeMappingModalProps {
model: string,
field: string,
type: string,
isOpen: boolean,
toggleModal: () => void,
}
export const TypeMappingModal: React.FC<TypeMappingModalProps> = ({ model, field, type, isOpen, toggleModal }) => {
return (
<FabModal isOpen={isOpen} toggleModal={toggleModal}>
<h1>{model}</h1>
<h2>{field}</h2>
<h3>{type}</h3>
</FabModal>
);
};

View File

@ -20,6 +20,7 @@ export const FormInput = <TFieldValues extends FieldValues>({ id, register, labe
// Compose classnames from props
const classNames = `
form-item ${className || ''}
${type === 'hidden' ? 'is-hidden' : ''}
${error && error[id] ? 'is-incorrect' : ''}
${rules && rules.required ? 'is-required' : ''}
${readOnly ? 'is-readOnly' : ''}

View File

@ -15,6 +15,7 @@
@import "app.components";
@import "app.plugins";
@import "modules/authentication-provider/data-mapping-form";
@import "modules/base/fab-alert";
@import "modules/base/fab-button";
@import "modules/base/fab-input";

View File

@ -0,0 +1,28 @@
.data-mapping-form {
.data-mapping-item {
margin: 1rem;
border-left: 4px solid var(--gray-soft-dark);
border-radius: 4px;
padding-left: 1em;
display: flex;
flex-direction: row;
align-items: center;
.inputs {
width: 85%;
}
.actions {
padding: 1em;
.delete-button {
background-color: #cb1117;
color: white;
}
}
}
.local-data,
.remote-data{
display: flex;
flex-direction: row;
}
}

View File

@ -13,6 +13,9 @@
margin: 0;
}
}
&.is-hidden {
display: none;
}
&.is-required &-header p::after {
content: "*";
margin-left: 0.5ch;

View File

@ -280,7 +280,7 @@ en:
define_the_fields_mapping: "Define the fields mapping"
add_a_match: "Add a match"
model: "Model"
field: "Fiels"
field: "Field"
api_endpoint_url: "API endpoint URL"
api_type: "API type"
api_fields: "API fields"