First iteration
Create shared folder for vue common files Update paths Second iteration - refactor main component to be 100% reusable between the 3 tables
This commit is contained in:
parent
fd46fb1cd9
commit
037b4fe939
|
@ -0,0 +1,61 @@
|
|||
/* eslint-disable no-new */
|
||||
/* global Vue, VueResource */
|
||||
|
||||
//= require vue
|
||||
//= require vue-resource
|
||||
//= require ./pipelines_store
|
||||
//= require ./pipelines_service
|
||||
//= require vue_shared/components/commit
|
||||
//= require vue_shared/vue_resource_interceptor
|
||||
//= require vue_shared/components/pipelines_table
|
||||
|
||||
/**
|
||||
* Commits View > Pipelines Tab > Pipelines Table.
|
||||
*
|
||||
* Renders Pipelines table in pipelines tab in the commits show view.
|
||||
*
|
||||
* Uses `pipelines-table-component` to render Pipelines table with an API call.
|
||||
* Endpoint is provided in HTML and passed as scope.
|
||||
* We need a store to make the request and store the received environemnts.
|
||||
*
|
||||
* Necessary SVG in the table are provided as props. This should be refactored
|
||||
* as soon as we have Webpack and can load them directly into JS files.
|
||||
*/
|
||||
(() => {
|
||||
window.gl = window.gl || {};
|
||||
gl.Commits = gl.Commits || {};
|
||||
|
||||
if (gl.Commits.PipelinesTableView) {
|
||||
gl.Commits.PipelinesTableView.$destroy(true);
|
||||
}
|
||||
|
||||
gl.Commits.PipelinesTableView = new Vue({
|
||||
|
||||
el: document.querySelector('#commit-pipeline-table-view'),
|
||||
|
||||
/**
|
||||
* Accesses the DOM to provide the needed data.
|
||||
* Returns the necessary props to render `pipelines-table-component` component.
|
||||
*
|
||||
* @return {Object} Props for `pipelines-table-component`
|
||||
*/
|
||||
data() {
|
||||
const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset;
|
||||
|
||||
return {
|
||||
scope: pipelinesTableData.pipelinesData,
|
||||
store: new CommitsPipelineStore(),
|
||||
service: new PipelinesService(),
|
||||
svgs: pipelinesTableData,
|
||||
};
|
||||
},
|
||||
|
||||
components: {
|
||||
'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
|
||||
},
|
||||
|
||||
template: `
|
||||
<pipelines-table-component :scope='scope' :store='store' :svgs='svgs'></pipelines-table-component>
|
||||
`,
|
||||
});
|
||||
});
|
|
@ -0,0 +1,77 @@
|
|||
/* globals Vue */
|
||||
/* eslint-disable no-unused-vars, no-param-reassign */
|
||||
|
||||
/**
|
||||
* Pipelines service.
|
||||
*
|
||||
* Used to fetch the data used to render the pipelines table.
|
||||
* Used Vue.Resource
|
||||
*/
|
||||
|
||||
window.gl = window.gl || {};
|
||||
gl.pipelines = gl.pipelines || {};
|
||||
|
||||
class PipelinesService {
|
||||
constructor(root) {
|
||||
Vue.http.options.root = root;
|
||||
|
||||
this.pipelines = Vue.resource(root);
|
||||
|
||||
Vue.http.interceptors.push((request, next) => {
|
||||
// needed in order to not break the tests.
|
||||
if ($.rails) {
|
||||
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the root param provided when the class is initialized, will
|
||||
* make a GET request.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
all() {
|
||||
return this.pipelines.get();
|
||||
}
|
||||
}
|
||||
|
||||
gl.pipelines.PipelinesService = PipelinesService;
|
||||
|
||||
// const pageValues = (headers) => {
|
||||
// const normalized = gl.utils.normalizeHeaders(headers);
|
||||
//
|
||||
// const paginationInfo = {
|
||||
// perPage: +normalized['X-PER-PAGE'],
|
||||
// page: +normalized['X-PAGE'],
|
||||
// total: +normalized['X-TOTAL'],
|
||||
// totalPages: +normalized['X-TOTAL-PAGES'],
|
||||
// nextPage: +normalized['X-NEXT-PAGE'],
|
||||
// previousPage: +normalized['X-PREV-PAGE'],
|
||||
// };
|
||||
//
|
||||
// return paginationInfo;
|
||||
// };
|
||||
|
||||
// gl.PipelineStore = class {
|
||||
// fetchDataLoop(Vue, pageNum, url, apiScope) {
|
||||
// const goFetch = () =>
|
||||
// this.$http.get(`${url}?scope=${apiScope}&page=${pageNum}`)
|
||||
// .then((response) => {
|
||||
// const pageInfo = pageValues(response.headers);
|
||||
// this.pageInfo = Object.assign({}, this.pageInfo, pageInfo);
|
||||
//
|
||||
// const res = JSON.parse(response.body);
|
||||
// this.count = Object.assign({}, this.count, res.count);
|
||||
// this.pipelines = Object.assign([], this.pipelines, res);
|
||||
//
|
||||
// this.pageRequest = false;
|
||||
// }, () => {
|
||||
// this.pageRequest = false;
|
||||
// return new Flash('Something went wrong on our end.');
|
||||
// });
|
||||
//
|
||||
// goFetch();
|
||||
// }
|
||||
// };
|
|
@ -0,0 +1,30 @@
|
|||
/* global gl, Flash */
|
||||
/* eslint-disable no-param-reassign, no-underscore-dangle */
|
||||
/*= require vue_realtime_listener/index.js */
|
||||
|
||||
/**
|
||||
* Pipelines' Store for commits view.
|
||||
*
|
||||
* Used to store the Pipelines rendered in the commit view in the pipelines table.
|
||||
*
|
||||
* TODO: take care of timeago instances in here
|
||||
*/
|
||||
|
||||
(() => {
|
||||
const CommitPipelineStore = {
|
||||
state: {},
|
||||
|
||||
create() {
|
||||
this.state.pipelines = [];
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
storePipelines(pipelines = []) {
|
||||
this.state.pipelines = pipelines;
|
||||
return pipelines;
|
||||
},
|
||||
};
|
||||
|
||||
return CommitPipelineStore;
|
||||
})();
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
/*= require timeago */
|
||||
/*= require lib/utils/text_utility */
|
||||
/*= require vue_common_component/commit */
|
||||
/*= require vue_shared/components/commit */
|
||||
/*= require ./environment_actions */
|
||||
/*= require ./environment_external_url */
|
||||
/*= require ./environment_stop */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* global Vue, VueResource, gl */
|
||||
/*= require vue_common_component/commit */
|
||||
/*= require vue_shared/components/commit */
|
||||
/*= require vue_pagination/index */
|
||||
/*= require vue-resource
|
||||
/*= require boards/vue_resource_interceptor */
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
/* eslint-disable no-param-reassign, no-new */
|
||||
/* global Vue */
|
||||
/* global PipelinesService */
|
||||
/* global Flash */
|
||||
|
||||
//= require vue_pipelines_index/status.js.es6
|
||||
//= require vue_pipelines_index/pipeline_url.js.es6
|
||||
//= require vue_pipelines_index/stage.js.es6
|
||||
//= require vue_pipelines_index/pipeline_actions.js.es6
|
||||
//= require vue_pipelines_index/time_ago.js.es6
|
||||
//= require vue_pipelines_index/pipelines.js.es6
|
||||
|
||||
(() => {
|
||||
window.gl = window.gl || {};
|
||||
gl.pipelines = gl.pipelines || {};
|
||||
|
||||
gl.pipelines.PipelinesTableComponent = Vue.component('pipelines-table-component', {
|
||||
|
||||
props: {
|
||||
|
||||
/**
|
||||
* Stores the Pipelines to render.
|
||||
* It's passed as a prop to allow different stores to use this Component.
|
||||
* Different API calls can result in different responses, using a custom
|
||||
* store allows us to use the same pipeline component.
|
||||
*/
|
||||
store: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
/**
|
||||
* Will be used to fetch the needed data.
|
||||
* This component is used in different and therefore different API calls
|
||||
* to different endpoints will be made. To guarantee this is a reusable
|
||||
* component, the endpoint must be provided.
|
||||
*/
|
||||
endpoint: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove this. Find a better way to do this. don't want to provide this 3 times.
|
||||
*/
|
||||
svgs: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
'commit-component': gl.CommitComponent,
|
||||
runningPipeline: gl.VueRunningPipeline,
|
||||
pipelineActions: gl.VuePipelineActions,
|
||||
'vue-stage': gl.VueStage,
|
||||
pipelineUrl: gl.VuePipelineUrl,
|
||||
pipelineHead: gl.VuePipelineHead,
|
||||
statusScope: gl.VueStatusScope,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
state: this.store.state,
|
||||
isLoading: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* If provided, returns the commit tag.
|
||||
*
|
||||
* @returns {Object|Undefined}
|
||||
*/
|
||||
commitAuthor() {
|
||||
if (this.pipeline &&
|
||||
this.pipeline.commit &&
|
||||
this.pipeline.commit.author) {
|
||||
return this.pipeline.commit.author;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* If provided, returns the commit tag.
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
commitTag() {
|
||||
if (this.model.last_deployment &&
|
||||
this.model.last_deployment.tag) {
|
||||
return this.model.last_deployment.tag;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* If provided, returns the commit ref.
|
||||
*
|
||||
* @returns {Object|Undefined}
|
||||
*/
|
||||
commitRef() {
|
||||
if (this.pipeline.ref) {
|
||||
return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => {
|
||||
if (prop === 'url') {
|
||||
accumulator.path = this.pipeline.ref[prop];
|
||||
} else {
|
||||
accumulator[prop] = this.pipeline.ref[prop];
|
||||
}
|
||||
return accumulator;
|
||||
}, {});
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* If provided, returns the commit url.
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
commitUrl() {
|
||||
if (this.pipeline.commit &&
|
||||
this.pipeline.commit.commit_path) {
|
||||
return this.pipeline.commit.commit_path;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* If provided, returns the commit short sha.
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
commitShortSha() {
|
||||
if (this.pipeline.commit &&
|
||||
this.pipeline.commit.short_id) {
|
||||
return this.pipeline.commit.short_id;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* If provided, returns the commit title.
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
commitTitle() {
|
||||
if (this.pipeline.commit &&
|
||||
this.pipeline.commit.title) {
|
||||
return this.pipeline.commit.title;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Figure this out!
|
||||
*/
|
||||
author(pipeline) {
|
||||
if (!pipeline.commit) return { avatar_url: '', web_url: '', username: '' };
|
||||
if (pipeline.commit.author) return pipeline.commit.author;
|
||||
return {
|
||||
avatar_url: pipeline.commit.author_gravatar_url,
|
||||
web_url: `mailto:${pipeline.commit.author_email}`,
|
||||
username: pipeline.commit.author_name,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Figure this out
|
||||
*/
|
||||
match(string) {
|
||||
return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase());
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* When the component is created the service to fetch the data will be
|
||||
* initialized with the correct endpoint.
|
||||
*
|
||||
* A request to fetch the pipelines will be made.
|
||||
* In case of a successfull response we will store the data in the provided
|
||||
* store, in case of a failed response we need to warn the user.
|
||||
*
|
||||
*/
|
||||
created() {
|
||||
gl.pipelines.pipelinesService = new PipelinesService(this.endpoint);
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
return gl.pipelines.pipelinesService.all()
|
||||
.then(resp => resp.json())
|
||||
.then((json) => {
|
||||
this.store.storePipelines(json);
|
||||
this.isLoading = false;
|
||||
}).catch(() => {
|
||||
this.isLoading = false;
|
||||
new Flash('An error occurred while fetching the pipelines.', 'alert');
|
||||
});
|
||||
},
|
||||
// this need to be reusable between the 3 tables :/
|
||||
template: `
|
||||
<div>
|
||||
<div class="pipelines realtime-loading" v-if='isLoading'>
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="blank-state blank-state-no-icon"
|
||||
v-if="!isLoading && state.pipelines.length === 0">
|
||||
<h2 class="blank-state-title js-blank-state-title">
|
||||
You don't have any pipelines.
|
||||
</h2>
|
||||
Put get started with pipelines button here!!!
|
||||
</div>
|
||||
|
||||
<div class="table-holder" v-if='!isLoading state.pipelines.length > 0'>
|
||||
<table class="table ci-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="pipeline-status">Status</th>
|
||||
<th class="pipeline-info">Pipeline</th>
|
||||
<th class="pipeline-commit">Commit</th>
|
||||
<th class="pipeline-stages">Stages</th>
|
||||
<th class="pipeline-date"></th>
|
||||
<th class="pipeline-actions hidden-xs"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="commit" v-for='pipeline in state.pipelines'>
|
||||
<status-scope
|
||||
:pipeline='pipeline'
|
||||
:match='match'
|
||||
:svgs='svgs'>
|
||||
</status-scope>
|
||||
|
||||
<pipeline-url :pipeline='pipeline'></pipeline-url>
|
||||
|
||||
<td>
|
||||
<commit-component
|
||||
:tag="commitTag"
|
||||
:commit-ref="commitRef"
|
||||
:commit-url="commitUrl"
|
||||
:short-sha="commitShortSha"
|
||||
:title="commitTitle"
|
||||
:author="commitAuthor"
|
||||
:commit-icon-svg="commitIconSvg">
|
||||
</commit-component>
|
||||
</td>
|
||||
|
||||
<td class="stage-cell">
|
||||
<div class="stage-container dropdown js-mini-pipeline-graph" v-for='stage in pipeline.details.stages'>
|
||||
<vue-stage :stage='stage' :svgs='svgs' :match='match'></vue-stage>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<time-ago :pipeline='pipeline' :svgs='svgs'></time-ago>
|
||||
|
||||
<pipeline-actions :pipeline='pipeline' :svgs='svgs'></pipeline-actions>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,10 @@
|
|||
/* eslint-disable func-names, prefer-arrow-callback, no-unused-vars */
|
||||
/* global Vue */
|
||||
|
||||
Vue.http.interceptors.push((request, next) => {
|
||||
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
|
||||
|
||||
next(function (response) {
|
||||
Vue.activeResources -= 1;
|
||||
});
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
%div
|
||||
- if pipelines.blank?
|
||||
%div
|
||||
.nothing-here-block No pipelines to show
|
||||
- else
|
||||
.table-holder.pipelines
|
||||
%table.table.ci-table.js-pipeline-table
|
||||
%thead
|
||||
%th.pipeline-status Status
|
||||
%th.pipeline-info Pipeline
|
||||
%th.pipeline-commit Commit
|
||||
%th.pipeline-stages Stages
|
||||
%th.pipeline-date
|
||||
%th.pipeline-actions
|
||||
= render pipelines, commit_sha: true, stage: true, allow_retry: true, show_commit: false
|
|
@ -2,4 +2,29 @@
|
|||
|
||||
= render 'commit_box'
|
||||
= render 'ci_menu'
|
||||
= render 'pipelines_list', pipelines: @pipelines
|
||||
|
||||
- content_for :page_specific_javascripts do
|
||||
= page_specific_javascript_tag("commit/pipelines_bundle.js")
|
||||
|
||||
#commit-pipeline-table-view{ data: { pipelines_data: pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id)}}
|
||||
.pipeline-svgs{ data: { "commit_icon_svg" => custom_icon("icon_commit"),
|
||||
"icon_status_canceled" => custom_icon("icon_status_canceled"),
|
||||
"icon_status_running" => custom_icon("icon_status_running"),
|
||||
"icon_status_skipped" => custom_icon("icon_status_skipped"),
|
||||
"icon_status_created" => custom_icon("icon_status_created"),
|
||||
"icon_status_pending" => custom_icon("icon_status_pending"),
|
||||
"icon_status_success" => custom_icon("icon_status_success"),
|
||||
"icon_status_failed" => custom_icon("icon_status_failed"),
|
||||
"icon_status_warning" => custom_icon("icon_status_warning"),
|
||||
"stage_icon_status_canceled" => custom_icon("icon_status_canceled_borderless"),
|
||||
"stage_icon_status_running" => custom_icon("icon_status_running_borderless"),
|
||||
"stage_icon_status_skipped" => custom_icon("icon_status_skipped_borderless"),
|
||||
"stage_icon_status_created" => custom_icon("icon_status_created_borderless"),
|
||||
"stage_icon_status_pending" => custom_icon("icon_status_pending_borderless"),
|
||||
"stage_icon_status_success" => custom_icon("icon_status_success_borderless"),
|
||||
"stage_icon_status_failed" => custom_icon("icon_status_failed_borderless"),
|
||||
"stage_icon_status_warning" => custom_icon("icon_status_warning_borderless"),
|
||||
"icon_play" => custom_icon("icon_play"),
|
||||
"icon_timer" => custom_icon("icon_timer"),
|
||||
"icon_status_manual" => custom_icon("icon_status_manual"),
|
||||
} }
|
||||
|
|
|
@ -105,6 +105,7 @@ module Gitlab
|
|||
config.assets.precompile << "merge_conflicts/merge_conflicts_bundle.js"
|
||||
config.assets.precompile << "boards/test_utils/simulate_drag.js"
|
||||
config.assets.precompile << "environments/environments_bundle.js"
|
||||
config.assets.precompile << "commit/pipelines_bundle.js"
|
||||
config.assets.precompile << "blob_edit/blob_edit_bundle.js"
|
||||
config.assets.precompile << "snippet/snippet_bundle.js"
|
||||
config.assets.precompile << "terminal/terminal_bundle.js"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//= require vue_common_component/commit
|
||||
//= require vue_shared/components/commit
|
||||
|
||||
describe('Commit component', () => {
|
||||
let props;
|
||||
|
|
Loading…
Reference in New Issue