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 35a6ef792a..dd67a17e33 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 @@ -14,6 +14,7 @@ import { RecordIndexOptionsDropdown } from '@/object-record/record-index/options import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; +import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState'; import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider'; @@ -62,6 +63,9 @@ export const RecordIndexContainer = ({ const setRecordIndexFilters = useSetRecoilState(recordIndexFiltersState); const setRecordIndexSorts = useSetRecoilState(recordIndexSortsState); + const setRecordIndexIsCompactModeActive = useSetRecoilState( + recordIndexIsCompactModeActiveState, + ); const { setTableFilters, setTableSorts, setTableColumns } = useRecordTable({ recordTableId: recordIndexId, @@ -118,6 +122,9 @@ export const RecordIndexContainer = ({ onViewTypeChange={(viewType: ViewType) => { setRecordIndexViewType(viewType); }} + onViewCompactModeChange={(isCompactModeActive: boolean) => { + setRecordIndexIsCompactModeActive(isCompactModeActive); + }} /> { setRecordIdsInBoard(records); }, [records, setRecordIdsInBoard]); @@ -71,6 +83,10 @@ export const useLoadRecordIndexBoard = ({ setEntityCountInCurrentView(records.length); }, [records.length, setEntityCountInCurrentView]); + useEffect(() => { + setIsCompactModeActive(recordIndexIsCompactModeActive); + }, [recordIndexIsCompactModeActive, setIsCompactModeActive]); + return { records, loading, diff --git a/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx b/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx index ff79b34e30..f731724e7a 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx @@ -93,7 +93,7 @@ export const RecordIndexOptionsDropdownContent = ({ handleReorderBoardFields, handleBoardFieldVisibilityChange, isCompactModeActive, - setIsCompactModeActive, + setAndPersistIsCompactModeActive, } = useRecordIndexOptionsForBoard({ objectNameSingular, recordBoardId: recordIndexId, @@ -184,7 +184,12 @@ export const RecordIndexOptionsDropdownContent = ({ + setAndPersistIsCompactModeActive( + !isCompactModeActive, + currentView, + ) + } toggled={isCompactModeActive} text="Compact view" toggleSize="small" diff --git a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard.ts b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard.ts index d4e5d9f73d..6d27f8f1f4 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard.ts @@ -11,6 +11,8 @@ import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/s import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns'; import { useViewFields } from '@/views/hooks/internal/useViewFields'; +import { useViews } from '@/views/hooks/internal/useViews'; +import { GraphQLView } from '@/views/types/GraphQLView'; type useRecordIndexOptionsForBoardParams = { objectNameSingular: string; @@ -27,6 +29,7 @@ export const useRecordIndexOptionsForBoard = ({ useRecoilState(recordIndexFieldDefinitionsState); const { persistViewFields } = useViewFields(viewBarId); + const { updateView } = useViews(viewBarId); const { getIsCompactModeActiveState } = useRecordBoard(recordBoardId); const [isCompactModeActive, setIsCompactModeActive] = useRecoilState( @@ -164,12 +167,24 @@ export const useRecordIndexOptionsForBoard = ({ ], ); + const setAndPersistIsCompactModeActive = useCallback( + (isCompactModeActive: boolean, view: GraphQLView | undefined) => { + if (!view) return; + setIsCompactModeActive(isCompactModeActive); + updateView({ + ...view, + isCompact: isCompactModeActive, + }); + }, + [setIsCompactModeActive, updateView], + ); + return { handleReorderBoardFields, handleBoardFieldVisibilityChange, visibleBoardFields, hiddenBoardFields, isCompactModeActive, - setIsCompactModeActive, + setAndPersistIsCompactModeActive, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexIsCompactModeActiveState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexIsCompactModeActiveState.ts new file mode 100644 index 0000000000..e0fba89404 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexIsCompactModeActiveState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const recordIndexIsCompactModeActiveState = atom({ + key: 'recordIndexIsCompactModeActiveState', + default: false, +}); diff --git a/packages/twenty-front/src/modules/views/components/ViewBar.tsx b/packages/twenty-front/src/modules/views/components/ViewBar.tsx index c695e77bf9..2ee4be3f2e 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBar.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBar.tsx @@ -32,6 +32,9 @@ export type ViewBarProps = { onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise; onViewFieldsChange?: (fields: ViewField[]) => void | Promise; onViewTypeChange?: (viewType: ViewType) => void | Promise; + onViewCompactModeChange?: ( + isCompactModeActive: boolean, + ) => void | Promise; }; export const ViewBar = ({ @@ -43,6 +46,7 @@ export const ViewBar = ({ onViewFiltersChange, onViewSortsChange, onViewTypeChange, + onViewCompactModeChange, }: ViewBarProps) => { const { openDropdown: openOptionsDropdownButton } = useDropdown( optionsDropdownScopeId, @@ -62,6 +66,7 @@ export const ViewBar = ({ onViewFiltersChange={onViewFiltersChange} onViewSortsChange={onViewSortsChange} onViewTypeChange={onViewTypeChange} + onViewCompactModeChange={onViewCompactModeChange} > { onViewFiltersChangeState, onViewSortsChangeState, onViewTypeChangeState, + onViewCompactModeChangeState, savedViewFieldsByKeySelector, savedViewFieldsState, savedViewFiltersByKeySelector, @@ -74,6 +75,7 @@ export const useViewScopedStates = (args?: { viewScopeId?: string }) => { onViewFiltersChangeState, onViewSortsChangeState, onViewTypeChangeState, + onViewCompactModeChangeState, savedViewFieldsByKeySelector, savedViewFieldsState, savedViewFiltersByKeySelector, diff --git a/packages/twenty-front/src/modules/views/hooks/internal/useViews.ts b/packages/twenty-front/src/modules/views/hooks/internal/useViews.ts index 8b2fdf05a7..8bc05a85a7 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/useViews.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/useViews.ts @@ -54,6 +54,7 @@ export const useViews = (scopeId: string) => { input: { id: view.id, name: view.name, + isCompact: view.isCompact, }, }, refetchQueries: [findManyQuery], diff --git a/packages/twenty-front/src/modules/views/hooks/useViewBar.ts b/packages/twenty-front/src/modules/views/hooks/useViewBar.ts index 2111217a2e..af1500336d 100644 --- a/packages/twenty-front/src/modules/views/hooks/useViewBar.ts +++ b/packages/twenty-front/src/modules/views/hooks/useViewBar.ts @@ -236,7 +236,7 @@ export const useViewBar = (props?: UseViewProps) => { (viewId: string) => { setCurrentViewId?.(viewId); - const { currentView, onViewTypeChange } = + const { currentView, onViewTypeChange, onViewCompactModeChange } = getViewScopedStateValuesFromSnapshot({ snapshot, viewScopeId: scopeId, @@ -248,6 +248,7 @@ export const useViewBar = (props?: UseViewProps) => { } onViewTypeChange?.(currentView.type); + onViewCompactModeChange?.(currentView.isCompact); loadViewFields(currentView.viewFields, viewId); loadViewFilters(currentView.viewFilters, viewId); loadViewSorts(currentView.viewSorts, viewId); diff --git a/packages/twenty-front/src/modules/views/scopes/ViewScope.tsx b/packages/twenty-front/src/modules/views/scopes/ViewScope.tsx index 899d49ecc0..2f83a18238 100644 --- a/packages/twenty-front/src/modules/views/scopes/ViewScope.tsx +++ b/packages/twenty-front/src/modules/views/scopes/ViewScope.tsx @@ -16,6 +16,9 @@ type ViewScopeProps = { onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise; onViewFieldsChange?: (fields: ViewField[]) => void | Promise; onViewTypeChange?: (viewType: ViewType) => void | Promise; + onViewCompactModeChange?: ( + isCompactModeActive: boolean, + ) => void | Promise; }; export const ViewScope = ({ @@ -25,6 +28,7 @@ export const ViewScope = ({ onViewFiltersChange, onViewFieldsChange, onViewTypeChange, + onViewCompactModeChange, }: ViewScopeProps) => { return ( {children} diff --git a/packages/twenty-front/src/modules/views/scopes/init-effect/ViewScopeInitEffect.tsx b/packages/twenty-front/src/modules/views/scopes/init-effect/ViewScopeInitEffect.tsx index 6ea16dbb2c..2452eab20d 100644 --- a/packages/twenty-front/src/modules/views/scopes/init-effect/ViewScopeInitEffect.tsx +++ b/packages/twenty-front/src/modules/views/scopes/init-effect/ViewScopeInitEffect.tsx @@ -13,6 +13,9 @@ type ViewScopeInitEffectProps = { onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise; onViewFieldsChange?: (fields: ViewField[]) => void | Promise; onViewTypeChange?: (viewType: ViewType) => void | Promise; + onViewCompactModeChange?: ( + isCompactModeActive: boolean, + ) => void | Promise; }; export const ViewScopeInitEffect = ({ @@ -20,29 +23,37 @@ export const ViewScopeInitEffect = ({ onViewFiltersChange, onViewFieldsChange, onViewTypeChange, + onViewCompactModeChange, }: ViewScopeInitEffectProps) => { const { onViewFieldsChangeState, onViewFiltersChangeState, onViewSortsChangeState, onViewTypeChangeState, + onViewCompactModeChangeState, } = useViewScopedStates(); const setOnViewSortsChange = useSetRecoilState(onViewSortsChangeState); const setOnViewFiltersChange = useSetRecoilState(onViewFiltersChangeState); const setOnViewFieldsChange = useSetRecoilState(onViewFieldsChangeState); const setOnViewTypeChange = useSetRecoilState(onViewTypeChangeState); + const setOnViewCompactModeChange = useSetRecoilState( + onViewCompactModeChangeState, + ); useEffect(() => { setOnViewSortsChange(() => onViewSortsChange); setOnViewFiltersChange(() => onViewFiltersChange); setOnViewFieldsChange(() => onViewFieldsChange); setOnViewTypeChange(() => onViewTypeChange); + setOnViewCompactModeChange(() => onViewCompactModeChange); }, [ + onViewCompactModeChange, onViewFieldsChange, onViewFiltersChange, onViewSortsChange, onViewTypeChange, + setOnViewCompactModeChange, setOnViewFieldsChange, setOnViewFiltersChange, setOnViewSortsChange, diff --git a/packages/twenty-front/src/modules/views/states/onViewCompactModeChangeScopeState.ts b/packages/twenty-front/src/modules/views/states/onViewCompactModeChangeScopeState.ts new file mode 100644 index 0000000000..c1e18cbaad --- /dev/null +++ b/packages/twenty-front/src/modules/views/states/onViewCompactModeChangeScopeState.ts @@ -0,0 +1,8 @@ +import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap'; + +export const onViewCompactModeChangeScopeState = createStateScopeMap< + ((isCompactModeActive: boolean) => void | Promise) | undefined +>({ + key: 'onViewCompactModeChangeScopeState', + defaultValue: undefined, +}); diff --git a/packages/twenty-front/src/modules/views/types/GraphQLView.ts b/packages/twenty-front/src/modules/views/types/GraphQLView.ts index 5aa4d2e629..2562079d8f 100644 --- a/packages/twenty-front/src/modules/views/types/GraphQLView.ts +++ b/packages/twenty-front/src/modules/views/types/GraphQLView.ts @@ -8,6 +8,7 @@ export type GraphQLView = { name: string; type: ViewType; objectMetadataId: string; + isCompact: boolean; viewFields: ViewField[]; viewFilters: ViewFilter[]; viewSorts: ViewSort[]; diff --git a/packages/twenty-front/src/modules/views/utils/getViewScopedStateValuesFromSnapshot.ts b/packages/twenty-front/src/modules/views/utils/getViewScopedStateValuesFromSnapshot.ts index 4c01f475c4..3d7b749f01 100644 --- a/packages/twenty-front/src/modules/views/utils/getViewScopedStateValuesFromSnapshot.ts +++ b/packages/twenty-front/src/modules/views/utils/getViewScopedStateValuesFromSnapshot.ts @@ -42,6 +42,7 @@ export const getViewScopedStateValuesFromSnapshot = ({ onViewFiltersChangeState, onViewSortsChangeState, onViewTypeChangeState, + onViewCompactModeChangeState, savedViewFieldsByKeySelector, savedViewFieldsState, savedViewFiltersByKeySelector, @@ -87,6 +88,10 @@ export const getViewScopedStateValuesFromSnapshot = ({ onViewFiltersChange: getSnapshotValue(snapshot, onViewFiltersChangeState), onViewSortsChange: getSnapshotValue(snapshot, onViewSortsChangeState), onViewTypeChange: getSnapshotValue(snapshot, onViewTypeChangeState), + onViewCompactModeChange: getSnapshotValue( + snapshot, + onViewCompactModeChangeState, + ), savedViewFieldsByKey: getSnapshotValue( snapshot, savedViewFieldsByKeySelector, diff --git a/packages/twenty-front/src/modules/views/utils/internal/getViewScopedStates.ts b/packages/twenty-front/src/modules/views/utils/internal/getViewScopedStates.ts index 42e0e4d01b..eb47990c95 100644 --- a/packages/twenty-front/src/modules/views/utils/internal/getViewScopedStates.ts +++ b/packages/twenty-front/src/modules/views/utils/internal/getViewScopedStates.ts @@ -3,6 +3,7 @@ import { getScopedSelectorDeprecated } from '@/ui/utilities/recoil-scope/utils/g import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated'; import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState'; import { isPersistingViewScopedState } from '@/views/states/isPersistingViewScopedState'; +import { onViewCompactModeChangeScopeState } from '@/views/states/onViewCompactModeChangeScopeState'; import { onViewTypeChangeScopedState } from '@/views/states/onViewTypeChangeScopedState'; import { currentViewScopedSelector } from '@/views/states/selectors/currentViewScopedSelector'; @@ -170,6 +171,11 @@ export const getViewScopedStates = ({ viewScopeId, ); + const onViewCompactModeChangeState = getScopedStateDeprecated( + onViewCompactModeChangeScopeState, + viewScopeId, + ); + const currentViewIdState = getScopedStateDeprecated( currentViewIdScopedState, viewScopeId, @@ -214,5 +220,6 @@ export const getViewScopedStates = ({ onViewFiltersChangeState, onViewFieldsChangeState, onViewTypeChangeState, + onViewCompactModeChangeState, }; }; diff --git a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/view.object-metadata.ts b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/view.object-metadata.ts index 4cc58d44c8..e0d70a79dc 100644 --- a/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/view.object-metadata.ts +++ b/packages/twenty-server/src/workspace/workspace-sync-metadata/standard-objects/view.object-metadata.ts @@ -41,6 +41,14 @@ export class ViewObjectMetadata extends BaseObjectMetadata { }) type: string; + @FieldMetadata({ + type: FieldMetadataType.BOOLEAN, + label: 'Compact View', + description: 'Describes if the view is in compact mode', + defaultValue: { value: false }, + }) + isCompact: boolean; + @FieldMetadata({ type: FieldMetadataType.RELATION, label: 'View Fields',