diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-01 21:07:03 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-01 21:07:03 +0300 |
commit | 4e3a998b8ec1351d8345863f6cad4b9bd497bd6a (patch) | |
tree | 9bab8c1089ef4bcc11bd8acdffd1f0f6f62c3e56 /app/assets/javascripts/issues/dashboard | |
parent | 08489a6db8ddff0794f9beaf770930803dc7bdca (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/issues/dashboard')
3 files changed, 170 insertions, 5 deletions
diff --git a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue index 29f6aecca03..080f1fe222c 100644 --- a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue +++ b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue @@ -1,5 +1,13 @@ <script> -import { GlButton, GlEmptyState } from '@gitlab/ui'; +import { GlButton, GlEmptyState, GlTooltipDirective } from '@gitlab/ui'; +import * as Sentry from '@sentry/browser'; +import getIssuesQuery from 'ee_else_ce/issues/dashboard/queries/get_issues.query.graphql'; +import IssueCardStatistics from 'ee_else_ce/issues/list/components/issue_card_statistics.vue'; +import IssueCardTimeInfo from 'ee_else_ce/issues/list/components/issue_card_time_info.vue'; +import { IssuableStatus } from '~/issues/constants'; +import { PAGE_SIZE } from '~/issues/list/constants'; +import { getInitialPageParams } from '~/issues/list/utils'; +import { scrollUp } from '~/lib/utils/scroll_utils'; import { __ } from '~/locale'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants'; @@ -7,38 +15,133 @@ import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/con export default { i18n: { calendarButtonText: __('Subscribe to calendar'), + closed: __('CLOSED'), + closedMoved: __('CLOSED (MOVED)'), + downvotes: __('Downvotes'), emptyStateTitle: __('Please select at least one filter to see results'), + errorFetchingIssues: __('An error occurred while loading issues'), + relatedMergeRequests: __('Related merge requests'), rssButtonText: __('Subscribe to RSS feed'), searchInputPlaceholder: __('Search or filter results...'), + upvotes: __('Upvotes'), }, IssuableListTabs, components: { GlButton, GlEmptyState, IssuableList, + IssueCardStatistics, + IssueCardTimeInfo, }, - inject: ['calendarPath', 'emptyStateSvgPath', 'isSignedIn', 'rssPath'], + directives: { + GlTooltip: GlTooltipDirective, + }, + inject: [ + 'calendarPath', + 'emptyStateSvgPath', + 'hasScopedLabelsFeature', + 'isPublicVisibilityRestricted', + 'isSignedIn', + 'rssPath', + ], data() { return { issues: [], + issuesError: null, + pageInfo: {}, + pageParams: getInitialPageParams(), searchTokens: [], sortOptions: [], state: IssuableStates.Opened, }; }, + apollo: { + issues: { + query: getIssuesQuery, + variables() { + return { + hideUsers: this.isPublicVisibilityRestricted && !this.isSignedIn, + isSignedIn: this.isSignedIn, + state: this.state, + ...this.pageParams, + }; + }, + update(data) { + return data.issues.nodes ?? []; + }, + result({ data }) { + this.pageInfo = data?.issues.pageInfo ?? {}; + }, + error(error) { + this.issuesError = this.$options.i18n.errorFetchingIssues; + Sentry.captureException(error); + }, + }, + }, + computed: { + showPaginationControls() { + return this.issues.length > 0 && (this.pageInfo.hasNextPage || this.pageInfo.hasPreviousPage); + }, + }, + methods: { + getStatus(issue) { + if (issue.state === IssuableStatus.Closed && issue.moved) { + return this.$options.i18n.closedMoved; + } + if (issue.state === IssuableStatus.Closed) { + return this.$options.i18n.closed; + } + return undefined; + }, + handleClickTab(state) { + if (this.state === state) { + return; + } + this.pageParams = getInitialPageParams(); + this.state = state; + }, + handleDismissAlert() { + this.issuesError = null; + }, + handleNextPage() { + this.pageParams = { + afterCursor: this.pageInfo.endCursor, + firstPageSize: PAGE_SIZE, + }; + scrollUp(); + }, + handlePreviousPage() { + this.pageParams = { + beforeCursor: this.pageInfo.startCursor, + lastPageSize: PAGE_SIZE, + }; + scrollUp(); + }, + }, }; </script> <template> <issuable-list + :current-tab="state" + :error="issuesError" + :has-next-page="pageInfo.hasNextPage" + :has-previous-page="pageInfo.hasPreviousPage" + :has-scoped-labels-feature="hasScopedLabelsFeature" + :issuables="issues" + :issuables-loading="$apollo.queries.issues.loading" namespace="dashboard" recent-searches-storage-key="issues" :search-input-placeholder="$options.i18n.searchInputPlaceholder" :search-tokens="searchTokens" + :show-pagination-controls="showPaginationControls" :sort-options="sortOptions" - :issuables="issues" :tabs="$options.IssuableListTabs" - :current-tab="state" + use-keyset-pagination + @click-tab="handleClickTab" + @dismiss-alert="handleDismissAlert" + @next-page="handleNextPage" + @previous-page="handlePreviousPage" > <template #nav-actions> <gl-button :href="rssPath" icon="rss"> @@ -49,6 +152,18 @@ export default { </gl-button> </template> + <template #timeframe="{ issuable = {} }"> + <issue-card-time-info :issue="issuable" /> + </template> + + <template #status="{ issuable = {} }"> + {{ getStatus(issuable) }} + </template> + + <template #statistics="{ issuable = {} }"> + <issue-card-statistics :issue="issuable" /> + </template> + <template #empty-state> <gl-empty-state :svg-path="emptyStateSvgPath" :title="$options.i18n.emptyStateTitle" /> </template> diff --git a/app/assets/javascripts/issues/dashboard/index.js b/app/assets/javascripts/issues/dashboard/index.js index a1ae3b93f7d..ed11a600d4c 100644 --- a/app/assets/javascripts/issues/dashboard/index.js +++ b/app/assets/javascripts/issues/dashboard/index.js @@ -1,4 +1,6 @@ import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; import { parseBoolean } from '~/lib/utils/common_utils'; import IssuesDashboardApp from './components/issues_dashboard_app.vue'; @@ -9,14 +11,34 @@ export function mountIssuesDashboardApp() { return null; } - const { calendarPath, emptyStateSvgPath, isSignedIn, rssPath } = el.dataset; + Vue.use(VueApollo); + + const { + calendarPath, + emptyStateSvgPath, + hasBlockedIssuesFeature, + hasIssuableHealthStatusFeature, + hasIssueWeightsFeature, + hasScopedLabelsFeature, + isPublicVisibilityRestricted, + isSignedIn, + rssPath, + } = el.dataset; return new Vue({ el, name: 'IssuesDashboardRoot', + apolloProvider: new VueApollo({ + defaultClient: createDefaultClient(), + }), provide: { calendarPath, emptyStateSvgPath, + hasBlockedIssuesFeature: parseBoolean(hasBlockedIssuesFeature), + hasIssuableHealthStatusFeature: parseBoolean(hasIssuableHealthStatusFeature), + hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature), + hasScopedLabelsFeature: parseBoolean(hasScopedLabelsFeature), + isPublicVisibilityRestricted: parseBoolean(isPublicVisibilityRestricted), isSignedIn: parseBoolean(isSignedIn), rssPath, }, diff --git a/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql new file mode 100644 index 00000000000..2e70fb1eade --- /dev/null +++ b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql @@ -0,0 +1,28 @@ +#import "~/graphql_shared/fragments/page_info.fragment.graphql" +#import "~/issues/list/queries/issue.fragment.graphql" + +query getDashboardIssues( + $hideUsers: Boolean = false + $isSignedIn: Boolean = false + $state: IssuableState + $afterCursor: String + $beforeCursor: String + $firstPageSize: Int + $lastPageSize: Int +) { + issues( + state: $state + after: $afterCursor + before: $beforeCursor + first: $firstPageSize + last: $lastPageSize + ) { + nodes { + ...IssueFragment + reference(full: true) + } + pageInfo { + ...PageInfo + } + } +} |