Display and update aggregate queries in kanban views (#8833)

Closes #8752, #8753, #8754

Implements usage of aggregate queries in kanban views.

https://github.com/user-attachments/assets/732590ca-2785-4c57-82d5-d999a2279e92

TO DO

1. write tests + storybook
2. Fix values displayed should have the same format as defined in number
fields + Fix display for amountMicros

---------

Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
Marie
2024-12-03 22:46:57 +01:00
committed by GitHub
parent 5e891a135b
commit 2fc247cb21
67 changed files with 1670 additions and 104 deletions

View File

@ -1,13 +1,15 @@
import { DROPDOWN_OFFSET_Y } from '@/dropdown/constants/DropdownOffsetY';
import { DROPDOWN_WIDTH } from '@/dropdown/constants/DropdownWidth';
import { useCurrentContentId } from '@/dropdown/hooks/useCurrentContentId';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { ObjectOptionsDropdownButton } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownButton';
import { ObjectOptionsDropdownContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownContent';
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext';
import { ObjectOptionsContentId } from '@/object-record/object-options-dropdown/types/ObjectOptionsContentId';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { ViewType } from '@/views/types/ViewType';
import { useCallback, useState } from 'react';
type ObjectOptionsDropdownProps = {
viewType: ViewType;
@ -20,24 +22,18 @@ export const ObjectOptionsDropdown = ({
objectMetadataItem,
viewType,
}: ObjectOptionsDropdownProps) => {
const [currentContentId, setCurrentContentId] =
useState<ObjectOptionsContentId | null>(null);
const handleContentChange = useCallback((key: ObjectOptionsContentId) => {
setCurrentContentId(key);
}, []);
const handleResetContent = useCallback(() => {
setCurrentContentId(null);
}, []);
const { currentContentId, handleContentChange, handleResetContent } =
useCurrentContentId<ObjectOptionsContentId>();
return (
<Dropdown
dropdownId={OBJECT_OPTIONS_DROPDOWN_ID}
clickableComponent={<ObjectOptionsDropdownButton />}
dropdownMenuWidth={'200px'}
dropdownHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
dropdownOffset={{ y: 8 }}
dropdownMenuWidth={DROPDOWN_WIDTH}
dropdownOffset={{ y: DROPDOWN_OFFSET_Y }}
clickableComponent={
<StyledHeaderDropdownButton>Options</StyledHeaderDropdownButton>
}
dropdownComponents={
<ObjectOptionsDropdownContext.Provider
value={{
@ -47,6 +43,7 @@ export const ObjectOptionsDropdown = ({
currentContentId,
onContentChange: handleContentChange,
resetContent: handleResetContent,
dropdownId: OBJECT_OPTIONS_DROPDOWN_ID,
}}
>
<ObjectOptionsDropdownContent />

View File

@ -1,18 +0,0 @@
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
export const ObjectOptionsDropdownButton = () => {
const { isDropdownOpen, toggleDropdown } = useDropdown(
OBJECT_OPTIONS_DROPDOWN_ID,
);
return (
<StyledHeaderDropdownButton
isUnfolded={isDropdownOpen}
onClick={toggleDropdown}
>
Options
</StyledHeaderDropdownButton>
);
};

View File

@ -4,6 +4,7 @@ import { ComponentDecorator } from 'twenty-ui';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { ObjectOptionsDropdownContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownContent';
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext';
import { ObjectOptionsContentId } from '@/object-record/object-options-dropdown/types/ObjectOptionsContentId';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
@ -94,6 +95,7 @@ const createStory = (contentId: ObjectOptionsContentId | null): Story => ({
currentContentId: contentId,
onContentChange: () => {},
resetContent: () => {},
dropdownId: OBJECT_OPTIONS_DROPDOWN_ID,
}}
>
<DropdownMenu>

View File

@ -2,6 +2,7 @@ import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
@ -55,6 +56,7 @@ describe('useOptionsDropdown', () => {
currentContentId: 'recordGroups',
onContentChange: mockOnContentChange,
resetContent: mockResetContent,
dropdownId: OBJECT_OPTIONS_DROPDOWN_ID,
...contextValue,
}}
>

View File

@ -1,26 +1,20 @@
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useDropdown } from '@/dropdown/hooks/useDropdown';
import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useCallback, useContext } from 'react';
import { useContext } from 'react';
export const useOptionsDropdown = () => {
const { closeDropdown } = useDropdown(OBJECT_OPTIONS_DROPDOWN_ID);
const context = useContext(ObjectOptionsDropdownContext);
if (!context) {
throw new Error(
'useOptionsDropdown must be used within a ObjectOptionsDropdownContext.Provider',
);
throw new Error('useOptionsDropdown must be used within a context');
}
const handleCloseDropdown = useCallback(() => {
context.resetContent();
closeDropdown();
}, [closeDropdown, context]);
const { closeDropdown } = useDropdown({
context: ObjectOptionsDropdownContext,
});
return {
...context,
closeDropdown: handleCloseDropdown,
closeDropdown,
};
};

View File

@ -10,6 +10,7 @@ export type ObjectOptionsDropdownContextValue = {
currentContentId: ObjectOptionsContentId | null;
onContentChange: (key: ObjectOptionsContentId) => void;
resetContent: () => void;
dropdownId: string;
};
export const ObjectOptionsDropdownContext =