diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2017-05-12 18:03:13 +0100 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2017-05-12 19:23:59 +0100 |
commit | 418f5d3f0c4a085c10c098684e8e572508585724 (patch) | |
tree | 5bc1605739360ed09619e14ab8ce6003417939cb | |
parent | 6e3960ab7ef1cb099e579da226d036e105f07405 (diff) | |
download | gitlab-ce-24339-job-page-header-step-2.tar.gz |
Adds reusable header component for job and pipeline24339-job-page-header-step-2
Adds timago reusable component port from ruby helper
12 files changed, 1857 insertions, 112 deletions
diff --git a/app/assets/javascripts/builds/build_bundle.js b/app/assets/javascripts/builds/build_bundle.js index 2e39e8b84ed..94ae4dc1b9e 100644 --- a/app/assets/javascripts/builds/build_bundle.js +++ b/app/assets/javascripts/builds/build_bundle.js @@ -2,9 +2,27 @@ import Vue from 'vue'; import buildApp from './components/build_component.vue'; document.addEventListener('DOMContentLoaded', () => new Vue({ - el: '#js-build-app-vue', + el: document.getElementById('js-build-app-vue'), + components: { buildApp, }, - render: createElement => createElement('build-app'), + + data() { + const dataset = this.$options.el.dataset; + + return { + endpoint: dataset.endpoint, + cssClass: dataset.class, + }; + }, + + render(createElement) { + return createElement('build-app', { + props: { + endpoint: this.endpoint, + cssClass: this.cssClass, + }, + }); + }, })); diff --git a/app/assets/javascripts/builds/components/build_component.vue b/app/assets/javascripts/builds/components/build_component.vue index 55c34a1d5b0..16bd3ef2d9d 100644 --- a/app/assets/javascripts/builds/components/build_component.vue +++ b/app/assets/javascripts/builds/components/build_component.vue @@ -6,19 +6,30 @@ import Poll from '../../lib/utils/poll'; import BuildStore from '../stores/build_store'; import BuildService from '../services/build_service'; import loadingIcon from '../../vue_shared/components/loading_icon.vue'; +import headerComponent from '../../vue_shared/components/header_ci_component.vue'; export default { + props: { + endpoint: { + type: String, + required: true, + }, + cssClass: { + type: String, + required: true, + }, + }, + components: { loadingIcon, + headerComponent, }, data() { - const endpoint = ''; const store = new BuildStore(); return { isLoading: false, - endpoint, store, state: store.state, }; @@ -60,15 +71,40 @@ export default { // eslint-disable-next-line no-new new Flash('An error occurred while fetching the job.'); }, + + postAction(action) { + this.service.postAction(action.path) + .then(() => { + this.service.get() + .then(this.successCallback) + .catch(this.errorCallback); + }) + .catch(() => { + // show error + }); + }, }, }; </script> <template> - <section> - <loading-icon v-if="isLoading" /> + <section :class="cssClass"> + <loading-icon + v-if="isLoading" + label="Loading job details" + size="3" + /> <section v-else> + <header-component + :status="state.status" + item-name="Job" + :item-id="state.build.id" + :time="state.build.created_at" + :user="state.user" + :actions="state.actions" + @postAction="postAction" + /> </section> </section> </template> diff --git a/app/assets/javascripts/builds/mock.js b/app/assets/javascripts/builds/mock.js new file mode 100644 index 00000000000..fedaee0c043 --- /dev/null +++ b/app/assets/javascripts/builds/mock.js @@ -0,0 +1,1366 @@ +/* eslint-disable */ + +// TO DELETE +export default { + "id": 131, + "user": { + "name": "Root", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon", + "web_url": "http://localhost:3000/root" + }, + "active": false, + "coverage": null, + "path": "/root/review-app/pipelines/131", + "details": { + "status": { + "icon": "icon_status_failed", + "text": "failed", + "label": "failed", + "group": "failed", + "has_details": true, + "details_path": "/root/review-app/pipelines/131", + "favicon": "/assets/ci_favicons/dev/favicon_status_failed-5f8edf9ec029ce816326fba3b72e6d48c4699845c6d0a7a40cef401395117931.ico" + }, + "duration": 13, + "finished_at": "2017-05-08T16:00:19.904Z", + "stages": [{ + "name": "review", + "title": "review: passed", + "groups": [{ + "name": "review_1", + "size": 1, + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4374", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4374/retry", + "method": "post" + } + }, + "jobs": [{ + "id": 4374, + "name": "review_1", + "build_path": "/root/review-app/builds/4374", + "retry_path": "/root/review-app/builds/4374/retry", + "playable": false, + "created_at": "2017-05-08T14:57:39.880Z", + "updated_at": "2017-05-08T14:57:52.639Z", + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4374", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4374/retry", + "method": "post" + } + } + }] + }, { + "name": "review_2", + "size": 1, + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4376", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4376/retry", + "method": "post" + } + }, + "jobs": [{ + "id": 4376, + "name": "review_2", + "build_path": "/root/review-app/builds/4376", + "retry_path": "/root/review-app/builds/4376/retry", + "playable": false, + "created_at": "2017-05-08T14:57:39.973Z", + "updated_at": "2017-05-08T14:57:57.773Z", + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4376", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4376/retry", + "method": "post" + } + } + }] + }, { + "name": "review_3", + "size": 1, + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4373", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4373/retry", + "method": "post" + } + }, + "jobs": [{ + "id": 4373, + "name": "review_3", + "build_path": "/root/review-app/builds/4373", + "retry_path": "/root/review-app/builds/4373/retry", + "playable": false, + "created_at": "2017-05-08T14:57:39.830Z", + "updated_at": "2017-05-08T14:57:47.117Z", + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4373", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4373/retry", + "method": "post" + } + } + }] + }, { + "name": "stop_review", + "size": 1, + "status": { + "icon": "icon_status_manual", + "text": "manual", + "label": "manual stop action", + "group": "manual", + "has_details": true, + "details_path": "/root/review-app/builds/4375", + "favicon": "/assets/ci_favicons/dev/favicon_status_manual-9f78d05507152e2336b392a51b28708907bde24753767fd7f95d112839b95223.ico", + "action": { + "icon": "icon_action_stop", + "title": "Stop", + "path": "/root/review-app/builds/4375/play", + "method": "post" + } + }, + "jobs": [{ + "id": 4375, + "name": "stop_review", + "build_path": "/root/review-app/builds/4375", + "retry_path": "/root/review-app/builds/4375/retry", + "play_path": "/root/review-app/builds/4375/play", + "playable": true, + "created_at": "2017-05-08T14:57:39.909Z", + "updated_at": "2017-05-08T14:57:41.755Z", + "status": { + "icon": "icon_status_manual", + "text": "manual", + "label": "manual stop action", + "group": "manual", + "has_details": true, + "details_path": "/root/review-app/builds/4375", + "favicon": "/assets/ci_favicons/dev/favicon_status_manual-9f78d05507152e2336b392a51b28708907bde24753767fd7f95d112839b95223.ico", + "action": { + "icon": "icon_action_stop", + "title": "Stop", + "path": "/root/review-app/builds/4375/play", + "method": "post" + } + } + }] + }], + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/pipelines/131#review", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico" + }, + "path": "/root/review-app/pipelines/131#review", + "dropdown_path": "/root/review-app/pipelines/131/stage.json?stage=review" + }, { + "name": "staging", + "title": "staging: failed", + "groups": [{ + "name": "staging", + "size": 1, + "status": { + "icon": "icon_status_failed", + "text": "failed", + "label": "failed", + "group": "failed", + "has_details": true, + "details_path": "/root/review-app/builds/4377", + "favicon": "/assets/ci_favicons/dev/favicon_status_failed-5f8edf9ec029ce816326fba3b72e6d48c4699845c6d0a7a40cef401395117931.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4377/retry", + "method": "post" + } + }, + "jobs": [{ + "id": 4377, + "name": "staging", + "build_path": "/root/review-app/builds/4377", + "retry_path": "/root/review-app/builds/4377/retry", + "playable": false, + "created_at": "2017-05-08T14:57:39.998Z", + "updated_at": "2017-05-08T16:00:11.544Z", + "status": { + "icon": "icon_status_failed", + "text": "failed", + "label": "failed", + "group": "failed", + "has_details": true, + "details_path": "/root/review-app/builds/4377", + "favicon": "/assets/ci_favicons/dev/favicon_status_failed-5f8edf9ec029ce816326fba3b72e6d48c4699845c6d0a7a40cef401395117931.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4377/retry", + "method": "post" + } + } + }] + }, { + "name": "staging_1", + "size": 1, + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4378", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4378/retry", + "method": "post" + } + }, + "jobs": [{ + "id": 4378, + "name": "staging_1", + "build_path": "/root/review-app/builds/4378", + "retry_path": "/root/review-app/builds/4378/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.047Z", + "updated_at": "2017-05-08T14:58:02.434Z", + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4378", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4378/retry", + "method": "post" + } + } + }] + }, { + "name": "staging_2", + "size": 1, + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4379", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4379/retry", + "method": "post" + } + }, + "jobs": [{ + "id": 4379, + "name": "staging_2", + "build_path": "/root/review-app/builds/4379", + "retry_path": "/root/review-app/builds/4379/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.085Z", + "updated_at": "2017-05-08T14:58:07.025Z", + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4379", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4379/retry", + "method": "post" + } + } + }] + }, { + "name": "staging_3", + "size": 1, + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4380", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4380/retry", + "method": "post" + } + }, + "jobs": [{ + "id": 4380, + "name": "staging_3", + "build_path": "/root/review-app/builds/4380", + "retry_path": "/root/review-app/builds/4380/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.109Z", + "updated_at": "2017-05-08T14:58:11.956Z", + "status": { + "icon": "icon_status_success", + "text": "passed", + "label": "passed", + "group": "success", + "has_details": true, + "details_path": "/root/review-app/builds/4380", + "favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico", + "action": { + "icon": "icon_action_retry", + "title": "Retry", + "path": "/root/review-app/builds/4380/retry", + "method": "post" + } + } + }] + }], + "status": { + "icon": "icon_status_failed", + "text": "failed", + "label": "failed", + "group": "failed", + "has_details": true, + "details_path": "/root/review-app/pipelines/131#staging", + "favicon": "/assets/ci_favicons/dev/favicon_status_failed-5f8edf9ec029ce816326fba3b72e6d48c4699845c6d0a7a40cef401395117931.ico" + }, + "path": "/root/review-app/pipelines/131#staging", + "dropdown_path": "/root/review-app/pipelines/131/stage.json?stage=staging" + }, { + "name": "production", + "title": "production: skipped", + "groups": [{ + "name": "prod_1", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4381", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4381, + "name": "prod_1", + "build_path": "/root/review-app/builds/4381", + "retry_path": "/root/review-app/builds/4381/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.135Z", + "updated_at": "2017-05-08T16:00:12.928Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4381", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "prod_2", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4382", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4382, + "name": "prod_2", + "build_path": "/root/review-app/builds/4382", + "retry_path": "/root/review-app/builds/4382/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.171Z", + "updated_at": "2017-05-08T16:00:12.653Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4382", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "prod_3", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4383", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4383, + "name": "prod_3", + "build_path": "/root/review-app/builds/4383", + "retry_path": "/root/review-app/builds/4383/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.296Z", + "updated_at": "2017-05-08T16:00:12.394Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4383", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "production", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped (not allowed)", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4384", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4384, + "name": "production", + "build_path": "/root/review-app/builds/4384", + "retry_path": "/root/review-app/builds/4384/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.338Z", + "updated_at": "2017-05-08T16:00:12.167Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped (not allowed)", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4384", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }], + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/pipelines/131#production", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "path": "/root/review-app/pipelines/131#production", + "dropdown_path": "/root/review-app/pipelines/131/stage.json?stage=production" + }, { + "name": "stage1", + "title": "stage1: skipped", + "groups": [{ + "name": "job_01", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4386", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4386, + "name": "job_01", + "build_path": "/root/review-app/builds/4386", + "retry_path": "/root/review-app/builds/4386/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.461Z", + "updated_at": "2017-05-08T16:00:13.751Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4386", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "job_02", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4387", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4387, + "name": "job_02", + "build_path": "/root/review-app/builds/4387", + "retry_path": "/root/review-app/builds/4387/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.520Z", + "updated_at": "2017-05-08T16:00:13.592Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4387", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "stage1", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4385", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4385, + "name": "stage1", + "build_path": "/root/review-app/builds/4385", + "retry_path": "/root/review-app/builds/4385/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.402Z", + "updated_at": "2017-05-08T16:00:13.531Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4385", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }], + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/pipelines/131#stage1", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "path": "/root/review-app/pipelines/131#stage1", + "dropdown_path": "/root/review-app/pipelines/131/stage.json?stage=stage1" + }, { + "name": "stage2", + "title": "stage2: skipped", + "groups": [{ + "name": "test", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4388", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4388, + "name": "test", + "build_path": "/root/review-app/builds/4388", + "retry_path": "/root/review-app/builds/4388/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.678Z", + "updated_at": "2017-05-08T16:00:14.608Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4388", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }], + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/pipelines/131#stage2", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "path": "/root/review-app/pipelines/131#stage2", + "dropdown_path": "/root/review-app/pipelines/131/stage.json?stage=stage2" + }, { + "name": "stage3", + "title": "stage3: skipped", + "groups": [{ + "name": "test1", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4390", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4390, + "name": "test1", + "build_path": "/root/review-app/builds/4390", + "retry_path": "/root/review-app/builds/4390/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.768Z", + "updated_at": "2017-05-08T16:00:15.495Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4390", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test2", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4391", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4391, + "name": "test2", + "build_path": "/root/review-app/builds/4391", + "retry_path": "/root/review-app/builds/4391/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.800Z", + "updated_at": "2017-05-08T16:00:15.784Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4391", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test3", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4389", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4389, + "name": "test3", + "build_path": "/root/review-app/builds/4389", + "retry_path": "/root/review-app/builds/4389/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.732Z", + "updated_at": "2017-05-08T16:00:15.121Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4389", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test4", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4392", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4392, + "name": "test4", + "build_path": "/root/review-app/builds/4392", + "retry_path": "/root/review-app/builds/4392/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.834Z", + "updated_at": "2017-05-08T16:00:16.084Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4392", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test5", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4393", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4393, + "name": "test5", + "build_path": "/root/review-app/builds/4393", + "retry_path": "/root/review-app/builds/4393/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.881Z", + "updated_at": "2017-05-08T16:00:16.539Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4393", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test6", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4394", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4394, + "name": "test6", + "build_path": "/root/review-app/builds/4394", + "retry_path": "/root/review-app/builds/4394/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.914Z", + "updated_at": "2017-05-08T16:00:17.071Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4394", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test7", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4395", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4395, + "name": "test7", + "build_path": "/root/review-app/builds/4395", + "retry_path": "/root/review-app/builds/4395/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.947Z", + "updated_at": "2017-05-08T16:00:17.402Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4395", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test8", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4396", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4396, + "name": "test8", + "build_path": "/root/review-app/builds/4396", + "retry_path": "/root/review-app/builds/4396/retry", + "playable": false, + "created_at": "2017-05-08T14:57:40.975Z", + "updated_at": "2017-05-08T16:00:17.611Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4396", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test9", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4397", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4397, + "name": "test9", + "build_path": "/root/review-app/builds/4397", + "retry_path": "/root/review-app/builds/4397/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.005Z", + "updated_at": "2017-05-08T16:00:17.714Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4397", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test10", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4398", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4398, + "name": "test10", + "build_path": "/root/review-app/builds/4398", + "retry_path": "/root/review-app/builds/4398/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.043Z", + "updated_at": "2017-05-08T16:00:17.534Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4398", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test11", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4399", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4399, + "name": "test11", + "build_path": "/root/review-app/builds/4399", + "retry_path": "/root/review-app/builds/4399/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.084Z", + "updated_at": "2017-05-08T16:00:17.324Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4399", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test12", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4400", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4400, + "name": "test12", + "build_path": "/root/review-app/builds/4400", + "retry_path": "/root/review-app/builds/4400/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.122Z", + "updated_at": "2017-05-08T16:00:16.646Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4400", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test13", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4401", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4401, + "name": "test13", + "build_path": "/root/review-app/builds/4401", + "retry_path": "/root/review-app/builds/4401/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.164Z", + "updated_at": "2017-05-08T16:00:16.318Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4401", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test14", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4402", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4402, + "name": "test14", + "build_path": "/root/review-app/builds/4402", + "retry_path": "/root/review-app/builds/4402/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.230Z", + "updated_at": "2017-05-08T16:00:15.968Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4402", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test15", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4403", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4403, + "name": "test15", + "build_path": "/root/review-app/builds/4403", + "retry_path": "/root/review-app/builds/4403/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.265Z", + "updated_at": "2017-05-08T16:00:15.708Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4403", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "test16", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4404", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4404, + "name": "test16", + "build_path": "/root/review-app/builds/4404", + "retry_path": "/root/review-app/builds/4404/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.304Z", + "updated_at": "2017-05-08T16:00:15.475Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4404", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }], + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/pipelines/131#stage3", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "path": "/root/review-app/pipelines/131#stage3", + "dropdown_path": "/root/review-app/pipelines/131/stage.json?stage=stage3" + }, { + "name": "stage4", + "title": "stage4: skipped", + "groups": [{ + "name": "stage4", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4405", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4405, + "name": "stage4", + "build_path": "/root/review-app/builds/4405", + "retry_path": "/root/review-app/builds/4405/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.353Z", + "updated_at": "2017-05-08T16:00:18.791Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4405", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "stage4_2", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4406", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4406, + "name": "stage4_2", + "build_path": "/root/review-app/builds/4406", + "retry_path": "/root/review-app/builds/4406/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.423Z", + "updated_at": "2017-05-08T16:00:19.007Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4406", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "stage4_3", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4407", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4407, + "name": "stage4_3", + "build_path": "/root/review-app/builds/4407", + "retry_path": "/root/review-app/builds/4407/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.478Z", + "updated_at": "2017-05-08T16:00:19.253Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4407", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }, { + "name": "stage4_4", + "size": 1, + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4408", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "jobs": [{ + "id": 4408, + "name": "stage4_4", + "build_path": "/root/review-app/builds/4408", + "retry_path": "/root/review-app/builds/4408/retry", + "playable": false, + "created_at": "2017-05-08T14:57:41.533Z", + "updated_at": "2017-05-08T16:00:19.354Z", + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/builds/4408", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + } + }] + }], + "status": { + "icon": "icon_status_skipped", + "text": "skipped", + "label": "skipped", + "group": "skipped", + "has_details": true, + "details_path": "/root/review-app/pipelines/131#stage4", + "favicon": "/assets/ci_favicons/dev/favicon_status_skipped-9a4245cf6584dcf7ae1aad5c0e649784f19770cbf4deea9ec3416889083cbc42.ico" + }, + "path": "/root/review-app/pipelines/131#stage4", + "dropdown_path": "/root/review-app/pipelines/131/stage.json?stage=stage4" + }], + "artifacts": [], + "manual_actions": [{ + "name": "production", + "path": "/root/review-app/builds/4384/play", + "playable": false + }, { + "name": "stop_review", + "path": "/root/review-app/builds/4375/play", + "playable": true + }] + }, + "flags": { + "latest": true, + "triggered": false, + "stuck": false, + "yaml_errors": false, + "retryable": true, + "cancelable": true + }, + "ref": { + "name": "master", + "path": "/root/review-app/tree/master", + "tag": false, + "branch": true + }, + "commit": { + "id": "d0672d958e785724593b6cbab15cacc1dbe4d65d", + "short_id": "d0672d95", + "title": "Update .gitlab-ci.yml", + "created_at": "2017-05-08T15:57:38.000+01:00", + "parent_ids": ["6431f5413b4465249444032f29256994c7bedc1a"], + "message": "Update .gitlab-ci.yml", + "author_name": "Root", + "author_email": "admin@example.com", + "authored_date": "2017-05-08T15:57:38.000+01:00", + "committer_name": "Root", + "committer_email": "admin@example.com", + "committed_date": "2017-05-08T15:57:38.000+01:00", + "author": { + "name": "Root", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon", + "web_url": "http://localhost:3000/root" + }, + "author_gravatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon", + "commit_url": "http://localhost:3000/root/review-app/commit/d0672d958e785724593b6cbab15cacc1dbe4d65d", + "commit_path": "/root/review-app/commit/d0672d958e785724593b6cbab15cacc1dbe4d65d" + }, + "retry_path": "/root/review-app/pipelines/131/retry", + "cancel_path": "/root/review-app/pipelines/131/cancel", + "created_at": "2017-05-08T14:57:39.781Z", + "updated_at": "2017-05-08T16:00:20.052Z" +} diff --git a/app/assets/javascripts/builds/services/build_service.js b/app/assets/javascripts/builds/services/build_service.js index 0bf51580496..b06c4d2f391 100644 --- a/app/assets/javascripts/builds/services/build_service.js +++ b/app/assets/javascripts/builds/services/build_service.js @@ -11,4 +11,9 @@ export default class BuildService { get() { return this.build.get(); } + + // eslint-disable-next-line class-methods-use-this + postAction(endpoint) { + return Vue.http.post(endpoint, {}, { emulateJSON: true }); + } } diff --git a/app/assets/javascripts/builds/stores/build_store.js b/app/assets/javascripts/builds/stores/build_store.js index cdae19c0c89..d37c357aa2d 100644 --- a/app/assets/javascripts/builds/stores/build_store.js +++ b/app/assets/javascripts/builds/stores/build_store.js @@ -3,7 +3,46 @@ export default class BuildStore { this.state = {}; } + reset() { + this.state = {}; + } + storeBuild(build = {}) { this.state.build = build; + + this.state.status = build.details.status; + this.state.commit = build.commit; + + // TODO verfiy if this data is correct + this.state.user = build.user; + + this.state.actions = []; + + if (build.retry_path) { + this.state.actions.push({ + label: 'Retry', + path: build.retry_path, + cssClass: 'btn btn-inverted-secondary', + type: 'button', + }); + } + + if (build.cancel_path) { + this.state.actions.push({ + label: 'Cancel', + path: build.cancel_path, + cssClass: 'btn btn-danger', + type: 'button', + }); + } + + if (this.state.status.group === 'failed') { + this.state.actions.push({ + label: 'New issue', + path: '', // TODO + cssClass: 'btn btn-new btn-inverted', + type: 'link', + }); + } } } diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index 0a952bc4dc7..d34a23a803d 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -1,4 +1,7 @@ <script> +import ciIconBadge from './ci_badge_link.vue'; +import timeagoTooltip from './time_ago_tooltip.vue'; + /** * Renders header component for job and pipeline page based on UI mockups * https://gitlab-org.gitlab.io/gitlab-design/hosted/27724-large-build-logs-spec-previews/ @@ -17,12 +20,12 @@ export default { type: String, required: true, }, - itemID: { - type: String, + itemId: { + type: Number, required: true, }, time: { - type: Number, + type: String, required: true, }, user: { @@ -36,12 +39,67 @@ export default { }, components: { - ciIconBage, + ciIconBadge, + timeagoTooltip, + }, + + methods: { + onClickAction(action) { + this.$emit('postAction', action); + }, }, }; </script> <template> - <div class="page-content-header"> + <header class="page-content-header top-area"> + <section class="header-main-content"> + + <ci-icon-badge :status="status" /> + + <strong> + {{itemName}} #{{itemId}} + </strong> + + triggered + + <timeago-tooltip :time="time" /> + + by + + <a> + <!-- replace this with avatar component bryce is making --> + user image + </a> + + <a + :href="user.web_url" + :title="user.email" + class="js-user-link commit-committer-link" + ref="tooltip"> + {{user.name}} + </a> + </section> + + <section + class="header-action-button nav-controls" + v-if="actions.length"> + <template v-for="action in actions"> + <a + v-if="action.type === 'link'" + :href="action.path" + :class="action.cssClass"> + {{action.label}} + </a> + + <button + v-else="action.type === 'button'" + @click="onClickAction(action)" + :class="action.cssClass" + type="button"> + {{action.label}} + </button> - </div> + </template> + </section> + </header> </template> diff --git a/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue b/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue new file mode 100644 index 00000000000..caf9414edaa --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue @@ -0,0 +1,65 @@ +<script> +import tooltipMixin from '../mixins/tooltip'; +import '../../lib/utils/datetime_utility'; + +/** + * Port of ruby helper time_ago_with_tooltip + */ + +export default { + props: { + time: { + type: String, + required: true, + }, + + tooltipPlacement: { + type: String, + required: false, + default: 'top', + }, + + shortFormat: { + type: Boolean, + required: false, + default: false, + }, + + htmlClass: { + type: String, + required: false, + default: '', + }, + }, + + mixins: [tooltipMixin], + + computed: { + cssClass() { + const className = this.shortFormat ? 'js-short-timeago' : 'js-timeago'; + return `${className} ${this.htmlClass}`; + }, + + tooltipTitle() { + return gl.utils.formatDate(this.time); + }, + + timeFormated() { + const timeago = gl.utils.getTimeago(); + + return timeago.format(this.finishedTime); + }, + }, +}; +</script> +<template> + <time + :class="cssClass" + class="js-timeago js-timeago-render" + :title="tooltipTitle" + :data-placement="tooltipPlacement" + data-container="body" + ref="tooltip"> + {{timeFormated}} + </time> +</template> diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js index d5f87588c28..992ef7ddad7 100644 --- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js +++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js @@ -22,3 +22,15 @@ Vue.http.interceptors.push((request, next) => { } next(); }); + +// delete +import mockData from '../builds/mock'; + +Vue.http.interceptors.push((request, next) => { + if (request.url === '/root/review-app/builds/4449.json') { + next(request.respondWith(JSON.stringify(mockData), { + status: 200, + })); + } + next(); +}); diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index 4a284247143..ed14034251d 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -1,142 +1,141 @@ -.container-fluid { - .ci-status { - padding: 2px 7px 4px; - margin-right: 10px; - border: 1px solid $gray-darker; - white-space: nowrap; - border-radius: 4px; - - &:hover, - &:focus { - text-decoration: none; - } - svg { - height: 13px; - width: 13px; - position: relative; - top: 2px; - overflow: visible; - } +.ci-status { + padding: 2px 7px 4px; + margin-right: 10px; + border: 1px solid $gray-darker; + white-space: nowrap; + border-radius: 4px; - &.ci-failed, - &.ci-failed_with_warnings { - color: $red-500; - border-color: $red-500; + &:hover, + &:focus { + text-decoration: none; + } - &:not(span):hover { - background-color: $red-50; - color: $red-600; - border-color: $red-600; + svg { + height: 13px; + width: 13px; + position: relative; + top: 2px; + overflow: visible; + } - svg { - fill: $red-600; - } - } + &.ci-failed, + &.ci-failed_with_warnings { + color: $red-500; + border-color: $red-500; + + &:not(span):hover { + background-color: $red-50; + color: $red-600; + border-color: $red-600; svg { - fill: $red-500; + fill: $red-600; } } - &.ci-success, - &.ci-success_with_warnings { - color: $green-600; - border-color: $green-500; + svg { + fill: $red-500; + } + } - &:not(span):hover { - background-color: $green-50; - color: $green-700; - border-color: $green-600; + &.ci-success, + &.ci-success_with_warnings { + color: $green-600; + border-color: $green-500; - svg { - fill: $green-600; - } - } + &:not(span):hover { + background-color: $green-50; + color: $green-700; + border-color: $green-600; svg { - fill: $green-500; + fill: $green-600; } } - &.ci-canceled, - &.ci-disabled { - color: $gl-text-color; - border-color: $gl-text-color; + svg { + fill: $green-500; + } + } - &:not(span):hover { - background-color: rgba($gl-text-color, .07); - } + &.ci-canceled, + &.ci-disabled { + color: $gl-text-color; + border-color: $gl-text-color; - svg { - fill: $gl-text-color; - } + &:not(span):hover { + background-color: rgba($gl-text-color, .07); } - &.ci-pending { - color: $orange-600; - border-color: $orange-500; + svg { + fill: $gl-text-color; + } + } - &:not(span):hover { - background-color: $orange-50; - color: $orange-700; - border-color: $orange-600; + &.ci-pending { + color: $orange-600; + border-color: $orange-500; - svg { - fill: $orange-600; - } - } + &:not(span):hover { + background-color: $orange-50; + color: $orange-700; + border-color: $orange-600; svg { - fill: $orange-500; + fill: $orange-600; } } - &.ci-info, - &.ci-running { - color: $blue-500; - border-color: $blue-500; + svg { + fill: $orange-500; + } + } - &:not(span):hover { - background-color: $blue-50; - color: $blue-600; - border-color: $blue-600; + &.ci-info, + &.ci-running { + color: $blue-500; + border-color: $blue-500; - svg { - fill: $blue-600; - } - } + &:not(span):hover { + background-color: $blue-50; + color: $blue-600; + border-color: $blue-600; svg { - fill: $blue-500; + fill: $blue-600; } } - &.ci-created, - &.ci-skipped { - color: $gl-text-color-secondary; - border-color: $gl-text-color-secondary; + svg { + fill: $blue-500; + } + } - &:not(span):hover { - background-color: rgba($gl-text-color-secondary, .07); - } + &.ci-created, + &.ci-skipped { + color: $gl-text-color-secondary; + border-color: $gl-text-color-secondary; - svg { - fill: $gl-text-color-secondary; - } + &:not(span):hover { + background-color: rgba($gl-text-color-secondary, .07); } - &.ci-manual { - color: $gl-text-color; - border-color: $gl-text-color; + svg { + fill: $gl-text-color-secondary; + } + } - &:not(span):hover { - background-color: rgba($gl-text-color, .07); - } + &.ci-manual { + color: $gl-text-color; + border-color: $gl-text-color; - svg { - fill: $gl-text-color; - } + &:not(span):hover { + background-color: rgba($gl-text-color, .07); + } + + svg { + fill: $gl-text-color; } } } diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 30d69a0656c..561d4b77c57 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -6,7 +6,7 @@ = page_specific_javascript_bundle_tag("common_vue") = page_specific_javascript_bundle_tag("build") -#js-build-app-vue{ class: container_class } +#js-build-app-vue{ data: { endpoint: namespace_project_build_path(@project.namespace, @project, @build, :json ), class: container_class} } .build-page = render "header" diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js new file mode 100644 index 00000000000..55fd745cef4 --- /dev/null +++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js @@ -0,0 +1,82 @@ +import Vue from 'vue'; +import headerCi from '~/vue_shared/components/header_ci_component.vue'; + +fdescribe('Header CI Component', () => { + let HeaderCi; + let vm; + let props; + + beforeEach(() => { + HeaderCi = Vue.extend(headerCi); + + props = { + status: { + group: 'failed', + icon: 'ci-status-failed', + label: 'failed', + text: 'failed', + details_path: 'path', + }, + itemName: 'job', + itemId: 123, + time: '2017-05-08T14:57:39.781Z', + user: { + web_url: 'path', + name: 'Foo', + username: 'foobar', + email: 'foo@bar.com', + avatar_url: 'link', + }, + actions: [ + { + label: 'Retry', + path: 'path', + type: 'button', + cssClass: 'btn', + }, + { + label: 'Go', + path: 'path', + type: 'link', + cssClass: 'link', + }, + ], + }; + + vm = new HeaderCi({ + propsData: props, + }).$mount(); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('should render status badge', () => { + expect(vm.$el.querySelector('.ci-failed')).toBeDefined(); + expect(vm.$el.querySelector('.ci-status-icon-failed svg')).toBeDefined(); + expect( + vm.$el.querySelector('.ci-failed').getAttribute('href'), + ).toEqual(props.status.details_path); + }); + + it('should render item name and id', () => { + expect(vm.$el.querySelector('strong').textContent.trim()).toEqual('job #123'); + }); + + it('should render timeago date', () => { + expect(vm.$el.querySelector('time')).toBeDefined(); + }); + + it('should render user icon and name', () => { + expect(vm.$el.querySelector('.js-user-link').textContent.trim()).toEqual(props.user.name); + }); + + it('should render provided actions', () => { + expect(vm.$el.querySelector('.btn').tagName).toEqual('BUTTON'); + expect(vm.$el.querySelector('.btn').textContent.trim()).toEqual(props.actions[0].label); + expect(vm.$el.querySelector('.link').tagName).toEqual('A'); + expect(vm.$el.querySelector('.link').textContent.trim()).toEqual(props.actions[1].label); + expect(vm.$el.querySelector('.link').getAttribute('href')).toEqual(props.actions[0].path); + }); +}); diff --git a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js new file mode 100644 index 00000000000..303e37138e9 --- /dev/null +++ b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js @@ -0,0 +1,65 @@ +import Vue from 'vue'; +import timeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import '~/lib/utils/datetime_utility'; + +describe('Time ago with tooltip component', () => { + let TimeagoTooltip; + let vm; + + beforeEach(() => { + TimeagoTooltip = Vue.extend(timeagoTooltip); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('should render timeago with a bootstrap tooltip', () => { + vm = new TimeagoTooltip({ + propsData: { + time: '2017-05-08T14:57:39.781Z', + }, + }).$mount(); + + expect(vm.$el.tagName).toEqual('TIME'); + expect(vm.$el.classList.contains('js-timeago')).toEqual(true); + expect( + vm.$el.getAttribute('data-original-title'), + ).toEqual(gl.utils.formatDate('2017-05-08T14:57:39.781Z')); + expect(vm.$el.getAttribute('data-placement')).toEqual('top'); + expect(vm.$el.textContent.trim()).toEqual(vm.$options.computed.timeFormated()); + }); + + it('should render tooltip placed in bottom', () => { + vm = new TimeagoTooltip({ + propsData: { + time: '2017-05-08T14:57:39.781Z', + tooltipPlacement: 'bottom', + }, + }).$mount(); + + expect(vm.$el.getAttribute('data-placement')).toEqual('bottom'); + }); + + it('should render short format class', () => { + vm = new TimeagoTooltip({ + propsData: { + time: '2017-05-08T14:57:39.781Z', + shortFormat: true, + }, + }).$mount(); + + expect(vm.$el.classList.contains('js-short-timeago')).toEqual(true); + }); + + it('should render provided html class', () => { + vm = new TimeagoTooltip({ + propsData: { + time: '2017-05-08T14:57:39.781Z', + htmlClass: 'foo', + }, + }).$mount(); + + expect(vm.$el.classList.contains('foo')).toEqual(true); + }); +}); |