Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
e49c6f86
Commit
e49c6f86
authored
Sep 19, 2016
by
Fatih Acet
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Finalise cycle analytics frontend.
parent
3f3bdece
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
164 additions
and
139 deletions
+164
-139
app/assets/javascripts/cycle-analytics.js.es6
app/assets/javascripts/cycle-analytics.js.es6
+59
-36
app/assets/javascripts/dispatcher.js
app/assets/javascripts/dispatcher.js
+2
-2
app/assets/stylesheets/pages/cycle_analytics.scss
app/assets/stylesheets/pages/cycle_analytics.scss
+49
-47
app/helpers/cycle_analytics_helper.rb
app/helpers/cycle_analytics_helper.rb
+2
-2
app/views/projects/cycle_analytics/show.html.haml
app/views/projects/cycle_analytics/show.html.haml
+52
-52
No files found.
app/assets/javascripts/cycle-analytics.js.es6
View file @
e49c6f86
((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);
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) => {
...
...
@@ -24,46 +41,52 @@
})
}
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').o
ff('click').o
n('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 = {}));
app/assets/javascripts/dispatcher.js
View file @
e49c6f86
...
...
@@ -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
())
{
...
...
app/assets/stylesheets/pages/cycle_analytics.scss
View file @
e49c6f86
#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,22 +64,36 @@
}
}
.col-md-2
span
{
.value-col
{
text-align
:
right
;
span
{
line-height
:
42px
;
}
}
}
.inner-content
{
width
:
450px
;
text-align
:
center
;
margin
:
0
auto
;
padding
:
62px
0
;
.landing
{
margin-bottom
:
$gl-padding
;
overflow
:
hidden
;
.btn-block
{
max-width
:
130px
;
margin
:
0
auto
;
.dismiss-icon
{
position
:
absolute
;
right
:
$gl-padding
;
cursor
:
pointer
;
}
svg
{
margin
:
0
20px
;
float
:
left
;
width
:
136px
;
height
:
136px
;
}
.inner-content
{
width
:
480px
;
float
:
left
;
h4
{
color
:
$gl-text-color
;
font-size
:
17px
;
...
...
@@ -80,36 +104,14 @@
margin-bottom
:
$gl-padding
;
}
}
&
.waiting
{
.panel
.header
{
width
:
35px
;
height
:
35px
;
margin-bottom
:
3px
;
}
span
{
background-color
:
#F8F8F8
;
color
:
#F8F8F8
!
important
;
display
:
inline-block
;
line-height
:
13px
!
important
;
}
.dropdown
{
opacity
:
.33
;
}
.col-md-2
span
{
position
:
relative
;
top
:
11px
;
}
.fa-spinner
{
font-size
:
32px
;
position
:
absolute
;
font-size
:
28px
;
position
:
relative
;
margin-left
:
-20px
;
left
:
50%
;
top
:
50%
;
margin
:
-16px
0
0
-16px
;
}
margin-top
:
36px
;
}
}
app/helpers/cycle_analytics_helper.rb
View file @
e49c6f86
...
...
@@ -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
)
|
...
...
app/views/projects/cycle_analytics/show.html.haml
View file @
e49c6f86
=
render
'projects/pipelines/head'
#cycle-analytics
{
"v-cloak"
=>
"true"
,
":class"
=>
"{ 'waiting': isLoading }"
}
#cycle-analytics
{
"v-cloak"
=>
"true"
,
data:
{
request_path:
"#{project_cycle_analytics_path(@project)}"
}}
.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.
=
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
=
icon
(
"spinner spin"
,
"v-if"
=>
"isLoading"
)
.container-fluid
.row
%template
{
"v-for"
=>
"info in analytics.
summary"
}
%template
{
"v-for"
=>
"item in
summary"
}
.col-xs-3.column
%span
.header
{{info
.value}}
%span
.header
{{item
.value}}
%br
%span
.text
{{info.desc
}}
%span
.text
{{item.title
}}
.col-xs-3.column
.dropdown.inline.js-ca-dropdown
%button
.dropdown-menu-toggle
{
"aria-expanded"
=>
"false"
,
"data-toggle"
=>
"dropdown"
,
:type
=>
"button"
}
%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'
=>
'30days
'
}
%a
{
'href'
=>
"#"
,
'data-value'
=>
'30
'
}
Last 30 days
%li
%a
{
'href'
=>
"#"
,
'data-value'
=>
'90days
'
}
%a
{
'href'
=>
"#"
,
'data-value'
=>
'90
'
}
Last 90 days
.bordered-box
=
icon
(
"spinner spin"
,
"v-if"
=>
"isLoading"
)
%ul
.content-list
{{
"v-if"
=>
"!notAvailable"
}}
%li
{
"v-for"
=>
"info in analytics.data"
}
%ul
.content-list
%li
{
"v-for"
=>
"item in stats"
}
.container-fluid
.row
.col-xs-10
.col-xs-10.title-col
%span
{{info
.title}}
{{item
.title}}
%br
%span
{{info.desc
}}
.col-xs-2
{{item.description
}}
.col-xs-2.value-col
%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'
{{item.value}}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment