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:
@ -74,7 +74,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
'id' | 'name' | 'icon' | 'kanbanFieldMetadataId' | 'type'
|
||||
>
|
||||
>,
|
||||
shouldCopyFiltersAndSorts?: boolean,
|
||||
shouldCopyFiltersAndSortsAndAggregate?: boolean,
|
||||
) => {
|
||||
const currentViewId = getSnapshotValue(
|
||||
snapshot,
|
||||
@ -101,6 +101,13 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
key: null,
|
||||
kanbanFieldMetadataId:
|
||||
kanbanFieldMetadataId ?? sourceView.kanbanFieldMetadataId,
|
||||
kanbanAggregateOperation: shouldCopyFiltersAndSortsAndAggregate
|
||||
? sourceView.kanbanAggregateOperation
|
||||
: undefined,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
shouldCopyFiltersAndSortsAndAggregate
|
||||
? sourceView.kanbanAggregateOperationFieldMetadataId
|
||||
: undefined,
|
||||
type: type ?? sourceView.type,
|
||||
objectMetadataId: sourceView.objectMetadataId,
|
||||
});
|
||||
@ -143,7 +150,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
await createViewGroupRecords(viewGroupsToCreate, newView);
|
||||
}
|
||||
|
||||
if (shouldCopyFiltersAndSorts === true) {
|
||||
if (shouldCopyFiltersAndSortsAndAggregate === true) {
|
||||
const sourceViewCombinedFilterGroups = getViewFilterGroupsCombined(
|
||||
sourceView.id,
|
||||
);
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useUpdateView } from '@/views/hooks/useUpdateView';
|
||||
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useUpdateViewAggregate = () => {
|
||||
const currentViewId = useRecoilComponentValueV2(currentViewIdComponentState);
|
||||
const { updateView } = useUpdateView();
|
||||
const updateViewAggregate = useCallback(
|
||||
({
|
||||
kanbanAggregateOperationFieldMetadataId,
|
||||
kanbanAggregateOperation,
|
||||
}: {
|
||||
kanbanAggregateOperationFieldMetadataId: string | null;
|
||||
kanbanAggregateOperation: AGGREGATE_OPERATIONS;
|
||||
}) =>
|
||||
updateView({
|
||||
id: currentViewId,
|
||||
kanbanAggregateOperationFieldMetadataId,
|
||||
kanbanAggregateOperation,
|
||||
}),
|
||||
[currentViewId, updateView],
|
||||
);
|
||||
|
||||
return {
|
||||
updateViewAggregate,
|
||||
};
|
||||
};
|
||||
@ -1,3 +1,4 @@
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
||||
@ -12,6 +13,8 @@ export type GraphQLView = {
|
||||
type: ViewType;
|
||||
key: ViewKey | null;
|
||||
kanbanFieldMetadataId: string;
|
||||
kanbanAggregateOperation?: AGGREGATE_OPERATIONS | null;
|
||||
kanbanAggregateOperationFieldMetadataId?: string | null;
|
||||
objectMetadataId: string;
|
||||
isCompact: boolean;
|
||||
viewFields: ViewField[];
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
|
||||
@ -19,6 +20,8 @@ export type View = {
|
||||
viewFilterGroups?: ViewFilterGroup[];
|
||||
viewSorts: ViewSort[];
|
||||
kanbanFieldMetadataId: string;
|
||||
kanbanAggregateOperation: AGGREGATE_OPERATIONS | null;
|
||||
kanbanAggregateOperationFieldMetadataId: string | null;
|
||||
position: number;
|
||||
icon: string;
|
||||
__typename: 'View';
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
|
||||
export type ViewField = {
|
||||
@ -9,6 +10,7 @@ export type ViewField = {
|
||||
position: number;
|
||||
isVisible: boolean;
|
||||
size: number;
|
||||
aggregateOperation?: AGGREGATE_OPERATIONS | null;
|
||||
definition:
|
||||
| ColumnDefinition<FieldMetadata>
|
||||
| RecordBoardFieldDefinition<FieldMetadata>;
|
||||
|
||||
@ -17,6 +17,9 @@ export const getObjectMetadataItemViews = (
|
||||
position: view.position,
|
||||
objectMetadataId: view.objectMetadataId,
|
||||
kanbanFieldMetadataId: view.kanbanFieldMetadataId,
|
||||
kanbanAggregateOperation: view.kanbanAggregateOperation,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
view.kanbanAggregateOperationFieldMetadataId,
|
||||
icon: view.icon,
|
||||
}));
|
||||
};
|
||||
|
||||
@ -47,6 +47,7 @@ export const mapViewFieldsToColumnDefinitions = ({
|
||||
isLabelIdentifier,
|
||||
isVisible: isLabelIdentifier || viewField.isVisible,
|
||||
viewFieldId: viewField.id,
|
||||
aggregateOperation: viewField.aggregateOperation,
|
||||
isSortable: correspondingColumnDefinition.isSortable,
|
||||
isFilterable: correspondingColumnDefinition.isFilterable,
|
||||
defaultValue: correspondingColumnDefinition.defaultValue,
|
||||
|
||||
@ -13,48 +13,40 @@ import { viewPickerTypeComponentState } from '@/views/view-picker/states/viewPic
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const useCreateViewFromCurrentState = (viewBarInstanceId?: string) => {
|
||||
export const useCreateViewFromCurrentState = () => {
|
||||
const { closeAndResetViewPicker } = useCloseAndResetViewPicker();
|
||||
|
||||
const viewPickerInputNameCallbackState = useRecoilComponentCallbackStateV2(
|
||||
viewPickerInputNameComponentState,
|
||||
viewBarInstanceId,
|
||||
);
|
||||
|
||||
const viewPickerSelectedIconCallbackState = useRecoilComponentCallbackStateV2(
|
||||
viewPickerSelectedIconComponentState,
|
||||
viewBarInstanceId,
|
||||
);
|
||||
|
||||
const viewPickerTypeCallbackState = useRecoilComponentCallbackStateV2(
|
||||
viewPickerTypeComponentState,
|
||||
viewBarInstanceId,
|
||||
);
|
||||
|
||||
const viewPickerKanbanFieldMetadataIdCallbackState =
|
||||
useRecoilComponentCallbackStateV2(
|
||||
viewPickerKanbanFieldMetadataIdComponentState,
|
||||
viewBarInstanceId,
|
||||
);
|
||||
|
||||
const viewPickerIsPersistingCallbackState = useRecoilComponentCallbackStateV2(
|
||||
viewPickerIsPersistingComponentState,
|
||||
viewBarInstanceId,
|
||||
);
|
||||
|
||||
const viewPickerIsDirtyCallbackState = useRecoilComponentCallbackStateV2(
|
||||
viewPickerIsDirtyComponentState,
|
||||
viewBarInstanceId,
|
||||
);
|
||||
|
||||
const viewPickerModeCallbackState = useRecoilComponentCallbackStateV2(
|
||||
viewPickerModeComponentState,
|
||||
viewBarInstanceId,
|
||||
);
|
||||
|
||||
const { createViewFromCurrentView } =
|
||||
useCreateViewFromCurrentView(viewBarInstanceId);
|
||||
const { changeView } = useChangeView(viewBarInstanceId);
|
||||
const { createViewFromCurrentView } = useCreateViewFromCurrentView();
|
||||
const { changeView } = useChangeView();
|
||||
|
||||
const createViewFromCurrentState = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
@ -78,7 +70,7 @@ export const useCreateViewFromCurrentState = (viewBarInstanceId?: string) => {
|
||||
viewPickerModeCallbackState,
|
||||
);
|
||||
|
||||
const shouldCopyFiltersAndSorts =
|
||||
const shouldCopyFiltersAndSortsAndAggregate =
|
||||
viewPickerMode === 'create-from-current';
|
||||
|
||||
const id = v4();
|
||||
@ -94,7 +86,7 @@ export const useCreateViewFromCurrentState = (viewBarInstanceId?: string) => {
|
||||
type,
|
||||
kanbanFieldMetadataId,
|
||||
},
|
||||
shouldCopyFiltersAndSorts,
|
||||
shouldCopyFiltersAndSortsAndAggregate,
|
||||
);
|
||||
|
||||
closeAndResetViewPicker();
|
||||
|
||||
Reference in New Issue
Block a user