diff --git a/app/frontend/src/javascript/components/authentication-provider/data-mapping-form.tsx b/app/frontend/src/javascript/components/authentication-provider/data-mapping-form.tsx index 2cff5b92a..887630768 100644 --- a/app/frontend/src/javascript/components/authentication-provider/data-mapping-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/data-mapping-form.tsx @@ -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 { +export interface DataMappingFormProps { register: UseFormRegister, + control: Control, } -export const DataMappingForm = ({ register }: DataMappingFormProps) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [dataMapping, setDataMapping] = React.useState(null); +type selectModelFieldOption = { value: string, label: string }; + +export const DataMappingForm = ({ register, control }: DataMappingFormProps) => { + const { t } = useTranslation('shared'); + const [dataMapping, setDataMapping] = useState(null); + const [isOpenTypeMappingModal, setIsOpenTypeMappingModal] = useState(false); + + const { fields, append, remove } = useFieldArray({ control, name: 'auth_provider_mappings_attributes' as ArrayPath }); + const output = useWatch({ name: 'auth_provider_mappings_attributes' as Path, control }); + + /** + * Build the list of available models for the data mapping + */ + const buildModelOptions = (): Array => { + 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, index: number): Array => { + 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, 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, 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, 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 = ({ register }: return (
- +

{t('app.shared.oauth2.define_the_fields_mapping')}

+ } + onClick={() => append({})}> + {t('app.shared.oauth2.add_a_match')} + + {fields.map((item, index) => ( +
+
+ +
+ + +
+
+ + + +
+
+
+ } onClick={toggleTypeMappingModal} /> + } onClick={() => remove(index)} className="delete-button" /> + +
+
+ ))}
); }; diff --git a/app/frontend/src/javascript/components/authentication-provider/provider-form.tsx b/app/frontend/src/javascript/components/authentication-provider/provider-form.tsx index a85e1754f..17a88ef3e 100644 --- a/app/frontend/src/javascript/components/authentication-provider/provider-form.tsx +++ b/app/frontend/src/javascript/components/authentication-provider/provider-form.tsx @@ -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 = ({ action, provider, on onChange={onProvidableTypeChange} rules={{ required: true }} /> {providableType === 'OAuth2Provider' && } + {providableType && providableType !== 'DatabaseProvider' && } ); diff --git a/app/frontend/src/javascript/components/authentication-provider/type-mapping-modal.tsx b/app/frontend/src/javascript/components/authentication-provider/type-mapping-modal.tsx new file mode 100644 index 000000000..5dd87703a --- /dev/null +++ b/app/frontend/src/javascript/components/authentication-provider/type-mapping-modal.tsx @@ -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 = ({ model, field, type, isOpen, toggleModal }) => { + return ( + +

{model}

+

{field}

+

{type}

+
+ ); +}; diff --git a/app/frontend/src/javascript/components/form/form-input.tsx b/app/frontend/src/javascript/components/form/form-input.tsx index f79fc4eca..71aeac8d6 100644 --- a/app/frontend/src/javascript/components/form/form-input.tsx +++ b/app/frontend/src/javascript/components/form/form-input.tsx @@ -20,6 +20,7 @@ export const FormInput = ({ 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' : ''} diff --git a/app/frontend/src/stylesheets/application.scss b/app/frontend/src/stylesheets/application.scss index 8ab17bd4e..9c22ed779 100644 --- a/app/frontend/src/stylesheets/application.scss +++ b/app/frontend/src/stylesheets/application.scss @@ -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"; diff --git a/app/frontend/src/stylesheets/modules/authentication-provider/data-mapping-form.scss b/app/frontend/src/stylesheets/modules/authentication-provider/data-mapping-form.scss new file mode 100644 index 000000000..3c430fcbc --- /dev/null +++ b/app/frontend/src/stylesheets/modules/authentication-provider/data-mapping-form.scss @@ -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; + } +} diff --git a/app/frontend/src/stylesheets/modules/form/form-item.scss b/app/frontend/src/stylesheets/modules/form/form-item.scss index f6db6dfd0..c47db7c2d 100644 --- a/app/frontend/src/stylesheets/modules/form/form-item.scss +++ b/app/frontend/src/stylesheets/modules/form/form-item.scss @@ -13,6 +13,9 @@ margin: 0; } } + &.is-hidden { + display: none; + } &.is-required &-header p::after { content: "*"; margin-left: 0.5ch; diff --git a/config/locales/app.shared.en.yml b/config/locales/app.shared.en.yml index 21f60259e..7e773d8d3 100644 --- a/config/locales/app.shared.en.yml +++ b/config/locales/app.shared.en.yml @@ -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"