From 0a798a6671b96f00b4aa4caeb79b3378e3c855af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20M?= Date: Thu, 9 Jan 2025 19:12:57 +0100 Subject: [PATCH] Feat/view groups fast follow (#9513) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #9512 - ๐ŸŸ  [Icon should be lighter](https://discord.com/channels/1130383047699738754/1326487470895923222) The current weight is the same as in Figma, waiting for confirmation - ๐ŸŸ  [None has an unwanted margin left](https://discord.com/channels/1130383047699738754/1326493647796961323) This component is used in lot of places, removing the padding left can brake other places - ๐ŸŸข [All cells should have a the same right design](https://discord.com/channels/1130383047699738754/1326489001926066176) - ๐Ÿ”ด [Group Sorting should not "freeze" when mouse is release](https://discord.com/channels/1130383047699738754/1326494381795966996) Can't find a good way to fix it, seems more related to the fact it's running in debug mode. - ๐ŸŸข [Alignment issue](https://discord.com/channels/1130383047699738754/1326486523822084140) - ๐ŸŸข [View record count error](https://discord.com/channels/1130383047699738754/1326491489466978365) - ๐ŸŸข [Vertically align tags and numbers/count](https://discord.com/channels/1130383047699738754/1326490661800902728) - ๐ŸŸข [Display "Calculate" only on hover in view groups](https://discord.com/channels/1130383047699738754/1326490411929436191) - ๐ŸŸข [Aggregates height in view groups is 28px instead of 32px](https://discord.com/channels/1130383047699738754/1326489587127943188) - ๐ŸŸ  [Picker under the aggregate](https://discord.com/channels/1130383047699738754/1326487940557439039) Can't reproduce the issue - ๐ŸŸข [Icon should not be hoverable](https://discord.com/channels/1130383047699738754/1326477402360123425) - ๐ŸŸข [Crop long view titles](https://discord.com/channels/1130383047699738754/1326477009576136755) - ๐ŸŸข [Removing the group by on opportunities (group by none) give an white screen](https://discord.com/channels/1130383047699738754/1324651927962910750) --- ...ptionsDropdownRecordGroupFieldsContent.tsx | 14 +- .../components/RecordIndexContainer.tsx | 2 +- .../RecordIndexTableContainerEffect.tsx | 10 +- .../hooks/useLoadRecordIndexBoard.ts | 123 ------------------ .../hooks/useSetRecordIndexEntityCount.ts | 49 +++++++ ...xEntityCountByGroupComponentFamilyState.ts | 12 ++ ...xEntityCountNoGroupComponentFamilyState.ts | 9 ++ ...recordIndexEntityCountComponentSelector.ts | 39 ++++++ .../record-table/components/RecordTable.tsx | 4 +- .../components/RecordTableRecordGroupRows.tsx | 13 ++ .../hooks/internal/useSetRecordTableData.ts | 7 +- .../record-table/hooks/useRecordTable.ts | 4 +- .../components/RecordTableBody.tsx | 2 +- .../RecordTableRecordGroupsBody.tsx | 12 -- .../components/RecordTableAggregateFooter.tsx | 119 +++-------------- .../RecordTableAggregateFooterCell.tsx | 1 + ...ordTableColumnAggregateFooterValueCell.tsx | 8 +- .../components/RecordTableActionRow.tsx | 7 +- .../RecordTableRecordGroupSection.tsx | 4 +- .../onEntityCountChangeComponentState.ts | 2 +- .../SignInBackgroundMockContainerEffect.tsx | 9 +- .../components/DropdownMenuHeader.tsx | 45 +++++-- .../hooks/useSetRecordCountInCurrentView.ts | 13 -- .../entityCountInCurrentViewComponentState.ts | 10 -- .../components/ViewPickerDropdown.tsx | 10 +- 25 files changed, 225 insertions(+), 303 deletions(-) delete mode 100644 packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-index/hooks/useSetRecordIndexEntityCount.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-index/states/recordIndexEntityCountByGroupComponentFamilyState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-index/states/recordIndexEntityCountNoGroupComponentFamilyState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexEntityCountComponentSelector.ts delete mode 100644 packages/twenty-front/src/modules/views/hooks/useSetRecordCountInCurrentView.ts delete mode 100644 packages/twenty-front/src/modules/views/states/entityCountInCurrentViewComponentState.ts diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx index 66eff4c46..8cb7beb4c 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx @@ -24,6 +24,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { ViewType } from '@/views/types/ViewType'; import { useLocation } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; import { FieldMetadataType } from '~/generated-metadata/graphql'; @@ -33,6 +34,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { const { getIcon } = useIcons(); const { + viewType, currentContentId, recordIndexId, objectMetadataItem, @@ -121,11 +123,13 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { onChange={(event) => setRecordGroupFieldSearchInput(event.target.value)} /> - + {viewType === ViewType.Table && ( + + )} {filteredRecordGroupFieldMetadataItems.map((fieldMetadataItem) => ( { const { columnDefinitions } = useColumnDefinitionsFromFieldMetadata(objectMetadataItem); - const { setRecordCountInCurrentView } = - useSetRecordCountInCurrentView(viewBarId); + const { setRecordIndexEntityCount } = useSetRecordIndexEntityCount(viewBarId); useEffect(() => { setAvailableTableColumns(columnDefinitions); @@ -68,9 +67,10 @@ export const RecordIndexTableContainerEffect = () => { useEffect(() => { setOnEntityCountChange( - () => (entityCount: number) => setRecordCountInCurrentView(entityCount), + () => (entityCount: number, currentRecordGroupId?: string) => + setRecordIndexEntityCount(entityCount, currentRecordGroupId), ); - }, [setRecordCountInCurrentView, setOnEntityCountChange]); + }, [setRecordIndexEntityCount, setOnEntityCountChange]); const setViewFieldAggregateOperation = useRecoilCallback( ({ set, snapshot }) => diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts deleted file mode 100644 index 9df7f7a29..000000000 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts +++ /dev/null @@ -1,123 +0,0 @@ -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 { useSetRecordBoardRecordIds } from '@/object-record/record-board/hooks/useSetRecordBoardRecordIds'; -import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState'; -import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState'; -import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies'; -import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; -import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields'; -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 { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; -import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView'; - -type UseLoadRecordIndexBoardProps = { - objectNameSingular: string; - viewBarId: string; - recordBoardId: string; -}; - -export const useLoadRecordIndexBoard = ({ - objectNameSingular, - viewBarId, - recordBoardId, -}: UseLoadRecordIndexBoardProps) => { - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular, - }); - - const setRecordBoardFieldDefinitions = useSetRecoilComponentStateV2( - recordBoardFieldDefinitionsComponentState, - recordBoardId, - ); - - const { setRecordIds: setRecordIdsInBoard } = - useSetRecordBoardRecordIds(recordBoardId); - - const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore(); - - const recordIndexFieldDefinitions = useRecoilValue( - recordIndexFieldDefinitionsState, - ); - useEffect(() => { - setRecordBoardFieldDefinitions(recordIndexFieldDefinitions); - }, [recordIndexFieldDefinitions, setRecordBoardFieldDefinitions]); - - const recordIndexViewFilterGroups = useRecoilValue( - recordIndexViewFilterGroupsState, - ); - - const recordIndexFilters = useRecoilValue(recordIndexFiltersState); - const recordIndexSorts = useRecoilValue(recordIndexSortsState); - - const { filterValueDependencies } = useFilterValueDependencies(); - - const requestFilters = computeViewRecordGqlOperationFilter( - filterValueDependencies, - recordIndexFilters, - objectMetadataItem?.fields ?? [], - recordIndexViewFilterGroups, - ); - const orderBy = turnSortsIntoOrderBy(objectMetadataItem, recordIndexSorts); - - const recordIndexIsCompactModeActive = useRecoilValue( - recordIndexIsCompactModeActiveState, - ); - - const recordGqlFields = useRecordBoardRecordGqlFields({ - objectMetadataItem, - recordBoardId, - }); - - const { - records, - totalCount, - loading, - fetchMoreRecords, - queryStateIdentifier, - } = useFindManyRecords({ - objectNameSingular, - filter: requestFilters, - orderBy, - recordGqlFields, - }); - - const { setRecordCountInCurrentView } = - useSetRecordCountInCurrentView(viewBarId); - - const setIsCompactModeActive = useSetRecoilComponentStateV2( - isRecordBoardCompactModeActiveComponentState, - recordBoardId, - ); - - useEffect(() => { - setRecordIdsInBoard(records); - }, [records, setRecordIdsInBoard]); - - useEffect(() => { - upsertRecordsInStore(records); - }, [records, upsertRecordsInStore]); - - useEffect(() => { - setRecordCountInCurrentView(totalCount); - }, [totalCount, setRecordCountInCurrentView]); - - useEffect(() => { - setIsCompactModeActive(recordIndexIsCompactModeActive); - }, [recordIndexIsCompactModeActive, setIsCompactModeActive]); - - return { - records, - loading, - fetchMoreRecords, - queryStateIdentifier, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useSetRecordIndexEntityCount.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useSetRecordIndexEntityCount.ts new file mode 100644 index 000000000..69100ac7e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useSetRecordIndexEntityCount.ts @@ -0,0 +1,49 @@ +import { hasRecordGroupsComponentSelector } from '@/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector'; +import { recordIndexEntityCountByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexEntityCountByGroupComponentFamilyState'; +import { recordIndexEntityCountNoGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexEntityCountNoGroupComponentFamilyState'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useRecoilCallback } from 'recoil'; +import { isDefined } from '~/utils/isDefined'; + +export const useSetRecordIndexEntityCount = (viewBarComponentId?: string) => { + const hasRecordGroup = useRecoilComponentValueV2( + hasRecordGroupsComponentSelector, + ); + + const recordIndexEntityCountNoGroupFamilyState = + useRecoilComponentCallbackStateV2( + recordIndexEntityCountNoGroupComponentFamilyState, + viewBarComponentId, + ); + + const recordIndexEntityCountByGroupFamilyState = + useRecoilComponentCallbackStateV2( + recordIndexEntityCountByGroupComponentFamilyState, + viewBarComponentId, + ); + + const setRecordIndexEntityCount = useRecoilCallback( + ({ set }) => + (count: number, recordGroupId?: string) => { + if (hasRecordGroup) { + if (!isDefined(recordGroupId)) { + throw new Error('Record group ID is required'); + } + + set(recordIndexEntityCountByGroupFamilyState(recordGroupId), count); + } else { + set(recordIndexEntityCountNoGroupFamilyState, count); + } + }, + [ + hasRecordGroup, + recordIndexEntityCountByGroupFamilyState, + recordIndexEntityCountNoGroupFamilyState, + ], + ); + + return { + setRecordIndexEntityCount, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexEntityCountByGroupComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexEntityCountByGroupComponentFamilyState.ts new file mode 100644 index 000000000..ebcb70790 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexEntityCountByGroupComponentFamilyState.ts @@ -0,0 +1,12 @@ +import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; +import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; + +export const recordIndexEntityCountByGroupComponentFamilyState = + createComponentFamilyStateV2( + { + key: 'recordIndexEntityCountByGroupComponentFamilyState', + defaultValue: undefined, + componentInstanceContext: ViewComponentInstanceContext, + }, + ); diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexEntityCountNoGroupComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexEntityCountNoGroupComponentFamilyState.ts new file mode 100644 index 000000000..911f7ae24 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexEntityCountNoGroupComponentFamilyState.ts @@ -0,0 +1,9 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; + +export const recordIndexEntityCountNoGroupComponentFamilyState = + createComponentStateV2({ + key: 'recordIndexEntityCountNoGroupComponentFamilyState', + defaultValue: undefined, + componentInstanceContext: ViewComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexEntityCountComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexEntityCountComponentSelector.ts new file mode 100644 index 000000000..1c364c7ea --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexEntityCountComponentSelector.ts @@ -0,0 +1,39 @@ +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; +import { recordIndexEntityCountByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexEntityCountByGroupComponentFamilyState'; +import { recordIndexEntityCountNoGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexEntityCountNoGroupComponentFamilyState'; +import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; + +export const recordIndexEntityCountComponentSelector = + createComponentSelectorV2({ + key: 'recordIndexEntityCountComponentSelector', + get: + ({ instanceId }) => + ({ get }) => { + const recordGroupIds = get( + recordGroupIdsComponentState.atomFamily({ + instanceId, + }), + ); + + if (recordGroupIds.length === 0) { + return get( + recordIndexEntityCountNoGroupComponentFamilyState.atomFamily({ + instanceId, + }), + ); + } + + return recordGroupIds.reduce((acc, recordGroupId) => { + const count = get( + recordIndexEntityCountByGroupComponentFamilyState.atomFamily({ + instanceId, + familyKey: recordGroupId, + }), + ); + + return acc + (count ?? 0); + }, 0); + }, + componentInstanceContext: ViewComponentInstanceContext, + }); 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 571510fcb..b23c08ca3 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 @@ -100,9 +100,7 @@ export const RecordTable = () => { {isAggregateQueryEnabled && !hasRecordGroups && !isRecordTableInitialLoading && - allRecordIds.length > 0 && ( - - )} + allRecordIds.length > 0 && } { + const isAggregateQueryEnabled = useIsFeatureEnabled( + FeatureFlagKey.IsAggregateQueryEnabled, + ); + const currentRecordGroupId = useCurrentRecordGroupId(); const allRecordIds = useRecoilComponentValueV2( @@ -59,6 +66,12 @@ export const RecordTableRecordGroupRows = () => { + {isAggregateQueryEnabled && ( + + )} ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts index dc09fd20a..3cac0bca2 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts @@ -13,7 +13,10 @@ import { isDefined } from '~/utils/isDefined'; type useSetRecordTableDataProps = { recordTableId?: string; - onEntityCountChange: (entityCount?: number) => void; + onEntityCountChange: ( + entityCount?: number, + currentRecordGroupId?: string, + ) => void; }; export const useSetRecordTableData = ({ @@ -93,7 +96,7 @@ export const useSetRecordTableData = ({ set(recordIndexAllRecordIdsSelector, recordIds); } - onEntityCountChange(totalCount); + onEntityCountChange(totalCount, currentRecordGroupId); } }, [ diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts index 9cd173f5d..cd54668fd 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts @@ -156,13 +156,13 @@ export const useRecordTable = (props?: useRecordTableProps) => { const onEntityCountChange = useRecoilCallback( ({ snapshot }) => - (count?: number) => { + (count?: number, currentRecordGroupId?: string) => { const onEntityCountChange = getSnapshotValue( snapshot, onEntityCountChangeState, ); - onEntityCountChange?.(count); + onEntityCountChange?.(count, currentRecordGroupId); }, [onEntityCountChangeState], ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx index 7a6dbe297..d121d7b76 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx @@ -33,7 +33,7 @@ const StyledTbody = styled.tbody` } } - &::after { + &:not(.disable-shadow)::after { content: ''; position: absolute; top: -1px; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx index 2d7703d99..087019240 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx @@ -6,19 +6,13 @@ import { RecordTableRecordGroupRows } from '@/object-record/record-table/compone import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; import { RecordTableBodyRecordGroupDragDropContextProvider } from '@/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider'; -import { RecordTableAggregateFooter } from '@/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter'; import { RecordTableRecordGroupSection } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { ViewType } from '@/views/types/ViewType'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import { FeatureFlagKey } from '~/generated/graphql'; export const RecordTableRecordGroupsBody = () => { - const isAggregateQueryEnabled = useIsFeatureEnabled( - FeatureFlagKey.IsAggregateQueryEnabled, - ); const allRecordIds = useRecoilComponentValueV2( recordIndexAllRecordIdsComponentSelector, ); @@ -49,12 +43,6 @@ export const RecordTableRecordGroupsBody = () => { - {isAggregateQueryEnabled && ( - - )} ))} diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx index 709643e01..1ddda7388 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter.tsx @@ -1,124 +1,41 @@ import styled from '@emotion/styled'; -import { MOBILE_VIEWPORT } from 'twenty-ui'; import { RecordTableAggregateFooterCell } from '@/object-record/record-table/record-table-footer/components/RecordTableAggregateFooterCell'; import { RecordTableColumnAggregateFooterCellContext } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterCellContext'; -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<{ endOfTableSticky?: boolean }>` - cursor: pointer; - - th:nth-of-type(1) { - width: ${FIRST_TH_WIDTH}; - left: 0; - border-right-color: ${({ theme }) => theme.background.primary}; - } - - th:nth-of-type(2) { - border-right-color: ${({ theme }) => theme.background.primary}; - } - - &.first-columns-sticky { - th:nth-of-type(1) { - position: sticky; - left: 0; - z-index: 5; - transition: 0.3s ease; - } - - th:nth-of-type(2) { - position: sticky; - left: 11px; - z-index: 5; - transition: 0.3s ease; - } - - th:nth-of-type(3) { - position: sticky; - left: 43px; - z-index: 5; - transition: 0.3s ease; - - &::after { - content: ''; - position: absolute; - top: -1px; - height: calc(100% + 2px); - width: 4px; - right: 0px; - box-shadow: ${({ theme }) => theme.boxShadow.light}; - clip-path: inset(0px -4px 0px 0px); - } - - @media (max-width: ${MOBILE_VIEWPORT}px) { - width: 34px; - max-width: 34px; - } - } - } - - tr { - position: sticky; - z-index: 5; - background: ${({ theme }) => theme.background.primary}; - ${({ endOfTableSticky }) => - endOfTableSticky && - ` - bottom: 10px; - &::after { - content: ''; - position: absolute; - bottom: -10px; - left: 0; - right: 0; - height: 10px; - background: inherit; - } - `} - } -`; - const StyledTh = styled.th` background-color: ${({ theme }) => theme.background.primary}; `; export const RecordTableAggregateFooter = ({ currentRecordGroupId, - endOfTableSticky, }: { currentRecordGroupId?: string; - endOfTableSticky?: boolean; }) => { const visibleTableColumns = useRecoilComponentValueV2( visibleTableColumnsComponentSelector, ); return ( - - - - - {visibleTableColumns.map((column, index) => ( - - - - ))} - - + + + + {visibleTableColumns.map((column, index) => ( + + + + ))} + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooterCell.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooterCell.tsx index f6f66b4dc..febc995d6 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooterCell.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableAggregateFooterCell.tsx @@ -34,6 +34,7 @@ const StyledColumnFooterCell = styled.th<{ }; `; }}; + height: 32px; user-select: none; overflow: auto; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterValueCell.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterValueCell.tsx index b6f91db7c..421ee8245 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterValueCell.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterValueCell.tsx @@ -1,7 +1,9 @@ +import { hasRecordGroupsComponentSelector } from '@/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector'; import { RecordTableColumnAggregateFooterCellContext } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterCellContext'; import { RecordTableColumnAggregateFooterValue } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterValue'; import { hasAggregateOperationForViewFieldFamilySelector } from '@/object-record/record-table/record-table-footer/states/hasAggregateOperationForViewFieldFamilySelector'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useContext, useState } from 'react'; @@ -53,6 +55,10 @@ export const RecordTableColumnAggregateFooterValueCell = ({ }), ); + const hasRecordGroups = useRecoilComponentValueV2( + hasRecordGroupsComponentSelector, + ); + return (
{ @@ -64,7 +70,7 @@ export const RecordTableColumnAggregateFooterValueCell = ({ {isHovered || isDropdownOpen || hasAggregateOperationForViewField || - isFirstCell ? ( + (isFirstCell && !hasRecordGroups) ? ( <> theme.font.color.secondary}; - text-align: center; - vertical-align: middle; - padding-top: 3px; + display: flex; + height: 32px; + align-items: center; + justify-content: center; `; const StyledRecordTableTdTextContainer = styled(RecordTableTd)` diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx index 8c38a68be..ea1da6afd 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx @@ -38,6 +38,8 @@ const StyledTotalRow = styled.span` const StyledRecordGroupSection = styled(RecordTableTd)` border-right: none; height: 32px; + display: flex; + align-items: center; `; const StyledEmptyTd = styled.td` @@ -92,7 +94,7 @@ export const RecordTableRecordGroupSection = () => { - + void) | undefined + ((entityCount?: number, currentRecordGroupId?: string) => void) | undefined >({ key: 'onEntityCountChangeComponentState', defaultValue: undefined, diff --git a/packages/twenty-front/src/modules/sign-in-background-mock/components/SignInBackgroundMockContainerEffect.tsx b/packages/twenty-front/src/modules/sign-in-background-mock/components/SignInBackgroundMockContainerEffect.tsx index 2bad3fa95..04b9d9205 100644 --- a/packages/twenty-front/src/modules/sign-in-background-mock/components/SignInBackgroundMockContainerEffect.tsx +++ b/packages/twenty-front/src/modules/sign-in-background-mock/components/SignInBackgroundMockContainerEffect.tsx @@ -2,13 +2,13 @@ import { useEffect } from 'react'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; +import { useSetRecordIndexEntityCount } from '@/object-record/record-index/hooks/useSetRecordIndexEntityCount'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { SIGN_IN_BACKGROUND_MOCK_COLUMN_DEFINITIONS } from '@/sign-in-background-mock/constants/SignInBackgroundMockColumnDefinitions'; import { SIGN_IN_BACKGROUND_MOCK_FILTER_DEFINITIONS } from '@/sign-in-background-mock/constants/SignInBackgroundMockFilterDefinitions'; import { SIGN_IN_BACKGROUND_MOCK_SORT_DEFINITIONS } from '@/sign-in-background-mock/constants/SignInBackgroundMockSortDefinitions'; import { SIGN_IN_BACKGROUND_MOCK_VIEW_FIELDS } from '@/sign-in-background-mock/constants/SignInBackgroundMockViewFields'; import { useInitViewBar } from '@/views/hooks/useInitViewBar'; -import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView'; import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions'; type SignInBackgroundMockContainerEffectProps = { @@ -42,8 +42,7 @@ export const SignInBackgroundMockContainerEffect = ({ setViewObjectMetadataId, } = useInitViewBar(viewId); - const { setRecordCountInCurrentView } = - useSetRecordCountInCurrentView(viewId); + const { setRecordIndexEntityCount } = useSetRecordIndexEntityCount(viewId); useEffect(() => { setViewObjectMetadataId?.(objectMetadataItem.id); @@ -72,9 +71,9 @@ export const SignInBackgroundMockContainerEffect = ({ useEffect(() => { setOnEntityCountChange( - () => (entityCount: number) => setRecordCountInCurrentView(entityCount), + () => (entityCount: number) => setRecordIndexEntityCount(entityCount), ); - }, [setRecordCountInCurrentView, setOnEntityCountChange]); + }, [setRecordIndexEntityCount, setOnEntityCountChange]); return <>; }; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader.tsx index 02ed2027b..901b47aad 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuHeader.tsx @@ -1,7 +1,7 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { ComponentProps, MouseEvent } from 'react'; -import { IconComponent, LightIconButton } from 'twenty-ui'; +import { IconComponent, isDefined, LightIconButton } from 'twenty-ui'; const StyledHeader = styled.li` align-items: center; @@ -41,6 +41,26 @@ const StyledEndIcon = styled.div` const StyledChildrenWrapper = styled.span` overflow: hidden; padding: 0 ${({ theme }) => theme.spacing(1)}; + white-space: nowrap; + text-overflow: ellipsis; +`; + +const StyledNonClickableStartIcon = styled.div` + align-items: center; + background: transparent; + border: none; + + display: flex; + flex-direction: row; + + font-family: ${({ theme }) => theme.font.family}; + font-weight: ${({ theme }) => theme.font.weight.regular}; + gap: ${({ theme }) => theme.spacing(1)}; + justify-content: center; + + white-space: nowrap; + height: 24px; + width: 24px; `; type DropdownMenuHeaderProps = ComponentProps<'li'> & { @@ -76,13 +96,22 @@ export const DropdownMenuHeader = ({ )} {StartIcon && ( - + {isDefined(onClick) ? ( + + ) : ( + + + + )} {children} )} diff --git a/packages/twenty-front/src/modules/views/hooks/useSetRecordCountInCurrentView.ts b/packages/twenty-front/src/modules/views/hooks/useSetRecordCountInCurrentView.ts deleted file mode 100644 index 778fa6906..000000000 --- a/packages/twenty-front/src/modules/views/hooks/useSetRecordCountInCurrentView.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { entityCountInCurrentViewComponentState } from '@/views/states/entityCountInCurrentViewComponentState'; - -export const useSetRecordCountInCurrentView = (viewBarComponentId?: string) => { - const setEntityCountInCurrentView = useSetRecoilComponentStateV2( - entityCountInCurrentViewComponentState, - viewBarComponentId, - ); - - return { - setRecordCountInCurrentView: setEntityCountInCurrentView, - }; -}; diff --git a/packages/twenty-front/src/modules/views/states/entityCountInCurrentViewComponentState.ts b/packages/twenty-front/src/modules/views/states/entityCountInCurrentViewComponentState.ts deleted file mode 100644 index f18908482..000000000 --- a/packages/twenty-front/src/modules/views/states/entityCountInCurrentViewComponentState.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; -import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; - -export const entityCountInCurrentViewComponentState = createComponentStateV2< - number | undefined ->({ - key: 'entityCountInCurrentViewComponentState', - defaultValue: undefined, - componentInstanceContext: ViewComponentInstanceContext, -}); diff --git a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx index 371a2c6ae..59709a48f 100644 --- a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx +++ b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx @@ -7,12 +7,12 @@ import { useIcons, } from 'twenty-ui'; +import { recordIndexEntityCountComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexEntityCountComponentSelector'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { StyledDropdownButtonContainer } from '@/ui/layout/dropdown/components/StyledDropdownButtonContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; -import { entityCountInCurrentViewComponentState } from '@/views/states/entityCountInCurrentViewComponentState'; import { ViewsHotkeyScope } from '@/views/types/ViewsHotkeyScope'; import { ViewPickerContentCreateMode } from '@/views/view-picker/components/ViewPickerContentCreateMode'; import { ViewPickerContentEditMode } from '@/views/view-picker/components/ViewPickerContentEditMode'; @@ -55,8 +55,8 @@ export const ViewPickerDropdown = () => { const { updateViewFromCurrentState } = useUpdateViewFromCurrentState(); - const entityCountInCurrentView = useRecoilComponentValueV2( - entityCountInCurrentViewComponentState, + const entityCount = useRecoilComponentValueV2( + recordIndexEntityCountComponentSelector, ); const { isDropdownOpen: isViewsListDropdownOpen } = useDropdown( @@ -94,9 +94,7 @@ export const ViewPickerDropdown = () => { {currentViewWithCombinedFiltersAndSorts?.name ?? 'All'} - {isDefined(entityCountInCurrentView) && ( - <>ยท {entityCountInCurrentView} - )} + {isDefined(entityCount) && <>ยท {entityCount} }