diff options
Diffstat (limited to 'app/assets/javascripts/pipelines/pipelines.js')
-rw-r--r-- | app/assets/javascripts/pipelines/pipelines.js | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/app/assets/javascripts/pipelines/pipelines.js b/app/assets/javascripts/pipelines/pipelines.js new file mode 100644 index 00000000000..6eea4812f33 --- /dev/null +++ b/app/assets/javascripts/pipelines/pipelines.js @@ -0,0 +1,288 @@ +import Vue from 'vue'; +import Visibility from 'visibilityjs'; +import PipelinesService from './services/pipelines_service'; +import eventHub from './event_hub'; +import PipelinesTableComponent from '../vue_shared/components/pipelines_table'; +import TablePaginationComponent from '../vue_shared/components/table_pagination'; +import EmptyState from './components/empty_state.vue'; +import ErrorState from './components/error_state.vue'; +import NavigationTabs from './components/navigation_tabs'; +import NavigationControls from './components/nav_controls'; +import Poll from '../lib/utils/poll'; + +export default { + props: { + store: { + type: Object, + required: true, + }, + }, + + components: { + 'gl-pagination': TablePaginationComponent, + 'pipelines-table-component': PipelinesTableComponent, + 'empty-state': EmptyState, + 'error-state': ErrorState, + 'navigation-tabs': NavigationTabs, + 'navigation-controls': NavigationControls, + }, + + data() { + const pipelinesData = document.querySelector('#pipelines-list-vue').dataset; + + return { + endpoint: pipelinesData.endpoint, + cssClass: pipelinesData.cssClass, + helpPagePath: pipelinesData.helpPagePath, + newPipelinePath: pipelinesData.newPipelinePath, + canCreatePipeline: pipelinesData.canCreatePipeline, + allPath: pipelinesData.allPath, + pendingPath: pipelinesData.pendingPath, + runningPath: pipelinesData.runningPath, + finishedPath: pipelinesData.finishedPath, + branchesPath: pipelinesData.branchesPath, + tagsPath: pipelinesData.tagsPath, + hasCi: pipelinesData.hasCi, + ciLintPath: pipelinesData.ciLintPath, + state: this.store.state, + apiScope: 'all', + pagenum: 1, + isLoading: false, + hasError: false, + isMakingRequest: false, + }; + }, + + computed: { + canCreatePipelineParsed() { + return gl.utils.convertPermissionToBoolean(this.canCreatePipeline); + }, + + scope() { + const scope = gl.utils.getParameterByName('scope'); + return scope === null ? 'all' : scope; + }, + + shouldRenderErrorState() { + return this.hasError && !this.isLoading; + }, + + /** + * The empty state should only be rendered when the request is made to fetch all pipelines + * and none is returned. + * + * @return {Boolean} + */ + shouldRenderEmptyState() { + return !this.isLoading && + !this.hasError && + !this.state.pipelines.length && + (this.scope === 'all' || this.scope === null); + }, + + /** + * When a specific scope does not have pipelines we render a message. + * + * @return {Boolean} + */ + shouldRenderNoPipelinesMessage() { + return !this.isLoading && + !this.hasError && + !this.state.pipelines.length && + this.scope !== 'all' && + this.scope !== null; + }, + + shouldRenderTable() { + return !this.hasError && + !this.isLoading && this.state.pipelines.length; + }, + + /** + * Pagination should only be rendered when there is more than one page. + * + * @return {Boolean} + */ + shouldRenderPagination() { + return !this.isLoading && + this.state.pipelines.length && + this.state.pageInfo.total > this.state.pageInfo.perPage; + }, + + hasCiEnabled() { + return this.hasCi !== undefined; + }, + + paths() { + return { + allPath: this.allPath, + pendingPath: this.pendingPath, + finishedPath: this.finishedPath, + runningPath: this.runningPath, + branchesPath: this.branchesPath, + tagsPath: this.tagsPath, + }; + }, + + pageParameter() { + return gl.utils.getParameterByName('page') || this.pagenum; + }, + + scopeParameter() { + return gl.utils.getParameterByName('scope') || this.apiScope; + }, + }, + + created() { + this.service = new PipelinesService(this.endpoint); + + const poll = new Poll({ + resource: this.service, + method: 'getPipelines', + data: { page: this.pageParameter, scope: this.scopeParameter }, + successCallback: this.successCallback, + errorCallback: this.errorCallback, + notificationCallback: this.setIsMakingRequest, + }); + + if (!Visibility.hidden()) { + this.isLoading = true; + poll.makeRequest(); + } + + Visibility.change(() => { + if (!Visibility.hidden()) { + poll.restart(); + } else { + poll.stop(); + } + }); + + eventHub.$on('refreshPipelines', this.fetchPipelines); + }, + + beforeUpdate() { + if (this.state.pipelines.length && + this.$children && + !this.isMakingRequest && + !this.isLoading) { + this.store.startTimeAgoLoops.call(this, Vue); + } + }, + + beforeDestroyed() { + eventHub.$off('refreshPipelines'); + }, + + methods: { + /** + * Will change the page number and update the URL. + * + * @param {Number} pageNumber desired page to go to. + */ + change(pageNumber) { + const param = gl.utils.setParamInURL('page', pageNumber); + + gl.utils.visitUrl(param); + return param; + }, + + fetchPipelines() { + if (!this.isMakingRequest) { + this.isLoading = true; + + this.service.getPipelines({ scope: this.scopeParameter, page: this.pageParameter }) + .then(response => this.successCallback(response)) + .catch(() => this.errorCallback()); + } + }, + + successCallback(resp) { + const response = { + headers: resp.headers, + body: resp.json(), + }; + + this.store.storeCount(response.body.count); + this.store.storePipelines(response.body.pipelines); + this.store.storePagination(response.headers); + + this.isLoading = false; + }, + + errorCallback() { + this.hasError = true; + this.isLoading = false; + }, + + setIsMakingRequest(isMakingRequest) { + this.isMakingRequest = isMakingRequest; + }, + }, + + template: ` + <div :class="cssClass"> + + <div + class="top-area scrolling-tabs-container inner-page-scroll-tabs" + v-if="!isLoading && !shouldRenderEmptyState"> + <div class="fade-left"> + <i class="fa fa-angle-left" aria-hidden="true"></i> + </div> + <div class="fade-right"> + <i class="fa fa-angle-right" aria-hidden="true"></i> + </div> + <navigation-tabs + :scope="scope" + :count="state.count" + :paths="paths" /> + + <navigation-controls + :new-pipeline-path="newPipelinePath" + :has-ci-enabled="hasCiEnabled" + :help-page-path="helpPagePath" + :ciLintPath="ciLintPath" + :can-create-pipeline="canCreatePipelineParsed " /> + </div> + + <div class="content-list pipelines"> + + <div + class="realtime-loading" + v-if="isLoading"> + <i + class="fa fa-spinner fa-spin" + aria-hidden="true" /> + </div> + + <empty-state + v-if="shouldRenderEmptyState" + :help-page-path="helpPagePath" /> + + <error-state v-if="shouldRenderErrorState" /> + + <div + class="blank-state blank-state-no-icon" + v-if="shouldRenderNoPipelinesMessage"> + <h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2> + </div> + + <div + class="table-holder" + v-if="shouldRenderTable"> + + <pipelines-table-component + :pipelines="state.pipelines" + :service="service"/> + </div> + + <gl-pagination + v-if="shouldRenderPagination" + :pagenum="pagenum" + :change="change" + :count="state.count.all" + :pageInfo="state.pageInfo"/> + </div> + </div> + `, +}; |