diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-01 18:13:56 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-01 18:13:56 +0000 |
commit | 1769b59b9fd05325e3016b1a53a82ae6cf56adb5 (patch) | |
tree | a7e81e0c94fce5cc033e802d53d0c08d833fc87d /app | |
parent | 05003789d95f2f5d28a2f018d9e1b51ad7ab983c (diff) | |
download | gitlab-ce-1769b59b9fd05325e3016b1a53a82ae6cf56adb5.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
20 files changed, 292 insertions, 102 deletions
diff --git a/app/assets/javascripts/issuable/components/csv_export_modal.vue b/app/assets/javascripts/issuable/components/csv_export_modal.vue index 78987a5c629..7bdd55ddda3 100644 --- a/app/assets/javascripts/issuable/components/csv_export_modal.vue +++ b/app/assets/javascripts/issuable/components/csv_export_modal.vue @@ -12,19 +12,23 @@ export default { }, inject: { issuableType: { - default: '', - }, - issuableCount: { - default: 0, + default: ISSUABLE_TYPE.issues, }, email: { default: '', }, + }, + props: { exportCsvPath: { + type: String, + required: false, default: '', }, - }, - props: { + issuableCount: { + type: Number, + required: false, + default: 0, + }, modalId: { type: String, required: true, diff --git a/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue b/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue index 14474d44e10..fb4d5aca2f5 100644 --- a/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue +++ b/app/assets/javascripts/issuable/components/csv_import_export_buttons.vue @@ -53,6 +53,18 @@ export default { default: false, }, }, + props: { + exportCsvPath: { + type: String, + required: false, + default: '', + }, + issuableCount: { + type: Number, + required: false, + default: undefined, + }, + }, computed: { exportModalId() { return `${this.issuableType}-export-modal`; @@ -105,7 +117,12 @@ export default { > </gl-dropdown> </gl-button-group> - <csv-export-modal v-if="showExportButton" :modal-id="exportModalId" /> + <csv-export-modal + v-if="showExportButton" + :modal-id="exportModalId" + :export-csv-path="exportCsvPath" + :issuable-count="issuableCount" + /> <csv-import-modal v-if="showImportButton" :modal-id="importModalId" /> </div> </template> diff --git a/app/assets/javascripts/issuable/init_csv_import_export_buttons.js b/app/assets/javascripts/issuable/init_csv_import_export_buttons.js index 5a720b89d33..83163e3c478 100644 --- a/app/assets/javascripts/issuable/init_csv_import_export_buttons.js +++ b/app/assets/javascripts/issuable/init_csv_import_export_buttons.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import { parseBoolean } from '~/lib/utils/common_utils'; -import ImportExportButtons from './components/csv_import_export_buttons.vue'; +import CsvImportExportButtons from './components/csv_import_export_buttons.vue'; export default () => { const el = document.querySelector('.js-csv-import-export-buttons'); @@ -28,9 +28,7 @@ export default () => { showExportButton: parseBoolean(showExportButton), showImportButton: parseBoolean(showImportButton), issuableType, - issuableCount, email, - exportCsvPath, importCsvIssuesPath, containerClass, canEdit: parseBoolean(canEdit), @@ -39,7 +37,12 @@ export default () => { showLabel, }, render(h) { - return h(ImportExportButtons); + return h(CsvImportExportButtons, { + props: { + exportCsvPath, + issuableCount: parseInt(issuableCount, 10), + }, + }); }, }); }; diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js index 1f707bc955f..97d50dde9f7 100644 --- a/app/assets/javascripts/issuable_bulk_update_sidebar.js +++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js @@ -46,14 +46,11 @@ export default class IssuableBulkUpdateSidebar { this.$bulkEditSubmitBtn.on('click', () => this.prepForSubmit()); this.$checkAllContainer.on('click', () => this.updateFormState()); - issueableEventHub.$on('issuables:updateBulkEdit', () => { - // Danger! Strong coupling ahead! - // The bulk update sidebar and its dropdowns look for checkboxes, and get data on which issue - // is selected by inspecting the DOM. Ideally, we would pass the selected issuable IDs and their properties - // explicitly, but this component is used in too many places right now to refactor straight away. - - this.updateFormState(); - }); + // The event hub connects this bulk update logic with `issues_list_app.vue`. + // We can remove it once we've refactored the issues list page bulk edit sidebar to Vue. + // https://gitlab.com/gitlab-org/gitlab/-/issues/325874 + issueableEventHub.$on('issuables:enableBulkEdit', () => this.toggleBulkEdit(null, true)); + issueableEventHub.$on('issuables:updateBulkEdit', () => this.updateFormState()); } initDropdowns() { @@ -110,7 +107,7 @@ export default class IssuableBulkUpdateSidebar { } toggleBulkEdit(e, enable) { - e.preventDefault(); + e?.preventDefault(); issueableEventHub.$emit('issuables:toggleBulkEdit', enable); diff --git a/app/assets/javascripts/issuable_list/components/issuable_tabs.vue b/app/assets/javascripts/issuable_list/components/issuable_tabs.vue index 57da030e22e..6bc621b52e6 100644 --- a/app/assets/javascripts/issuable_list/components/issuable_tabs.vue +++ b/app/assets/javascripts/issuable_list/components/issuable_tabs.vue @@ -26,6 +26,9 @@ export default { isTabActive(tabName) { return tabName === this.currentTab; }, + isTabCountNumeric(tab) { + return Number.isInteger(this.tabCounts[tab.name]); + }, }, }; </script> @@ -44,9 +47,13 @@ export default { > <template #title> <span :title="tab.titleTooltip">{{ tab.title }}</span> - <gl-badge v-if="tabCounts" variant="neutral" size="sm" class="gl-tab-counter-badge">{{ - tabCounts[tab.name] - }}</gl-badge> + <gl-badge + v-if="isTabCountNumeric(tab)" + variant="neutral" + size="sm" + class="gl-tab-counter-badge" + >{{ tabCounts[tab.name] }}</gl-badge + > </template> </gl-tab> </gl-tabs> diff --git a/app/assets/javascripts/issues_list/components/issues_list_app.vue b/app/assets/javascripts/issues_list/components/issues_list_app.vue index d7af388c893..1f26c65475b 100644 --- a/app/assets/javascripts/issues_list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues_list/components/issues_list_app.vue @@ -1,9 +1,10 @@ <script> -import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; +import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { toNumber } from 'lodash'; import createFlash from '~/flash'; +import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue'; import IssuableList from '~/issuable_list/components/issuable_list_root.vue'; -import { IssuableStatus } from '~/issue_show/constants'; +import { IssuableListTabs, IssuableStates } from '~/issuable_list/constants'; import { CREATED_DESC, PAGE_SIZE, @@ -19,13 +20,18 @@ import IssueCardTimeInfo from './issue_card_time_info.vue'; export default { CREATED_DESC, + IssuableListTabs, PAGE_SIZE, sortOptions, sortParams, i18n: { + calendarLabel: __('Subscribe to calendar'), reorderError: __('An error occurred while reordering issues.'), + rssLabel: __('Subscribe to RSS feed'), }, components: { + CsvImportExportButtons, + GlButton, GlIcon, IssuableList, IssueCardTimeInfo, @@ -35,15 +41,33 @@ export default { GlTooltip: GlTooltipDirective, }, inject: { + calendarPath: { + default: '', + }, + canBulkUpdate: { + default: false, + }, endpoint: { default: '', }, + exportCsvPath: { + default: '', + }, fullPath: { default: '', }, issuesPath: { default: '', }, + newIssuePath: { + default: '', + }, + rssPath: { + default: '', + }, + showNewIssueLink: { + default: false, + }, }, data() { const orderBy = getParameterByName('order_by'); @@ -53,20 +77,31 @@ export default { ); return { - currentPage: toNumber(getParameterByName('page')) || 1, + exportCsvPathWithQuery: this.getExportCsvPathWithQuery(), filters: sortParams[sortKey] || {}, isLoading: false, issues: [], + page: toNumber(getParameterByName('page')) || 1, showBulkEditSidebar: false, sortKey: sortKey || CREATED_DESC, + state: getParameterByName('state') || IssuableStates.Opened, totalIssues: 0, }; }, computed: { + tabCounts() { + return Object.values(IssuableStates).reduce( + (acc, state) => ({ + ...acc, + [state]: this.state === state ? this.totalIssues : undefined, + }), + {}, + ); + }, urlParams() { return { - page: this.currentPage, - state: IssuableStatus.Open, + page: this.page, + state: this.state, ...this.filters, }; }, @@ -85,23 +120,24 @@ export default { eventHub.$off('issuables:toggleBulkEdit'); }, methods: { - fetchIssues(pageToFetch) { + fetchIssues() { this.isLoading = true; return axios .get(this.endpoint, { params: { - page: pageToFetch || this.currentPage, + page: this.page, per_page: this.$options.PAGE_SIZE, - state: IssuableStatus.Open, + state: this.state, with_labels_details: true, ...this.filters, }, }) .then(({ data, headers }) => { - this.currentPage = Number(headers['x-page']); + this.page = Number(headers['x-page']); this.totalIssues = Number(headers['x-total']); this.issues = data.map((issue) => convertObjectPropsToCamelCase(issue, { deep: true })); + this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery(); }) .catch(() => { createFlash({ message: __('An error occurred while loading issues') }); @@ -110,6 +146,9 @@ export default { this.isLoading = false; }); }, + getExportCsvPathWithQuery() { + return `${this.exportCsvPath}${window.location.search}`; + }, handleUpdateLegacyBulkEdit() { // If "select all" checkbox was checked, wait for all checkboxes // to be checked before updating IssuableBulkUpdateSidebar class @@ -117,8 +156,19 @@ export default { eventHub.$emit('issuables:updateBulkEdit'); }); }, + handleBulkUpdateClick() { + eventHub.$emit('issuables:enableBulkEdit'); + }, + handleClickTab(state) { + if (this.state !== state) { + this.page = 1; + } + this.state = state; + this.fetchIssues(); + }, handlePageChange(page) { - this.fetchIssues(page); + this.page = page; + this.fetchIssues(); }, handleReorder({ newIndex, oldIndex }) { const issueToMove = this.issues[oldIndex]; @@ -171,25 +221,60 @@ export default { :sort-options="$options.sortOptions" :initial-sort-by="sortKey" :issuables="issues" - :tabs="[]" - current-tab="" + :tabs="$options.IssuableListTabs" + :current-tab="state" + :tab-counts="tabCounts" :issuables-loading="isLoading" :is-manual-ordering="isManualOrdering" :show-bulk-edit-sidebar="showBulkEditSidebar" :show-pagination-controls="true" :total-items="totalIssues" - :current-page="currentPage" - :previous-page="currentPage - 1" - :next-page="currentPage + 1" + :current-page="page" + :previous-page="page - 1" + :next-page="page + 1" :url-params="urlParams" + @click-tab="handleClickTab" @page-change="handlePageChange" @reorder="handleReorder" @sort="handleSort" @update-legacy-bulk-edit="handleUpdateLegacyBulkEdit" > + <template #nav-actions> + <gl-button + v-gl-tooltip + :href="rssPath" + icon="rss" + :title="$options.i18n.rssLabel" + :aria-label="$options.i18n.rssLabel" + /> + <gl-button + v-gl-tooltip + :href="calendarPath" + icon="calendar" + :title="$options.i18n.calendarLabel" + :aria-label="$options.i18n.calendarLabel" + /> + <csv-import-export-buttons + class="gl-mr-3" + :export-csv-path="exportCsvPathWithQuery" + :issuable-count="totalIssues" + /> + <gl-button + v-if="canBulkUpdate" + :disabled="showBulkEditSidebar" + @click="handleBulkUpdateClick" + > + {{ __('Edit issues') }} + </gl-button> + <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm"> + {{ __('New issue') }} + </gl-button> + </template> + <template #timeframe="{ issuable = {} }"> <issue-card-time-info :issue="issuable" /> </template> + <template #statistics="{ issuable = {} }"> <li v-if="issuable.mergeRequestsCount" diff --git a/app/assets/javascripts/issues_list/index.js b/app/assets/javascripts/issues_list/index.js index b55ebf8dbdb..0ba20e43050 100644 --- a/app/assets/javascripts/issues_list/index.js +++ b/app/assets/javascripts/issues_list/index.js @@ -73,12 +73,23 @@ export function initIssuesListApp() { } const { + calendarPath, + canBulkUpdate, + canEdit, + email, endpoint, + exportCsvPath, fullPath, hasBlockedIssuesFeature, hasIssuableHealthStatusFeature, hasIssueWeightsFeature, + importCsvIssuesPath, issuesPath, + maxAttachmentSize, + newIssuePath, + projectImportJiraPath, + rssPath, + showNewIssueLink, } = el.dataset; return new Vue({ @@ -87,12 +98,26 @@ export function initIssuesListApp() { // issue is fixed upstream in https://github.com/vuejs/vue-apollo/pull/1153 apolloProvider: {}, provide: { + calendarPath, + canBulkUpdate: parseBoolean(canBulkUpdate), endpoint, fullPath, hasBlockedIssuesFeature: parseBoolean(hasBlockedIssuesFeature), hasIssuableHealthStatusFeature: parseBoolean(hasIssuableHealthStatusFeature), hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature), issuesPath, + newIssuePath, + rssPath, + showNewIssueLink: parseBoolean(showNewIssueLink), + // For CsvImportExportButtons component + canEdit: parseBoolean(canEdit), + email, + exportCsvPath, + importCsvIssuesPath, + maxAttachmentSize, + projectImportJiraPath, + showExportButton: true, + showImportButton: true, }, render: (createComponent) => createComponent(IssuesListApp), }); diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 73eadfe3cbe..fb257228597 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -681,6 +681,19 @@ export const roundOffFloat = (number, precision = 0) => { }; /** + * Method to round values to the nearest half (0.5) + * + * Eg; roundToNearestHalf(3.141592) = 3, roundToNearestHalf(3.41592) = 3.5 + * + * Refer to spec/javascripts/lib/utils/common_utils_spec.js for + * more supported examples. + * + * @param {Float} number + * @returns {Float|Number} + */ +export const roundToNearestHalf = (num) => Math.round(num * 2).toFixed() / 2; + +/** * Method to round down values with decimal places * with provided precision. * diff --git a/app/controllers/groups/email_campaigns_controller.rb b/app/controllers/groups/email_campaigns_controller.rb index 8c3c289dc99..e085cefbc24 100644 --- a/app/controllers/groups/email_campaigns_controller.rb +++ b/app/controllers/groups/email_campaigns_controller.rb @@ -19,12 +19,14 @@ class Groups::EmailCampaignsController < Groups::ApplicationController def track_click data = { namespace_id: group.id, - track: @track, + track: @track.to_s, series: @series, subject_line: subject_line(@track, @series) } - ::Gitlab::Tracking.self_describing_event(EMAIL_CAMPAIGNS_SCHEMA_URL, data: data) + context = SnowplowTracker::SelfDescribingJson.new(EMAIL_CAMPAIGNS_SCHEMA_URL, data) + + ::Gitlab::Tracking.event(self.class.name, 'click', context: [context]) end def redirect_link diff --git a/app/graphql/types/board_type.rb b/app/graphql/types/board_type.rb index f33f3f5e537..42d8eecc366 100644 --- a/app/graphql/types/board_type.rb +++ b/app/graphql/types/board_type.rb @@ -20,6 +20,12 @@ module Types field :hide_closed_list, type: GraphQL::BOOLEAN_TYPE, null: true, description: 'Whether or not closed list is hidden.' + field :created_at, Types::TimeType, null: false, + description: 'Timestamp of when the board was created.' + + field :updated_at, Types::TimeType, null: false, + description: 'Timestamp of when the board was last updated.' + field :lists, Types::BoardListType.connection_type, null: true, diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 0a9965496b8..7e3c64ff736 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -163,6 +163,25 @@ module IssuesHelper } end + def issues_list_data(project, current_user, finder) + { + calendar_path: url_for(safe_params.merge(calendar_url_options)), + can_bulk_update: can?(current_user, :admin_issue, project).to_s, + can_edit: can?(current_user, :admin_project, project).to_s, + email: current_user&.notification_email, + endpoint: expose_path(api_v4_projects_issues_path(id: project.id)), + export_csv_path: export_csv_project_issues_path(project), + full_path: project.full_path, + import_csv_issues_path: import_csv_namespace_project_issues_path, + issues_path: project_issues_path(project), + max_attachment_size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes), + new_issue_path: new_project_issue_path(project, issue: { assignee_id: finder.assignee.try(:id), milestone_id: finder.milestones.first.try(:id) }), + project_import_jira_path: project_import_jira_path(project), + rss_path: url_for(safe_params.merge(rss_url_options)), + show_new_issue_link: show_new_issue_link?(project).to_s + } + end + # Overridden in EE def scoped_labels_available?(parent) false diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index e81986d4453..1d3242ca44a 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -65,6 +65,13 @@ module TabHelper # # When `TreeController#index` is requested # # => '<li>Hello</li>' # + # # Paths, controller and actions can be used at the same time + # nav_link(path: 'tree#show', controller: 'admin/appearances') { "Hello" } + # + # nav_link(path: 'foo#bar', controller: 'tree') { "Hello" } + # nav_link(path: 'foo#bar', controller: 'tree', action: 'show') { "Hello" } + # nav_link(path: 'foo#bar', action: 'show') { "Hello" } + # # Returns a list item element String def nav_link(options = {}, &block) klass = active_nav_link?(options) ? 'active' : '' @@ -85,34 +92,12 @@ module TabHelper def active_nav_link?(options) return false if options[:unless]&.call - if path = options.delete(:path) - unless path.respond_to?(:each) - path = [path] - end - - path.any? do |single_path| - current_path?(single_path) - end - elsif page = options.delete(:page) - unless page.respond_to?(:each) - page = [page] - end - - page.any? do |single_page| - current_page?(single_page) - end - else - c = options.delete(:controller) - a = options.delete(:action) + controller = options.delete(:controller) + action = options.delete(:action) - if c && a - # When given both options, make sure BOTH are true - current_controller?(*c) && current_action?(*a) - else - # Otherwise check EITHER option - current_controller?(*c) || current_action?(*a) - end - end + route_matches_paths?(options.delete(:path)) || + route_matches_pages?(options.delete(:page)) || + route_matches_controllers_and_or_actions?(controller, action) end def current_path?(path) @@ -127,4 +112,26 @@ module TabHelper 'active' end end + + private + + def route_matches_paths?(paths) + Array(paths).compact.any? do |single_path| + current_path?(single_path) + end + end + + def route_matches_pages?(pages) + Array(pages).compact.any? do |single_page| + current_page?(single_page) + end + end + + def route_matches_controllers_and_or_actions?(controller, action) + if controller && action + current_controller?(*controller) && current_action?(*action) + else + current_controller?(*controller) || current_action?(*action) + end + end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 76b97041166..dce9168a9a4 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -73,7 +73,14 @@ module Ci return unless has_environment? strong_memoize(:persisted_environment) do - Environment.find_by(name: expanded_environment_name, project: project) + # This code path has caused N+1s in the past, since environments are only indirectly + # associated to builds and pipelines; see https://gitlab.com/gitlab-org/gitlab/-/issues/326445 + # We therefore batch-load them to prevent dormant N+1s until we found a proper solution. + BatchLoader.for(expanded_environment_name).batch(key: project_id) do |names, loader, args| + Environment.where(name: names, project: args[:key]).find_each do |environment| + loader.call(environment.name, environment) + end + end end end diff --git a/app/models/ci/pipeline_artifact.rb b/app/models/ci/pipeline_artifact.rb index f538a4cd808..9dfe4252e95 100644 --- a/app/models/ci/pipeline_artifact.rb +++ b/app/models/ci/pipeline_artifact.rb @@ -57,3 +57,5 @@ module Ci end end end + +Ci::PipelineArtifact.prepend_ee_mod diff --git a/app/models/concerns/ci/artifactable.rb b/app/models/concerns/ci/artifactable.rb index 66a02f49030..0d29955268f 100644 --- a/app/models/concerns/ci/artifactable.rb +++ b/app/models/concerns/ci/artifactable.rb @@ -42,3 +42,5 @@ module Ci end end end + +Ci::Artifactable.prepend_ee_mod diff --git a/app/models/repository.rb b/app/models/repository.rb index e675f604b0a..b23b9486cfe 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -829,12 +829,6 @@ class Repository end end - def merge_to_ref(user, source_sha, merge_request, target_ref, message, first_parent_ref, allow_conflicts = false) - branch = merge_request.target_branch - - raw.merge_to_ref(user, source_sha, branch, target_ref, message, first_parent_ref, allow_conflicts) - end - def delete_refs(*ref_names) raw.delete_refs(*ref_names) end diff --git a/app/presenters/search_service_presenter.rb b/app/presenters/search_service_presenter.rb index ca5c3094509..e14446bb2f7 100644 --- a/app/presenters/search_service_presenter.rb +++ b/app/presenters/search_service_presenter.rb @@ -11,7 +11,9 @@ class SearchServicePresenter < Gitlab::View::Presenter::Delegated merge_requests: :with_web_entity_associations, epics: :with_web_entity_associations, notes: :with_web_entity_associations, - milestones: :with_web_entity_associations + milestones: :with_web_entity_associations, + commits: :with_web_entity_associations, + blobs: :with_web_entity_associations }.freeze SORT_ENABLED_SCOPES = %w(issues merge_requests).freeze diff --git a/app/services/merge_requests/merge_to_ref_service.rb b/app/services/merge_requests/merge_to_ref_service.rb index c0115e94903..e07e0c985b4 100644 --- a/app/services/merge_requests/merge_to_ref_service.rb +++ b/app/services/merge_requests/merge_to_ref_service.rb @@ -66,7 +66,13 @@ module MergeRequests end def commit - repository.merge_to_ref(current_user, source, merge_request, target_ref, commit_message, first_parent_ref, allow_conflicts) + repository.merge_to_ref(current_user, + source_sha: source, + branch: merge_request.target_branch, + target_ref: target_ref, + message: commit_message, + first_parent_ref: first_parent_ref, + allow_conflicts: allow_conflicts) rescue Gitlab::Git::PreReceiveError, Gitlab::Git::CommandError => error raise MergeError, error.message end diff --git a/app/views/admin/abuse_reports/index.html.haml b/app/views/admin/abuse_reports/index.html.haml index daa766429e0..2af31b93c0b 100644 --- a/app/views/admin/abuse_reports/index.html.haml +++ b/app/views/admin/abuse_reports/index.html.haml @@ -19,13 +19,13 @@ %table.table.responsive-table %thead.d-none.d-md-table-header-group %tr - %th User - %th Reported by - %th.wide Message - %th Action + %th= _('User') + %th= _('Reported by') + %th.wide= _('Message') + %th= _('Action') = render @abuse_reports = paginate @abuse_reports, theme: 'gitlab' - else .empty-state .text-center - %h4 There are no abuse reports! #{emoji_icon('tada')} + %h4= _("There are no abuse reports!") + emoji_icon('tada') diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 3b35bd7544a..9b043ea3c47 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -13,32 +13,24 @@ issues_path: project_issues_path(@project), project_path: @project.full_path } } -- if project_issues(@project).exists? +- if Feature.enabled?(:vue_issues_list, @project) + .js-issues-list{ data: issues_list_data(@project, current_user, finder) } + - if @can_bulk_update + = render 'shared/issuable/bulk_update_sidebar', type: :issues +- elsif project_issues(@project).exists? .top-area = render 'shared/issuable/nav', type: :issues = render "projects/issues/nav_btns" + = render 'shared/issuable/search_bar', type: :issues - - if Feature.enabled?(:vue_issues_list, @project) - - data_endpoint = local_assigns.fetch(:data_endpoint, expose_path(api_v4_projects_issues_path(id: @project.id))) - .js-issues-list{ data: { endpoint: data_endpoint, - full_path: @project.full_path, - has_blocked_issues_feature: Gitlab.ee? && @project.feature_available?(:blocked_issues).to_s, - has_issuable_health_status_feature: Gitlab.ee? && @project.feature_available?(:issuable_health_status).to_s, - has_issue_weights_feature: Gitlab.ee? && @project.feature_available?(:issue_weights).to_s, - issues_path: project_issues_path(@project) } } - - if @can_bulk_update - = render 'shared/issuable/bulk_update_sidebar', type: :issues - - else - = render 'shared/issuable/search_bar', type: :issues + - if @can_bulk_update + = render 'shared/issuable/bulk_update_sidebar', type: :issues - - if @can_bulk_update - = render 'shared/issuable/bulk_update_sidebar', type: :issues - - .issues-holder - = render 'issues' - - if new_issue_email - .issuable-footer.text-center - .js-issueable-by-email{ data: { initial_email: new_issue_email, issuable_type: issuable_type, emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), quick_actions_help_path: help_page_path('user/project/quick_actions'), markdown_help_path: help_page_path('user/markdown'), reset_path: new_issuable_address_project_path(@project, issuable_type: issuable_type) } } + .issues-holder + = render 'issues' + - if new_issue_email + .issuable-footer.text-center + .js-issueable-by-email{ data: { initial_email: new_issue_email, issuable_type: issuable_type, emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), quick_actions_help_path: help_page_path('user/project/quick_actions'), markdown_help_path: help_page_path('user/markdown'), reset_path: new_issuable_address_project_path(@project, issuable_type: issuable_type) } } - else - new_project_issue_button_path = @project.archived? ? false : new_project_issue_path(@project) = render 'shared/empty_states/issues', new_project_issue_button_path: new_project_issue_button_path, show_import_button: true |