diff --git a/static/css/sources/components/builds.less b/static/css/sources/components/builds.less index 8c2e49b..411dfcc 100644 --- a/static/css/sources/components/builds.less +++ b/static/css/sources/components/builds.less @@ -7,19 +7,46 @@ } .builds { - padding: 0 15px; -} + &_item { + &:hover { + .builds { + &_buttons { + opacity: 1; + } + } + } + } -.build { - .row(); - padding: 15px 0; - margin-bottom: 3px; - background: @well-bg; + &_inner { + background: @well-bg; + padding: 15px; + } + + &_header { + .make-xs-column(9); + } + + &_controls { + .make-xs-column(3); + .text-right; + } + + &_buttons { + // margin-top: 8px; + transition: opacity 0.2s ease; + opacity: 0; + } + + &_progress { + .progress { + height: 18px; + margin-bottom: 0; + } + } &_status { float: left; - padding-top: 14px; - margin-right: 13px; + margin-right: 8px; } &_info { @@ -27,65 +54,221 @@ margin-right: 10px; } - &_controls { - .make-md-column(3); - .make-xs-column(3); - .text-right; + &__timeline { + position: relative; - &_buttons { - margin-top: 8px; - transition: opacity 0.2s ease; - opacity: 0; - } + .builds { + &_item { + display: inline-block; + vertical-align: top; + position: relative; + width: 50%; + margin: 10px 0; - &_progress { - padding-top: 14px; - .progress { - height: 18px; - margin-bottom: 0; + &:after { + content: ''; + position: absolute; + width: 24px; + height: 24px; + border-radius: 50%; + background: @well-bg; + top: 25px; + z-index: 1; + } + + &:before { + content: ''; + position: absolute; + border: 11px solid transparent; + top: 25px; + } + + &__in-progress { + &:after { + background: @brand-info; + } + } + + &__done { + &:after { + background: @link-color; + } + } + + &__error { + &:after { + background: @brand-danger; + } + } + + &__queued { + &:after { + background: @brand-primary; + } + } + } + + &_header { + margin-bottom: 6px; + font-size: 18px; + } + + &_progress { + padding: 3px 0; } } - } - &_content { - .make-md-column(9); - .make-xs-column(9); - } + &:after { + content: ''; + position: absolute; + top: 0; + left: 50%; + bottom: 0; + width: 2px; + margin-left: -1px; + background: darken(@well-bg, 10%); + } - &_header { - margin-bottom: 6px; - font-size: 18px; - a { - font-size: inherit; - } - } + &-left { + .builds { + &_item { + &:nth-child(odd) { + padding-right: 30px; - &:hover { - .build_controls_buttons { - opacity: 1; - } - } + .builds { + &_inner { + border-right: 6px solid darken(@well-bg, 10%); + } + } - &__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 { - padding-top: 4px; - .progress { - height: 12px; + &: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%); + } + } + } + } + } + + &-small { + .builds { + &_item { + display: block; + width: auto; + padding-left: 30px; + margin-bottom: 4px; + + &:after { + left: 0; + top: 16px; + height: 16px; + width: 16px; + } + + &:before { + left: 10px; + border-right-color: darken(@well-bg, 10%); + top: 14px; + } + } + + &_inner { + border-left: 6px solid darken(@well-bg, 10%); + } + + &_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: 9px; + } + } } } @@ -108,12 +291,15 @@ animation: pulsate @animation-duration ease-out; animation-iteration-count: infinite; } + &__done { background: @link-color; } + &__error { background: @brand-danger; } + &__queued { background: @brand-primary; } diff --git a/static/css/sources/components/layout.less b/static/css/sources/components/layout.less index b1f864c..9337f27 100644 --- a/static/css/sources/components/layout.less +++ b/static/css/sources/components/layout.less @@ -1,7 +1,18 @@ body { font-family: 'Open Sans', sans-serif; + padding-top: @navbar-height; + min-width: 320px; } .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; + } +} \ No newline at end of file diff --git a/static/js/app/components/buildSidebar/index.jade b/static/js/app/components/buildSidebar/index.jade index 86eeca3..475d85b 100644 --- a/static/js/app/components/buildSidebar/index.jade +++ b/static/js/app/components/buildSidebar/index.jade @@ -1,17 +1,22 @@ -.builds(style={paddingTop: '20px'}) +.builds.builds__timeline.builds__timeline-small each item in this.state.items - .build.build__small(key=item.id) - .build_content - .build_status - .status.status__small(class="status__#{item.status}") - .build_header - Link(to="build", params={id: item.id}) - span build # - span= item.number - .build_controls - if item.status === 'in-progress' - .build_controls_progress - if item.project.avgBuildDuration - Progress(build=item) - if item.endDate - DateTime(value=item.endDate) + .builds_item(key=item.id, class="builds_item__#{item.status}") + .builds_inner + .row + //- .builds_content + //- .builds_status + //- .status.status__small(class="status__#{item.status}") + + .builds_header + Link(to="build", params={id: item.id}) + span build # + span= item.number + + .builds_controls + if item.status === 'in-progress' + .builds_progress + if item.project.avgBuildDuration + Progress(build=item) + + if !item.endDate + DateTime(value=item.endDate) diff --git a/static/js/app/components/builds/item.jade b/static/js/app/components/builds/item.jade index 2478d1c..91a2471 100644 --- a/static/js/app/components/builds/item.jade +++ b/static/js/app/components/builds/item.jade @@ -13,42 +13,64 @@ mixin statusText(build) - var build = this.props.build; -.build(class="") - .build_content - .build_status - .status(class="status__#{build.status}") - div.build_header - if build.project - span - Scm(scm=build.project.scm.type) +.builds_item(class="builds_item__#{build.status}") + .builds_inner + .row + .builds_header + //- .builds_status + //- .status(class="status__#{build.status}") + + if build.project + span + Scm(scm=build.project.scm.type) + | + Link(to="project", params={name: build.project.name}) + span= build.project.name | - Link(to="project", params={name: build.project.name}) - span= build.project.name - | - if build.number - span(style={fontSize: '15px', color: '#a6a6a6'}) build - | - if build.status !== 'queued' - Link(to="build", params={id: build.id}) - span # - span= build.number - else - span # - span= build.number + if build.number + span(style={fontSize: '15px', color: '#a6a6a6'}) build + | + if build.status !== 'queued' + Link(to="build", params={id: build.id}) + span # + span= build.number + else + span # + span= build.number - if build.waitReason - span ( - span= build.waitReason - span , waiting) + if build.waitReason + span ( + span= build.waitReason + span , waiting) - if build.status === 'in-progress' && build.currentStep - span ( - span= build.currentStep - span ) + if build.status === 'in-progress' && build.currentStep + span ( + span= build.currentStep + 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 - span.build_info + span.builds_info i.fa.fa-fw.fa-clock-o | finished DateTime(value=build.endDate) @@ -56,38 +78,18 @@ mixin statusText(build) Duration(value=(build.endDate - build.startDate), withSuffix=true) else if build.startDate - span.build_info + span.builds_info i.fa.fa-fw.fa-clock-o | started DateTime(value=build.startDate) else - span.build_info + span.builds_info i.fa.fa-fw.fa-clock-o | queued DateTime(value=build.createDate) | if build.scm - span.build_info + span.builds_info i.fa.fa-fw.fa-comment-o | - 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 - + span= utils.prune(build.scm.rev.comment, 40) \ No newline at end of file diff --git a/static/js/app/components/builds/list.jade b/static/js/app/components/builds/list.jade index e15b6e6..b0a30cd 100644 --- a/static/js/app/components/builds/list.jade +++ b/static/js/app/components/builds/list.jade @@ -1,6 +1,8 @@ -.builds - if !this.state.items.length +- var itemsCount = this.state.items.length; + +.builds.builds__timeline(class="builds__timeline-#{itemsCount % 2 ? 'left' : 'right'}") + if itemsCount + each build, index in this.state.items + Item(build=build, key=build.id) + else p Build history is empty - - console.log('>>>> builds = ', JSON.stringify(this.state.items).length, this.state.items) - each build, index in this.state.items - Item(build=build, key=build.id) diff --git a/static/js/app/components/builds/view.jade b/static/js/app/components/builds/view.jade index ba4e0fa..13a4eb0 100644 --- a/static/js/app/components/builds/view.jade +++ b/static/js/app/components/builds/view.jade @@ -15,29 +15,30 @@ mixin statusBadge(build) if this.state.build .col-sm-3.hidden-xs BuildSidebar(projectName=this.state.build.project.name) + .col-sm-9 - h1 + h1.page-header .pull-right(style={fontSize: '22px'}) mixin statusBadge(this.state.build) span Build # span= this.state.build.number - .text-muted(style={marginTop: '-10px'}) - | Initiated by - - var initiator = this.state.build.initiator; - if initiator.type === 'build' - Link(to="project", params={name: initiator.project.name}) - span= initiator.project.name - | - | during the - | - Link(to="build", params={id: initiator.id}) - span build # - span= initiator.number - else - span= initiator.type + .small.text-muted + | Initiated by + - var initiator = this.state.build.initiator; + if initiator.type === 'build' + Link(to="project", params={name: initiator.project.name}) + span= initiator.project.name + | + | during the + | + Link(to="build", params={id: initiator.id}) + span build # + span= initiator.number + else + span= initiator.type - hr + //- hr .build-view_info if this.state.build.error diff --git a/static/js/app/components/common/progress/index.jade b/static/js/app/components/common/progress/index.jade index ef90fb2..cb7e1dc 100644 --- a/static/js/app/components/common/progress/index.jade +++ b/static/js/app/components/common/progress/index.jade @@ -1,2 +1,2 @@ .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 + '%'}) diff --git a/static/js/app/components/dashboard/index.jade b/static/js/app/components/dashboard/index.jade index f680a21..cfefea7 100644 --- a/static/js/app/components/dashboard/index.jade +++ b/static/js/app/components/dashboard/index.jade @@ -1,5 +1,4 @@ -.main-row - .row - .col-md-8.col-sm-12 - h2 Builds history - BuildsList() +div + h1.page-header Builds history + + BuildsList() diff --git a/static/js/app/components/projects/view/index.jade b/static/js/app/components/projects/view/index.jade index 8e0c063..ab5d1ee 100644 --- a/static/js/app/components/projects/view/index.jade +++ b/static/js/app/components/projects/view/index.jade @@ -1,58 +1,56 @@ -.row - .col-md-8 - h1.clearfix - .pull-right - button.btn.btn-sm.btn-primary.dropdown-toggle( - data-toggle="dropdown", - aria-expanded="false", - disabled="true" - ) - | target revision: - span= this.state.project.scm ? this.state.project.scm.rev : '' - | - if this.state.project.name - button.btn.btn-sm.btn-success(onClick=this.onBuildProject) - i.fa.fa-fw.fa-play - | - span Build - div - Scm(scm=this.state.project.scm ? this.state.project.scm.type : '') - span= this.state.project.name - - hr - - div.text-muted - - var lastDoneBuild = this.state.project.lastDoneBuild; - p Last successfully built: - if lastDoneBuild - DateTime(value=lastDoneBuild.endDate) +div + h1.page-header.clearfix + .pull-right + button.btn.btn-sm.btn-primary.dropdown-toggle( + data-toggle="dropdown", + aria-expanded="false", + disabled="true" + ) + | target revision: + span= this.state.project.scm ? this.state.project.scm.rev : '' + | + if this.state.project.name + button.btn.btn-sm.btn-success(onClick=this.onBuildProject) + i.fa.fa-fw.fa-play | - | (build # - span= lastDoneBuild.number - | ) - else - | - + span Build + div + Scm(scm=this.state.project.scm ? this.state.project.scm.type : '') + span= this.state.project.name - p Current successfully streak: - if lastDoneBuild - span= this.state.project.doneBuildsStreak - else - | - + div.text-muted + - var lastDoneBuild = this.state.project.lastDoneBuild; + p Last successfully built: + if lastDoneBuild + DateTime(value=lastDoneBuild.endDate) + | + | (build # + span= lastDoneBuild.number + | ) + else + | - - p Last build duration: - if lastDoneBuild - Duration(value=(lastDoneBuild.endDate - lastDoneBuild.startDate)) - else - | - + p Current successfully streak: + if lastDoneBuild + span= this.state.project.doneBuildsStreak + else + | - - p Average build duration: - if this.state.project.avgBuildDuration - Duration(value=this.state.project.avgBuildDuration) - else - | - + p Last build duration: + if lastDoneBuild + Duration(value=(lastDoneBuild.endDate - lastDoneBuild.startDate)) + else + | - - h2 - i.fa.fa-fw.fa-history - span - span Build history - Builds(projectName=this.props.params.name) + p Average build duration: + if this.state.project.avgBuildDuration + Duration(value=this.state.project.avgBuildDuration) + else + | - + + h2 + i.fa.fa-fw.fa-history + span + span Build history + + Builds(projectName=this.props.params.name)