diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/sortTwoDimensionalBarChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/sortTwoDimensionalBarChartData.ts index 2c6a047556..76465215b0 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/sortTwoDimensionalBarChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/sortTwoDimensionalBarChartData.ts @@ -4,9 +4,11 @@ import { sortBarChartDataBySecondaryDimensionSum } from '@/page-layout/widgets/g import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor'; import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue'; import { sortSecondaryAxisData } from '@/page-layout/widgets/graph/utils/sortSecondaryAxisData'; -import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually'; +import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded'; import { type BarDatum } from '@nivo/bar'; +import { type CompositeFieldSubFieldName } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; +import { type FieldMetadataType } from '~/generated-metadata/graphql'; import { type BarChartConfiguration, GraphOrderBy } from '~/generated/graphql'; type SortTwoDimensionalBarChartDataConfiguration = { @@ -18,6 +20,8 @@ type SortTwoDimensionalBarChartDataConfiguration = { primaryAxisSelectFieldOptions?: FieldMetadataItemOption[] | null; secondaryAxisFormattedToRawLookup?: Map; secondaryAxisSelectFieldOptions?: FieldMetadataItemOption[] | null; + secondaryAxisFieldType?: FieldMetadataType; + secondaryAxisSubFieldName?: CompositeFieldSubFieldName; }; type SortTwoDimensionalBarChartDataResult = { @@ -41,6 +45,8 @@ export const sortTwoDimensionalBarChartData = ({ primaryAxisSelectFieldOptions, secondaryAxisFormattedToRawLookup, secondaryAxisSelectFieldOptions, + secondaryAxisFieldType, + secondaryAxisSubFieldName, }: SortTwoDimensionalBarChartDataConfiguration): SortTwoDimensionalBarChartDataResult => { const sortedKeys = sortSecondaryAxisData({ items: keys, @@ -49,6 +55,8 @@ export const sortTwoDimensionalBarChartData = ({ formattedToRawLookup: secondaryAxisFormattedToRawLookup, selectFieldOptions: secondaryAxisSelectFieldOptions, getFormattedValue: (item) => item, + fieldType: secondaryAxisFieldType, + subFieldName: secondaryAxisSubFieldName, }); const sortedSeries: BarChartSeries[] = sortedKeys.map((key) => ({ @@ -70,14 +78,15 @@ export const sortTwoDimensionalBarChartData = ({ orderBy: primaryAxisOrderBy, }); } else { - sortedData = sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually({ - data, - orderBy: primaryAxisOrderBy, - manualSortOrder: primaryAxisManualSortOrder, - formattedToRawLookup: primaryAxisFormattedToRawLookup, - getFormattedValue: (datum) => datum[indexByKey] as string, - selectFieldOptions: primaryAxisSelectFieldOptions, - }); + sortedData = + sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded({ + data, + orderBy: primaryAxisOrderBy, + manualSortOrder: primaryAxisManualSortOrder, + formattedToRawLookup: primaryAxisFormattedToRawLookup, + getFormattedValue: (datum) => datum[indexByKey] as string, + selectFieldOptions: primaryAxisSelectFieldOptions, + }); } } diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformOneDimensionalGroupByToBarChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformOneDimensionalGroupByToBarChartData.ts index 9dc8692490..c650f4b2f4 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformOneDimensionalGroupByToBarChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformOneDimensionalGroupByToBarChartData.ts @@ -9,7 +9,7 @@ import { type GroupByRawResult } from '@/page-layout/widgets/graph/types/GroupBy import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue'; import { getFieldKey } from '@/page-layout/widgets/graph/utils/getFieldKey'; import { processOneDimensionalGroupByResults } from '@/page-layout/widgets/graph/utils/processOneDimensionalGroupByResults'; -import { sortChartData } from '@/page-layout/widgets/graph/utils/sortChartData'; +import { sortChartDataIfNeeded } from '@/page-layout/widgets/graph/utils/sortChartDataIfNeeded'; import { type BarDatum } from '@nivo/bar'; import { type FirstDayOfTheWeek, @@ -79,7 +79,7 @@ export const transformOneDimensionalGroupByToBarChartData = ({ }), ); - const sortedData = sortChartData({ + const sortedData = sortChartDataIfNeeded({ data: unsortedData, orderBy: configuration.primaryAxisOrderBy, manualSortOrder: configuration.primaryAxisManualSortOrder, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformTwoDimensionalGroupByToBarChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformTwoDimensionalGroupByToBarChartData.ts index 8a5950afa1..fbbaa38038 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformTwoDimensionalGroupByToBarChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformTwoDimensionalGroupByToBarChartData.ts @@ -10,6 +10,7 @@ import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDim import { getFieldKey } from '@/page-layout/widgets/graph/utils/getFieldKey'; import { processTwoDimensionalGroupByResults } from '@/page-layout/widgets/graph/utils/processTwoDimensionalGroupByResults'; import { type BarDatum } from '@nivo/bar'; +import { type CompositeFieldSubFieldName } from 'twenty-shared/types'; import { type FirstDayOfTheWeek } from 'twenty-shared/utils'; import { type BarChartConfiguration } from '~/generated/graphql'; @@ -81,6 +82,11 @@ export const transformTwoDimensionalGroupByToBarChartData = ({ primaryAxisSelectFieldOptions: groupByFieldX.options, secondaryAxisFormattedToRawLookup: yFormattedToRawLookup, secondaryAxisSelectFieldOptions: groupByFieldY.options, + secondaryAxisFieldType: groupByFieldY.type, + secondaryAxisSubFieldName: + (configuration.secondaryAxisGroupBySubFieldName ?? undefined) as + | CompositeFieldSubFieldName + | undefined, }); const { limitedData, limitedKeys, limitedSeries, hasTooManyGroups } = diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/sortTwoDimensionalLineChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/sortTwoDimensionalLineChartData.ts index 366f5a5411..20962fc700 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/sortTwoDimensionalLineChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/sortTwoDimensionalLineChartData.ts @@ -4,8 +4,10 @@ import { type LineChartSeries } from '@/page-layout/widgets/graph/graphWidgetLin import { sortLineChartDataBySecondaryDimensionSum } from '@/page-layout/widgets/graph/graphWidgetLineChart/utils/sortLineChartDataBySecondaryDimensionSum'; import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue'; import { sortSecondaryAxisData } from '@/page-layout/widgets/graph/utils/sortSecondaryAxisData'; -import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually'; +import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded'; +import { type CompositeFieldSubFieldName } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; +import { type FieldMetadataType } from '~/generated-metadata/graphql'; import { type LineChartConfiguration, GraphOrderBy } from '~/generated/graphql'; type SortTwoDimensionalLineChartDataConfiguration = { @@ -15,6 +17,8 @@ type SortTwoDimensionalLineChartDataConfiguration = { primaryAxisSelectFieldOptions?: FieldMetadataItemOption[] | null; secondaryAxisFormattedToRawLookup?: Map; secondaryAxisSelectFieldOptions?: FieldMetadataItemOption[] | null; + secondaryAxisFieldType?: FieldMetadataType; + secondaryAxisSubFieldName?: CompositeFieldSubFieldName; }; type SortTwoDimensionalLineChartDataResult = { @@ -33,6 +37,8 @@ export const sortTwoDimensionalLineChartData = ({ primaryAxisSelectFieldOptions, secondaryAxisFormattedToRawLookup, secondaryAxisSelectFieldOptions, + secondaryAxisFieldType, + secondaryAxisSubFieldName, }: SortTwoDimensionalLineChartDataConfiguration): SortTwoDimensionalLineChartDataResult => { let sortedSeries = series; @@ -48,7 +54,7 @@ export const sortTwoDimensionalLineChartData = ({ } else { sortedSeries = series.map((seriesItem) => { const sortedDataPoints = - sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually({ + sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded({ data: seriesItem.data, orderBy: primaryAxisOrderBy, manualSortOrder: primaryAxisManualSortOrder, @@ -73,6 +79,8 @@ export const sortTwoDimensionalLineChartData = ({ formattedToRawLookup: secondaryAxisFormattedToRawLookup, selectFieldOptions: secondaryAxisSelectFieldOptions, getFormattedValue: (item) => item.id, + fieldType: secondaryAxisFieldType, + subFieldName: secondaryAxisSubFieldName, }); return { diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/transformOneDimensionalGroupByToLineChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/transformOneDimensionalGroupByToLineChartData.ts index b20cd0c168..a5e1d5e2ab 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/transformOneDimensionalGroupByToLineChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/transformOneDimensionalGroupByToLineChartData.ts @@ -9,7 +9,7 @@ import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor'; import { type GroupByRawResult } from '@/page-layout/widgets/graph/types/GroupByRawResult'; import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue'; import { processOneDimensionalGroupByResults } from '@/page-layout/widgets/graph/utils/processOneDimensionalGroupByResults'; -import { sortChartData } from '@/page-layout/widgets/graph/utils/sortChartData'; +import { sortChartDataIfNeeded } from '@/page-layout/widgets/graph/utils/sortChartDataIfNeeded'; import { isFieldMetadataSelectKind, type FirstDayOfTheWeek, @@ -65,7 +65,7 @@ export const transformOneDimensionalGroupByToLineChartData = ({ }), ); - const sortedData = sortChartData({ + const sortedData = sortChartDataIfNeeded({ data: unsortedData, orderBy: configuration.primaryAxisOrderBy, manualSortOrder: configuration.primaryAxisManualSortOrder, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/transformTwoDimensionalGroupByToLineChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/transformTwoDimensionalGroupByToLineChartData.ts index a6621d3595..8a669caa76 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/transformTwoDimensionalGroupByToLineChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/transformTwoDimensionalGroupByToLineChartData.ts @@ -10,6 +10,7 @@ import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor'; import { type GroupByRawResult } from '@/page-layout/widgets/graph/types/GroupByRawResult'; import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue'; import { processTwoDimensionalGroupByResults } from '@/page-layout/widgets/graph/utils/processTwoDimensionalGroupByResults'; +import { type CompositeFieldSubFieldName } from 'twenty-shared/types'; import { type FirstDayOfTheWeek } from 'twenty-shared/utils'; import { type LineChartConfiguration } from '~/generated/graphql'; @@ -70,6 +71,11 @@ export const transformTwoDimensionalGroupByToLineChartData = ({ primaryAxisSelectFieldOptions: groupByFieldX.options, secondaryAxisFormattedToRawLookup: yFormattedToRawLookup, secondaryAxisSelectFieldOptions: groupByFieldY.options, + secondaryAxisFieldType: groupByFieldY.type, + secondaryAxisSubFieldName: + (configuration.secondaryAxisGroupBySubFieldName ?? undefined) as + | CompositeFieldSubFieldName + | undefined, }); const { limitedSeries, hasTooManyGroups } = limitTwoDimensionalLineChartData({ diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/transformGroupByDataToPieChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/transformGroupByDataToPieChartData.ts index 9fb0260f38..f4a1d5181b 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/transformGroupByDataToPieChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/transformGroupByDataToPieChartData.ts @@ -8,7 +8,7 @@ import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor'; import { type GroupByRawResult } from '@/page-layout/widgets/graph/types/GroupByRawResult'; import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue'; import { processOneDimensionalGroupByResults } from '@/page-layout/widgets/graph/utils/processOneDimensionalGroupByResults'; -import { sortChartData } from '@/page-layout/widgets/graph/utils/sortChartData'; +import { sortChartDataIfNeeded } from '@/page-layout/widgets/graph/utils/sortChartDataIfNeeded'; import { type FirstDayOfTheWeek } from 'twenty-shared/types'; import { isDefined, isFieldMetadataSelectKind } from 'twenty-shared/utils'; import { type PieChartConfiguration } from '~/generated/graphql'; @@ -106,7 +106,7 @@ export const transformGroupByDataToPieChartData = ({ rawValue: isDefined(rawXValue) ? String(rawXValue) : null, })); - const sortedDataWithRawValues = sortChartData({ + const sortedDataWithRawValues = sortChartDataIfNeeded({ data: unsortedDataWithRawValues, orderBy: configuration.orderBy ?? undefined, manualSortOrder: configuration.manualSortOrder ?? undefined, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/sortChartData.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/sortChartDataIfNeeded.test.ts similarity index 82% rename from packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/sortChartData.test.ts rename to packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/sortChartDataIfNeeded.test.ts index c356a146f7..7f699b6ab2 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/sortChartData.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/sortChartDataIfNeeded.test.ts @@ -1,9 +1,9 @@ import { type FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem'; import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue'; -import { sortChartData } from '@/page-layout/widgets/graph/utils/sortChartData'; +import { sortChartDataIfNeeded } from '@/page-layout/widgets/graph/utils/sortChartDataIfNeeded'; import { GraphOrderBy } from '~/generated/graphql'; -describe('sortChartData', () => { +describe('sortChartDataIfNeeded', () => { type TestDataPoint = { label: string; value: number }; const testData: TestDataPoint[] = [ @@ -12,6 +12,18 @@ describe('sortChartData', () => { { label: 'Gamma', value: 20 }, ]; + const fieldAscTestData: TestDataPoint[] = [ + { label: 'Alpha', value: 10 }, + { label: 'Beta', value: 30 }, + { label: 'Gamma', value: 20 }, + ]; + + const fieldDescTestData: TestDataPoint[] = [ + { label: 'Gamma', value: 20 }, + { label: 'Beta', value: 30 }, + { label: 'Alpha', value: 10 }, + ]; + const formattedToRawLookup = new Map([ ['Alpha', 'ALPHA'], ['Beta', 'BETA'], @@ -23,7 +35,7 @@ describe('sortChartData', () => { describe('null or undefined orderBy', () => { it('should return data unchanged when orderBy is null', () => { - const result = sortChartData({ + const result = sortChartDataIfNeeded({ data: testData, orderBy: null, formattedToRawLookup, @@ -35,7 +47,7 @@ describe('sortChartData', () => { }); it('should return data unchanged when orderBy is undefined', () => { - const result = sortChartData({ + const result = sortChartDataIfNeeded({ data: testData, orderBy: undefined, formattedToRawLookup, @@ -48,9 +60,9 @@ describe('sortChartData', () => { }); describe('FIELD_ASC sorting', () => { - it('should sort by field value ascending', () => { - const result = sortChartData({ - data: testData, + it('should return data unchanged by field value ascending, since it is already sorted by the backend', () => { + const result = sortChartDataIfNeeded({ + data: fieldAscTestData, orderBy: GraphOrderBy.FIELD_ASC, formattedToRawLookup, getFieldValue, @@ -66,9 +78,9 @@ describe('sortChartData', () => { }); describe('FIELD_DESC sorting', () => { - it('should sort by field value descending', () => { - const result = sortChartData({ - data: testData, + it('should return data unchanged by field value descending, since it is already sorted by the backend', () => { + const result = sortChartDataIfNeeded({ + data: fieldDescTestData, orderBy: GraphOrderBy.FIELD_DESC, formattedToRawLookup, getFieldValue, @@ -85,7 +97,7 @@ describe('sortChartData', () => { describe('VALUE_ASC sorting', () => { it('should sort by numeric value ascending', () => { - const result = sortChartData({ + const result = sortChartDataIfNeeded({ data: testData, orderBy: GraphOrderBy.VALUE_ASC, formattedToRawLookup, @@ -99,7 +111,7 @@ describe('sortChartData', () => { describe('VALUE_DESC sorting', () => { it('should sort by numeric value descending', () => { - const result = sortChartData({ + const result = sortChartDataIfNeeded({ data: testData, orderBy: GraphOrderBy.VALUE_DESC, formattedToRawLookup, @@ -115,7 +127,7 @@ describe('sortChartData', () => { it('should sort by manual order', () => { const manualSortOrder = ['GAMMA', 'ALPHA', 'BETA']; - const result = sortChartData({ + const result = sortChartDataIfNeeded({ data: testData, orderBy: GraphOrderBy.MANUAL, manualSortOrder, @@ -132,7 +144,7 @@ describe('sortChartData', () => { }); it('should return data unchanged when manual order is undefined', () => { - const result = sortChartData({ + const result = sortChartDataIfNeeded({ data: testData, orderBy: GraphOrderBy.MANUAL, manualSortOrder: undefined, @@ -145,7 +157,7 @@ describe('sortChartData', () => { }); it('should return data unchanged when manual order is null', () => { - const result = sortChartData({ + const result = sortChartDataIfNeeded({ data: testData, orderBy: GraphOrderBy.MANUAL, manualSortOrder: null, @@ -166,7 +178,7 @@ describe('sortChartData', () => { ]; it('should sort by select option position ascending', () => { - const result = sortChartData({ + const result = sortChartDataIfNeeded({ data: testData, orderBy: GraphOrderBy.FIELD_POSITION_ASC, formattedToRawLookup, @@ -184,7 +196,7 @@ describe('sortChartData', () => { it('should throw error when select options are not provided', () => { expect(() => - sortChartData({ + sortChartDataIfNeeded({ data: testData, orderBy: GraphOrderBy.FIELD_POSITION_ASC, formattedToRawLookup, @@ -196,7 +208,7 @@ describe('sortChartData', () => { it('should throw error when select options are empty', () => { expect(() => - sortChartData({ + sortChartDataIfNeeded({ data: testData, orderBy: GraphOrderBy.FIELD_POSITION_ASC, formattedToRawLookup, @@ -216,7 +228,7 @@ describe('sortChartData', () => { ]; it('should sort by select option position descending', () => { - const result = sortChartData({ + const result = sortChartDataIfNeeded({ data: testData, orderBy: GraphOrderBy.FIELD_POSITION_DESC, formattedToRawLookup, @@ -237,7 +249,7 @@ describe('sortChartData', () => { it('should not mutate the original data array', () => { const originalData = [...testData]; - sortChartData({ + sortChartDataIfNeeded({ data: testData, orderBy: GraphOrderBy.FIELD_ASC, formattedToRawLookup, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually.test.ts index 8a84fc83e5..cf08a31c59 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually.test.ts @@ -1,4 +1,4 @@ -import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually'; +import { sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded } from '@/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded'; import { GraphOrderBy } from '~/generated/graphql'; type TestItem = { label: string }; @@ -8,44 +8,55 @@ const testData: TestItem[] = [ { label: 'Alpha' }, { label: 'Gamma' }, ]; + +const fieldAscTestData: TestItem[] = [ + { label: 'Alpha' }, + { label: 'Beta' }, + { label: 'Gamma' }, +]; + const formattedToRawLookup = new Map([ ['Alpha', 'ALPHA'], ['Beta', 'BETA'], ['Gamma', 'GAMMA'], ]); + const getFormattedValue = (item: TestItem) => item.label; -describe('sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually', () => { - it('should sort by FIELD_ASC', () => { - const result = sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually({ - data: testData, - orderBy: GraphOrderBy.FIELD_ASC, - formattedToRawLookup, - getFormattedValue, - }); +describe('sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded', () => { + it('should return data unchanged by FIELD_ASC, since it is already sorted by the backend', () => { + const result = + sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded({ + data: fieldAscTestData, + orderBy: GraphOrderBy.FIELD_ASC, + formattedToRawLookup, + getFormattedValue, + }); expect(result.map((i) => i.label)).toEqual(['Alpha', 'Beta', 'Gamma']); }); it('should sort by MANUAL order', () => { - const result = sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually({ - data: testData, - orderBy: GraphOrderBy.MANUAL, - manualSortOrder: ['GAMMA', 'ALPHA', 'BETA'], - formattedToRawLookup, - getFormattedValue, - }); + const result = + sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded({ + data: testData, + orderBy: GraphOrderBy.MANUAL, + manualSortOrder: ['GAMMA', 'ALPHA', 'BETA'], + formattedToRawLookup, + getFormattedValue, + }); expect(result.map((i) => i.label)).toEqual(['Gamma', 'Alpha', 'Beta']); }); it('should return data unchanged when orderBy is undefined', () => { - const result = sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually({ - data: testData, - orderBy: undefined, - formattedToRawLookup, - getFormattedValue, - }); + const result = + sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded({ + data: testData, + orderBy: undefined, + formattedToRawLookup, + getFormattedValue, + }); expect(result).toEqual(testData); }); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/compareDimensionValues.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/compareDimensionValues.ts new file mode 100644 index 0000000000..315270c8a3 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/compareDimensionValues.ts @@ -0,0 +1,72 @@ +import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue'; +import { Temporal } from 'temporal-polyfill'; +import { type CompositeFieldSubFieldName } from 'twenty-shared/types'; +import { + isDefined, + isFieldMetadataDateKind, + isFieldMetadataNumericKind, +} from 'twenty-shared/utils'; +import { FieldMetadataType } from '~/generated-metadata/graphql'; + +const parseDate = ( + rawValue: RawDimensionValue | undefined, + fieldType?: FieldMetadataType, +): Temporal.PlainDate | null => { + if (!isDefined(rawValue)) return null; + const stringValue = String(rawValue); + return fieldType === FieldMetadataType.DATE + ? Temporal.PlainDate.from(stringValue) + : Temporal.Instant.from(stringValue) + .toZonedDateTimeISO('UTC') + .toPlainDate(); +}; + +type CompareDimensionValuesParams = { + rawValueA: RawDimensionValue | undefined; + rawValueB: RawDimensionValue | undefined; + formattedValueA: string; + formattedValueB: string; + direction: 'ASC' | 'DESC'; + fieldType?: FieldMetadataType; + subFieldName?: CompositeFieldSubFieldName; +}; + +export const compareDimensionValues = ({ + rawValueA, + rawValueB, + formattedValueA, + formattedValueB, + direction, + fieldType, + subFieldName, +}: CompareDimensionValuesParams): number => { + const applyDirection = (comparison: number) => + direction === 'ASC' ? comparison : -comparison; + + if (isDefined(fieldType)) { + if (isFieldMetadataDateKind(fieldType)) { + const dateA = parseDate(rawValueA, fieldType); + const dateB = parseDate(rawValueB, fieldType); + if (isDefined(dateA) && isDefined(dateB)) { + return applyDirection(Temporal.PlainDate.compare(dateA, dateB)); + } + } + + if (fieldType === FieldMetadataType.CURRENCY) { + if (subFieldName === 'amountMicros') { + if (isDefined(rawValueA) && isDefined(rawValueB)) { + return applyDirection(Number(rawValueA) - Number(rawValueB)); + } + } + return applyDirection(formattedValueA.localeCompare(formattedValueB)); + } + + if (isFieldMetadataNumericKind(fieldType)) { + if (isDefined(rawValueA) && isDefined(rawValueB)) { + return applyDirection(Number(rawValueA) - Number(rawValueB)); + } + } + } + + return applyDirection(formattedValueA.localeCompare(formattedValueB)); +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortChartDataIfNeeded.ts similarity index 91% rename from packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortChartData.ts rename to packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortChartDataIfNeeded.ts index 7b1a91e2b0..62dc169528 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortChartDataIfNeeded.ts @@ -15,7 +15,7 @@ type SortChartDataParams = { selectFieldOptions?: FieldMetadataItemOption[] | null; }; -export const sortChartData = ({ +export const sortChartDataIfNeeded = ({ data, orderBy, manualSortOrder, @@ -50,13 +50,8 @@ export const sortChartData = ({ case GraphOrderBy.VALUE_DESC: return data.toSorted((a, b) => getNumericValue(b) - getNumericValue(a)); case GraphOrderBy.FIELD_ASC: - return data.toSorted((a, b) => - getFieldValue(a).localeCompare(getFieldValue(b)), - ); case GraphOrderBy.FIELD_DESC: - return data.toSorted((a, b) => - getFieldValue(b).localeCompare(getFieldValue(a)), - ); + return data; case GraphOrderBy.FIELD_POSITION_ASC: if (!isDefined(selectFieldOptions) || selectFieldOptions.length === 0) { throw new Error('Select field options are required'); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortSecondaryAxisData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortSecondaryAxisData.ts index a0aa8b5f43..c1fdc7d523 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortSecondaryAxisData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortSecondaryAxisData.ts @@ -1,8 +1,11 @@ import { type FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem'; import { type RawDimensionValue } from '@/page-layout/widgets/graph/types/RawDimensionValue'; +import { compareDimensionValues } from '@/page-layout/widgets/graph/utils/compareDimensionValues'; import { sortByManualOrder } from '@/page-layout/widgets/graph/utils/sortByManualOrder'; import { sortBySelectOptionPosition } from '@/page-layout/widgets/graph/utils/sortBySelectOptionPosition'; +import { type CompositeFieldSubFieldName } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; +import { type FieldMetadataType } from '~/generated-metadata/graphql'; import { GraphOrderBy } from '~/generated/graphql'; type SortSecondaryAxisDataParams = { @@ -12,6 +15,8 @@ type SortSecondaryAxisDataParams = { formattedToRawLookup?: Map; selectFieldOptions?: FieldMetadataItemOption[] | null; getFormattedValue: (item: T) => string; + fieldType?: FieldMetadataType; + subFieldName?: CompositeFieldSubFieldName; }; export const sortSecondaryAxisData = ({ @@ -21,17 +26,26 @@ export const sortSecondaryAxisData = ({ formattedToRawLookup, selectFieldOptions, getFormattedValue, + fieldType, + subFieldName, }: SortSecondaryAxisDataParams): T[] => { switch (orderBy) { case GraphOrderBy.FIELD_ASC: - return items.toSorted((a, b) => - getFormattedValue(a).localeCompare(getFormattedValue(b)), - ); - case GraphOrderBy.FIELD_DESC: - return items.toSorted((a, b) => - getFormattedValue(b).localeCompare(getFormattedValue(a)), - ); + return items.toSorted((a, b) => { + const formattedValueA = getFormattedValue(a); + const formattedValueB = getFormattedValue(b); + + return compareDimensionValues({ + rawValueA: formattedToRawLookup?.get(formattedValueA), + rawValueB: formattedToRawLookup?.get(formattedValueB), + formattedValueA, + formattedValueB, + direction: orderBy === GraphOrderBy.FIELD_ASC ? 'ASC' : 'DESC', + fieldType, + subFieldName, + }); + }); case GraphOrderBy.FIELD_POSITION_ASC: case GraphOrderBy.FIELD_POSITION_DESC: { diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded.ts similarity index 90% rename from packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually.ts rename to packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded.ts index 9355c3a4ae..aa574591a9 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded.ts @@ -19,7 +19,9 @@ type SortTwoDimensionalChartPrimaryAxisDataParams = { selectFieldOptions?: FieldMetadataItemOption[] | null; }; -export const sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually = ({ +export const sortTwoDimensionalChartPrimaryAxisDataByFieldOrManuallyIfNeeded = < + T, +>({ data, orderBy, manualSortOrder, @@ -50,14 +52,8 @@ export const sortTwoDimensionalChartPrimaryAxisDataByFieldOrManually = ({ } case GraphOrderBy.FIELD_ASC: - return data.toSorted((a, b) => - getFormattedValue(a).localeCompare(getFormattedValue(b)), - ); - case GraphOrderBy.FIELD_DESC: - return data.toSorted((a, b) => - getFormattedValue(b).localeCompare(getFormattedValue(a)), - ); + return data; case GraphOrderBy.FIELD_POSITION_ASC: case GraphOrderBy.FIELD_POSITION_DESC: {