fix: view group followup (#9162)

This PR fixes all followup that @Bonapara add on Discord.

- [x] When no group by is set, clicking on group by should open the
"field selection" menu
- [x] When closed, chevron should be "chevron-right" instead of
"chevron-up"
- [x] Sort : Add ability to switch from alphabetical to manual when
moving a option in sort alphabetical
- [x] Add subtext for group by and sort
- [x] Group by menu display bug
- [x] Changing the sort should not close the menu
- [x] Group by Activation -> shows empty state + is slow
- [x] Switching from Kanban view Settings to Table Options menu displays
an empty menu
- [x] Unnecessary spacing under groups
- [x] When no "select" are set on an object, redirect the user directly
to the new Select field page
- [x] Sort : Default should be manual
- [x] Hidding "no value" displays all options and remove the "hide empty
group" toggle
- [x] Hide Empty group option disappeared
- [x] Group by should not be persisted on "Locked/Main view" (**For now
we just disable the group by on main view**)
- [x] Hide Empty group should not be activated by default on
Opportunities Kanban view
- [ ] Animate the group opening/closing (**We'll be done later**)

Performance improvement:

https://github.com/user-attachments/assets/fd2acf66-0e56-45d0-8b2f-99c62e57d6f7

https://github.com/user-attachments/assets/80f1a2e1-9f77-4923-b85d-acb9cad96886

Also fix #9036

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Jérémy M
2025-01-02 16:40:28 +01:00
committed by GitHub
parent 866c29e9ee
commit 0f1458cbe9
49 changed files with 676 additions and 320 deletions

View File

@ -34,6 +34,7 @@ export const ObjectOptionsDropdown = ({
clickableComponent={
<StyledHeaderDropdownButton>Options</StyledHeaderDropdownButton>
}
onClose={handleResetContent}
dropdownComponents={
<ObjectOptionsDropdownContext.Provider
value={{

View File

@ -25,6 +25,7 @@ import { useSetRecoilState } from 'recoil';
export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
const {
viewType,
currentContentId,
recordIndexId,
objectMetadataItem,
@ -47,6 +48,7 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
const { handleVisibilityChange: handleRecordGroupVisibilityChange } =
useRecordGroupVisibility({
viewBarId: recordIndexId,
viewType,
});
const viewGroupSettingsUrl = getSettingsPagePath(

View File

@ -30,8 +30,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { ViewType } from '@/views/types/ViewType';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { FeatureFlagKey } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
export const ObjectOptionsDropdownMenuContent = () => {
const {
@ -42,10 +41,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
closeDropdown,
} = useOptionsDropdown();
const isViewGroupEnabled = useIsFeatureEnabled(
FeatureFlagKey.IsViewGroupsEnabled,
);
const { getIcon } = useIcons();
const { currentViewWithCombinedFiltersAndSorts: currentView } =
useGetCurrentView();
@ -120,9 +115,13 @@ export const ObjectOptionsDropdownMenuContent = () => {
contextualText={`${visibleBoardFields.length} shown`}
hasSubMenu
/>
{(viewType === ViewType.Kanban || isViewGroupEnabled) && (
{viewType === ViewType.Kanban && currentView?.key !== 'INDEX' && (
<MenuItem
onClick={() => onContentChange('recordGroups')}
onClick={() =>
isDefined(recordGroupFieldMetadata)
? onContentChange('recordGroups')
: onContentChange('recordGroupFields')
}
LeftIcon={IconLayoutList}
text="Group by"
contextualText={recordGroupFieldMetadata?.label}

View File

@ -26,6 +26,7 @@ import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMe
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
@ -36,6 +37,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
recordIndexId,
objectMetadataItem,
onContentChange,
resetContent,
closeDropdown,
} = useOptionsDropdown();
@ -47,7 +49,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
hiddenRecordGroupIdsComponentSelector,
);
const recordGroupFieldMetadataItem = useRecoilComponentValueV2(
const recordGroupFieldMetadata = useRecoilComponentValueV2(
recordGroupFieldMetadataComponentState,
);
@ -64,11 +66,14 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
viewBarComponentId: recordIndexId,
});
const newFieldSettingsUrl = getSettingsPagePath(
SettingsPath.ObjectNewFieldSelect,
const newSelectFieldSettingsUrl = getSettingsPagePath(
SettingsPath.ObjectNewFieldConfigure,
{
objectSlug: objectNamePlural,
},
{
fieldType: FieldMetadataType.Select,
},
);
const location = useLocation();
@ -101,7 +106,11 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
<>
<DropdownMenuHeader
StartIcon={IconChevronLeft}
onClick={() => onContentChange('recordGroups')}
onClick={() =>
isDefined(recordGroupFieldMetadata)
? onContentChange('recordGroups')
: resetContent()
}
>
Group by
</DropdownMenuHeader>
@ -114,13 +123,13 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
<DropdownMenuItemsContainer>
<MenuItemSelect
text="None"
selected={!isDefined(recordGroupFieldMetadataItem)}
selected={!isDefined(recordGroupFieldMetadata)}
onClick={handleResetRecordGroupField}
/>
{filteredRecordGroupFieldMetadataItems.map((fieldMetadataItem) => (
<MenuItemSelect
key={fieldMetadataItem.id}
selected={fieldMetadataItem.id === recordGroupFieldMetadataItem?.id}
selected={fieldMetadataItem.id === recordGroupFieldMetadata?.id}
onClick={() => handleRecordGroupFieldChange(fieldMetadataItem)}
LeftIcon={getIcon(fieldMetadataItem.icon)}
text={fieldMetadataItem.label}
@ -130,7 +139,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
<DropdownMenuSeparator />
<DropdownMenuItemsContainer>
<UndecoratedLink
to={newFieldSettingsUrl}
to={newSelectFieldSettingsUrl}
onClick={() => {
setNavigationMemorizedUrl(location.pathname + location.search);
closeDropdown();

View File

@ -17,8 +17,7 @@ import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const ObjectOptionsDropdownRecordGroupSortContent = () => {
const { currentContentId, onContentChange, closeDropdown } =
useOptionsDropdown();
const { currentContentId, onContentChange } = useOptionsDropdown();
const hiddenRecordGroupIds = useRecoilComponentValueV2(
hiddenRecordGroupIdsComponentSelector,
@ -30,7 +29,6 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => {
const handleRecordGroupSortChange = (sort: RecordGroupSort) => {
setRecordGroupSort(sort);
closeDropdown();
};
useEffect(() => {

View File

@ -11,47 +11,54 @@ import {
} from 'twenty-ui';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { RecordGroupReorderConfirmationModal } from '@/object-record/record-group/components/RecordGroupReorderConfirmationModal';
import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection';
import { useRecordGroupReorder } from '@/object-record/record-group/hooks/useRecordGroupReorder';
import { useRecordGroupReorderConfirmationModal } from '@/object-record/record-group/hooks/useRecordGroupReorderConfirmationModal';
import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector';
import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState';
import { recordIndexRecordGroupIsDraggableSortComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexRecordGroupIsDraggableSortComponentSelector';
import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentFamilySelector';
import { recordIndexRecordGroupHideComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentFamilyState';
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { FeatureFlagKey } from '~/generated/graphql';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
export const ObjectOptionsDropdownRecordGroupsContent = () => {
const isViewGroupEnabled = useIsFeatureEnabled(
FeatureFlagKey.IsViewGroupsEnabled,
);
const {
viewType,
currentContentId,
recordIndexId,
onContentChange,
resetContent,
} = useOptionsDropdown();
const { currentContentId, recordIndexId, onContentChange, resetContent } =
useOptionsDropdown();
const { currentViewWithCombinedFiltersAndSorts: currentView } =
useGetCurrentView();
const recordGroupFieldMetadata = useRecoilComponentValueV2(
recordGroupFieldMetadataComponentState,
);
const visibleRecordGroupIds = useRecoilComponentValueV2(
visibleRecordGroupIdsComponentSelector,
const visibleRecordGroupIds = useRecoilComponentFamilyValueV2(
visibleRecordGroupIdsComponentFamilySelector,
viewType,
);
const hiddenRecordGroupIds = useRecoilComponentValueV2(
hiddenRecordGroupIdsComponentSelector,
);
const isDragableSortRecordGroup = useRecoilComponentValueV2(
recordIndexRecordGroupIsDraggableSortComponentSelector,
const hideEmptyRecordGroup = useRecoilComponentFamilyValueV2(
recordIndexRecordGroupHideComponentFamilyState,
viewType,
);
const hideEmptyRecordGroup = useRecoilComponentValueV2(
recordIndexRecordGroupHideComponentState,
const recordGroupSort = useRecoilComponentValueV2(
recordIndexRecordGroupSortComponentState,
);
const {
@ -59,12 +66,16 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
handleHideEmptyRecordGroupChange,
} = useRecordGroupVisibility({
viewBarId: recordIndexId,
viewType,
});
const { handleOrderChange: handleRecordGroupOrderChange } =
useRecordGroupReorder({
viewBarId: recordIndexId,
});
const {
handleRecordGroupOrderChangeWithModal,
handleRecordGroupReorderConfirmClick,
} = useRecordGroupReorderConfirmationModal({
recordIndexId,
viewType,
});
useEffect(() => {
if (
@ -81,22 +92,20 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
Group by
</DropdownMenuHeader>
<DropdownMenuItemsContainer>
{isViewGroupEnabled && (
{currentView?.key !== 'INDEX' && (
<>
<MenuItem
onClick={() => onContentChange('recordGroupFields')}
LeftIcon={IconLayoutList}
text={
!recordGroupFieldMetadata
? 'Group by'
: `Group by "${recordGroupFieldMetadata.label}"`
}
text="Group by"
contextualText={recordGroupFieldMetadata?.label}
hasSubMenu
/>
<MenuItem
onClick={() => onContentChange('recordGroupSort')}
LeftIcon={IconSortDescending}
text="Sort"
contextualText={recordGroupSort}
hasSubMenu
/>
</>
@ -115,9 +124,9 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
<RecordGroupsVisibilityDropdownSection
title="Visible groups"
recordGroupIds={visibleRecordGroupIds}
onDragEnd={handleRecordGroupOrderChange}
onDragEnd={handleRecordGroupOrderChangeWithModal}
onVisibilityChange={handleRecordGroupVisibilityChange}
isDraggable={isDragableSortRecordGroup}
isDraggable={true}
showDragGrip={true}
/>
</>
@ -134,6 +143,9 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
</DropdownMenuItemsContainer>
</>
)}
<RecordGroupReorderConfirmationModal
onConfirmClick={handleRecordGroupReorderConfirmClick}
/>
</>
);
};