From 8918d05b92bb3bee9dd02cf0bd20d6d63870ced9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Magrin?= Date: Mon, 16 Dec 2024 16:48:16 +0100 Subject: [PATCH 01/13] feat: hide empty record group default true --- .../hooks/useRecordGroupVisibility.ts | 77 ++----------------- .../visibleRecordGroupIdsComponentSelector.ts | 25 ++++++ ...ecordIndexRecordGroupHideComponentState.ts | 2 +- 3 files changed, 31 insertions(+), 73 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts index 734dc61c86c9..405d637e2d15 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts @@ -1,15 +1,10 @@ import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; -import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState'; -import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; -import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups'; -import { mapRecordGroupDefinitionsToViewGroups } from '@/views/utils/mapRecordGroupDefinitionsToViewGroups'; import { recordGroupDefinitionToViewGroup } from '@/views/utils/recordGroupDefinitionToViewGroup'; import { useRecoilCallback } from 'recoil'; -import { isDefined } from '~/utils/isDefined'; type UseRecordGroupVisibilityParams = { viewBarId: string; @@ -18,20 +13,10 @@ type UseRecordGroupVisibilityParams = { export const useRecordGroupVisibility = ({ viewBarId, }: UseRecordGroupVisibilityParams) => { - const recordIndexRecordGroupIdsState = useRecoilComponentCallbackStateV2( - recordGroupIdsComponentState, - ); - - const recordIndexRecordIdsByGroupFamilyState = - useRecoilComponentCallbackStateV2( - recordIndexRecordIdsByGroupComponentFamilyState, - viewBarId, - ); - const objectOptionsDropdownRecordGroupHideState = useRecoilComponentCallbackStateV2(recordIndexRecordGroupHideComponentState); - const { saveViewGroup, saveViewGroups } = useSaveCurrentViewGroups(viewBarId); + const { saveViewGroup } = useSaveCurrentViewGroups(viewBarId); const handleVisibilityChange = useRecoilCallback( ({ set }) => @@ -50,66 +35,14 @@ export const useRecordGroupVisibility = ({ ); const handleHideEmptyRecordGroupChange = useRecoilCallback( - ({ snapshot, set }) => + ({ set }) => async () => { - const updatedRecordGroupDefinitions: RecordGroupDefinition[] = []; - const recordGroupIds = getSnapshotValue( - snapshot, - recordIndexRecordGroupIdsState, - ); - - const currentHideState = getSnapshotValue( - snapshot, + set( objectOptionsDropdownRecordGroupHideState, - ); - const newHideState = !currentHideState; - - set(objectOptionsDropdownRecordGroupHideState, newHideState); - - for (const recordGroupId of recordGroupIds) { - const recordGroup = getSnapshotValue( - snapshot, - recordGroupDefinitionFamilyState(recordGroupId), - ); - - if (!isDefined(recordGroup)) { - throw new Error( - `Record group with id ${recordGroupId} not found in snapshot`, - ); - } - - const recordGroupRowIds = getSnapshotValue( - snapshot, - recordIndexRecordIdsByGroupFamilyState(recordGroupId), - ); - - if (recordGroupRowIds.length > 0) { - continue; - } - - const updatedRecordGroup = { - ...recordGroup, - isVisible: !newHideState, - }; - - set( - recordGroupDefinitionFamilyState(recordGroupId), - updatedRecordGroup, - ); - - updatedRecordGroupDefinitions.push(updatedRecordGroup); - } - - saveViewGroups( - mapRecordGroupDefinitionsToViewGroups(updatedRecordGroupDefinitions), + (currentHideState) => !currentHideState, ); }, - [ - recordIndexRecordGroupIdsState, - objectOptionsDropdownRecordGroupHideState, - saveViewGroups, - recordIndexRecordIdsByGroupFamilyState, - ], + [objectOptionsDropdownRecordGroupHideState], ); return { diff --git a/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts index 9ff30316c246..10daa3f3c6c9 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts @@ -3,7 +3,9 @@ import { recordGroupIdsComponentState } from '@/object-record/record-group/state import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort'; import { recordGroupSortedInsert } from '@/object-record/record-group/utils/recordGroupSortedInsert'; +import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState'; import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; +import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; @@ -27,6 +29,11 @@ export const visibleRecordGroupIdsComponentSelector = createComponentSelectorV2< instanceId, }), ); + const hideEmptyRecordGroup = get( + recordIndexRecordGroupHideComponentState.atomFamily({ + instanceId, + }), + ); const result: RecordGroupDefinition[] = []; @@ -49,6 +56,24 @@ export const visibleRecordGroupIdsComponentSelector = createComponentSelectorV2< const recordGroupDefinition = get( recordGroupDefinitionFamilyState(recordGroupId), ); + const recordIds = get( + recordIndexRecordIdsByGroupComponentFamilyState.atomFamily({ + instanceId, + familyKey: recordGroupId, + }), + ); + + if (!isDefined(recordGroupDefinition)) { + continue; + } + + if (hideEmptyRecordGroup && recordIds.length === 0) { + continue; + } + + if (!recordGroupDefinition.isVisible) { + continue; + } if ( isDefined(recordGroupDefinition) && diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordGroupHideComponentState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordGroupHideComponentState.ts index 3500e331e57e..bc571f675526 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordGroupHideComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordGroupHideComponentState.ts @@ -4,6 +4,6 @@ import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewCompon export const recordIndexRecordGroupHideComponentState = createComponentStateV2({ key: 'recordIndexRecordGroupHideComponentState', - defaultValue: false, + defaultValue: true, componentInstanceContext: ViewComponentInstanceContext, }); From 14a75d566f30525a9a07bafdf750efb717022faf Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Tue, 17 Dec 2024 11:44:03 +0100 Subject: [PATCH 02/13] Fix re-render view groups --- .../record-group/hooks/useSetRecordGroup.ts | 6 +++--- ...TableBodyRecordGroupDragDropContextProvider.tsx | 14 ++++++-------- .../utils/mapViewGroupsToRecordGroupDefinitions.ts | 3 +-- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts index 37dc16c1d578..96b5d0184f11 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts @@ -24,7 +24,7 @@ export const useSetRecordGroup = (viewId?: string) => { return useRecoilCallback( ({ snapshot, set }) => (recordGroups: RecordGroupDefinition[]) => { - const currentRecordGroupId = getSnapshotValue( + const currentRecordGroupIds = getSnapshotValue( snapshot, recordIndexRecordGroupIdsState, ); @@ -61,7 +61,7 @@ export const useSetRecordGroup = (viewId?: string) => { const recordGroupIds = recordGroups.map(({ id }) => id); // Get ids that has been removed between the current and new record groups - const removedRecordGroupIds = currentRecordGroupId.filter( + const removedRecordGroupIds = currentRecordGroupIds.filter( (id) => !recordGroupIds.includes(id), ); @@ -70,7 +70,7 @@ export const useSetRecordGroup = (viewId?: string) => { set(recordGroupDefinitionFamilyState(id), undefined); }); - if (isDeeplyEqual(currentRecordGroupId, recordGroupIds)) { + if (isDeeplyEqual(currentRecordGroupIds, recordGroupIds)) { return; } diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx index 98474d1efa9c..977af5b47e15 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx @@ -6,12 +6,12 @@ import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { getDraggedRecordPosition } from '@/object-record/record-board/utils/getDraggedRecordPosition'; import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; +import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { isDefined } from '~/utils/isDefined'; export const RecordTableBodyRecordGroupDragDropContextProvider = ({ @@ -26,11 +26,6 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ objectNameSingular, }); - const { currentViewWithCombinedFiltersAndSorts } = - useGetCurrentView(recordTableId); - - const viewSorts = currentViewWithCombinedFiltersAndSorts?.viewSorts || []; - const setIsRemoveSortingModalOpenState = useSetRecoilState( isRemoveSortingModalOpenState, ); @@ -57,6 +52,10 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ recordGroupDefinitionFamilyState(destinationRecordGroupId), ); + const indexSorts = snapshot + .getLoadable(recordIndexSortsState) + .getValue(); + if (!isDefined(destinationRecordGroup)) { throw new Error('Record group is not defined'); } @@ -69,7 +68,7 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ throw new Error('Field metadata is not defined'); } - if (viewSorts.length > 0) { + if (indexSorts.length > 0) { setIsRemoveSortingModalOpenState(true); return; } @@ -128,7 +127,6 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ }, [ objectMetadataItem.fields, - viewSorts.length, recordIdsByGroupFamilyState, updateOneRow, setIsRemoveSortingModalOpenState, diff --git a/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts b/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts index 73f4782479be..523dc70488bd 100644 --- a/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts +++ b/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts @@ -1,4 +1,3 @@ -import { v4 } from 'uuid'; import { isDefined } from '~/utils/isDefined'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; @@ -65,7 +64,7 @@ export const mapViewGroupsToRecordGroupDefinitions = ({ ); const noValueColumn = { - id: viewGroup?.id ?? v4(), + id: viewGroup?.id ?? '20202020-c05f-46c9-ae1e-2b3c5c702049', title: 'No Value', type: RecordGroupDefinitionType.NoValue, value: null, From 0c327bb78b10770f7290b8efa5d3c1c6d20726e3 Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Tue, 17 Dec 2024 12:04:22 +0100 Subject: [PATCH 03/13] Fix unsused recordTableId --- .../RecordTableBodyRecordGroupDragDropContextProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx index 977af5b47e15..55af955e5046 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx @@ -19,7 +19,7 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ }: { children: ReactNode; }) => { - const { objectNameSingular, recordTableId, objectMetadataItem } = + const { objectNameSingular, objectMetadataItem } = useRecordTableContextOrThrow(); const { updateOneRecord: updateOneRow } = useUpdateOneRecord({ From c303b79d77c5193f6e667dd831d71c7648065229 Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Tue, 17 Dec 2024 17:01:29 +0100 Subject: [PATCH 04/13] Make footer sticky --- .../record-table/components/RecordTable.tsx | 2 +- .../components/RecordTableFooter.tsx | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx index 72d874499ae1..090e44006d6c 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx @@ -88,7 +88,7 @@ export const RecordTable = () => { ) : ( <> - + {!hasRecordGroups ? ( diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx index b0fb27dd6465..832fe7a5fc2a 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx @@ -59,15 +59,13 @@ const StyledTableFoot = styled.thead` } } - &.header-sticky { - th { - position: sticky; - top: 0; - z-index: 5; - } + tr { + position: sticky; + bottom: 0; + z-index: 5; } - &.header-sticky.first-columns-sticky { + &.first-columns-sticky { th:nth-of-type(1), th:nth-of-type(2), th:nth-of-type(3) { From 0859d50fc6d5c4243e59f79f1a780f20c3810514 Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Wed, 18 Dec 2024 15:20:04 +0100 Subject: [PATCH 05/13] Improve display of aggregate values --- ...ardColumnHeaderAggregateDropdownButton.tsx | 6 +- ...useAggregateRecordsForRecordBoardColumn.ts | 4 +- .../computeAggregateValueAndLabel.test.ts | 77 ++++++++++++++++++- .../utils/computeAggregateValueAndLabel.ts | 34 ++++++-- .../RecordTableColumnFooterAggregateValue.tsx | 49 +++++++----- .../components/RecordTableFooter.tsx | 3 +- .../components/RecordTableFooterCell.tsx | 21 +++-- .../components/RecordTableHeader.tsx | 4 +- .../scroll/contexts/ScrollWrapperContexts.tsx | 7 +- ...ct-records-to-graphql-connection.helper.ts | 5 +- 10 files changed, 165 insertions(+), 45 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownButton.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownButton.tsx index dd12c11f72fb..5b945ba8b353 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownButton.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownButton.tsx @@ -1,7 +1,6 @@ import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton'; import styled from '@emotion/styled'; import { AppTooltip, Tag, TooltipDelay } from 'twenty-ui'; -import { formatNumber } from '~/utils/format/number'; const StyledButton = styled(StyledHeaderDropdownButton)` padding: 0; @@ -19,10 +18,7 @@ export const RecordBoardColumnHeaderAggregateDropdownButton = ({ return (
- + { skip: !isAggregateQueryEnabled, }); - const { value, label } = computeAggregateValueAndLabel({ + const { value, labelWithFieldName } = computeAggregateValueAndLabel({ data, objectMetadataItem, fieldMetadataId: recordIndexKanbanAggregateOperation?.fieldMetadataId, @@ -84,6 +84,6 @@ export const useAggregateRecordsForRecordBoardColumn = () => { return { aggregateValue: isAggregateQueryEnabled ? value : recordCount, - aggregateLabel: isDefined(value) ? label : undefined, + aggregateLabel: isDefined(value) ? labelWithFieldName : undefined, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/__tests__/computeAggregateValueAndLabel.test.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/__tests__/computeAggregateValueAndLabel.test.ts index 44401248616b..188f82ea4883 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/__tests__/computeAggregateValueAndLabel.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/__tests__/computeAggregateValueAndLabel.test.ts @@ -48,7 +48,80 @@ describe('computeAggregateValueAndLabel', () => { expect(result).toEqual({ value: 2, - label: 'Sum of amount', + label: 'Sum', + labelWithFieldName: 'Sum of amount', + }); + }); + + it('should handle number field as percentage', () => { + const mockObjectMetadataWithPercentageField: ObjectMetadataItem = { + id: '123', + fields: [ + { + id: MOCK_FIELD_ID, + name: 'percentage', + type: FieldMetadataType.Number, + settings: { + type: 'percentage', + }, + } as FieldMetadataItem, + ], + } as ObjectMetadataItem; + + const mockData = { + percentage: { + [AGGREGATE_OPERATIONS.avg]: 0.3, + }, + }; + + const result = computeAggregateValueAndLabel({ + data: mockData, + objectMetadataItem: mockObjectMetadataWithPercentageField, + fieldMetadataId: MOCK_FIELD_ID, + aggregateOperation: AGGREGATE_OPERATIONS.avg, + fallbackFieldName: MOCK_KANBAN_FIELD_NAME, + }); + + expect(result).toEqual({ + value: '30%', + label: 'Average', + labelWithFieldName: 'Average of percentage', + }); + }); + + it('should handle number field with decimals', () => { + const mockObjectMetadataWithDecimalsField: ObjectMetadataItem = { + id: '123', + fields: [ + { + id: MOCK_FIELD_ID, + name: 'decimals', + type: FieldMetadataType.Number, + settings: { + decimals: 2, + }, + } as FieldMetadataItem, + ], + } as ObjectMetadataItem; + + const mockData = { + decimals: { + [AGGREGATE_OPERATIONS.sum]: 0.009, + }, + }; + + const result = computeAggregateValueAndLabel({ + data: mockData, + objectMetadataItem: mockObjectMetadataWithDecimalsField, + fieldMetadataId: MOCK_FIELD_ID, + aggregateOperation: AGGREGATE_OPERATIONS.sum, + fallbackFieldName: MOCK_KANBAN_FIELD_NAME, + }); + + expect(result).toEqual({ + value: '0.01', + label: 'Sum', + labelWithFieldName: 'Sum of decimals', }); }); @@ -87,7 +160,7 @@ describe('computeAggregateValueAndLabel', () => { expect(result).toEqual({ value: undefined, - label: 'Sum of amount', + label: 'Sum', }); }); }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel.ts index b30c673c708a..80beb0e39dec 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel.ts @@ -4,6 +4,8 @@ import { getAggregateOperationLabel } from '@/object-record/record-board/record- import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations'; import isEmpty from 'lodash.isempty'; import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { formatAmount } from '~/utils/format/formatAmount'; +import { formatNumber } from '~/utils/format/number'; import { isDefined } from '~/utils/isDefined'; export const computeAggregateValueAndLabel = ({ @@ -42,12 +44,33 @@ export const computeAggregateValueAndLabel = ({ const aggregateValue = data[field.name]?.[aggregateOperation]; - const value = - isDefined(aggregateValue) && field.type === FieldMetadataType.Currency - ? Number(aggregateValue) / 1_000_000 - : aggregateValue; + let value; - const label = + if (aggregateOperation === AGGREGATE_OPERATIONS.count) { + value = aggregateValue; + } else if (!isDefined(aggregateValue)) { + value = '-'; + } else { + value = Number(aggregateValue); + + switch (field.type) { + case FieldMetadataType.Currency: { + value = formatAmount(value / 1_000_000); + break; + } + + case FieldMetadataType.Number: { + const { decimals, type } = field.settings ?? {}; + value = + type === 'percentage' + ? `${formatNumber(value * 100, decimals)}%` + : formatNumber(value, decimals); + break; + } + } + } + const label = getAggregateOperationLabel(aggregateOperation); + const labelWithFieldName = aggregateOperation === AGGREGATE_OPERATIONS.count ? `${getAggregateOperationLabel(AGGREGATE_OPERATIONS.count)}` : `${getAggregateOperationLabel(aggregateOperation)} of ${field.name}`; @@ -55,5 +78,6 @@ export const computeAggregateValueAndLabel = ({ return { value, label, + labelWithFieldName, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterAggregateValue.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterAggregateValue.tsx index a306c1029e08..2eddb55a35cf 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterAggregateValue.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterAggregateValue.tsx @@ -1,12 +1,7 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useState } from 'react'; -import { - AppTooltip, - IconChevronDown, - isDefined, - TooltipDelay, -} from 'twenty-ui'; +import { IconChevronDown, isDefined } from 'twenty-ui'; const StyledCell = styled.div` align-items: center; @@ -37,6 +32,27 @@ const StyledText = styled.span` z-index: 1; `; +const StyledValueContainer = styled.div` + align-items: center; + display: flex; + flex: 1 0 0; + gap: 4px; + height: 32px; + justify-content: flex-end; + padding: 8px; +`; + +const StyledLabel = styled.div` + align-items: center; + display: flex; + gap: 4px; +`; + +const StyledValue = styled.div` + color: ${({ theme }) => theme.color.gray60}; + flex: 1 0 0; +`; + const StyledIcon = styled(IconChevronDown)` align-items: center; display: flex; @@ -70,20 +86,15 @@ export const RecordTableColumnFooterAggregateValue = ({ {isHovered || isDefined(aggregateValue) || isFirstCell ? ( <> - - {aggregateValue ?? 'Calculate'} - - - {isDefined(aggregateValue) && isDefined(aggregateLabel) && ( - + {isDefined(aggregateValue) ? ( + + {aggregateLabel} + {aggregateValue} + + ) : ( + Calculate )} + ) : ( <> diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx index 832fe7a5fc2a..8e76e751301d 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx @@ -4,6 +4,7 @@ import { MOBILE_VIEWPORT } from 'twenty-ui'; import { TABLE_CELL_CHECKBOX_MIN_WIDTH } from '@/object-record/record-table/record-table-cell/components/RecordTableCellCheckbox'; import { TABLE_CELL_GRIP_WIDTH } from '@/object-record/record-table/record-table-cell/components/RecordTableCellGrip'; import { RecordTableFooterCell } from '@/object-record/record-table/record-table-footer/components/RecordTableFooterCell'; +import { FIRST_TH_WIDTH } from '@/object-record/record-table/record-table-header/components/RecordTableHeader'; import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -11,7 +12,7 @@ const StyledTableFoot = styled.thead` cursor: pointer; th:nth-of-type(1) { - width: 9px; + width: ${FIRST_TH_WIDTH}; left: 0; border-right-color: ${({ theme }) => theme.background.primary}; } diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooterCell.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooterCell.tsx index 4d4e3747f14b..69187789728a 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooterCell.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooterCell.tsx @@ -5,6 +5,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata' import { RecordTableColumnFooterWithDropdown } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnFooterWithDropdown'; import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; +import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { mapArrayToObject } from '~/utils/array/mapArrayToObject'; @@ -85,13 +86,19 @@ export const RecordTableFooterCell = ({ COLUMN_MIN_WIDTH, )} > - - - + + + + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx index ae08ea612f3c..cf33fa7d7134 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx @@ -7,11 +7,13 @@ import { RecordTableHeaderCheckboxColumn } from '@/object-record/record-table/re import { RecordTableHeaderDragDropColumn } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderDragDropColumn'; import { RecordTableHeaderLastColumn } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderLastColumn'; +export const FIRST_TH_WIDTH = '9px'; + const StyledTableHead = styled.thead` cursor: pointer; th:nth-of-type(1) { - width: 9px; + width: ${FIRST_TH_WIDTH}; left: 0; border-right-color: ${({ theme }) => theme.background.primary}; } diff --git a/packages/twenty-front/src/modules/ui/utilities/scroll/contexts/ScrollWrapperContexts.tsx b/packages/twenty-front/src/modules/ui/utilities/scroll/contexts/ScrollWrapperContexts.tsx index 1d83d1ddd94b..c61640e9ebbd 100644 --- a/packages/twenty-front/src/modules/ui/utilities/scroll/contexts/ScrollWrapperContexts.tsx +++ b/packages/twenty-front/src/modules/ui/utilities/scroll/contexts/ScrollWrapperContexts.tsx @@ -18,7 +18,8 @@ export type ContextProviderName = | 'releases' | 'test' | 'showPageActivityContainer' - | 'navigationDrawer'; + | 'navigationDrawer' + | 'aggregateFooterCell'; const createScrollWrapperContext = (id: string) => createContext({ @@ -51,6 +52,8 @@ export const ShowPageActivityContainerScrollWrapperContext = export const NavigationDrawerScrollWrapperContext = createScrollWrapperContext('navigationDrawer'); export const TestScrollWrapperContext = createScrollWrapperContext('test'); +export const AggregateFooterCellScrollWrapperContext = + createScrollWrapperContext('aggregateFooterCell'); export const getContextByProviderName = ( contextProviderName: ContextProviderName, @@ -82,6 +85,8 @@ export const getContextByProviderName = ( return ShowPageActivityContainerScrollWrapperContext; case 'navigationDrawer': return NavigationDrawerScrollWrapperContext; + case 'aggregateFooterCell': + return AggregateFooterCellScrollWrapperContext; default: throw new Error('Context Provider not available'); } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper.ts index b91b0301b26e..777919d2f51c 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper.ts @@ -20,6 +20,7 @@ import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-met import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory'; import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util'; +import { isDefined } from 'src/utils/is-defined'; import { isPlainObject } from 'src/utils/is-plain-object'; export class ObjectRecordsToGraphqlConnectionHelper { @@ -95,7 +96,7 @@ export class ObjectRecordsToGraphqlConnectionHelper { selectedAggregatedFields: Record; objectRecordsAggregatedValues: Record; }) => { - if (!objectRecordsAggregatedValues) { + if (!isDefined(objectRecordsAggregatedValues)) { return {}; } @@ -104,7 +105,7 @@ export class ObjectRecordsToGraphqlConnectionHelper { const aggregatedFieldValue = objectRecordsAggregatedValues[aggregatedFieldName]; - if (!aggregatedFieldValue) { + if (!isDefined(aggregatedFieldValue)) { return acc; } From 7a9a4d9a7a186cd746129623f34bec2357466064 Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Wed, 18 Dec 2024 15:48:11 +0100 Subject: [PATCH 06/13] Revert "feat: hide empty record group default true" This reverts commit 8918d05b92bb3bee9dd02cf0bd20d6d63870ced9. --- .../hooks/useRecordGroupVisibility.ts | 77 +++++++++++++++++-- .../visibleRecordGroupIdsComponentSelector.ts | 25 ------ ...ecordIndexRecordGroupHideComponentState.ts | 2 +- 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts index 405d637e2d15..734dc61c86c9 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts @@ -1,10 +1,15 @@ import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState'; +import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups'; +import { mapRecordGroupDefinitionsToViewGroups } from '@/views/utils/mapRecordGroupDefinitionsToViewGroups'; import { recordGroupDefinitionToViewGroup } from '@/views/utils/recordGroupDefinitionToViewGroup'; import { useRecoilCallback } from 'recoil'; +import { isDefined } from '~/utils/isDefined'; type UseRecordGroupVisibilityParams = { viewBarId: string; @@ -13,10 +18,20 @@ type UseRecordGroupVisibilityParams = { export const useRecordGroupVisibility = ({ viewBarId, }: UseRecordGroupVisibilityParams) => { + const recordIndexRecordGroupIdsState = useRecoilComponentCallbackStateV2( + recordGroupIdsComponentState, + ); + + const recordIndexRecordIdsByGroupFamilyState = + useRecoilComponentCallbackStateV2( + recordIndexRecordIdsByGroupComponentFamilyState, + viewBarId, + ); + const objectOptionsDropdownRecordGroupHideState = useRecoilComponentCallbackStateV2(recordIndexRecordGroupHideComponentState); - const { saveViewGroup } = useSaveCurrentViewGroups(viewBarId); + const { saveViewGroup, saveViewGroups } = useSaveCurrentViewGroups(viewBarId); const handleVisibilityChange = useRecoilCallback( ({ set }) => @@ -35,14 +50,66 @@ export const useRecordGroupVisibility = ({ ); const handleHideEmptyRecordGroupChange = useRecoilCallback( - ({ set }) => + ({ snapshot, set }) => async () => { - set( + const updatedRecordGroupDefinitions: RecordGroupDefinition[] = []; + const recordGroupIds = getSnapshotValue( + snapshot, + recordIndexRecordGroupIdsState, + ); + + const currentHideState = getSnapshotValue( + snapshot, objectOptionsDropdownRecordGroupHideState, - (currentHideState) => !currentHideState, + ); + const newHideState = !currentHideState; + + set(objectOptionsDropdownRecordGroupHideState, newHideState); + + for (const recordGroupId of recordGroupIds) { + const recordGroup = getSnapshotValue( + snapshot, + recordGroupDefinitionFamilyState(recordGroupId), + ); + + if (!isDefined(recordGroup)) { + throw new Error( + `Record group with id ${recordGroupId} not found in snapshot`, + ); + } + + const recordGroupRowIds = getSnapshotValue( + snapshot, + recordIndexRecordIdsByGroupFamilyState(recordGroupId), + ); + + if (recordGroupRowIds.length > 0) { + continue; + } + + const updatedRecordGroup = { + ...recordGroup, + isVisible: !newHideState, + }; + + set( + recordGroupDefinitionFamilyState(recordGroupId), + updatedRecordGroup, + ); + + updatedRecordGroupDefinitions.push(updatedRecordGroup); + } + + saveViewGroups( + mapRecordGroupDefinitionsToViewGroups(updatedRecordGroupDefinitions), ); }, - [objectOptionsDropdownRecordGroupHideState], + [ + recordIndexRecordGroupIdsState, + objectOptionsDropdownRecordGroupHideState, + saveViewGroups, + recordIndexRecordIdsByGroupFamilyState, + ], ); return { diff --git a/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts index abd766868eee..78ead53c8656 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts @@ -3,9 +3,7 @@ import { recordGroupIdsComponentState } from '@/object-record/record-group/state import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort'; import { recordGroupSortedInsert } from '@/object-record/record-group/utils/recordGroupSortedInsert'; -import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState'; import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; -import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; @@ -29,11 +27,6 @@ export const visibleRecordGroupIdsComponentSelector = createComponentSelectorV2< instanceId, }), ); - const hideEmptyRecordGroup = get( - recordIndexRecordGroupHideComponentState.atomFamily({ - instanceId, - }), - ); const result: RecordGroupDefinition[] = []; @@ -56,24 +49,6 @@ export const visibleRecordGroupIdsComponentSelector = createComponentSelectorV2< const recordGroupDefinition = get( recordGroupDefinitionFamilyState(recordGroupId), ); - const recordIds = get( - recordIndexRecordIdsByGroupComponentFamilyState.atomFamily({ - instanceId, - familyKey: recordGroupId, - }), - ); - - if (!isDefined(recordGroupDefinition)) { - continue; - } - - if (hideEmptyRecordGroup && recordIds.length === 0) { - continue; - } - - if (!recordGroupDefinition.isVisible) { - continue; - } recordGroupSortedInsert(result, recordGroupDefinition, comparator); } diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordGroupHideComponentState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordGroupHideComponentState.ts index bc571f675526..3500e331e57e 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordGroupHideComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordGroupHideComponentState.ts @@ -4,6 +4,6 @@ import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewCompon export const recordIndexRecordGroupHideComponentState = createComponentStateV2({ key: 'recordIndexRecordGroupHideComponentState', - defaultValue: true, + defaultValue: false, componentInstanceContext: ViewComponentInstanceContext, }); From 9d524d663605a8dc2c9d7333f766e66d37c5c99e Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Wed, 18 Dec 2024 15:48:19 +0100 Subject: [PATCH 07/13] Revert "Fix re-render view groups" This reverts commit 14a75d566f30525a9a07bafdf750efb717022faf. --- .../record-group/hooks/useSetRecordGroup.ts | 6 +++--- ...TableBodyRecordGroupDragDropContextProvider.tsx | 14 ++++++++------ .../utils/mapViewGroupsToRecordGroupDefinitions.ts | 3 ++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts index 96b5d0184f11..37dc16c1d578 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts @@ -24,7 +24,7 @@ export const useSetRecordGroup = (viewId?: string) => { return useRecoilCallback( ({ snapshot, set }) => (recordGroups: RecordGroupDefinition[]) => { - const currentRecordGroupIds = getSnapshotValue( + const currentRecordGroupId = getSnapshotValue( snapshot, recordIndexRecordGroupIdsState, ); @@ -61,7 +61,7 @@ export const useSetRecordGroup = (viewId?: string) => { const recordGroupIds = recordGroups.map(({ id }) => id); // Get ids that has been removed between the current and new record groups - const removedRecordGroupIds = currentRecordGroupIds.filter( + const removedRecordGroupIds = currentRecordGroupId.filter( (id) => !recordGroupIds.includes(id), ); @@ -70,7 +70,7 @@ export const useSetRecordGroup = (viewId?: string) => { set(recordGroupDefinitionFamilyState(id), undefined); }); - if (isDeeplyEqual(currentRecordGroupIds, recordGroupIds)) { + if (isDeeplyEqual(currentRecordGroupId, recordGroupIds)) { return; } diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx index 55af955e5046..4796217217c7 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx @@ -6,12 +6,12 @@ import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { getDraggedRecordPosition } from '@/object-record/record-board/utils/getDraggedRecordPosition'; import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; -import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { isDefined } from '~/utils/isDefined'; export const RecordTableBodyRecordGroupDragDropContextProvider = ({ @@ -26,6 +26,11 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ objectNameSingular, }); + const { currentViewWithCombinedFiltersAndSorts } = + useGetCurrentView(recordTableId); + + const viewSorts = currentViewWithCombinedFiltersAndSorts?.viewSorts || []; + const setIsRemoveSortingModalOpenState = useSetRecoilState( isRemoveSortingModalOpenState, ); @@ -52,10 +57,6 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ recordGroupDefinitionFamilyState(destinationRecordGroupId), ); - const indexSorts = snapshot - .getLoadable(recordIndexSortsState) - .getValue(); - if (!isDefined(destinationRecordGroup)) { throw new Error('Record group is not defined'); } @@ -68,7 +69,7 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ throw new Error('Field metadata is not defined'); } - if (indexSorts.length > 0) { + if (viewSorts.length > 0) { setIsRemoveSortingModalOpenState(true); return; } @@ -127,6 +128,7 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ }, [ objectMetadataItem.fields, + viewSorts.length, recordIdsByGroupFamilyState, updateOneRow, setIsRemoveSortingModalOpenState, diff --git a/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts b/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts index 523dc70488bd..73f4782479be 100644 --- a/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts +++ b/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts @@ -1,3 +1,4 @@ +import { v4 } from 'uuid'; import { isDefined } from '~/utils/isDefined'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; @@ -64,7 +65,7 @@ export const mapViewGroupsToRecordGroupDefinitions = ({ ); const noValueColumn = { - id: viewGroup?.id ?? '20202020-c05f-46c9-ae1e-2b3c5c702049', + id: viewGroup?.id ?? v4(), title: 'No Value', type: RecordGroupDefinitionType.NoValue, value: null, From fe6bcc3b409a1108e50a01808b064f4b821e82c2 Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Wed, 18 Dec 2024 15:49:41 +0100 Subject: [PATCH 08/13] Fix issue at sign in before initialization --- .../components/RecordTableColumnFooterDropdown.tsx | 7 +++---- .../useAggregateRecordsForRecordTableColumnFooter.tsx | 11 ++++------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterDropdown.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterDropdown.tsx index 2a9b45eced43..9a2df2c584dd 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterDropdown.tsx @@ -30,10 +30,6 @@ export const RecordTableColumnFooterDropdown = ({ (viewField) => viewField.fieldMetadataId === column.fieldMetadataId, ); - if (!currentViewField) { - throw new Error('ViewField not found'); - } - useScopedHotkeys( [Key.Escape], () => { @@ -56,6 +52,9 @@ export const RecordTableColumnFooterDropdown = ({ const handleAggregationChange = ( aggregateOperation: AGGREGATE_OPERATIONS, ) => { + if (!currentViewField) { + throw new Error('ViewField not found'); + } updateViewFieldRecords([ { ...currentViewField, aggregateOperation: aggregateOperation }, ]); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx index 96bb3d7ca169..c9851f04d118 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx @@ -32,13 +32,10 @@ export const useAggregateRecordsForRecordTableColumnFooter = ( recordIndexViewFilterGroups, ); - const viewFieldId = currentViewWithSavedFiltersAndSorts?.viewFields?.find( - (viewField) => viewField.fieldMetadataId === fieldMetadataId, - )?.id; - - if (!viewFieldId) { - throw new Error('ViewField not found'); - } + const viewFieldId = + currentViewWithSavedFiltersAndSorts?.viewFields?.find( + (viewField) => viewField.fieldMetadataId === fieldMetadataId, + )?.id ?? ''; const aggregateOperationForViewField = useRecoilValue( aggregateOperationForViewFieldState({ viewFieldId: viewFieldId }), From e4a82467040a86a945020ee6f37eebe7233ccea7 Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Wed, 18 Dec 2024 17:52:38 +0100 Subject: [PATCH 09/13] revert changes from previous PR --- .../hooks/useRecordGroupVisibility.ts | 77 ++----------------- .../visibleRecordGroupIdsComponentSelector.ts | 25 ++++++ 2 files changed, 30 insertions(+), 72 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts index 734dc61c86c9..405d637e2d15 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts @@ -1,15 +1,10 @@ import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; -import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState'; -import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; -import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups'; -import { mapRecordGroupDefinitionsToViewGroups } from '@/views/utils/mapRecordGroupDefinitionsToViewGroups'; import { recordGroupDefinitionToViewGroup } from '@/views/utils/recordGroupDefinitionToViewGroup'; import { useRecoilCallback } from 'recoil'; -import { isDefined } from '~/utils/isDefined'; type UseRecordGroupVisibilityParams = { viewBarId: string; @@ -18,20 +13,10 @@ type UseRecordGroupVisibilityParams = { export const useRecordGroupVisibility = ({ viewBarId, }: UseRecordGroupVisibilityParams) => { - const recordIndexRecordGroupIdsState = useRecoilComponentCallbackStateV2( - recordGroupIdsComponentState, - ); - - const recordIndexRecordIdsByGroupFamilyState = - useRecoilComponentCallbackStateV2( - recordIndexRecordIdsByGroupComponentFamilyState, - viewBarId, - ); - const objectOptionsDropdownRecordGroupHideState = useRecoilComponentCallbackStateV2(recordIndexRecordGroupHideComponentState); - const { saveViewGroup, saveViewGroups } = useSaveCurrentViewGroups(viewBarId); + const { saveViewGroup } = useSaveCurrentViewGroups(viewBarId); const handleVisibilityChange = useRecoilCallback( ({ set }) => @@ -50,66 +35,14 @@ export const useRecordGroupVisibility = ({ ); const handleHideEmptyRecordGroupChange = useRecoilCallback( - ({ snapshot, set }) => + ({ set }) => async () => { - const updatedRecordGroupDefinitions: RecordGroupDefinition[] = []; - const recordGroupIds = getSnapshotValue( - snapshot, - recordIndexRecordGroupIdsState, - ); - - const currentHideState = getSnapshotValue( - snapshot, + set( objectOptionsDropdownRecordGroupHideState, - ); - const newHideState = !currentHideState; - - set(objectOptionsDropdownRecordGroupHideState, newHideState); - - for (const recordGroupId of recordGroupIds) { - const recordGroup = getSnapshotValue( - snapshot, - recordGroupDefinitionFamilyState(recordGroupId), - ); - - if (!isDefined(recordGroup)) { - throw new Error( - `Record group with id ${recordGroupId} not found in snapshot`, - ); - } - - const recordGroupRowIds = getSnapshotValue( - snapshot, - recordIndexRecordIdsByGroupFamilyState(recordGroupId), - ); - - if (recordGroupRowIds.length > 0) { - continue; - } - - const updatedRecordGroup = { - ...recordGroup, - isVisible: !newHideState, - }; - - set( - recordGroupDefinitionFamilyState(recordGroupId), - updatedRecordGroup, - ); - - updatedRecordGroupDefinitions.push(updatedRecordGroup); - } - - saveViewGroups( - mapRecordGroupDefinitionsToViewGroups(updatedRecordGroupDefinitions), + (currentHideState) => !currentHideState, ); }, - [ - recordIndexRecordGroupIdsState, - objectOptionsDropdownRecordGroupHideState, - saveViewGroups, - recordIndexRecordIdsByGroupFamilyState, - ], + [objectOptionsDropdownRecordGroupHideState], ); return { diff --git a/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts index 78ead53c8656..abd766868eee 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts @@ -3,7 +3,9 @@ import { recordGroupIdsComponentState } from '@/object-record/record-group/state import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort'; import { recordGroupSortedInsert } from '@/object-record/record-group/utils/recordGroupSortedInsert'; +import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState'; import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; +import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; @@ -27,6 +29,11 @@ export const visibleRecordGroupIdsComponentSelector = createComponentSelectorV2< instanceId, }), ); + const hideEmptyRecordGroup = get( + recordIndexRecordGroupHideComponentState.atomFamily({ + instanceId, + }), + ); const result: RecordGroupDefinition[] = []; @@ -49,6 +56,24 @@ export const visibleRecordGroupIdsComponentSelector = createComponentSelectorV2< const recordGroupDefinition = get( recordGroupDefinitionFamilyState(recordGroupId), ); + const recordIds = get( + recordIndexRecordIdsByGroupComponentFamilyState.atomFamily({ + instanceId, + familyKey: recordGroupId, + }), + ); + + if (!isDefined(recordGroupDefinition)) { + continue; + } + + if (hideEmptyRecordGroup && recordIds.length === 0) { + continue; + } + + if (!recordGroupDefinition.isVisible) { + continue; + } recordGroupSortedInsert(result, recordGroupDefinition, comparator); } From b2553adb001e8eb660c93872a1be389235144436 Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Wed, 18 Dec 2024 17:54:32 +0100 Subject: [PATCH 10/13] Fix merge --- .../RecordTableBodyRecordGroupDragDropContextProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx index 4796217217c7..98474d1efa9c 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx @@ -19,7 +19,7 @@ export const RecordTableBodyRecordGroupDragDropContextProvider = ({ }: { children: ReactNode; }) => { - const { objectNameSingular, objectMetadataItem } = + const { objectNameSingular, recordTableId, objectMetadataItem } = useRecordTableContextOrThrow(); const { updateOneRecord: updateOneRow } = useUpdateOneRecord({ From 7982a4bc796f11d63d625f7fd598a9285bb7dfec Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Wed, 18 Dec 2024 17:54:46 +0100 Subject: [PATCH 11/13] revert scrollWrapper --- .../components/RecordTableFooterCell.tsx | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooterCell.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooterCell.tsx index 69187789728a..4d4e3747f14b 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooterCell.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooterCell.tsx @@ -5,7 +5,6 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata' import { RecordTableColumnFooterWithDropdown } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnFooterWithDropdown'; import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; -import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { mapArrayToObject } from '~/utils/array/mapArrayToObject'; @@ -86,19 +85,13 @@ export const RecordTableFooterCell = ({ COLUMN_MIN_WIDTH, )} > - - - - - + + + ); }; From 81a20c8acd96fa228ce4fff647cd5fc07ddee71e Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Thu, 19 Dec 2024 12:23:23 +0100 Subject: [PATCH 12/13] Fix stickiness --- .../record-table/components/RecordTable.tsx | 2 +- .../components/RecordTableRecordGroupRows.tsx | 11 ----- .../components/RecordTableBodyDroppable.tsx | 3 +- .../RecordTableRecordGroupsBody.tsx | 47 +++++++++++------- .../components/RecordTableFooter.tsx | 25 ++++------ .../RecordTableRecordGroupSection.tsx | 2 + .../RecordTableRecordGroupStickyEffect.tsx | 48 +++++++++++++++++++ 7 files changed, 93 insertions(+), 45 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupStickyEffect.tsx diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx index 090e44006d6c..aab93ff82d6e 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx @@ -97,7 +97,7 @@ export const RecordTable = () => { )} {isAggregateQueryEnabled && !hasRecordGroups && ( - + )} { - const isAggregateQueryEnabled = useIsFeatureEnabled( - 'IS_AGGREGATE_QUERY_ENABLED', - ); const currentRecordGroupId = useCurrentRecordGroupId(); const allRecordIds = useRecoilComponentValueV2( @@ -63,12 +58,6 @@ export const RecordTableRecordGroupRows = () => { })} - {isAggregateQueryEnabled && ( - - )} ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx index f2e19d7506af..8e731aaf5a1b 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx @@ -15,6 +15,7 @@ export const RecordTableBodyDroppable = ({ isDropDisabled, }: RecordTableBodyDroppableProps) => { const [v4Persistable] = useState(v4()); + const recordTableBodyId = `record-table-body${recordGroupId ? '-' + recordGroupId : ''}`; return ( {(provided) => ( { + const isAggregateQueryEnabled = useIsFeatureEnabled( + 'IS_AGGREGATE_QUERY_ENABLED', + ); const allRecordIds = useRecoilComponentValueV2( recordIndexAllRecordIdsComponentSelector, ); @@ -29,21 +34,31 @@ export const RecordTableRecordGroupsBody = () => { } return ( - - {visibleRecordGroupIds.map((recordGroupId, index) => ( - - - - {index > 0 && } - - - - - - ))} - + <> + + {visibleRecordGroupIds.map((recordGroupId, index) => ( + <> + + + + {index > 0 && } + + + + {isAggregateQueryEnabled && ( + + )} + + + + ))} + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx index 8e76e751301d..11b24f0bb481 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx @@ -1,14 +1,12 @@ import styled from '@emotion/styled'; import { MOBILE_VIEWPORT } from 'twenty-ui'; -import { TABLE_CELL_CHECKBOX_MIN_WIDTH } from '@/object-record/record-table/record-table-cell/components/RecordTableCellCheckbox'; -import { TABLE_CELL_GRIP_WIDTH } from '@/object-record/record-table/record-table-cell/components/RecordTableCellGrip'; import { RecordTableFooterCell } from '@/object-record/record-table/record-table-footer/components/RecordTableFooterCell'; import { FIRST_TH_WIDTH } from '@/object-record/record-table/record-table-header/components/RecordTableHeader'; import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -const StyledTableFoot = styled.thead` +const StyledTableFoot = styled.thead<{ endOfTableSticky?: boolean }>` cursor: pointer; th:nth-of-type(1) { @@ -62,27 +60,21 @@ const StyledTableFoot = styled.thead` tr { position: sticky; - bottom: 0; z-index: 5; - } - - &.first-columns-sticky { - th:nth-of-type(1), - th:nth-of-type(2), - th:nth-of-type(3) { - z-index: 10; - } + ${({ endOfTableSticky }) => endOfTableSticky && `bottom: 0;`} } `; -const StyledDiv = styled.div` - width: calc(${TABLE_CELL_GRIP_WIDTH} + ${TABLE_CELL_CHECKBOX_MIN_WIDTH}); +const StyledTh = styled.th` + background-color: ${({ theme }) => theme.background.primary}; `; export const RecordTableFooter = ({ currentRecordGroupId, + endOfTableSticky, }: { currentRecordGroupId?: string; + endOfTableSticky?: boolean; }) => { const visibleTableColumns = useRecoilComponentValueV2( visibleTableColumnsComponentSelector, @@ -92,10 +84,11 @@ export const RecordTableFooter = ({ - - + + {visibleTableColumns.map((column, index) => ( { weight="medium" /> {recordIdsByGroup.length} + diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupStickyEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupStickyEffect.tsx new file mode 100644 index 000000000000..889730417cb6 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupStickyEffect.tsx @@ -0,0 +1,48 @@ +import { useEffect } from 'react'; + +import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId'; +import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState'; +import { scrollWrapperScrollLeftComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; + +export const RecordTableRecordGroupStickyEffect = () => { + const scrollLeft = useRecoilComponentValueV2( + scrollWrapperScrollLeftComponentState, + ); + + const setIsRecordTableScrolledLeft = useSetRecoilComponentStateV2( + isRecordTableScrolledLeftComponentState, + ); + + const currentRecordGroupId = useCurrentRecordGroupId(); + + useEffect(() => { + setIsRecordTableScrolledLeft(scrollLeft === 0); + if (scrollLeft > 0) { + document + .getElementById( + `record-table-footer${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`, + ) + ?.classList.add('first-columns-sticky'); + document + .getElementById( + `record-table-body${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`, + ) + ?.classList.add('first-columns-sticky'); + } else { + document + .getElementById( + `record-table-footer${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`, + ) + ?.classList.remove('first-columns-sticky'); + document + .getElementById( + `record-table-body${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`, + ) + ?.classList.remove('first-columns-sticky'); + } + }, [currentRecordGroupId, scrollLeft, setIsRecordTableScrolledLeft]); + + return <>; +}; From a8d97b589fcbeb2d8a3bf735a1d103ee27789d1b Mon Sep 17 00:00:00 2001 From: Marie Stoppa Date: Thu, 19 Dec 2024 14:29:04 +0100 Subject: [PATCH 13/13] Rename Components + fix tests --- .../computeAggregateValueAndLabel.test.ts | 5 ++- .../record-table/components/RecordTable.tsx | 4 +- .../RecordTableRecordGroupsBody.tsx | 40 +++++++++---------- ...ter.tsx => RecordTableAggregateFooter.tsx} | 6 +-- ...tsx => RecordTableAggregateFooterCell.tsx} | 4 +- ...ordTableColumnAggregateFooterDropdown.tsx} | 2 +- ...RecordTableColumnAggregateFooterValue.tsx} | 2 +- ...ableColumnAggregateFooterWithDropdown.tsx} | 8 ++-- 8 files changed, 35 insertions(+), 36 deletions(-) rename packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/{RecordTableFooter.tsx => RecordTableAggregateFooter.tsx} (91%) rename packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/{RecordTableFooterCell.tsx => RecordTableAggregateFooterCell.tsx} (97%) rename packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/{RecordTableColumnFooterDropdown.tsx => RecordTableColumnAggregateFooterDropdown.tsx} (98%) rename packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/{RecordTableColumnFooterAggregateValue.tsx => RecordTableColumnAggregateFooterValue.tsx} (97%) rename packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/{RecordTableColumnFooterWithDropdown.tsx => RecordTableColumnAggregateFooterWithDropdown.tsx} (86%) diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/__tests__/computeAggregateValueAndLabel.test.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/__tests__/computeAggregateValueAndLabel.test.ts index 188f82ea4883..5a03afa7b36a 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/__tests__/computeAggregateValueAndLabel.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/utils/__tests__/computeAggregateValueAndLabel.test.ts @@ -47,7 +47,7 @@ describe('computeAggregateValueAndLabel', () => { }); expect(result).toEqual({ - value: 2, + value: '2', label: 'Sum', labelWithFieldName: 'Sum of amount', }); @@ -159,8 +159,9 @@ describe('computeAggregateValueAndLabel', () => { }); expect(result).toEqual({ - value: undefined, + value: '-', label: 'Sum', + labelWithFieldName: 'Sum of amount', }); }); }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx index aab93ff82d6e..853a3623dc64 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx @@ -13,7 +13,7 @@ import { RecordTableNoRecordGroupBody } from '@/object-record/record-table/recor import { RecordTableNoRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBodyEffect'; import { RecordTableRecordGroupBodyEffects } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects'; import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody'; -import { RecordTableFooter } from '@/object-record/record-table/record-table-footer/components/RecordTableFooter'; +import { RecordTableAggregateFooter } from '@/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter'; import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState'; @@ -97,7 +97,7 @@ export const RecordTable = () => { )} {isAggregateQueryEnabled && !hasRecordGroups && ( - + )} { <> {visibleRecordGroupIds.map((recordGroupId, index) => ( - <> - - - - {index > 0 && } - - - - {isAggregateQueryEnabled && ( - - )} - - - + + + + {index > 0 && } + + + + {isAggregateQueryEnabled && ( + + )} + + ))} diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx similarity index 91% rename from packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx rename to packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx index 11b24f0bb481..31b7368da550 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableFooter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; import { MOBILE_VIEWPORT } from 'twenty-ui'; -import { RecordTableFooterCell } from '@/object-record/record-table/record-table-footer/components/RecordTableFooterCell'; +import { RecordTableAggregateFooterCell } from '@/object-record/record-table/record-table-footer/components/RecordTableAggregateFooterCell'; import { FIRST_TH_WIDTH } from '@/object-record/record-table/record-table-header/components/RecordTableHeader'; import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -69,7 +69,7 @@ const StyledTh = styled.th` background-color: ${({ theme }) => theme.background.primary}; `; -export const RecordTableFooter = ({ +export const RecordTableAggregateFooter = ({ currentRecordGroupId, endOfTableSticky, }: { @@ -90,7 +90,7 @@ export const RecordTableFooter = ({ {visibleTableColumns.map((column, index) => ( - theme.spacing(2)}; `; -export const RecordTableColumnFooterAggregateValue = ({ +export const RecordTableColumnAggregateFooterValue = ({ dropdownId, aggregateValue, aggregateLabel, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterWithDropdown.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterWithDropdown.tsx similarity index 86% rename from packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterWithDropdown.tsx rename to packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterWithDropdown.tsx index 6c7cf7db1934..13c49fc30e1c 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnFooterWithDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterWithDropdown.tsx @@ -1,6 +1,6 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; -import { RecordTableColumnFooterAggregateValue } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnFooterAggregateValue'; -import { RecordTableColumnFooterDropdown } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnFooterDropdown'; +import { RecordTableColumnAggregateFooterDropdown } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterDropdown'; +import { RecordTableColumnAggregateFooterValue } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterValue'; import { useAggregateRecordsForRecordTableColumnFooter } from '@/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; @@ -44,7 +44,7 @@ export const RecordTableColumnFooterWithDropdown = ({ onClose={handleDropdownClose} dropdownId={dropdownId} clickableComponent={ - } dropdownComponents={ -