From 07a8f68ef180984de2ce8df9671dce18d51143c2 Mon Sep 17 00:00:00 2001 From: Weiko Date: Thu, 10 Aug 2023 11:26:27 -0700 Subject: [PATCH] Add FieldDefinition (#1162) * add fieldDefinition * update naming * use a unique contextProvider for editable fields * remove EntityUpdateMutationHookContext.Provider usage in CompanyBoardCard * add fieldDefinitionState * remove unnecessary refetchQueries to avoid re-render * add FieldMetadata * add type guards and update useUpdateGenericEntityField * restore refetchQueries --- .../activities/hooks/useCompleteTask.ts | 2 - .../companies/components/CompanyBoardCard.tsx | 90 ++++---- .../components/HooksCompanyBoard.tsx | 6 +- .../ui/board/components/EntityBoard.tsx | 2 +- .../ui/board/states/FieldDefinitionContext.ts | 17 +- ...State.ts => viewFieldsDefinitionsState.ts} | 4 +- .../components/GenericEditableDateField.tsx | 29 ++- .../GenericEditableDateFieldEditMode.tsx | 30 +-- .../components/GenericEditableField.tsx | 39 ++-- .../components/GenericEditableNumberField.tsx | 29 ++- .../GenericEditableNumberFieldEditMode.tsx | 32 +-- .../GenericEditableRelationField.tsx | 37 +-- .../GenericEditableRelationFieldEditMode.tsx | 30 ++- .../components/ProbabilityEditableField.tsx | 21 +- .../ProbabilityEditableFieldEditMode.tsx | 32 +-- .../hooks/useUpdateGenericEntityField.ts | 215 ++++++++---------- .../states/EditableFieldContext.ts | 16 ++ .../editable-field/types/FieldDefinition.ts | 9 + .../ui/editable-field/types/FieldMetadata.ts | 113 +++++++++ .../types/guards/isFieldChip.ts | 8 + .../types/guards/isFieldChipValue.ts | 12 + .../types/guards/isFieldDate.ts | 8 + .../types/guards/isFieldDateValue.ts | 12 + .../types/guards/isFieldDoubleText.ts | 8 + .../types/guards/isFieldDoubleTextChip.ts | 8 + .../guards/isFieldDoubleTextChipValue.ts | 12 + .../types/guards/isFieldDoubleTextValue.ts | 12 + .../types/guards/isFieldNumber.ts | 8 + .../types/guards/isFieldNumberValue.ts | 12 + .../types/guards/isFieldPhone.ts | 8 + .../types/guards/isFieldPhoneValue.ts | 12 + .../types/guards/isFieldProbability.ts | 8 + .../types/guards/isFieldProbabilityValue.ts | 12 + .../types/guards/isFieldRelation.ts | 8 + .../types/guards/isFieldRelationValue.ts | 12 + .../types/guards/isFieldText.ts | 8 + .../types/guards/isFieldTextValue.ts | 12 + .../editable-field/types/guards/isFieldURL.ts | 8 + .../types/guards/isFieldURLValue.ts | 12 + 39 files changed, 644 insertions(+), 309 deletions(-) rename front/src/modules/ui/board/states/{fieldsDefinitionsState.ts => viewFieldsDefinitionsState.ts} (70%) create mode 100644 front/src/modules/ui/editable-field/states/EditableFieldContext.ts create mode 100644 front/src/modules/ui/editable-field/types/FieldDefinition.ts create mode 100644 front/src/modules/ui/editable-field/types/FieldMetadata.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldChip.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldChipValue.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldDate.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldDateValue.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldDoubleText.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextChip.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextChipValue.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextValue.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldNumber.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldNumberValue.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldPhone.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldPhoneValue.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldProbability.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldProbabilityValue.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldRelation.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldRelationValue.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldText.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldTextValue.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldURL.ts create mode 100644 front/src/modules/ui/editable-field/types/guards/isFieldURLValue.ts diff --git a/front/src/modules/activities/hooks/useCompleteTask.ts b/front/src/modules/activities/hooks/useCompleteTask.ts index 9b535c2595..ecf6973e21 100644 --- a/front/src/modules/activities/hooks/useCompleteTask.ts +++ b/front/src/modules/activities/hooks/useCompleteTask.ts @@ -16,8 +16,6 @@ export function useCompleteTask(task: Task) { fragment: ACTIVITY_UPDATE_FRAGMENT, }); - console.log('cachedTask', cachedTask); - const completeTask = useCallback( (value: boolean) => { const completedAt = value ? new Date().toISOString() : null; diff --git a/front/src/modules/companies/components/CompanyBoardCard.tsx b/front/src/modules/companies/components/CompanyBoardCard.tsx index e92ecd150d..8e4b620aa3 100644 --- a/front/src/modules/companies/components/CompanyBoardCard.tsx +++ b/front/src/modules/companies/components/CompanyBoardCard.tsx @@ -3,16 +3,15 @@ import styled from '@emotion/styled'; import { useRecoilState, useRecoilValue } from 'recoil'; import { BoardCardIdContext } from '@/ui/board/states/BoardCardIdContext'; -import { fieldsDefinitionsState } from '@/ui/board/states/fieldsDefinitionsState'; import { selectedBoardCardIdsState } from '@/ui/board/states/selectedBoardCardIdsState'; +import { viewFieldsDefinitionsState } from '@/ui/board/states/viewFieldsDefinitionsState'; import { EntityChipVariant } from '@/ui/chip/components/EntityChip'; import { GenericEditableField } from '@/ui/editable-field/components/GenericEditableField'; -import { EditableFieldEntityIdContext } from '@/ui/editable-field/states/EditableFieldEntityIdContext'; +import { EditableFieldContext } from '@/ui/editable-field/states/EditableFieldContext'; import { Checkbox, CheckboxVariant, } from '@/ui/input/checkbox/components/Checkbox'; -import { EntityUpdateMutationHookContext } from '@/ui/table/states/EntityUpdateMutationHookContext'; import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql'; import { getLogoUrlFromDomainName } from '~/utils'; @@ -111,7 +110,7 @@ export function CompanyBoardCard() { const [selectedBoardCards, setSelectedBoardCards] = useRecoilState( selectedBoardCardIdsState, ); - const fieldsDefinitions = useRecoilValue(fieldsDefinitionsState); + const viewFieldsDefinitions = useRecoilValue(viewFieldsDefinitionsState); const selected = selectedBoardCards.includes(boardCardId ?? ''); @@ -125,7 +124,8 @@ export function CompanyBoardCard() { } } - if (!company || !pipelineProgress) { + // boardCardId check can be moved to a wrapper to avoid unnecessary logic above + if (!company || !pipelineProgress || !boardCardId) { return null; } @@ -146,42 +146,50 @@ export function CompanyBoardCard() { } return ( - - - setSelected(!selected)} - > - - + setSelected(!selected)} + > + + + + setSelected(!selected)} + variant={CheckboxVariant.Secondary} /> - - setSelected(!selected)} - variant={CheckboxVariant.Secondary} - /> - - - - {fieldsDefinitions.map((viewField) => { - return ( - - - - - - ); - })} - - - - + + + + {viewFieldsDefinitions.map((viewField) => { + return ( + + + + + + ); + })} + + + ); } diff --git a/front/src/modules/companies/components/HooksCompanyBoard.tsx b/front/src/modules/companies/components/HooksCompanyBoard.tsx index 0bde12d68d..ffda2cf6e6 100644 --- a/front/src/modules/companies/components/HooksCompanyBoard.tsx +++ b/front/src/modules/companies/components/HooksCompanyBoard.tsx @@ -2,8 +2,8 @@ import { useEffect, useMemo } from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; import { pipelineViewFields } from '@/pipeline/constants/pipelineViewFields'; -import { fieldsDefinitionsState } from '@/ui/board/states/fieldsDefinitionsState'; import { isBoardLoadedState } from '@/ui/board/states/isBoardLoadedState'; +import { viewFieldsDefinitionsState } from '@/ui/board/states/viewFieldsDefinitionsState'; import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState'; import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; @@ -27,7 +27,9 @@ export function HooksCompanyBoard({ }: { orderBy: PipelineProgresses_Order_By[]; }) { - const setFieldsDefinitionsState = useSetRecoilState(fieldsDefinitionsState); + const setFieldsDefinitionsState = useSetRecoilState( + viewFieldsDefinitionsState, + ); useEffect(() => { setFieldsDefinitionsState(pipelineViewFields); diff --git a/front/src/modules/ui/board/components/EntityBoard.tsx b/front/src/modules/ui/board/components/EntityBoard.tsx index 6f3090887e..d681c0e62f 100644 --- a/front/src/modules/ui/board/components/EntityBoard.tsx +++ b/front/src/modules/ui/board/components/EntityBoard.tsx @@ -7,6 +7,7 @@ import { IconList } from '@tabler/icons-react'; import { useRecoilState } from 'recoil'; import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext'; +import { GET_PIPELINE_PROGRESS } from '@/pipeline/queries'; import { BoardHeader } from '@/ui/board/components/BoardHeader'; import { StyledBoard } from '@/ui/board/components/StyledBoard'; import { useUpdateBoardCardIds } from '@/ui/board/hooks/useUpdateBoardCardIds'; @@ -21,7 +22,6 @@ import { useUpdateOnePipelineProgressStageMutation, } from '~/generated/graphql'; -import { GET_PIPELINE_PROGRESS } from '../../../pipeline/queries'; import { BoardColumnContext } from '../states/BoardColumnContext'; import { boardColumnsState } from '../states/boardColumnsState'; import { selectedBoardCardIdsState } from '../states/selectedBoardCardIdsState'; diff --git a/front/src/modules/ui/board/states/FieldDefinitionContext.ts b/front/src/modules/ui/board/states/FieldDefinitionContext.ts index c5fb7dc366..0a7fb8b2ac 100644 --- a/front/src/modules/ui/board/states/FieldDefinitionContext.ts +++ b/front/src/modules/ui/board/states/FieldDefinitionContext.ts @@ -1,9 +1,14 @@ import { createContext } from 'react'; -import { - ViewFieldDefinition, - ViewFieldMetadata, -} from '../../editable-field/types/ViewField'; +import { FieldDefinition } from '@/ui/editable-field/types/FieldDefinition'; +import { FieldMetadata } from '@/ui/editable-field/types/FieldMetadata'; -export const FieldDefinitionContext = - createContext | null>(null); +export const FieldDefinitionContext = createContext< + FieldDefinition +>({ + id: '', + label: '', + icon: undefined, + type: '', + metadata: {} as FieldMetadata, +}); diff --git a/front/src/modules/ui/board/states/fieldsDefinitionsState.ts b/front/src/modules/ui/board/states/viewFieldsDefinitionsState.ts similarity index 70% rename from front/src/modules/ui/board/states/fieldsDefinitionsState.ts rename to front/src/modules/ui/board/states/viewFieldsDefinitionsState.ts index 6fd293646e..c3f40224a4 100644 --- a/front/src/modules/ui/board/states/fieldsDefinitionsState.ts +++ b/front/src/modules/ui/board/states/viewFieldsDefinitionsState.ts @@ -5,9 +5,9 @@ import type { ViewFieldMetadata, } from '../../editable-field/types/ViewField'; -export const fieldsDefinitionsState = atom< +export const viewFieldsDefinitionsState = atom< ViewFieldDefinition[] >({ - key: 'fieldsDefinitionState', + key: 'viewFieldsDefinitionState', default: [], }); diff --git a/front/src/modules/ui/editable-field/components/GenericEditableDateField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableDateField.tsx index 5d9c561558..10a095c4b2 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableDateField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableDateField.tsx @@ -1,32 +1,31 @@ import { useContext } from 'react'; import { useRecoilValue } from 'recoil'; -import { - ViewFieldDateMetadata, - ViewFieldDefinition, -} from '@/ui/editable-field/types/ViewField'; import { DateInputDisplay } from '@/ui/input/date/components/DateInputDisplay'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { parseDate } from '~/utils/date-utils'; -import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { EditableFieldContext } from '../states/EditableFieldContext'; import { FieldContext } from '../states/FieldContext'; import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldDateMetadata } from '../types/FieldMetadata'; import { EditableField } from './EditableField'; import { GenericEditableDateFieldEditMode } from './GenericEditableDateFieldEditMode'; -type OwnProps = { - viewField: ViewFieldDefinition; -}; - -export function GenericEditableDateField({ viewField }: OwnProps) { - const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); +export function GenericEditableDateField() { + const currentEditableField = useContext(EditableFieldContext); + const currentEditableFieldEntityId = currentEditableField.entityId; + const currentEditableFieldDefinition = + currentEditableField.fieldDefinition as FieldDefinition; const fieldValue = useRecoilValue( genericEntityFieldFamilySelector({ entityId: currentEditableFieldEntityId ?? '', - fieldName: viewField.metadata.fieldName, + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', }), ); @@ -37,10 +36,8 @@ export function GenericEditableDateField({ viewField }: OwnProps) { return ( - } + iconLabel={currentEditableFieldDefinition.icon} + editModeContent={} displayModeContent={} isDisplayModeContentEmpty={!fieldValue} /> diff --git a/front/src/modules/ui/editable-field/components/GenericEditableDateFieldEditMode.tsx b/front/src/modules/ui/editable-field/components/GenericEditableDateFieldEditMode.tsx index 8d3b256d7a..7163105c9b 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableDateFieldEditMode.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableDateFieldEditMode.tsx @@ -1,28 +1,26 @@ import { useContext } from 'react'; import { useRecoilState } from 'recoil'; -import { - ViewFieldDateMetadata, - ViewFieldDefinition, -} from '@/ui/editable-field/types/ViewField'; - import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField'; -import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { EditableFieldContext } from '../states/EditableFieldContext'; import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldDateMetadata } from '../types/FieldMetadata'; import { EditableFieldEditModeDate } from '../variants/components/EditableFieldEditModeDate'; -type OwnProps = { - viewField: ViewFieldDefinition; -}; - -export function GenericEditableDateFieldEditMode({ viewField }: OwnProps) { - const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); +export function GenericEditableDateFieldEditMode() { + const currentEditableField = useContext(EditableFieldContext); + const currentEditableFieldEntityId = currentEditableField.entityId; + const currentEditableFieldDefinition = + currentEditableField.fieldDefinition as FieldDefinition; // TODO: we could use a hook that would return the field value with the right type const [fieldValue, setFieldValue] = useRecoilState( genericEntityFieldFamilySelector({ entityId: currentEditableFieldEntityId ?? '', - fieldName: viewField.metadata.fieldName, + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', }), ); @@ -34,7 +32,11 @@ export function GenericEditableDateFieldEditMode({ viewField }: OwnProps) { setFieldValue(newDateISO); if (currentEditableFieldEntityId && updateField) { - updateField(currentEditableFieldEntityId, viewField, newDateISO); + updateField( + currentEditableFieldEntityId, + currentEditableFieldDefinition, + newDateISO, + ); } } diff --git a/front/src/modules/ui/editable-field/components/GenericEditableField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableField.tsx index 70d639a80e..af1fa59432 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableField.tsx @@ -1,34 +1,31 @@ -import { - ViewFieldDefinition, - ViewFieldMetadata, -} from '@/ui/editable-field/types/ViewField'; +import { useContext } from 'react'; -import { isViewFieldDate } from '../types/guards/isViewFieldDate'; -import { isViewFieldNumber } from '../types/guards/isViewFieldNumber'; -import { isViewFieldProbability } from '../types/guards/isViewFieldProbability'; -import { isViewFieldRelation } from '../types/guards/isViewFieldRelation'; +import { EditableFieldContext } from '../states/EditableFieldContext'; +import { isFieldDate } from '../types/guards/isFieldDate'; +import { isFieldNumber } from '../types/guards/isFieldNumber'; +import { isFieldProbability } from '../types/guards/isFieldProbability'; +import { isFieldRelation } from '../types/guards/isFieldRelation'; import { GenericEditableDateField } from './GenericEditableDateField'; import { GenericEditableNumberField } from './GenericEditableNumberField'; import { GenericEditableRelationField } from './GenericEditableRelationField'; import { ProbabilityEditableField } from './ProbabilityEditableField'; -type OwnProps = { - viewField: ViewFieldDefinition; -}; +export function GenericEditableField() { + const currentEditableField = useContext(EditableFieldContext); + const fieldDefinition = currentEditableField.fieldDefinition; -export function GenericEditableField({ viewField: fieldDefinition }: OwnProps) { - if (isViewFieldDate(fieldDefinition)) { - return ; - } else if (isViewFieldNumber(fieldDefinition)) { - return ; - } else if (isViewFieldRelation(fieldDefinition)) { - return ; - } else if (isViewFieldProbability(fieldDefinition)) { - return ; + if (isFieldRelation(fieldDefinition)) { + return ; + } else if (isFieldDate(fieldDefinition)) { + return ; + } else if (isFieldNumber(fieldDefinition)) { + return ; + } else if (isFieldProbability(fieldDefinition)) { + return ; } else { console.warn( - `Unknown field metadata type: ${fieldDefinition.metadata.type} in GenericEditableField`, + `Unknown field metadata type: ${fieldDefinition.metadata.type} in GenericEditableCell`, ); return <>; } diff --git a/front/src/modules/ui/editable-field/components/GenericEditableNumberField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableNumberField.tsx index 252352e4b1..8ef7c60236 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableNumberField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableNumberField.tsx @@ -1,40 +1,37 @@ import { useContext } from 'react'; import { useRecoilValue } from 'recoil'; -import { - ViewFieldDefinition, - ViewFieldNumberMetadata, -} from '@/ui/editable-field/types/ViewField'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; -import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { EditableFieldContext } from '../states/EditableFieldContext'; import { FieldContext } from '../states/FieldContext'; import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldNumberMetadata } from '../types/FieldMetadata'; import { EditableField } from './EditableField'; import { GenericEditableNumberFieldEditMode } from './GenericEditableNumberFieldEditMode'; -type OwnProps = { - viewField: ViewFieldDefinition; -}; - -export function GenericEditableNumberField({ viewField }: OwnProps) { - const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); +export function GenericEditableNumberField() { + const currentEditableField = useContext(EditableFieldContext); + const currentEditableFieldEntityId = currentEditableField.entityId; + const currentEditableFieldDefinition = + currentEditableField.fieldDefinition as FieldDefinition; const fieldValue = useRecoilValue( genericEntityFieldFamilySelector({ entityId: currentEditableFieldEntityId ?? '', - fieldName: viewField.metadata.fieldName, + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', }), ); return ( - } + iconLabel={currentEditableFieldDefinition.icon} + editModeContent={} displayModeContent={fieldValue} isDisplayModeContentEmpty={!fieldValue} /> diff --git a/front/src/modules/ui/editable-field/components/GenericEditableNumberFieldEditMode.tsx b/front/src/modules/ui/editable-field/components/GenericEditableNumberFieldEditMode.tsx index 35afc59c50..905e9fd613 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableNumberFieldEditMode.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableNumberFieldEditMode.tsx @@ -1,10 +1,6 @@ import { useContext, useRef, useState } from 'react'; import { useRecoilState } from 'recoil'; -import { - ViewFieldDefinition, - ViewFieldNumberMetadata, -} from '@/ui/editable-field/types/ViewField'; import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit'; import { canBeCastAsIntegerOrNull, @@ -13,21 +9,24 @@ import { import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers'; import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField'; -import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { EditableFieldContext } from '../states/EditableFieldContext'; import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldNumberMetadata } from '../types/FieldMetadata'; -type OwnProps = { - viewField: ViewFieldDefinition; -}; - -export function GenericEditableNumberFieldEditMode({ viewField }: OwnProps) { - const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); +export function GenericEditableNumberFieldEditMode() { + const currentEditableField = useContext(EditableFieldContext); + const currentEditableFieldEntityId = currentEditableField.entityId; + const currentEditableFieldDefinition = + currentEditableField.fieldDefinition as FieldDefinition; // TODO: we could use a hook that would return the field value with the right type const [fieldValue, setFieldValue] = useRecoilState( genericEntityFieldFamilySelector({ entityId: currentEditableFieldEntityId ?? '', - fieldName: viewField.metadata.fieldName, + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', }), ); const [internalValue, setInternalValue] = useState( @@ -36,6 +35,10 @@ export function GenericEditableNumberFieldEditMode({ viewField }: OwnProps) { const updateField = useUpdateGenericEntityField(); + const wrapperRef = useRef(null); + + useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel); + function handleSubmit() { if (!canBeCastAsIntegerOrNull(internalValue)) { return; @@ -47,7 +50,7 @@ export function GenericEditableNumberFieldEditMode({ viewField }: OwnProps) { if (currentEditableFieldEntityId && updateField) { updateField( currentEditableFieldEntityId, - viewField, + currentEditableFieldDefinition, castAsIntegerOrNull(internalValue), ); } @@ -60,9 +63,6 @@ export function GenericEditableNumberFieldEditMode({ viewField }: OwnProps) { function handleChange(newValue: string) { setInternalValue(newValue); } - const wrapperRef = useRef(null); - - useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel); return (
diff --git a/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx index a52f9ecf30..1b2c632200 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx @@ -2,30 +2,25 @@ import { useContext } from 'react'; import { useRecoilValue } from 'recoil'; import { PersonChip } from '@/people/components/PersonChip'; -import { - ViewFieldDefinition, - ViewFieldRelationMetadata, -} from '@/ui/editable-field/types/ViewField'; +import { ViewFieldRelationMetadata } from '@/ui/editable-field/types/ViewField'; import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; -import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { EditableFieldContext } from '../states/EditableFieldContext'; import { FieldContext } from '../states/FieldContext'; import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldRelationMetadata } from '../types/FieldMetadata'; import { EditableField } from './EditableField'; import { GenericEditableRelationFieldEditMode } from './GenericEditableRelationFieldEditMode'; -type OwnProps = { - viewField: ViewFieldDefinition; -}; - function RelationChip({ fieldDefinition, fieldValue, }: { - fieldDefinition: ViewFieldDefinition; + fieldDefinition: FieldDefinition; fieldValue: any | null; }) { switch (fieldDefinition.metadata.relationType) { @@ -46,13 +41,18 @@ function RelationChip({ } } -export function GenericEditableRelationField({ viewField }: OwnProps) { - const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); +export function GenericEditableRelationField() { + const currentEditableField = useContext(EditableFieldContext); + const currentEditableFieldEntityId = currentEditableField.entityId; + const currentEditableFieldDefinition = + currentEditableField.fieldDefinition as FieldDefinition; const fieldValue = useRecoilValue( genericEntityFieldFamilySelector({ entityId: currentEditableFieldEntityId ?? '', - fieldName: viewField.metadata.fieldName, + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', }), ); @@ -64,12 +64,13 @@ export function GenericEditableRelationField({ viewField }: OwnProps) { customEditHotkeyScope={{ scope: RelationPickerHotkeyScope.RelationPicker, }} - iconLabel={viewField.columnIcon} - editModeContent={ - - } + iconLabel={currentEditableFieldDefinition.icon} + editModeContent={} displayModeContent={ - + } isDisplayModeContentEmpty={!fieldValue} isDisplayModeFixHeight diff --git a/front/src/modules/ui/editable-field/components/GenericEditableRelationFieldEditMode.tsx b/front/src/modules/ui/editable-field/components/GenericEditableRelationFieldEditMode.tsx index ce00ba74b3..a053031355 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableRelationFieldEditMode.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableRelationFieldEditMode.tsx @@ -4,7 +4,6 @@ import { useRecoilState } from 'recoil'; import { PeoplePicker } from '@/people/components/PeoplePicker'; import { - ViewFieldDefinition, ViewFieldRelationMetadata, ViewFieldRelationValue, } from '@/ui/editable-field/types/ViewField'; @@ -13,8 +12,10 @@ import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; import { useEditableField } from '../hooks/useEditableField'; import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField'; -import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { EditableFieldContext } from '../states/EditableFieldContext'; import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldRelationMetadata } from '../types/FieldMetadata'; const RelationPickerContainer = styled.div` left: 0px; @@ -22,17 +23,13 @@ const RelationPickerContainer = styled.div` top: -8px; `; -type OwnProps = { - viewField: ViewFieldDefinition; -}; - function RelationPicker({ fieldDefinition, fieldValue, handleEntitySubmit, handleCancel, }: { - fieldDefinition: ViewFieldDefinition; + fieldDefinition: FieldDefinition; fieldValue: ViewFieldRelationValue; handleEntitySubmit: (newRelationId: EntityForSelect | null) => void; handleCancel: () => void; @@ -55,14 +52,19 @@ function RelationPicker({ } } -export function GenericEditableRelationFieldEditMode({ viewField }: OwnProps) { - const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); +export function GenericEditableRelationFieldEditMode() { + const currentEditableField = useContext(EditableFieldContext); + const currentEditableFieldEntityId = currentEditableField.entityId; + const currentEditableFieldDefinition = + currentEditableField.fieldDefinition as FieldDefinition; // TODO: we could use a hook that would return the field value with the right type const [fieldValue, setFieldValue] = useRecoilState( genericEntityFieldFamilySelector({ entityId: currentEditableFieldEntityId ?? '', - fieldName: viewField.metadata.fieldName, + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', }), ); @@ -79,7 +81,11 @@ export function GenericEditableRelationFieldEditMode({ viewField }: OwnProps) { }); if (currentEditableFieldEntityId && updateField) { - updateField(currentEditableFieldEntityId, viewField, newRelation); + updateField( + currentEditableFieldEntityId, + currentEditableFieldDefinition, + newRelation, + ); } closeEditableField(); @@ -92,7 +98,7 @@ export function GenericEditableRelationFieldEditMode({ viewField }: OwnProps) { return ( ; -}; +export function ProbabilityEditableField() { + const currentEditableField = useContext(EditableFieldContext); + const currentEditableFieldDefinition = currentEditableField.fieldDefinition; -export function ProbabilityEditableField({ viewField }: OwnProps) { return ( - } + iconLabel={currentEditableFieldDefinition.icon} + displayModeContent={} displayModeContentOnly disableHoverEffect /> diff --git a/front/src/modules/ui/editable-field/components/ProbabilityEditableFieldEditMode.tsx b/front/src/modules/ui/editable-field/components/ProbabilityEditableFieldEditMode.tsx index e41c010889..03873ecd62 100644 --- a/front/src/modules/ui/editable-field/components/ProbabilityEditableFieldEditMode.tsx +++ b/front/src/modules/ui/editable-field/components/ProbabilityEditableFieldEditMode.tsx @@ -5,12 +5,10 @@ import { useRecoilState } from 'recoil'; import { useEditableField } from '@/ui/editable-field/hooks/useEditableField'; import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField'; -import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { EditableFieldContext } from '../states/EditableFieldContext'; import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; -import { - ViewFieldDefinition, - ViewFieldProbabilityMetadata, -} from '../types/ViewField'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldProbabilityMetadata } from '../types/FieldMetadata'; const StyledContainer = styled.div` align-items: center; @@ -60,10 +58,6 @@ const StyledLabel = styled.div` width: ${({ theme }) => theme.spacing(12)}; `; -type OwnProps = { - viewField: ViewFieldDefinition; -}; - const PROBABILITY_VALUES = [ { label: '0%', value: 0 }, { label: '25%', value: 25 }, @@ -72,28 +66,38 @@ const PROBABILITY_VALUES = [ { label: '100%', value: 100 }, ]; -export function ProbabilityEditableFieldEditMode({ viewField }: OwnProps) { +export function ProbabilityEditableFieldEditMode() { const [nextProbabilityIndex, setNextProbabilityIndex] = useState< number | null >(null); - const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); + const currentEditableField = useContext(EditableFieldContext); + const currentEditableFieldEntityId = currentEditableField.entityId; + const currentEditableFieldDefinition = + currentEditableField.fieldDefinition as FieldDefinition; const [fieldValue, setFieldValue] = useRecoilState( genericEntityFieldFamilySelector({ entityId: currentEditableFieldEntityId ?? '', - fieldName: viewField.metadata.fieldName, + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', }), ); - const probabilityIndex = Math.ceil(fieldValue / 25); const { closeEditableField } = useEditableField(); const updateField = useUpdateGenericEntityField(); + const probabilityIndex = Math.ceil(fieldValue / 25); + function handleChange(newValue: number) { setFieldValue(newValue); if (currentEditableFieldEntityId && updateField) { - updateField(currentEditableFieldEntityId, viewField, newValue); + updateField( + currentEditableFieldEntityId, + currentEditableFieldDefinition, + newValue, + ); } closeEditableField(); } diff --git a/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts b/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts index 87165dd9f1..16a2f903e4 100644 --- a/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts +++ b/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts @@ -1,100 +1,98 @@ import { useContext } from 'react'; -import { isViewFieldChip } from '@/ui/editable-field/types/guards/isViewFieldChip'; -import { EntityUpdateMutationHookContext } from '@/ui/table/states/EntityUpdateMutationHookContext'; +import { isFieldChip } from '@/ui/editable-field/types/guards/isFieldChip'; -import { isViewFieldChipValue } from '../types/guards/isViewFieldChipValue'; -import { isViewFieldDate } from '../types/guards/isViewFieldDate'; -import { isViewFieldDateValue } from '../types/guards/isViewFieldDateValue'; -import { isViewFieldDoubleText } from '../types/guards/isViewFieldDoubleText'; -import { isViewFieldDoubleTextChip } from '../types/guards/isViewFieldDoubleTextChip'; -import { isViewFieldDoubleTextChipValue } from '../types/guards/isViewFieldDoubleTextChipValue'; -import { isViewFieldDoubleTextValue } from '../types/guards/isViewFieldDoubleTextValue'; -import { isViewFieldNumber } from '../types/guards/isViewFieldNumber'; -import { isViewFieldNumberValue } from '../types/guards/isViewFieldNumberValue'; -import { isViewFieldPhone } from '../types/guards/isViewFieldPhone'; -import { isViewFieldPhoneValue } from '../types/guards/isViewFieldPhoneValue'; -import { isViewFieldProbability } from '../types/guards/isViewFieldProbability'; -import { isViewFieldProbabilityValue } from '../types/guards/isViewFieldProbabilityValue'; -import { isViewFieldRelation } from '../types/guards/isViewFieldRelation'; -import { isViewFieldRelationValue } from '../types/guards/isViewFieldRelationValue'; -import { isViewFieldText } from '../types/guards/isViewFieldText'; -import { isViewFieldTextValue } from '../types/guards/isViewFieldTextValue'; -import { isViewFieldURL } from '../types/guards/isViewFieldURL'; -import { isViewFieldURLValue } from '../types/guards/isViewFieldURLValue'; +import { EditableFieldContext } from '../states/EditableFieldContext'; +import { FieldDefinition } from '../types/FieldDefinition'; import { - ViewFieldChipMetadata, - ViewFieldChipValue, - ViewFieldDateMetadata, - ViewFieldDateValue, - ViewFieldDefinition, - ViewFieldDoubleTextChipMetadata, - ViewFieldDoubleTextChipValue, - ViewFieldDoubleTextMetadata, - ViewFieldDoubleTextValue, - ViewFieldMetadata, - ViewFieldNumberMetadata, - ViewFieldNumberValue, - ViewFieldPhoneMetadata, - ViewFieldPhoneValue, - ViewFieldProbabilityMetadata, - ViewFieldProbabilityValue, - ViewFieldRelationMetadata, - ViewFieldRelationValue, - ViewFieldTextMetadata, - ViewFieldTextValue, - ViewFieldURLMetadata, - ViewFieldURLValue, -} from '../types/ViewField'; + FieldChipMetadata, + FieldChipValue, + FieldDateMetadata, + FieldDateValue, + FieldDoubleTextChipMetadata, + FieldDoubleTextChipValue, + FieldDoubleTextMetadata, + FieldDoubleTextValue, + FieldMetadata, + FieldNumberMetadata, + FieldNumberValue, + FieldPhoneMetadata, + FieldPhoneValue, + FieldProbabilityMetadata, + FieldProbabilityValue, + FieldRelationMetadata, + FieldRelationValue, + FieldTextMetadata, + FieldTextValue, + FieldURLMetadata, + FieldURLValue, +} from '../types/FieldMetadata'; +import { isFieldChipValue } from '../types/guards/isFieldChipValue'; +import { isFieldDate } from '../types/guards/isFieldDate'; +import { isFieldDateValue } from '../types/guards/isFieldDateValue'; +import { isFieldDoubleText } from '../types/guards/isFieldDoubleText'; +import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip'; +import { isFieldDoubleTextChipValue } from '../types/guards/isFieldDoubleTextChipValue'; +import { isFieldDoubleTextValue } from '../types/guards/isFieldDoubleTextValue'; +import { isFieldNumber } from '../types/guards/isFieldNumber'; +import { isFieldNumberValue } from '../types/guards/isFieldNumberValue'; +import { isFieldPhone } from '../types/guards/isFieldPhone'; +import { isFieldPhoneValue } from '../types/guards/isFieldPhoneValue'; +import { isFieldProbability } from '../types/guards/isFieldProbability'; +import { isFieldProbabilityValue } from '../types/guards/isFieldProbabilityValue'; +import { isFieldRelation } from '../types/guards/isFieldRelation'; +import { isFieldRelationValue } from '../types/guards/isFieldRelationValue'; +import { isFieldText } from '../types/guards/isFieldText'; +import { isFieldTextValue } from '../types/guards/isFieldTextValue'; +import { isFieldURL } from '../types/guards/isFieldURL'; +import { isFieldURLValue } from '../types/guards/isFieldURLValue'; export function useUpdateGenericEntityField() { - const useUpdateEntityMutation = useContext(EntityUpdateMutationHookContext); + const currentEditableField = useContext(EditableFieldContext); + const useUpdateEntityMutation = currentEditableField?.mutation; const [updateEntity] = useUpdateEntityMutation(); - return function updatePeopleField< - MetadataType extends ViewFieldMetadata, - ValueType extends MetadataType extends ViewFieldDoubleTextMetadata - ? ViewFieldDoubleTextValue - : MetadataType extends ViewFieldTextMetadata - ? ViewFieldTextValue - : MetadataType extends ViewFieldPhoneMetadata - ? ViewFieldPhoneValue - : MetadataType extends ViewFieldURLMetadata - ? ViewFieldURLValue - : MetadataType extends ViewFieldNumberMetadata - ? ViewFieldNumberValue - : MetadataType extends ViewFieldDateMetadata - ? ViewFieldDateValue - : MetadataType extends ViewFieldChipMetadata - ? ViewFieldChipValue - : MetadataType extends ViewFieldDoubleTextChipMetadata - ? ViewFieldDoubleTextChipValue - : MetadataType extends ViewFieldRelationMetadata - ? ViewFieldRelationValue - : MetadataType extends ViewFieldProbabilityMetadata - ? ViewFieldProbabilityValue + return function updateEntityField< + MetadataType extends FieldMetadata, + ValueType extends MetadataType extends FieldDoubleTextMetadata + ? FieldDoubleTextValue + : MetadataType extends FieldTextMetadata + ? FieldTextValue + : MetadataType extends FieldPhoneMetadata + ? FieldPhoneValue + : MetadataType extends FieldURLMetadata + ? FieldURLValue + : MetadataType extends FieldNumberMetadata + ? FieldNumberValue + : MetadataType extends FieldDateMetadata + ? FieldDateValue + : MetadataType extends FieldChipMetadata + ? FieldChipValue + : MetadataType extends FieldDoubleTextChipMetadata + ? FieldDoubleTextChipValue + : MetadataType extends FieldRelationMetadata + ? FieldRelationValue + : MetadataType extends FieldProbabilityMetadata + ? FieldProbabilityValue : unknown, >( currentEntityId: string, - viewField: ViewFieldDefinition, + field: FieldDefinition, newFieldValue: ValueType, ) { const newFieldValueUnknown = newFieldValue as unknown; - // TODO: improve type guards organization, maybe with a common typeguard for all view fields - // taking an object of options as parameter ? + // TODO: improve type guards organization, maybe with a common typeguard for all fields + // taking an object of options as parameter ? // - // The goal would be to check that the view field value not only is valid, - // but also that it is validated against the corresponding view field type + // The goal would be to check that the field value not only is valid, + // but also that it is validated against the corresponding field type // Relation - if ( - isViewFieldRelation(viewField) && - isViewFieldRelationValue(newFieldValueUnknown) - ) { + if (isFieldRelation(field) && isFieldRelationValue(newFieldValueUnknown)) { const newSelectedEntity = newFieldValueUnknown; - const fieldName = viewField.metadata.fieldName; + const fieldName = field.metadata.fieldName; if (!newSelectedEntity) { updateEntity({ @@ -120,35 +118,29 @@ export function useUpdateGenericEntityField() { }); } // Chip - } else if ( - isViewFieldChip(viewField) && - isViewFieldChipValue(newFieldValueUnknown) - ) { + } else if (isFieldChip(field) && isFieldChipValue(newFieldValueUnknown)) { const newContent = newFieldValueUnknown; updateEntity({ variables: { where: { id: currentEntityId }, - data: { [viewField.metadata.contentFieldName]: newContent }, + data: { [field.metadata.contentFieldName]: newContent }, }, }); // Text - } else if ( - isViewFieldText(viewField) && - isViewFieldTextValue(newFieldValueUnknown) - ) { + } else if (isFieldText(field) && isFieldTextValue(newFieldValueUnknown)) { const newContent = newFieldValueUnknown; updateEntity({ variables: { where: { id: currentEntityId }, - data: { [viewField.metadata.fieldName]: newContent }, + data: { [field.metadata.fieldName]: newContent }, }, }); // Double text } else if ( - isViewFieldDoubleText(viewField) && - isViewFieldDoubleTextValue(newFieldValueUnknown) + isFieldDoubleText(field) && + isFieldDoubleTextValue(newFieldValueUnknown) ) { const newContent = newFieldValueUnknown; @@ -156,15 +148,15 @@ export function useUpdateGenericEntityField() { variables: { where: { id: currentEntityId }, data: { - [viewField.metadata.firstValueFieldName]: newContent.firstValue, - [viewField.metadata.secondValueFieldName]: newContent.secondValue, + [field.metadata.firstValueFieldName]: newContent.firstValue, + [field.metadata.secondValueFieldName]: newContent.secondValue, }, }, }); // Double Text Chip } else if ( - isViewFieldDoubleTextChip(viewField) && - isViewFieldDoubleTextChipValue(newFieldValueUnknown) + isFieldDoubleTextChip(field) && + isFieldDoubleTextChipValue(newFieldValueUnknown) ) { const newContent = newFieldValueUnknown; @@ -172,73 +164,64 @@ export function useUpdateGenericEntityField() { variables: { where: { id: currentEntityId }, data: { - [viewField.metadata.firstValueFieldName]: newContent.firstValue, - [viewField.metadata.secondValueFieldName]: newContent.secondValue, + [field.metadata.firstValueFieldName]: newContent.firstValue, + [field.metadata.secondValueFieldName]: newContent.secondValue, }, }, }); // Phone - } else if ( - isViewFieldPhone(viewField) && - isViewFieldPhoneValue(newFieldValueUnknown) - ) { + } else if (isFieldPhone(field) && isFieldPhoneValue(newFieldValueUnknown)) { const newContent = newFieldValueUnknown; updateEntity({ variables: { where: { id: currentEntityId }, - data: { [viewField.metadata.fieldName]: newContent }, + data: { [field.metadata.fieldName]: newContent }, }, }); // URL - } else if ( - isViewFieldURL(viewField) && - isViewFieldURLValue(newFieldValueUnknown) - ) { + } else if (isFieldURL(field) && isFieldURLValue(newFieldValueUnknown)) { const newContent = newFieldValueUnknown; updateEntity({ variables: { where: { id: currentEntityId }, - data: { [viewField.metadata.fieldName]: newContent }, + data: { [field.metadata.fieldName]: newContent }, }, }); // Number } else if ( - isViewFieldNumber(viewField) && - isViewFieldNumberValue(newFieldValueUnknown) + isFieldNumber(field) && + isFieldNumberValue(newFieldValueUnknown) ) { const newContent = newFieldValueUnknown; updateEntity({ variables: { where: { id: currentEntityId }, - data: { [viewField.metadata.fieldName]: newContent }, + data: { [field.metadata.fieldName]: newContent }, }, }); // Date - } else if ( - isViewFieldDate(viewField) && - isViewFieldDateValue(newFieldValueUnknown) - ) { + } else if (isFieldDate(field) && isFieldDateValue(newFieldValueUnknown)) { const newContent = newFieldValueUnknown; updateEntity({ variables: { where: { id: currentEntityId }, - data: { [viewField.metadata.fieldName]: newContent }, + data: { [field.metadata.fieldName]: newContent }, }, }); } else if ( - isViewFieldProbability(viewField) && - isViewFieldProbabilityValue(newFieldValueUnknown) + isFieldProbability(field) && + isFieldProbabilityValue(newFieldValueUnknown) ) { const newContent = newFieldValueUnknown; updateEntity({ variables: { where: { id: currentEntityId }, - data: { [viewField.metadata.fieldName]: newContent }, + data: { [field.metadata.fieldName]: newContent }, }, }); } diff --git a/front/src/modules/ui/editable-field/states/EditableFieldContext.ts b/front/src/modules/ui/editable-field/states/EditableFieldContext.ts new file mode 100644 index 0000000000..8182560073 --- /dev/null +++ b/front/src/modules/ui/editable-field/states/EditableFieldContext.ts @@ -0,0 +1,16 @@ +import { createContext } from 'react'; + +import { FieldDefinition } from '../types/FieldDefinition'; +import { ViewFieldMetadata } from '../types/ViewField'; + +type EditableFieldContextValue = { + entityId: string; + fieldDefinition: FieldDefinition; + mutation: any; +}; + +export const EditableFieldContext = createContext({ + entityId: '', + fieldDefinition: {} as FieldDefinition, + mutation: undefined, +}); diff --git a/front/src/modules/ui/editable-field/types/FieldDefinition.ts b/front/src/modules/ui/editable-field/types/FieldDefinition.ts new file mode 100644 index 0000000000..0cb8583089 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/FieldDefinition.ts @@ -0,0 +1,9 @@ +import { FieldMetadata } from './FieldMetadata'; + +export type FieldDefinition = { + id: string; + label: string; + icon?: JSX.Element; + type: string; + metadata: T; +}; diff --git a/front/src/modules/ui/editable-field/types/FieldMetadata.ts b/front/src/modules/ui/editable-field/types/FieldMetadata.ts new file mode 100644 index 0000000000..0b2e598b36 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/FieldMetadata.ts @@ -0,0 +1,113 @@ +import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; +import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; + +export type FieldType = + | 'text' + | 'relation' + | 'chip' + | 'double-text-chip' + | 'double-text' + | 'number' + | 'date' + | 'phone' + | 'url' + | 'probability'; + +export type FieldTextMetadata = { + type: 'text'; + placeHolder: string; + fieldName: string; +}; + +export type FieldPhoneMetadata = { + type: 'phone'; + placeHolder: string; + fieldName: string; +}; + +export type FieldURLMetadata = { + type: 'url'; + placeHolder: string; + fieldName: string; +}; + +export type FieldDateMetadata = { + type: 'date'; + fieldName: string; +}; + +export type FieldNumberMetadata = { + type: 'number'; + fieldName: string; +}; + +export type FieldRelationMetadata = { + type: 'relation'; + relationType: Entity; + fieldName: string; +}; + +export type FieldChipMetadata = { + type: 'chip'; + relationType: Entity; + contentFieldName: string; + urlFieldName: string; + placeHolder: string; +}; + +export type FieldDoubleTextMetadata = { + type: 'double-text'; + firstValueFieldName: string; + firstValuePlaceholder: string; + secondValueFieldName: string; + secondValuePlaceholder: string; +}; + +export type FieldDoubleTextChipMetadata = { + type: 'double-text-chip'; + firstValueFieldName: string; + firstValuePlaceholder: string; + secondValueFieldName: string; + secondValuePlaceholder: string; + avatarUrlFieldName: string; + entityType: Entity; +}; + +export type FieldProbabilityMetadata = { + type: 'probability'; + fieldName: string; +}; + +export type FieldMetadata = { type: FieldType } & ( + | FieldTextMetadata + | FieldRelationMetadata + | FieldChipMetadata + | FieldDoubleTextChipMetadata + | FieldDoubleTextMetadata + | FieldPhoneMetadata + | FieldURLMetadata + | FieldNumberMetadata + | FieldDateMetadata + | FieldProbabilityMetadata +); + +export type FieldTextValue = string; + +export type FieldChipValue = string; +export type FieldDateValue = string; +export type FieldPhoneValue = string; +export type FieldURLValue = string; +export type FieldNumberValue = number | null; +export type FieldProbabilityValue = number; + +export type FieldDoubleTextValue = { + firstValue: string; + secondValue: string; +}; + +export type FieldDoubleTextChipValue = { + firstValue: string; + secondValue: string; +}; + +export type FieldRelationValue = EntityForSelect | null; diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldChip.ts b/front/src/modules/ui/editable-field/types/guards/isFieldChip.ts new file mode 100644 index 0000000000..b7e2eac37e --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldChip.ts @@ -0,0 +1,8 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldChipMetadata, FieldMetadata } from '../FieldMetadata'; + +export function isFieldChip( + field: FieldDefinition, +): field is FieldDefinition { + return field.type === 'chip'; +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldChipValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldChipValue.ts new file mode 100644 index 0000000000..649a08fa4b --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldChipValue.ts @@ -0,0 +1,12 @@ +import { FieldChipValue } from '../FieldMetadata'; + +// TODO: add yup +export function isFieldChipValue( + fieldValue: unknown, +): fieldValue is FieldChipValue { + return ( + fieldValue !== null && + fieldValue !== undefined && + typeof fieldValue === 'string' + ); +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldDate.ts b/front/src/modules/ui/editable-field/types/guards/isFieldDate.ts new file mode 100644 index 0000000000..e6258847eb --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldDate.ts @@ -0,0 +1,8 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldDateMetadata, FieldMetadata } from '../FieldMetadata'; + +export function isFieldDate( + field: FieldDefinition, +): field is FieldDefinition { + return field.type === 'date'; +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldDateValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldDateValue.ts new file mode 100644 index 0000000000..1d7bdfd884 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldDateValue.ts @@ -0,0 +1,12 @@ +import { FieldDateValue } from '../FieldMetadata'; + +// TODO: add yup +export function isFieldDateValue( + fieldValue: unknown, +): fieldValue is FieldDateValue { + return ( + fieldValue !== null && + fieldValue !== undefined && + typeof fieldValue === 'string' + ); +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldDoubleText.ts b/front/src/modules/ui/editable-field/types/guards/isFieldDoubleText.ts new file mode 100644 index 0000000000..b30c0c6a03 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldDoubleText.ts @@ -0,0 +1,8 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldDoubleTextMetadata, FieldMetadata } from '../FieldMetadata'; + +export function isFieldDoubleText( + field: FieldDefinition, +): field is FieldDefinition { + return field.type === 'double-text'; +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextChip.ts b/front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextChip.ts new file mode 100644 index 0000000000..73bc0c545b --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextChip.ts @@ -0,0 +1,8 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldDoubleTextChipMetadata, FieldMetadata } from '../FieldMetadata'; + +export function isFieldDoubleTextChip( + field: FieldDefinition, +): field is FieldDefinition { + return field.type === 'double-text-chip'; +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextChipValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextChipValue.ts new file mode 100644 index 0000000000..b5d7812a39 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextChipValue.ts @@ -0,0 +1,12 @@ +import { FieldDoubleTextChipValue } from '../FieldMetadata'; + +// TODO: add yup +export function isFieldDoubleTextChipValue( + fieldValue: unknown, +): fieldValue is FieldDoubleTextChipValue { + return ( + fieldValue !== null && + fieldValue !== undefined && + typeof fieldValue === 'object' + ); +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextValue.ts new file mode 100644 index 0000000000..9e5047676c --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldDoubleTextValue.ts @@ -0,0 +1,12 @@ +import { FieldDoubleTextValue } from '../FieldMetadata'; + +// TODO: add yup +export function isFieldDoubleTextValue( + fieldValue: unknown, +): fieldValue is FieldDoubleTextValue { + return ( + fieldValue !== null && + fieldValue !== undefined && + typeof fieldValue === 'object' + ); +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldNumber.ts b/front/src/modules/ui/editable-field/types/guards/isFieldNumber.ts new file mode 100644 index 0000000000..7c23d61674 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldNumber.ts @@ -0,0 +1,8 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldMetadata, FieldNumberMetadata } from '../FieldMetadata'; + +export function isFieldNumber( + field: FieldDefinition, +): field is FieldDefinition { + return field.type === 'number'; +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldNumberValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldNumberValue.ts new file mode 100644 index 0000000000..452bdbedef --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldNumberValue.ts @@ -0,0 +1,12 @@ +import { FieldNumberValue } from '../FieldMetadata'; + +// TODO: add yup +export function isFieldNumberValue( + fieldValue: unknown, +): fieldValue is FieldNumberValue { + return ( + fieldValue !== null && + fieldValue !== undefined && + typeof fieldValue === 'number' + ); +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldPhone.ts b/front/src/modules/ui/editable-field/types/guards/isFieldPhone.ts new file mode 100644 index 0000000000..e886b9bbc1 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldPhone.ts @@ -0,0 +1,8 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldMetadata, FieldPhoneMetadata } from '../FieldMetadata'; + +export function isFieldPhone( + field: FieldDefinition, +): field is FieldDefinition { + return field.type === 'phone'; +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldPhoneValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldPhoneValue.ts new file mode 100644 index 0000000000..1337385a78 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldPhoneValue.ts @@ -0,0 +1,12 @@ +import { FieldPhoneValue } from '../FieldMetadata'; + +// TODO: add yup +export function isFieldPhoneValue( + fieldValue: unknown, +): fieldValue is FieldPhoneValue { + return ( + fieldValue !== null && + fieldValue !== undefined && + typeof fieldValue === 'string' + ); +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldProbability.ts b/front/src/modules/ui/editable-field/types/guards/isFieldProbability.ts new file mode 100644 index 0000000000..97c12661ea --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldProbability.ts @@ -0,0 +1,8 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldMetadata, FieldProbabilityMetadata } from '../FieldMetadata'; + +export function isFieldProbability( + field: FieldDefinition, +): field is FieldDefinition { + return field.type === 'probability'; +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldProbabilityValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldProbabilityValue.ts new file mode 100644 index 0000000000..d4dad6eb35 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldProbabilityValue.ts @@ -0,0 +1,12 @@ +import { FieldProbabilityValue } from '../FieldMetadata'; + +// TODO: add yup +export function isFieldProbabilityValue( + fieldValue: unknown, +): fieldValue is FieldProbabilityValue { + return ( + fieldValue !== null && + fieldValue !== undefined && + typeof fieldValue === 'number' + ); +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldRelation.ts b/front/src/modules/ui/editable-field/types/guards/isFieldRelation.ts new file mode 100644 index 0000000000..bb24ecac2d --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldRelation.ts @@ -0,0 +1,8 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldMetadata, FieldRelationMetadata } from '../FieldMetadata'; + +export function isFieldRelation( + field: FieldDefinition, +): field is FieldDefinition { + return field.type === 'relation'; +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldRelationValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldRelationValue.ts new file mode 100644 index 0000000000..5624e0c25e --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldRelationValue.ts @@ -0,0 +1,12 @@ +import { FieldRelationValue } from '../FieldMetadata'; + +// TODO: add yup +export function isFieldRelationValue( + fieldValue: unknown, +): fieldValue is FieldRelationValue { + return ( + fieldValue !== null && + fieldValue !== undefined && + typeof fieldValue === 'object' + ); +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldText.ts b/front/src/modules/ui/editable-field/types/guards/isFieldText.ts new file mode 100644 index 0000000000..d616dd5956 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldText.ts @@ -0,0 +1,8 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldMetadata, FieldTextMetadata } from '../FieldMetadata'; + +export function isFieldText( + field: FieldDefinition, +): field is FieldDefinition { + return field.type === 'text'; +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldTextValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldTextValue.ts new file mode 100644 index 0000000000..19f958249b --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldTextValue.ts @@ -0,0 +1,12 @@ +import { FieldTextValue } from '../FieldMetadata'; + +// TODO: add yup +export function isFieldTextValue( + fieldValue: unknown, +): fieldValue is FieldTextValue { + return ( + fieldValue !== null && + fieldValue !== undefined && + typeof fieldValue === 'string' + ); +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldURL.ts b/front/src/modules/ui/editable-field/types/guards/isFieldURL.ts new file mode 100644 index 0000000000..5092c760a4 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldURL.ts @@ -0,0 +1,8 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldMetadata, FieldURLMetadata } from '../FieldMetadata'; + +export function isFieldURL( + field: FieldDefinition, +): field is FieldDefinition { + return field.type === 'url'; +} diff --git a/front/src/modules/ui/editable-field/types/guards/isFieldURLValue.ts b/front/src/modules/ui/editable-field/types/guards/isFieldURLValue.ts new file mode 100644 index 0000000000..96afe41546 --- /dev/null +++ b/front/src/modules/ui/editable-field/types/guards/isFieldURLValue.ts @@ -0,0 +1,12 @@ +import { FieldURLValue } from '../FieldMetadata'; + +// TODO: add yup +export function isFieldURLValue( + fieldValue: unknown, +): fieldValue is FieldURLValue { + return ( + fieldValue !== null && + fieldValue !== undefined && + typeof fieldValue === 'string' + ); +}