2021-04-17 20:07:23 +05:30
|
|
|
<script>
|
2021-10-27 15:23:28 +05:30
|
|
|
import { GlIcon, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
|
2021-04-17 20:07:23 +05:30
|
|
|
import Cookies from 'js-cookie';
|
2021-09-04 01:27:46 +05:30
|
|
|
import { mapActions, mapState, mapGetters } from 'vuex';
|
|
|
|
import PathNavigation from '~/cycle_analytics/components/path_navigation.vue';
|
2021-10-27 15:23:28 +05:30
|
|
|
import StageTable from '~/cycle_analytics/components/stage_table.vue';
|
|
|
|
import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue';
|
2021-04-17 20:07:23 +05:30
|
|
|
import { __ } from '~/locale';
|
2021-10-27 15:23:28 +05:30
|
|
|
import { SUMMARY_METRICS_REQUEST, METRICS_REQUESTS } from '../constants';
|
2021-04-17 20:07:23 +05:30
|
|
|
|
|
|
|
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
|
|
|
|
|
|
|
|
export default {
|
|
|
|
name: 'CycleAnalytics',
|
|
|
|
components: {
|
|
|
|
GlIcon,
|
|
|
|
GlLoadingIcon,
|
|
|
|
GlSprintf,
|
2021-09-04 01:27:46 +05:30
|
|
|
PathNavigation,
|
2021-10-27 15:23:28 +05:30
|
|
|
StageTable,
|
|
|
|
ValueStreamMetrics,
|
2021-04-17 20:07:23 +05:30
|
|
|
},
|
|
|
|
props: {
|
|
|
|
noDataSvgPath: {
|
|
|
|
type: String,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
noAccessSvgPath: {
|
|
|
|
type: String,
|
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
isOverviewDialogDismissed: Cookies.get(OVERVIEW_DIALOG_COOKIE),
|
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
2021-06-08 01:23:25 +05:30
|
|
|
...mapState([
|
|
|
|
'isLoading',
|
|
|
|
'isLoadingStage',
|
|
|
|
'isEmptyStage',
|
|
|
|
'selectedStage',
|
|
|
|
'selectedStageEvents',
|
2021-09-04 01:27:46 +05:30
|
|
|
'selectedStageError',
|
2021-06-08 01:23:25 +05:30
|
|
|
'stages',
|
|
|
|
'summary',
|
2021-10-27 15:23:28 +05:30
|
|
|
'daysInPast',
|
2021-09-04 01:27:46 +05:30
|
|
|
'permissions',
|
2021-10-27 15:23:28 +05:30
|
|
|
'stageCounts',
|
|
|
|
'endpoints',
|
|
|
|
'features',
|
2021-06-08 01:23:25 +05:30
|
|
|
]),
|
2021-10-27 15:23:28 +05:30
|
|
|
...mapGetters(['pathNavigationData', 'filterParams']),
|
2021-06-08 01:23:25 +05:30
|
|
|
displayStageEvents() {
|
|
|
|
const { selectedStageEvents, isLoadingStage, isEmptyStage } = this;
|
|
|
|
return selectedStageEvents.length && !isLoadingStage && !isEmptyStage;
|
|
|
|
},
|
|
|
|
displayNotEnoughData() {
|
2021-10-27 15:23:28 +05:30
|
|
|
return !this.isLoadingStage && this.isEmptyStage;
|
2021-06-08 01:23:25 +05:30
|
|
|
},
|
|
|
|
displayNoAccess() {
|
2021-10-27 15:23:28 +05:30
|
|
|
return (
|
|
|
|
!this.isLoadingStage && this.selectedStage?.id && !this.isUserAllowed(this.selectedStage.id)
|
|
|
|
);
|
2021-09-04 01:27:46 +05:30
|
|
|
},
|
2021-10-27 15:23:28 +05:30
|
|
|
displayPathNavigation() {
|
|
|
|
return this.isLoading || (this.selectedStage && this.pathNavigationData.length);
|
2021-09-04 01:27:46 +05:30
|
|
|
},
|
|
|
|
emptyStageTitle() {
|
2021-10-27 15:23:28 +05:30
|
|
|
if (this.displayNoAccess) {
|
|
|
|
return __('You need permission.');
|
|
|
|
}
|
2021-09-04 01:27:46 +05:30
|
|
|
return this.selectedStageError
|
|
|
|
? this.selectedStageError
|
|
|
|
: __("We don't have enough data to show this stage.");
|
|
|
|
},
|
|
|
|
emptyStageText() {
|
2021-10-27 15:23:28 +05:30
|
|
|
if (this.displayNoAccess) {
|
|
|
|
return __('Want to see the data? Please ask an administrator for access.');
|
|
|
|
}
|
|
|
|
return !this.selectedStageError && this.selectedStage?.emptyStageText
|
|
|
|
? this.selectedStage?.emptyStageText
|
|
|
|
: '';
|
|
|
|
},
|
|
|
|
selectedStageCount() {
|
|
|
|
if (this.selectedStage) {
|
|
|
|
const {
|
|
|
|
stageCounts,
|
|
|
|
selectedStage: { id },
|
|
|
|
} = this;
|
|
|
|
return stageCounts[id];
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
},
|
|
|
|
metricsRequests() {
|
|
|
|
return this.features?.cycleAnalyticsForGroups ? METRICS_REQUESTS : SUMMARY_METRICS_REQUEST;
|
2021-04-17 20:07:23 +05:30
|
|
|
},
|
|
|
|
},
|
|
|
|
methods: {
|
2021-06-08 01:23:25 +05:30
|
|
|
...mapActions([
|
|
|
|
'fetchCycleAnalyticsData',
|
|
|
|
'fetchStageData',
|
|
|
|
'setSelectedStage',
|
|
|
|
'setDateRange',
|
|
|
|
]),
|
2021-10-27 15:23:28 +05:30
|
|
|
handleDateSelect(daysInPast) {
|
|
|
|
this.setDateRange(daysInPast);
|
2021-04-17 20:07:23 +05:30
|
|
|
},
|
2021-09-04 01:27:46 +05:30
|
|
|
onSelectStage(stage) {
|
2021-06-08 01:23:25 +05:30
|
|
|
this.setSelectedStage(stage);
|
2021-04-17 20:07:23 +05:30
|
|
|
},
|
|
|
|
dismissOverviewDialog() {
|
|
|
|
this.isOverviewDialogDismissed = true;
|
|
|
|
Cookies.set(OVERVIEW_DIALOG_COOKIE, '1', { expires: 365 });
|
|
|
|
},
|
2021-09-04 01:27:46 +05:30
|
|
|
isUserAllowed(id) {
|
|
|
|
const { permissions } = this;
|
|
|
|
return Boolean(permissions?.[id]);
|
|
|
|
},
|
2021-04-17 20:07:23 +05:30
|
|
|
},
|
|
|
|
dayRangeOptions: [7, 30, 90],
|
|
|
|
i18n: {
|
|
|
|
dropdownText: __('Last %{days} days'),
|
2021-10-27 15:23:28 +05:30
|
|
|
pageTitle: __('Value Stream Analytics'),
|
|
|
|
recentActivity: __('Recent Project Activity'),
|
2021-04-17 20:07:23 +05:30
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
<template>
|
|
|
|
<div class="cycle-analytics">
|
2021-10-27 15:23:28 +05:30
|
|
|
<h3>{{ $options.i18n.pageTitle }}</h3>
|
|
|
|
<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"
|
|
|
|
: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>
|
2021-04-17 20:07:23 +05:30
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2021-10-27 15:23:28 +05:30
|
|
|
<value-stream-metrics
|
|
|
|
:request-path="endpoints.fullPath"
|
|
|
|
:request-params="filterParams"
|
|
|
|
:requests="metricsRequests"
|
|
|
|
/>
|
|
|
|
<gl-loading-icon v-if="isLoading" size="lg" />
|
|
|
|
<stage-table
|
|
|
|
v-else
|
|
|
|
:is-loading="isLoading || isLoadingStage"
|
|
|
|
:stage-events="selectedStageEvents"
|
|
|
|
:selected-stage="selectedStage"
|
|
|
|
:stage-count="selectedStageCount"
|
|
|
|
:empty-state-title="emptyStageTitle"
|
|
|
|
:empty-state-message="emptyStageText"
|
|
|
|
:no-data-svg-path="noDataSvgPath"
|
|
|
|
:pagination="null"
|
|
|
|
/>
|
2021-04-17 20:07:23 +05:30
|
|
|
</div>
|
|
|
|
</template>
|