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:
parent
294c47b796
commit
e51e2d63cb
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
@ -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' : ''}
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -13,6 +13,9 @@
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
&.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
&.is-required &-header p::after {
|
||||
content: "*";
|
||||
margin-left: 0.5ch;
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user