Merge pull request #3 from artzhuchka/master

new build timeline style, some layout fixes
This commit is contained in:
Oleg Korobenko 2015-12-16 23:56:58 +03:00
commit 6a863740c4
9 changed files with 471 additions and 257 deletions

View File

@ -6,20 +6,48 @@
} }
} }
@animation-duration: 1.5s;
.builds { .builds {
padding: 0 15px; &_item {
&:hover {
.builds {
&_buttons {
opacity: 1;
}
}
}
} }
.build { &_inner {
.row();
padding: 15px 0;
margin-bottom: 3px;
background: @well-bg; background: @well-bg;
padding: 15px;
}
&_header {
.make-xs-column(9);
}
&_controls {
.make-xs-column(3);
.text-right;
}
&_buttons {
transition: opacity 0.2s ease;
opacity: 0;
}
&_progress {
.progress {
height: 18px;
margin-bottom: 0;
}
}
&_status { &_status {
float: left; float: left;
padding-top: 14px; margin-right: 8px;
margin-right: 13px;
} }
&_info { &_info {
@ -27,76 +55,45 @@
margin-right: 10px; margin-right: 10px;
} }
&_controls { &__timeline {
.make-md-column(3); position: relative;
.make-xs-column(3);
.text-right;
&_buttons { .builds {
margin-top: 8px; &_inner {
transition: opacity 0.2s ease; border-left: 6px solid darken(@well-bg, 10%);
opacity: 0;
}
&_progress {
padding-top: 14px;
.progress {
height: 18px;
margin-bottom: 0;
}
}
}
&_content {
.make-md-column(9);
.make-xs-column(9);
} }
&_header { &_header {
margin-bottom: 6px; margin-bottom: 6px;
font-size: 18px; font-size: 18px;
a {
font-size: inherit;
}
} }
&:hover {
.build_controls_buttons {
opacity: 1;
}
}
&__small {
.build_status {
padding-top: 5px;
}
.build_header {
font-size: 14px;
}
.build_content {
.make-xs-column(7);
}
.build_controls {
.make-xs-column(5);
font-size: 12px;
&_progress { &_progress {
padding-top: 4px; padding: 3px 0;
.progress {
height: 12px;
}
}
}
}
} }
@animation-duration: 1.5s; &_item {
margin: 4px 0;
position: relative;
.status { &:after {
width: 25px; content: '';
height: 25px; position: absolute;
border-radius: 50%; border-radius: 50%;
background: @well-bg;
left: 0;
z-index: 1;
}
&:before {
content: '';
position: absolute;
border: 11px solid transparent;
top: 25px;
}
&__in-progress { &__in-progress {
&:after {
background: @brand-info; background: @brand-info;
-webkit-animation: pulsate @animation-duration ease-out; -webkit-animation: pulsate @animation-duration ease-out;
@ -108,45 +105,251 @@
animation: pulsate @animation-duration ease-out; animation: pulsate @animation-duration ease-out;
animation-iteration-count: infinite; animation-iteration-count: infinite;
} }
&__done {
background: @link-color;
}
&__error {
background: @brand-danger;
}
&__queued {
background: @brand-primary;
} }
&__small { &__done {
width: 10px; &:after {
height: 10px; background: @link-color;
}
}
&__error {
&:after {
background: @brand-danger;
}
}
&__queued {
&:after {
background: @brand-primary;
}
}
}
}
&:after {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 2px;
margin-left: -1px;
background: darken(@well-bg, 10%);
}
&-large {
.builds {
&_item {
padding-left: 40px;
&:after {
width: 24px;
height: 24px;
top: 25px;
}
&:before {
left: 20px;
border-right-color: darken(@well-bg, 10%);
top: 25px;
}
}
}
&:after {
left: 12px;
}
}
&-small {
.builds {
&_item {
padding-left: 30px;
&:after {
top: 16px;
height: 16px;
width: 16px;
}
&:before {
left: 10px;
border-right-color: darken(@well-bg, 10%);
top: 13px;
}
}
&_header {
font-size: 14px;
.make-xs-column(7);
margin-bottom: 0;
}
&_controls {
.make-xs-column(5);
font-size: 12px;
}
&_progress {
padding: 1px 0;
}
}
&:after {
left: 8px;
}
}
}
}
@media (min-width: @screen-sm-min) {
.builds {
&__timeline {
&-large {
.builds {
&_item {
padding-left: 0;
display: inline-block;
vertical-align: top;
width: 50%;
margin: 10px 0;
&:after {
left: auto;
}
&:before {
left: auto;
border-right-color: transparent;
}
}
&_inner {
border-left: 0;
}
}
&:after {
left: 50%;
}
}
&-left {
.builds {
&_item {
&:nth-child(odd) {
padding-right: 30px;
.builds {
&_inner {
border-right: 6px solid darken(@well-bg, 10%);
}
}
&:after {
right: -12px;
}
&:before {
right: 10px;
border-left-color: darken(@well-bg, 10%);
}
}
&:nth-child(even) {
padding-left: 30px;
top: 50px;
.builds {
&_inner {
border-left: 6px solid darken(@well-bg, 10%);
}
}
&:after {
left: -12px;
}
&:before {
left: 10px;
border-right-color: darken(@well-bg, 10%);
}
}
}
}
}
&-right {
.builds {
&_item {
&:first-child {
margin-left: 50%;
}
&:nth-child(even) {
padding-right: 30px;
top: -50px;
.builds {
&_inner {
border-right: 6px solid darken(@well-bg, 10%);
}
}
&:after {
right: -12px;
}
&:before {
right: 10px;
border-left-color: darken(@well-bg, 10%);
}
}
&:nth-child(odd) {
padding-left: 30px;
.builds {
&_inner {
border-left: 6px solid darken(@well-bg, 10%);
}
}
&:after {
left: -12px;
}
&:before {
left: 10px;
border-right-color: darken(@well-bg, 10%);
}
}
}
}
}
}
} }
} }
.pulsate-frames() { .pulsate-frames() {
.transform(@scaleX, @scaleY) { .transform(@scaleX, @scaleY) {
-webkit-transform: scale(@scaleX, @scaleY); opacity: 1; -webkit-transform: scale3d(@scaleX, @scaleY, 1);
-moz-transform: scale(@scaleX, @scaleY); opacity: 1; -moz-transform: scale3d(@scaleX, @scaleY, 1);
-ms-transform: scale(@scaleX, @scaleY); opacity: 1; -ms-transform: scale3d(@scaleX, @scaleY, 1);
-o-transform: scale(@scaleX, @scaleY); opacity: 1; -o-transform: scale3d(@scaleX, @scaleY, 1);
transform: scale(@scaleX, @scaleY); opacity: 1; transform: scale3d(@scaleX, @scaleY, 1);
} }
0% { 0% {
.transform(1.0, 1.0); .transform(1, 1);
}
25% {
opacity: 1.0;
} }
50% { 50% {
.transform(0.75, 0.75); .transform(0.75, 0.75);
} }
50% {
opacity: 1.0;
}
100% { 100% {
.transform(1.0, 1.0); .transform(1, 1);
} }
} }

