summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-09-22 17:57:08 +0100
committerFilipa Lacerda <filipa@gitlab.com>2017-09-22 17:57:08 +0100
commitc3da38f451ecd08cc22dafe7dd7734115a8c164e (patch)
tree3fb5fb9b8569c5307cc0757eb3bf7098486a64a0
parent40e32546f5041fd848ffd2f232850a9c51feb59c (diff)
downloadgitlab-ce-34371-cycle-analitcs.tar.gz
-rw-r--r--app/assets/javascripts/cycle_analytics/components/banner.vue2
-rw-r--r--app/assets/javascripts/cycle_analytics/components/cycle_analitics_app.vue54
-rw-r--r--app/assets/javascripts/cycle_analytics/components/panel_header.vue19
-rw-r--r--app/assets/javascripts/cycle_analytics/components/pipeline_health.vue27
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_component.vue2
-rw-r--r--app/assets/javascripts/cycle_analytics/components/total_time_component.js25
-rw-r--r--app/assets/javascripts/cycle_analytics/components/total_time_component.vue20
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js137
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_service.js4
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_store.js35
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss4
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml91
12 files changed, 107 insertions, 313 deletions
diff --git a/app/assets/javascripts/cycle_analytics/components/banner.vue b/app/assets/javascripts/cycle_analytics/components/banner.vue
index 8c5d7a406e9..46daea785b6 100644
--- a/app/assets/javascripts/cycle_analytics/components/banner.vue
+++ b/app/assets/javascripts/cycle_analytics/components/banner.vue
@@ -1,5 +1,5 @@
<script>
- import iconCycleAnalyticsSplash from 'icons/icon_cycle_analytics_splash.svg';
+ import iconCycleAnalyticsSplash from 'icons/_icon_cycle_analytics_splash.svg';
export default {
name: 'cycleAnalyticsBanner',
diff --git a/app/assets/javascripts/cycle_analytics/components/cycle_analitics_app.vue b/app/assets/javascripts/cycle_analytics/components/cycle_analitics_app.vue
index 1329e54fa31..98b144884b3 100644
--- a/app/assets/javascripts/cycle_analytics/components/cycle_analitics_app.vue
+++ b/app/assets/javascripts/cycle_analytics/components/cycle_analitics_app.vue
@@ -4,8 +4,9 @@
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import pipelineHealth from './pipeline_health.vue';
import panelHeader from './panel_header.vue';
- import iconLock from '~/icons/icon_lock.svg';
- import iconNoData from '~/icons/icon_no_data.svg';
+ import banner from './banner.vue'
+ import iconLock from 'icons/_icon_lock.svg';
+ import iconNoData from 'icons/_icon_no_data.svg';
import Store from '../cycle_analytics_store';
import Service from '../cycle_analytics_service';
@@ -26,16 +27,24 @@
type: Boolean,
required: true,
},
+ cssClass: {
+ type: String,
+ required: false,
+ },
},
components: {
loadingIcon,
+ banner,
panelHeader,
pipelineHealth,
stageComponent,
+ 'stage-issue-component': stageComponent,
+ 'stage-code-component': stageComponent,
+ 'stage-test-component': stageComponent,
},
data() {
const store = new Store();
- const service = new Service(this.endpoint);
+ const service = new Service({ endpoint: this.endpoint });
return {
store,
@@ -51,12 +60,16 @@
},
computed: {
currentStage() {
- return this.store.currentActiveStage();
+ console.log(this.store.currentActiveStage())
+ return this.store.currentActiveStage() || {};
},
iconLock() {
return iconLock;
},
},
+ created() {
+ this.fetchCycleAnalyticsData();
+ },
methods: {
handleError() {
this.store.setErrorState(true);
@@ -66,19 +79,6 @@
this.startDate = value;
this.fetchCycleAnalyticsData({ startDate: this.startDate });
},
- // initDropdown() {
- // const $dropdown = $('.js-ca-dropdown');
- // const $label = $dropdown.find('.dropdown-label');
-
- // $dropdown.find('li a').off('click').on('click', (e) => {
- // e.preventDefault();
- // const $target = $(e.currentTarget);
- // this.startDate = $target.data('value');
-
- // $label.text($target.text().trim());
- // this.fetchCycleAnalyticsData({ startDate: this.startDate });
- // });
- // },
fetchCycleAnalyticsData(options) {
const fetchOptions = options || { startDate: this.startDate };
@@ -90,7 +90,6 @@
.then((response) => {
this.store.setCycleAnalyticsData(response);
this.selectDefaultStage();
- this.initDropdown();
this.isLoading = false;
})
.catch(() => {
@@ -139,7 +138,7 @@
};
</script>
<template>
- <div>
+ <div :class="cssClass" id="cycle-analytics">
<banner
v-if="noData && !isOverviewDialogDismissed"
@dimissBanner="dismissOverviewDialog"
@@ -148,20 +147,21 @@
<template v-if="!isLoading && !hasError">
<pipeline-health
:analytics-data="state"
+ :start-date="startDate"
@onClickDropdown="onClickDropdown"
/>
<div class="panel panel-default stage-panel">
- <panel-heading :current-state="currentStage" />
+ <panel-header :current-state="currentStage" />
<div class="stage-panel-body">
<div class="nav stage-nav">
<ul>
<li
class="stage-nav-item"
+ v-for="(stage, i) in state.stages"
:class="{ active: stage.active }"
- @click="selectStage(stage)"
- v-for="(stage, i) in stage.stages">
+ @click="selectStage(stage)">
<div class="stage-nav-item-cell stage-name">
{{ stage.title }}
</div>
@@ -173,22 +173,22 @@
<span
v-else
class="stage-empty">
- {{ __('Not enough data') }}
+ {{__('Not enough data')}}
</span>
</template>
<template v-else>
<span class="not-available">
- {{ __('Not available') }}
+ {{__('Not available')}}
</span>
</template>
</div>
- </li>
+ </li>
</ul>
</div>
<div class="section stage-events">
<loading-icon v-if="isLoadingStage" />
<div
- v-if="currentStage && !currentStage.isUserAlllowed"
+ v-else-if="!isLoadingStage && currentStage && !currentStage.isUserAlllowed"
class="no-access-stage">
<div class="icon-lock" v-html="iconLock">
</div>
@@ -217,7 +217,7 @@
:stage="currentStage"
:items="state.events"
/>
- </div>
+ </template>
</template>
</div>
</div>
diff --git a/app/assets/javascripts/cycle_analytics/components/panel_header.vue b/app/assets/javascripts/cycle_analytics/components/panel_header.vue
index 64620f0e59c..fe253ed2b85 100644
--- a/app/assets/javascripts/cycle_analytics/components/panel_header.vue
+++ b/app/assets/javascripts/cycle_analytics/components/panel_header.vue
@@ -1,11 +1,16 @@
<script>
+ import tooltip from '../../vue_shared/directives/tooltip';
+
export default {
props: {
currentStage: {
type: Object,
- required: true,
+ required: false,
},
},
+ directives: {
+ tooltip
+ },
};
</script>
<template>
@@ -14,24 +19,24 @@
<ul>
<li class="stage-header">
<span class="stage-name">
- {{s__('ProjectLifecycle|Stage')}}
+ {{__('ProjectLifecycle|Stage')}}
</span>
<i
class="fa fa-question-circle"
v-tooltip
- :title="_("The phase of the development lifecycle.")"
+ :title="__('The phase of the development lifecycle.')"
data-placement="top"
aria-hidden="true">
</i>
</li>
<li class="median-header">
<span class="stage-name">
- {{ __('Median') }}
+ {{ __('Median')}}
</span>
<i
class="fa fa-question-circle"
v-tooltip
- :title="_("The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.")"
+ :title="__('The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.')"
data-placement="top"
aria-hidden="true">
</i>
@@ -43,7 +48,7 @@
<i
class="fa fa-question-circle"
v-tooltip
- :title="_("The collection of events added to the data gathered for that stage.")"
+ :title="__('The collection of events added to the data gathered for that stage.')"
data-placement="top"
aria-hidden="true">
</i>
@@ -55,7 +60,7 @@
<i
class="fa fa-question-circle"
v-tooltip
- :title="_("The time taken by each data entry gathered by that stage.")"
+ :title="__('The time taken by each data entry gathered by that stage.')"
data-placement="top"
aria-hidden="true">
</i>
diff --git a/app/assets/javascripts/cycle_analytics/components/pipeline_health.vue b/app/assets/javascripts/cycle_analytics/components/pipeline_health.vue
index e3b9e626f2d..225d4155879 100644
--- a/app/assets/javascripts/cycle_analytics/components/pipeline_health.vue
+++ b/app/assets/javascripts/cycle_analytics/components/pipeline_health.vue
@@ -5,6 +5,10 @@
type: Object,
required: true,
},
+ startDate: {
+ type: Number,
+ required: true,
+ },
},
methods: {
onClickDropdown(value) {
@@ -32,13 +36,13 @@
</p>
</div>
<div class="col-sm-3 col-xs-12 column">
- <div class="dropdown">
+ <div class="dropdown prepend-top-10">
<button
type="button"
data-toggle="dropdown"
class="dropdown-menu-toggle">
<span class="dropdown-label">
- {{ n__('Last %d day', 'Last %d days', 30) }}
+ {{ n__('Last %d day', 'Last %d days', startDate) }}
</span>
<i
class="fa fa-chevron-down"
@@ -46,18 +50,25 @@
</i>
</button>
<ul class="dropdown-menu dropdowm-menu-align-right">
- <li @click="onClickDropdown(7)">
- {{ n__('Last %d day', 'Last %d days', 7) }}
+ <li>
+ <a @click="onClickDropdown(7)">
+ {{ n__('Last %d day', 'Last %d days', 7) }}
+ </a>
</li>
- <li @click="onClickDropdown(30)">
- {{ n__('Last %d day', 'Last %d days', 30) }}
+ <li>
+ <a @click="onClickDropdown(30)">
+ {{ n__('Last %d day', 'Last %d days', 30) }}
+ </a>
</li>
- <li @click="onClickDropdown(90)">
- {{ n__('Last %d day', 'Last %d days', 90) }}
+ <li>
+ <a @click="onClickDropdown(90)">
+ {{ n__('Last %d day', 'Last %d days', 90) }}
+ </a>
</li>
</ul>
</div>
</div>
</div>
</div>
+ </div>
</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_component.vue
index 032b088f46b..cf5ed934d83 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_component.vue
@@ -2,7 +2,7 @@
<script>
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
import limitWarning from './limit_warning_component.vue';
- import totalTime from './total_time_component';
+ import totalTime from './total_time_component.vue';
export default {
props: {
diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.js b/app/assets/javascripts/cycle_analytics/components/total_time_component.js
deleted file mode 100644
index d5e6167b2a8..00000000000
--- a/app/assets/javascripts/cycle_analytics/components/total_time_component.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/* eslint-disable no-param-reassign */
-
-import Vue from 'vue';
-
-const global = window.gl || (window.gl = {});
-global.cycleAnalytics = global.cycleAnalytics || {};
-
-global.cycleAnalytics.TotalTimeComponent = Vue.extend({
- props: {
- time: Object,
- },
- template: `
- <span class="total-time">
- <template v-if="Object.keys(time).length">
- <template v-if="time.days">{{ time.days }} <span>{{ n__('day', 'days', time.days) }}</span></template>
- <template v-if="time.hours">{{ time.hours }} <span>{{ n__('Time|hr', 'Time|hrs', time.hours) }}</span></template>
- <template v-if="time.mins && !time.days">{{ time.mins }} <span>{{ n__('Time|min', 'Time|mins', time.mins) }}</span></template>
- <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>{{ s__('Time|s') }}</span></template>
- </template>
- <template v-else>
- --
- </template>
- </span>
- `,
-});
diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.vue b/app/assets/javascripts/cycle_analytics/components/total_time_component.vue
new file mode 100644
index 00000000000..e7985507c0c
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/total_time_component.vue
@@ -0,0 +1,20 @@
+<script>
+ export default {
+ props: {
+ time: Object,
+ },
+ };
+</script>
+<template>
+ <span class="total-time">
+ <template v-if="Object.keys(time).length">
+ <template v-if="time.days">{{ time.days }} <span>{{ n__('day', 'days', time.days) }}</span></template>
+ <template v-if="time.hours">{{ time.hours }} <span>{{ n__('Time|hr', 'Time|hrs', time.hours) }}</span></template>
+ <template v-if="time.mins && !time.days">{{ time.mins }} <span>{{ n__('Time|min', 'Time|mins', time.mins) }}</span></template>
+ <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>{{ s__('Time|s') }}</span></template>
+ </template>
+ <template v-else>
+ --
+ </template>
+ </span>
+</template>
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index f3e4432b62d..5c9e0ad9357 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -1,31 +1,21 @@
/* global Flash */
import Vue from 'vue';
-// import Cookies from 'js-cookie';
import Translate from '../vue_shared/translate';
import cycleAnalytics from './components/cycle_analitics_app.vue';
-// import limitWarningComponent from './components/limit_warning_component.vue';
-// import stateCodeComponent from './components/stage_code_component.vue';
-// import stageIssueComponent from './components/stage_issue_component.vue';
-// import './components/stage_plan_component';
-// import './components/stage_production_component';
-// import './components/stage_review_component';
-// import './components/stage_staging_component';
-// import './components/stage_test_component';
-// import './components/total_time_component';
-// import './cycle_analytics_service';
Vue.use(Translate);
document.addEventListener('DOMContentLoaded', () => new Vue({
- el: '#cycle-analytics',
+ el: '#js-vue-cycle-analytics',
data() {
- const dataset = document.querySelector(this.$options.$el).dataset;
+ const dataset = document.querySelector(this.$options.el).dataset;
return {
endpoint: dataset.requestPath,
noData: dataset.cycleAnalyticsNoData,
helpPath: dataset.helpPath,
+ cssClass: dataset.class,
};
},
components: {
@@ -37,127 +27,8 @@ document.addEventListener('DOMContentLoaded', () => new Vue({
endpoint: this.endpoint,
noData: this.noData,
helpPath: this.helpPath,
+ cssClass: this.cssClass,
},
});
},
}));
-
-// $(() => {
-// const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
-// const cycleAnalyticsEl = document.querySelector('#cycle-analytics');
- // const cycleAnalyticsStore = gl.cycleAnalytics.CycleAnalyticsStore;
- // const cycleAnalyticsService = new gl.cycleAnalytics.CycleAnalyticsService({
- // requestPath: cycleAnalyticsEl.dataset.requestPath,
- // });
-
- // gl.cycleAnalyticsApp = new Vue({
- // el: '#cycle-analytics',
- // name: 'CycleAnalytics',
- // data: {
- // state: cycleAnalyticsStore.state,
- // isLoading: false,
- // isLoadingStage: false,
- // isEmptyStage: false,
- // hasError: false,
- // startDate: 30,
- // isOverviewDialogDismissed: Cookies.get(OVERVIEW_DIALOG_COOKIE),
- // },
- // computed: {
- // currentStage() {
- // return cycleAnalyticsStore.currentActiveStage();
- // },
- // },
- // components: {
- // stageComponent,
- // stageIssueComponent,
- // 'stage-plan-component': gl.cycleAnalytics.StagePlanComponent,
- // stateCodeComponent,
- // 'stage-test-component': gl.cycleAnalytics.StageTestComponent,
- // 'stage-review-component': gl.cycleAnalytics.StageReviewComponent,
- // 'stage-staging-component': gl.cycleAnalytics.StageStagingComponent,
- // 'stage-production-component': gl.cycleAnalytics.StageProductionComponent,
- // },
- // created() {
- // this.fetchCycleAnalyticsData();
- // },
- // methods: {
- // handleError() {
- // cycleAnalyticsStore.setErrorState(true);
- // return new Flash('There was an error while fetching cycle analytics data.');
- // },
- // initDropdown() {
- // const $dropdown = $('.js-ca-dropdown');
- // const $label = $dropdown.find('.dropdown-label');
-
- // $dropdown.find('li a').off('click').on('click', (e) => {
- // e.preventDefault();
- // const $target = $(e.currentTarget);
- // this.startDate = $target.data('value');
-
- // $label.text($target.text().trim());
- // this.fetchCycleAnalyticsData({ startDate: this.startDate });
- // });
- // },
- // fetchCycleAnalyticsData(options) {
- // const fetchOptions = options || { startDate: this.startDate };
-
- // this.isLoading = true;
-
- // cycleAnalyticsService
- // .fetchCycleAnalyticsData(fetchOptions)
- // .done((response) => {
- // cycleAnalyticsStore.setCycleAnalyticsData(response);
- // this.selectDefaultStage();
- // this.initDropdown();
- // })
- // .error(() => {
- // this.handleError();
- // })
- // .always(() => {
- // this.isLoading = false;
- // });
- // },
- // selectDefaultStage() {
- // const stage = this.state.stages[0];
- // this.selectStage(stage);
- // },
- // selectStage(stage) {
- // if (this.isLoadingStage) return;
- // if (this.currentStage === stage) return;
-
- // if (!stage.isUserAllowed) {
- // cycleAnalyticsStore.setActiveStage(stage);
- // return;
- // }
-
- // this.isLoadingStage = true;
- // cycleAnalyticsStore.setStageEvents([], stage);
- // cycleAnalyticsStore.setActiveStage(stage);
-
- // cycleAnalyticsService
- // .fetchStageData({
- // stage,
- // startDate: this.startDate,
- // })
- // .done((response) => {
- // this.isEmptyStage = !response.events.length;
- // cycleAnalyticsStore.setStageEvents(response.events, stage);
- // })
- // .error(() => {
- // this.isEmptyStage = true;
- // })
- // .always(() => {
- // this.isLoadingStage = false;
- // });
- // },
- // dismissOverviewDialog() {
- // this.isOverviewDialogDismissed = true;
- // Cookies.set(OVERVIEW_DIALOG_COOKIE, '1', { expires: 365 });
- // },
- // },
- // });
-
- // // Register global components
- // Vue.component('limit-warning', limitWarningComponent);
- // Vue.component('total-time', gl.cycleAnalytics.TotalTimeComponent);
-// });
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
index ce5066dc10b..f603fb2fefb 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
@@ -5,8 +5,8 @@ Vue.use(VueResource);
export default class CycleAnalyticsService {
constructor(options) {
- this.requestPath = options.requestPath;
- this.cycleAnalytics = Vue.resource(options.requestPath);
+ this.requestPath = options.endpoint;
+ this.cycleAnalytics = Vue.resource(options.endpoint);
}
fetchCycleAnalyticsData(options = { startDate: 30 }) {
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
index fa4be1a2981..40c489737ca 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
@@ -2,35 +2,36 @@ import { __ } from '../locale';
import '../lib/utils/text_utility';
import DEFAULT_EVENT_OBJECTS from './default_event_objects';
-const EMPTY_STAGE_TEXTS = {
- issue: __('The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.'),
- plan: __('The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.'),
- code: __('The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.'),
- test: __('The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.'),
- review: __('The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.'),
- staging: __('The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.'),
- production: __('The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.'),
-};
-
export default class CycleAnalyticsStore {
constructor() {
this.state = {
- summary: '',
- stats: '',
- analytics: '',
+ summary: [],
+ analytics: {},
events: [],
stages: [],
};
+
+ this.EMPTY_STAGE_TEXTS = {
+ issue: __('The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.'),
+ plan: __('The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.'),
+ code: __('The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.'),
+ test: __('The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.'),
+ review: __('The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.'),
+ staging: __('The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.'),
+ production: __('The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.'),
+ };
}
setCycleAnalyticsData(data) {
+ debugger;
const summary = data.summary.map(item => Object.assign({}, item, { value: item.value || '-' }));
- const stages = data.stages.map((el) => {
+ const stages = data.stats.map((el) => {
const stageSlug = gl.text.dasherize(el.name.toLowerCase());
+
return Object.assign({}, el, {
active: false,
isUserAllowed: data.permissions[stageSlug],
- emptyStageText: EMPTY_STAGE_TEXTS[stageSlug],
+ emptyStageText: this.EMPTY_STAGE_TEXTS[stageSlug],
component: `stage-${stageSlug}-component`,
slug: stageSlug,
});
@@ -52,12 +53,12 @@ export default class CycleAnalyticsStore {
}
deactivateAllStages() {
- this.state.stages = this.state.map(stage => Object.assign({}, stage, { active: false }));
+ this.state.stages = this.state.stages.map(stage => Object.assign({}, stage, { active: false }));
}
setActiveStage(stage) {
this.deactivateAllStages();
- Object.assign(stage, { active: true });
+ this.state.stages.find(state => state.name === stage.name).active = true;
}
setStageEvents(events, stage) {
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index 2a92673d9fa..4af6f1e529a 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -110,10 +110,6 @@
}
}
- .js-ca-dropdown {
- top: $gl-padding-top;
- }
-
.content-list {
li {
padding: 18px $gl-padding $gl-padding;
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index 8a198e3131a..a8ddd44e284 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -1,94 +1,9 @@
- @no_container = true
- page_title "Cycle Analytics"
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('cycle_analytics')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('cycle_analytics')
= render "projects/head"
-#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) cycle_analytics_no_data: @cycle_analytics_no_data}, help_path: help_page_path('user/project/cycle_analytics')}
- - if @cycle_analytics_no_data
- .landing.content-block{ "v-if" => "!isOverviewDialogDismissed" }
- %button.dismiss-button{ type: 'button', 'aria-label': 'Dismiss Cycle Analytics introduction box', "@click" => "dismissOverviewDialog()" }
- = icon("times")
- .svg-container
- = custom_icon('icon_cycle_analytics_splash')
- .inner-content
- %h4
- {{ __('Introducing Cycle Analytics') }}
- %p
- {{ __('Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.') }}
- %p
- = link_to _('Read more'), help_page_path('user/project/cycle_analytics'), target: '_blank', class: 'btn'
- = icon("spinner spin", "v-show" => "isLoading")
- .wrapper{ "v-show" => "!isLoading && !hasError" }
- .panel.panel-default
- .panel-heading
- {{ __('Pipeline Health') }}
- .content-block
- .container-fluid
- .row
- .col-sm-3.col-xs-12.column{ "v-for" => "item in state.summary" }
- %h3.header {{ item.value }}
- %p.text {{ item.title }}
- .col-sm-3.col-xs-12.column
- .dropdown.inline.js-ca-dropdown
- %button.dropdown-menu-toggle{ "data-toggle" => "dropdown", :type => "button" }
- %span.dropdown-label {{ n__('Last %d day', 'Last %d days', 30) }}
- %i.fa.fa-chevron-down
- %ul.dropdown-menu.dropdown-menu-align-right
- %li
- %a{ "href" => "#", "data-value" => "7" }
- {{ n__('Last %d day', 'Last %d days', 7) }}
- %li
- %a{ "href" => "#", "data-value" => "30" }
- {{ n__('Last %d day', 'Last %d days', 30) }}
- %li
- %a{ "href" => "#", "data-value" => "90" }
- {{ n__('Last %d day', 'Last %d days', 90) }}
- .stage-panel-container
- .panel.panel-default.stage-panel
- .panel-heading
- %nav.col-headers
- %ul
- %li.stage-header
- %span.stage-name
- {{ s__('ProjectLifecycle|Stage') }}
- %i.has-tooltip.fa.fa-question-circle{ "data-placement" => "top", title: _("The phase of the development lifecycle."), "aria-hidden" => "true" }
- %li.median-header
- %span.stage-name
- {{ __('Median') }}
- %i.has-tooltip.fa.fa-question-circle{ "data-placement" => "top", title: _("The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."), "aria-hidden" => "true" }
- %li.event-header
- %span.stage-name
- {{ currentStage ? __(currentStage.legend) : __('Related Issues') }}
- %i.has-tooltip.fa.fa-question-circle{ "data-placement" => "top", title: _("The collection of events added to the data gathered for that stage."), "aria-hidden" => "true" }
- %li.total-time-header
- %span.stage-name
- {{ __('Total Time') }}
- %i.has-tooltip.fa.fa-question-circle{ "data-placement" => "top", title: _("The time taken by each data entry gathered by that stage."), "aria-hidden" => "true" }
- .stage-panel-body
- %nav.stage-nav
- %ul
- %li.stage-nav-item{ ':class' => '{ active: stage.active }', '@click' => 'selectStage(stage)', "v-for" => "stage in state.stages" }
- .stage-nav-item-cell.stage-name
- {{ stage.title }}
- .stage-nav-item-cell.stage-median
- %template{ "v-if" => "stage.isUserAllowed" }
- %span{ "v-if" => "stage.value" }
- {{ stage.value }}
- %span.stage-empty{ "v-else" => true }
- {{ __('Not enough data') }}
- %template{ "v-else" => true }
- %span.not-available
- {{ __('Not available') }}
- .section.stage-events
- %template{ "v-if" => "isLoadingStage" }
- = icon("spinner spin")
- %template{ "v-if" => "currentStage && !currentStage.isUserAllowed" }
- = render partial: "no_access"
- %template{ "v-else" => true }
- %template{ "v-if" => "isEmptyStage && !isLoadingStage" }
- = render partial: "empty_stage"
- %template{ "v-if" => "state.events.length && !isLoadingStage && !isEmptyStage" }
- %component{ ":is" => "currentStage.component", ":stage" => "currentStage", ":items" => "state.events" }
+#js-vue-cycle-analytics{ "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project), cycle_analytics_no_data: @cycle_analytics_no_data, help_path: help_page_path('user/project/cycle_analytics'), class: container_class} } \ No newline at end of file