Skip to content
代码片段 群组 项目
提交 c7fe26c6 编辑于 作者: Rudy Crespo's avatar Rudy Crespo 提交者: Ezekiel Kigbo
浏览文件

Fix months count in Enhanced Issue Analytics chart x-axis title

上级 d8ad4b2f
No related branches found
No related tags found
无相关合并请求
显示
297 个添加51 个删除
doc/user/analytics/img/enhanced_issue_analytics_v16_7.png

60.5 KB

doc/user/analytics/img/issues_closed_analytics_v16_4.png

16.8 KB

...@@ -50,7 +50,7 @@ available. This feature is not ready for production use. ...@@ -50,7 +50,7 @@ available. This feature is not ready for production use.
Enhanced issue analytics display the additional metric "Issues closed", which represents the total number of resolved issues in your project over a selected period. Enhanced issue analytics display the additional metric "Issues closed", which represents the total number of resolved issues in your project over a selected period.
You can use this metric to improve the overall turn-around time and value delivered to your customers. You can use this metric to improve the overall turn-around time and value delivered to your customers.
![Issues opened and closed per month](img/issues_closed_analytics_v16_4.png) ![Issues opened and closed per month](img/enhanced_issue_analytics_v16_7.png)
## Drill into the information ## Drill into the information
......
doc/user/group/issues_analytics/img/enhanced_issue_analytics_v16_7.png

60.5 KB

doc/user/group/issues_analytics/img/issues_closed_analytics_v16_4.png

16.8 KB

...@@ -50,7 +50,7 @@ available. This feature is not ready for production use. ...@@ -50,7 +50,7 @@ available. This feature is not ready for production use.
Enhanced issue analytics display the additional metric "Issues closed", which represents the total number of resolved issues in your group over a selected period. Enhanced issue analytics display the additional metric "Issues closed", which represents the total number of resolved issues in your group over a selected period.
You can use this metric to improve the overall turn-around time and value delivered to your customers. You can use this metric to improve the overall turn-around time and value delivered to your customers.
![Issues opened and closed per month](img/issues_closed_analytics_v16_4.png) ![Issues opened and closed per month](img/enhanced_issue_analytics_v16_7.png)
## Drill into the information ## Drill into the information
......
...@@ -3,7 +3,7 @@ import { GlLoadingIcon, GlAlert } from '@gitlab/ui'; ...@@ -3,7 +3,7 @@ import { GlLoadingIcon, GlAlert } from '@gitlab/ui';
import { GlStackedColumnChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts'; import { GlStackedColumnChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
import * as Sentry from '~/sentry/sentry_browser_wrapper'; import * as Sentry from '~/sentry/sentry_browser_wrapper';
import { s__, n__, sprintf, __ } from '~/locale'; import { s__, n__, sprintf, __ } from '~/locale';
import { isValidDate } from '~/lib/utils/datetime_utility'; import { isValidDate, differenceInMonths } from '~/lib/utils/datetime_utility';
import { generateChartDateRangeData } from '../utils'; import { generateChartDateRangeData } from '../utils';
import issuesAnalyticsCountsQueryBuilder from '../graphql/issues_analytics_counts_query_builder'; import issuesAnalyticsCountsQueryBuilder from '../graphql/issues_analytics_counts_query_builder';
import { extractIssuesAnalyticsCounts } from '../api'; import { extractIssuesAnalyticsCounts } from '../api';
...@@ -162,27 +162,32 @@ export default { ...@@ -162,27 +162,32 @@ export default {
monthYearLabels() { monthYearLabels() {
return this.dates.map(({ month, year }) => `${month} ${year}`); return this.dates.map(({ month, year }) => `${month} ${year}`);
}, },
firstMonthYearLabel() {
return this.monthYearLabels[0];
},
monthsCount() { monthsCount() {
return this.dates.length; return differenceInMonths(this.startDate, this.endDate);
}, },
dateRange() { dateRange() {
const { monthYearLabels, monthsCount } = this; const { monthYearLabels, firstMonthYearLabel } = this;
const [startMonthYear] = monthYearLabels;
if (monthsCount === 1) return startMonthYear;
return sprintf(__('%{startDate} – %{dueDate}'), { return sprintf(__('%{startDate} – %{dueDate}'), {
startDate: startMonthYear, startDate: firstMonthYearLabel,
dueDate: monthYearLabels.at(-1), dueDate: monthYearLabels.at(-1),
}); });
}, },
xAxisTitle() { xAxisTitle() {
const { monthsCount, dateRange } = this; const { monthsCount, dateRange, firstMonthYearLabel } = this;
if (monthsCount === 0) {
return sprintf(s__('IssuesAnalytics|This month (%{currentMonthYear})'), {
currentMonthYear: firstMonthYearLabel,
});
}
return sprintf( return sprintf(
n__( n__(
'IssuesAnalytics|This month (%{dateRange})', 'IssuesAnalytics|Last month (%{dateRange})',
'IssuesAnalytics|Last %{monthsCount} months (%{dateRange})', 'IssuesAnalytics|Last %{monthsCount} months (%{dateRange})',
monthsCount, monthsCount,
), ),
......
...@@ -33,7 +33,21 @@ describe('TotalIssuesAnalyticsChart', () => { ...@@ -33,7 +33,21 @@ describe('TotalIssuesAnalyticsChart', () => {
let mockApollo; let mockApollo;
const fullPath = 'toolbox'; const fullPath = 'toolbox';
const mockGroupBy = ['Jul', 'Aug', 'Sep']; const mockGroupBy = [
'Nov',
'Dec',
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
];
const queryError = jest.fn().mockRejectedValueOnce(new Error('Something went wrong')); const queryError = jest.fn().mockRejectedValueOnce(new Error('Something went wrong'));
const mockDataNullResponse = mockGraphqlIssuesAnalyticsCountsResponse({ mockDataResponse: null }); const mockDataNullResponse = mockGraphqlIssuesAnalyticsCountsResponse({ mockDataResponse: null });
const issuesOpenedCountsSuccess = mockGraphqlIssuesAnalyticsCountsResponse({ const issuesOpenedCountsSuccess = mockGraphqlIssuesAnalyticsCountsResponse({
...@@ -123,7 +137,7 @@ describe('TotalIssuesAnalyticsChart', () => { ...@@ -123,7 +137,7 @@ describe('TotalIssuesAnalyticsChart', () => {
presentation: 'tiled', presentation: 'tiled',
groupBy: mockGroupBy, groupBy: mockGroupBy,
xAxisType: 'category', xAxisType: 'category',
xAxisTitle: 'Last 3 months (Jul 2023Sep 2023)', xAxisTitle: 'Last 12 months (Nov 2022Nov 2023)',
yAxisTitle: 'Issues Opened vs Closed', yAxisTitle: 'Issues Opened vs Closed',
customPalette: TOTAL_ISSUES_ANALYTICS_CHART_COLOR_PALETTE, customPalette: TOTAL_ISSUES_ANALYTICS_CHART_COLOR_PALETTE,
}); });
...@@ -147,13 +161,19 @@ describe('TotalIssuesAnalyticsChart', () => { ...@@ -147,13 +161,19 @@ describe('TotalIssuesAnalyticsChart', () => {
}); });
}); });
it('should display correct x-axis title when date range is month to date', async () => { it.each`
const mockStartDate = new Date('2023-09-01T00:00:00.000Z'); startDate | expectedXAxisTitle
${new Date('2023-09-01T00:00:00.000Z')} | ${'Last 2 months (Sep 2023 – Nov 2023)'}
await createComponent({ startDate: mockStartDate }); ${new Date('2023-10-01T00:00:00.000Z')} | ${'Last month (Oct 2023 – Nov 2023)'}
${new Date('2023-11-01T00:00:00.000Z')} | ${'This month (Nov 2023)'}
`(
`should display the correct x-axis title when startDate=$startDate and endDate=${mockIssuesAnalyticsCountsEndDate}`,
async ({ startDate, expectedXAxisTitle }) => {
await createComponent({ startDate });
expect(findTotalIssuesAnalyticsChart().props('xAxisTitle')).toBe('This month (Sep 2023)'); expect(findTotalIssuesAnalyticsChart().props('xAxisTitle')).toBe(expectedXAxisTitle);
}); },
);
}); });
describe('when fetching data', () => { describe('when fetching data', () => {
......
...@@ -107,8 +107,8 @@ export const getQueryIssuesAnalyticsResponse = { ...@@ -107,8 +107,8 @@ export const getQueryIssuesAnalyticsResponse = {
}, },
}; };
export const mockIssuesAnalyticsCountsStartDate = new Date('2023-07-04T00:00:00.000Z'); export const mockIssuesAnalyticsCountsStartDate = new Date('2022-11-01T00:00:00.000Z');
export const mockIssuesAnalyticsCountsEndDate = new Date('2023-09-15T00:00:00.000Z'); export const mockIssuesAnalyticsCountsEndDate = new Date('2023-11-20T00:00:00.000Z');
export const getMockIssuesAnalyticsCountsQuery = ({ export const getMockIssuesAnalyticsCountsQuery = ({
queryAlias, queryAlias,
...@@ -118,8 +118,128 @@ export const getMockIssuesAnalyticsCountsQuery = ({ ...@@ -118,8 +118,128 @@ export const getMockIssuesAnalyticsCountsQuery = ({
namespace: ${isProject ? 'project' : 'group'}(fullPath: $fullPath) { namespace: ${isProject ? 'project' : 'group'}(fullPath: $fullPath) {
id id
${queryAlias}: flowMetrics { ${queryAlias}: flowMetrics {
Nov_2022: ${metricType}(
from: "2022-11-01"
to: "2022-12-01"
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
labelNames: $labelNames
epicId: $epicId
iterationId: $iterationId
myReactionEmoji: $myReactionEmoji
weight: $weight
not: $not
) {
value
}
Dec_2022: ${metricType}(
from: "2022-12-01"
to: "2023-01-01"
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
labelNames: $labelNames
epicId: $epicId
iterationId: $iterationId
myReactionEmoji: $myReactionEmoji
weight: $weight
not: $not
) {
value
}
Jan_2023: ${metricType}(
from: "2023-01-01"
to: "2023-02-01"
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
labelNames: $labelNames
epicId: $epicId
iterationId: $iterationId
myReactionEmoji: $myReactionEmoji
weight: $weight
not: $not
) {
value
}
Feb_2023: ${metricType}(
from: "2023-02-01"
to: "2023-03-01"
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
labelNames: $labelNames
epicId: $epicId
iterationId: $iterationId
myReactionEmoji: $myReactionEmoji
weight: $weight
not: $not
) {
value
}
Mar_2023: ${metricType}(
from: "2023-03-01"
to: "2023-04-01"
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
labelNames: $labelNames
epicId: $epicId
iterationId: $iterationId
myReactionEmoji: $myReactionEmoji
weight: $weight
not: $not
) {
value
}
Apr_2023: ${metricType}(
from: "2023-04-01"
to: "2023-05-01"
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
labelNames: $labelNames
epicId: $epicId
iterationId: $iterationId
myReactionEmoji: $myReactionEmoji
weight: $weight
not: $not
) {
value
}
May_2023: ${metricType}(
from: "2023-05-01"
to: "2023-06-01"
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
labelNames: $labelNames
epicId: $epicId
iterationId: $iterationId
myReactionEmoji: $myReactionEmoji
weight: $weight
not: $not
) {
value
}
Jun_2023: ${metricType}(
from: "2023-06-01"
to: "2023-07-01"
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
labelNames: $labelNames
epicId: $epicId
iterationId: $iterationId
myReactionEmoji: $myReactionEmoji
weight: $weight
not: $not
) {
value
}
Jul_2023: ${metricType}( Jul_2023: ${metricType}(
from: "2023-07-04" from: "2023-07-01"
to: "2023-08-01" to: "2023-08-01"
assigneeUsernames: $assigneeUsernames assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername authorUsername: $authorUsername
...@@ -150,7 +270,37 @@ export const getMockIssuesAnalyticsCountsQuery = ({ ...@@ -150,7 +270,37 @@ export const getMockIssuesAnalyticsCountsQuery = ({
} }
Sep_2023: ${metricType}( Sep_2023: ${metricType}(
from: "2023-09-01" from: "2023-09-01"
to: "2023-09-15" to: "2023-10-01"
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
labelNames: $labelNames
epicId: $epicId
iterationId: $iterationId
myReactionEmoji: $myReactionEmoji
weight: $weight
not: $not
) {
value
}
Oct_2023: ${metricType}(
from: "2023-10-01"
to: "2023-11-01"
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
labelNames: $labelNames
epicId: $epicId
iterationId: $iterationId
myReactionEmoji: $myReactionEmoji
weight: $weight
not: $not
) {
value
}
Nov_2023: ${metricType}(
from: "2023-11-01"
to: "2023-11-20"
assigneeUsernames: $assigneeUsernames assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername authorUsername: $authorUsername
milestoneTitle: $milestoneTitle milestoneTitle: $milestoneTitle
...@@ -171,16 +321,56 @@ export const getMockIssuesAnalyticsCountsQuery = ({ ...@@ -171,16 +321,56 @@ export const getMockIssuesAnalyticsCountsQuery = ({
export const getMockIssuesOpenedCountsResponse = ({ isProject = false, isEmpty = false } = {}) => ({ export const getMockIssuesOpenedCountsResponse = ({ isProject = false, isEmpty = false } = {}) => ({
id: 'fake-id', id: 'fake-id',
issuesOpenedCounts: { issuesOpenedCounts: {
Nov_2022: {
value: isEmpty ? 0 : 18,
__typename: 'ValueStreamAnalyticsMetric',
},
Dec_2022: {
value: isEmpty ? 0 : 38,
__typename: 'ValueStreamAnalyticsMetric',
},
Jan_2023: {
value: isEmpty ? 0 : 51,
__typename: 'ValueStreamAnalyticsMetric',
},
Feb_2023: {
value: isEmpty ? 0 : 39,
__typename: 'ValueStreamAnalyticsMetric',
},
Mar_2023: {
value: isEmpty ? 0 : 45,
__typename: 'ValueStreamAnalyticsMetric',
},
Apr_2023: {
value: isEmpty ? 0 : 40,
__typename: 'ValueStreamAnalyticsMetric',
},
May_2023: {
value: isEmpty ? 0 : 44,
__typename: 'ValueStreamAnalyticsMetric',
},
Jun_2023: {
value: isEmpty ? 0 : 44,
__typename: 'ValueStreamAnalyticsMetric',
},
Jul_2023: { Jul_2023: {
value: isEmpty ? 0 : 134, value: isEmpty ? 0 : 34,
__typename: 'ValueStreamAnalyticsMetric', __typename: 'ValueStreamAnalyticsMetric',
}, },
Aug_2023: { Aug_2023: {
value: isEmpty ? 0 : 21, value: isEmpty ? 0 : 48,
__typename: 'ValueStreamAnalyticsMetric', __typename: 'ValueStreamAnalyticsMetric',
}, },
Sep_2023: { Sep_2023: {
value: isEmpty ? 0 : 11, value: isEmpty ? 0 : 40,
__typename: 'ValueStreamAnalyticsMetric',
},
Oct_2023: {
value: isEmpty ? 0 : 39,
__typename: 'ValueStreamAnalyticsMetric',
},
Nov_2023: {
value: isEmpty ? 0 : 20,
__typename: 'ValueStreamAnalyticsMetric', __typename: 'ValueStreamAnalyticsMetric',
}, },
__typename: isProject __typename: isProject
...@@ -193,16 +383,56 @@ export const getMockIssuesOpenedCountsResponse = ({ isProject = false, isEmpty = ...@@ -193,16 +383,56 @@ export const getMockIssuesOpenedCountsResponse = ({ isProject = false, isEmpty =
export const getMockIssuesClosedCountsResponse = ({ isProject = false, isEmpty = false } = {}) => ({ export const getMockIssuesClosedCountsResponse = ({ isProject = false, isEmpty = false } = {}) => ({
id: 'fake-id', id: 'fake-id',
issuesClosedCounts: { issuesClosedCounts: {
Nov_2022: {
value: isEmpty ? 0 : 0,
__typename: 'ValueStreamAnalyticsMetric',
},
Dec_2022: {
value: isEmpty ? 0 : 0,
__typename: 'ValueStreamAnalyticsMetric',
},
Jan_2023: {
value: isEmpty ? 0 : 1,
__typename: 'ValueStreamAnalyticsMetric',
},
Feb_2023: {
value: isEmpty ? 0 : 3,
__typename: 'ValueStreamAnalyticsMetric',
},
Mar_2023: {
value: isEmpty ? 0 : 4,
__typename: 'ValueStreamAnalyticsMetric',
},
Apr_2023: {
value: isEmpty ? 0 : 9,
__typename: 'ValueStreamAnalyticsMetric',
},
May_2023: {
value: isEmpty ? 0 : 13,
__typename: 'ValueStreamAnalyticsMetric',
},
Jun_2023: {
value: isEmpty ? 0 : 12,
__typename: 'ValueStreamAnalyticsMetric',
},
Jul_2023: { Jul_2023: {
value: isEmpty ? 0 : 110, value: isEmpty ? 0 : 14,
__typename: 'ValueStreamAnalyticsMetric', __typename: 'ValueStreamAnalyticsMetric',
}, },
Aug_2023: { Aug_2023: {
value: isEmpty ? 0 : 1, value: isEmpty ? 0 : 21,
__typename: 'ValueStreamAnalyticsMetric', __typename: 'ValueStreamAnalyticsMetric',
}, },
Sep_2023: { Sep_2023: {
value: isEmpty ? 0 : 15, value: isEmpty ? 0 : 24,
__typename: 'ValueStreamAnalyticsMetric',
},
Oct_2023: {
value: isEmpty ? 0 : 45,
__typename: 'ValueStreamAnalyticsMetric',
},
Nov_2023: {
value: isEmpty ? 0 : 60,
__typename: 'ValueStreamAnalyticsMetric', __typename: 'ValueStreamAnalyticsMetric',
}, },
__typename: isProject __typename: isProject
...@@ -224,14 +454,8 @@ export const mockProjectIssuesAnalyticsCountsResponseData = getMockTotalIssuesAn ...@@ -224,14 +454,8 @@ export const mockProjectIssuesAnalyticsCountsResponseData = getMockTotalIssuesAn
); );
export const mockIssuesAnalyticsCountsChartData = [ export const mockIssuesAnalyticsCountsChartData = [
{ { name: 'Opened', data: [18, 38, 51, 39, 45, 40, 44, 44, 34, 48, 40, 39, 20] },
name: 'Opened', { name: 'Closed', data: [0, 0, 1, 3, 4, 9, 13, 12, 14, 21, 24, 45, 60] },
data: [134, 21, 11],
},
{
name: 'Closed',
data: [110, 1, 15],
},
]; ];
export const mockChartDateRangeData = [ export const mockChartDateRangeData = [
......
import { transformFilters, generateChartDateRangeData } from 'ee/issues_analytics/utils'; import { transformFilters, generateChartDateRangeData } from 'ee/issues_analytics/utils';
import { import { mockOriginalFilters, mockFilters, mockChartDateRangeData } from './mock_data';
mockOriginalFilters,
mockFilters,
mockIssuesAnalyticsCountsStartDate,
mockIssuesAnalyticsCountsEndDate,
mockChartDateRangeData,
} from './mock_data';
describe('Issues Analytics utils', () => { describe('Issues Analytics utils', () => {
describe('transformFilters', () => { describe('transformFilters', () => {
...@@ -46,8 +40,8 @@ describe('Issues Analytics utils', () => { ...@@ -46,8 +40,8 @@ describe('Issues Analytics utils', () => {
}); });
describe('generateChartDateRangeData', () => { describe('generateChartDateRangeData', () => {
const startDate = mockIssuesAnalyticsCountsStartDate; const startDate = new Date('2023-07-04T00:00:00.000Z');
const endDate = mockIssuesAnalyticsCountsEndDate; const endDate = new Date('2023-09-15T00:00:00.000Z');
it('returns the data as expected', () => { it('returns the data as expected', () => {
const chartDateRangeData = generateChartDateRangeData(startDate, endDate); const chartDateRangeData = generateChartDateRangeData(startDate, endDate);
......
...@@ -26565,6 +26565,11 @@ msgstr "" ...@@ -26565,6 +26565,11 @@ msgstr ""
msgid "IssuesAnalytics|Last 12 months (%{chartDateRange})" msgid "IssuesAnalytics|Last 12 months (%{chartDateRange})"
msgstr "" msgstr ""
   
msgid "IssuesAnalytics|Last month (%{dateRange})"
msgid_plural "IssuesAnalytics|Last %{monthsCount} months (%{dateRange})"
msgstr[0] ""
msgstr[1] ""
msgid "IssuesAnalytics|Opened" msgid "IssuesAnalytics|Opened"
msgstr "" msgstr ""
   
...@@ -26574,10 +26579,8 @@ msgstr "" ...@@ -26574,10 +26579,8 @@ msgstr ""
msgid "IssuesAnalytics|Sorry, your filter produced no results" msgid "IssuesAnalytics|Sorry, your filter produced no results"
msgstr "" msgstr ""
   
msgid "IssuesAnalytics|This month (%{dateRange})" msgid "IssuesAnalytics|This month (%{currentMonthYear})"
msgid_plural "IssuesAnalytics|Last %{monthsCount} months (%{dateRange})" msgstr ""
msgstr[0] ""
msgstr[1] ""
   
msgid "IssuesAnalytics|To widen your search, change or remove filters in the filter bar above." msgid "IssuesAnalytics|To widen your search, change or remove filters in the filter bar above."
msgstr "" msgstr ""
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册