[Dashboards]: polish week on date granularity (#16128)
https://github.com/user-attachments/assets/2b7ef230-49e2-4882-9029-6df3d01f5f20
This commit is contained in:
+1
-1
@@ -45,7 +45,7 @@ export const generateDateGroupsInRange = ({
|
||||
break;
|
||||
|
||||
case ObjectRecordGroupByDateGranularity.WEEK:
|
||||
currentDateCursor.setDate(currentDateCursor.getDate() + 1);
|
||||
currentDateCursor.setDate(currentDateCursor.getDate() + 7);
|
||||
break;
|
||||
|
||||
case ObjectRecordGroupByDateGranularity.MONTH:
|
||||
|
||||
+5
@@ -1,3 +1,4 @@
|
||||
import { useDateTimeFormat } from '@/localization/hooks/useDateTimeFormat';
|
||||
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
|
||||
import { generateGroupByAggregateQuery } from '@/object-record/record-aggregate/utils/generateGroupByAggregateQuery';
|
||||
import { getAvailableAggregationsFromObjectFields } from '@/object-record/utils/getAvailableAggregationsFromObjectFields';
|
||||
@@ -24,6 +25,8 @@ export const useGraphWidgetGroupByQuery = ({
|
||||
configuration: GroupByChartConfiguration;
|
||||
limit?: number;
|
||||
}) => {
|
||||
const { calendarStartDay } = useDateTimeFormat();
|
||||
|
||||
const { objectMetadataItem, aggregateField, gqlOperationFilter } =
|
||||
useGraphWidgetQueryCommon({
|
||||
objectMetadataItemId,
|
||||
@@ -63,6 +66,7 @@ export const useGraphWidgetGroupByQuery = ({
|
||||
chartConfiguration: configuration,
|
||||
aggregateOperation: aggregateOperation,
|
||||
limit,
|
||||
firstDayOfTheWeek: calendarStartDay,
|
||||
})
|
||||
: generateGroupByQueryVariablesFromBarOrLineChartConfiguration({
|
||||
objectMetadataItem,
|
||||
@@ -71,6 +75,7 @@ export const useGraphWidgetGroupByQuery = ({
|
||||
| LineChartConfiguration,
|
||||
aggregateOperation: aggregateOperation,
|
||||
limit,
|
||||
firstDayOfTheWeek: calendarStartDay,
|
||||
});
|
||||
|
||||
const variables = {
|
||||
|
||||
+2
@@ -16,4 +16,6 @@ exports[`formatDateByGranularity should format date for NONE granularity 1`] = `
|
||||
|
||||
exports[`formatDateByGranularity should format date for QUARTER granularity 1`] = `"Q1 2024"`;
|
||||
|
||||
exports[`formatDateByGranularity should format date for WEEK granularity 1`] = `"Mar 20, 2024 20 - 26, 2024"`;
|
||||
|
||||
exports[`formatDateByGranularity should format date for YEAR granularity 1`] = `"2024"`;
|
||||
|
||||
+79
@@ -1,4 +1,5 @@
|
||||
import { buildGroupByFieldObject } from '@/page-layout/widgets/graph/utils/buildGroupByFieldObject';
|
||||
import { CalendarStartDay } from 'twenty-shared';
|
||||
import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -101,4 +102,82 @@ describe('buildGroupByFieldObject', () => {
|
||||
|
||||
expect(result).toEqual({ status: true });
|
||||
});
|
||||
|
||||
it('should include weekStartDay for WEEK granularity with MONDAY', () => {
|
||||
const field = {
|
||||
name: 'createdAt',
|
||||
type: FieldMetadataType.DATE,
|
||||
} as any;
|
||||
|
||||
const result = buildGroupByFieldObject({
|
||||
field,
|
||||
dateGranularity: ObjectRecordGroupByDateGranularity.WEEK,
|
||||
firstDayOfTheWeek: CalendarStartDay.MONDAY,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
createdAt: {
|
||||
granularity: ObjectRecordGroupByDateGranularity.WEEK,
|
||||
weekStartDay: 'MONDAY',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should include weekStartDay for WEEK granularity with SUNDAY', () => {
|
||||
const field = {
|
||||
name: 'createdAt',
|
||||
type: FieldMetadataType.DATE,
|
||||
} as any;
|
||||
|
||||
const result = buildGroupByFieldObject({
|
||||
field,
|
||||
dateGranularity: ObjectRecordGroupByDateGranularity.WEEK,
|
||||
firstDayOfTheWeek: CalendarStartDay.SUNDAY,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
createdAt: {
|
||||
granularity: ObjectRecordGroupByDateGranularity.WEEK,
|
||||
weekStartDay: 'SUNDAY',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not include weekStartDay for WEEK granularity with SYSTEM', () => {
|
||||
const field = {
|
||||
name: 'createdAt',
|
||||
type: FieldMetadataType.DATE,
|
||||
} as any;
|
||||
|
||||
const result = buildGroupByFieldObject({
|
||||
field,
|
||||
dateGranularity: ObjectRecordGroupByDateGranularity.WEEK,
|
||||
firstDayOfTheWeek: CalendarStartDay.SYSTEM,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
createdAt: {
|
||||
granularity: ObjectRecordGroupByDateGranularity.WEEK,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not include weekStartDay for non-WEEK granularity', () => {
|
||||
const field = {
|
||||
name: 'createdAt',
|
||||
type: FieldMetadataType.DATE,
|
||||
} as any;
|
||||
|
||||
const result = buildGroupByFieldObject({
|
||||
field,
|
||||
dateGranularity: ObjectRecordGroupByDateGranularity.MONTH,
|
||||
firstDayOfTheWeek: CalendarStartDay.MONDAY,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
createdAt: {
|
||||
granularity: ObjectRecordGroupByDateGranularity.MONTH,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+49
@@ -27,12 +27,14 @@ describe('formatDateByGranularity', () => {
|
||||
const testCases: {
|
||||
granularity:
|
||||
| ObjectRecordGroupByDateGranularity.DAY
|
||||
| ObjectRecordGroupByDateGranularity.WEEK
|
||||
| ObjectRecordGroupByDateGranularity.MONTH
|
||||
| ObjectRecordGroupByDateGranularity.QUARTER
|
||||
| ObjectRecordGroupByDateGranularity.YEAR
|
||||
| ObjectRecordGroupByDateGranularity.NONE;
|
||||
}[] = [
|
||||
{ granularity: ObjectRecordGroupByDateGranularity.DAY },
|
||||
{ granularity: ObjectRecordGroupByDateGranularity.WEEK },
|
||||
{ granularity: ObjectRecordGroupByDateGranularity.MONTH },
|
||||
{ granularity: ObjectRecordGroupByDateGranularity.QUARTER },
|
||||
{ granularity: ObjectRecordGroupByDateGranularity.YEAR },
|
||||
@@ -46,6 +48,53 @@ describe('formatDateByGranularity', () => {
|
||||
},
|
||||
);
|
||||
|
||||
describe('week calculations', () => {
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest
|
||||
.spyOn(Date.prototype, 'toLocaleDateString')
|
||||
.mockImplementation((_locales, options) => {
|
||||
if (options?.weekday === 'long') return 'Wednesday';
|
||||
if (options?.month === 'long' && !isDefined(options?.year))
|
||||
return 'March';
|
||||
if (options?.month === 'long' && isDefined(options?.year))
|
||||
return 'March 2024';
|
||||
if (options?.month === 'short') return 'Mar 20, 2024';
|
||||
return '3/20/2024';
|
||||
});
|
||||
});
|
||||
|
||||
it('should format week within same month', () => {
|
||||
const date = new Date('2024-05-06');
|
||||
const result = formatDateByGranularity(
|
||||
date,
|
||||
ObjectRecordGroupByDateGranularity.WEEK,
|
||||
);
|
||||
expect(result).toBe('May 6 - 12, 2024');
|
||||
});
|
||||
|
||||
it('should format week crossing months', () => {
|
||||
const date = new Date('2024-05-27');
|
||||
const result = formatDateByGranularity(
|
||||
date,
|
||||
ObjectRecordGroupByDateGranularity.WEEK,
|
||||
);
|
||||
expect(result).toBe('May 27 - Jun 2, 2024');
|
||||
});
|
||||
|
||||
it('should format week crossing years', () => {
|
||||
const date = new Date('2024-12-30');
|
||||
const result = formatDateByGranularity(
|
||||
date,
|
||||
ObjectRecordGroupByDateGranularity.WEEK,
|
||||
);
|
||||
expect(result).toBe('Dec 30, 2024 - Jan 5, 2025');
|
||||
});
|
||||
});
|
||||
|
||||
describe('quarter calculations', () => {
|
||||
const quarterTestCases: {
|
||||
date: Date;
|
||||
|
||||
+23
-6
@@ -3,7 +3,11 @@ import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/uti
|
||||
import { isFieldMorphRelation } from '@/object-record/record-field/ui/types/guards/isFieldMorphRelation';
|
||||
import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity.constant';
|
||||
import { type ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
|
||||
import { CalendarStartDay } from 'twenty-shared';
|
||||
import {
|
||||
type FirstDayOfTheWeek,
|
||||
ObjectRecordGroupByDateGranularity,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
@@ -12,10 +16,12 @@ export const buildGroupByFieldObject = ({
|
||||
field,
|
||||
subFieldName,
|
||||
dateGranularity,
|
||||
firstDayOfTheWeek,
|
||||
}: {
|
||||
field: FieldMetadataItem;
|
||||
subFieldName?: string | null;
|
||||
dateGranularity?: ObjectRecordGroupByDateGranularity;
|
||||
firstDayOfTheWeek?: number | null;
|
||||
}): Record<string, boolean | Record<string, boolean | string>> => {
|
||||
const isRelation = isFieldRelation(field) || isFieldMorphRelation(field);
|
||||
const isComposite = isCompositeFieldType(field.type);
|
||||
@@ -41,11 +47,22 @@ export const buildGroupByFieldObject = ({
|
||||
}
|
||||
|
||||
if (isDateField) {
|
||||
return {
|
||||
[field.name]: {
|
||||
granularity: dateGranularity ?? GRAPH_DEFAULT_DATE_GRANULARITY,
|
||||
},
|
||||
};
|
||||
const granularity = dateGranularity ?? GRAPH_DEFAULT_DATE_GRANULARITY;
|
||||
const result: Record<string, string> = { granularity };
|
||||
|
||||
if (
|
||||
granularity === ObjectRecordGroupByDateGranularity.WEEK &&
|
||||
isDefined(firstDayOfTheWeek) &&
|
||||
firstDayOfTheWeek !== CalendarStartDay.SYSTEM
|
||||
) {
|
||||
const weekStartDay = CalendarStartDay[
|
||||
firstDayOfTheWeek
|
||||
] as FirstDayOfTheWeek;
|
||||
|
||||
result.weekStartDay = weekStartDay;
|
||||
}
|
||||
|
||||
return { [field.name]: result };
|
||||
}
|
||||
|
||||
return { [field.name]: true };
|
||||
|
||||
+24
-4
@@ -17,12 +17,32 @@ export const formatDateByGranularity = (
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
});
|
||||
case ObjectRecordGroupByDateGranularity.WEEK:
|
||||
return date.toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
case ObjectRecordGroupByDateGranularity.WEEK: {
|
||||
const weekStart = new Date(date);
|
||||
const weekEnd = new Date(date);
|
||||
weekEnd.setDate(weekEnd.getDate() + 6);
|
||||
|
||||
const startMonth = weekStart.toLocaleDateString(undefined, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
});
|
||||
const endMonth = weekEnd.toLocaleDateString(undefined, {
|
||||
month: 'short',
|
||||
});
|
||||
const startDay = weekStart.getDate();
|
||||
const endDay = weekEnd.getDate();
|
||||
const startYear = weekStart.getFullYear();
|
||||
const endYear = weekEnd.getFullYear();
|
||||
|
||||
if (startYear !== endYear) {
|
||||
return `${startMonth} ${startDay}, ${startYear} - ${endMonth} ${endDay}, ${endYear}`;
|
||||
}
|
||||
|
||||
if (startMonth !== endMonth) {
|
||||
return `${startMonth} ${startDay} - ${endMonth} ${endDay}, ${endYear}`;
|
||||
}
|
||||
|
||||
return `${startMonth} ${startDay} - ${endDay}, ${endYear}`;
|
||||
}
|
||||
case ObjectRecordGroupByDateGranularity.MONTH:
|
||||
return date.toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
|
||||
+4
@@ -18,11 +18,13 @@ export const generateGroupByQueryVariablesFromBarOrLineChartConfiguration = ({
|
||||
chartConfiguration,
|
||||
aggregateOperation,
|
||||
limit,
|
||||
firstDayOfTheWeek,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
chartConfiguration: BarChartConfiguration | LineChartConfiguration;
|
||||
aggregateOperation?: string;
|
||||
limit?: number;
|
||||
firstDayOfTheWeek?: number;
|
||||
}) => {
|
||||
const groupByFieldXId = chartConfiguration.primaryAxisGroupByFieldMetadataId;
|
||||
|
||||
@@ -59,6 +61,7 @@ export const generateGroupByQueryVariablesFromBarOrLineChartConfiguration = ({
|
||||
subFieldName: groupBySubFieldNameX,
|
||||
dateGranularity:
|
||||
chartConfiguration.primaryAxisDateGranularity ?? undefined,
|
||||
firstDayOfTheWeek,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -69,6 +72,7 @@ export const generateGroupByQueryVariablesFromBarOrLineChartConfiguration = ({
|
||||
subFieldName: groupBySubFieldNameY,
|
||||
dateGranularity:
|
||||
chartConfiguration.secondaryAxisGroupByDateGranularity ?? undefined,
|
||||
firstDayOfTheWeek,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
+3
@@ -15,11 +15,13 @@ export const generateGroupByQueryVariablesFromPieChartConfiguration = ({
|
||||
chartConfiguration,
|
||||
aggregateOperation,
|
||||
limit,
|
||||
firstDayOfTheWeek,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
chartConfiguration: PieChartConfiguration;
|
||||
aggregateOperation?: string;
|
||||
limit?: number;
|
||||
firstDayOfTheWeek?: number;
|
||||
}) => {
|
||||
const groupByFieldId = chartConfiguration.groupByFieldMetadataId;
|
||||
const groupBySubFieldName =
|
||||
@@ -43,6 +45,7 @@ export const generateGroupByQueryVariablesFromPieChartConfiguration = ({
|
||||
field: groupByField,
|
||||
subFieldName: groupBySubFieldName,
|
||||
dateGranularity,
|
||||
firstDayOfTheWeek,
|
||||
}),
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user