summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFatih Acet <acetfatih@gmail.com>2016-09-19 23:41:36 +0300
committerFatih Acet <acetfatih@gmail.com>2016-09-19 23:41:36 +0300
commite49c6f8666f0e4a575bc9e2955c06eb59e213fd1 (patch)
tree0e72bfd8613087f0baa2ac8814de083e891115c5
parent3f3bdeced1e2e1f35b9f8e37a45f46233ee5cb58 (diff)
downloadgitlab-ce-e49c6f8666f0e4a575bc9e2955c06eb59e213fd1.tar.gz
Finalise cycle analytics frontend.
-rw-r--r--app/assets/javascripts/cycle-analytics.js.es695
-rw-r--r--app/assets/javascripts/dispatcher.js4
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss96
-rw-r--r--app/helpers/cycle_analytics_helper.rb4
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml104
5 files changed, 164 insertions, 139 deletions
diff --git a/app/assets/javascripts/cycle-analytics.js.es6 b/app/assets/javascripts/cycle-analytics.js.es6
index 727ae12b4fc..acae5439fa2 100644
--- a/app/assets/javascripts/cycle-analytics.js.es6
+++ b/app/assets/javascripts/cycle-analytics.js.es6
@@ -1,69 +1,92 @@
((global) => {
+ const COOKIE_NAME = 'cycle_analytics_help_dismissed';
+
gl.CycleAnalytics = class CycleAnalytics {
constructor() {
+ const that = this;
+
+ this.isHelpDismissed = $.cookie(COOKIE_NAME);
this.vue = new Vue({
el: '#cycle-analytics',
name: 'CycleAnalytics',
created: this.fetchData(),
- data: this.getData({ isLoading: true })
+ data: this.decorateData({ isLoading: true }),
+ methods: {
+ dismissLanding() {
+ that.dismissLanding();
+ }
+ }
});
}
- fetchData() {
- $.get('cycle_analytics.json')
- .done((data) => {
- this.vue.$data = this.getData(data);
- this.initDropdown();
- })
- .error((data) => {
- this.handleError(data);
- })
- .always(() => {
- this.vue.isLoading = false;
- })
+ fetchData(options) {
+ options = options || { startDate: 30 };
+
+ $.ajax({
+ url: $('#cycle-analytics').data('request-path'),
+ method: 'GET',
+ dataType: 'json',
+ contentType: 'application/json',
+ data: { start_date: options.startDate }
+ }).done((data) => {
+ this.vue.$data = this.decorateData(data);
+ this.initDropdown();
+ })
+ .error((data) => {
+ this.handleError(data);
+ })
+ .always(() => {
+ this.vue.isLoading = false;
+ })
}
- getData(data) {
- return {
- notAvailable: data.notAvailable || false,
- isLoading: data.isLoading || false,
- analytics: {
- summary: [
- { desc: 'New Issues', value: data.issues || '-' },
- { desc: 'Commits', value: data.commits || '-' },
- { desc: 'Deploys', value: data.deploys || '-' }
- ],
- data: [
- { title: 'Issue', desc: 'Time before an issue get scheduled', value: data.issue || '-' },
- { title: 'Plan', desc: 'Time before an issue starts implementation', value: data.plan || '-' },
- { title: 'Code', desc: 'Time until first merge request', value: data.code || '-' },
- { title: 'Test', desc: 'CI test time of the default branch', value: data.test || '-' },
- { title: 'Review', desc: 'Time between MR creation and merge/close', value: data.review || '-' },
- { title: 'Deploy', desc: 'Time for a new commit to land in one of the environments', value: data.deploy || '-' }
- ]
- }
- }
+ decorateData(data) {
+ data.summary = data.summary || [];
+ data.stats = data.stats || [];
+ data.isHelpDismissed = this.isHelpDismissed;
+ data.isLoading = data.isLoading || false;
+
+ data.summary.forEach((item) => {
+ item.value = item.value || '-';
+ });
+
+ data.stats.forEach((item) => {
+ item.value = item.value || '-';
+ })
+
+ return data;
}
handleError(data) {
- // TODO: Make sure that this is the proper error handling
- new Flash('There was an error while fetching cycyle analytics data.', 'alert');
+ this.vue.$data = {
+ hasError: true,
+ isHelpDismissed: this.isHelpDismissed
+ };
+
+ new Flash('There was an error while fetching cycle analytics data.', 'alert');
+ }
+
+ dismissLanding() {
+ this.vue.isHelpDismissed = true;
+ $.cookie(COOKIE_NAME, true);
}
initDropdown() {
const $dropdown = $('.js-ca-dropdown');
const $label = $dropdown.find('.dropdown-label');
- $dropdown.find('li a').on('click', (e) => {
+ $dropdown.find('li a').off('click').on('click', (e) => {
e.preventDefault();
const $target = $(e.currentTarget);
const value = $target.data('value');
$label.text($target.text().trim());
this.vue.isLoading = true;
+ this.fetchData({ startDate: value });
})
}
+
}
})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 46734761e86..6e620e9976c 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -92,7 +92,7 @@
new MergedButtons();
break;
case "projects:merge_requests:conflicts":
- window.mcui = new MergeConflictResolver()
+ new MergeConflictResolver()
case 'projects:merge_requests:index':
shortcut_handler = new ShortcutsNavigation();
Issuable.init();
@@ -187,7 +187,7 @@
new gl.ProtectedBranchEditList();
break;
case 'projects:cycle_analytics:show':
- window.ca = new gl.CycleAnalytics();
+ new gl.CycleAnalytics();
break;
}
switch (path.first()) {
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index 16a9b0eebfc..4bbd942112a 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -1,5 +1,7 @@
#cycle-analytics {
- margin-top: 24px;
+ margin: 24px auto 0;
+ width: 800px;
+ position: relative;
.panel {
@@ -22,6 +24,10 @@
.text {
color: $layout-link-gray;
}
+
+ &:last-child {
+ text-align: right;
+ }
}
.dropdown {
@@ -39,9 +45,13 @@
.content-list {
li {
padding: 18px $gl-padding $gl-padding;
+
+ .container-fluid {
+ padding: 0;
+ }
}
- .col-md-10 {
+ .title-col {
span {
&:first-child {
line-height: 19px;
@@ -54,62 +64,54 @@
}
}
- .col-md-2 span {
- line-height: 42px;
- }
- }
-
- .inner-content {
- width: 450px;
- text-align: center;
- margin: 0 auto;
- padding: 62px 0;
+ .value-col {
+ text-align: right;
- .btn-block {
- max-width: 130px;
- margin: 0 auto;
+ span {
+ line-height: 42px;
+ }
}
+ }
- h4 {
- color: $gl-text-color;
- font-size: 17px;
- }
+ .landing {
+ margin-bottom: $gl-padding;
+ overflow: hidden;
- p {
- color: #8C8C8C;
- margin-bottom: $gl-padding;
+ .dismiss-icon {
+ position: absolute;
+ right: $gl-padding;
+ cursor: pointer;
}
- }
- &.waiting {
- .panel .header {
- width: 35px;
- height: 35px;
- margin-bottom: 3px;
+ svg {
+ margin: 0 20px;
+ float: left;
+ width: 136px;
+ height: 136px;
}
- span {
- background-color: #F8F8F8;
- color: #F8F8F8 !important;
- display: inline-block;
- line-height: 13px !important;
- }
+ .inner-content {
+ width: 480px;
+ float: left;
- .dropdown {
- opacity: .33;
- }
+ h4 {
+ color: $gl-text-color;
+ font-size: 17px;
+ }
- .col-md-2 span {
- position: relative;
- top: 11px;
+ p {
+ color: #8C8C8C;
+ margin-bottom: $gl-padding;
+ }
}
+ }
- .fa-spinner {
- font-size: 32px;
- position: absolute;
- left: 50%;
- top: 50%;
- margin: -16px 0 0 -16px;
- }
+ .fa-spinner {
+ font-size: 28px;
+ position: relative;
+ margin-left: -20px;
+ left: 50%;
+ margin-top: 36px;
}
+
}
diff --git a/app/helpers/cycle_analytics_helper.rb b/app/helpers/cycle_analytics_helper.rb
index c0d1eb56fd3..5caf7254c5c 100644
--- a/app/helpers/cycle_analytics_helper.rb
+++ b/app/helpers/cycle_analytics_helper.rb
@@ -6,8 +6,8 @@ module CycleAnalyticsHelper
[:plan, "Plan", "Time before an issue starts implementation"],
[:code, "Code", "Time until first merge request"],
[:test, "Test", "Total test time for all commits/merges"],
- [:review, "Review", "Time between MR creation and merge/close"],
- [:staging, "Staging", "From MR merge until deploy to production"],
+ [:review, "Review", "Time between merge request creation and merge/close"],
+ [:staging, "Staging", "From merge request merge until deploy to production"],
[:production, "Production", "From issue creation until deploy to production"]]
stats = cycle_analytics_view_data.reduce([]) do |stats, (stage_method, stage_text, stage_description)|
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index 33c403118c1..b9439014fb5 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -1,58 +1,58 @@
= render 'projects/pipelines/head'
-#cycle-analytics{"v-cloak" => "true", ":class" => "{ 'waiting': isLoading }"}
- .panel.panel-default
- .panel-heading
- Pipeline Health
+#cycle-analytics{"v-cloak" => "true", data: { request_path: "#{project_cycle_analytics_path(@project)}"}}
- .content-block
- = icon("spinner spin", "v-if" => "isLoading")
+ .bordered-box.landing.content-block{"v-if" => "!isHelpDismissed"}
+ = icon('times', class: 'dismiss-icon', "@click": "dismissLanding()")
+ = custom_icon('icon_cycle_analytics_splash')
+ .inner-content
+ %h4
+ Introducing Cycle Analytics
+ %p
+ Cycle Analytics gives an overview on how much time it takes to go from idea to production in your project.
- .container-fluid
- .row
- %template{"v-for" => "info in analytics.summary"}
- .col-xs-3.column
- %span.header {{info.value}}
- %br
- %span.text {{info.desc}}
-
- .col-xs-3.column
- .dropdown.inline.js-ca-dropdown
- %button.dropdown-menu-toggle{"aria-expanded" => "false", "data-toggle" => "dropdown", :type => "button"}
- %span.dropdown-label Last 30 days
- %i.fa.fa-chevron-down
- %ul.dropdown-menu.dropdown-menu-align-right
- %li
- %a{'href' => "#", 'data-value' => '30days'}
- Last 30 days
- %li
- %a{'href' => "#", 'data-value' => '90days'}
- Last 90 days
-
- .bordered-box
- = icon("spinner spin", "v-if" => "isLoading")
-
- %ul.content-list{{"v-if" => "!notAvailable"}}
- %li{"v-for" => "info in analytics.data"}
+ = button_tag 'Read more', 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-xs-10
- %span
- {{info.title}}
- %br
- %span
- {{info.desc}}
- .col-xs-2
- %span
- {{info.value}}
-
-
- .content-block{{"v-if" => "notAvailable"}}
- .inner-content
- = custom_icon('icon_cycle_analytics_splash')
- %h4
- Set up your deploys to environment!
- %p
- Cycle Analytics will give an overview on how much time it takes to go from an idea to production in your project.
-
- = button_tag 'Set up', class: 'btn btn-create btn-block'
+ %template{"v-for" => "item in summary"}
+ .col-xs-3.column
+ %span.header {{item.value}}
+ %br
+ %span.text {{item.title}}
+
+ .col-xs-3.column
+ .dropdown.inline.js-ca-dropdown
+ %button.dropdown-menu-toggle{"data-toggle" => "dropdown", :type => "button"}
+ %span.dropdown-label Last 30 days
+ %i.fa.fa-chevron-down
+ %ul.dropdown-menu.dropdown-menu-align-right
+ %li
+ %a{'href' => "#", 'data-value' => '30'}
+ Last 30 days
+ %li
+ %a{'href' => "#", 'data-value' => '90'}
+ Last 90 days
+
+ .bordered-box
+ %ul.content-list
+ %li{"v-for" => "item in stats"}
+ .container-fluid
+ .row
+ .col-xs-10.title-col
+ %span
+ {{item.title}}
+ %br
+ %span
+ {{item.description}}
+ .col-xs-2.value-col
+ %span
+ {{item.value}}