summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/cycle_analytics
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/cycle_analytics')
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_card_list_item.vue41
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue88
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js36
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_service.js8
4 files changed, 163 insertions, 10 deletions
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_card_list_item.vue b/app/assets/javascripts/cycle_analytics/components/stage_card_list_item.vue
new file mode 100644
index 00000000000..d946594a069
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_card_list_item.vue
@@ -0,0 +1,41 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import { GlButton } from '@gitlab/ui';
+
+export default {
+ name: 'StageCardListItem',
+ components: {
+ Icon,
+ GlButton,
+ },
+ props: {
+ isActive: {
+ type: Boolean,
+ required: true,
+ },
+ canEdit: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ },
+};
+</script>
+
+<template>
+ <div :class="{ active: isActive }" class="stage-nav-item d-flex pl-4 pr-4 m-0 mb-1 ml-2 rounded">
+ <slot></slot>
+ <div v-if="canEdit" class="dropdown">
+ <gl-button
+ :title="__('More actions')"
+ class="more-actions-toggle btn btn-transparent p-0"
+ data-toggle="dropdown"
+ >
+ <icon css-classes="icon" name="ellipsis_v" />
+ </gl-button>
+ <ul class="more-actions-dropdown dropdown-menu dropdown-open-left">
+ <slot name="dropdown-options"></slot>
+ </ul>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue b/app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue
new file mode 100644
index 00000000000..004d335f572
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/stage_nav_item.vue
@@ -0,0 +1,88 @@
+<script>
+import StageCardListItem from './stage_card_list_item.vue';
+
+export default {
+ name: 'StageNavItem',
+ components: {
+ StageCardListItem,
+ },
+ props: {
+ isDefaultStage: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ isActive: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ isUserAllowed: {
+ type: Boolean,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ value: {
+ type: String,
+ default: '',
+ required: false,
+ },
+ canEdit: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ },
+ computed: {
+ hasValue() {
+ return this.value && this.value.length > 0;
+ },
+ editable() {
+ return this.isUserAllowed && this.canEdit;
+ },
+ },
+};
+</script>
+
+<template>
+ <li @click="$emit('select')">
+ <stage-card-list-item :is-active="isActive" :can-edit="editable">
+ <div class="stage-nav-item-cell stage-name p-0" :class="{ 'font-weight-bold': isActive }">
+ {{ title }}
+ </div>
+ <div class="stage-nav-item-cell stage-median mr-4">
+ <template v-if="isUserAllowed">
+ <span v-if="hasValue">{{ value }}</span>
+ <span v-else class="stage-empty">{{ __('Not enough data') }}</span>
+ </template>
+ <template v-else>
+ <span class="not-available">{{ __('Not available') }}</span>
+ </template>
+ </div>
+ <template v-slot:dropdown-options>
+ <template v-if="isDefaultStage">
+ <li>
+ <button type="button" class="btn-default btn-transparent">
+ {{ __('Hide stage') }}
+ </button>
+ </li>
+ </template>
+ <template v-else>
+ <li>
+ <button type="button" class="btn-default btn-transparent">
+ {{ __('Edit stage') }}
+ </button>
+ </li>
+ <li>
+ <button type="button" class="btn-danger danger">
+ {{ __('Remove stage') }}
+ </button>
+ </li>
+ </template>
+ </template>
+ </stage-card-list-item>
+ </li>
+</template>
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index b56e08175cc..b3ae47af750 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -1,7 +1,10 @@
import $ from 'jquery';
import Vue from 'vue';
import Cookies from 'js-cookie';
+import { GlEmptyState } from '@gitlab/ui';
+import filterMixins from 'ee_else_ce/analytics/cycle_analytics/mixins/filter_mixins';
import Flash from '../flash';
+import { __ } from '~/locale';
import Translate from '../vue_shared/translate';
import banner from './components/banner.vue';
import stageCodeComponent from './components/stage_code_component.vue';
@@ -9,20 +12,22 @@ import stageComponent from './components/stage_component.vue';
import stageReviewComponent from './components/stage_review_component.vue';
import stageStagingComponent from './components/stage_staging_component.vue';
import stageTestComponent from './components/stage_test_component.vue';
+import stageNavItem from './components/stage_nav_item.vue';
import CycleAnalyticsService from './cycle_analytics_service';
import CycleAnalyticsStore from './cycle_analytics_store';
-import { __ } from '~/locale';
Vue.use(Translate);
export default () => {
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
+ const cycleAnalyticsEl = document.querySelector('#cycle-analytics');
// eslint-disable-next-line no-new
new Vue({
el: '#cycle-analytics',
name: 'CycleAnalytics',
components: {
+ GlEmptyState,
banner,
'stage-issue-component': stageComponent,
'stage-plan-component': stageComponent,
@@ -31,13 +36,16 @@ export default () => {
'stage-review-component': stageReviewComponent,
'stage-staging-component': stageStagingComponent,
'stage-production-component': stageComponent,
+ GroupsDropdownFilter: () =>
+ import('ee_component/analytics/shared/components/groups_dropdown_filter.vue'),
+ ProjectsDropdownFilter: () =>
+ import('ee_component/analytics/shared/components/projects_dropdown_filter.vue'),
+ DateRangeDropdown: () =>
+ import('ee_component/analytics/shared/components/date_range_dropdown.vue'),
+ 'stage-nav-item': stageNavItem,
},
+ mixins: [filterMixins],
data() {
- const cycleAnalyticsEl = document.querySelector('#cycle-analytics');
- const cycleAnalyticsService = new CycleAnalyticsService({
- requestPath: cycleAnalyticsEl.dataset.requestPath,
- });
-
return {
store: CycleAnalyticsStore,
state: CycleAnalyticsStore.state,
@@ -47,7 +55,7 @@ export default () => {
hasError: false,
startDate: 30,
isOverviewDialogDismissed: Cookies.get(OVERVIEW_DIALOG_COOKIE),
- service: cycleAnalyticsService,
+ service: this.createCycleAnalyticsService(cycleAnalyticsEl.dataset.requestPath),
};
},
computed: {
@@ -56,7 +64,13 @@ export default () => {
},
},
created() {
- this.fetchCycleAnalyticsData();
+ // Conditional check placed here to prevent this method from being called on the
+ // new Cycle Analytics page (i.e. the new page will be initialized blank and only
+ // after a group is selected the cycle analyitcs data will be fetched). Once the
+ // old (current) page has been removed this entire created method as well as the
+ // variable itself can be completely removed.
+ // Follow up issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/64490
+ if (cycleAnalyticsEl.dataset.requestPath) this.fetchCycleAnalyticsData();
},
methods: {
handleError() {
@@ -118,6 +132,7 @@ export default () => {
.fetchStageData({
stage,
startDate: this.startDate,
+ projectIds: this.selectedProjectIds,
})
.then(response => {
this.isEmptyStage = !response.events.length;
@@ -133,6 +148,11 @@ export default () => {
this.isOverviewDialogDismissed = true;
Cookies.set(OVERVIEW_DIALOG_COOKIE, '1', { expires: 365 });
},
+ createCycleAnalyticsService(requestPath) {
+ return new CycleAnalyticsService({
+ requestPath,
+ });
+ },
},
});
};
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
index a0426301a0a..babbfe93082 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
@@ -8,22 +8,26 @@ export default class CycleAnalyticsService {
}
fetchCycleAnalyticsData(options = { startDate: 30 }) {
+ const { startDate, projectIds } = options;
+
return this.axios
.get('', {
params: {
- 'cycle_analytics[start_date]': options.startDate,
+ 'cycle_analytics[start_date]': startDate,
+ 'cycle_analytics[project_ids]': projectIds,
},
})
.then(x => x.data);
}
fetchStageData(options) {
- const { stage, startDate } = options;
+ const { stage, startDate, projectIds } = options;
return this.axios
.get(`events/${stage.name}.json`, {
params: {
'cycle_analytics[start_date]': startDate,
+ 'cycle_analytics[project_ids]': projectIds,
},
})
.then(x => x.data);