summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/jobs/components/table/jobs_table_app.vue')
-rw-r--r--app/assets/javascripts/jobs/components/table/jobs_table_app.vue92
1 files changed, 83 insertions, 9 deletions
diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
index 864e322eecd..3ea50dfb7a3 100644
--- a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
+++ b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
@@ -1,26 +1,34 @@
<script>
import { GlAlert, GlSkeletonLoader, GlIntersectionObserver, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
+import createFlash from '~/flash';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import JobsFilteredSearch from '../filtered_search/jobs_filtered_search.vue';
import eventHub from './event_hub';
import GetJobs from './graphql/queries/get_jobs.query.graphql';
import JobsTable from './jobs_table.vue';
import JobsTableEmptyState from './jobs_table_empty_state.vue';
import JobsTableTabs from './jobs_table_tabs.vue';
+import { RAW_TEXT_WARNING } from './constants';
export default {
i18n: {
errorMsg: __('There was an error fetching the jobs for your project.'),
loadingAriaLabel: __('Loading'),
},
+ filterSearchBoxStyles:
+ 'gl-my-0 gl-p-5 gl-bg-gray-10 gl-text-gray-900 gl-border-gray-100 gl-border-b',
components: {
GlAlert,
GlSkeletonLoader,
+ JobsFilteredSearch,
JobsTable,
JobsTableEmptyState,
JobsTableTabs,
GlIntersectionObserver,
GlLoadingIcon,
},
+ mixins: [glFeatureFlagMixin()],
inject: {
fullPath: {
default: '',
@@ -35,10 +43,11 @@ export default {
};
},
update(data) {
- const { jobs: { nodes: list = [], pageInfo = {} } = {} } = data.project || {};
+ const { jobs: { nodes: list = [], pageInfo = {}, count } = {} } = data.project || {};
return {
list,
pageInfo,
+ count,
};
},
error() {
@@ -54,19 +63,52 @@ export default {
hasError: false,
isAlertDismissed: false,
scope: null,
- firstLoad: true,
+ infiniteScrollingTriggered: false,
+ filterSearchTriggered: false,
+ count: 0,
};
},
computed: {
+ loading() {
+ return this.$apollo.queries.jobs.loading;
+ },
shouldShowAlert() {
return this.hasError && !this.isAlertDismissed;
},
+ // Show when on All tab with no jobs
+ // Show only when not loading and filtered search has not been triggered
+ // So we don't show empty state when results are empty on a filtered search
showEmptyState() {
- return this.jobs.list.length === 0 && !this.scope;
+ return (
+ this.jobs.list.length === 0 && !this.scope && !this.loading && !this.filterSearchTriggered
+ );
},
hasNextPage() {
return this.jobs?.pageInfo?.hasNextPage;
},
+ showLoadingSpinner() {
+ return this.loading && this.infiniteScrollingTriggered;
+ },
+ showSkeletonLoader() {
+ return this.loading && !this.showLoadingSpinner;
+ },
+ showFilteredSearch() {
+ return this.glFeatures?.jobsTableVueSearch && !this.scope;
+ },
+ jobsCount() {
+ return this.jobs.count;
+ },
+ },
+ watch: {
+ // this watcher ensures that the count on the all tab
+ // is not updated when switching to the finished tab
+ jobsCount(newCount, oldCount) {
+ if (this.scope) {
+ this.count = oldCount;
+ } else {
+ this.count = newCount;
+ }
+ },
},
mounted() {
eventHub.$on('jobActionPerformed', this.handleJobAction);
@@ -79,16 +121,38 @@ export default {
this.$apollo.queries.jobs.refetch({ statuses: this.scope });
},
fetchJobsByStatus(scope) {
- this.firstLoad = true;
+ this.infiniteScrollingTriggered = false;
this.scope = scope;
this.$apollo.queries.jobs.refetch({ statuses: scope });
},
+ filterJobsBySearch(filters) {
+ this.infiniteScrollingTriggered = false;
+ this.filterSearchTriggered = true;
+
+ // Eventually there will be more tokens available
+ // this code is written to scale for those tokens
+ filters.forEach((filter) => {
+ // Raw text input in filtered search does not have a type
+ // when a user enters raw text we alert them that it is
+ // not supported and we do not make an additional API call
+ if (!filter.type) {
+ createFlash({
+ message: RAW_TEXT_WARNING,
+ type: 'warning',
+ });
+ }
+
+ if (filter.type === 'status') {
+ this.$apollo.queries.jobs.refetch({ statuses: filter.value.data });
+ }
+ });
+ },
fetchMoreJobs() {
- this.firstLoad = false;
+ if (!this.loading) {
+ this.infiniteScrollingTriggered = true;
- if (!this.$apollo.queries.jobs.loading) {
this.$apollo.queries.jobs.fetchMore({
variables: {
fullPath: this.fullPath,
@@ -113,9 +177,19 @@ export default {
{{ $options.i18n.errorMsg }}
</gl-alert>
- <jobs-table-tabs @fetchJobsByStatus="fetchJobsByStatus" />
+ <jobs-table-tabs
+ :all-jobs-count="count"
+ :loading="loading"
+ @fetchJobsByStatus="fetchJobsByStatus"
+ />
+
+ <jobs-filtered-search
+ v-if="showFilteredSearch"
+ :class="$options.filterSearchBoxStyles"
+ @filterJobsBySearch="filterJobsBySearch"
+ />
- <div v-if="$apollo.loading && firstLoad" class="gl-mt-5">
+ <div v-if="showSkeletonLoader" class="gl-mt-5">
<gl-skeleton-loader :width="1248" :height="73">
<circle cx="748.031" cy="37.7193" r="15.0307" />
<circle cx="787.241" cy="37.7193" r="15.0307" />
@@ -138,7 +212,7 @@ export default {
<gl-intersection-observer v-if="hasNextPage" @appear="fetchMoreJobs">
<gl-loading-icon
- v-if="$apollo.loading"
+ v-if="showLoadingSpinner"
size="md"
:aria-label="$options.i18n.loadingAriaLabel"
/>