Commit 037b4fe9 authored by Filipa Lacerda's avatar Filipa Lacerda

First iteration

Create shared folder for vue common files
Update paths

Second iteration - refactor main component to be 100% reusable between the 3 tables
parent fd46fb1c
/* 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>
`,
});
});
/* 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();
// }
// };
/* 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 */
......
/* 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 */
......
/* 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>
`,
});
})();
/* 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;
});
});
%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"
......
//= require vue_common_component/commit
//= require vue_shared/components/commit
describe('Commit component', () => {
let props;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment