Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 16:18:24 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 16:18:24 +0300
commit0653e08efd039a5905f3fa4f6e9cef9f5d2f799c (patch)
tree4dcc884cf6d81db44adae4aa99f8ec1233a41f55 /app/assets/javascripts/cycle_analytics
parent744144d28e3e7fddc117924fef88de5d9674fe4c (diff)
Add latest changes from gitlab-org/gitlab@14-3-stable-eev14.3.0-rc42
Diffstat (limited to 'app/assets/javascripts/cycle_analytics')
-rw-r--r--app/assets/javascripts/cycle_analytics/components/banner.vue54
-rw-r--r--app/assets/javascripts/cycle_analytics/components/base.vue57
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_table.vue13
-rw-r--r--app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue47
-rw-r--r--app/assets/javascripts/cycle_analytics/index.js14
-rw-r--r--app/assets/javascripts/cycle_analytics/store/actions.js15
-rw-r--r--app/assets/javascripts/cycle_analytics/store/getters.js17
-rw-r--r--app/assets/javascripts/cycle_analytics/store/mutations.js18
-rw-r--r--app/assets/javascripts/cycle_analytics/store/state.js3
9 files changed, 108 insertions, 130 deletions
diff --git a/app/assets/javascripts/cycle_analytics/components/banner.vue b/app/assets/javascripts/cycle_analytics/components/banner.vue
deleted file mode 100644
index cf4c35ef12b..00000000000
--- a/app/assets/javascripts/cycle_analytics/components/banner.vue
+++ /dev/null
@@ -1,54 +0,0 @@
-<script>
-/* eslint-disable vue/no-v-html */
-import { GlIcon } from '@gitlab/ui';
-import iconCycleAnalyticsSplash from 'icons/_icon_cycle_analytics_splash.svg';
-
-export default {
- components: {
- GlIcon,
- },
- props: {
- documentationLink: {
- type: String,
- required: true,
- },
- },
- computed: {
- iconCycleAnalyticsSplash() {
- return iconCycleAnalyticsSplash;
- },
- },
- methods: {
- dismissOverviewDialog() {
- this.$emit('dismiss-overview-dialog');
- },
- },
-};
-</script>
-<template>
- <div class="landing content-block">
- <button
- :aria-label="__('Dismiss Value Stream Analytics introduction box')"
- class="js-ca-dismiss-button dismiss-button"
- type="button"
- @click="dismissOverviewDialog"
- >
- <gl-icon name="close" />
- </button>
- <div class="svg-container" v-html="iconCycleAnalyticsSplash"></div>
- <div class="inner-content">
- <h4>{{ __('Introducing Value Stream Analytics') }}</h4>
- <p>
- {{
- __(`Value Stream Analytics gives an overview
-of how much time it takes to go from idea to production in your project.`)
- }}
- </p>
- <p>
- <a :href="documentationLink" target="_blank" rel="nofollow" class="btn">
- {{ __('Read more') }}
- </a>
- </p>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/base.vue b/app/assets/javascripts/cycle_analytics/components/base.vue
index c9ecac6829b..ae78ce33263 100644
--- a/app/assets/javascripts/cycle_analytics/components/base.vue
+++ b/app/assets/javascripts/cycle_analytics/components/base.vue
@@ -1,9 +1,10 @@
<script>
-import { GlIcon, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
+import { GlLoadingIcon } from '@gitlab/ui';
import Cookies from 'js-cookie';
import { mapActions, mapState, mapGetters } from 'vuex';
import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
import StageTable from '~/cycle_analytics/components/stage_table.vue';
+import ValueStreamFilters from '~/cycle_analytics/components/value_stream_filters.vue';
import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue';
import { __ } from '~/locale';
import { SUMMARY_METRICS_REQUEST, METRICS_REQUESTS } from '../constants';
@@ -13,11 +14,10 @@ const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
export default {
name: 'CycleAnalytics',
components: {
- GlIcon,
GlLoadingIcon,
- GlSprintf,
PathNavigation,
StageTable,
+ ValueStreamFilters,
ValueStreamMetrics,
},
props: {
@@ -45,11 +45,12 @@ export default {
'selectedStageError',
'stages',
'summary',
- 'daysInPast',
'permissions',
'stageCounts',
'endpoints',
'features',
+ 'createdBefore',
+ 'createdAfter',
]),
...mapGetters(['pathNavigationData', 'filterParams']),
displayStageEvents() {
@@ -98,14 +99,12 @@ export default {
},
},
methods: {
- ...mapActions([
- 'fetchCycleAnalyticsData',
- 'fetchStageData',
- 'setSelectedStage',
- 'setDateRange',
- ]),
- handleDateSelect(daysInPast) {
- this.setDateRange(daysInPast);
+ ...mapActions(['fetchStageData', 'setSelectedStage', 'setDateRange']),
+ onSetDateRange({ startDate, endDate }) {
+ this.setDateRange({
+ createdAfter: new Date(startDate),
+ createdBefore: new Date(endDate),
+ });
},
onSelectStage(stage) {
this.setSelectedStage(stage);
@@ -133,35 +132,22 @@ export default {
<div class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row">
<path-navigation
v-if="displayPathNavigation"
- class="js-path-navigation gl-w-full gl-pb-2"
+ data-testid="vsa-path-navigation"
+ class="gl-w-full gl-pb-2"
:loading="isLoading || isLoadingStage"
:stages="pathNavigationData"
:selected-stage="selectedStage"
@selected="onSelectStage"
/>
- <div class="gl-flex-grow gl-align-self-end">
- <div class="js-ca-dropdown dropdown inline">
- <!-- eslint-disable-next-line @gitlab/vue-no-data-toggle -->
- <button class="dropdown-menu-toggle" data-toggle="dropdown" type="button">
- <span class="dropdown-label">
- <gl-sprintf :message="$options.i18n.dropdownText">
- <template #days>{{ daysInPast }}</template>
- </gl-sprintf>
- <gl-icon name="chevron-down" class="dropdown-menu-toggle-icon gl-top-3" />
- </span>
- </button>
- <ul class="dropdown-menu dropdown-menu-right">
- <li v-for="days in $options.dayRangeOptions" :key="`day-range-${days}`">
- <a href="#" @click.prevent="handleDateSelect(days)">
- <gl-sprintf :message="$options.i18n.dropdownText">
- <template #days>{{ days }}</template>
- </gl-sprintf>
- </a>
- </li>
- </ul>
- </div>
- </div>
</div>
+ <value-stream-filters
+ :group-id="endpoints.groupId"
+ :group-path="endpoints.groupPath"
+ :has-project-filter="false"
+ :start-date="createdAfter"
+ :end-date="createdBefore"
+ @setDateRange="onSetDateRange"
+ />
<value-stream-metrics
:request-path="endpoints.fullPath"
:request-params="filterParams"
@@ -178,6 +164,7 @@ export default {
:empty-state-message="emptyStageText"
:no-data-svg-path="noDataSvgPath"
:pagination="null"
+ :sortable="false"
/>
</div>
</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_table.vue b/app/assets/javascripts/cycle_analytics/components/stage_table.vue
index 0c47838c773..8a2667a4ab1 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_table.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_table.vue
@@ -23,8 +23,8 @@ import TotalTime from './total_time_component.vue';
const DEFAULT_WORKFLOW_TITLE_PROPERTIES = {
thClass: 'gl-w-half',
key: PAGINATION_SORT_FIELD_END_EVENT,
- sortable: true,
};
+
const WORKFLOW_COLUMN_TITLES = {
issues: { ...DEFAULT_WORKFLOW_TITLE_PROPERTIES, label: __('Issues') },
jobs: { ...DEFAULT_WORKFLOW_TITLE_PROPERTIES, label: __('Jobs') },
@@ -84,6 +84,11 @@ export default {
required: false,
default: null,
},
+ sortable: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
data() {
if (this.pagination) {
@@ -122,9 +127,11 @@ export default {
key: PAGINATION_SORT_FIELD_DURATION,
label: __('Time'),
thClass: 'gl-w-half',
- sortable: true,
},
- ];
+ ].map((field) => ({
+ ...field,
+ sortable: this.sortable,
+ }));
},
prevPage() {
return Math.max(this.pagination.page - 1, 0);
diff --git a/app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue b/app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue
index 6b1e537dc77..8610dfc2b03 100644
--- a/app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue
+++ b/app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue
@@ -61,33 +61,38 @@ export default {
<template>
<div class="gl-mt-3 gl-py-2 gl-px-3 bg-gray-light border-top border-bottom">
<filter-bar
- class="js-filter-bar filtered-search-box gl-display-flex gl-mb-2 gl-mr-3 gl-border-none"
+ data-testid="vsa-filter-bar"
+ class="filtered-search-box gl-display-flex gl-mb-2 gl-mr-3 gl-border-none"
:group-path="groupPath"
/>
<div
v-if="hasDateRangeFilter || hasProjectFilter"
class="gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row gl-justify-content-space-between"
>
- <projects-dropdown-filter
- v-if="hasProjectFilter"
- :key="groupId"
- class="js-projects-dropdown-filter project-select gl-mb-2 gl-lg-mb-0"
- :group-id="groupId"
- :group-namespace="groupPath"
- :query-params="projectsQueryParams"
- :multi-select="$options.multiProjectSelect"
- :default-projects="selectedProjects"
- @selected="$emit('selectProject', $event)"
- />
- <date-range
- v-if="hasDateRangeFilter"
- :start-date="startDate"
- :end-date="endDate"
- :max-date-range="$options.maxDateRange"
- :include-selected-date="true"
- class="js-daterange-picker"
- @change="$emit('setDateRange', $event)"
- />
+ <div>
+ <projects-dropdown-filter
+ v-if="hasProjectFilter"
+ :key="groupId"
+ class="js-projects-dropdown-filter project-select gl-mb-2 gl-lg-mb-0"
+ :group-id="groupId"
+ :group-namespace="groupPath"
+ :query-params="projectsQueryParams"
+ :multi-select="$options.multiProjectSelect"
+ :default-projects="selectedProjects"
+ @selected="$emit('selectProject', $event)"
+ />
+ </div>
+ <div>
+ <date-range
+ v-if="hasDateRangeFilter"
+ :start-date="startDate"
+ :end-date="endDate"
+ :max-date-range="$options.maxDateRange"
+ :include-selected-date="true"
+ class="js-daterange-picker"
+ @change="$emit('setDateRange', $event)"
+ />
+ </div>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/cycle_analytics/index.js b/app/assets/javascripts/cycle_analytics/index.js
index 3827db4d9b2..620da0104e0 100644
--- a/app/assets/javascripts/cycle_analytics/index.js
+++ b/app/assets/javascripts/cycle_analytics/index.js
@@ -1,7 +1,9 @@
import Vue from 'vue';
import Translate from '../vue_shared/translate';
import CycleAnalytics from './components/base.vue';
+import { DEFAULT_DAYS_TO_DISPLAY } from './constants';
import createStore from './store';
+import { calculateFormattedDayInPast } from './utils';
Vue.use(Translate);
@@ -14,19 +16,29 @@ export default () => {
requestPath,
fullPath,
projectId,
+ groupId,
groupPath,
+ labelsPath,
+ milestonesPath,
} = el.dataset;
+ const { now, past } = calculateFormattedDayInPast(DEFAULT_DAYS_TO_DISPLAY);
+
store.dispatch('initializeVsa', {
projectId: parseInt(projectId, 10),
- groupPath,
endpoints: {
requestPath,
fullPath,
+ labelsPath,
+ milestonesPath,
+ groupId: parseInt(groupId, 10),
+ groupPath,
},
features: {
cycleAnalyticsForGroups: Boolean(gon?.licensed_features?.cycleAnalyticsForGroups),
},
+ createdBefore: new Date(now),
+ createdAfter: new Date(past),
});
// eslint-disable-next-line no-new
diff --git a/app/assets/javascripts/cycle_analytics/store/actions.js b/app/assets/javascripts/cycle_analytics/store/actions.js
index a7a2c8ea9d3..e39cd224199 100644
--- a/app/assets/javascripts/cycle_analytics/store/actions.js
+++ b/app/assets/javascripts/cycle_analytics/store/actions.js
@@ -163,6 +163,7 @@ const refetchStageData = (dispatch) => {
dispatch('fetchCycleAnalyticsData'),
dispatch('fetchStageData'),
dispatch('fetchStageMedians'),
+ dispatch('fetchStageCountValues'),
]),
)
.finally(() => dispatch('setLoading', false));
@@ -170,14 +171,24 @@ const refetchStageData = (dispatch) => {
export const setFilters = ({ dispatch }) => refetchStageData(dispatch);
-export const setDateRange = ({ dispatch, commit }, daysInPast) => {
- commit(types.SET_DATE_RANGE, daysInPast);
+export const setDateRange = ({ dispatch, commit }, { createdAfter, createdBefore }) => {
+ commit(types.SET_DATE_RANGE, { createdAfter, createdBefore });
return refetchStageData(dispatch);
};
export const initializeVsa = ({ commit, dispatch }, initialData = {}) => {
commit(types.INITIALIZE_VSA, initialData);
+ const {
+ endpoints: { fullPath, groupPath, milestonesPath = '', labelsPath = '' },
+ } = initialData;
+ dispatch('filters/setEndpoints', {
+ labelsEndpoint: labelsPath,
+ milestonesEndpoint: milestonesPath,
+ groupEndpoint: groupPath,
+ projectEndpoint: fullPath,
+ });
+
return dispatch('setLoading', true)
.then(() => dispatch('fetchValueStreams'))
.finally(() => dispatch('setLoading', false));
diff --git a/app/assets/javascripts/cycle_analytics/store/getters.js b/app/assets/javascripts/cycle_analytics/store/getters.js
index 9faccabcaad..77c285f5ce0 100644
--- a/app/assets/javascripts/cycle_analytics/store/getters.js
+++ b/app/assets/javascripts/cycle_analytics/store/getters.js
@@ -1,5 +1,6 @@
import dateFormat from 'dateformat';
import { dateFormats } from '~/analytics/shared/constants';
+import { filterToQueryObject } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import { transformStagesForPathNavigation, filterStagesByHiddenStatus } from '../utils';
export const pathNavigationData = ({ stages, medians, stageCounts, selectedStage }) => {
@@ -20,6 +21,21 @@ export const requestParams = (state) => {
return { requestPath: fullPath, valueStreamId, stageId };
};
+const filterBarParams = ({ filters }) => {
+ const {
+ authors: { selected: selectedAuthor },
+ milestones: { selected: selectedMilestone },
+ assignees: { selectedList: selectedAssigneeList },
+ labels: { selectedList: selectedLabelList },
+ } = filters;
+ return filterToQueryObject({
+ milestone_title: selectedMilestone,
+ author_username: selectedAuthor,
+ label_name: selectedLabelList,
+ assignee_username: selectedAssigneeList,
+ });
+};
+
const dateRangeParams = ({ createdAfter, createdBefore }) => ({
created_after: createdAfter ? dateFormat(createdAfter, dateFormats.isoDate) : null,
created_before: createdBefore ? dateFormat(createdBefore, dateFormats.isoDate) : null,
@@ -33,6 +49,7 @@ export const legacyFilterParams = ({ daysInPast }) => {
export const filterParams = (state) => {
return {
+ ...filterBarParams(state),
...dateRangeParams(state),
};
};
diff --git a/app/assets/javascripts/cycle_analytics/store/mutations.js b/app/assets/javascripts/cycle_analytics/store/mutations.js
index e41de85c1fa..301e7d95f8c 100644
--- a/app/assets/javascripts/cycle_analytics/store/mutations.js
+++ b/app/assets/javascripts/cycle_analytics/store/mutations.js
@@ -1,14 +1,12 @@
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import { DEFAULT_DAYS_TO_DISPLAY } from '../constants';
-import { formatMedianValues, calculateFormattedDayInPast } from '../utils';
+import { formatMedianValues } from '../utils';
import * as types from './mutation_types';
export default {
- [types.INITIALIZE_VSA](state, { endpoints, features }) {
+ [types.INITIALIZE_VSA](state, { endpoints, features, createdBefore, createdAfter }) {
state.endpoints = endpoints;
- const { now, past } = calculateFormattedDayInPast(DEFAULT_DAYS_TO_DISPLAY);
- state.createdBefore = now;
- state.createdAfter = past;
+ state.createdBefore = createdBefore;
+ state.createdAfter = createdAfter;
state.features = features;
},
[types.SET_LOADING](state, loadingState) {
@@ -20,11 +18,9 @@ export default {
[types.SET_SELECTED_STAGE](state, stage) {
state.selectedStage = stage;
},
- [types.SET_DATE_RANGE](state, daysInPast) {
- state.daysInPast = daysInPast;
- const { now, past } = calculateFormattedDayInPast(daysInPast);
- state.createdBefore = now;
- state.createdAfter = past;
+ [types.SET_DATE_RANGE](state, { createdAfter, createdBefore }) {
+ state.createdBefore = createdBefore;
+ state.createdAfter = createdAfter;
},
[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 e6da3f609b2..0882db51218 100644
--- a/app/assets/javascripts/cycle_analytics/store/state.js
+++ b/app/assets/javascripts/cycle_analytics/store/state.js
@@ -1,10 +1,7 @@
-import { DEFAULT_DAYS_TO_DISPLAY } from '../constants';
-
export default () => ({
id: null,
features: {},
endpoints: {},
- daysInPast: DEFAULT_DAYS_TO_DISPLAY,
createdAfter: null,
createdBefore: null,
stages: [],