Add useGroupBy hook + update calendar view to use groupBy (#15439)
## Context Took inspiration from findMany + kanban => Implemented a hook for the new groupBy API endpoints Fixed an issue when DnD to an empty day
This commit is contained in:
+69
@@ -0,0 +1,69 @@
|
||||
import { type Reference } from '@apollo/client';
|
||||
|
||||
import { encodeCursor } from '@/apollo/utils/encodeCursor';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||
import { type ToReferenceFunction } from '@apollo/client/cache/core/types/common';
|
||||
|
||||
import { createCacheEdgeWithRecordRef } from '../createCacheEdgeWithRecordRef';
|
||||
|
||||
describe('createCacheEdgeWithRecordRef', () => {
|
||||
it('should create an edge with reference when toReference returns a reference', () => {
|
||||
// Given
|
||||
const record: RecordGqlNode = {
|
||||
__typename: 'Person',
|
||||
id: '123',
|
||||
};
|
||||
|
||||
const objectMetadataItem = {
|
||||
nameSingular: 'person',
|
||||
} as ObjectMetadataItem;
|
||||
|
||||
const mockReference: Reference = {
|
||||
__ref: 'Person:123',
|
||||
};
|
||||
|
||||
const toReference: ToReferenceFunction = jest.fn(() => mockReference);
|
||||
|
||||
// When
|
||||
const result = createCacheEdgeWithRecordRef({
|
||||
record,
|
||||
objectMetadataItem,
|
||||
toReference,
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(result).not.toBeNull();
|
||||
expect(result).toEqual({
|
||||
__typename: 'PersonEdge',
|
||||
node: mockReference,
|
||||
cursor: encodeCursor(record),
|
||||
});
|
||||
expect(toReference).toHaveBeenCalledWith(record);
|
||||
});
|
||||
|
||||
it('should return null when toReference returns undefined', () => {
|
||||
// Given
|
||||
const record: RecordGqlNode = {
|
||||
__typename: 'Person',
|
||||
id: '123',
|
||||
};
|
||||
|
||||
const objectMetadataItem = {
|
||||
nameSingular: 'person',
|
||||
} as ObjectMetadataItem;
|
||||
|
||||
const toReference: ToReferenceFunction = jest.fn(() => undefined);
|
||||
|
||||
// When
|
||||
const result = createCacheEdgeWithRecordRef({
|
||||
record,
|
||||
objectMetadataItem,
|
||||
toReference,
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(result).toBeNull();
|
||||
expect(toReference).toHaveBeenCalledWith(record);
|
||||
});
|
||||
});
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
import { encodeCursor } from '@/apollo/utils/encodeCursor';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type RecordGqlRefEdge } from '@/object-record/cache/types/RecordGqlRefEdge';
|
||||
import { getEdgeTypename } from '@/object-record/cache/utils/getEdgeTypename';
|
||||
import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode';
|
||||
import { type ToReferenceFunction } from '@apollo/client/cache/core/types/common';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type CreateCacheEdgeWithRecordRefParams = {
|
||||
record: RecordGqlNode;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
toReference: ToReferenceFunction;
|
||||
};
|
||||
|
||||
export const createCacheEdgeWithRecordRef = ({
|
||||
record,
|
||||
objectMetadataItem,
|
||||
toReference,
|
||||
}: CreateCacheEdgeWithRecordRefParams): RecordGqlRefEdge | null => {
|
||||
const reference = toReference(record);
|
||||
if (!isDefined(reference)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
__typename: getEdgeTypename(objectMetadataItem.nameSingular),
|
||||
node: reference,
|
||||
cursor: encodeCursor(record),
|
||||
};
|
||||
};
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { type RecordGqlConnection } from '@/object-record/graphql/types/RecordGqlConnection';
|
||||
|
||||
export type RecordGqlGroupByConnection = RecordGqlConnection & {
|
||||
groupByDimensionValues: string[];
|
||||
};
|
||||
|
||||
export type RecordGqlOperationGroupByResult = {
|
||||
[objectNamePlural: string]: RecordGqlGroupByConnection[];
|
||||
};
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
||||
import { type RecordGqlOperationFilter } from 'twenty-shared/types';
|
||||
|
||||
export type RecordGqlOperationGroupByVariables = {
|
||||
groupBy: Record<string, any>[];
|
||||
filter?: RecordGqlOperationFilter;
|
||||
orderBy?: Record<string, any>[];
|
||||
orderByForRecords?: RecordGqlOperationOrderBy;
|
||||
viewId?: string;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { generateGroupByRecordsQuery } from '@/object-record/utils/generateGroupByRecordsQuery';
|
||||
|
||||
export const useGroupByRecordsQuery = ({
|
||||
objectNameSingular,
|
||||
recordGqlFields,
|
||||
computeReferences,
|
||||
}: {
|
||||
objectNameSingular: string;
|
||||
recordGqlFields?: RecordGqlOperationGqlRecordFields;
|
||||
computeReferences?: boolean;
|
||||
}) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const groupByRecordsQuery = generateGroupByRecordsQuery({
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
recordGqlFields,
|
||||
computeReferences,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
return {
|
||||
groupByRecordsQuery,
|
||||
};
|
||||
};
|
||||
+7
-24
@@ -1,18 +1,14 @@
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext';
|
||||
import { useRecordCalendarGroupByRecords } from '@/object-record/record-calendar/hooks/useRecordCalendarGroupByRecords';
|
||||
import { RecordCalendarComponentInstanceContext } from '@/object-record/record-calendar/states/contexts/RecordCalendarComponentInstanceContext';
|
||||
import { recordCalendarSelectedDateComponentState } from '@/object-record/record-calendar/states/recordCalendarSelectedDateComponentState';
|
||||
import { recordCalendarSelectedRecordIdsComponentSelector } from '@/object-record/record-calendar/states/selectors/recordCalendarSelectedRecordIdsComponentSelector';
|
||||
import { useRecordsFieldVisibleGqlFields } from '@/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields';
|
||||
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
|
||||
import { recordIndexCalendarFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexCalendarFieldMetadataIdState';
|
||||
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
||||
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const RecordIndexCalendarDataLoaderEffect = () => {
|
||||
const recordCalendarId = useAvailableComponentInstanceIdOrThrow(
|
||||
@@ -24,7 +20,9 @@ export const RecordIndexCalendarDataLoaderEffect = () => {
|
||||
recordCalendarId,
|
||||
);
|
||||
|
||||
const { objectMetadataItem } = useRecordCalendarContextOrThrow();
|
||||
const recordCalendarSelectedDate = useRecoilComponentValue(
|
||||
recordCalendarSelectedDateComponentState,
|
||||
);
|
||||
|
||||
const { upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
|
||||
@@ -36,25 +34,10 @@ export const RecordIndexCalendarDataLoaderEffect = () => {
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const recordIndexCalendarFieldMetadataId = useRecoilValue(
|
||||
recordIndexCalendarFieldMetadataIdState,
|
||||
const { records } = useRecordCalendarGroupByRecords(
|
||||
recordCalendarSelectedDate,
|
||||
);
|
||||
|
||||
const objectNameSingular = objectMetadataItem.nameSingular;
|
||||
|
||||
const params = useFindManyRecordIndexTableParams(objectNameSingular);
|
||||
|
||||
const recordGqlFields = useRecordsFieldVisibleGqlFields({
|
||||
objectMetadataItem,
|
||||
additionalFieldMetadataId: recordIndexCalendarFieldMetadataId,
|
||||
});
|
||||
|
||||
const { records } = useFindManyRecords({
|
||||
...params,
|
||||
limit: 100,
|
||||
recordGqlFields,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
upsertRecordsInStore(records);
|
||||
setRecordIndexAllRecordIdsSelector(records.map((record) => record.id));
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
|
||||
import { hasObjectMetadataItemPositionField } from '@/object-metadata/utils/hasObjectMetadataItemPositionField';
|
||||
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||
import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/RecordGqlOperationOrderBy';
|
||||
import { useGroupByRecordsQuery } from '@/object-record/hooks/useGroupByRecordsQuery';
|
||||
import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext';
|
||||
import { useRecordCalendarQueryDateRangeFilter } from '@/object-record/record-calendar/month/hooks/useRecordCalendarQueryDateRangeFilter';
|
||||
import { useRecordsFieldVisibleGqlFields } from '@/object-record/record-field/hooks/useRecordsFieldVisibleGqlFields';
|
||||
import { recordIndexCalendarFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexCalendarFieldMetadataIdState';
|
||||
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { buildGroupByFieldObject } from '@/page-layout/widgets/graph/utils/buildGroupByFieldObject';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useRecordCalendarGroupByRecords = (selectedDate: Date) => {
|
||||
const { objectMetadataItem } = useRecordCalendarContextOrThrow();
|
||||
|
||||
const recordIndexCalendarFieldMetadataId = useRecoilValue(
|
||||
recordIndexCalendarFieldMetadataIdState,
|
||||
);
|
||||
|
||||
const recordGqlFields = useRecordsFieldVisibleGqlFields({
|
||||
objectMetadataItem,
|
||||
additionalFieldMetadataId: recordIndexCalendarFieldMetadataId,
|
||||
});
|
||||
|
||||
const { dateRangeFilter } =
|
||||
useRecordCalendarQueryDateRangeFilter(selectedDate);
|
||||
|
||||
const calendarFieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.id === recordIndexCalendarFieldMetadataId,
|
||||
);
|
||||
|
||||
const groupBy = !isDefined(calendarFieldMetadataItem)
|
||||
? []
|
||||
: [
|
||||
buildGroupByFieldObject({
|
||||
field: calendarFieldMetadataItem,
|
||||
dateGranularity: ObjectRecordGroupByDateGranularity.DAY,
|
||||
}),
|
||||
];
|
||||
|
||||
const orderByForRecords: RecordGqlOperationOrderBy | undefined =
|
||||
!objectMetadataItem.isRemote &&
|
||||
hasObjectMetadataItemPositionField(objectMetadataItem)
|
||||
? [
|
||||
{
|
||||
position: 'AscNullsFirst',
|
||||
},
|
||||
]
|
||||
: undefined;
|
||||
|
||||
const { groupByRecordsQuery } = useGroupByRecordsQuery({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
recordGqlFields,
|
||||
computeReferences: false,
|
||||
});
|
||||
|
||||
const apolloCoreClient = useApolloCoreClient();
|
||||
|
||||
const { data, loading, error } = useQuery(groupByRecordsQuery, {
|
||||
client: apolloCoreClient,
|
||||
skip:
|
||||
!isDefined(calendarFieldMetadataItem) ||
|
||||
groupBy.length === 0 ||
|
||||
Object.keys(dateRangeFilter).length === 0,
|
||||
fetchPolicy: 'cache-and-network',
|
||||
variables: {
|
||||
groupBy,
|
||||
filter: dateRangeFilter,
|
||||
orderByForRecords,
|
||||
},
|
||||
});
|
||||
|
||||
const groupByResults = data?.[`${objectMetadataItem.namePlural}GroupBy`];
|
||||
|
||||
const records: ObjectRecord[] = useMemo(() => {
|
||||
if (!isDefined(groupByResults)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const allRecords = groupByResults.flatMap((group: any) =>
|
||||
getRecordsFromRecordConnection<ObjectRecord>({
|
||||
recordConnection: group,
|
||||
}),
|
||||
);
|
||||
|
||||
// Deduplicate records by ID to prevent duplicate keys in React
|
||||
const uniqueRecordsMap = new Map<string, ObjectRecord>();
|
||||
allRecords.forEach((record: ObjectRecord) => {
|
||||
if (!uniqueRecordsMap.has(record.id)) {
|
||||
uniqueRecordsMap.set(record.id, record);
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(uniqueRecordsMap.values());
|
||||
}, [groupByResults]);
|
||||
|
||||
const groupByDimensionValues = useMemo(
|
||||
() =>
|
||||
!isDefined(groupByResults)
|
||||
? []
|
||||
: groupByResults.flatMap(
|
||||
(group: any) => group.groupByDimensionValues || [],
|
||||
),
|
||||
[groupByResults],
|
||||
);
|
||||
|
||||
return {
|
||||
records,
|
||||
groupByDimensionValues,
|
||||
loading,
|
||||
error,
|
||||
calendarFieldMetadataItem,
|
||||
};
|
||||
};
|
||||
+37
-4
@@ -1,11 +1,18 @@
|
||||
import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext';
|
||||
import { useRecordCalendarMonthDaysRange } from '@/object-record/record-calendar/month/hooks/useRecordCalendarMonthDaysRange';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import {
|
||||
combineFilters,
|
||||
computeRecordGqlOperationFilter,
|
||||
isDefined,
|
||||
turnAnyFieldFilterIntoRecordGqlFilter,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
const DATE_RANGE_FILTER_AFTER_ID = 'DATE_RANGE_FILTER_AFTER_ID';
|
||||
@@ -18,6 +25,20 @@ export const useRecordCalendarQueryDateRangeFilter = (selectedDate: Date) => {
|
||||
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
|
||||
const currentRecordFilterGroups = useRecoilComponentValue(
|
||||
currentRecordFilterGroupsComponentState,
|
||||
);
|
||||
|
||||
const currentRecordFilters = useRecoilComponentValue(
|
||||
currentRecordFiltersComponentState,
|
||||
);
|
||||
|
||||
const { filterValueDependencies } = useFilterValueDependencies();
|
||||
|
||||
const anyFieldFilterValue = useRecoilComponentValue(
|
||||
anyFieldFilterValueComponentState,
|
||||
);
|
||||
|
||||
if (
|
||||
!isDefined(currentView) ||
|
||||
!isDefined(currentView.calendarFieldMetadataId)
|
||||
@@ -50,13 +71,25 @@ export const useRecordCalendarQueryDateRangeFilter = (selectedDate: Date) => {
|
||||
};
|
||||
|
||||
const dateRangeFilter = computeRecordGqlOperationFilter({
|
||||
filterValueDependencies: {},
|
||||
recordFilters: [dateRangeFilterAfter, dateRangeFilterBefore],
|
||||
recordFilterGroups: [],
|
||||
filterValueDependencies,
|
||||
recordFilters: [
|
||||
...currentRecordFilters,
|
||||
dateRangeFilterAfter,
|
||||
dateRangeFilterBefore,
|
||||
],
|
||||
recordFilterGroups: currentRecordFilterGroups,
|
||||
fields: objectMetadataItem.fields,
|
||||
});
|
||||
|
||||
const { recordGqlOperationFilter: anyFieldFilter } =
|
||||
turnAnyFieldFilterIntoRecordGqlFilter({
|
||||
fields: objectMetadataItem.fields,
|
||||
filterValue: anyFieldFilterValue,
|
||||
});
|
||||
|
||||
const combinedFilter = combineFilters([dateRangeFilter, anyFieldFilter]);
|
||||
|
||||
return {
|
||||
dateRangeFilter,
|
||||
dateRangeFilter: combinedFilter,
|
||||
};
|
||||
};
|
||||
|
||||
+20
@@ -1,4 +1,5 @@
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { hasObjectMetadataItemPositionField } from '@/object-metadata/utils/hasObjectMetadataItemPositionField';
|
||||
import { RecordCalendarComponentInstanceContext } from '@/object-record/record-calendar/states/contexts/RecordCalendarComponentInstanceContext';
|
||||
import { recordIndexCalendarFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexCalendarFieldMetadataIdState';
|
||||
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
||||
@@ -60,6 +61,25 @@ export const calendarDayRecordIdsComponentFamilySelector =
|
||||
return isSameDay(recordDateObj, dayDate);
|
||||
});
|
||||
|
||||
if (
|
||||
!objectMetadataItem.isRemote &&
|
||||
hasObjectMetadataItemPositionField(objectMetadataItem)
|
||||
) {
|
||||
return recordIds.sort((a, b) => {
|
||||
const recordA = get(recordStoreFamilyState(a));
|
||||
const recordB = get(recordStoreFamilyState(b));
|
||||
|
||||
const positionA = recordA?.position;
|
||||
const positionB = recordB?.position;
|
||||
|
||||
if (!isDefined(positionA) && !isDefined(positionB)) return 0;
|
||||
if (!isDefined(positionA)) return -1;
|
||||
if (!isDefined(positionB)) return 1;
|
||||
|
||||
return positionA - positionB;
|
||||
});
|
||||
}
|
||||
|
||||
return recordIds;
|
||||
},
|
||||
});
|
||||
|
||||
+29
-10
@@ -58,18 +58,37 @@ export const useHandleDragOneCalendarCard = () => {
|
||||
.getLoadable(calendarDayRecordIdsSelector(destinationDate))
|
||||
.getValue() as string[];
|
||||
|
||||
const recordsWithPosition = extractRecordPositions(
|
||||
destinationRecordIds,
|
||||
snapshot,
|
||||
);
|
||||
const targetDayIsEmpty = destinationRecordIds.length === 0;
|
||||
|
||||
const targetRecordId = destinationRecordIds[destinationIndex];
|
||||
let newPosition: number;
|
||||
|
||||
const newPosition = computeNewPositionOfDraggedRecord({
|
||||
arrayOfRecordsWithPosition: recordsWithPosition,
|
||||
idOfItemToMove: recordId,
|
||||
idOfTargetItem: targetRecordId,
|
||||
});
|
||||
if (targetDayIsEmpty) {
|
||||
newPosition = 1;
|
||||
} else {
|
||||
const recordsWithPosition = extractRecordPositions(
|
||||
destinationRecordIds,
|
||||
snapshot,
|
||||
);
|
||||
|
||||
const isDroppedAfterList =
|
||||
destinationIndex >= recordsWithPosition.length;
|
||||
|
||||
const targetRecord = isDroppedAfterList
|
||||
? recordsWithPosition.at(-1)
|
||||
: recordsWithPosition.at(destinationIndex);
|
||||
|
||||
if (!isDefined(targetRecord)) {
|
||||
throw new Error(
|
||||
`targetRecord cannot be found in passed recordsWithPosition, this should not happen.`,
|
||||
);
|
||||
}
|
||||
|
||||
newPosition = computeNewPositionOfDraggedRecord({
|
||||
arrayOfRecordsWithPosition: recordsWithPosition,
|
||||
idOfItemToMove: recordId,
|
||||
idOfTargetItem: targetRecord.id,
|
||||
});
|
||||
}
|
||||
|
||||
const targetDate = parse(destinationDate, 'yyyy-MM-dd', new Date());
|
||||
const currentFieldValue = record[calendarFieldMetadata.name];
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||
import { type RecordGqlOperationGqlRecordFields } from '@/object-record/graphql/types/RecordGqlOperationGqlRecordFields';
|
||||
import { type ObjectPermissions } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
export const generateGroupByRecordsQuery = ({
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
recordGqlFields,
|
||||
computeReferences,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
recordGqlFields?: RecordGqlOperationGqlRecordFields;
|
||||
computeReferences?: boolean;
|
||||
objectPermissionsByObjectMetadataId: Record<
|
||||
string,
|
||||
ObjectPermissions & { objectMetadataId: string }
|
||||
>;
|
||||
}) => gql`
|
||||
query GroupBy${capitalize(
|
||||
objectMetadataItem.namePlural,
|
||||
)}($groupBy: [${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}GroupByInput!]!, $filter: ${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}FilterInput, $orderBy: [${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}OrderByWithGroupByInput!], $orderByForRecords: [${capitalize(
|
||||
objectMetadataItem.nameSingular,
|
||||
)}OrderByInput], $viewId: UUID) {
|
||||
${objectMetadataItem.namePlural}GroupBy(
|
||||
groupBy: $groupBy
|
||||
filter: $filter
|
||||
orderBy: $orderBy
|
||||
orderByForRecords: $orderByForRecords
|
||||
viewId: $viewId
|
||||
) {
|
||||
edges {
|
||||
node ${mapObjectMetadataToGraphQLQuery({
|
||||
objectMetadataItems,
|
||||
objectMetadataItem,
|
||||
recordGqlFields,
|
||||
computeReferences,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
})}
|
||||
cursor
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
groupByDimensionValues
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -6,11 +6,14 @@ export const getQueryIdentifier = ({
|
||||
orderBy,
|
||||
limit,
|
||||
cursorFilter,
|
||||
groupBy,
|
||||
}: RecordGqlOperationVariables & {
|
||||
objectNameSingular: string;
|
||||
groupBy?: Record<string, any>[];
|
||||
}) =>
|
||||
objectNameSingular +
|
||||
JSON.stringify(filter) +
|
||||
JSON.stringify(orderBy) +
|
||||
limit +
|
||||
(cursorFilter ? JSON.stringify(cursorFilter) : undefined);
|
||||
(cursorFilter ? JSON.stringify(cursorFilter) : undefined) +
|
||||
(groupBy ? JSON.stringify(groupBy) : '');
|
||||
|
||||
Reference in New Issue
Block a user