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
index 8ead97030..e5b61e4e6 100644
--- 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
@@ -2,6 +2,7 @@ import { useRecoilCallback } from 'recoil';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
+import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const useSetRecordBoardRecordIds = (recordBoardId?: string) => {
@@ -60,21 +61,3 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => {
setRecordIds,
};
};
-
-const sortRecordsByPosition = (
- record1: ObjectRecord,
- record2: ObjectRecord,
-) => {
- if (
- typeof record1.position == 'number' &&
- typeof record2.position == 'number'
- ) {
- return record1.position - record2.position;
- } else if (record1.position === 'first' || record2.position === 'last') {
- return -1;
- } else if (record2.position === 'first' || record1.position === 'last') {
- return 1;
- } else {
- return 0;
- }
-};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts
new file mode 100644
index 000000000..25138622d
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts
@@ -0,0 +1,55 @@
+import { useRecoilCallback } from 'recoil';
+
+import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
+import { ObjectRecord } from '@/object-record/types/ObjectRecord';
+import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition';
+import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
+
+export const useSetRecordIdsForColumn = (recordBoardId?: string) => {
+ const {
+ scopeId,
+ recordIdsByColumnIdFamilyState,
+ columnsFamilySelector,
+ kanbanFieldMetadataNameState,
+ } = useRecordBoardStates(recordBoardId);
+
+ const setRecordIdsForColumn = useRecoilCallback(
+ ({ set, snapshot }) =>
+ (columnId: string, records: ObjectRecord[]) => {
+ const column = snapshot
+ .getLoadable(columnsFamilySelector(columnId))
+ .getValue();
+
+ const existingColumnRecordIds = snapshot
+ .getLoadable(recordIdsByColumnIdFamilyState(columnId))
+ .getValue();
+
+ const kanbanFieldMetadataName = snapshot
+ .getLoadable(kanbanFieldMetadataNameState)
+ .getValue();
+
+ if (!kanbanFieldMetadataName) {
+ return;
+ }
+
+ const columnRecordIds = records
+ .filter((record) => record[kanbanFieldMetadataName] === column?.value)
+ .sort(sortRecordsByPosition)
+ .map((record) => record.id);
+
+ if (!isDeeplyEqual(existingColumnRecordIds, columnRecordIds)) {
+ set(recordIdsByColumnIdFamilyState(columnId), columnRecordIds);
+ }
+ },
+ [
+ columnsFamilySelector,
+ recordIdsByColumnIdFamilyState,
+ kanbanFieldMetadataNameState,
+ ],
+ );
+
+ return {
+ scopeId,
+ setRecordIdsForColumn,
+ };
+};
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 c8f57c6ab..b516e6dc7 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
@@ -3,6 +3,7 @@ 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';
+import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/internal/useSetRecordIdsForColumn';
export const useRecordBoard = (recordBoardId?: string) => {
const {
@@ -17,6 +18,8 @@ export const useRecordBoard = (recordBoardId?: string) => {
const { setColumns } = useSetRecordBoardColumns(recordBoardId);
const { setRecordIds } = useSetRecordBoardRecordIds(recordBoardId);
+ const { setRecordIdsForColumn } = useSetRecordIdsForColumn(recordBoardId);
+
const setFieldDefinitions = useSetRecoilState(fieldDefinitionsState);
const setObjectSingularName = useSetRecoilState(objectSingularNameState);
const setKanbanFieldMetadataName = useSetRecoilState(
@@ -33,5 +36,6 @@ export const useRecordBoard = (recordBoardId?: string) => {
selectedRecordIdsSelector,
isCompactModeActiveState,
shouldFetchMoreSelector,
+ setRecordIdsForColumn,
};
};
diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx
index 7dd71ba08..447eb6ae1 100644
--- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx
@@ -23,15 +23,15 @@ export const RecordBoardColumnFetchMoreLoader = () => {
useRecordBoardStates();
const isFetchingRecord = useRecoilValue(isFetchingRecordState);
- const shouldFetchMore = useSetRecoilState(
+ const setShouldFetchMore = useSetRecoilState(
shouldFetchMoreInColumnFamilyState(columnDefinition.id),
);
const { ref, inView } = useInView();
useEffect(() => {
- shouldFetchMore(inView);
- }, [shouldFetchMore, inView]);
+ setShouldFetchMore(inView);
+ }, [setShouldFetchMore, inView]);
return (
diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx
new file mode 100644
index 000000000..b97e6e3a6
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx
@@ -0,0 +1,39 @@
+import { useEffect } from 'react';
+import { useRecoilValue } from 'recoil';
+
+import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard';
+import { useLoadRecordIndexBoardColumn } from '@/object-record/record-index/hooks/useLoadRecordIndexBoardColumn';
+
+export const RecordIndexBoardColumnLoaderEffect = ({
+ objectNameSingular,
+ boardFieldSelectValue,
+ boardFieldMetadataId,
+ recordBoardId,
+ columnId,
+}: {
+ recordBoardId: string;
+ objectNameSingular: string;
+ boardFieldSelectValue: string;
+ boardFieldMetadataId: string | null;
+ columnId: string;
+}) => {
+ const { shouldFetchMoreSelector } = useRecordBoard(recordBoardId);
+
+ const shouldFetchMore = useRecoilValue(shouldFetchMoreSelector());
+
+ const { fetchMoreRecords, loading } = useLoadRecordIndexBoardColumn({
+ objectNameSingular,
+ recordBoardId,
+ boardFieldMetadataId,
+ columnFieldSelectValue: boardFieldSelectValue,
+ columnId,
+ });
+
+ useEffect(() => {
+ if (!loading && shouldFetchMore) {
+ fetchMoreRecords?.();
+ }
+ }, [fetchMoreRecords, loading, shouldFetchMore, boardFieldSelectValue]);
+
+ return <>>;
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx
new file mode 100644
index 000000000..d6ca8ddfd
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx
@@ -0,0 +1,50 @@
+import { useRecoilValue } from 'recoil';
+
+import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
+import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
+import { RecordIndexBoardColumnLoaderEffect } from '@/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect';
+import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
+
+type RecordIndexBoardDataLoaderProps = {
+ objectNameSingular: string;
+ recordBoardId: string;
+};
+
+export const RecordIndexBoardDataLoader = ({
+ objectNameSingular,
+ recordBoardId,
+}: RecordIndexBoardDataLoaderProps) => {
+ const { objectMetadataItem } = useObjectMetadataItem({
+ objectNameSingular,
+ });
+
+ const recordIndexKanbanFieldMetadataId = useRecoilValue(
+ recordIndexKanbanFieldMetadataIdState,
+ );
+
+ const recordIndexKanbanFieldMetadataItem = objectMetadataItem.fields.find(
+ (field) => field.id === recordIndexKanbanFieldMetadataId,
+ );
+
+ const possibleKanbanSelectFieldValues =
+ recordIndexKanbanFieldMetadataItem?.options ?? [];
+
+ const { columnIdsState } = useRecordBoardStates(recordBoardId);
+
+ // TODO: we should make sure there's no way to have a mismatch between columnIds and possibleKanbanSelectFieldValues order
+ const columnIds = useRecoilValue(columnIdsState);
+
+ return (
+ <>
+ {possibleKanbanSelectFieldValues.map((option, index) => (
+
+ ))}
+ >
+ );
+};
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/RecordIndexBoardDataLoaderEffect.tsx
similarity index 79%
rename from packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainerEffect.tsx
rename to packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx
index d2e0d279a..b7b31f73c 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/RecordIndexBoardDataLoaderEffect.tsx
@@ -1,51 +1,62 @@
import { useCallback, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
-import { useRecoilValue } from 'recoil';
+import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useRecordActionBar } from '@/object-record/record-action-bar/hooks/useRecordActionBar';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard';
import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
-import { useLoadRecordIndexBoard } from '@/object-record/record-index/hooks/useLoadRecordIndexBoard';
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
+import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState';
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
import { computeRecordBoardColumnDefinitionsFromObjectMetadata } from '@/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
-type RecordIndexBoardContainerEffectProps = {
+type RecordIndexBoardDataLoaderEffectProps = {
objectNameSingular: string;
recordBoardId: string;
- viewBarId: string;
};
-export const RecordIndexBoardContainerEffect = ({
+export const RecordIndexBoardDataLoaderEffect = ({
objectNameSingular,
recordBoardId,
- viewBarId,
-}: RecordIndexBoardContainerEffectProps) => {
+}: RecordIndexBoardDataLoaderEffectProps) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
+ const recordIndexFieldDefinitions = useRecoilValue(
+ recordIndexFieldDefinitionsState,
+ );
+
+ const recordIndexKanbanFieldMetadataId = useRecoilValue(
+ recordIndexKanbanFieldMetadataIdState,
+ );
+
+ const recordIndexIsCompactModeActive = useRecoilValue(
+ recordIndexIsCompactModeActiveState,
+ );
+
+ const { isCompactModeActiveState } = useRecordBoard(recordBoardId);
+
+ const setIsCompactModeActive = useSetRecoilState(isCompactModeActiveState);
+
+ useEffect(() => {
+ setIsCompactModeActive(recordIndexIsCompactModeActive);
+ }, [recordIndexIsCompactModeActive, setIsCompactModeActive]);
+
const {
setColumns,
setObjectSingularName,
selectedRecordIdsSelector,
setFieldDefinitions,
- shouldFetchMoreSelector,
setKanbanFieldMetadataName,
} = useRecordBoard(recordBoardId);
- const { fetchMoreRecords, loading } = useLoadRecordIndexBoard({
- objectNameSingular,
- recordBoardId,
- viewBarId,
- });
-
- const recordIndexKanbanFieldMetadataId = useRecoilValue(
- recordIndexKanbanFieldMetadataIdState,
- );
+ useEffect(() => {
+ setFieldDefinitions(recordIndexFieldDefinitions);
+ }, [recordIndexFieldDefinitions, setFieldDefinitions]);
const navigate = useNavigate();
@@ -53,21 +64,6 @@ export const RecordIndexBoardContainerEffect = ({
navigate(`/settings/objects/${objectMetadataItem.namePlural}`);
}, [navigate, objectMetadataItem.namePlural]);
- const columnDefinitions =
- computeRecordBoardColumnDefinitionsFromObjectMetadata(
- objectMetadataItem,
- recordIndexKanbanFieldMetadataId ?? '',
- navigateToSelectSettings,
- );
-
- const shouldFetchMore = useRecoilValue(shouldFetchMoreSelector());
-
- useEffect(() => {
- if (!loading && shouldFetchMore) {
- fetchMoreRecords?.();
- }
- }, [columnDefinitions, fetchMoreRecords, loading, shouldFetchMore]);
-
const { resetRecordSelection } = useRecordBoardSelection(recordBoardId);
useEffect(() => {
@@ -90,10 +86,6 @@ export const RecordIndexBoardContainerEffect = ({
setColumns,
]);
- const recordIndexFieldDefinitions = useRecoilValue(
- recordIndexFieldDefinitionsState,
- );
-
useEffect(() => {
setFieldDefinitions(recordIndexFieldDefinitions);
}, [objectMetadataItem, setFieldDefinitions, recordIndexFieldDefinitions]);
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 3deaaa9af..e74b9e9be 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
@@ -5,7 +5,8 @@ import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/u
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { RecordIndexBoardContainer } from '@/object-record/record-index/components/RecordIndexBoardContainer';
-import { RecordIndexBoardContainerEffect } from '@/object-record/record-index/components/RecordIndexBoardContainerEffect';
+import { RecordIndexBoardDataLoader } from '@/object-record/record-index/components/RecordIndexBoardDataLoader';
+import { RecordIndexBoardDataLoaderEffect } from '@/object-record/record-index/components/RecordIndexBoardDataLoaderEffect';
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';
@@ -170,10 +171,13 @@ export const RecordIndexContainer = ({
objectNameSingular={objectNameSingular}
createRecord={createRecord}
/>
-
+
>
)}
diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts
new file mode 100644
index 000000000..f15f04fab
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts
@@ -0,0 +1,81 @@
+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 { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
+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';
+
+type UseLoadRecordIndexBoardProps = {
+ objectNameSingular: string;
+ boardFieldMetadataId: string | null;
+ recordBoardId: string;
+ columnFieldSelectValue: string;
+ columnId: string;
+};
+
+export const useLoadRecordIndexBoardColumn = ({
+ objectNameSingular,
+ boardFieldMetadataId,
+ recordBoardId,
+ columnFieldSelectValue,
+ columnId,
+}: UseLoadRecordIndexBoardProps) => {
+ const { objectMetadataItem } = useObjectMetadataItem({
+ objectNameSingular,
+ });
+ const { setRecordIdsForColumn } = useRecordBoard(recordBoardId);
+ const { setRecords: setRecordsInStore } = useSetRecordInStore();
+
+ const recordIndexFilters = useRecoilValue(recordIndexFiltersState);
+ const recordIndexSorts = useRecoilValue(recordIndexSortsState);
+ const requestFilters = turnObjectDropdownFilterIntoQueryFilter(
+ recordIndexFilters,
+ objectMetadataItem?.fields ?? [],
+ );
+ const orderBy = turnSortsIntoOrderBy(objectMetadataItem, recordIndexSorts);
+
+ const recordGqlFields = useRecordBoardRecordGqlFields({
+ objectMetadataItem,
+ recordBoardId,
+ });
+
+ const recordIndexKanbanFieldMetadataItem = objectMetadataItem.fields.find(
+ (field) => field.id === boardFieldMetadataId,
+ );
+
+ const filter = {
+ ...requestFilters,
+ [recordIndexKanbanFieldMetadataItem?.name ?? '']: {
+ in: [columnFieldSelectValue],
+ },
+ };
+
+ const { records, loading, fetchMoreRecords, queryStateIdentifier } =
+ useFindManyRecords({
+ objectNameSingular,
+ filter,
+ orderBy,
+ recordGqlFields,
+ });
+
+ useEffect(() => {
+ setRecordIdsForColumn(columnId, records);
+ }, [records, setRecordIdsForColumn, columnId]);
+
+ useEffect(() => {
+ setRecordsInStore(records);
+ }, [records, setRecordsInStore]);
+
+ return {
+ records,
+ loading,
+ fetchMoreRecords,
+ queryStateIdentifier,
+ };
+};
diff --git a/packages/twenty-front/src/modules/object-record/utils/sortRecordsByPosition.ts b/packages/twenty-front/src/modules/object-record/utils/sortRecordsByPosition.ts
new file mode 100644
index 000000000..e6ab0e63c
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/utils/sortRecordsByPosition.ts
@@ -0,0 +1,19 @@
+import { ObjectRecord } from '@/object-record/types/ObjectRecord';
+
+export const sortRecordsByPosition = (
+ record1: ObjectRecord,
+ record2: ObjectRecord,
+) => {
+ if (
+ typeof record1.position == 'number' &&
+ typeof record2.position == 'number'
+ ) {
+ return record1.position - record2.position;
+ } else if (record1.position === 'first' || record2.position === 'last') {
+ return -1;
+ } else if (record2.position === 'first' || record1.position === 'last') {
+ return 1;
+ } else {
+ return 0;
+ }
+};