View File

@ -1,7 +1,18 @@
body { body {
font-family: 'Open Sans', sans-serif; font-family: 'Open Sans', sans-serif;
padding-top: @navbar-height;
min-width: 320px;
} }
.page-wrapper { .page-wrapper {
margin-top: @navbar-height + 15px; padding: 20px 0;
}
.page-header {
margin-top: 0;
border-bottom-color: #d7d7d7;
.small {
font-size: @font-size-base;
}
} }

View File

@ -1,17 +1,18 @@
.builds(style={paddingTop: '20px'}) .builds.builds__timeline.builds__timeline-small
each item in this.state.items each item in this.state.items
.build.build__small(key=item.id) .builds_item(key=item.id, class="builds_item__#{item.status}")
.build_content .builds_inner
.build_status .row
.status.status__small(class="status__#{item.status}") .builds_header
.build_header
Link(to="build", params={id: item.id}) Link(to="build", params={id: item.id})
span build # span build #
span= item.number span= item.number
.build_controls
.builds_controls
if item.status === 'in-progress' if item.status === 'in-progress'
.build_controls_progress .builds_progress
if item.project.avgBuildDuration if item.project.avgBuildDuration
Progress(build=item) Progress(build=item)
if item.endDate
if !item.endDate
DateTime(value=item.endDate) DateTime(value=item.endDate)

View File

