Skip to content

Commit

Permalink
Add count and percent aggregations to kanban headers (#9348)
Browse files Browse the repository at this point in the history
  • Loading branch information
ijreilly authored Jan 6, 2025
1 parent b22a598 commit a9b95bc
Show file tree
Hide file tree
Showing 30 changed files with 502 additions and 327 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@ import { useCallback, useState } from 'react';
export const useCurrentContentId = <T>() => {
const [currentContentId, setCurrentContentId] = useState<T | null>(null);

const handleContentChange = useCallback((key: T) => {
setCurrentContentId(key);
}, []);
const [previousContentId, setPreviousContentId] = useState<T | null>(null);

const handleContentChange = useCallback(
(key: T) => {
setPreviousContentId(currentContentId);
setCurrentContentId(key);
},
[currentContentId],
);

const handleResetContent = useCallback(() => {
setPreviousContentId(null);
setCurrentContentId(null);
}, []);

return {
previousContentId,
currentContentId,
handleContentChange,
handleResetContent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ import { useAggregateRecordsQuery } from '@/object-record/hooks/useAggregateReco
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
import { generateAggregateQuery } from '@/object-record/utils/generateAggregateQuery';
import { renderHook } from '@testing-library/react';
import { getColumnNameForAggregateOperation } from 'twenty-shared';
import { FieldMetadataType } from '~/generated/graphql';

jest.mock('@/object-metadata/hooks/useObjectMetadataItem');
jest.mock('@/object-record/utils/generateAggregateQuery');
jest.mock('twenty-shared', () => ({
getColumnNameForAggregateOperation: jest.fn(),
}));

const mockObjectMetadataItem: ObjectMetadataItem = {
nameSingular: 'company',
Expand Down Expand Up @@ -69,7 +65,6 @@ describe('useAggregateRecordsQuery', () => {
});

it('should handle simple count operation', () => {
(getColumnNameForAggregateOperation as jest.Mock).mockReturnValue('name');
const { result } = renderHook(() =>
useAggregateRecordsQuery({
objectNameSingular: 'company',
Expand All @@ -91,7 +86,6 @@ describe('useAggregateRecordsQuery', () => {
});

it('should handle field aggregation', () => {
(getColumnNameForAggregateOperation as jest.Mock).mockReturnValue('amount');
const { result } = renderHook(() =>
useAggregateRecordsQuery({
objectNameSingular: 'company',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ export const RecordBoardColumnHeaderAggregateDropdown = ({
aggregateLabel,
dropdownId,
}: RecordBoardColumnHeaderAggregateDropdownProps) => {
const { currentContentId, handleContentChange, handleResetContent } =
useCurrentContentId<RecordBoardColumnHeaderAggregateContentId>();
const {
currentContentId,
handleContentChange,
handleResetContent,
previousContentId,
} = useCurrentContentId<RecordBoardColumnHeaderAggregateContentId>();

return (
<RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext.Provider
Expand All @@ -51,6 +55,7 @@ export const RecordBoardColumnHeaderAggregateDropdown = ({
currentContentId,
onContentChange: handleContentChange,
resetContent: handleResetContent,
previousContentId,
objectMetadataItem: objectMetadataItem,
dropdownId: dropdownId,
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,58 @@ import { useDropdown } from '@/dropdown/hooks/useDropdown';
import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext';
import { RecordBoardColumnHeaderAggregateDropdownFieldsContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownFieldsContent';
import { RecordBoardColumnHeaderAggregateDropdownMenuContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownMenuContent';
import { RecordBoardColumnHeaderAggregateDropdownMoreOptionsContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownMoreOptionsContent';
import { RecordBoardColumnHeaderAggregateDropdownOptionsContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownOptionsContent';
import { COUNT_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/countAggregateOperationOptions';
import { NON_STANDARD_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/nonStandardAggregateOperationsOptions';
import { PERCENT_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/percentAggregateOperationOption';
import { AvailableFieldsForAggregateOperation } from '@/object-record/types/AvailableFieldsForAggregateOperation';
import { getAvailableFieldsIdsForAggregationFromObjectFields } from '@/object-record/utils/getAvailableFieldsIdsForAggregationFromObjectFields';

export const AggregateDropdownContent = () => {
const { currentContentId } = useDropdown({
const { currentContentId, objectMetadataItem } = useDropdown({
context: RecordBoardColumnHeaderAggregateDropdownContext,
});

switch (currentContentId) {
case 'moreAggregateOperationOptions':
return <RecordBoardColumnHeaderAggregateDropdownMoreOptionsContent />;
case 'countAggregateOperationsOptions': {
const availableAggregations: AvailableFieldsForAggregateOperation =
getAvailableFieldsIdsForAggregationFromObjectFields(
objectMetadataItem.fields,
COUNT_AGGREGATE_OPERATION_OPTIONS,
);
return (
<RecordBoardColumnHeaderAggregateDropdownOptionsContent
availableAggregations={availableAggregations}
title="Count"
/>
);
}
case 'percentAggregateOperationsOptions': {
const availableAggregations: AvailableFieldsForAggregateOperation =
getAvailableFieldsIdsForAggregationFromObjectFields(
objectMetadataItem.fields,
PERCENT_AGGREGATE_OPERATION_OPTIONS,
);
return (
<RecordBoardColumnHeaderAggregateDropdownOptionsContent
availableAggregations={availableAggregations}
title="Percent"
/>
);
}
case 'moreAggregateOperationOptions': {
const availableAggregations: AvailableFieldsForAggregateOperation =
getAvailableFieldsIdsForAggregationFromObjectFields(
objectMetadataItem.fields,
NON_STANDARD_AGGREGATE_OPERATION_OPTIONS,
);
return (
<RecordBoardColumnHeaderAggregateDropdownOptionsContent
availableAggregations={availableAggregations}
title="More options"
/>
);
}
case 'aggregateFields':
return <RecordBoardColumnHeaderAggregateDropdownFieldsContent />;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type RecordBoardColumnHeaderAggregateDropdownContextValue = {
currentContentId: RecordBoardColumnHeaderAggregateContentId | null;
onContentChange: (key: RecordBoardColumnHeaderAggregateContentId) => void;
resetContent: () => void;
previousContentId: RecordBoardColumnHeaderAggregateContentId | null;
dropdownId: string;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ import {
import { isDefined } from '~/utils/isDefined';

export const RecordBoardColumnHeaderAggregateDropdownFieldsContent = () => {
const { closeDropdown, objectMetadataItem, onContentChange } = useDropdown({
const {
closeDropdown,
objectMetadataItem,
onContentChange,
resetContent,
previousContentId,
} = useDropdown({
context: RecordBoardColumnHeaderAggregateDropdownContext,
});

Expand All @@ -45,7 +51,11 @@ export const RecordBoardColumnHeaderAggregateDropdownFieldsContent = () => {
<>
<DropdownMenuHeader
StartIcon={IconChevronLeft}
onClick={() => onContentChange('moreAggregateOperationOptions')}
onClick={() =>
previousContentId
? onContentChange(previousContentId)
: resetContent()
}
>
{getAggregateOperationLabel(aggregateOperation)}
</DropdownMenuHeader>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import { Key } from 'ts-key-enum';
import { IconCheck, MenuItem } from 'twenty-ui';
import { MenuItem } from 'twenty-ui';

import { useDropdown } from '@/dropdown/hooks/useDropdown';
import {
RecordBoardColumnHeaderAggregateDropdownContext,
RecordBoardColumnHeaderAggregateDropdownContextValue,
} from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext';

import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel';
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useUpdateViewAggregate } from '@/views/hooks/useUpdateViewAggregate';
import { useRecoilValue } from 'recoil';
import { isDefined } from '~/utils/isDefined';

export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
const { onContentChange, closeDropdown } =
Expand All @@ -31,31 +25,22 @@ export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
TableOptionsHotkeyScope.Dropdown,
);

const { updateViewAggregate } = useUpdateViewAggregate();

const recordIndexKanbanAggregateOperation = useRecoilValue(
recordIndexKanbanAggregateOperationState,
);

return (
<>
<DropdownMenuItemsContainer>
<MenuItem
onClick={() => {
updateViewAggregate({
kanbanAggregateOperationFieldMetadataId: null,
kanbanAggregateOperation: AGGREGATE_OPERATIONS.count,
});
closeDropdown();
onContentChange('countAggregateOperationsOptions');
}}
text={getAggregateOperationLabel(AGGREGATE_OPERATIONS.count)}
RightIcon={
!isDefined(recordIndexKanbanAggregateOperation?.operation) ||
recordIndexKanbanAggregateOperation?.operation ===
AGGREGATE_OPERATIONS.count
? IconCheck
: undefined
}
text={'Count'}
hasSubMenu
/>
<MenuItem
onClick={() => {
onContentChange('percentAggregateOperationsOptions');
}}
text={'Percent'}
hasSubMenu
/>
<MenuItem
onClick={() => {
Expand Down

This file was deleted.

Loading

0 comments on commit a9b95bc

Please sign in to comment.