diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-20 08:43:02 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-20 08:43:02 +0000 |
commit | d9ab72d6080f594d0b3cae15f14b3ef2c6c638cb (patch) | |
tree | 2341ef426af70ad1e289c38036737e04b0aa5007 /app/assets/javascripts/cycle_analytics | |
parent | d6e514dd13db8947884cd58fe2a9c2a063400a9b (diff) | |
download | gitlab-ce-d9ab72d6080f594d0b3cae15f14b3ef2c6c638cb.tar.gz |
Add latest changes from gitlab-org/gitlab@14-4-stable-eev14.4.0-rc42
Diffstat (limited to 'app/assets/javascripts/cycle_analytics')
11 files changed, 77 insertions, 37 deletions
diff --git a/app/assets/javascripts/cycle_analytics/components/base.vue b/app/assets/javascripts/cycle_analytics/components/base.vue index ae78ce33263..1d98a42ce58 100644 --- a/app/assets/javascripts/cycle_analytics/components/base.vue +++ b/app/assets/javascripts/cycle_analytics/components/base.vue @@ -51,6 +51,7 @@ export default { 'features', 'createdBefore', 'createdAfter', + 'pagination', ]), ...mapGetters(['pathNavigationData', 'filterParams']), displayStageEvents() { @@ -99,7 +100,12 @@ export default { }, }, methods: { - ...mapActions(['fetchStageData', 'setSelectedStage', 'setDateRange']), + ...mapActions([ + 'fetchStageData', + 'setSelectedStage', + 'setDateRange', + 'updateStageTablePagination', + ]), onSetDateRange({ startDate, endDate }) { this.setDateRange({ createdAfter: new Date(startDate), @@ -108,6 +114,7 @@ export default { }, onSelectStage(stage) { this.setSelectedStage(stage); + this.updateStageTablePagination({ ...this.pagination, page: 1 }); }, dismissOverviewDialog() { this.isOverviewDialogDismissed = true; @@ -117,6 +124,9 @@ export default { const { permissions } = this; return Boolean(permissions?.[id]); }, + onHandleUpdatePagination(data) { + this.updateStageTablePagination(data); + }, }, dayRangeOptions: [7, 30, 90], i18n: { @@ -163,8 +173,8 @@ export default { :empty-state-title="emptyStageTitle" :empty-state-message="emptyStageText" :no-data-svg-path="noDataSvgPath" - :pagination="null" - :sortable="false" + :pagination="pagination" + @handleUpdatePagination="onHandleUpdatePagination" /> </div> </template> diff --git a/app/assets/javascripts/cycle_analytics/components/filter_bar.vue b/app/assets/javascripts/cycle_analytics/components/filter_bar.vue index 5140b05e189..016fea354fe 100644 --- a/app/assets/javascripts/cycle_analytics/components/filter_bar.vue +++ b/app/assets/javascripts/cycle_analytics/components/filter_bar.vue @@ -79,7 +79,6 @@ export default { title: __('Assignees'), type: 'assignees', token: AuthorToken, - defaultAuthors: [], initialAuthors: this.assigneesData, unique: false, operators: OPERATOR_IS_ONLY, diff --git a/app/assets/javascripts/cycle_analytics/components/stage_table.vue b/app/assets/javascripts/cycle_analytics/components/stage_table.vue index 8a2667a4ab1..fc4dfafb809 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_table.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_table.vue @@ -194,6 +194,9 @@ export default { ><formatted-stage-count :stage-count="stageCount" /></gl-badge> </template> + <template #head(duration)="data"> + <span data-testid="vsa-stage-header-duration">{{ data.label }}</span> + </template> <template #cell(end_event)="{ item }"> <div data-testid="vsa-stage-event"> <div v-if="item.id" data-testid="vsa-stage-content"> diff --git a/app/assets/javascripts/cycle_analytics/constants.js b/app/assets/javascripts/cycle_analytics/constants.js index c1be2ce7096..c205aa1e831 100644 --- a/app/assets/javascripts/cycle_analytics/constants.js +++ b/app/assets/javascripts/cycle_analytics/constants.js @@ -44,7 +44,7 @@ export const METRICS_POPOVER_CONTENT = { }, 'cycle-time': { description: s__( - 'ValueStreamAnalytics|Median time from issue first merge request created to issue closed.', + "ValueStreamAnalytics|Median time from the earliest commit of a linked issue's merge request to when that issue is closed.", ), }, 'new-issue': { description: s__('ValueStreamAnalytics|Number of new issues created.') }, diff --git a/app/assets/javascripts/cycle_analytics/index.js b/app/assets/javascripts/cycle_analytics/index.js index 620da0104e0..34ef03409b8 100644 --- a/app/assets/javascripts/cycle_analytics/index.js +++ b/app/assets/javascripts/cycle_analytics/index.js @@ -45,6 +45,7 @@ export default () => { new Vue({ el, name: 'CycleAnalytics', + apolloProvider: {}, store, render: (createElement) => createElement(CycleAnalytics, { diff --git a/app/assets/javascripts/cycle_analytics/store/actions.js b/app/assets/javascripts/cycle_analytics/store/actions.js index e39cd224199..24b62849db7 100644 --- a/app/assets/javascripts/cycle_analytics/store/actions.js +++ b/app/assets/javascripts/cycle_analytics/store/actions.js @@ -6,6 +6,7 @@ import { getValueStreamStageRecords, getValueStreamStageCounts, } from '~/api/analytics_api'; +import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils'; import createFlash from '~/flash'; import { __ } from '~/locale'; import { DEFAULT_VALUE_STREAM, I18N_VSA_ERROR_STAGE_MEDIAN } from '../constants'; @@ -72,16 +73,21 @@ export const fetchCycleAnalyticsData = ({ }); }; -export const fetchStageData = ({ getters: { requestParams, filterParams }, commit }) => { +export const fetchStageData = ({ + getters: { requestParams, filterParams, paginationParams }, + commit, +}) => { commit(types.REQUEST_STAGE_DATA); - return getValueStreamStageRecords(requestParams, filterParams) - .then(({ data }) => { + return getValueStreamStageRecords(requestParams, { ...filterParams, ...paginationParams }) + .then(({ data, headers }) => { // when there's a query timeout, the request succeeds but the error is encoded in the response data if (data?.error) { commit(types.RECEIVE_STAGE_DATA_ERROR, data.error); } else { commit(types.RECEIVE_STAGE_DATA_SUCCESS, data); + const { page = null, nextPage = null } = parseIntPagination(normalizeHeaders(headers)); + commit(types.SET_PAGINATION, { ...paginationParams, page, hasNextPage: Boolean(nextPage) }); } }) .catch(() => commit(types.RECEIVE_STAGE_DATA_ERROR)); @@ -176,6 +182,14 @@ export const setDateRange = ({ dispatch, commit }, { createdAfter, createdBefore return refetchStageData(dispatch); }; +export const updateStageTablePagination = ( + { commit, dispatch, state: { selectedStage } }, + paginationParams, +) => { + commit(types.SET_PAGINATION, paginationParams); + return dispatch('fetchStageData', selectedStage.id); +}; + export const initializeVsa = ({ commit, dispatch }, initialData = {}) => { commit(types.INITIALIZE_VSA, initialData); diff --git a/app/assets/javascripts/cycle_analytics/store/getters.js b/app/assets/javascripts/cycle_analytics/store/getters.js index 77c285f5ce0..962e1d50d12 100644 --- a/app/assets/javascripts/cycle_analytics/store/getters.js +++ b/app/assets/javascripts/cycle_analytics/store/getters.js @@ -1,6 +1,7 @@ import dateFormat from 'dateformat'; import { dateFormats } from '~/analytics/shared/constants'; import { filterToQueryObject } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils'; +import { PAGINATION_TYPE } from '../constants'; import { transformStagesForPathNavigation, filterStagesByHiddenStatus } from '../utils'; export const pathNavigationData = ({ stages, medians, stageCounts, selectedStage }) => { @@ -21,6 +22,13 @@ export const requestParams = (state) => { return { requestPath: fullPath, valueStreamId, stageId }; }; +export const paginationParams = ({ pagination: { page, sort, direction } }) => ({ + pagination: PAGINATION_TYPE, + sort, + direction, + page, +}); + const filterBarParams = ({ filters }) => { const { authors: { selected: selectedAuthor }, diff --git a/app/assets/javascripts/cycle_analytics/store/mutation_types.js b/app/assets/javascripts/cycle_analytics/store/mutation_types.js index 0d94aad2ca5..0ad67d4e6bd 100644 --- a/app/assets/javascripts/cycle_analytics/store/mutation_types.js +++ b/app/assets/javascripts/cycle_analytics/store/mutation_types.js @@ -4,6 +4,7 @@ export const SET_LOADING = 'SET_LOADING'; export const SET_SELECTED_VALUE_STREAM = 'SET_SELECTED_VALUE_STREAM'; export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE'; export const SET_DATE_RANGE = 'SET_DATE_RANGE'; +export const SET_PAGINATION = 'SET_PAGINATION'; export const REQUEST_VALUE_STREAMS = 'REQUEST_VALUE_STREAMS'; export const RECEIVE_VALUE_STREAMS_SUCCESS = 'RECEIVE_VALUE_STREAMS_SUCCESS'; diff --git a/app/assets/javascripts/cycle_analytics/store/mutations.js b/app/assets/javascripts/cycle_analytics/store/mutations.js index 301e7d95f8c..64930a5b51f 100644 --- a/app/assets/javascripts/cycle_analytics/store/mutations.js +++ b/app/assets/javascripts/cycle_analytics/store/mutations.js @@ -1,13 +1,24 @@ +import Vue from 'vue'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import { PAGINATION_SORT_FIELD_END_EVENT, PAGINATION_SORT_DIRECTION_DESC } from '../constants'; import { formatMedianValues } from '../utils'; import * as types from './mutation_types'; export default { - [types.INITIALIZE_VSA](state, { endpoints, features, createdBefore, createdAfter }) { + [types.INITIALIZE_VSA]( + state, + { endpoints, features, createdBefore, createdAfter, pagination = {} }, + ) { state.endpoints = endpoints; state.createdBefore = createdBefore; state.createdAfter = createdAfter; state.features = features; + + Vue.set(state, 'pagination', { + page: pagination.page ?? state.pagination.page, + sort: pagination.sort ?? state.pagination.sort, + direction: pagination.direction ?? state.pagination.direction, + }); }, [types.SET_LOADING](state, loadingState) { state.isLoading = loadingState; @@ -22,6 +33,14 @@ export default { state.createdBefore = createdBefore; state.createdAfter = createdAfter; }, + [types.SET_PAGINATION](state, { page, hasNextPage, sort, direction }) { + Vue.set(state, 'pagination', { + page, + hasNextPage, + sort: sort || PAGINATION_SORT_FIELD_END_EVENT, + direction: direction || PAGINATION_SORT_DIRECTION_DESC, + }); + }, [types.REQUEST_VALUE_STREAMS](state) { state.valueStreams = []; }, diff --git a/app/assets/javascripts/cycle_analytics/store/state.js b/app/assets/javascripts/cycle_analytics/store/state.js index 0882db51218..52bc01cafa4 100644 --- a/app/assets/javascripts/cycle_analytics/store/state.js +++ b/app/assets/javascripts/cycle_analytics/store/state.js @@ -1,3 +1,8 @@ +import { + PAGINATION_SORT_FIELD_END_EVENT, + PAGINATION_SORT_DIRECTION_DESC, +} from '~/cycle_analytics/constants'; + export default () => ({ id: null, features: {}, @@ -20,4 +25,10 @@ export default () => ({ isLoadingStage: false, isEmptyStage: false, permissions: {}, + pagination: { + page: null, + hasNextPage: false, + sort: PAGINATION_SORT_FIELD_END_EVENT, + direction: PAGINATION_SORT_DIRECTION_DESC, + }, }); diff --git a/app/assets/javascripts/cycle_analytics/utils.js b/app/assets/javascripts/cycle_analytics/utils.js index fa02fdf914a..3c6267bac06 100644 --- a/app/assets/javascripts/cycle_analytics/utils.js +++ b/app/assets/javascripts/cycle_analytics/utils.js @@ -1,13 +1,10 @@ import dateFormat from 'dateformat'; -import { unescape } from 'lodash'; import { dateFormats } from '~/analytics/shared/constants'; import { hideFlash } from '~/flash'; -import { sanitize } from '~/lib/dompurify'; -import { roundToNearestHalf } from '~/lib/utils/common_utils'; import { getDateInPast } from '~/lib/utils/datetime/date_calculation_utility'; import { parseSeconds } from '~/lib/utils/datetime_utility'; +import { formatTimeAsSummary } from '~/lib/utils/datetime/date_format_utility'; import { slugify } from '~/lib/utils/text_utility'; -import { s__, sprintf } from '../locale'; export const removeFlash = (type = 'alert') => { const flashEl = document.querySelector(`.flash-${type}`); @@ -45,29 +42,6 @@ export const transformStagesForPathNavigation = ({ return formattedStages; }; -export const timeSummaryForPathNavigation = ({ seconds, hours, days, minutes, weeks, months }) => { - if (months) { - return sprintf(s__('ValueStreamAnalytics|%{value}M'), { - value: roundToNearestHalf(months), - }); - } else if (weeks) { - return sprintf(s__('ValueStreamAnalytics|%{value}w'), { - value: roundToNearestHalf(weeks), - }); - } else if (days) { - return sprintf(s__('ValueStreamAnalytics|%{value}d'), { - value: roundToNearestHalf(days), - }); - } else if (hours) { - return sprintf(s__('ValueStreamAnalytics|%{value}h'), { value: hours }); - } else if (minutes) { - return sprintf(s__('ValueStreamAnalytics|%{value}m'), { value: minutes }); - } else if (seconds) { - return unescape(sanitize(s__('ValueStreamAnalytics|<1m'), { ALLOWED_TAGS: [] })); - } - return '-'; -}; - /** * Takes a raw median value in seconds and converts it to a string representation * ie. converts 172800 => 2d (2 days) @@ -76,7 +50,7 @@ export const timeSummaryForPathNavigation = ({ seconds, hours, days, minutes, we * @returns {String} String representation ie 2w */ export const medianTimeToParsedSeconds = (value) => - timeSummaryForPathNavigation({ + formatTimeAsSummary({ ...parseSeconds(value, { daysPerWeek: 7, hoursPerDay: 24 }), seconds: value, }); |