summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/jobs
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2018-10-03 15:29:07 +0000
committerPhil Hughes <me@iamphill.com>2018-10-03 15:29:07 +0000
commit9128e7849dbc064913b52ad427dcfb15386ad23e (patch)
tree664e0fca75719f1841d06d3e3eb493f6f9347f34 /app/assets/javascripts/jobs
parent88c1cf676cf02c3fca16093ad8ee5f6cf02dc462 (diff)
downloadgitlab-ce-9128e7849dbc064913b52ad427dcfb15386ad23e.tar.gz
Uses Vue app to render part of job show page
Diffstat (limited to 'app/assets/javascripts/jobs')
-rw-r--r--app/assets/javascripts/jobs/components/environments_block.vue68
-rw-r--r--app/assets/javascripts/jobs/components/header.vue95
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue99
-rw-r--r--app/assets/javascripts/jobs/job_details_bundle.js7
-rw-r--r--app/assets/javascripts/jobs/store/getters.js42
-rw-r--r--app/assets/javascripts/jobs/store/index.js2
6 files changed, 191 insertions, 122 deletions
diff --git a/app/assets/javascripts/jobs/components/environments_block.vue b/app/assets/javascripts/jobs/components/environments_block.vue
index ca6386595c7..e6e1d418194 100644
--- a/app/assets/javascripts/jobs/components/environments_block.vue
+++ b/app/assets/javascripts/jobs/components/environments_block.vue
@@ -12,12 +12,16 @@
type: Object,
required: true,
},
+ iconStatus: {
+ type: Object,
+ required: true,
+ },
},
computed: {
environment() {
let environmentText;
switch (this.deploymentStatus.status) {
- case 'latest':
+ case 'last':
environmentText = sprintf(
__('This job is the most recent deployment to %{link}.'),
{ link: this.environmentLink },
@@ -32,7 +36,7 @@
),
{
environmentLink: this.environmentLink,
- deploymentLink: this.deploymentLink,
+ deploymentLink: this.deploymentLink(`#${this.lastDeployment.iid}`),
},
false,
);
@@ -56,11 +60,11 @@
if (this.hasLastDeployment) {
environmentText = sprintf(
__(
- 'This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}.',
+ 'This job is creating a deployment to %{environmentLink} and will overwrite the %{deploymentLink}.',
),
{
environmentLink: this.environmentLink,
- deploymentLink: this.deploymentLink,
+ deploymentLink: this.deploymentLink(__('latest deployment')),
},
false,
);
@@ -78,41 +82,57 @@
return environmentText;
},
environmentLink() {
- return sprintf(
- '%{startLink}%{name}%{endLink}',
- {
- startLink: `<a href="${this.deploymentStatus.environment.path}">`,
- name: _.escape(this.deploymentStatus.environment.name),
- endLink: '</a>',
- },
- false,
- );
+ if (this.hasEnvironment) {
+ return sprintf(
+ '%{startLink}%{name}%{endLink}',
+ {
+ startLink: `<a href="${
+ this.deploymentStatus.environment.environment_path
+ }" class="js-environment-link">`,
+ name: _.escape(this.deploymentStatus.environment.name),
+ endLink: '</a>',
+ },
+ false,
+ );
+ }
+ return '';
},
- deploymentLink() {
+ hasLastDeployment() {
+ return this.hasEnvironment && this.deploymentStatus.environment.last_deployment;
+ },
+ lastDeployment() {
+ return this.hasLastDeployment ? this.deploymentStatus.environment.last_deployment : {};
+ },
+ hasEnvironment() {
+ return !_.isEmpty(this.deploymentStatus.environment);
+ },
+ lastDeploymentPath() {
+ return !_.isEmpty(this.lastDeployment.deployable) ? this.lastDeployment.deployable.build_path : '';
+ },
+ },
+ methods: {
+ deploymentLink(name) {
return sprintf(
'%{startLink}%{name}%{endLink}',
{
- startLink: `<a href="${this.lastDeployment.path}">`,
- name: _.escape(this.lastDeployment.name),
+ startLink: `<a href="${this.lastDeploymentPath}" class="js-job-deployment-link">`,
+ name,
endLink: '</a>',
},
false,
);
},
- hasLastDeployment() {
- return this.deploymentStatus.environment.last_deployment;
- },
- lastDeployment() {
- return this.deploymentStatus.environment.last_deployment;
- },
},
};
</script>
<template>
<div class="prepend-top-default js-environment-container">
<div class="environment-information">
- <ci-icon :status="deploymentStatus.icon" />
- <p v-html="environment"></p>
+ <ci-icon :status="iconStatus"/>
+ <p
+ class="inline append-bottom-0"
+ v-html="environment"
+ ></p>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/jobs/components/header.vue b/app/assets/javascripts/jobs/components/header.vue
deleted file mode 100644
index 63324e68d68..00000000000
--- a/app/assets/javascripts/jobs/components/header.vue
+++ /dev/null
@@ -1,95 +0,0 @@
-<script>
-import ciHeader from '../../vue_shared/components/header_ci_component.vue';
-import callout from '../../vue_shared/components/callout.vue';
-
-export default {
- name: 'JobHeaderSection',
- components: {
- ciHeader,
- callout,
- },
- props: {
- job: {
- type: Object,
- required: true,
- },
- isLoading: {
- type: Boolean,
- required: true,
- },
- },
- data() {
- return {
- actions: this.getActions(),
- };
- },
- computed: {
- status() {
- return this.job && this.job.status;
- },
- shouldRenderContent() {
- return !this.isLoading && Object.keys(this.job).length;
- },
- shouldRenderReason() {
- return !!(this.job.status && this.job.callout_message);
- },
- /**
- * When job has not started the key will be `false`
- * When job started the key will be a string with a date.
- */
- jobStarted() {
- return !this.job.started === false;
- },
- headerTime() {
- return this.jobStarted ? this.job.started : this.job.created_at;
- },
- },
- watch: {
- job() {
- this.actions = this.getActions();
- },
- },
- methods: {
- getActions() {
- const actions = [];
-
- if (this.job.new_issue_path) {
- actions.push({
- label: 'New issue',
- path: this.job.new_issue_path,
- cssClass: 'js-new-issue btn btn-success btn-inverted d-none d-md-block d-lg-block d-xl-block',
- type: 'link',
- });
- }
- return actions;
- },
- },
-};
-</script>
-<template>
- <header>
- <div class="js-build-header build-header top-area">
- <ci-header
- v-if="shouldRenderContent"
- :status="status"
- :item-id="job.id"
- :time="headerTime"
- :user="job.user"
- :actions="actions"
- :has-sidebar-button="true"
- :should-render-triggered-label="jobStarted"
- item-name="Job"
- />
- <gl-loading-icon
- v-if="isLoading"
- :size="2"
- class="prepend-top-default append-bottom-default"
- />
- </div>
-
- <callout
- v-if="shouldRenderReason"
- :message="job.callout_message"
- />
- </header>
-</template>
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
new file mode 100644
index 00000000000..bac8bd71d64
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -0,0 +1,99 @@
+<script>
+ import { mapGetters, mapState } from 'vuex';
+ import CiHeader from '~/vue_shared/components/header_ci_component.vue';
+ import Callout from '~/vue_shared/components/callout.vue';
+ import EnvironmentsBlock from './environments_block.vue';
+ import ErasedBlock from './erased_block.vue';
+ import StuckBlock from './stuck_block.vue';
+
+ export default {
+ name: 'JobPageApp',
+ components: {
+ CiHeader,
+ Callout,
+ EnvironmentsBlock,
+ ErasedBlock,
+ StuckBlock,
+ },
+ props: {
+ runnerHelpUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ ...mapState(['isLoading', 'job']),
+ ...mapGetters([
+ 'headerActions',
+ 'headerTime',
+ 'shouldRenderCalloutMessage',
+ 'jobHasStarted',
+ 'hasEnvironment',
+ 'isJobStuck',
+ ]),
+ },
+ };
+</script>
+<template>
+ <div>
+ <gl-loading-icon
+ v-if="isLoading"
+ :size="2"
+ class="prepend-top-20"
+ />
+
+ <template v-else>
+ <!-- Header Section -->
+ <header>
+ <div class="js-build-header build-header top-area">
+ <ci-header
+ :status="job.status"
+ :item-id="job.id"
+ :time="headerTime"
+ :user="job.user"
+ :actions="headerActions"
+ :has-sidebar-button="true"
+ :should-render-triggered-label="jobHasStarted"
+ :item-name="__('Job')"
+ />
+ </div>
+
+ <callout
+ v-if="shouldRenderCalloutMessage"
+ :message="job.callout_message"
+ />
+ </header>
+ <!-- EO Header Section -->
+
+ <!-- Body Section -->
+ <stuck-block
+ v-if="isJobStuck"
+ class="js-job-stuck"
+ :has-no-runners-for-project="job.runners.available"
+ :tags="job.tags"
+ :runners-path="runnerHelpUrl"
+ />
+
+ <environments-block
+ v-if="hasEnvironment"
+ :deployment-status="job.deployment_status"
+ :icon-status="job.status"
+ />
+
+ <erased-block
+ v-if="job.erased"
+ :user="job.erased_by"
+ :erased-at="job.erased_at"
+ />
+
+ <!--job log -->
+ <!-- EO job log -->
+
+ <!--empty state -->
+ <!-- EO empty state -->
+
+ <!-- EO Body Section -->
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js
index ae40f4cdf3b..3eb75e72506 100644
--- a/app/assets/javascripts/jobs/job_details_bundle.js
+++ b/app/assets/javascripts/jobs/job_details_bundle.js
@@ -2,7 +2,7 @@ import _ from 'underscore';
import { mapState, mapActions } from 'vuex';
import Vue from 'vue';
import Job from '../job';
-import JobHeader from './components/header.vue';
+import JobApp from './components/job_app.vue';
import Sidebar from './components/sidebar.vue';
import createStore from './store';
@@ -22,17 +22,18 @@ export default () => {
new Vue({
el: '#js-build-header-vue',
components: {
- JobHeader,
+ JobApp,
},
store,
computed: {
...mapState(['job', 'isLoading']),
},
render(createElement) {
- return createElement('job-header', {
+ return createElement('job-app', {
props: {
isLoading: this.isLoading,
job: this.job,
+ runnerHelpUrl: dataset.runnerHelpUrl,
},
});
},
diff --git a/app/assets/javascripts/jobs/store/getters.js b/app/assets/javascripts/jobs/store/getters.js
new file mode 100644
index 00000000000..62d154ff584
--- /dev/null
+++ b/app/assets/javascripts/jobs/store/getters.js
@@ -0,0 +1,42 @@
+import _ from 'underscore';
+import { __ } from '~/locale';
+
+export const headerActions = state => {
+ if (state.job.new_issue_path) {
+ return [
+ {
+ label: __('New issue'),
+ path: state.job.new_issue_path,
+ cssClass:
+ 'js-new-issue btn btn-success btn-inverted d-none d-md-block d-lg-block d-xl-block',
+ type: 'link',
+ },
+ ];
+ }
+ return [];
+};
+
+export const headerTime = state => (state.job.started ? state.job.started : state.job.created_at);
+
+export const shouldRenderCalloutMessage = state =>
+ !_.isEmpty(state.job.status) && !_.isEmpty(state.job.callout_message);
+
+/**
+ * When job has not started the key will be `false`
+ * When job started the key will be a string with a date.
+ */
+export const jobHasStarted = state => !(state.job.started === false);
+
+export const hasEnvironment = state => !_.isEmpty(state.job.deployment_status);
+
+/**
+ * When the job is pending and there are no available runners
+ * we need to render the stuck block;
+ *
+ * @returns {Boolean}
+ */
+export const isJobStuck = state =>
+ state.job.status.group === 'pending' && state.job.runners && state.job.runners.available === false;
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/jobs/store/index.js b/app/assets/javascripts/jobs/store/index.js
index d8f6f56ce61..96e38f9a2fa 100644
--- a/app/assets/javascripts/jobs/store/index.js
+++ b/app/assets/javascripts/jobs/store/index.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import Vuex from 'vuex';
import state from './state';
import * as actions from './actions';
+import * as getters from './getters';
import mutations from './mutations';
Vue.use(Vuex);
@@ -9,5 +10,6 @@ Vue.use(Vuex);
export default () => new Vuex.Store({
actions,
mutations,
+ getters,
state: state(),
});