diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-21 12:10:27 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-21 12:10:27 +0000 |
commit | 888264e6b732927699bc2c855a8184aa2a095fbb (patch) | |
tree | 8cc728e4b0d2efe86a3549b07574d0d2bbbf603f /app | |
parent | 34ad6d995bcab9f88a236bfed15aebdad76df960 (diff) | |
download | gitlab-ce-888264e6b732927699bc2c855a8184aa2a095fbb.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
24 files changed, 217 insertions, 70 deletions
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue index d9e5878b9e3..717d4cb01ab 100644 --- a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue +++ b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue @@ -155,7 +155,7 @@ export default { <span v-if="item.active" data-testid="integration-activated-status"> <gl-icon v-gl-tooltip - name="check-circle-filled" + name="check" :size="16" class="gl-text-green-500 gl-hover-cursor-pointer gl-mr-3" :title="$options.i18n.status.enabled.tooltip" diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue index 3917e4c5fdd..97a3cdbd45a 100644 --- a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue +++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue @@ -167,7 +167,7 @@ export default { if (testAfterSubmit) { this.viewIntegration(integration, tabIndices.sendTestAlert); } else { - this.clearCurrentIntegration(type); + this.clearCurrentIntegration({ type }); } createFlash({ diff --git a/app/assets/javascripts/alerts_settings/graphql.js b/app/assets/javascripts/alerts_settings/graphql.js index 72817f636ff..15862f4034a 100644 --- a/app/assets/javascripts/alerts_settings/graphql.js +++ b/app/assets/javascripts/alerts_settings/graphql.js @@ -1,9 +1,15 @@ +import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import produce from 'immer'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; +import introspectionQueryResultData from './graphql/fragmentTypes.json'; import getCurrentIntegrationQuery from './graphql/queries/get_current_integration.query.graphql'; +const fragmentMatcher = new IntrospectionFragmentMatcher({ + introspectionQueryResultData, +}); + Vue.use(VueApollo); const resolvers = { @@ -50,7 +56,9 @@ const resolvers = { export default new VueApollo({ defaultClient: createDefaultClient(resolvers, { - cacheConfig: {}, + cacheConfig: { + fragmentMatcher, + }, assumeImmutableResults: true, }), }); diff --git a/app/assets/javascripts/alerts_settings/graphql/fragmentTypes.json b/app/assets/javascripts/alerts_settings/graphql/fragmentTypes.json new file mode 100644 index 00000000000..07dfc43aa6c --- /dev/null +++ b/app/assets/javascripts/alerts_settings/graphql/fragmentTypes.json @@ -0,0 +1 @@ +{"__schema":{"types":[{"kind":"UNION","name":"AlertManagementIntegration","possibleTypes":[{"name":"AlertManagementHttpIntegration"},{"name":"AlertManagementPrometheusIntegration"}]}]}} diff --git a/app/assets/javascripts/blob/components/table_contents.vue b/app/assets/javascripts/blob/components/table_contents.vue new file mode 100644 index 00000000000..3a0a385d494 --- /dev/null +++ b/app/assets/javascripts/blob/components/table_contents.vue @@ -0,0 +1,74 @@ +<script> +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; + +function getHeaderNumber(el) { + return parseInt(el.tagName.match(/\d+/)[0], 10); +} + +export default { + components: { + GlDropdown, + GlDropdownItem, + }, + data() { + return { + isHidden: false, + items: [], + }; + }, + mounted() { + this.blobViewer = document.querySelector('.blob-viewer[data-type="rich"]'); + + this.observer = new MutationObserver(() => { + if (this.blobViewer.classList.contains('hidden')) { + this.isHidden = true; + } else if (this.blobViewer.getAttribute('data-loaded') === 'true') { + this.isHidden = false; + this.generateHeaders(); + } + }); + + if (this.blobViewer) { + this.observer.observe(this.blobViewer, { + attributes: true, + }); + } + }, + beforeDestroy() { + if (this.observer) { + this.observer.disconnect(); + } + }, + methods: { + generateHeaders() { + const headers = [...this.blobViewer.querySelectorAll('h1,h2,h3,h4,h5,h6')]; + + if (headers.length) { + const firstHeader = getHeaderNumber(headers[0]); + + headers.forEach((el) => { + this.items.push({ + text: el.textContent.trim(), + anchor: el.querySelector('a').getAttribute('id'), + spacing: Math.max((getHeaderNumber(el) - firstHeader) * 8, 0), + }); + }); + } + }, + }, +}; +</script> + +<template> + <gl-dropdown v-if="!isHidden && items.length" icon="list-bulleted" class="gl-mr-2"> + <gl-dropdown-item v-for="(item, index) in items" :key="index" :href="`#${item.anchor}`"> + <span + :style="{ 'padding-left': `${item.spacing}px` }" + class="gl-display-block" + data-testid="tableContentsLink" + > + {{ item.text }} + </span> + </gl-dropdown-item> + </gl-dropdown> +</template> diff --git a/app/assets/javascripts/grafana_integration/components/grafana_integration.vue b/app/assets/javascripts/grafana_integration/components/grafana_integration.vue index e941318dce0..3911201457f 100644 --- a/app/assets/javascripts/grafana_integration/components/grafana_integration.vue +++ b/app/assets/javascripts/grafana_integration/components/grafana_integration.vue @@ -1,5 +1,13 @@ <script> -import { GlButton, GlFormGroup, GlFormInput, GlFormCheckbox, GlIcon, GlLink } from '@gitlab/ui'; +import { + GlButton, + GlFormGroup, + GlFormInput, + GlFormCheckbox, + GlIcon, + GlLink, + GlSprintf, +} from '@gitlab/ui'; import { mapState, mapActions } from 'vuex'; import { helpPagePath } from '~/helpers/help_page_helper'; @@ -11,6 +19,7 @@ export default { GlFormInput, GlIcon, GlLink, + GlSprintf, }, data() { return { @@ -78,13 +87,11 @@ export default { </div> <div class="settings-content"> <form> - <gl-form-checkbox - id="grafana-integration-enabled" - v-model="integrationEnabled" - class="mb-4" - > - {{ s__('GrafanaIntegration|Active') }} - </gl-form-checkbox> + <gl-form-group :label="__('Enable authentication')" label-for="grafana-integration-enabled"> + <gl-form-checkbox id="grafana-integration-enabled" v-model="integrationEnabled"> + {{ s__('GrafanaIntegration|Active') }} + </gl-form-checkbox> + </gl-form-group> <gl-form-group :label="s__('GrafanaIntegration|Grafana URL')" label-for="grafana-url" @@ -95,18 +102,27 @@ export default { <gl-form-group :label="s__('GrafanaIntegration|API token')" label-for="grafana-token"> <gl-form-input id="grafana-token" v-model="localGrafanaToken" /> <p class="form-text text-muted"> - {{ s__('GrafanaIntegration|Enter the Grafana API token.') }} - <a - href="https://grafana.com/docs/http_api/auth/#create-api-token" - target="_blank" - rel="noopener noreferrer" + <gl-sprintf + :message=" + s__('GrafanaIntegration|Enter the %{docLinkStart}Grafana API token%{docLinkEnd}.') + " > - {{ __('More information.') }} - <gl-icon name="external-link" class="vertical-align-middle" /> - </a> + <template #docLink="{ content }"> + <gl-link + href="https://grafana.com/docs/http_api/auth/#create-api-token" + target="_blank" + >{{ content }} <gl-icon name="external-link" class="gl-vertical-align-middle" + /></gl-link> + </template> + </gl-sprintf> </p> </gl-form-group> - <gl-button variant="success" category="primary" @click="updateGrafanaIntegration"> + <gl-button + variant="confirm" + category="primary" + data-testid="save-grafana-settings-button" + @click="updateGrafanaIntegration" + > {{ __('Save changes') }} </gl-button> </form> diff --git a/app/assets/javascripts/incidents_settings/components/alerts_form.vue b/app/assets/javascripts/incidents_settings/components/alerts_form.vue index e8daad8811e..b22ec6add96 100644 --- a/app/assets/javascripts/incidents_settings/components/alerts_form.vue +++ b/app/assets/javascripts/incidents_settings/components/alerts_form.vue @@ -134,7 +134,7 @@ export default { ref="submitBtn" data-qa-selector="save_changes_button" :disabled="loading" - variant="success" + variant="confirm" type="submit" class="js-no-auto-disable" > diff --git a/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue b/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue index b56dd66342a..866d2ff399e 100644 --- a/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue +++ b/app/assets/javascripts/incidents_settings/components/pagerduty_form.vue @@ -11,7 +11,6 @@ import { GlModal, GlModalDirective, } from '@gitlab/ui'; -import { isEqual } from 'lodash'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import { I18N_PAGERDUTY_SETTINGS_FORM, CONFIGURE_PAGERDUTY_WEBHOOK_DOCS_LINK } from '../constants'; @@ -50,14 +49,8 @@ export default { pagerduty_active: this.active, }; }, - isFormUpdated() { - return isEqual(this.pagerDutySettings, { - active: this.active, - webhookUrl: this.webhookUrl, - }); - }, isSaveDisabled() { - return this.isFormUpdated || this.loading || this.resettingWebhook; + return this.loading || this.resettingWebhook; }, webhookUpdateAlertMsg() { return this.webhookUpdateFailed @@ -123,13 +116,15 @@ export default { </template> </gl-sprintf> </p> - <form ref="settingsForm" @submit.prevent="updatePagerDutyIntegrationSettings"> + <form ref="settingsForm"> <gl-form-group class="col-8 col-md-9 gl-p-0"> <gl-toggle id="active" v-model="active" + :disabled="isSaveDisabled" :is-loading="loading" :label="$options.i18n.activeToggle.label" + @change="updatePagerDutyIntegrationSettings" /> </gl-form-group> @@ -166,15 +161,6 @@ export default { {{ $options.i18n.webhookUrl.restKeyInfo }} </gl-modal> </gl-form-group> - <gl-button - ref="submitBtn" - :disabled="isSaveDisabled" - variant="success" - type="submit" - class="js-no-auto-disable" - > - {{ $options.i18n.saveBtnLabel }} - </gl-button> </form> </div> </template> diff --git a/app/assets/javascripts/incidents_settings/constants.js b/app/assets/javascripts/incidents_settings/constants.js index d479838b491..d72e2aedee9 100644 --- a/app/assets/javascripts/incidents_settings/constants.js +++ b/app/assets/javascripts/incidents_settings/constants.js @@ -23,7 +23,7 @@ export const I18N_INTEGRATION_TABS = { headerText: s__('IncidentSettings|Incidents'), expandBtnLabel: __('Expand'), subHeaderText: s__( - 'IncidentSettings|Set up integrations with external tools to help better manage incidents.', + 'IncidentSettings|Fine-tune incident settings and set up integrations with external tools to help better manage incidents.', ), }; diff --git a/app/assets/javascripts/nav/components/top_nav_menu_item.vue b/app/assets/javascripts/nav/components/top_nav_menu_item.vue index a0d92811a6f..067180abd08 100644 --- a/app/assets/javascripts/nav/components/top_nav_menu_item.vue +++ b/app/assets/javascripts/nav/components/top_nav_menu_item.vue @@ -1,5 +1,8 @@ <script> import { GlButton, GlIcon } from '@gitlab/ui'; +import { kebabCase, mapKeys } from 'lodash'; + +const getDataKey = (key) => `data-${kebabCase(key)}`; export default { components: { @@ -12,6 +15,11 @@ export default { required: true, }, }, + computed: { + dataAttrs() { + return mapKeys(this.menuItem.data || {}, (value, key) => getDataKey(key)); + }, + }, }; </script> @@ -20,6 +28,8 @@ export default { category="tertiary" :href="menuItem.href" class="top-nav-menu-item gl-display-block" + :class="menuItem.css_class" + v-bind="dataAttrs" v-on="$listeners" > <span class="gl-display-flex"> diff --git a/app/assets/javascripts/operation_settings/components/metrics_settings.vue b/app/assets/javascripts/operation_settings/components/metrics_settings.vue index a24612e4680..959fffa2629 100644 --- a/app/assets/javascripts/operation_settings/components/metrics_settings.vue +++ b/app/assets/javascripts/operation_settings/components/metrics_settings.vue @@ -34,19 +34,19 @@ export default { <h4 class="js-section-header settings-title js-settings-toggle js-settings-toggle-trigger-only" > - {{ s__('MetricsSettings|Metrics dashboard') }} + {{ s__('MetricsSettings|Metrics') }} </h4> <gl-button class="js-settings-toggle">{{ __('Expand') }}</gl-button> <p class="js-section-sub-header"> - {{ s__('MetricsSettings|Manage Metrics Dashboard settings.') }} - <gl-link :href="helpPage">{{ __('Learn more') }}</gl-link> + {{ s__('MetricsSettings|Manage metrics dashboard settings.') }} + <gl-link :href="helpPage">{{ __('Learn more.') }}</gl-link> </p> </div> <div class="settings-content"> <form> <dashboard-timezone /> <external-dashboard /> - <gl-button variant="success" category="primary" @click="saveChanges"> + <gl-button variant="confirm" category="primary" @click="saveChanges"> {{ __('Save Changes') }} </gl-button> </form> diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js index 8a8ce70e998..6179586e56c 100644 --- a/app/assets/javascripts/pages/projects/blob/show/index.js +++ b/app/assets/javascripts/pages/projects/blob/show/index.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import TableOfContents from '~/blob/components/table_contents.vue'; import PipelineTourSuccessModal from '~/blob/pipeline_tour_success_modal.vue'; import BlobViewer from '~/blob/viewer/index'; import GpgBadges from '~/gpg_badges'; @@ -92,3 +93,15 @@ if (successPipelineEl) { }, }); } + +const tableContentsEl = document.querySelector('.js-table-contents'); + +if (tableContentsEl) { + // eslint-disable-next-line no-new + new Vue({ + el: tableContentsEl, + render(h) { + return h(TableOfContents); + }, + }); +} diff --git a/app/finders/concerns/packages/finder_helper.rb b/app/finders/concerns/packages/finder_helper.rb index f0ad998cadb..d2784a1d270 100644 --- a/app/finders/concerns/packages/finder_helper.rb +++ b/app/finders/concerns/packages/finder_helper.rb @@ -29,7 +29,7 @@ module Packages end def projects_visible_to_reporters(user, within_group:) - if user.is_a?(DeployToken) && Feature.enabled?(:packages_finder_helper_deploy_token, default_enabled: :yaml) + if user.is_a?(DeployToken) user.accessible_projects else within_group.all_projects diff --git a/app/graphql/mutations/concerns/mutations/resolves_subscription.rb b/app/graphql/mutations/concerns/mutations/resolves_subscription.rb index e26ae7d228c..ed9fb5fceb0 100644 --- a/app/graphql/mutations/concerns/mutations/resolves_subscription.rb +++ b/app/graphql/mutations/concerns/mutations/resolves_subscription.rb @@ -3,6 +3,7 @@ module Mutations module ResolvesSubscription extend ActiveSupport::Concern + included do argument :subscribed_state, GraphQL::BOOLEAN_TYPE, diff --git a/app/graphql/mutations/issues/set_subscription.rb b/app/graphql/mutations/issues/set_subscription.rb index a04c8f5ba2d..55c9049b7cf 100644 --- a/app/graphql/mutations/issues/set_subscription.rb +++ b/app/graphql/mutations/issues/set_subscription.rb @@ -2,10 +2,32 @@ module Mutations module Issues - class SetSubscription < Base + class SetSubscription < BaseMutation graphql_name 'IssueSetSubscription' include ResolvesSubscription + include Mutations::ResolvesIssuable + + argument :project_path, GraphQL::ID_TYPE, + required: true, + description: "The project the issue to mutate is in." + + argument :iid, GraphQL::STRING_TYPE, + required: true, + description: "The IID of the issue to mutate." + + field :issue, + Types::IssueType, + null: true, + description: "The issue after mutation." + + authorize :update_subscription + + private + + def find_object(project_path:, iid:) + resolve_issuable(type: :issue, parent_path: project_path, iid: iid) + end end end end diff --git a/app/graphql/mutations/merge_requests/set_subscription.rb b/app/graphql/mutations/merge_requests/set_subscription.rb index 7d3c40185c9..981daa81c28 100644 --- a/app/graphql/mutations/merge_requests/set_subscription.rb +++ b/app/graphql/mutations/merge_requests/set_subscription.rb @@ -2,10 +2,32 @@ module Mutations module MergeRequests - class SetSubscription < Base + class SetSubscription < BaseMutation graphql_name 'MergeRequestSetSubscription' include ResolvesSubscription + include Mutations::ResolvesIssuable + + argument :project_path, GraphQL::ID_TYPE, + required: true, + description: "The project the merge request to mutate is in." + + argument :iid, GraphQL::STRING_TYPE, + required: true, + description: "The IID of the merge request to mutate." + + field :merge_request, + Types::MergeRequestType, + null: true, + description: "The merge request after mutation." + + authorize :update_subscription + + private + + def find_object(project_path:, iid:) + resolve_issuable(type: :merge_request, parent_path: project_path, iid: iid) + end end end end diff --git a/app/helpers/nav/top_nav_helper.rb b/app/helpers/nav/top_nav_helper.rb index c7939b29aa8..16eaab4c7c8 100644 --- a/app/helpers/nav/top_nav_helper.rb +++ b/app/helpers/nav/top_nav_helper.rb @@ -132,7 +132,7 @@ module Nav active: active_nav_link?(controller: 'admin/sessions'), icon: 'lock-open', href: destroy_admin_session_path, - method: :post + data: { method: 'post' } ) elsif current_user.admin? builder.add_secondary_menu_item( diff --git a/app/models/member.rb b/app/models/member.rb index 00255f97f85..0c786cb0f94 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -336,7 +336,7 @@ class Member < ApplicationRecord return User.find_by(id: user) if user.is_a?(Integer) - User.find_by(email: user) || user + User.find_by_any_email(user) || user end def retrieve_member(source, user, existing_members) diff --git a/app/policies/issue_policy.rb b/app/policies/issue_policy.rb index 6eec03d6d75..6176faec06a 100644 --- a/app/policies/issue_policy.rb +++ b/app/policies/issue_policy.rb @@ -38,6 +38,7 @@ class IssuePolicy < IssuablePolicy rule { ~anonymous & can?(:read_issue) }.policy do enable :create_todo + enable :update_subscription end end diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb index e53a916f3ca..29a7a174759 100644 --- a/app/policies/merge_request_policy.rb +++ b/app/policies/merge_request_policy.rb @@ -20,6 +20,7 @@ class MergeRequestPolicy < IssuablePolicy rule { ~anonymous & can?(:read_merge_request) }.policy do enable :create_todo + enable :update_subscription end condition(:can_merge) { @subject.can_be_merged_by?(@user) } diff --git a/app/views/projects/blob/_header_content.html.haml b/app/views/projects/blob/_header_content.html.haml index b310939c5a3..95a5d63e07f 100644 --- a/app/views/projects/blob/_header_content.html.haml +++ b/app/views/projects/blob/_header_content.html.haml @@ -1,4 +1,6 @@ .file-header-content + - if Gitlab::MarkupHelper.gitlab_markdown?(blob.path) + .js-table-contents = blob_icon blob.mode, blob.name %strong.file-title-name.gl-word-break-all{ data: { qa_selector: 'file_name_content' } } diff --git a/app/views/projects/settings/operations/_alert_management.html.haml b/app/views/projects/settings/operations/_alert_management.html.haml index 0418d7df42d..34255af9cc6 100644 --- a/app/views/projects/settings/operations/_alert_management.html.haml +++ b/app/views/projects/settings/operations/_alert_management.html.haml @@ -6,7 +6,7 @@ %section.settings.no-animate#js-alert-management-settings{ class: ('expanded' if expanded) } .settings-header %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only - = _('Alert integrations') + = _('Alerts') %button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' } = _('Expand') %p diff --git a/app/views/projects/settings/operations/_tracing.html.haml b/app/views/projects/settings/operations/_tracing.html.haml index a591fa33096..343fd22c051 100644 --- a/app/views/projects/settings/operations/_tracing.html.haml +++ b/app/views/projects/settings/operations/_tracing.html.haml @@ -1,23 +1,13 @@ - setting = tracing_setting -- has_jaeger_url = setting.external_url.present? %section.settings.border-0.no-animate - .settings-header{ :class => "border-top" } + .settings-header{ :class => 'border-top' } %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only - = _("Jaeger tracing") + = _('Tracing') %button.btn.btn-default.gl-button.js-settings-toggle{ type: 'button' } = _('Expand') %p - - if has_jaeger_url - - tracing_link = link_to sanitize(setting.external_url, scrubber: Rails::Html::TextOnlyScrubber.new), target: "_blank", rel: 'noopener noreferrer' do - %span - = _('Tracing') - = sprite_icon('external-link', css_class: 'ml-1 vertical-align-middle') - - else - - tracing_link = link_to project_tracing_path(@project) do - %span - = _('Tracing') - = _("To open Jaeger from GitLab to view tracing from the %{link} page, add a URL to your Jaeger server.").html_safe % { link: tracing_link } + = _('Embed an image of your existing Jaeger server in GitLab.') = link_to _('Learn more.'), help_page_path('operations/tracing'), target: '_blank', rel: 'noopener noreferrer' .settings-content = form_for @project, url: project_settings_operations_path(@project), method: :patch do |f| @@ -27,8 +17,8 @@ = form.label :external_url, _('Jaeger URL'), class: 'label-bold' = form.url_field :external_url, class: 'form-control gl-form-input', placeholder: 'https://jaeger.example.com' %p.form-text.text-muted - - jaeger_help_url = "https://www.jaegertracing.io/docs/getting-started/" + - jaeger_help_url = 'https://www.jaegertracing.io/docs/getting-started/' - link_start_tag = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: jaeger_help_url } - - link_end_tag = "#{sprite_icon('external-link', css_class: 'ml-1 vertical-align-middle')}</a>".html_safe - = _("Learn more about %{link_start_tag}Jaeger configuration%{link_end_tag}.").html_safe % { link_start_tag: link_start_tag, link_end_tag: link_end_tag } + - link_end_tag = "#{sprite_icon('external-link', css_class: 'gl-ml-2 gl-vertical-align-middle')}</a>".html_safe + = _('Learn more about %{link_start_tag}Jaeger configuration%{link_end_tag}.').html_safe % { link_start_tag: link_start_tag, link_end_tag: link_end_tag } = f.submit _('Save changes'), class: 'gl-button btn btn-confirm' diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml index af183046e1e..e2c1a00a587 100644 --- a/app/views/projects/settings/operations/show.html.haml +++ b/app/views/projects/settings/operations/show.html.haml @@ -3,11 +3,11 @@ - page_title title - breadcrumb_title title += render 'projects/settings/operations/metrics_dashboard' += render 'projects/settings/operations/tracing' += render 'projects/settings/operations/error_tracking' = render 'projects/settings/operations/alert_management' = render 'projects/settings/operations/incidents' -= render 'projects/settings/operations/error_tracking' -= render 'projects/settings/operations/prometheus', service: prometheus_service if Feature.enabled?(:settings_operations_prometheus_service) -= render 'projects/settings/operations/metrics_dashboard' = render 'projects/settings/operations/grafana_integration' -= render 'projects/settings/operations/tracing' = render_if_exists 'projects/settings/operations/status_page' += render 'projects/settings/operations/prometheus', service: prometheus_service if Feature.enabled?(:settings_operations_prometheus_service) |