@ -13,11 +13,10 @@ mixin statusText(build)
- var build = this.props.build; - var build = this.props.build;
.build(class="") .builds_item(class="builds_item__#{build.status}")
.build_content .builds_inner
.build_status .row
.status(class="status__#{build.status}") .builds_header
div.build_header
if build.project if build.project
span span
Scm(scm=build.project.scm.type) Scm(scm=build.project.scm.type)
@ -46,9 +45,29 @@ mixin statusText(build)
span= build.currentStep span= build.currentStep
span ) span )
div .builds_controls
if build.completed
.builds_buttons
a.btn.btn-sm.btn-default(href="javascript:void(0);", onClick=this.onRebuildProject(build.project.name))
i.fa.fa-fw.fa-repeat(title="Rebuild")
|
| Build again
if build.status === 'in-progress'
.builds_progress
if build.project.avgBuildDuration
Progress(build=build)
if build.status === 'queued'
.builds_buttons
a.btn.btn-sm.btn-default(href="javascript:void(0);", onClick=this.onCancelBuild(build.id))
i.fa.fa-fw.fa-times(title="Cancel build")
|
| Cancel build
.builds_content
if build.endDate if build.endDate
span.build_info span.builds_info
i.fa.fa-fw.fa-clock-o i.fa.fa-fw.fa-clock-o
| finished | finished
DateTime(value=build.endDate) DateTime(value=build.endDate)
@ -56,38 +75,18 @@ mixin statusText(build)
Duration(value=(build.endDate - build.startDate), withSuffix=true) Duration(value=(build.endDate - build.startDate), withSuffix=true)
else else
if build.startDate if build.startDate
span.build_info span.builds_info
i.fa.fa-fw.fa-clock-o i.fa.fa-fw.fa-clock-o
| started | started
DateTime(value=build.startDate) DateTime(value=build.startDate)
else else
span.build_info span.builds_info
i.fa.fa-fw.fa-clock-o i.fa.fa-fw.fa-clock-o
| queued | queued
DateTime(value=build.createDate) DateTime(value=build.createDate)
| |
if build.scm if build.scm
span.build_info span.builds_info
i.fa.fa-fw.fa-comment-o i.fa.fa-fw.fa-comment-o
| |
span= utils.prune(build.scm.rev.comment, 40) span= utils.prune(build.scm.rev.comment, 40)
|
.build_controls
if build.completed
.build_controls_buttons
a.btn.btn-sm.btn-default(href="javascript:void(0);", onClick=this.onRebuildProject(build.project.name))
i.fa.fa-fw.fa-repeat(title="Rebuild")
|
| Build again
if build.status === 'in-progress'
.build_controls_progress
if build.project.avgBuildDuration
Progress(build=build)
if build.status === 'queued'
.build_controls_buttons
a.btn.btn-sm.btn-default(href="javascript:void(0);", onClick=this.onCancelBuild(build.id))
i.fa.fa-fw.fa-times(title="Cancel build")
|
| Cancel build

View File

@ -1,6 +1,8 @@
.builds - var itemsCount = this.state.items.length;
if !this.state.items.length
p Build history is empty if itemsCount
- console.log('>>>> builds = ', JSON.stringify(this.state.items).length, this.state.items) .builds.builds__timeline.builds__timeline-large(class="builds__timeline-#{itemsCount % 2 ? 'left' : 'right'}")
each build, index in this.state.items each build, index in this.state.items
Item(build=build, key=build.id) Item(build=build, key=build.id)
else
p Build history is empty

View File

@ -15,14 +15,15 @@ mixin statusBadge(build)
if this.state.build if this.state.build
.col-sm-3.hidden-xs .col-sm-3.hidden-xs
BuildSidebar(projectName=this.state.build.project.name) BuildSidebar(projectName=this.state.build.project.name)
.col-sm-9 .col-sm-9
h1 h1.page-header
.pull-right(style={fontSize: '22px'}) .pull-right(style={fontSize: '22px'})
mixin statusBadge(this.state.build) mixin statusBadge(this.state.build)
span Build # span Build #
span= this.state.build.number span= this.state.build.number
.text-muted(style={marginTop: '-10px'}) .small.text-muted
| Initiated by | Initiated by
- var initiator = this.state.build.initiator; - var initiator = this.state.build.initiator;
if initiator.type === 'build' if initiator.type === 'build'
@ -37,7 +38,7 @@ mixin statusBadge(build)
else else
span= initiator.type span= initiator.type
hr //- hr
.build-view_info .build-view_info
if this.state.build.error if this.state.build.error

View File

@ -1,2 +1,2 @@
.progress .progress
.progress-bar.progress-bar-success(style={width: this.state.percent + '%'}) .progress-bar.progress-bar-success.progress-bar-striped.active(style={width: this.state.percent + '%'})

View File

@ -1,5 +1,4 @@
.main-row div
.row h1.page-header Builds history
.col-md-8.col-sm-12
h2 Builds history
BuildsList() BuildsList()

View File

@ -1,6 +1,5 @@
.row div
.col-md-8 h1.page-header.clearfix
h1.clearfix
.pull-right .pull-right
button.btn.btn-sm.btn-primary.dropdown-toggle( button.btn.btn-sm.btn-primary.dropdown-toggle(
data-toggle="dropdown", data-toggle="dropdown",
@ -19,8 +18,6 @@
Scm(scm=this.state.project.scm ? this.state.project.scm.type : '') Scm(scm=this.state.project.scm ? this.state.project.scm.type : '')
span= this.state.project.name span= this.state.project.name
hr
div.text-muted div.text-muted
- var lastDoneBuild = this.state.project.lastDoneBuild; - var lastDoneBuild = this.state.project.lastDoneBuild;
p Last successfully built: p Last successfully built:
@ -55,4 +52,5 @@
i.fa.fa-fw.fa-history i.fa.fa-fw.fa-history
span span
span Build history span Build history
Builds(projectName=this.props.params.name) Builds(projectName=this.props.params.name)