From 070900e4ebe141b13555a57b44962f00c2b0e442 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Fri, 26 Jan 2024 17:09:30 +0100 Subject: [PATCH] Remap items in board (#3643) * Remap items in board * Fix according to review --- .../record-board/components/RecordBoard.tsx | 4 +- .../hooks/internal/useRecordBoardStates.ts | 21 ++++++ .../internal/useSetRecordBoardColumns.ts | 4 +- .../internal/useSetRecordBoardRecordIds.ts | 44 +++++++++++++ .../record-board/hooks/useRecordBoard.ts | 15 +++-- .../components/RecordBoardColumn.tsx | 16 +++-- ...ecordBoardFieldDefinitionsStateScopeMap.ts | 10 +++ .../states/recordBoardFiltersStateScopeMap.ts | 7 ++ ...ObjectMetadataSingularNameStateScopeMap.ts | 7 ++ ...dRecordIdsByColumnIdFamilyStateScopeMap.ts | 7 ++ .../states/recordBoardSortsStateScopeMap.ts | 7 ++ .../types/RecordBoardColumnDefinition.ts | 1 + .../types/RecordBoardFieldDefinition.ts | 9 +++ .../components/RecordIndexBoardContainer.tsx | 4 +- .../RecordIndexBoardContainerEffect.tsx | 22 +++---- .../components/RecordIndexContainer.tsx | 23 +++++-- .../components/RecordIndexTableContainer.tsx | 12 ++-- .../RecordIndexTableContainerEffect.tsx | 11 +--- .../hooks/useLoadRecordIndexBoard.ts | 64 +++++++++++++++++++ .../recordIndexFieldDefinitionsState.ts | 11 ++++ .../states/recordIndexFiltersState.ts | 8 +++ .../states/recordIndexSortsState.ts | 8 +++ .../record-store/hooks/useSetRecordInStore.ts | 26 ++++++++ .../states/recordStoreFamilyState.ts | 8 +++ .../selectors/recordStoreFamilySelector.ts | 17 +++++ ...oardColumnDefinitionsFromObjectMetadata.ts | 1 + 26 files changed, 320 insertions(+), 47 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFieldDefinitionsStateScopeMap.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFiltersStateScopeMap.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardObjectMetadataSingularNameStateScopeMap.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardRecordIdsByColumnIdFamilyStateScopeMap.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardSortsStateScopeMap.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-board/types/RecordBoardFieldDefinition.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFieldDefinitionsState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFiltersState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-index/states/recordIndexSortsState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-store/hooks/useSetRecordInStore.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-store/states/recordStoreFamilyState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreFamilySelector.ts diff --git a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx index 27e78a85c..7faaaa17e 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx @@ -3,7 +3,7 @@ import styled from '@emotion/styled'; import { DragDropContext } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350 import { useRecoilValue } from 'recoil'; -import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; +import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { RecordBoardColumn } from '@/object-record/record-board/record-board-column/components/RecordBoardColumn'; import { RecordBoardScope } from '@/object-record/record-board/scopes/RecordBoardScope'; import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; @@ -40,7 +40,7 @@ const StyledBoardHeader = styled.div` export const RecordBoard = ({ recordBoardId }: RecordBoardProps) => { const boardRef = useRef(null); - const { getColumnIdsState } = useRecordBoard(recordBoardId); + const { getColumnIdsState } = useRecordBoardStates(recordBoardId); const columnIds = useRecoilValue(getColumnIdsState()); diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts index fe2f8ed61..92848e5e7 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts @@ -2,6 +2,11 @@ import { RecordBoardScopeInternalContext } from '@/object-record/record-board/sc import { isFirstRecordBoardColumnFamilyStateScopeMap } from '@/object-record/record-board/states/isFirstRecordBoardColumnFamilyStateScopeMap'; import { isLastRecordBoardColumnFamilyStateScopeMap } from '@/object-record/record-board/states/isLastRecordBoardColumnFamilyStateScopeMap'; import { recordBoardColumnIdsStateScopeMap } from '@/object-record/record-board/states/recordBoardColumnIdsStateScopeMap'; +import { recordBoardFieldDefinitionsStateScopeMap } from '@/object-record/record-board/states/recordBoardFieldDefinitionsStateScopeMap'; +import { recordBoardFiltersStateScopeMap } from '@/object-record/record-board/states/recordBoardFiltersStateScopeMap'; +import { recordBoardObjectMetadataSingularNameStateScopeMap } from '@/object-record/record-board/states/recordBoardObjectMetadataSingularNameStateScopeMap'; +import { recordBoardRecordIdsByColumnIdFamilyStateScopeMap } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdFamilyStateScopeMap'; +import { recordBoardSortsStateScopeMap } from '@/object-record/record-board/states/recordBoardSortsStateScopeMap'; import { recordBoardColumnsFamilySelectorScopeMap } from '@/object-record/record-board/states/selectors/recordBoardColumnsFamilySelectorScopeMap'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { getFamilyState } from '@/ui/utilities/recoil-scope/utils/getFamilyState'; @@ -16,6 +21,10 @@ export const useRecordBoardStates = (recordBoardId?: string) => { return { scopeId, + getObjectMetadataSingularNameState: getState( + recordBoardObjectMetadataSingularNameStateScopeMap, + scopeId, + ), getColumnIdsState: getState(recordBoardColumnIdsStateScopeMap, scopeId), isFirstColumnFamilyState: getFamilyState( isFirstRecordBoardColumnFamilyStateScopeMap, @@ -29,5 +38,17 @@ export const useRecordBoardStates = (recordBoardId?: string) => { recordBoardColumnsFamilySelectorScopeMap, scopeId, ), + + getFiltersState: getState(recordBoardFiltersStateScopeMap, scopeId), + getSortsState: getState(recordBoardSortsStateScopeMap, scopeId), + getFieldDefinitionsState: getState( + recordBoardFieldDefinitionsStateScopeMap, + scopeId, + ), + + recordBoardRecordIdsByColumnIdFamilyState: getFamilyState( + recordBoardRecordIdsByColumnIdFamilyStateScopeMap, + scopeId, + ), }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardColumns.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardColumns.ts index 393f0250b..34ca40620 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardColumns.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardColumns.ts @@ -8,7 +8,7 @@ export const useSetRecordBoardColumns = (recordBoardId?: string) => { const { scopeId, getColumnIdsState, columnsFamilySelector } = useRecordBoardStates(recordBoardId); - const setRecordBoardColumns = useRecoilCallback( + const setColumns = useRecoilCallback( ({ set, snapshot }) => (columns: RecordBoardColumnDefinition[]) => { const currentColumnsIds = snapshot @@ -43,6 +43,6 @@ export const useSetRecordBoardColumns = (recordBoardId?: string) => { return { scopeId, - setRecordBoardColumns, + setColumns, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts new file mode 100644 index 000000000..b0fa4fb17 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts @@ -0,0 +1,44 @@ +import { useRecoilCallback } from 'recoil'; + +import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; + +export const useSetRecordBoardRecordIds = (recordBoardId?: string) => { + const { + scopeId, + recordBoardRecordIdsByColumnIdFamilyState, + columnsFamilySelector, + getColumnIdsState, + } = useRecordBoardStates(recordBoardId); + + const setRecordIds = useRecoilCallback( + ({ set, snapshot }) => + (records: ObjectRecord[]) => { + const columnIds = snapshot.getLoadable(getColumnIdsState()).getValue(); + + columnIds.forEach((columnId) => { + const column = snapshot + .getLoadable(columnsFamilySelector(columnId)) + .getValue(); + + const columnRecordIds = records + .filter((record) => record.stage === column?.value) + .map((record) => record.id); + set( + recordBoardRecordIdsByColumnIdFamilyState(columnId), + columnRecordIds, + ); + }); + }, + [ + columnsFamilySelector, + getColumnIdsState, + recordBoardRecordIdsByColumnIdFamilyState, + ], + ); + + return { + scopeId, + setRecordIds, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts index 2ddfcfc04..d6ea6530d 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts @@ -1,16 +1,21 @@ +import { useSetRecoilState } from 'recoil'; + import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useSetRecordBoardColumns } from '@/object-record/record-board/hooks/internal/useSetRecordBoardColumns'; +import { useSetRecordBoardRecordIds } from '@/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds'; export const useRecordBoard = (recordBoardId?: string) => { - const { scopeId, getColumnIdsState, columnsFamilySelector } = + const { scopeId, getFieldDefinitionsState } = useRecordBoardStates(recordBoardId); - const { setRecordBoardColumns } = useSetRecordBoardColumns(recordBoardId); + const { setColumns } = useSetRecordBoardColumns(recordBoardId); + const { setRecordIds } = useSetRecordBoardRecordIds(recordBoardId); + const setFieldDefinitions = useSetRecoilState(getFieldDefinitionsState()); return { scopeId, - getColumnIdsState, - columnsFamilySelector, - setRecordBoardColumns, + setColumns, + setRecordIds, + setFieldDefinitions, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx index 97b14a715..c1c7bb34a 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx @@ -33,6 +33,7 @@ export const RecordBoardColumn = ({ isFirstColumnFamilyState, isLastColumnFamilyState, columnsFamilySelector, + recordBoardRecordIdsByColumnIdFamilyState, } = useRecordBoardStates(); const columnDefinition = useRecoilValue( columnsFamilySelector(recordBoardColumnId), @@ -46,7 +47,11 @@ export const RecordBoardColumn = ({ isLastColumnFamilyState(recordBoardColumnId), ); - if (!columnDefinition) { + const recordIds = useRecoilValue( + recordBoardRecordIdsByColumnIdFamilyState(recordBoardColumnId), + ); + + if (!columnDefinition || !recordIds) { return null; } @@ -65,11 +70,10 @@ export const RecordBoardColumn = ({ - {[].map((cardId, _index) => ( - + {recordIds.map((recordId) => ( + +
Card
+
))}
diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFieldDefinitionsStateScopeMap.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFieldDefinitionsStateScopeMap.ts new file mode 100644 index 000000000..db00cd8fd --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFieldDefinitionsStateScopeMap.ts @@ -0,0 +1,10 @@ +import { FieldMetadata } from '@/object-record/field/types/FieldMetadata'; +import { RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition'; +import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap'; + +export const recordBoardFieldDefinitionsStateScopeMap = createStateScopeMap< + RecordBoardFieldDefinition[] +>({ + key: 'recordBoardFieldDefinitionsStateScopeMap', + defaultValue: [], +}); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFiltersStateScopeMap.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFiltersStateScopeMap.ts new file mode 100644 index 000000000..71c8528ce --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFiltersStateScopeMap.ts @@ -0,0 +1,7 @@ +import { Filter } from '@/object-record/object-filter-dropdown/types/Filter'; +import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap'; + +export const recordBoardFiltersStateScopeMap = createStateScopeMap({ + key: 'recordBoardFiltersStateScopeMap', + defaultValue: [], +}); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardObjectMetadataSingularNameStateScopeMap.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardObjectMetadataSingularNameStateScopeMap.ts new file mode 100644 index 000000000..915561d6e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardObjectMetadataSingularNameStateScopeMap.ts @@ -0,0 +1,7 @@ +import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap'; + +export const recordBoardObjectMetadataSingularNameStateScopeMap = + createStateScopeMap({ + key: 'recordBoardObjectMetadataSingularNameStateScopeMap', + defaultValue: undefined, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardRecordIdsByColumnIdFamilyStateScopeMap.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardRecordIdsByColumnIdFamilyStateScopeMap.ts new file mode 100644 index 000000000..8a413e0b5 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardRecordIdsByColumnIdFamilyStateScopeMap.ts @@ -0,0 +1,7 @@ +import { createFamilyStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createFamilyStateScopeMap'; + +export const recordBoardRecordIdsByColumnIdFamilyStateScopeMap = + createFamilyStateScopeMap({ + key: 'recordBoardRecordIdsByColumnIdFamilyStateScopeMap', + defaultValue: [], + }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardSortsStateScopeMap.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardSortsStateScopeMap.ts new file mode 100644 index 000000000..b49376153 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardSortsStateScopeMap.ts @@ -0,0 +1,7 @@ +import { Sort } from '@/object-record/object-sort-dropdown/types/Sort'; +import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap'; + +export const recordBoardSortsStateScopeMap = createStateScopeMap({ + key: 'recordBoardSortsStateScopeMap', + defaultValue: [], +}); diff --git a/packages/twenty-front/src/modules/object-record/record-board/types/RecordBoardColumnDefinition.ts b/packages/twenty-front/src/modules/object-record/record-board/types/RecordBoardColumnDefinition.ts index cafdb56e7..af3ee5e8f 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/types/RecordBoardColumnDefinition.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/types/RecordBoardColumnDefinition.ts @@ -4,6 +4,7 @@ import { ThemeColor } from '@/ui/theme/constants/colors'; export type RecordBoardColumnDefinition = { id: string; title: string; + value: string; position: number; color: ThemeColor; actions: RecordBoardColumnAction[]; diff --git a/packages/twenty-front/src/modules/object-record/record-board/types/RecordBoardFieldDefinition.ts b/packages/twenty-front/src/modules/object-record/record-board/types/RecordBoardFieldDefinition.ts new file mode 100644 index 000000000..bd7bef44d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/types/RecordBoardFieldDefinition.ts @@ -0,0 +1,9 @@ +import { FieldDefinition } from '@/object-record/field/types/FieldDefinition'; +import { FieldMetadata } from '@/object-record/field/types/FieldMetadata'; + +export type RecordBoardFieldDefinition = + FieldDefinition & { + viewFieldId?: string; + position: number; + isVisible?: boolean; + }; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainer.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainer.tsx index 1eb73a407..312e6859c 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainer.tsx @@ -3,12 +3,12 @@ import { RecordBoard } from '@/object-record/record-board/components/RecordBoard type RecordIndexBoardContainerProps = { recordBoardId: string; viewBarId: string; - objectNamePlural: string; + objectNameSingular: string; createRecord: () => Promise; }; export const RecordIndexBoardContainer = ({ recordBoardId, }: RecordIndexBoardContainerProps) => { - return ; + return ; }; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainerEffect.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainerEffect.tsx index e6a18ce09..9a7486db6 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainerEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainerEffect.tsx @@ -2,38 +2,36 @@ import { useCallback, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; +import { useLoadRecordIndexBoard } from '@/object-record/record-index/hooks/useLoadRecordIndexBoard'; import { computeRecordBoardColumnDefinitionsFromObjectMetadata } from '@/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata'; type RecordIndexBoardContainerEffectProps = { - objectNamePlural: string; + objectNameSingular: string; recordBoardId: string; viewBarId: string; }; export const RecordIndexBoardContainerEffect = ({ - objectNamePlural, + objectNameSingular, recordBoardId, }: RecordIndexBoardContainerEffectProps) => { - const { objectNameSingular } = useObjectNameSingularFromPlural({ - objectNamePlural, - }); - const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); + useLoadRecordIndexBoard(objectNameSingular, recordBoardId); + const navigate = useNavigate(); const navigateToSelectSettings = useCallback(() => { - navigate(`/settings/objects/${objectNamePlural}`); - }, [navigate, objectNamePlural]); + navigate(`/settings/objects/${objectMetadataItem.namePlural}`); + }, [navigate, objectMetadataItem.namePlural]); - const { setRecordBoardColumns } = useRecordBoard(recordBoardId); + const { setColumns } = useRecordBoard(recordBoardId); useEffect(() => { - setRecordBoardColumns( + setColumns( computeRecordBoardColumnDefinitionsFromObjectMetadata( objectMetadataItem, navigateToSelectSettings, @@ -43,7 +41,7 @@ export const RecordIndexBoardContainerEffect = ({ navigateToSelectSettings, objectMetadataItem, objectNameSingular, - setRecordBoardColumns, + setColumns, ]); return <>; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx index 02a522033..9c35cccad 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; import styled from '@emotion/styled'; +import { useSetRecoilState } from 'recoil'; import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport'; import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata'; @@ -10,6 +11,9 @@ import { RecordIndexBoardContainerEffect } from '@/object-record/record-index/co import { RecordIndexTableContainer } from '@/object-record/record-index/components/RecordIndexTableContainer'; import { RecordIndexTableContainerEffect } from '@/object-record/record-index/components/RecordIndexTableContainerEffect'; import { RecordIndexViewBarEffect } from '@/object-record/record-index/components/RecordIndexViewBarEffect'; +import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; +import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; +import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; import { TableOptionsDropdownId } from '@/object-record/record-table/constants/TableOptionsDropdownId'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { TableOptionsDropdown } from '@/object-record/record-table/options/components/TableOptionsDropdown'; @@ -56,6 +60,12 @@ export const RecordIndexContainer = ({ const { columnDefinitions } = useColumnDefinitionsFromFieldMetadata(objectMetadataItem); + const setRecordIndexFieldDefinitions = useSetRecoilState( + recordIndexFieldDefinitionsState, + ); + const setRecordIndexFilters = useSetRecoilState(recordIndexFiltersState); + const setRecordIndexSorts = useSetRecoilState(recordIndexSortsState); + const { openPersonSpreadsheetImport } = useSpreadsheetPersonImport(); const { openCompanySpreadsheetImport } = useSpreadsheetCompanyImport(); @@ -91,12 +101,17 @@ export const RecordIndexContainer = ({ setTableColumns( mapViewFieldsToColumnDefinitions(viewFields, columnDefinitions), ); + setRecordIndexFieldDefinitions( + mapViewFieldsToColumnDefinitions(viewFields, columnDefinitions), + ); }} onViewFiltersChange={(viewFilters) => { setTableFilters(mapViewFiltersToFilters(viewFilters)); + setRecordIndexFilters(mapViewFiltersToFilters(viewFilters)); }} onViewSortsChange={(viewSorts) => { setTableSorts(mapViewSortsToSorts(viewSorts)); + setRecordIndexSorts(mapViewSortsToSorts(viewSorts)); }} onViewTypeChange={(viewType: ViewType) => { setRecordIndexViewType(viewType); @@ -112,11 +127,11 @@ export const RecordIndexContainer = ({ @@ -127,11 +142,11 @@ export const RecordIndexContainer = ({ diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainer.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainer.tsx index 1de72a2bc..8d40bd874 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexTableContainer.tsx @@ -1,4 +1,4 @@ -import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { RecordUpdateHookParams } from '@/object-record/field/contexts/FieldContext'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { RecordTableActionBar } from '@/object-record/record-table/action-bar/components/RecordTableActionBar'; @@ -8,18 +8,18 @@ import { RecordTableContextMenu } from '@/object-record/record-table/context-men type RecordIndexTableContainerProps = { recordTableId: string; viewBarId: string; - objectNamePlural: string; + objectNameSingular: string; createRecord: () => Promise; }; export const RecordIndexTableContainer = ({ recordTableId, viewBarId, - objectNamePlural, + objectNameSingular, createRecord, }: RecordIndexTableContainerProps) => { - const { objectNameSingular } = useObjectNameSingularFromPlural({ - objectNamePlural, + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular, }); const { updateOneRecord } = useUpdateOneRecord({ @@ -37,7 +37,7 @@ export const RecordIndexTableContainer = ({ <> { @@ -25,10 +24,6 @@ export const RecordIndexTableContainerEffect = ({ setObjectMetadataConfig, } = useRecordTable({ recordTableId }); - const { objectNameSingular } = useObjectNameSingularFromPlural({ - objectNamePlural, - }); - const { objectMetadataItem, basePathToShowPage, @@ -74,7 +69,7 @@ export const RecordIndexTableContainerEffect = ({ const { setActionBarEntries, setContextMenuEntries } = useRecordTableContextMenuEntries({ - objectNamePlural, + objectNamePlural: objectMetadataItem.namePlural, recordTableId, }); diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts new file mode 100644 index 000000000..f7a394f8e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts @@ -0,0 +1,64 @@ +import { useEffect } from 'react'; +import { useRecoilValue } from 'recoil'; + +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; +import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; +import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; +import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter'; +import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; +import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; +import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; +import { useSetRecordInStore } from '@/object-record/record-store/hooks/useSetRecordInStore'; + +export const useLoadRecordIndexBoard = ( + objectNameSingular: string, + recordBoardId: string, +) => { + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular, + }); + const { setRecordIds: setRecordIdsInBoard, setFieldDefinitions } = + useRecordBoard(recordBoardId); + const { setRecords: setRecordsInStore } = useSetRecordInStore(); + + const recordIndexFieldDefinitions = useRecoilValue( + recordIndexFieldDefinitionsState, + ); + useEffect(() => { + setFieldDefinitions(recordIndexFieldDefinitions); + }, [recordIndexFieldDefinitions, setFieldDefinitions]); + + const recordIndexFilters = useRecoilValue(recordIndexFiltersState); + const recordIndexSorts = useRecoilValue(recordIndexSortsState); + const requestFilters = turnObjectDropdownFilterIntoQueryFilter( + recordIndexFilters, + objectMetadataItem?.fields ?? [], + ); + const orderBy = turnSortsIntoOrderBy( + recordIndexSorts, + objectMetadataItem?.fields ?? [], + ); + + const { records, loading, fetchMoreRecords, queryStateIdentifier } = + useFindManyRecords({ + objectNameSingular, + filter: requestFilters, + orderBy, + }); + + useEffect(() => { + setRecordIdsInBoard(records); + }, [records, setRecordIdsInBoard]); + + useEffect(() => { + setRecordsInStore(records); + }, [records, setRecordsInStore]); + + return { + records, + loading, + fetchMoreRecords, + queryStateIdentifier, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFieldDefinitionsState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFieldDefinitionsState.ts new file mode 100644 index 000000000..439c55e4e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFieldDefinitionsState.ts @@ -0,0 +1,11 @@ +import { atom } from 'recoil'; + +import { FieldMetadata } from '@/object-record/field/types/FieldMetadata'; +import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; + +export const recordIndexFieldDefinitionsState = atom< + ColumnDefinition[] +>({ + key: 'recordIndexFieldDefinitionsState', + default: [], +}); diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFiltersState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFiltersState.ts new file mode 100644 index 000000000..83349211e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexFiltersState.ts @@ -0,0 +1,8 @@ +import { atom } from 'recoil'; + +import { Filter } from '@/object-record/object-filter-dropdown/types/Filter'; + +export const recordIndexFiltersState = atom({ + key: 'recordIndexFiltersState', + default: [], +}); diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexSortsState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexSortsState.ts new file mode 100644 index 000000000..fe98a2f61 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexSortsState.ts @@ -0,0 +1,8 @@ +import { atom } from 'recoil'; + +import { Sort } from '@/object-record/object-sort-dropdown/types/Sort'; + +export const recordIndexSortsState = atom({ + key: 'recordIndexSortsState', + default: [], +}); diff --git a/packages/twenty-front/src/modules/object-record/record-store/hooks/useSetRecordInStore.ts b/packages/twenty-front/src/modules/object-record/record-store/hooks/useSetRecordInStore.ts new file mode 100644 index 000000000..fb3f09a1c --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-store/hooks/useSetRecordInStore.ts @@ -0,0 +1,26 @@ +import { useRecoilCallback } from 'recoil'; + +import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; + +export const useSetRecordInStore = () => { + const setRecords = useRecoilCallback( + ({ set, snapshot }) => + (records: ObjectRecord[]) => { + records.forEach((record) => { + const currentRecord = snapshot + .getLoadable(recordStoreFamilyState(record.id)) + .valueOrThrow(); + + if (JSON.stringify(currentRecord) !== JSON.stringify(record)) { + set(recordStoreFamilyState(record.id), record); + } + }); + }, + [], + ); + + return { + setRecords, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-store/states/recordStoreFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-store/states/recordStoreFamilyState.ts new file mode 100644 index 000000000..a0e71f0bb --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-store/states/recordStoreFamilyState.ts @@ -0,0 +1,8 @@ +import { atomFamily } from 'recoil'; + +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; + +export const recordStoreFamilyState = atomFamily({ + key: 'recordStoreFamilyState', + default: null, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreFamilySelector.ts b/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreFamilySelector.ts new file mode 100644 index 000000000..5e81114b8 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreFamilySelector.ts @@ -0,0 +1,17 @@ +import { selectorFamily } from 'recoil'; + +import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; + +export const recordStoreFamilySelector = selectorFamily({ + key: 'recordStoreFamilySelector', + get: + ({ fieldName, recordId }: { fieldName: string; recordId: string }) => + ({ get }) => + get(recordStoreFamilyState(recordId))?.[fieldName] as T, + set: + ({ fieldName, recordId }: { fieldName: string; recordId: string }) => + ({ set }, newValue: T) => + set(recordStoreFamilyState(recordId), (prevState) => + prevState ? { ...prevState, [fieldName]: newValue } : null, + ), +}); diff --git a/packages/twenty-front/src/modules/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata.ts b/packages/twenty-front/src/modules/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata.ts index d06f01dc2..5af6540ae 100644 --- a/packages/twenty-front/src/modules/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata.ts @@ -24,6 +24,7 @@ export const computeRecordBoardColumnDefinitionsFromObjectMetadata = ( return selectFieldMetadataItem.options.map((selectOption) => ({ id: selectOption.id, title: selectOption.label, + value: selectOption.value, color: selectOption.color, position: selectOption.position, actions: [