From 0a86de81cab5a577ee623ec7d5247c3700ae2898 Mon Sep 17 00:00:00 2001 From: JyTosTT <44032246+JyTosTT@users.noreply.github.com> Date: Tue, 10 Oct 2023 18:26:40 +0000 Subject: [PATCH] feat/services-logic (#133) * feat(context): adding interface * feat(context): adding interface * feat(context): adding form * feat(context): adding form * feat(context): adding form --- front/src/forms/editor.structure.ts | 18 +- front/src/hooks/useDrawerEditor.ts | 1 + front/src/hooks/useLinkerEditor.ts | 1 + front/src/interfaces/Forms/Input.interface.ts | 1 + front/src/interfaces/Service.interface.ts | 4 + front/src/types/board/drawer/Common.bases.ts | 2 + front/src/views/atoms/forms/Input.atom.tsx | 3 +- .../views/atoms/forms/RadioButton.atom.tsx | 22 +++ .../ServiceVariable/PortVariable.molecule.tsx | 2 +- .../ServiceVariable/Variable.molecule.tsx | 4 +- front/src/views/organisms/Editor.organism.tsx | 12 +- .../ServiceForm/LocalisationForm.organism.tsx | 168 ++++++++++++++++++ 12 files changed, 222 insertions(+), 16 deletions(-) create mode 100644 front/src/views/atoms/forms/RadioButton.atom.tsx create mode 100644 front/src/views/organisms/ServiceForm/LocalisationForm.organism.tsx diff --git a/front/src/forms/editor.structure.ts b/front/src/forms/editor.structure.ts index 491898d8..85465fb9 100644 --- a/front/src/forms/editor.structure.ts +++ b/front/src/forms/editor.structure.ts @@ -6,6 +6,7 @@ import TextArea from '../views/atoms/forms/TextArea.atom' import EnvVariablesOrganism from '../views/organisms/ServiceVariables/EnvVariables.organism' import PortVariablesOrganism from '../views/organisms/ServiceVariables/PortVariables.organism' import VolumeVariablesOrganism from '../views/organisms/ServiceVariables/VolumeVariables.organism' +import LocalisationFormOrganism from '../views/organisms/ServiceForm/LocalisationForm.organism' export type EditorStructure = { [key in DrawerTypes]: EditorForm[] @@ -27,6 +28,7 @@ export interface EditorForm { type: TypeList component: any + disabled?: any validator?: any maxLength?: number } @@ -111,18 +113,10 @@ export const DRAWER_TYPE_STRUCTURES: EditorStructure = { maxLength: 25 }, { - label: 'Docker Image', - key: 'dockerImage', - type: TypeList.TEXT, - component: Input, - validator: string().nullable() - }, - { - label: 'Docker Tag', - key: 'dockerTag', - type: TypeList.TEXT, - component: Input, - validator: string().nullable() + label: 'Context', + key: 'context', + type: TypeList.CUSTOM, + component: LocalisationFormOrganism }, { label: 'Env file', diff --git a/front/src/hooks/useDrawerEditor.ts b/front/src/hooks/useDrawerEditor.ts index 1d90018c..93717b3b 100644 --- a/front/src/hooks/useDrawerEditor.ts +++ b/front/src/hooks/useDrawerEditor.ts @@ -54,6 +54,7 @@ const useDrawerEditor = (drawer: TDrawer, stackId: string): TEditor => return { fields: structure, entityForm, + onForm: setEntityForm, onSubmit, onChange, onDelete, diff --git a/front/src/hooks/useLinkerEditor.ts b/front/src/hooks/useLinkerEditor.ts index b2f5b97f..d74126e2 100644 --- a/front/src/hooks/useLinkerEditor.ts +++ b/front/src/hooks/useLinkerEditor.ts @@ -47,6 +47,7 @@ const useDrawerEditor = (linker: TBaseLinker, _: string): TEditor = fields: structure, entityForm: linkerForm, onSubmit, + onForm: setLinkerForm, onDelete, onChange, onClose diff --git a/front/src/interfaces/Forms/Input.interface.ts b/front/src/interfaces/Forms/Input.interface.ts index 48db97ed..bba5e8cc 100644 --- a/front/src/interfaces/Forms/Input.interface.ts +++ b/front/src/interfaces/Forms/Input.interface.ts @@ -9,6 +9,7 @@ export interface InputProps { className?: string required?: boolean maxLength?: number + disabled?: boolean onChange?: (e: TOnChange) => void onKeyDown?: (e: React.KeyboardEvent) => void } diff --git a/front/src/interfaces/Service.interface.ts b/front/src/interfaces/Service.interface.ts index c3f162f1..8cd17c22 100644 --- a/front/src/interfaces/Service.interface.ts +++ b/front/src/interfaces/Service.interface.ts @@ -8,12 +8,16 @@ export interface IService { name: string description: string + containerName: string dockerImage: string dockerTag: string entrypoint: string isExternal: boolean + context: string + dockerFile: string + positionX: number positionY: number test: number diff --git a/front/src/types/board/drawer/Common.bases.ts b/front/src/types/board/drawer/Common.bases.ts index 9efb10d8..e0190916 100644 --- a/front/src/types/board/drawer/Common.bases.ts +++ b/front/src/types/board/drawer/Common.bases.ts @@ -1,5 +1,6 @@ import { type EditorForm } from '../../../forms/editor.structure' import { type TOnChange } from '../../../interfaces/Forms/Input.interface' +import { type IForm } from '../../../hooks/useForm' export interface TCommonBases { create: () => void @@ -12,5 +13,6 @@ export interface TEditor { onChange: (event: TOnChange) => void onDelete?: () => void onClose: () => void + onForm: IForm['setForm'] entityForm: T } diff --git a/front/src/views/atoms/forms/Input.atom.tsx b/front/src/views/atoms/forms/Input.atom.tsx index 41c3b143..72e2b5be 100644 --- a/front/src/views/atoms/forms/Input.atom.tsx +++ b/front/src/views/atoms/forms/Input.atom.tsx @@ -1,7 +1,7 @@ import React from 'react' import { type InputProps } from '../../../interfaces/Forms/Input.interface' -const Input = ({ label, type, placeholder, name, value, className = '', onChange, onKeyDown, maxLength, required = false }: InputProps): JSX.Element => { +const Input = ({ label, type, placeholder, name, value, className = '', onChange, onKeyDown, maxLength, required = false, disabled }: InputProps): JSX.Element => { return ( <> {(label != null) && } @@ -13,6 +13,7 @@ const Input = ({ label, type, placeholder, name, value, className = '', onChange onChange={onChange} onKeyDown={onKeyDown} maxLength={maxLength} + disabled={disabled} className={`input input-bordered w-full max-w-xs mt-1 ${className}`} required={required} /> diff --git a/front/src/views/atoms/forms/RadioButton.atom.tsx b/front/src/views/atoms/forms/RadioButton.atom.tsx new file mode 100644 index 00000000..3e3aeae0 --- /dev/null +++ b/front/src/views/atoms/forms/RadioButton.atom.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import { type CheckboxProps } from '../../../interfaces/Forms/Checkbox.interface' + +const Radio = ({ label, name, value = false, className = '', onChange, onKeyDown }: CheckboxProps): JSX.Element => { + return ( + + ) +} + +export default Radio diff --git a/front/src/views/molecules/ServiceVariable/PortVariable.molecule.tsx b/front/src/views/molecules/ServiceVariable/PortVariable.molecule.tsx index 1da7910a..8f715706 100644 --- a/front/src/views/molecules/ServiceVariable/PortVariable.molecule.tsx +++ b/front/src/views/molecules/ServiceVariable/PortVariable.molecule.tsx @@ -1,7 +1,7 @@ import React from 'react' import VariableMolecule from './Variable.molecule' import { type IServicePortVariable, type IServicePortVariableCreate } from '../../../interfaces/ServiceVariable/Port.interface' -import { type IVariableMolecule } from '../../../hooks/ServiceVariables/useEnvVariablesEditor' +import { type IVariableMolecule } from '../../../interfaces/VariableConfig.interface' const PortVariableMolecule = (props: IVariableMolecule): JSX.Element => { return {...props} /> diff --git a/front/src/views/molecules/ServiceVariable/Variable.molecule.tsx b/front/src/views/molecules/ServiceVariable/Variable.molecule.tsx index 0ff099c5..61b0a6c0 100644 --- a/front/src/views/molecules/ServiceVariable/Variable.molecule.tsx +++ b/front/src/views/molecules/ServiceVariable/Variable.molecule.tsx @@ -23,7 +23,9 @@ IVariableMolecule): JSX.Element { const defaultByType = { [TypeList.TEXT]: '', [TypeList.NUMBER]: 0, - [TypeList.CUSTOM]: undefined + [TypeList.CUSTOM]: undefined, + [TypeList.CHECKBOX]: undefined, + [TypeList.FILE]: undefined } const keyList = fields.map(({ key, type }) => ({ [key]: defaultByType[type] })) diff --git a/front/src/views/organisms/Editor.organism.tsx b/front/src/views/organisms/Editor.organism.tsx index 2ff5a440..c99c8d91 100644 --- a/front/src/views/organisms/Editor.organism.tsx +++ b/front/src/views/organisms/Editor.organism.tsx @@ -11,7 +11,15 @@ const EditorOrganism = ({ entity, stackId, useEditor }: { stackId: string useEditor: typeof useDrawerEditor | typeof useLinkerEditor }): JSX.Element => { - const { fields, onSubmit, onChange, entityForm, onDelete, onClose } = useEditor(entity, stackId) + const { + fields, + onSubmit, + onChange, + entityForm, + onForm, + onDelete, + onClose + } = useEditor(entity, stackId) return (
@@ -39,7 +47,9 @@ const EditorOrganism = ({ entity, stackId, useEditor }: { type={field.type} name={field.key} maxLength={field.maxLength} + entityForm={entityForm} entity={entity} + onForm={onForm} value={value} onChange={onChange} /> diff --git a/front/src/views/organisms/ServiceForm/LocalisationForm.organism.tsx b/front/src/views/organisms/ServiceForm/LocalisationForm.organism.tsx new file mode 100644 index 00000000..64f7c0b8 --- /dev/null +++ b/front/src/views/organisms/ServiceForm/LocalisationForm.organism.tsx @@ -0,0 +1,168 @@ +import React, { type ChangeEvent } from 'react' +import Input from '../../atoms/forms/Input.atom' +import { type IService } from '../../../interfaces/Service.interface' +import { type TEditor } from '../../../types/board/drawer/Common.bases' +import { type TEntity } from '../../../types/Entity' +import { type EditorForm, TypeList } from '../../../forms/editor.structure' +import { string } from 'yup' +import Radio from '../../atoms/forms/RadioButton.atom' +import useToggle from '../../../hooks/useToggle' +import { Errors } from '../../../enums/errors' + +const isDefined = (value: string): boolean => + value !== undefined && value !== null && value !== '' + +const keys = (fields: EditorForm[]): string[] => fields.map((field) => field.key) + +const LOCAL_RADIO_NAME: string = 'local' +const REMOTE_RADIO_NAME: string = 'remote' + +const LocalisationFormOrganism = ({ entityForm: serviceForm, onChange, onForm }: { + entityForm: IService + onChange: TEditor['onChange'] + onForm: TEditor['onForm'] +}): JSX.Element => { + const [hasSelectedRemote, toggleSelectedRemote] = useToggle( + isDefined(serviceForm.dockerImage) && isDefined(serviceForm.dockerTag) + ) + + const [hasSelectedLocal, toggleSelectedLocal] = useToggle( + isDefined(serviceForm.context) && isDefined(serviceForm.dockerFile) + ) + + const isEmptyFields = !hasSelectedRemote && !hasSelectedLocal + + const remoteFieldsDisabled = hasSelectedLocal || isEmptyFields + const REMOTE_FIELDS: EditorForm[] = [ + { + label: 'Docker image', + key: 'dockerImage', + type: TypeList.TEXT, + component: Input, + validator: string().nullable(), + disabled: remoteFieldsDisabled + }, + { + label: 'Docker tag', + key: 'dockerTag', + type: TypeList.TEXT, + component: Input, + validator: string().nullable(), + disabled: remoteFieldsDisabled + } + ] + + const localFieldsDisabled = hasSelectedRemote || isEmptyFields + const LOCAL_FIELDS: EditorForm[] = [ + { + label: 'Context', + key: 'context', + type: TypeList.TEXT, + component: Input, + validator: string().nullable(), + disabled: localFieldsDisabled + }, + { + label: 'Docker file', + key: 'dockerFile', + type: TypeList.TEXT, + component: Input, + validator: string().nullable(), + disabled: localFieldsDisabled + } + ] + + const LOCALISATION_FIELDS: EditorForm[][] = [ + REMOTE_FIELDS, + LOCAL_FIELDS + ] + + const onKeyFormReset = (key: keyof TEntity): void => { + // @ts-expect-error ts-migrate Object is possibly 'undefined'. + serviceForm[key] = '' + } + + const emptyFieldsRadioSelection = (name: string): void => { + if (name === 'local') { + toggleSelectedLocal() + } else if (name === 'remote') { + toggleSelectedRemote() + } else { + throw new Error(Errors.NOT_IMPLEMENTED) + } + } + + const onEntityRadioChange = (name: string): void => { + if (name === 'local') { + keys(REMOTE_FIELDS).forEach((key) => { + onKeyFormReset(key as keyof TEntity) + }) + } else if (name === 'remote') { + keys(LOCAL_FIELDS).forEach((key) => { + onKeyFormReset(key as keyof TEntity) + }) + } else { + throw new Error(Errors.NOT_IMPLEMENTED) + } + + toggleSelectedRemote() + toggleSelectedLocal() + onForm({ ...serviceForm }) + } + + const onRadioChange = (event: ChangeEvent): void => { + const { name, value } = event.target + + if (value !== 'on') return + + if (isEmptyFields) { + emptyFieldsRadioSelection(name) + } else { + onEntityRadioChange(name) + } + } + + const generateFields = (fields: EditorForm[]): JSX.Element[] => { + return fields.map((field, index) => { + const Component = field.component + const value = serviceForm[field.key as keyof TEntity] + + return ( + ) + }) + } + + return ( +
+
+
+ +
+
+ +
+ + {LOCALISATION_FIELDS.map((editor, index) => { + return ( +
+
+ {generateFields(editor)} +
+
) + })} +
+
+ ) +} + +export default LocalisationFormOrganism