Feat: revamp group by settings (#8503)

This PR fix #8202 that is revamping the `Options` settings for board and
table.

<img width="221" alt="Screenshot 2024-11-15 at 11 47 52 AM"
src="https://github.com/user-attachments/assets/0b143c95-810d-408b-b19e-c2678cd5653a">
<img width="214" alt="Screenshot 2024-11-15 at 11 47 59 AM"
src="https://github.com/user-attachments/assets/3468734a-8174-4e36-a8ee-08dad6c56227">
<img width="210" alt="Screenshot 2024-11-15 at 11 48 10 AM"
src="https://github.com/user-attachments/assets/300628f5-6645-4f1c-af8a-befce2714716">
<img width="212" alt="Screenshot 2024-11-15 at 11 48 37 AM"
src="https://github.com/user-attachments/assets/37a3db40-2146-45eb-bea4-44e1041f5bcf">
<img width="214" alt="Screenshot 2024-11-15 at 11 48 44 AM"
src="https://github.com/user-attachments/assets/42d2adcc-8f03-4f28-928b-d3c3d54d388a">
<img width="213" alt="Screenshot 2024-11-15 at 11 48 51 AM"
src="https://github.com/user-attachments/assets/90824568-b979-46a7-9841-ab8b9978e138">
<img width="211" alt="Screenshot 2024-11-15 at 11 49 00 AM"
src="https://github.com/user-attachments/assets/fa22446a-b1db-4d97-9a45-0778bf09ae3c">

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Jérémy M
2024-11-20 17:03:18 +01:00
committed by GitHub
parent 0f7ebd3026
commit 2968085e73
73 changed files with 2222 additions and 731 deletions

View File

@ -1,191 +0,0 @@
import {
DropResult,
OnDragEndResponder,
ResponderProvided,
} from '@hello-pangea/dnd';
import { useRef } from 'react';
import { IconEye, IconEyeOff, MenuItemDraggable, Tag } from 'twenty-ui';
import {
RecordGroupDefinition,
RecordGroupDefinitionType,
} from '@/object-record/record-group/types/RecordGroupDefinition';
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { StyledDropdownMenuSubheader } from '@/ui/layout/dropdown/components/StyledDropdownMenuSubheader';
import { isDefined } from '~/utils/isDefined';
type ViewGroupsVisibilityDropdownSectionProps = {
viewGroups: RecordGroupDefinition[];
isDraggable: boolean;
onDragEnd?: OnDragEndResponder;
onVisibilityChange: (viewGroup: RecordGroupDefinition) => void;
title: string;
showSubheader: boolean;
showDragGrip: boolean;
};
export const ViewGroupsVisibilityDropdownSection = ({
viewGroups,
isDraggable,
onDragEnd,
onVisibilityChange,
title,
showSubheader = true,
showDragGrip,
}: ViewGroupsVisibilityDropdownSectionProps) => {
const handleOnDrag = (result: DropResult, provided: ResponderProvided) => {
onDragEnd?.(result, provided);
};
const getIconButtons = (index: number, viewGroup: RecordGroupDefinition) => {
const iconButtons = [
{
Icon: viewGroup.isVisible ? IconEyeOff : IconEye,
onClick: () => onVisibilityChange(viewGroup),
},
].filter(isDefined);
return iconButtons.length ? iconButtons : undefined;
};
const noValueViewGroups =
viewGroups.filter(
(viewGroup) => viewGroup.type === RecordGroupDefinitionType.NoValue,
) ?? [];
const viewGroupsWithoutNoValueGroups = viewGroups.filter(
(viewGroup) => viewGroup.type !== RecordGroupDefinitionType.NoValue,
);
const ref = useRef<HTMLDivElement>(null);
return (
<div ref={ref}>
{showSubheader && (
<StyledDropdownMenuSubheader>{title}</StyledDropdownMenuSubheader>
)}
<DropdownMenuItemsContainer>
{!!viewGroups.length && (
<>
{!isDraggable ? (
viewGroupsWithoutNoValueGroups.map(
(viewGroup, viewGroupIndex) => (
<MenuItemDraggable
key={viewGroup.id}
text={
<Tag
variant={
viewGroup.type !== RecordGroupDefinitionType.NoValue
? 'solid'
: 'outline'
}
color={
viewGroup.type !== RecordGroupDefinitionType.NoValue
? viewGroup.color
: 'transparent'
}
text={viewGroup.title}
weight={
viewGroup.type !== RecordGroupDefinitionType.NoValue
? 'regular'
: 'medium'
}
/>
}
iconButtons={getIconButtons(viewGroupIndex, viewGroup)}
accent={showDragGrip ? 'placeholder' : 'default'}
showGrip={showDragGrip}
isDragDisabled={!isDraggable}
/>
),
)
) : (
<DraggableList
onDragEnd={handleOnDrag}
draggableItems={
<>
{viewGroupsWithoutNoValueGroups.map(
(viewGroup, viewGroupIndex) => (
<DraggableItem
key={viewGroup.id}
draggableId={viewGroup.id}
index={viewGroupIndex + 1}
itemComponent={
<MenuItemDraggable
key={viewGroup.id}
text={
<Tag
variant={
viewGroup.type !==
RecordGroupDefinitionType.NoValue
? 'solid'
: 'outline'
}
color={
viewGroup.type !==
RecordGroupDefinitionType.NoValue
? viewGroup.color
: 'transparent'
}
text={viewGroup.title}
weight={
viewGroup.type !==
RecordGroupDefinitionType.NoValue
? 'regular'
: 'medium'
}
/>
}
iconButtons={getIconButtons(
viewGroupIndex,
viewGroup,
)}
accent={showDragGrip ? 'placeholder' : 'default'}
showGrip={showDragGrip}
isDragDisabled={!isDraggable}
/>
}
/>
),
)}
</>
}
/>
)}
{noValueViewGroups.map((viewGroup) => (
<MenuItemDraggable
key={viewGroup.id}
text={
<Tag
variant={
viewGroup.type !== RecordGroupDefinitionType.NoValue
? 'solid'
: 'outline'
}
color={
viewGroup.type !== RecordGroupDefinitionType.NoValue
? viewGroup.color
: 'transparent'
}
text={viewGroup.title}
weight={
viewGroup.type !== RecordGroupDefinitionType.NoValue
? 'regular'
: 'medium'
}
/>
}
accent={showDragGrip ? 'placeholder' : 'default'}
showGrip={true}
isDragDisabled={true}
isHoverDisabled
/>
))}
</>
)}
</DropdownMenuItemsContainer>
</div>
);
};

View File

@ -7,6 +7,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useCreateOneRecordMutation } from '@/object-record/hooks/useCreateOneRecordMutation';
import { useDeleteOneRecordMutation } from '@/object-record/hooks/useDeleteOneRecordMutation';
import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRecordMutation';
import { GraphQLView } from '@/views/types/GraphQLView';
import { ViewGroup } from '@/views/types/ViewGroup';
@ -24,6 +25,10 @@ export const usePersistViewGroupRecords = () => {
objectNameSingular: CoreObjectNameSingular.ViewGroup,
});
const { deleteOneRecordMutation } = useDeleteOneRecordMutation({
objectNameSingular: CoreObjectNameSingular.ViewGroup,
});
const { objectMetadataItems } = useObjectMetadataItems();
const apolloClient = useApolloClient();
@ -111,8 +116,42 @@ export const usePersistViewGroupRecords = () => {
[apolloClient, updateOneRecordMutation],
);
const deleteViewGroupRecords = useCallback(
async (viewGroupsToDelete: ViewGroup[]) => {
if (!viewGroupsToDelete.length) return;
const mutationPromises = viewGroupsToDelete.map((viewGroup) =>
apolloClient.mutate<{ deleteViewGroup: ViewGroup }>({
mutation: deleteOneRecordMutation,
variables: {
idToDelete: viewGroup.id,
},
// Avoid cache being updated with stale data
fetchPolicy: 'no-cache',
}),
);
const mutationResults = await Promise.all(mutationPromises);
mutationResults.forEach(({ data }) => {
const record = data?.['deleteViewGroup'];
if (!record) return;
apolloClient.cache.evict({
id: apolloClient.cache.identify({
__typename: 'ViewGroup',
id: record.id,
}),
});
});
},
[apolloClient, deleteOneRecordMutation],
);
return {
createViewGroupRecords,
updateViewGroupRecords,
deleteViewGroupRecords,
};
};