parent
af07c490b2
commit
90216b2066
11 changed files with 175 additions and 30 deletions
125
app/assets/javascripts/ide/components/jobs/detail.vue
Normal file
125
app/assets/javascripts/ide/components/jobs/detail.vue
Normal file
|
@ -0,0 +1,125 @@
|
|||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import tooltip from '../../../vue_shared/directives/tooltip';
|
||||
import Icon from '../../../vue_shared/components/icon.vue';
|
||||
import Job from '../../../job';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
components: {
|
||||
Icon,
|
||||
},
|
||||
computed: {
|
||||
...mapState('pipelines', ['detailJob']),
|
||||
rawUrl() {
|
||||
return `${this.detailJob.path}/raw`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.job = new Job({
|
||||
buildStage: 'a',
|
||||
buildState: this.detailJob.status.text,
|
||||
pagePath: this.detailJob.path,
|
||||
redirectToJob: false,
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.job.destroy();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ide-pipeline build-page">
|
||||
<header
|
||||
class="ide-tree-header ide-pipeline-header"
|
||||
>
|
||||
<button
|
||||
class="btn btn-default btn-sm"
|
||||
@click="() => { $store.state.pipelines.detailJob = null; $store.dispatch('setRightPane', 'pipelines-list') }"
|
||||
>
|
||||
<icon
|
||||
name="chevron-left"
|
||||
/>
|
||||
{{ __('View jobs') }}
|
||||
</button>
|
||||
</header>
|
||||
<div class="build-trace-container prepend-top-default">
|
||||
<div
|
||||
v-once
|
||||
class="top-bar js-top-bar"
|
||||
>
|
||||
<div class="controllers float-right">
|
||||
<a
|
||||
v-tooltip
|
||||
:title="__('Show complete raw')"
|
||||
data-placement="top"
|
||||
data-container="body"
|
||||
class="js-raw-link-controller controllers-buttons"
|
||||
:href="rawUrl"
|
||||
>
|
||||
<i
|
||||
aria-hidden="true"
|
||||
class="fa fa-file-text-o"
|
||||
></i>
|
||||
</a>
|
||||
<div
|
||||
v-tooltip
|
||||
class="controllers-buttons"
|
||||
data-container="body"
|
||||
data-placement="top"
|
||||
:title="__('Scroll to top')"
|
||||
>
|
||||
<button
|
||||
class="js-scroll-up btn-scroll btn-transparent btn-blank"
|
||||
disabled
|
||||
type="button"
|
||||
>
|
||||
<icon
|
||||
name="scroll_up"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-tooltip
|
||||
class="controllers-buttons"
|
||||
data-container="body"
|
||||
data-placement="top"
|
||||
:title="__('Scroll to top')"
|
||||
>
|
||||
<button
|
||||
class="js-scroll-up btn-scroll btn-transparent btn-blank"
|
||||
disabled
|
||||
type="button"
|
||||
>
|
||||
<icon
|
||||
name="scroll_down"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<pre
|
||||
class="build-trace"
|
||||
id="build-trace"
|
||||
>
|
||||
<code class="bash js-build-output">
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.build-trace-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ide-tree-header .btn {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
|
@ -42,5 +42,17 @@ export default {
|
|||
/>
|
||||
</a>
|
||||
</span>
|
||||
<button
|
||||
class="btn btn-default btn-sm"
|
||||
@click="() => { $store.state.pipelines.detailJob = job; $store.dispatch('setRightPane', 'jobs-detail') }"
|
||||
>
|
||||
{{ __('View log') }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,6 +4,7 @@ import tooltip from '../../../vue_shared/directives/tooltip';
|
|||
import Icon from '../../../vue_shared/components/icon.vue';
|
||||
import { rightSidebarViews } from '../../constants';
|
||||
import PipelinesList from '../pipelines/list.vue';
|
||||
import JobsDetail from '../jobs/detail.vue';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
|
@ -12,6 +13,7 @@ export default {
|
|||
components: {
|
||||
Icon,
|
||||
PipelinesList,
|
||||
JobsDetail,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['rightPane']),
|
||||
|
|
|
@ -23,4 +23,5 @@ export const viewerTypes = {
|
|||
|
||||
export const rightSidebarViews = {
|
||||
pipelines: 'pipelines-list',
|
||||
jobsDetail: 'jobs-detail',
|
||||
};
|
||||
|
|
|
@ -77,4 +77,6 @@ export const fetchJobs = ({ dispatch }, stage) => {
|
|||
export const toggleStageCollapsed = ({ commit }, stageId) =>
|
||||
commit(types.TOGGLE_STAGE_COLLAPSE, stageId);
|
||||
|
||||
export const setDetailJob = ({ commit }, job) => commit(types.SET_DETAIL_JOB, job);
|
||||
|
||||
export default () => {};
|
||||
|
|
|
@ -7,3 +7,5 @@ export const RECEIVE_JOBS_ERROR = 'RECEIVE_JOBS_ERROR';
|
|||
export const RECEIVE_JOBS_SUCCESS = 'RECEIVE_JOBS_SUCCESS';
|
||||
|
||||
export const TOGGLE_STAGE_COLLAPSE = 'TOGGLE_STAGE_COLLAPSE';
|
||||
|
||||
export const SET_DETAIL_JOB = 'SET_DETAIL_JOB';
|
||||
|
|
|
@ -63,4 +63,7 @@ export default {
|
|||
isCollapsed: stage.id === id ? !stage.isCollapsed : stage.isCollapsed,
|
||||
}));
|
||||
},
|
||||
[types.SET_DETAIL_JOB](state, job) {
|
||||
state.detailJob = job;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -3,4 +3,5 @@ export default () => ({
|
|||
isLoadingJobs: false,
|
||||
latestPipeline: null,
|
||||
stages: [],
|
||||
detailJob: null,
|
||||
});
|
||||
|
|
|
@ -4,4 +4,5 @@ export const normalizeJob = job => ({
|
|||
name: job.name,
|
||||
status: job.status,
|
||||
path: job.build_path,
|
||||
started: job.started,
|
||||
});
|
||||
|
|
|
@ -22,6 +22,8 @@ export default class Job {
|
|||
this.$window = $(window);
|
||||
this.logBytes = 0;
|
||||
this.updateDropdown = this.updateDropdown.bind(this);
|
||||
this.redirectToJob =
|
||||
this.options.redirectToJob !== undefined ? this.options.redirectToJob : true;
|
||||
|
||||
this.$buildTrace = $('#build-trace');
|
||||
this.$buildRefreshAnimation = $('.js-build-refresh');
|
||||
|
@ -44,31 +46,23 @@ export default class Job {
|
|||
.off('click', '.js-sidebar-build-toggle')
|
||||
.on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this));
|
||||
|
||||
this.$document
|
||||
.off('click', '.stage-item')
|
||||
.on('click', '.stage-item', this.updateDropdown);
|
||||
this.$document.off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
|
||||
|
||||
// add event listeners to the scroll buttons
|
||||
this.$scrollTopBtn
|
||||
.off('click')
|
||||
.on('click', this.scrollToTop.bind(this));
|
||||
this.$scrollTopBtn.off('click').on('click', this.scrollToTop.bind(this));
|
||||
|
||||
this.$scrollBottomBtn
|
||||
.off('click')
|
||||
.on('click', this.scrollToBottom.bind(this));
|
||||
this.$scrollBottomBtn.off('click').on('click', this.scrollToBottom.bind(this));
|
||||
|
||||
this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
|
||||
|
||||
this.$window
|
||||
.off('scroll')
|
||||
.on('scroll', () => {
|
||||
if (!this.isScrolledToBottom()) {
|
||||
this.toggleScrollAnimation(false);
|
||||
} else if (this.isScrolledToBottom() && !this.isLogComplete) {
|
||||
this.toggleScrollAnimation(true);
|
||||
}
|
||||
this.scrollThrottled();
|
||||
});
|
||||
this.$window.off('scroll').on('scroll', () => {
|
||||
if (!this.isScrolledToBottom()) {
|
||||
this.toggleScrollAnimation(false);
|
||||
} else if (this.isScrolledToBottom() && !this.isLogComplete) {
|
||||
this.toggleScrollAnimation(true);
|
||||
}
|
||||
this.scrollThrottled();
|
||||
});
|
||||
|
||||
this.$window
|
||||
.off('resize.build')
|
||||
|
@ -79,6 +73,10 @@ export default class Job {
|
|||
this.getBuildTrace();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
initAffixTopArea() {
|
||||
/**
|
||||
If the browser does not support position sticky, it returns the position as static.
|
||||
|
@ -102,9 +100,8 @@ export default class Job {
|
|||
|
||||
const windowHeight = $(window).height();
|
||||
if (this.canScroll()) {
|
||||
if (currentPosition > 0 &&
|
||||
(scrollHeight - currentPosition !== windowHeight)) {
|
||||
// User is in the middle of the log
|
||||
if (currentPosition > 0 && scrollHeight - currentPosition !== windowHeight) {
|
||||
// User is in the middle of the log
|
||||
|
||||
this.toggleDisableButton(this.$scrollTopBtn, false);
|
||||
this.toggleDisableButton(this.$scrollBottomBtn, false);
|
||||
|
@ -169,10 +166,11 @@ export default class Job {
|
|||
}
|
||||
|
||||
getBuildTrace() {
|
||||
return axios.get(`${this.pagePath}/trace.json`, {
|
||||
params: { state: this.state },
|
||||
})
|
||||
.then((res) => {
|
||||
return axios
|
||||
.get(`${this.pagePath}/trace.json`, {
|
||||
params: { state: this.state },
|
||||
})
|
||||
.then(res => {
|
||||
const log = res.data;
|
||||
|
||||
if (!this.fetchingStatusFavicon) {
|
||||
|
@ -222,7 +220,7 @@ export default class Job {
|
|||
this.toggleScrollAnimation(false);
|
||||
}
|
||||
|
||||
if (log.status !== this.buildStatus) {
|
||||
if (log.status !== this.buildStatus && this.redirectToJob) {
|
||||
visitUrl(this.pagePath);
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1202,7 +1202,7 @@
|
|||
}
|
||||
|
||||
.ide-pipeline-header {
|
||||
min-height: 50px;
|
||||
min-height: 55px;
|
||||
padding-left: $gl-padding;
|
||||
padding-right: $gl-padding;
|
||||
|
||||
|
@ -1222,8 +1222,6 @@
|
|||
.ci-status-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 20px;
|
||||
margin-top: -2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue