From 25950ab82a21b32c8538b59d33f2fb8443a65051 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Mon, 20 Nov 2023 10:33:36 +0100 Subject: [PATCH] Introduce main identifier to power RelationFieldDisplay (#2577) * Introduce main identifier to power RelationFieldDisplay, FilterDrodown, TableFirstColumn * Apply to RelationPicker --- .../components/ActivityTargetChips.tsx | 4 +- .../ActivityRelationEditableFieldEditMode.tsx | 50 +++++----- .../components/TimelineActivityCardFooter.tsx | 2 +- .../__stories__/CompanyChip.stories.tsx | 4 +- .../components/AddPersonToCompany.tsx | 21 ++-- .../companies/components/CompanyBoardCard.tsx | 5 +- .../companies/components/CompanyChip.tsx | 6 +- .../companies/components/CompanyPicker.tsx | 78 --------------- .../components/CompanyProgressPicker.tsx | 15 ++- .../FilterDropdownCompanySearchSelect.tsx | 24 ----- .../components/HooksCompanyBoardEffect.tsx | 10 +- .../components/NewCompanyProgressButton.tsx | 26 +++-- .../hooks/useFilteredSearchCompanyQuery.ts | 44 --------- .../useComputeDefinitionsFromFieldMetadata.ts | 1 - .../useMapFieldMetadataToGraphQLQuery.ts | 2 +- .../hooks/useObjectMainIdentifier.ts | 56 +++++++++++ .../types/FieldMetadataItem.ts | 10 +- ...rmatFieldMetadataItemAsColumnDefinition.ts | 72 ++++++++++---- .../components/RecordShowPage.tsx | 2 +- .../components/RecordTableEffect.tsx | 28 ++++++ .../utils/filterAvailableTableColumns.ts | 5 +- .../utils/mapPaginatedObjectsToObjects.ts | 1 + .../FilterDropdownPeopleSearchSelect.tsx | 23 ----- .../people/components/PeoplePicker.tsx | 96 ------------------- .../modules/people/components/PersonChip.tsx | 6 +- .../hooks/useFilteredSearchPeopleQuery.ts | 43 --------- .../pipelineAvailableFieldDefinitions.tsx | 78 --------------- ...yV2.ts => useFilteredSearchEntityQuery.ts} | 2 +- .../components/SettingsObjectFieldPreview.tsx | 16 ++-- .../ui/display/chip/components/EntityChip.tsx | 6 +- .../SingleEntitySelect.stories.tsx | 4 +- .../relation-picker/types/EntityForSelect.ts | 5 +- .../types/EntityTypeForSelect.ts | 10 -- .../board/components/BoardColumnMenu.tsx | 25 +++-- .../ui/object/field/contexts/FieldContext.ts | 7 ++ .../ui/object/field/hooks/useIsFieldEmpty.ts | 4 +- .../__stories__/FieldContextProvider.tsx | 1 + .../display/components/ChipFieldDisplay.tsx | 4 +- .../components/DoubleTextChipFieldDisplay.tsx | 3 +- .../components/RelationFieldDisplay.tsx | 19 ++-- .../__stories__/DateFieldDisplay.stories.tsx | 1 + .../__stories__/EmailFieldDisplay.stories.tsx | 1 + .../__stories__/EnumFieldDisplay.stories.tsx | 1 + .../__stories__/MoneyFieldDisplay.stories.tsx | 1 + .../NumberFieldDisplay.stories.tsx | 1 + .../__stories__/PhoneFieldDisplay.stories.tsx | 1 + .../__stories__/TextFieldDisplay.stories.tsx | 1 + .../__stories__/URLFieldDisplay.stories.tsx | 1 + .../components/ChipDisplay.tsx | 49 ++++------ .../utils/getEntityChipFromFieldMetadata.ts | 42 -------- .../field/meta-types/hooks/useChipField.ts | 3 - .../hooks/useDoubleTextChipField.ts | 3 - .../input/components/RelationFieldInput.tsx | 24 +++-- .../__stories__/ChipFieldInput.stories.tsx | 2 - .../DoubleTextChipFieldInput.stories.tsx | 3 +- .../RelationFieldInput.stories.tsx | 2 - .../components/internal/RelationPicker.tsx} | 42 ++++---- .../isEntityFieldEmptyFamilySelector.ts | 7 +- .../ui/object/field/types/FieldDefinition.ts | 9 -- .../ui/object/field/types/FieldMetadata.ts | 16 +++- .../field/types/MainIdentifierMapper.ts | 9 ++ .../object/field/types/RelationFieldConfig.ts | 14 +++ .../components/GenericEntityFilterChip.tsx | 2 +- .../components/RecordTableCell.tsx | 10 +- .../internal/useRecordTableScopedStates.ts | 2 + .../record-table/hooks/useRecordTable.ts | 3 + .../record-table-cell/hooks/useTableCell.ts | 7 +- .../states/objectMetadataConfigScopedState.ts | 8 ++ .../types/ObjectMetadataConfig.ts | 11 +++ .../utils/getRecordTableScopedStates.ts | 7 ++ .../FilterDropdownUserSearchSelect.tsx | 9 +- .../src/modules/users/components/UserChip.tsx | 6 +- .../mapViewFieldsToBoardFieldDefinitions.ts | 3 - .../utils/mapViewFieldsToColumnDefinitions.ts | 3 - .../opportunityBoardFilterDefinitions.tsx | 7 +- 75 files changed, 412 insertions(+), 717 deletions(-) delete mode 100644 front/src/modules/companies/components/CompanyPicker.tsx delete mode 100644 front/src/modules/companies/components/FilterDropdownCompanySearchSelect.tsx delete mode 100644 front/src/modules/companies/hooks/useFilteredSearchCompanyQuery.ts create mode 100644 front/src/modules/object-metadata/hooks/useObjectMainIdentifier.ts delete mode 100644 front/src/modules/people/components/FilterDropdownPeopleSearchSelect.tsx delete mode 100644 front/src/modules/people/components/PeoplePicker.tsx delete mode 100644 front/src/modules/people/hooks/useFilteredSearchPeopleQuery.ts delete mode 100644 front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx rename front/src/modules/search/hooks/{useFilteredSearchEntityQueryV2.ts => useFilteredSearchEntityQuery.ts} (98%) delete mode 100644 front/src/modules/ui/input/relation-picker/types/EntityTypeForSelect.ts delete mode 100644 front/src/modules/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata.ts rename front/src/modules/{workspace-member/components/WorkspaceMemberPicker.tsx => ui/object/field/meta-types/input/components/internal/RelationPicker.tsx} (63%) create mode 100644 front/src/modules/ui/object/field/types/MainIdentifierMapper.ts create mode 100644 front/src/modules/ui/object/field/types/RelationFieldConfig.ts create mode 100644 front/src/modules/ui/object/record-table/states/objectMetadataConfigScopedState.ts create mode 100644 front/src/modules/ui/object/record-table/types/ObjectMetadataConfig.ts diff --git a/front/src/modules/activities/components/ActivityTargetChips.tsx b/front/src/modules/activities/components/ActivityTargetChips.tsx index 48470e31d..fd1966936 100644 --- a/front/src/modules/activities/components/ActivityTargetChips.tsx +++ b/front/src/modules/activities/components/ActivityTargetChips.tsx @@ -36,7 +36,7 @@ export const ActivityTargetChips = ({ key={company.id} id={company.id} name={company.name} - pictureUrl={getLogoUrlFromDomainName(company.domainName)} + avatarUrl={getLogoUrlFromDomainName(company.domainName)} /> ); } @@ -46,7 +46,7 @@ export const ActivityTargetChips = ({ key={person.id} id={person.id} name={person.name.firstName + ' ' + person.name.lastName} - pictureUrl={person.avatarUrl ?? undefined} + avatarUrl={person.avatarUrl ?? undefined} /> ); } diff --git a/front/src/modules/activities/editable-fields/components/ActivityRelationEditableFieldEditMode.tsx b/front/src/modules/activities/editable-fields/components/ActivityRelationEditableFieldEditMode.tsx index 48a7f6e31..b0318d218 100644 --- a/front/src/modules/activities/editable-fields/components/ActivityRelationEditableFieldEditMode.tsx +++ b/front/src/modules/activities/editable-fields/components/ActivityRelationEditableFieldEditMode.tsx @@ -4,10 +4,6 @@ import styled from '@emotion/styled'; import { useHandleCheckableActivityTargetChange } from '@/activities/hooks/useHandleCheckableActivityTargetChange'; import { Activity } from '@/activities/types/Activity'; import { ActivityTarget } from '@/activities/types/ActivityTarget'; -import { flatMapAndSortEntityForSelectArrayOfArrayByName } from '@/activities/utils/flatMapAndSortEntityForSelectArrayByName'; -import { useFilteredSearchCompanyQuery } from '@/companies/hooks/useFilteredSearchCompanyQuery'; -import { useFilteredSearchPeopleQuery } from '@/people/hooks/useFilteredSearchPeopleQuery'; -import { MultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect'; import { useInlineCell } from '@/ui/object/record-inline-cell/hooks/useInlineCell'; import { assertNotNull } from '~/utils/assert'; @@ -60,31 +56,31 @@ export const ActivityRelationEditableFieldEditMode = ({ Record >(initialSelectedEntityIds); - const personsForMultiSelect = useFilteredSearchPeopleQuery({ - searchFilter, - selectedIds: initialPeopleIds, - }); + // const personsForMultiSelect = useFilteredSearchPeopleQuery({ + // searchFilter, + // selectedIds: initialPeopleIds, + // }); - const companiesForMultiSelect = useFilteredSearchCompanyQuery({ - searchFilter, - selectedIds: initialCompanyIds, - }); + // const companiesForMultiSelect = useFilteredSearchCompanyQuery({ + // searchFilter, + // selectedIds: initialCompanyIds, + // }); - const selectedEntities = flatMapAndSortEntityForSelectArrayOfArrayByName([ - personsForMultiSelect.selectedEntities, - companiesForMultiSelect.selectedEntities, - ]); + // const selectedEntities = flatMapAndSortEntityForSelectArrayOfArrayByName([ + // personsForMultiSelect.selectedEntities, + // companiesForMultiSelect.selectedEntities, + // ]); - const filteredSelectedEntities = - flatMapAndSortEntityForSelectArrayOfArrayByName([ - personsForMultiSelect.filteredSelectedEntities, - companiesForMultiSelect.filteredSelectedEntities, - ]); + // const filteredSelectedEntities = + // flatMapAndSortEntityForSelectArrayOfArrayByName([ + // personsForMultiSelect.filteredSelectedEntities, + // companiesForMultiSelect.filteredSelectedEntities, + // ]); - const entitiesToSelect = flatMapAndSortEntityForSelectArrayOfArrayByName([ - personsForMultiSelect.entitiesToSelect, - companiesForMultiSelect.entitiesToSelect, - ]); + // const entitiesToSelect = flatMapAndSortEntityForSelectArrayOfArrayByName([ + // personsForMultiSelect.entitiesToSelect, + // companiesForMultiSelect.entitiesToSelect, + // ]); const handleCheckItemsChange = useHandleCheckableActivityTargetChange({ activity, @@ -102,7 +98,7 @@ export const ActivityRelationEditableFieldEditMode = ({ return ( - + /> */} ); }; diff --git a/front/src/modules/activities/timeline/components/TimelineActivityCardFooter.tsx b/front/src/modules/activities/timeline/components/TimelineActivityCardFooter.tsx index 1e5027348..ab26b4cab 100644 --- a/front/src/modules/activities/timeline/components/TimelineActivityCardFooter.tsx +++ b/front/src/modules/activities/timeline/components/TimelineActivityCardFooter.tsx @@ -49,7 +49,7 @@ export const TimelineActivityCardFooter = ({ ' ' + activity.assignee.name.lastName ?? '' } - pictureUrl={activity.assignee.avatarUrl ?? ''} + avatarUrl={activity.assignee.avatarUrl ?? ''} /> )} diff --git a/front/src/modules/companies/__stories__/CompanyChip.stories.tsx b/front/src/modules/companies/__stories__/CompanyChip.stories.tsx index aedd09d52..1ca09a543 100644 --- a/front/src/modules/companies/__stories__/CompanyChip.stories.tsx +++ b/front/src/modules/companies/__stories__/CompanyChip.stories.tsx @@ -17,7 +17,7 @@ export const SmallName: Story = { args: { id: 'airbnb', name: 'Airbnb', - pictureUrl: 'https://api.faviconkit.com/airbnb.com/144', + avatarUrl: 'https://api.faviconkit.com/airbnb.com/144', }, }; @@ -25,6 +25,6 @@ export const BigName: Story = { args: { id: 'google', name: 'Google with a real big name to overflow the cell', - pictureUrl: 'https://api.faviconkit.com/google.com/144', + avatarUrl: 'https://api.faviconkit.com/google.com/144', }, }; diff --git a/front/src/modules/companies/components/AddPersonToCompany.tsx b/front/src/modules/companies/components/AddPersonToCompany.tsx index 2d830cf44..33b344e61 100644 --- a/front/src/modules/companies/components/AddPersonToCompany.tsx +++ b/front/src/modules/companies/components/AddPersonToCompany.tsx @@ -3,10 +3,6 @@ import styled from '@emotion/styled'; import { flip, offset, useFloating } from '@floating-ui/react'; import { v4 } from 'uuid'; -import { - PeoplePicker, - PersonForSelect, -} from '@/people/components/PeoplePicker'; import { IconPlus } from '@/ui/display/icon'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; @@ -67,7 +63,7 @@ export const AddPersonToCompany = ({ } = usePreviousHotkeyScope(); const handlePersonSelected = - (companyId: string) => async (newPerson: PersonForSelect | null) => { + (companyId: string) => async (newPerson: any | null) => { if (newPerson) { // await updatePerson({ // variables: { @@ -146,13 +142,14 @@ export const AddPersonToCompany = ({ /> ) : ( - setIsCreationDropdownOpen(true)} - excludePersonIds={peopleIds} - /> + <>todo + // setIsCreationDropdownOpen(true)} + // excludePersonIds={peopleIds} + // /> )} )} diff --git a/front/src/modules/companies/components/CompanyBoardCard.tsx b/front/src/modules/companies/components/CompanyBoardCard.tsx index 62d4e5959..95a0e911b 100644 --- a/front/src/modules/companies/components/CompanyBoardCard.tsx +++ b/front/src/modules/companies/components/CompanyBoardCard.tsx @@ -208,7 +208,7 @@ export const CompanyBoardCard = () => { {showCompactView && ( @@ -239,14 +239,13 @@ export const CompanyBoardCard = () => { value={{ entityId: boardCardId, recoilScopeId: boardCardId + viewField.fieldMetadataId, + isMainIdentifier: false, fieldDefinition: { fieldMetadataId: viewField.fieldMetadataId, label: viewField.label, iconName: viewField.iconName, type: viewField.type, metadata: viewField.metadata, - entityChipDisplayMapper: - viewField.entityChipDisplayMapper, }, useUpdateEntityMutation: useUpdateOneObjectMutation, hotkeyScope: InlineCellHotkeyScope.InlineCell, diff --git a/front/src/modules/companies/components/CompanyChip.tsx b/front/src/modules/companies/components/CompanyChip.tsx index c6d4a2dfb..60a8c5519 100644 --- a/front/src/modules/companies/components/CompanyChip.tsx +++ b/front/src/modules/companies/components/CompanyChip.tsx @@ -6,14 +6,14 @@ import { type CompanyChipProps = { id: string; name: string; - pictureUrl?: string; + avatarUrl?: string; variant?: EntityChipVariant; }; export const CompanyChip = ({ id, name, - pictureUrl, + avatarUrl, variant = EntityChipVariant.Regular, }: CompanyChipProps) => ( ); diff --git a/front/src/modules/companies/components/CompanyPicker.tsx b/front/src/modules/companies/components/CompanyPicker.tsx deleted file mode 100644 index 02e6b928a..000000000 --- a/front/src/modules/companies/components/CompanyPicker.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { useEffect } from 'react'; -import { useQuery } from '@apollo/client'; - -import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem'; -import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2'; -import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect'; -import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState'; -import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; -import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; -import { getLogoUrlFromDomainName } from '~/utils'; - -export type CompanyPickerProps = { - companyId: string | null; - onSubmit: (newCompanyId: EntityForSelect | null) => void; - onCancel?: () => void; - initialSearchFilter?: string | null; -}; - -export const CompanyPicker = ({ - companyId, - onSubmit, - onCancel, - initialSearchFilter, -}: CompanyPickerProps) => { - const [relationPickerSearchFilter, setRelationPickerSearchFilter] = - useRecoilScopedState(relationPickerSearchFilterScopedState); - - useEffect(() => { - if (initialSearchFilter) { - setRelationPickerSearchFilter(initialSearchFilter); - } - }, [initialSearchFilter, setRelationPickerSearchFilter]); - - const { findManyQuery } = useFindOneObjectMetadataItem({ - objectNamePlural: 'companies', - }); - - const useFindManyCompanies = (options: any) => - useQuery(findManyQuery, options); - - const companies = useFilteredSearchEntityQueryV2({ - queryHook: useFindManyCompanies, - filters: [ - { - fieldNames: ['name'], - filter: relationPickerSearchFilter, - }, - ], - orderByField: 'name', - mappingFunction: (company) => ({ - entityType: Entity.Company, - id: company.id, - name: company.name, - avatarType: 'squared', - avatarUrl: getLogoUrlFromDomainName(company.domainName), - originalEntity: company, - }), - selectedIds: companyId ? [companyId] : [], - objectNamePlural: 'companies', - }); - - const handleEntitySelected = async ( - selectedCompany: EntityForSelect | null | undefined, - ) => { - onSubmit(selectedCompany ?? null); - }; - - return ( - - ); -}; diff --git a/front/src/modules/companies/components/CompanyProgressPicker.tsx b/front/src/modules/companies/components/CompanyProgressPicker.tsx index ba0f1c769..bf70bc294 100644 --- a/front/src/modules/companies/components/CompanyProgressPicker.tsx +++ b/front/src/modules/companies/components/CompanyProgressPicker.tsx @@ -3,7 +3,6 @@ import { useRecoilState } from 'recoil'; import { currentPipelineState } from '@/pipeline/states/currentPipelineState'; import { IconChevronDown } from '@/ui/display/icon'; -import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase'; import { useEntitySelectSearch } from '@/ui/input/relation-picker/hooks/useEntitySelectSearch'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; @@ -14,8 +13,6 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; -import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery'; - export type CompanyProgressPickerProps = { companyId: string | null; onSubmit: ( @@ -34,10 +31,10 @@ export const CompanyProgressPicker = ({ const { searchFilter, handleSearchFilterChange } = useEntitySelectSearch(); - const companies = useFilteredSearchCompanyQuery({ - searchFilter, - selectedIds: companyId ? [companyId] : [], - }); + // const companies = useFilteredSearchCompanyQuery({ + // searchFilter, + // selectedIds: companyId ? [companyId] : [], + // }); const [isProgressSelectionUnfolded, setIsProgressSelectionUnfolded] = useState(false); @@ -113,13 +110,13 @@ export const CompanyProgressPicker = ({ /> - + /> */} )} diff --git a/front/src/modules/companies/components/FilterDropdownCompanySearchSelect.tsx b/front/src/modules/companies/components/FilterDropdownCompanySearchSelect.tsx deleted file mode 100644 index 6d45de671..000000000 --- a/front/src/modules/companies/components/FilterDropdownCompanySearchSelect.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { ObjectFilterDropdownEntitySearchSelect } from '@/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect'; -import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter'; - -import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery'; - -export const FilterDropdownCompanySearchSelect = () => { - const { - objectFilterDropdownSearchInput, - objectFilterDropdownSelectedEntityId, - } = useFilter(); - - const usersForSelect = useFilteredSearchCompanyQuery({ - searchFilter: objectFilterDropdownSearchInput, - selectedIds: objectFilterDropdownSelectedEntityId - ? [objectFilterDropdownSelectedEntityId] - : [], - }); - - return ( - - ); -}; diff --git a/front/src/modules/companies/components/HooksCompanyBoardEffect.tsx b/front/src/modules/companies/components/HooksCompanyBoardEffect.tsx index 572d5c518..f9ce3959b 100644 --- a/front/src/modules/companies/components/HooksCompanyBoardEffect.tsx +++ b/front/src/modules/companies/components/HooksCompanyBoardEffect.tsx @@ -5,7 +5,6 @@ import { useRecoilState, useRecoilValue } from 'recoil'; import { Company } from '@/companies/types/Company'; import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords'; import { PaginatedObjectTypeResults } from '@/object-record/types/PaginatedObjectTypeResults'; -import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions'; import { Opportunity } from '@/pipeline/types/Opportunity'; import { PipelineStep } from '@/pipeline/types/PipelineStep'; import { useBoardActionBarEntries } from '@/ui/layout/board/hooks/useBoardActionBarEntries'; @@ -117,7 +116,7 @@ export const HooksCompanyBoardEffect = () => { useEffect(() => { setAvailableFilterDefinitions(opportunitiesBoardOptions.filterDefinitions); setAvailableSortDefinitions?.(opportunitiesBoardOptions.sortDefinitions); - setAvailableFieldDefinitions?.(pipelineAvailableFieldDefinitions); + setAvailableFieldDefinitions?.([]); }, [ setAvailableFieldDefinitions, setAvailableFilterDefinitions, @@ -140,7 +139,7 @@ export const HooksCompanyBoardEffect = () => { if (!loading && opportunities && companies) { setActionBarEntries(); setContextMenuEntries(); - setAvailableBoardCardFields(pipelineAvailableFieldDefinitions); + setAvailableBoardCardFields([]); updateCompanyBoard(pipelineSteps, opportunities, companies); setEntityCountInCurrentView(companies.length); } @@ -160,10 +159,7 @@ export const HooksCompanyBoardEffect = () => { useEffect(() => { if (currentViewFields) { setBoardCardFields( - mapViewFieldsToBoardFieldDefinitions( - currentViewFields, - pipelineAvailableFieldDefinitions, - ), + mapViewFieldsToBoardFieldDefinitions(currentViewFields, []), ); } }, [currentViewFields, setBoardCardFields]); diff --git a/front/src/modules/companies/components/NewCompanyProgressButton.tsx b/front/src/modules/companies/components/NewCompanyProgressButton.tsx index de66283f0..2f3e386f1 100644 --- a/front/src/modules/companies/components/NewCompanyProgressButton.tsx +++ b/front/src/modules/companies/components/NewCompanyProgressButton.tsx @@ -1,7 +1,6 @@ import { useCallback, useContext, useState } from 'react'; import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar'; -import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect'; import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; import { NewButton } from '@/ui/layout/board/components/NewButton'; @@ -9,8 +8,6 @@ import { BoardColumnContext } from '@/ui/layout/board/contexts/BoardColumnContex import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; -import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery'; - export const NewCompanyProgressButton = () => { const [isCreatingCard, setIsCreatingCard] = useState(false); const column = useContext(BoardColumnContext); @@ -55,22 +52,23 @@ export const NewCompanyProgressButton = () => { relationPickerSearchFilterScopedState, ); - const companies = useFilteredSearchCompanyQuery({ - searchFilter: relationPickerSearchFilter, - }); + // const companies = useFilteredSearchCompanyQuery({ + // searchFilter: relationPickerSearchFilter, + // }); return ( <> {isCreatingCard ? ( - + <>TODO ) : ( + // )} diff --git a/front/src/modules/companies/hooks/useFilteredSearchCompanyQuery.ts b/front/src/modules/companies/hooks/useFilteredSearchCompanyQuery.ts deleted file mode 100644 index 313069c26..000000000 --- a/front/src/modules/companies/hooks/useFilteredSearchCompanyQuery.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { useQuery } from '@apollo/client'; - -import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem'; -import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; - -export const useFilteredSearchCompanyQuery = ({ - searchFilter, - selectedIds = [], - limit, -}: { - searchFilter: string; - selectedIds?: string[]; - limit?: number; -}) => { - const { findManyQuery } = useFindOneObjectMetadataItem({ - objectNameSingular: 'company', - }); - - const useFindManyCompanies = (options: any) => - useQuery(findManyQuery, options); - - return useFilteredSearchEntityQueryV2({ - queryHook: useFindManyCompanies, - filters: [ - { - fieldNames: ['name.firstName', 'name.lastName'], - filter: searchFilter, - }, - ], - orderByField: 'createdAt', - mappingFunction: (company) => ({ - entityType: Entity.Company, - id: company.id, - name: company.name, - avatarType: 'squared', - avatarUrl: '', - originalEntity: company, - }), - selectedIds: selectedIds, - objectNamePlural: 'workspaceMembers', - limit, - }); -}; diff --git a/front/src/modules/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata.ts b/front/src/modules/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata.ts index 35c32aefa..ee3ce25c1 100644 --- a/front/src/modules/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata.ts +++ b/front/src/modules/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata.ts @@ -26,7 +26,6 @@ export const useComputeDefinitionsFromFieldMetadata = ( formatFieldMetadataItemAsColumnDefinition({ position: index, field, - objectMetadataItem: objectMetadataItem, }), ); diff --git a/front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts b/front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts index b97ae4d42..41e143e4e 100644 --- a/front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts +++ b/front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts @@ -8,7 +8,7 @@ export const useMapFieldMetadataToGraphQLQuery = () => { const mapFieldMetadataToGraphQLQuery = ( field: FieldMetadataItem, - maxDepthForRelations: number = 1, + maxDepthForRelations: number = 2, ): any => { if (maxDepthForRelations <= 0) { return ''; diff --git a/front/src/modules/object-metadata/hooks/useObjectMainIdentifier.ts b/front/src/modules/object-metadata/hooks/useObjectMainIdentifier.ts new file mode 100644 index 000000000..50e48b1f8 --- /dev/null +++ b/front/src/modules/object-metadata/hooks/useObjectMainIdentifier.ts @@ -0,0 +1,56 @@ +import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { MainIdentifierMapper } from '@/ui/object/field/types/MainIdentifierMapper'; + +export const useObjectMainIdentifier = ( + objectMetadataItem?: ObjectMetadataItem, +) => { + if (!objectMetadataItem) { + return { + mainIdentifierMapper: undefined, + mainIdentifierFieldMetadataId: undefined, + basePathToShowPage: undefined, + }; + } + + const mainIdentifierMapper: MainIdentifierMapper = (record: any) => { + if (objectMetadataItem.nameSingular === 'company') { + return { + id: record.id, + name: record.name, + avatarUrl: record.avatarUrl, + avatarType: 'squared', + record: record, + }; + } + + if (objectMetadataItem.nameSingular === 'workspaceMember') { + return { + id: record.id, + name: record.name.firstName + ' ' + record.name.lastName, + avatarUrl: record.avatarUrl, + avatarType: 'rounded', + record: record, + }; + } + + return { + id: record.id, + name: record.name, + avatarUrl: record.avatarUrl, + avatarType: 'rounded', + record: record, + }; + }; + + const mainIdentifierFieldMetadataId = objectMetadataItem.fields.find( + ({ name }) => name === 'name', + )?.id; + + const basePathToShowPage = `/object/${objectMetadataItem.nameSingular}/`; + + return { + mainIdentifierMapper, + mainIdentifierFieldMetadataId, + basePathToShowPage, + }; +}; diff --git a/front/src/modules/object-metadata/types/FieldMetadataItem.ts b/front/src/modules/object-metadata/types/FieldMetadataItem.ts index ac10c17af..978d2dcd9 100644 --- a/front/src/modules/object-metadata/types/FieldMetadataItem.ts +++ b/front/src/modules/object-metadata/types/FieldMetadataItem.ts @@ -6,12 +6,18 @@ export type FieldMetadataItem = Omit< > & { fromRelationMetadata?: | (Pick & { - toObjectMetadata: Pick; + toObjectMetadata: Pick< + Relation['toObjectMetadata'], + 'id' | 'nameSingular' | 'namePlural' + >; }) | null; toRelationMetadata?: | (Pick & { - fromObjectMetadata: Pick; + fromObjectMetadata: Pick< + Relation['fromObjectMetadata'], + 'id' | 'nameSingular' | 'namePlural' + >; }) | null; }; diff --git a/front/src/modules/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition.ts b/front/src/modules/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition.ts index 3894054e4..a7d18f2ae 100644 --- a/front/src/modules/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition.ts +++ b/front/src/modules/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition.ts @@ -1,32 +1,68 @@ import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType'; import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata'; +import { MainIdentifierMapper } from '@/ui/object/field/types/MainIdentifierMapper'; import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition'; +import { getLogoUrlFromDomainName } from '~/utils'; import { FieldMetadataItem } from '../types/FieldMetadataItem'; -import { ObjectMetadataItem } from '../types/ObjectMetadataItem'; import { parseFieldType } from './parseFieldType'; export const formatFieldMetadataItemAsColumnDefinition = ({ position, field, - objectMetadataItem, }: { position: number; field: FieldMetadataItem; - objectMetadataItem: Omit; -}): ColumnDefinition => ({ - position, - fieldMetadataId: field.id, - label: field.label, - size: 100, - type: parseFieldType(field.type), - metadata: { - fieldName: field.name, - placeHolder: field.label, - }, - iconName: field.icon ?? 'Icon123', - isVisible: true, - basePathToShowPage: `/object/${objectMetadataItem.nameSingular}/`, - relationType: parseFieldRelationType(field), -}); +}): ColumnDefinition => { + const relationObjectMetadataItem = + field.toRelationMetadata?.fromObjectMetadata; + + const mainIdentifierMapper: MainIdentifierMapper = (record: any) => { + if (relationObjectMetadataItem?.nameSingular === 'company') { + return { + id: record.id, + name: record.name, + avatarUrl: getLogoUrlFromDomainName(record.domainName), + avatarType: 'squared', + record: record, + }; + } + if (relationObjectMetadataItem?.nameSingular === 'workspaceMember') { + return { + id: record.id, + name: record.name.firstName + ' ' + record.name.lastName, + avatarUrl: record.avatarUrl, + avatarType: 'rounded', + record: record, + }; + } + + return { + id: record.id, + name: record.name, + avatarUrl: record.avatarUrl, + avatarType: 'rounded', + record: record, + }; + }; + + return { + position, + fieldMetadataId: field.id, + label: field.label, + size: 100, + type: parseFieldType(field.type), + metadata: { + fieldName: field.name, + placeHolder: field.label, + mainIdentifierMapper: mainIdentifierMapper, + relationType: parseFieldRelationType(field), + searchFields: ['name'], + objectMetadataNamePlural: relationObjectMetadataItem?.namePlural, + objectMetadataNameSingular: relationObjectMetadataItem?.nameSingular, + }, + iconName: field.icon ?? 'Icon123', + isVisible: true, + }; +}; diff --git a/front/src/modules/object-record/components/RecordShowPage.tsx b/front/src/modules/object-record/components/RecordShowPage.tsx index 020418fea..2c5c94da5 100644 --- a/front/src/modules/object-record/components/RecordShowPage.tsx +++ b/front/src/modules/object-record/components/RecordShowPage.tsx @@ -165,11 +165,11 @@ export const RecordShowPage = () => { value={{ entityId: object.id, recoilScopeId: object.id + metadataField.id, + isMainIdentifier: false, fieldDefinition: formatFieldMetadataItemAsColumnDefinition({ field: metadataField, position: index, - objectMetadataItem: foundObjectMetadataItem, }), useUpdateEntityMutation: useUpdateOneObjectMutation, hotkeyScope: InlineCellHotkeyScope.InlineCell, diff --git a/front/src/modules/object-record/components/RecordTableEffect.tsx b/front/src/modules/object-record/components/RecordTableEffect.tsx index fb7ab109b..c6c779e9d 100644 --- a/front/src/modules/object-record/components/RecordTableEffect.tsx +++ b/front/src/modules/object-record/components/RecordTableEffect.tsx @@ -2,6 +2,7 @@ import { useEffect } from 'react'; import { useComputeDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata'; import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem'; +import { useObjectMainIdentifier } from '@/object-metadata/hooks/useObjectMainIdentifier'; import { useRecordTableContextMenuEntries } from '@/object-record/hooks/useRecordTableContextMenuEntries'; import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns'; import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable'; @@ -13,12 +14,19 @@ export const RecordTableEffect = () => { scopeId: objectNamePlural, setAvailableTableColumns, setOnEntityCountChange, + setObjectMetadataConfig, } = useRecordTable(); const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({ objectNamePlural, }); + const { + mainIdentifierMapper, + basePathToShowPage, + mainIdentifierFieldMetadataId, + } = useObjectMainIdentifier(foundObjectMetadataItem); + const { columnDefinitions, filterDefinitions, sortDefinitions } = useComputeDefinitionsFromFieldMetadata(foundObjectMetadataItem); @@ -31,6 +39,26 @@ export const RecordTableEffect = () => { setEntityCountInCurrentView, } = useView(); + useEffect(() => { + if ( + mainIdentifierMapper && + basePathToShowPage && + mainIdentifierFieldMetadataId + ) { + setObjectMetadataConfig?.({ + mainIdentifierMapper, + basePathToShowPage, + mainIdentifierFieldMetadataId, + }); + } + }, [ + basePathToShowPage, + foundObjectMetadataItem, + mainIdentifierFieldMetadataId, + mainIdentifierMapper, + setObjectMetadataConfig, + ]); + useEffect(() => { if (!foundObjectMetadataItem) { return; diff --git a/front/src/modules/object-record/utils/filterAvailableTableColumns.ts b/front/src/modules/object-record/utils/filterAvailableTableColumns.ts index 008673f19..a082bcb76 100644 --- a/front/src/modules/object-record/utils/filterAvailableTableColumns.ts +++ b/front/src/modules/object-record/utils/filterAvailableTableColumns.ts @@ -1,12 +1,13 @@ import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata'; +import { isFieldRelation } from '@/ui/object/field/types/guards/isFieldRelation'; import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition'; export const filterAvailableTableColumns = ( columnDefinition: ColumnDefinition, ): boolean => { if ( - columnDefinition.type === 'RELATION' && - columnDefinition.relationType !== 'TO_ONE_OBJECT' + isFieldRelation(columnDefinition) && + columnDefinition.metadata?.relationType !== 'TO_ONE_OBJECT' ) { return false; } diff --git a/front/src/modules/object-record/utils/mapPaginatedObjectsToObjects.ts b/front/src/modules/object-record/utils/mapPaginatedObjectsToObjects.ts index 7ba7d8f53..f29669678 100644 --- a/front/src/modules/object-record/utils/mapPaginatedObjectsToObjects.ts +++ b/front/src/modules/object-record/utils/mapPaginatedObjectsToObjects.ts @@ -15,6 +15,7 @@ export const mapPaginatedObjectsToObjects = < pagedObjects: ObjectTypeQuery | undefined; objectNamePlural: string; }) => { + console.log(objectNamePlural); const formattedObjects: ObjectType[] = pagedObjects?.[objectNamePlural].edges.map((objectEdge: ObjectEdge) => ({ ...objectEdge.node, diff --git a/front/src/modules/people/components/FilterDropdownPeopleSearchSelect.tsx b/front/src/modules/people/components/FilterDropdownPeopleSearchSelect.tsx deleted file mode 100644 index f8db13285..000000000 --- a/front/src/modules/people/components/FilterDropdownPeopleSearchSelect.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useFilteredSearchPeopleQuery } from '@/people/hooks/useFilteredSearchPeopleQuery'; -import { ObjectFilterDropdownEntitySearchSelect } from '@/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect'; -import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter'; - -export const FilterDropdownPeopleSearchSelect = () => { - const { - objectFilterDropdownSearchInput, - objectFilterDropdownSelectedEntityId, - } = useFilter(); - - const peopleForSelect = useFilteredSearchPeopleQuery({ - searchFilter: objectFilterDropdownSearchInput, - selectedIds: objectFilterDropdownSelectedEntityId - ? [objectFilterDropdownSelectedEntityId] - : [], - }); - - return ( - - ); -}; diff --git a/front/src/modules/people/components/PeoplePicker.tsx b/front/src/modules/people/components/PeoplePicker.tsx deleted file mode 100644 index 9c8ce3c43..000000000 --- a/front/src/modules/people/components/PeoplePicker.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { useEffect } from 'react'; -import { useQuery } from '@apollo/client'; - -import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem'; -import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2'; -import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect'; -import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState'; -import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; -import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; - -export type PeoplePickerProps = { - personId: string | null; - companyId?: string; - onSubmit: (newPersonId: PersonForSelect | null) => void; - onCancel?: () => void; - onCreate?: () => void; - excludePersonIds?: string[]; - initialSearchFilter?: string | null; -}; - -export type PersonForSelect = EntityForSelect & { - entityType: Entity.Person; -}; - -export const PeoplePicker = ({ - personId, - companyId, - onSubmit, - onCancel, - onCreate, - excludePersonIds, - initialSearchFilter, -}: PeoplePickerProps) => { - const [relationPickerSearchFilter, setRelationPickerSearchFilter] = - useRecoilScopedState(relationPickerSearchFilterScopedState); - - useEffect(() => { - setRelationPickerSearchFilter(initialSearchFilter ?? ''); - }, [initialSearchFilter, setRelationPickerSearchFilter]); - - const queryFilters = [ - { - fieldNames: ['name.firstName', 'name.lastName'], - filter: relationPickerSearchFilter, - }, - ]; - - if (companyId) { - queryFilters.push({ - fieldNames: ['companyId'], - filter: companyId, - }); - } - - const { findManyQuery } = useFindOneObjectMetadataItem({ - objectNameSingular: 'person', - }); - - const useFindManyPeople = (options: any) => useQuery(findManyQuery, options); - - const people = useFilteredSearchEntityQueryV2({ - queryHook: useFindManyPeople, - filters: queryFilters, - orderByField: 'createdAt', - mappingFunction: (workspaceMember) => ({ - entityType: Entity.WorkspaceMember, - id: workspaceMember.id, - name: - workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName, - avatarType: 'rounded', - avatarUrl: '', - originalEntity: workspaceMember, - }), - selectedIds: [personId ?? ''], - excludeEntityIds: excludePersonIds, - objectNamePlural: 'people', - }); - - const handleEntitySelected = async ( - selectedPerson: any | null | undefined, - ) => { - onSubmit(selectedPerson ?? null); - }; - - return ( - - ); -}; diff --git a/front/src/modules/people/components/PersonChip.tsx b/front/src/modules/people/components/PersonChip.tsx index 5879ee96f..4edfb305c 100644 --- a/front/src/modules/people/components/PersonChip.tsx +++ b/front/src/modules/people/components/PersonChip.tsx @@ -6,14 +6,14 @@ import { export type PersonChipProps = { id: string; name: string; - pictureUrl?: string; + avatarUrl?: string; variant?: EntityChipVariant; }; export const PersonChip = ({ id, name, - pictureUrl, + avatarUrl, variant, }: PersonChipProps) => ( ); diff --git a/front/src/modules/people/hooks/useFilteredSearchPeopleQuery.ts b/front/src/modules/people/hooks/useFilteredSearchPeopleQuery.ts deleted file mode 100644 index 92584b505..000000000 --- a/front/src/modules/people/hooks/useFilteredSearchPeopleQuery.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { useQuery } from '@apollo/client'; - -import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem'; -import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; - -export const useFilteredSearchPeopleQuery = ({ - searchFilter, - selectedIds = [], - limit, -}: { - searchFilter: string; - selectedIds?: string[]; - limit?: number; -}) => { - const { findManyQuery } = useFindOneObjectMetadataItem({ - objectNameSingular: 'person', - }); - - const useFindManyPeople = (options: any) => useQuery(findManyQuery, options); - - return useFilteredSearchEntityQueryV2({ - queryHook: useFindManyPeople, - filters: [ - { - fieldNames: ['name.firstName', 'name.lastName'], - filter: searchFilter, - }, - ], - orderByField: 'createdAt', - mappingFunction: (person) => ({ - entityType: Entity.Person, - id: person.id, - name: person.name.firstName + ' ' + person.name.lastName, - avatarType: 'rounded', - avatarUrl: '', - originalEntity: person, - }), - selectedIds: selectedIds, - objectNamePlural: 'workspaceMembers', - limit, - }); -}; diff --git a/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx b/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx deleted file mode 100644 index 6a90a5111..000000000 --- a/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { Person } from '@/people/types/Person'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; -import { - FieldDateMetadata, - FieldMetadata, - FieldNumberMetadata, - FieldProbabilityMetadata, - FieldRelationMetadata, -} from '@/ui/object/field/types/FieldMetadata'; -import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition'; - -export const pipelineAvailableFieldDefinitions: ColumnDefinition[] = - [ - { - fieldMetadataId: 'closeDate', - label: 'Close Date', - iconName: 'IconCalendarEvent', - position: 0, - type: 'DATE', - metadata: { - fieldName: 'closeDate', - }, - size: 0, - isVisible: true, - infoTooltipContent: - 'Specified date by which an opportunity must be completed.', - } satisfies ColumnDefinition, - { - fieldMetadataId: 'amount', - label: 'Amount', - iconName: 'IconCurrencyDollar', - position: 1, - type: 'NUMBER', - metadata: { - fieldName: 'amount', - placeHolder: '0', - }, - size: 0, - isVisible: true, - infoTooltipContent: 'Potential monetary value of a business opportunity.', - } satisfies ColumnDefinition, - { - fieldMetadataId: 'probability', - label: 'Probability', - iconName: 'IconProgressCheck', - position: 2, - type: 'PROBABILITY', - metadata: { - fieldName: 'probability', - }, - size: 0, - isVisible: true, - infoTooltipContent: - "Level of certainty in the lead's potential to convert into a success.", - } satisfies ColumnDefinition, - { - fieldMetadataId: 'pointOfContact', - label: 'Point of Contact', - iconName: 'IconUser', - position: 3, - type: 'RELATION', - metadata: { - fieldName: 'pointOfContact', - relationType: Entity.Person, - useEditButton: true, - }, - size: 0, - isVisible: true, - infoTooltipContent: 'Primary contact within the company.', - entityChipDisplayMapper: (dataObject: Person) => { - return { - name: dataObject?.name.firstName + ' ' + dataObject?.name.lastName, - pictureUrl: dataObject?.avatarUrl ?? undefined, - avatarType: 'rounded', - }; - }, - } satisfies ColumnDefinition, - ]; diff --git a/front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts b/front/src/modules/search/hooks/useFilteredSearchEntityQuery.ts similarity index 98% rename from front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts rename to front/src/modules/search/hooks/useFilteredSearchEntityQuery.ts index cdeffeca1..e01ea17f4 100644 --- a/front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts +++ b/front/src/modules/search/hooks/useFilteredSearchEntityQuery.ts @@ -18,7 +18,7 @@ const DEFAULT_SEARCH_REQUEST_LIMIT = 30; // TODO: use this for all search queries, because we need selectedEntities and entitiesToSelect each time we want to search // Filtered entities to select are -export const useFilteredSearchEntityQueryV2 = ({ +export const useFilteredSearchEntityQuery = ({ queryHook, orderByField, filters, diff --git a/front/src/modules/settings/data-model/components/SettingsObjectFieldPreview.tsx b/front/src/modules/settings/data-model/components/SettingsObjectFieldPreview.tsx index 6e5b302d4..eaa31bbfb 100644 --- a/front/src/modules/settings/data-model/components/SettingsObjectFieldPreview.tsx +++ b/front/src/modules/settings/data-model/components/SettingsObjectFieldPreview.tsx @@ -90,12 +90,11 @@ export const SettingsObjectFieldPreview = ({ objectMetadataId, }); - const { defaultValue: relationDefaultValue, entityChipDisplayMapper } = - useRelationFieldPreview({ - relationObjectMetadataId, - skipDefaultValue: - fieldMetadata.type !== FieldMetadataType.Relation || hasValue, - }); + const { defaultValue: relationDefaultValue } = useRelationFieldPreview({ + relationObjectMetadataId, + skipDefaultValue: + fieldMetadata.type !== FieldMetadataType.Relation || hasValue, + }); const defaultValue = fieldMetadata.type === FieldMetadataType.Relation @@ -138,16 +137,13 @@ export const SettingsObjectFieldPreview = ({ ) : ( ((person) => ({ id: person.id, - entityType: Entity.Person, name: person.name.firstName + ' ' + person.name.lastName, - originalEntity: person, + record: person, })); const meta: Meta = { diff --git a/front/src/modules/ui/input/relation-picker/types/EntityForSelect.ts b/front/src/modules/ui/input/relation-picker/types/EntityForSelect.ts index 87dad1d9d..4737e6b8c 100644 --- a/front/src/modules/ui/input/relation-picker/types/EntityForSelect.ts +++ b/front/src/modules/ui/input/relation-picker/types/EntityForSelect.ts @@ -1,12 +1,9 @@ import { AvatarType } from '@/users/components/Avatar'; -import { EntityTypeForSelect } from './EntityTypeForSelect'; - export type EntityForSelect = { id: string; - entityType: EntityTypeForSelect; name: string; avatarUrl?: string; avatarType?: AvatarType; - originalEntity: any; + record: any; }; diff --git a/front/src/modules/ui/input/relation-picker/types/EntityTypeForSelect.ts b/front/src/modules/ui/input/relation-picker/types/EntityTypeForSelect.ts deleted file mode 100644 index f0cefc03f..000000000 --- a/front/src/modules/ui/input/relation-picker/types/EntityTypeForSelect.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity'; - -export enum Entity { - Company = 'Company', - Person = 'Person', - User = 'User', - WorkspaceMember = 'WorkspaceMember', -} - -export type EntityTypeForSelect = ActivityTargetableEntityType | Entity; diff --git a/front/src/modules/ui/layout/board/components/BoardColumnMenu.tsx b/front/src/modules/ui/layout/board/components/BoardColumnMenu.tsx index 741391b4a..04d5de599 100644 --- a/front/src/modules/ui/layout/board/components/BoardColumnMenu.tsx +++ b/front/src/modules/ui/layout/board/components/BoardColumnMenu.tsx @@ -2,7 +2,6 @@ import { useCallback, useContext, useRef, useState } from 'react'; import styled from '@emotion/styled'; import { Key } from 'ts-key-enum'; -import { useFilteredSearchCompanyQuery } from '@/companies/hooks/useFilteredSearchCompanyQuery'; import { IconArrowLeft, IconArrowRight, @@ -10,7 +9,6 @@ import { IconPlus, } from '@/ui/display/icon'; import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar'; -import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect'; import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; @@ -98,9 +96,9 @@ export const BoardColumnMenu = ({ const [relationPickerSearchFilter] = useRecoilScopedState( relationPickerSearchFilterScopedState, ); - const companies = useFilteredSearchCompanyQuery({ - searchFilter: relationPickerSearchFilter, - }); + // const companies = useFilteredSearchCompanyQuery({ + // searchFilter: relationPickerSearchFilter, + // }); useListenClickOutside({ refs: [boardColumnMenuRef], @@ -172,14 +170,15 @@ export const BoardColumnMenu = ({ /> )} {currentMenu === 'add' && ( - +
add
+ // )} diff --git a/front/src/modules/ui/object/field/contexts/FieldContext.ts b/front/src/modules/ui/object/field/contexts/FieldContext.ts index fbab866ab..611d27ffa 100644 --- a/front/src/modules/ui/object/field/contexts/FieldContext.ts +++ b/front/src/modules/ui/object/field/contexts/FieldContext.ts @@ -10,6 +10,13 @@ export type GenericFieldContextType = { entityId: string; recoilScopeId?: string; hotkeyScope: string; + isMainIdentifier: boolean; + mainIdentifierMapper?: (record: any) => { + name: string; + avatarUrl?: string; + avatarType: string; + }; + basePathToShowPage?: string; }; export const FieldContext = createContext( diff --git a/front/src/modules/ui/object/field/hooks/useIsFieldEmpty.ts b/front/src/modules/ui/object/field/hooks/useIsFieldEmpty.ts index b7af6865a..2d80c6abe 100644 --- a/front/src/modules/ui/object/field/hooks/useIsFieldEmpty.ts +++ b/front/src/modules/ui/object/field/hooks/useIsFieldEmpty.ts @@ -10,10 +10,8 @@ export const useIsFieldEmpty = () => { const isFieldEmpty = useRecoilValue( isEntityFieldEmptyFamilySelector({ fieldDefinition: { - fieldMetadataId: fieldDefinition.fieldMetadataId, - label: fieldDefinition.label, type: fieldDefinition.type, - metadata: fieldDefinition.metadata, + metadata: { ...fieldDefinition.metadata, mainIdentifierMapper: null }, }, entityId, }), diff --git a/front/src/modules/ui/object/field/meta-types/__stories__/FieldContextProvider.tsx b/front/src/modules/ui/object/field/meta-types/__stories__/FieldContextProvider.tsx index a2d4bbdd6..9c642e454 100644 --- a/front/src/modules/ui/object/field/meta-types/__stories__/FieldContextProvider.tsx +++ b/front/src/modules/ui/object/field/meta-types/__stories__/FieldContextProvider.tsx @@ -18,6 +18,7 @@ export const FieldContextProvider = ({ { - const { avatarFieldValue, contentFieldValue, entityType, entityId } = - useChipField(); + const { avatarFieldValue, contentFieldValue, entityId } = useChipField(); return ( ); diff --git a/front/src/modules/ui/object/field/meta-types/display/components/DoubleTextChipFieldDisplay.tsx b/front/src/modules/ui/object/field/meta-types/display/components/DoubleTextChipFieldDisplay.tsx index 653a19e12..03c4d2e84 100644 --- a/front/src/modules/ui/object/field/meta-types/display/components/DoubleTextChipFieldDisplay.tsx +++ b/front/src/modules/ui/object/field/meta-types/display/components/DoubleTextChipFieldDisplay.tsx @@ -2,7 +2,7 @@ import { useDoubleTextChipField } from '../../hooks/useDoubleTextChipField'; import { ChipDisplay } from '../content-display/components/ChipDisplay'; export const DoubleTextChipFieldDisplay = () => { - const { avatarUrl, firstValue, secondValue, entityType, entityId } = + const { avatarUrl, firstValue, secondValue, entityId } = useDoubleTextChipField(); const content = [firstValue, secondValue].filter(Boolean).join(' '); @@ -11,7 +11,6 @@ export const DoubleTextChipFieldDisplay = () => { ); diff --git a/front/src/modules/ui/object/field/meta-types/display/components/RelationFieldDisplay.tsx b/front/src/modules/ui/object/field/meta-types/display/components/RelationFieldDisplay.tsx index b04c82f45..092fe0a2a 100644 --- a/front/src/modules/ui/object/field/meta-types/display/components/RelationFieldDisplay.tsx +++ b/front/src/modules/ui/object/field/meta-types/display/components/RelationFieldDisplay.tsx @@ -1,22 +1,23 @@ import { EntityChip } from '@/ui/display/chip/components/EntityChip'; -import { getEntityChipFromFieldMetadata } from '@/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata'; import { useRelationField } from '../../hooks/useRelationField'; export const RelationFieldDisplay = () => { const { fieldValue, fieldDefinition } = useRelationField(); - const entityChipProps = getEntityChipFromFieldMetadata( - fieldDefinition, - fieldValue, - ); + if (!fieldValue || !fieldDefinition) { + return <>; + } + + const mainIdentifierMapped = + fieldDefinition.metadata.mainIdentifierMapper(fieldValue); return ( ); }; diff --git a/front/src/modules/ui/object/field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx b/front/src/modules/ui/object/field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx index 7d57508d6..253d815bf 100644 --- a/front/src/modules/ui/object/field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx +++ b/front/src/modules/ui/object/field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx @@ -26,6 +26,7 @@ const meta: Meta = { { - switch (entityType) { - case Entity.Company: { - return ( - - ); - } - case Entity.Person: { - return ( - - ); - } + switch (true) { + // case Entity.Company: { + // return ( + // + // ); + // } + // case Entity.Person: { + // return ( + // + // ); + // } default: - logError( - `Unknown relation type: "${entityType}" in DoubleTextChipDisplay`, - ); return <> ; } }; diff --git a/front/src/modules/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata.ts b/front/src/modules/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata.ts deleted file mode 100644 index 11ef68df9..000000000 --- a/front/src/modules/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { EntityChipProps } from '@/ui/display/chip/components/EntityChip'; -import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition'; -import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata'; -import { getLogoUrlFromDomainName } from '~/utils'; - -export const getEntityChipFromFieldMetadata = ( - fieldDefinition: FieldDefinition, - fieldValue: any, -) => { - const { entityChipDisplayMapper } = fieldDefinition; - const { fieldName } = fieldDefinition.metadata; - - const defaultChipValue: Pick< - EntityChipProps, - 'name' | 'pictureUrl' | 'avatarType' | 'entityId' - > = { - name: '', - pictureUrl: '', - avatarType: 'rounded', - entityId: fieldValue?.id, - }; - - if (['accountOwner', 'person'].includes(fieldName) && fieldValue) { - return { - ...defaultChipValue, - name: `${fieldValue.firstName} ${fieldValue.lastName}`, - }; - } - - if (fieldName === 'company' && fieldValue) { - return { - ...defaultChipValue, - name: fieldValue.name, - pictureUrl: getLogoUrlFromDomainName(fieldValue.domainName), - }; - } - - return { - ...defaultChipValue, - ...entityChipDisplayMapper?.(fieldValue), - }; -}; diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useChipField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useChipField.ts index 79b25015d..6a5163b9b 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useChipField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useChipField.ts @@ -29,8 +29,6 @@ export const useChipField = () => { }), ); - const entityType = fieldDefinition.metadata.relationType; - const fieldInitialValue = useFieldInitialValue(); const initialContentValue = fieldInitialValue?.isEmpty @@ -51,7 +49,6 @@ export const useChipField = () => { avatarFieldValue, initialAvatarValue, setAvatarFieldValue, - entityType, entityId, hotkeyScope, }; diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextChipField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextChipField.ts index cb7571c6e..279a914ea 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextChipField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextChipField.ts @@ -39,8 +39,6 @@ export const useDoubleTextChipField = () => { const fullValue = [firstValue, secondValue].filter(Boolean).join(' '); - const entityType = fieldDefinition.metadata.entityType; - const fieldInitialValue = useFieldInitialValue(); const initialFirstValue = fieldInitialValue?.isEmpty @@ -68,7 +66,6 @@ export const useDoubleTextChipField = () => { firstValue, setFirstValue, fullValue, - entityType, entityId, hotkeyScope, initialAvatarUrl, diff --git a/front/src/modules/ui/object/field/meta-types/input/components/RelationFieldInput.tsx b/front/src/modules/ui/object/field/meta-types/input/components/RelationFieldInput.tsx index 6047218f3..afca7f2c3 100644 --- a/front/src/modules/ui/object/field/meta-types/input/components/RelationFieldInput.tsx +++ b/front/src/modules/ui/object/field/meta-types/input/components/RelationFieldInput.tsx @@ -1,10 +1,8 @@ import { useEffect } from 'react'; import styled from '@emotion/styled'; -import { CompanyPicker } from '@/companies/components/CompanyPicker'; -import { PeoplePicker } from '@/people/components/PeoplePicker'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; -import { WorkspaceMemberPicker } from '@/workspace-member/components/WorkspaceMemberPicker'; +import { RelationPicker } from '@/ui/object/field/meta-types/input/components/internal/RelationPicker'; import { usePersistField } from '../../../hooks/usePersistField'; import { useRelationField } from '../../hooks/useRelationField'; @@ -32,14 +30,21 @@ export const RelationFieldInput = ({ const persistField = usePersistField(); const handleSubmit = (newEntity: EntityForSelect | null) => { - onSubmit?.(() => persistField(newEntity?.originalEntity ?? null)); + onSubmit?.(() => persistField(newEntity?.record ?? null)); }; useEffect(() => {}, [initialSearchValue]); return ( - {fieldDefinition.metadata.fieldName === 'person' ? ( + + {/* {fieldDefinition.metadata.fieldName === 'person' ? ( - ) : fieldDefinition.metadata.fieldName === 'accountOwner' ? ( - ) : fieldDefinition.metadata.fieldName === 'company' ? ( - ) : null} + ) : null} */} ); }; diff --git a/front/src/modules/ui/object/field/meta-types/input/components/__stories__/ChipFieldInput.stories.tsx b/front/src/modules/ui/object/field/meta-types/input/components/__stories__/ChipFieldInput.stories.tsx index 3185ef59d..58bbdf490 100644 --- a/front/src/modules/ui/object/field/meta-types/input/components/__stories__/ChipFieldInput.stories.tsx +++ b/front/src/modules/ui/object/field/meta-types/input/components/__stories__/ChipFieldInput.stories.tsx @@ -3,7 +3,6 @@ import { expect, jest } from '@storybook/jest'; import { Decorator, Meta, StoryObj } from '@storybook/react'; import { userEvent, waitFor, within } from '@storybook/testing-library'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { FieldContextProvider } from '../../../__stories__/FieldContextProvider'; @@ -52,7 +51,6 @@ const ChipFieldInputWithContext = ({ contentFieldName: 'name', urlFieldName: 'xURL', placeHolder: 'X URL', - relationType: Entity.Person, }, }} entityId={entityId} diff --git a/front/src/modules/ui/object/field/meta-types/input/components/__stories__/DoubleTextChipFieldInput.stories.tsx b/front/src/modules/ui/object/field/meta-types/input/components/__stories__/DoubleTextChipFieldInput.stories.tsx index 7494ebc28..501d479e9 100644 --- a/front/src/modules/ui/object/field/meta-types/input/components/__stories__/DoubleTextChipFieldInput.stories.tsx +++ b/front/src/modules/ui/object/field/meta-types/input/components/__stories__/DoubleTextChipFieldInput.stories.tsx @@ -3,7 +3,6 @@ import { expect, jest } from '@storybook/jest'; import { Decorator, Meta, StoryObj } from '@storybook/react'; import { userEvent, waitFor, within } from '@storybook/testing-library'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { FieldContextProvider } from '../../../__stories__/FieldContextProvider'; @@ -67,7 +66,7 @@ const DoubleTextChipFieldInputWithContext = ({ secondValueFieldName: 'Second-text', secondValuePlaceholder: 'Second-text', avatarUrlFieldName: 'avatarUrl', - entityType: Entity.Person, + fieldName: '', }, }} entityId={entityId} diff --git a/front/src/modules/ui/object/field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx b/front/src/modules/ui/object/field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx index be623ad8e..d30080a4a 100644 --- a/front/src/modules/ui/object/field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx +++ b/front/src/modules/ui/object/field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx @@ -3,7 +3,6 @@ import { expect, jest } from '@storybook/jest'; import { Decorator, Meta, StoryObj } from '@storybook/react'; import { userEvent, waitFor, within } from '@storybook/testing-library'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; @@ -52,7 +51,6 @@ const RelationFieldInputWithContext = ({ iconName: 'IconLink', metadata: { fieldName: 'Relation', - relationType: Entity.Person, }, }} entityId={entityId} diff --git a/front/src/modules/workspace-member/components/WorkspaceMemberPicker.tsx b/front/src/modules/ui/object/field/meta-types/input/components/internal/RelationPicker.tsx similarity index 63% rename from front/src/modules/workspace-member/components/WorkspaceMemberPicker.tsx rename to front/src/modules/ui/object/field/meta-types/input/components/internal/RelationPicker.tsx index 0f67e526e..07f3556ba 100644 --- a/front/src/modules/workspace-member/components/WorkspaceMemberPicker.tsx +++ b/front/src/modules/ui/object/field/meta-types/input/components/internal/RelationPicker.tsx @@ -2,29 +2,32 @@ import { useEffect } from 'react'; import { useQuery } from '@apollo/client'; import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem'; -import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2'; +import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; import { IconUserCircle } from '@/ui/display/icon'; import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect'; import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; +import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition'; +import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; -export type WorkspaceMemberPickerProps = { - userId: string; +export type RelationPickerProps = { + recordId: string; onSubmit: (newUser: EntityForSelect | null) => void; onCancel?: () => void; width?: number; initialSearchFilter?: string | null; + fieldDefinition: FieldDefinition; }; -export const WorkspaceMemberPicker = ({ - userId, +export const RelationPicker = ({ + recordId, onSubmit, onCancel, width, initialSearchFilter, -}: WorkspaceMemberPickerProps) => { + fieldDefinition, +}: RelationPickerProps) => { const [relationPickerSearchFilter, setRelationPickerSearchFilter] = useRecoilScopedState(relationPickerSearchFilterScopedState); @@ -33,32 +36,23 @@ export const WorkspaceMemberPicker = ({ }, [initialSearchFilter, setRelationPickerSearchFilter]); const { findManyQuery } = useFindOneObjectMetadataItem({ - objectNameSingular: 'workspaceMember', + objectNameSingular: fieldDefinition.metadata.objectMetadataNameSingular, }); - const useFindManyWorkspaceMembers = (options: any) => - useQuery(findManyQuery, options); + const useFindManyQuery = (options: any) => useQuery(findManyQuery, options); - const workspaceMembers = useFilteredSearchEntityQueryV2({ - queryHook: useFindManyWorkspaceMembers, + const workspaceMembers = useFilteredSearchEntityQuery({ + queryHook: useFindManyQuery, filters: [ { - fieldNames: ['name.firstName', 'name.lastName'], + fieldNames: fieldDefinition.metadata.searchFields, filter: relationPickerSearchFilter, }, ], orderByField: 'createdAt', - mappingFunction: (workspaceMember) => ({ - entityType: Entity.WorkspaceMember, - id: workspaceMember.id, - name: - workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName, - avatarType: 'rounded', - avatarUrl: '', - originalEntity: workspaceMember, - }), - selectedIds: userId ? [userId] : [], - objectNamePlural: 'workspaceMembers', + mappingFunction: fieldDefinition.metadata.mainIdentifierMapper, + selectedIds: recordId ? [recordId] : [], + objectNamePlural: fieldDefinition.metadata.objectMetadataNamePlural, }); const handleEntitySelected = async (selectedUser: any | null | undefined) => { diff --git a/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts b/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts index 56dedbcc3..8e42cf78e 100644 --- a/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts +++ b/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts @@ -34,10 +34,9 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({ fieldDefinition, entityId, }: { - fieldDefinition: Pick< - FieldDefinition, - 'type' | 'metadata' | 'fieldMetadataId' | 'label' - >; + fieldDefinition: Pick, 'type'> & { + metadata: Omit; + }; entityId: string; }) => { return ({ get }) => { diff --git a/front/src/modules/ui/object/field/types/FieldDefinition.ts b/front/src/modules/ui/object/field/types/FieldDefinition.ts index 084e27105..eb4abf3a4 100644 --- a/front/src/modules/ui/object/field/types/FieldDefinition.ts +++ b/front/src/modules/ui/object/field/types/FieldDefinition.ts @@ -1,5 +1,3 @@ -import { AvatarType } from '@/users/components/Avatar'; - import { FieldMetadata } from './FieldMetadata'; import { FieldType } from './FieldType'; @@ -15,12 +13,5 @@ export type FieldDefinition = { iconName: string; type: FieldType; metadata: T; - basePathToShowPage?: string; infoTooltipContent?: string; - entityChipDisplayMapper?: (dataObject: any) => { - name: string; - pictureUrl?: string; - avatarType: AvatarType; - }; - relationType?: FieldDefinitionRelationType; }; diff --git a/front/src/modules/ui/object/field/types/FieldMetadata.ts b/front/src/modules/ui/object/field/types/FieldMetadata.ts index efcd272cf..c83b46f4f 100644 --- a/front/src/modules/ui/object/field/types/FieldMetadata.ts +++ b/front/src/modules/ui/object/field/types/FieldMetadata.ts @@ -1,5 +1,5 @@ import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; +import { MainIdentifierMapper } from '@/ui/object/field/types/MainIdentifierMapper'; import { ThemeColor } from '@/ui/theme/constants/colors'; export type FieldUuidMetadata = { @@ -58,14 +58,23 @@ export type FieldEmailMetadata = { placeHolder: string; }; +export type FieldDefinitionRelationType = + | 'FROM_MANY_OBJECTS' + | 'FROM_ONE_OBJECT' + | 'TO_MANY_OBJECTS' + | 'TO_ONE_OBJECT'; + export type FieldRelationMetadata = { - relationType: Entity; fieldName: string; useEditButton?: boolean; + relationType?: FieldDefinitionRelationType; + mainIdentifierMapper: MainIdentifierMapper; + searchFields: string[]; + objectMetadataNameSingular: string; + objectMetadataNamePlural: string; }; export type FieldChipMetadata = { - relationType: Entity; contentFieldName: string; urlFieldName: string; placeHolder: string; @@ -84,7 +93,6 @@ export type FieldDoubleTextChipMetadata = { secondValueFieldName: string; secondValuePlaceholder: string; avatarUrlFieldName: string; - entityType: Entity; }; export type FieldProbabilityMetadata = { diff --git a/front/src/modules/ui/object/field/types/MainIdentifierMapper.ts b/front/src/modules/ui/object/field/types/MainIdentifierMapper.ts new file mode 100644 index 000000000..2b275de6b --- /dev/null +++ b/front/src/modules/ui/object/field/types/MainIdentifierMapper.ts @@ -0,0 +1,9 @@ +import { AvatarType } from '@/users/components/Avatar'; + +export type MainIdentifierMapper = (record: any) => { + id: string; + name: string; + avatarUrl?: string; + avatarType: AvatarType; + record: any; +}; diff --git a/front/src/modules/ui/object/field/types/RelationFieldConfig.ts b/front/src/modules/ui/object/field/types/RelationFieldConfig.ts new file mode 100644 index 000000000..e2e32229c --- /dev/null +++ b/front/src/modules/ui/object/field/types/RelationFieldConfig.ts @@ -0,0 +1,14 @@ +import { MainIdentifierMapper } from '@/ui/object/field/types/MainIdentifierMapper'; + +export type FieldDefinitionRelationType = + | 'FROM_MANY_OBJECTS' + | 'FROM_ONE_OBJECT' + | 'TO_MANY_OBJECTS' + | 'TO_ONE_OBJECT'; + +export type RelationFieldConfig = { + relationType?: FieldDefinitionRelationType; + mainIdentifierMapper: MainIdentifierMapper; + searchFields: string[]; + objectMetadataNameSingular: string; +}; diff --git a/front/src/modules/ui/object/object-filter-dropdown/components/GenericEntityFilterChip.tsx b/front/src/modules/ui/object/object-filter-dropdown/components/GenericEntityFilterChip.tsx index 41df2e51a..38e3b89c4 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/components/GenericEntityFilterChip.tsx +++ b/front/src/modules/ui/object/object-filter-dropdown/components/GenericEntityFilterChip.tsx @@ -16,7 +16,7 @@ export const GenericEntityFilterChip = ({ entityId={filter.value} name={filter.displayValue} avatarType="rounded" - pictureUrl={filter.displayAvatarUrl} + avatarUrl={filter.displayAvatarUrl} LeftIcon={Icon} /> ); diff --git a/front/src/modules/ui/object/record-table/components/RecordTableCell.tsx b/front/src/modules/ui/object/record-table/components/RecordTableCell.tsx index 519050790..375b1ca7f 100644 --- a/front/src/modules/ui/object/record-table/components/RecordTableCell.tsx +++ b/front/src/modules/ui/object/record-table/components/RecordTableCell.tsx @@ -1,9 +1,10 @@ import { useContext } from 'react'; -import { useSetRecoilState } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState'; import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState'; +import { useRecordTableScopedStates } from '@/ui/object/record-table/hooks/internal/useRecordTableScopedStates'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { FieldContext } from '../../field/contexts/FieldContext'; @@ -20,6 +21,9 @@ export const RecordTableCell = ({ cellIndex }: { cellIndex: number }) => { const setContextMenuPosition = useSetRecoilState(contextMenuPositionState); const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState); const currentRowId = useContext(RowIdContext); + const { objectMetadataConfigState } = useRecordTableScopedStates(); + + const objectMetadataConfig = useRecoilValue(objectMetadataConfigState); const { setCurrentRowSelected } = useCurrentRowSelected(); @@ -56,6 +60,10 @@ export const RecordTableCell = ({ cellIndex }: { cellIndex: number }) => { fieldDefinition: columnDefinition, useUpdateEntityMutation: () => [updateEntityMutation, {}], hotkeyScope: customHotkeyScope, + isMainIdentifier: + columnDefinition.fieldMetadataId === + objectMetadataConfig?.mainIdentifierFieldMetadataId, + mainIdentifierMapper: objectMetadataConfig?.mainIdentifierMapper, }} > diff --git a/front/src/modules/ui/object/record-table/hooks/internal/useRecordTableScopedStates.ts b/front/src/modules/ui/object/record-table/hooks/internal/useRecordTableScopedStates.ts index a7d71e21f..7ad3ca608 100644 --- a/front/src/modules/ui/object/record-table/hooks/internal/useRecordTableScopedStates.ts +++ b/front/src/modules/ui/object/record-table/hooks/internal/useRecordTableScopedStates.ts @@ -18,6 +18,7 @@ export const useRecordTableScopedStates = (args?: { tableFiltersState, tableSortsState, tableColumnsState, + objectMetadataConfigState, tableColumnsByKeySelector, hiddenTableColumnsSelector, visibleTableColumnsSelector, @@ -33,6 +34,7 @@ export const useRecordTableScopedStates = (args?: { tableFiltersState, tableSortsState, tableColumnsState, + objectMetadataConfigState, tableColumnsByKeySelector, hiddenTableColumnsSelector, visibleTableColumnsSelector, diff --git a/front/src/modules/ui/object/record-table/hooks/useRecordTable.ts b/front/src/modules/ui/object/record-table/hooks/useRecordTable.ts index ef1921bef..076c46e63 100644 --- a/front/src/modules/ui/object/record-table/hooks/useRecordTable.ts +++ b/front/src/modules/ui/object/record-table/hooks/useRecordTable.ts @@ -43,6 +43,7 @@ export const useRecordTable = (props?: useRecordTableProps) => { tableFiltersState, tableSortsState, tableColumnsState, + objectMetadataConfigState, onEntityCountChangeState, } = useRecordTableScopedStates({ customRecordTableScopeId: scopeId, @@ -54,6 +55,7 @@ export const useRecordTable = (props?: useRecordTableProps) => { const setOnEntityCountChange = useSetRecoilState(onEntityCountChangeState); const setTableFilters = useSetRecoilState(tableFiltersState); + const setObjectMetadataConfig = useSetRecoilState(objectMetadataConfigState); const setTableSorts = useSetRecoilState(tableSortsState); @@ -301,6 +303,7 @@ export const useRecordTable = (props?: useRecordTableProps) => { setAvailableTableColumns, setTableFilters, setTableSorts, + setObjectMetadataConfig, setOnEntityCountChange, setRecordTableData, setTableColumns, diff --git a/front/src/modules/ui/object/record-table/record-table-cell/hooks/useTableCell.ts b/front/src/modules/ui/object/record-table/record-table-cell/hooks/useTableCell.ts index b578bbacf..cfeb8543c 100644 --- a/front/src/modules/ui/object/record-table/record-table-cell/hooks/useTableCell.ts +++ b/front/src/modules/ui/object/record-table/record-table-cell/hooks/useTableCell.ts @@ -37,7 +37,8 @@ export const useTableCell = () => { const isEmpty = useIsFieldEmpty(); - const { entityId, fieldDefinition } = useContext(FieldContext); + const { entityId, fieldDefinition, basePathToShowPage } = + useContext(FieldContext); const [, setFieldInitialValue] = useRecoilState( entityFieldInitialValueFamilyState({ @@ -47,8 +48,8 @@ export const useTableCell = () => { ); const openTableCell = (options?: { initialValue?: FieldInitialValue }) => { - if (isFirstColumnCell && !isEmpty && fieldDefinition.basePathToShowPage) { - navigate(`${fieldDefinition.basePathToShowPage}${entityId}`); + if (isFirstColumnCell && !isEmpty && basePathToShowPage) { + navigate(`${basePathToShowPage}${entityId}`); return; } diff --git a/front/src/modules/ui/object/record-table/states/objectMetadataConfigScopedState.ts b/front/src/modules/ui/object/record-table/states/objectMetadataConfigScopedState.ts new file mode 100644 index 000000000..35817a352 --- /dev/null +++ b/front/src/modules/ui/object/record-table/states/objectMetadataConfigScopedState.ts @@ -0,0 +1,8 @@ +import { ObjectMetadataConfig } from '@/ui/object/record-table/types/ObjectMetadataConfig'; +import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; + +export const objectMetadataConfigScopedState = + createScopedState({ + key: 'objectMetadataConfigScopedState', + defaultValue: null, + }); diff --git a/front/src/modules/ui/object/record-table/types/ObjectMetadataConfig.ts b/front/src/modules/ui/object/record-table/types/ObjectMetadataConfig.ts new file mode 100644 index 000000000..41b7d5047 --- /dev/null +++ b/front/src/modules/ui/object/record-table/types/ObjectMetadataConfig.ts @@ -0,0 +1,11 @@ +import { AvatarType } from '@/users/components/Avatar'; + +export type ObjectMetadataConfig = { + mainIdentifierFieldMetadataId: string; + mainIdentifierMapper: (record: any) => { + name: string; + avatarUrl?: string; + avatarType: AvatarType; + }; + basePathToShowPage: string; +}; diff --git a/front/src/modules/ui/object/record-table/utils/getRecordTableScopedStates.ts b/front/src/modules/ui/object/record-table/utils/getRecordTableScopedStates.ts index fcb716692..819c743e9 100644 --- a/front/src/modules/ui/object/record-table/utils/getRecordTableScopedStates.ts +++ b/front/src/modules/ui/object/record-table/utils/getRecordTableScopedStates.ts @@ -1,3 +1,4 @@ +import { objectMetadataConfigScopedState } from '@/ui/object/record-table/states/objectMetadataConfigScopedState'; import { getScopedState } from '@/ui/utilities/recoil-scope/utils/getScopedState'; import { availableTableColumnsScopedState } from '../states/availableTableColumnsScopedState'; @@ -36,6 +37,11 @@ export const getRecordTableScopedStates = ({ recordTableScopeId, ); + const objectMetadataConfigState = getScopedState( + objectMetadataConfigScopedState, + recordTableScopeId, + ); + const tableColumnsByKeySelector = tableColumnsByKeyScopedSelector(recordTableScopeId); @@ -60,6 +66,7 @@ export const getRecordTableScopedStates = ({ tableFiltersState, tableSortsState, tableColumnsState, + objectMetadataConfigState, tableColumnsByKeySelector, hiddenTableColumnsSelector, visibleTableColumnsSelector, diff --git a/front/src/modules/users/components/FilterDropdownUserSearchSelect.tsx b/front/src/modules/users/components/FilterDropdownUserSearchSelect.tsx index fdd577d4f..428773d90 100644 --- a/front/src/modules/users/components/FilterDropdownUserSearchSelect.tsx +++ b/front/src/modules/users/components/FilterDropdownUserSearchSelect.tsx @@ -1,8 +1,7 @@ import { useQuery } from '@apollo/client'; import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem'; -import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2'; -import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; +import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; import { ObjectFilterDropdownEntitySearchSelect } from '@/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect'; import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter'; @@ -19,7 +18,7 @@ export const FilterDropdownUserSearchSelect = () => { const useFindManyWorkspaceMembers = (options: any) => useQuery(findManyQuery, options); - const workspaceMembers = useFilteredSearchEntityQueryV2({ + const workspaceMembers = useFilteredSearchEntityQuery({ queryHook: useFindManyWorkspaceMembers, filters: [ { @@ -29,13 +28,13 @@ export const FilterDropdownUserSearchSelect = () => { ], orderByField: 'createdAt', mappingFunction: (workspaceMember) => ({ - entityType: Entity.WorkspaceMember, + entityType: 'WorkspaceMember', id: workspaceMember.id, name: workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName, avatarType: 'rounded', avatarUrl: '', - originalEntity: workspaceMember, + record: workspaceMember, }), selectedIds: objectFilterDropdownSelectedEntityId ? [objectFilterDropdownSelectedEntityId] diff --git a/front/src/modules/users/components/UserChip.tsx b/front/src/modules/users/components/UserChip.tsx index 85a3073e7..b5a5d1a23 100644 --- a/front/src/modules/users/components/UserChip.tsx +++ b/front/src/modules/users/components/UserChip.tsx @@ -3,14 +3,14 @@ import { EntityChip } from '@/ui/display/chip/components/EntityChip'; export type UserChipProps = { id: string; name: string; - pictureUrl?: string; + avatarUrl?: string; }; -export const UserChip = ({ id, name, pictureUrl }: UserChipProps) => ( +export const UserChip = ({ id, name, avatarUrl }: UserChipProps) => ( ); diff --git a/front/src/modules/views/utils/mapViewFieldsToBoardFieldDefinitions.ts b/front/src/modules/views/utils/mapViewFieldsToBoardFieldDefinitions.ts index 5dada6459..59607cc45 100644 --- a/front/src/modules/views/utils/mapViewFieldsToBoardFieldDefinitions.ts +++ b/front/src/modules/views/utils/mapViewFieldsToBoardFieldDefinitions.ts @@ -19,10 +19,7 @@ export const mapViewFieldsToBoardFieldDefinitions = ( fieldMetadataId: viewField.fieldMetadataId, label: correspondingFieldMetadata.label, metadata: correspondingFieldMetadata.metadata, - entityChipDisplayMapper: - correspondingFieldMetadata.entityChipDisplayMapper, infoTooltipContent: correspondingFieldMetadata.infoTooltipContent, - basePathToShowPage: correspondingFieldMetadata.basePathToShowPage, iconName: correspondingFieldMetadata.iconName, type: correspondingFieldMetadata.type, position: viewField.position, diff --git a/front/src/modules/views/utils/mapViewFieldsToColumnDefinitions.ts b/front/src/modules/views/utils/mapViewFieldsToColumnDefinitions.ts index 52c6432ea..7372c427b 100644 --- a/front/src/modules/views/utils/mapViewFieldsToColumnDefinitions.ts +++ b/front/src/modules/views/utils/mapViewFieldsToColumnDefinitions.ts @@ -19,10 +19,7 @@ export const mapViewFieldsToColumnDefinitions = ( fieldMetadataId: viewField.fieldMetadataId, label: correspondingFieldMetadata.label, metadata: correspondingFieldMetadata.metadata, - entityChipDisplayMapper: - correspondingFieldMetadata.entityChipDisplayMapper, infoTooltipContent: correspondingFieldMetadata.infoTooltipContent, - basePathToShowPage: correspondingFieldMetadata.basePathToShowPage, iconName: correspondingFieldMetadata.iconName, type: correspondingFieldMetadata.type, position: viewField.position, diff --git a/front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx b/front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx index c52b0d8f1..cb39b522d 100644 --- a/front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx +++ b/front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx @@ -1,9 +1,6 @@ -import { FilterDropdownCompanySearchSelect } from '@/companies/components/FilterDropdownCompanySearchSelect'; import { Opportunity } from '@/pipeline/types/Opportunity'; import { FilterDefinitionByEntity } from '@/ui/object/object-filter-dropdown/types/FilterDefinitionByEntity'; -import { FilterDropdownPeopleSearchSelect } from '../../../modules/people/components/FilterDropdownPeopleSearchSelect'; - export const opportunityBoardFilterDefinitions: FilterDefinitionByEntity[] = [ { @@ -23,13 +20,13 @@ export const opportunityBoardFilterDefinitions: FilterDefinitionByEntity, + // entitySelectComponent: , }, { fieldMetadataId: 'pointOfContactId', label: 'Point of contact', iconName: 'IconUser', type: 'ENTITY', - entitySelectComponent: , + //entitySelectComponent: , }, ];