diff --git a/app/assets/javascripts/vue_pipelines_index/components/empty_state.js b/app/assets/javascripts/vue_pipelines_index/components/empty_state.js new file mode 100644 index 00000000000..cfe23b65d55 --- /dev/null +++ b/app/assets/javascripts/vue_pipelines_index/components/empty_state.js @@ -0,0 +1,39 @@ +import pipelinesEmptyStateSVG from 'empty_states/icons/_pipelines_empty.svg'; + +export default { + props: { + helpPagePath: { + type: String, + required: true, + }, + }, + + data() { + return { + pipelinesEmptyStateSVG, + }; + }, + + template: ` +
+
+
+ ${pipelinesEmptyStateSVG} +
+
+ +
+
+

Build with confidence

+

+ Continous Integration can help catch bugs by running your tests automatically, + while Continuous Deployment can help you deliver code to your product environment. + + Get started with Pipelines + +

+
+
+
+ `, +}; diff --git a/app/assets/javascripts/vue_pipelines_index/components/error_state.js b/app/assets/javascripts/vue_pipelines_index/components/error_state.js new file mode 100644 index 00000000000..9071ecdea73 --- /dev/null +++ b/app/assets/javascripts/vue_pipelines_index/components/error_state.js @@ -0,0 +1,25 @@ +import pipelinesErrorStateSVG from 'empty_states/icons/_pipelines_failed.svg'; + +export default { + data() { + return { + pipelinesErrorStateSVG, + }; + }, + + template: ` +
+
+
+ ${pipelinesErrorStateSVG} +
+
+ +
+
+

The API failed to fetch the pipelines.

+
+
+
+ `, +}; diff --git a/app/assets/javascripts/vue_pipelines_index/components/nav_controls.js b/app/assets/javascripts/vue_pipelines_index/components/nav_controls.js new file mode 100644 index 00000000000..73eaaf6aa72 --- /dev/null +++ b/app/assets/javascripts/vue_pipelines_index/components/nav_controls.js @@ -0,0 +1,52 @@ +export default { + props: { + newPipelinePath: { + type: String, + required: true, + }, + + hasCIEnabled: { + type: Boolean, + required: true, + }, + + helpPagePath: { + type: String, + required: true, + }, + + ciLintPath: { + type: String, + required: true, + }, + + canCreatePipeline: { + type: Boolean, + required: true, + }, + }, + + template: ` + + `, +}; diff --git a/app/assets/javascripts/vue_pipelines_index/components/navigation_tabs.js b/app/assets/javascripts/vue_pipelines_index/components/navigation_tabs.js new file mode 100644 index 00000000000..a494d2459aa --- /dev/null +++ b/app/assets/javascripts/vue_pipelines_index/components/navigation_tabs.js @@ -0,0 +1,66 @@ +export default { + props: { + scope: { + type: String, + required: true, + }, + + count: { + type: Object, + required: true, + }, + + paths: { + type: Object, + required: true, + }, + }, + + template: ` + + `, +}; diff --git a/app/assets/javascripts/vue_pipelines_index/index.js b/app/assets/javascripts/vue_pipelines_index/index.js index c35fc63e7b5..031b78b8410 100644 --- a/app/assets/javascripts/vue_pipelines_index/index.js +++ b/app/assets/javascripts/vue_pipelines_index/index.js @@ -9,20 +9,16 @@ $(() => new Vue({ el: document.querySelector('#pipelines-list-vue'), data() { - const project = document.querySelector('.pipelines'); const store = new PipelinesStore(); return { store, - endpoint: project.dataset.url, }; }, components: { 'vue-pipelines': PipelinesComponent, }, template: ` - + `, })); diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js b/app/assets/javascripts/vue_pipelines_index/pipelines.js index 4a729153bfa..64b8be78bc1 100644 --- a/app/assets/javascripts/vue_pipelines_index/pipelines.js +++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js @@ -2,17 +2,19 @@ /* eslint-disable no-new */ import Vue from 'vue'; import '~/flash'; -import pipelinesEmptyStateSVG from 'empty_states/icons/_pipelines_empty.svg'; -import pipelinesErrorStateSVG from 'empty_states/icons/_pipelines_failed.svg'; 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'; +import ErrorState from './components/error_state'; +import NavigationTabs from './components/navigation_tabs'; +import NavigationControls from './components/nav_controls'; export default { props: { - endpoint: { - type: String, + store: { + type: Object, required: true, }, }, @@ -20,6 +22,23 @@ export default { 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 { + ...pipelinesData, + state: this.store.state, + apiScope: 'all', + pagenum: 1, + pageRequest: false, + hasError: false, + }; }, computed: { @@ -28,7 +47,8 @@ export default { }, scope() { - return gl.utils.getParameterByName('scope'); + const scope = gl.utils.getParameterByName('scope'); + return scope === null ? 'all' : scope; }, shouldRenderErrorState() { @@ -42,25 +62,28 @@ export default { * @return {Boolean} */ shouldRenderEmptyState() { - return !this.hasError && - !this.pageRequest && ( - !this.pipelines.length && (this.scope === 'all' || this.scope === null) - ); + return !this.pageRequest && + !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.pageRequest && + !this.hasError && + !this.state.pipelines.length && + this.scope !== 'all' && + this.scope !== null; }, shouldRenderTable() { return !this.hasError && - !this.pageRequest && this.pipelines.length; - }, - - /** - * Header tabs should only be rendered when we receive an error or a successfull response with - * pipelines. - * - * @return {Boolean} - */ - shouldRenderTabs() { - return !this.pageRequest && !this.hasError && this.pipelines.length; + !this.pageRequest && this.state.pipelines.length; }, /** @@ -70,24 +93,24 @@ export default { */ shouldRenderPagination() { return !this.pageRequest && - this.pipelines.length && - this.pageInfo.total > this.pageInfo.perPage; + this.state.pipelines.length && + this.state.pageInfo.total > this.state.pageInfo.perPage; }, - }, - data() { - const pipelinesData = document.querySelector('#pipelines-list-vue').dataset; + hasCIEnabled() { + return this.hasCi !== undefined; + }, - return { - ...pipelinesData, - state: this.store.state, - apiScope: 'all', - pagenum: 1, - pageRequest: false, - hasError: false, - pipelinesEmptyStateSVG, - pipelinesErrorStateSVG, - }; + paths() { + return { + allPath: this.allPath, + pendingPath: this.pendingPath, + finishedPath: this.finishedPath, + runningPath: this.runningPath, + branchesPath: this.branchesPath, + tagsPath: this.tagsPath, + }; + }, }, created() { @@ -147,144 +170,57 @@ export default { }, template: ` -
-
- - - +
-
-
-
-
- ${pipelinesEmptyStateSVG} -
-
+ -
-
-

Build with confidence

-

- Continous Integration can help catch bugs by running your tests automatically, - while Continuous Deployment can help you deliver code to your product environment. - - Get started with Pipelines - -

-
-
+ + +
+

No pipelines to show.

-
-
-
- ${pipelinesErrorStateSVG} -
-
- -
-
-

The API failed to fetch the pipelines.

-
-
-
- -
- + +
+ :count="state.count.all" + :pageInfo="state.pageInfo"/>
`, }; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 33b38ca6923..44ed6dcf33a 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -2,6 +2,7 @@ .realtime-loading { font-size: 40px; text-align: center; + margin: 0 auto; } .stage {