diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-31 18:08:39 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-31 18:08:39 +0000 |
commit | e7fb61499317b3c044845817b991e13aea276955 (patch) | |
tree | 127aef09952fa595b8e5b886db5dd89d974d91da /app | |
parent | 85ea3dd4f4855e99d9a69c8e609095199597195a (diff) | |
download | gitlab-ce-e7fb61499317b3c044845817b991e13aea276955.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
43 files changed, 305 insertions, 537 deletions
diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue index aee61a5b2a5..02b99e86fd3 100644 --- a/app/assets/javascripts/boards/components/board_card_inner.vue +++ b/app/assets/javascripts/boards/components/board_card_inner.vue @@ -240,7 +240,7 @@ export default { class="board-card-footer gl-display-flex gl-justify-content-space-between gl-align-items-flex-end" > <div - class="gl-display-flex align-items-start flex-wrap-reverse board-card-number-container gl-overflow-hidden js-board-card-number-container" + class="gl-display-flex align-items-start flex-wrap-reverse board-card-number-container gl-overflow-hidden" > <gl-loading-icon v-if="item.isLoading" size="md" class="mt-3" /> <span diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue index aba1f42ead5..a874c9e070a 100644 --- a/app/assets/javascripts/boards/components/board_form.vue +++ b/app/assets/javascripts/boards/components/board_form.vue @@ -289,7 +289,7 @@ export default { <p v-if="isDeleteForm" data-testid="delete-confirmation-message"> {{ $options.i18n.deleteConfirmationMessage }} </p> - <form v-else class="js-board-config-modal" data-testid="board-form-wrapper" @submit.prevent> + <form v-else data-testid="board-form-wrapper" @submit.prevent> <div v-if="!readonly" class="gl-mb-5" data-testid="board-form"> <label class="gl-font-weight-bold gl-font-lg" for="board-new-name"> {{ $options.i18n.titleFieldLabel }} diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue index 1024be61359..858cd368b12 100644 --- a/app/assets/javascripts/boards/components/board_list.vue +++ b/app/assets/javascripts/boards/components/board_list.vue @@ -287,7 +287,7 @@ export default { :data-board-type="list.listType" :class="{ 'bg-danger-100': boardItemsSizeExceedsMax }" draggable=".board-card" - class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-2 js-board-list" + class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-2" data-testid="tree-root-wrapper" @start="handleDragOnStart" @end="handleDragOnEnd" diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue index 46b28d20da9..f02feab264c 100644 --- a/app/assets/javascripts/boards/components/board_list_header.vue +++ b/app/assets/javascripts/boards/components/board_list_header.vue @@ -262,7 +262,7 @@ export default { 'gl-py-2': list.collapsed && isSwimlanesHeader, 'gl-flex-direction-column': list.collapsed, }" - class="board-title gl-m-0 gl-display-flex gl-align-items-center gl-font-base gl-px-3 js-board-handle" + class="board-title gl-m-0 gl-display-flex gl-align-items-center gl-font-base gl-px-3" > <gl-button v-gl-tooltip.hover @@ -443,7 +443,7 @@ export default { ref="settingsBtn" v-gl-tooltip.hover :aria-label="$options.i18n.listSettings" - class="no-drag js-board-settings-button" + class="no-drag" :title="$options.i18n.listSettings" icon="settings" @click="openSidebarSettings" diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue index 8333856e996..2951eda1112 100644 --- a/app/assets/javascripts/boards/components/boards_selector.vue +++ b/app/assets/javascripts/boards/components/boards_selector.vue @@ -239,11 +239,12 @@ export default { </script> <template> - <div class="boards-switcher js-boards-selector gl-mr-3"> - <span class="boards-selector-wrapper js-boards-selector-wrapper"> + <div class="boards-switcher gl-mr-3" data-testid="boards-selector"> + <span class="boards-selector-wrapper"> <gl-dropdown + data-testid="boards-dropdown" data-qa-selector="boards_dropdown" - toggle-class="dropdown-menu-toggle js-dropdown-toggle" + toggle-class="dropdown-menu-toggle" menu-class="flex-column dropdown-extended-height" :loading="isBoardLoading" :text="board.name" @@ -276,8 +277,8 @@ export default { <gl-dropdown-item v-for="recentBoard in recentBoards" :key="`recent-${recentBoard.id}`" - class="js-dropdown-item" :href="`${boardBaseUrl}/${recentBoard.id}`" + data-testid="dropdown-item" > {{ recentBoard.name }} </gl-dropdown-item> @@ -292,8 +293,8 @@ export default { <gl-dropdown-item v-for="otherBoard in filteredBoards" :key="otherBoard.id" - class="js-dropdown-item" :href="`${boardBaseUrl}/${otherBoard.id}`" + data-testid="dropdown-item" > {{ otherBoard.name }} </gl-dropdown-item> @@ -331,7 +332,7 @@ export default { <gl-dropdown-item v-if="showDelete" v-gl-modal-directive="'board-config-modal'" - class="text-danger js-delete-board" + class="text-danger" @click.prevent="showPage('delete')" > {{ s__('IssueBoards|Delete board') }} diff --git a/app/assets/javascripts/boards/components/issue_time_estimate.vue b/app/assets/javascripts/boards/components/issue_time_estimate.vue index 1ab7deebfaf..9312db06efe 100644 --- a/app/assets/javascripts/boards/components/issue_time_estimate.vue +++ b/app/assets/javascripts/boards/components/issue_time_estimate.vue @@ -43,7 +43,7 @@ export default { <gl-tooltip :target="() => $refs.issueTimeEstimate" placement="bottom" - class="js-issue-time-estimate" + data-testid="issue-time-estimate" > <span class="gl-font-weight-bold gl-display-block">{{ $options.i18n.timeEstimate }}</span> {{ title }} diff --git a/app/assets/javascripts/boards/components/item_count.vue b/app/assets/javascripts/boards/components/item_count.vue index 9b1ff254766..a11c23e5625 100644 --- a/app/assets/javascripts/boards/components/item_count.vue +++ b/app/assets/javascripts/boards/components/item_count.vue @@ -29,7 +29,7 @@ export default { <span :class="{ 'text-danger': issuesExceedMax }" data-testid="board-items-count"> {{ itemsSize }} </span> - <span v-if="isMaxLimitSet" class="js-max-issue-size"> + <span v-if="isMaxLimitSet" class="max-issue-size"> {{ maxIssueCount }} </span> </div> diff --git a/app/assets/javascripts/boards/components/toggle_focus.vue b/app/assets/javascripts/boards/components/toggle_focus.vue index 0169c9c72ed..71612e0742f 100644 --- a/app/assets/javascripts/boards/components/toggle_focus.vue +++ b/app/assets/javascripts/boards/components/toggle_focus.vue @@ -38,7 +38,6 @@ export default { v-gl-tooltip category="tertiary" :icon="isFullscreen ? 'minimize' : 'maximize'" - class="js-focus-mode-btn" data-qa-selector="focus_mode_button" :title="$options.i18n.toggleFocusMode" :aria-label="$options.i18n.toggleFocusMode" diff --git a/app/assets/javascripts/boards/config_toggle.js b/app/assets/javascripts/boards/config_toggle.js deleted file mode 100644 index 1e54c2511b8..00000000000 --- a/app/assets/javascripts/boards/config_toggle.js +++ /dev/null @@ -1,25 +0,0 @@ -import Vue from 'vue'; -import { parseBoolean } from '~/lib/utils/common_utils'; -import ConfigToggle from './components/config_toggle.vue'; - -export default () => { - const el = document.querySelector('.js-board-config'); - - if (!el) { - return; - } - - // eslint-disable-next-line no-new - new Vue({ - el, - name: 'ConfigToggleRoot', - render(h) { - return h(ConfigToggle, { - props: { - canAdminList: parseBoolean(el.dataset.canAdminList), - hasScope: parseBoolean(el.dataset.hasScope), - }, - }); - }, - }); -}; diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 2abc283c5b4..77c5994b5a1 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -13,7 +13,6 @@ import { } from '~/lib/utils/common_utils'; import { queryToObject } from '~/lib/utils/url_utility'; import { fullBoardId } from './boards_util'; -import initNewBoard from './new_board'; import { gqlClient } from './graphql'; Vue.use(VueApollo); @@ -112,6 +111,4 @@ export default () => { }); mountBoardApp($boardApp); - - initNewBoard(); }; diff --git a/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js deleted file mode 100644 index bb659eb075a..00000000000 --- a/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js +++ /dev/null @@ -1,42 +0,0 @@ -import Vue from 'vue'; -import IssueBoardFilteredSearch from 'ee_else_ce/boards/components/issue_board_filtered_search.vue'; -import store from '~/boards/stores'; -import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; -import { queryToObject } from '~/lib/utils/url_utility'; - -export default ( - apolloProvider, - isSignedIn, - releasesFetchPath, - epicFeatureAvailable, - iterationFeatureAvailable, -) => { - const el = document.getElementById('js-issue-board-filtered-search'); - const rawFilterParams = queryToObject(window.location.search, { gatherArrays: true }); - - const initialFilterParams = { - ...convertObjectPropsToCamelCase(rawFilterParams, {}), - }; - - if (!el) { - return null; - } - - return new Vue({ - el, - name: 'BoardFilteredSearchRoot', - provide: { - initialFilterParams, - isSignedIn, - releasesFetchPath, - epicFeatureAvailable, - iterationFeatureAvailable, - }, - store, // TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/324094 - apolloProvider, - render: (createElement) => - createElement(IssueBoardFilteredSearch, { - props: { fullPath: store.state?.fullPath || '', boardType: store.state?.boardType || '' }, - }), - }); -}; diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js deleted file mode 100644 index 0bc9cfbd867..00000000000 --- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js +++ /dev/null @@ -1,50 +0,0 @@ -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import BoardsSelector from 'ee_else_ce/boards/components/boards_selector.vue'; -import store from '~/boards/stores'; -import createDefaultClient from '~/lib/graphql'; -import { parseBoolean } from '~/lib/utils/common_utils'; - -Vue.use(VueApollo); - -const apolloProvider = new VueApollo({ - defaultClient: createDefaultClient(), -}); - -export default (params = {}) => { - const boardsSwitcherElement = document.getElementById('js-multiple-boards-switcher'); - const { dataset } = boardsSwitcherElement; - return new Vue({ - el: boardsSwitcherElement, - name: 'BoardsSelectorRoot', - components: { - BoardsSelector, - }, - apolloProvider, - store, - provide: { - fullPath: params.fullPath, - rootPath: params.rootPath, - allowScopedLabels: params.allowScopedLabels, - labelsManagePath: params.labelsManagePath, - allowLabelCreate: parseBoolean(dataset.canAdminBoard), - }, - data() { - const boardsSelectorProps = { - ...dataset, - hasMissingBoards: parseBoolean(dataset.hasMissingBoards), - canAdminBoard: parseBoolean(dataset.canAdminBoard), - multipleIssueBoardsAvailable: parseBoolean(dataset.multipleIssueBoardsAvailable), - scopedIssueBoardFeatureEnabled: parseBoolean(dataset.scopedIssueBoardFeatureEnabled), - weights: JSON.parse(dataset.weights), - }; - - return { boardsSelectorProps }; - }, - render(createElement) { - return createElement(BoardsSelector, { - props: this.boardsSelectorProps, - }); - }, - }); -}; diff --git a/app/assets/javascripts/boards/new_board.js b/app/assets/javascripts/boards/new_board.js deleted file mode 100644 index 34f2fea79a9..00000000000 --- a/app/assets/javascripts/boards/new_board.js +++ /dev/null @@ -1,29 +0,0 @@ -import Vue from 'vue'; -import { parseBoolean } from '~/lib/utils/common_utils'; -import { getExperimentVariant } from '~/experimentation/utils'; -import { CANDIDATE_VARIANT } from '~/experimentation/constants'; -import NewBoardButton from './components/new_board_button.vue'; - -export default () => { - if (getExperimentVariant('prominent_create_board_btn') !== CANDIDATE_VARIANT) { - return; - } - - const el = document.querySelector('.js-new-board'); - - if (!el) { - return; - } - - // eslint-disable-next-line no-new - new Vue({ - el, - provide: { - multipleIssueBoardsAvailable: parseBoolean(el.dataset.multipleIssueBoardsAvailable), - canAdminBoard: parseBoolean(el.dataset.canAdminBoard), - }, - render(h) { - return h(NewBoardButton); - }, - }); -}; diff --git a/app/assets/javascripts/boards/toggle_epics_swimlanes.js b/app/assets/javascripts/boards/toggle_epics_swimlanes.js deleted file mode 100644 index 2d1ec238274..00000000000 --- a/app/assets/javascripts/boards/toggle_epics_swimlanes.js +++ /dev/null @@ -1 +0,0 @@ -export default () => {}; diff --git a/app/assets/javascripts/boards/toggle_focus.js b/app/assets/javascripts/boards/toggle_focus.js deleted file mode 100644 index 8f057e192dd..00000000000 --- a/app/assets/javascripts/boards/toggle_focus.js +++ /dev/null @@ -1,18 +0,0 @@ -import Vue from 'vue'; -import ToggleFocus from './components/toggle_focus.vue'; - -export default () => { - const issueBoardsContentSelector = '.content-wrapper > .js-focus-mode-board'; - - return new Vue({ - el: '#js-toggle-focus-btn', - name: 'ToggleFocusRoot', - render(h) { - return h(ToggleFocus, { - props: { - issueBoardsContentSelector, - }, - }); - }, - }); -}; diff --git a/app/assets/javascripts/boards/toggle_labels.js b/app/assets/javascripts/boards/toggle_labels.js deleted file mode 100644 index 2d1ec238274..00000000000 --- a/app/assets/javascripts/boards/toggle_labels.js +++ /dev/null @@ -1 +0,0 @@ -export default () => {}; diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/registry_header.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/registry_header.vue index 6d2ff9ea7b6..154e176dc6e 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/registry_header.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/list_page/registry_header.vue @@ -1,4 +1,5 @@ <script> +import { GlLink } from '@gitlab/ui'; import { approximateDuration, calculateRemainingMilliseconds } from '~/lib/utils/datetime_utility'; import { n__, sprintf } from '~/locale'; import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue'; @@ -9,6 +10,7 @@ import { LIST_INTRO_TEXT, EXPIRATION_POLICY_WILL_RUN_IN, EXPIRATION_POLICY_DISABLED_TEXT, + SET_UP_CLEANUP, } from '../../constants/index'; export default { @@ -16,6 +18,7 @@ export default { components: { TitleArea, MetadataItem, + GlLink, }, props: { expirationPolicy: { @@ -43,6 +46,16 @@ export default { required: false, default: false, }, + cleanupPoliciesSettingsPath: { + type: String, + default: '', + required: false, + }, + showCleanupPolicyLink: { + type: Boolean, + required: false, + default: false, + }, }, loader: { repeat: 10, @@ -51,6 +64,7 @@ export default { }, i18n: { CONTAINER_REGISTRY_TITLE, + SET_UP_CLEANUP, }, computed: { imagesCountText() { @@ -105,6 +119,9 @@ export default { :text="expirationPolicyText" size="xl" /> + <gl-link v-if="showCleanupPolicyLink" class="gl-ml-2" :href="cleanupPoliciesSettingsPath">{{ + $options.i18n.SET_UP_CLEANUP + }}</gl-link> </template> </title-area> </template> diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/expiration_policies.js b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/expiration_policies.js index 40f9b09a982..e584da23edb 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/expiration_policies.js +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/constants/expiration_policies.js @@ -4,7 +4,7 @@ export const EXPIRATION_POLICY_WILL_RUN_IN = s__( 'ContainerRegistry|Expiration policy will run in %{time}', ); export const EXPIRATION_POLICY_DISABLED_TEXT = s__( - 'ContainerRegistry|Expiration policy is disabled', + 'ContainerRegistry|Expiration policy is disabled.', ); export const DELETE_ALERT_TITLE = s__('ContainerRegistry|Some tags were not deleted'); export const DELETE_ALERT_LINK_TEXT = s__( @@ -13,3 +13,4 @@ export const DELETE_ALERT_LINK_TEXT = s__( export const CLEANUP_TIMED_OUT_ERROR_MESSAGE = s__( 'ContainerRegistry|Cleanup timed out before it could delete all tags', ); +export const SET_UP_CLEANUP = s__('ContainerRegistry|Set up cleanup'); diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js b/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js index ca5bd8d6964..a558550c91f 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js @@ -35,7 +35,7 @@ export default () => { expirationPolicy, isGroupPage, isAdmin, - showCleanupPolicyOnAlert, + showCleanupPolicyLink, showUnfinishedTagCleanupCallout, connectionError, invalidPathError, @@ -68,7 +68,7 @@ export default () => { expirationPolicy: expirationPolicy ? JSON.parse(expirationPolicy) : undefined, isGroupPage: parseBoolean(isGroupPage), isAdmin: parseBoolean(isAdmin), - showCleanupPolicyOnAlert: parseBoolean(showCleanupPolicyOnAlert), + showCleanupPolicyLink: parseBoolean(showCleanupPolicyLink), showUnfinishedTagCleanupCallout: parseBoolean(showUnfinishedTagCleanupCallout), connectionError: parseBoolean(connectionError), invalidPathError: parseBoolean(invalidPathError), diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue index 5f9e614bebb..d1cab406984 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue @@ -11,7 +11,6 @@ import { import { get } from 'lodash'; import getContainerRepositoriesQuery from 'shared_queries/container_registry/get_container_repositories.query.graphql'; import createFlash from '~/flash'; -import CleanupPolicyEnabledAlert from '~/packages_and_registries/shared/components/cleanup_policy_enabled_alert.vue'; import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants'; import Tracking from '~/tracking'; import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue'; @@ -60,7 +59,6 @@ export default { GlSkeletonLoader, RegistryHeader, DeleteImage, - CleanupPolicyEnabledAlert, PersistedSearch, }, directives: { @@ -273,12 +271,6 @@ export default { </gl-sprintf> </gl-alert> - <cleanup-policy-enabled-alert - v-if="config.showCleanupPolicyOnAlert" - :project-path="config.projectPath" - :cleanup-policies-settings-path="config.cleanupPoliciesSettingsPath" - /> - <gl-empty-state v-if="showConnectionError" :title="$options.i18n.CONNECTION_ERROR_TITLE" @@ -304,6 +296,8 @@ export default { :expiration-policy="config.expirationPolicy" :help-page-path="config.helpPagePath" :hide-expiration-policy-data="config.isGroupPage" + :cleanup-policies-settings-path="config.cleanupPoliciesSettingsPath" + :show-cleanup-policy-link="config.showCleanupPolicyLink" > <template #commands> <cli-commands diff --git a/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue index 7be3bba7cae..854c88b2ad3 100644 --- a/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue +++ b/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue @@ -9,7 +9,6 @@ import { UNAVAILABLE_ADMIN_FEATURE_TEXT, } from '~/packages_and_registries/settings/project/constants'; import expirationPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_expiration_policy.query.graphql'; -import CleanupPolicyEnabledAlert from '~/packages_and_registries/shared/components/cleanup_policy_enabled_alert.vue'; import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue'; import SettingsForm from './settings_form.vue'; @@ -18,19 +17,11 @@ export default { components: { SettingsBlock, SettingsForm, - CleanupPolicyEnabledAlert, GlAlert, GlSprintf, GlLink, }, - inject: [ - 'projectPath', - 'isAdmin', - 'adminSettingsPath', - 'enableHistoricEntries', - 'helpPagePath', - 'showCleanupPolicyOnAlert', - ], + inject: ['projectPath', 'isAdmin', 'adminSettingsPath', 'enableHistoricEntries', 'helpPagePath'], i18n: { UNAVAILABLE_FEATURE_TITLE, UNAVAILABLE_FEATURE_INTRO_TEXT, @@ -87,7 +78,6 @@ export default { <template> <section data-testid="registry-settings-app"> - <cleanup-policy-enabled-alert v-if="showCleanupPolicyOnAlert" :project-path="projectPath" /> <settings-block :collapsible="false"> <template #title> {{ __('Clean up image tags') }}</template> <template #description> diff --git a/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js b/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js index 2a3e2c28fa6..17c33073668 100644 --- a/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js +++ b/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js @@ -20,7 +20,6 @@ export default () => { adminSettingsPath, tagsRegexHelpPagePath, helpPagePath, - showCleanupPolicyOnAlert, } = el.dataset; return new Vue({ el, @@ -35,7 +34,6 @@ export default () => { adminSettingsPath, tagsRegexHelpPagePath, helpPagePath, - showCleanupPolicyOnAlert: parseBoolean(showCleanupPolicyOnAlert), }, render(createElement) { return createElement('registry-settings-app', {}); diff --git a/app/assets/javascripts/packages_and_registries/shared/components/cleanup_policy_enabled_alert.vue b/app/assets/javascripts/packages_and_registries/shared/components/cleanup_policy_enabled_alert.vue deleted file mode 100644 index d51c62e0623..00000000000 --- a/app/assets/javascripts/packages_and_registries/shared/components/cleanup_policy_enabled_alert.vue +++ /dev/null @@ -1,54 +0,0 @@ -<script> -import { GlSprintf, GlAlert, GlLink } from '@gitlab/ui'; -import { s__ } from '~/locale'; -import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; - -export default { - components: { - GlAlert, - GlLink, - GlSprintf, - LocalStorageSync, - }, - props: { - projectPath: { - type: String, - required: true, - }, - cleanupPoliciesSettingsPath: { - type: String, - required: false, - default: '', - }, - }, - data() { - return { - dismissed: false, - }; - }, - computed: { - storageKey() { - return `cleanup_policy_enabled_for_project_${this.projectPath}`; - }, - }, - i18n: { - message: s__( - 'ContainerRegistry|Cleanup policies are now available for this project. %{linkStart}Click here to get started.%{linkEnd}', - ), - }, -}; -</script> - -<template> - <local-storage-sync v-model="dismissed" :storage-key="storageKey"> - <gl-alert v-if="!dismissed" class="gl-mt-2" dismissible @dismiss="dismissed = true"> - <gl-sprintf :message="$options.i18n.message"> - <template #link="{ content }"> - <gl-link v-if="cleanupPoliciesSettingsPath" :href="cleanupPoliciesSettingsPath">{{ - content - }}</gl-link> - </template> - </gl-sprintf> - </gl-alert> - </local-storage-sync> -</template> diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js index 88ff05b20a0..5dae812bbcb 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js @@ -33,13 +33,7 @@ function initIntervalPatternInput() { } function getEnabledRefTypes() { - const refTypes = [REF_TYPE_BRANCHES]; - - if (gon.features.pipelineSchedulesWithTags) { - refTypes.push(REF_TYPE_TAGS); - } - - return refTypes; + return [REF_TYPE_BRANCHES, REF_TYPE_TAGS]; } function initTargetRefDropdown() { @@ -61,9 +55,7 @@ function initTargetRefDropdown() { value: $refField.value, useSymbolicRefNames: true, translations: { - dropdownHeader: gon.features.pipelineSchedulesWithTags - ? __('Select target branch or tag') - : __('Select target branch'), + dropdownHeader: __('Select target branch or tag'), }, }, class: 'gl-w-full', diff --git a/app/assets/stylesheets/page_bundles/boards.scss b/app/assets/stylesheets/page_bundles/boards.scss index f91ca489bdf..eecd4954e39 100644 --- a/app/assets/stylesheets/page_bundles/boards.scss +++ b/app/assets/stylesheets/page_bundles/boards.scss @@ -198,7 +198,7 @@ border-bottom: 1px solid var(--gray-100, $gray-100); height: 3rem; - .js-max-issue-size::before { + .max-issue-size::before { content: '/'; } } diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb index 641b3adb12b..e223e2a914d 100644 --- a/app/controllers/groups/boards_controller.rb +++ b/app/controllers/groups/boards_controller.rb @@ -43,8 +43,6 @@ class Groups::BoardsController < Groups::ApplicationController def assign_endpoint_vars @boards_endpoint = group_boards_path(group) - @namespace_path = group.to_param - @labels_endpoint = group_labels_path(group) end def authorize_read_board! diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb index 7ad3a2ee358..936ecc171d7 100644 --- a/app/controllers/import/base_controller.rb +++ b/app/controllers/import/base_controller.rb @@ -70,7 +70,7 @@ class Import::BaseController < ApplicationController end def already_added_projects - @already_added_projects ||= filtered(find_already_added_projects(provider_name)) + @already_added_projects ||= find_already_added_projects(provider_name) end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index c44a0830e2e..b82e60e9ac7 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -44,8 +44,6 @@ class Projects::BoardsController < Projects::ApplicationController def assign_endpoint_vars @boards_endpoint = project_boards_path(project) @bulk_issues_path = bulk_update_project_issues_path(project) - @namespace_path = project.namespace.full_path - @labels_endpoint = project_labels_path(project) end def authorize_read_board! diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index 271c31b6429..ac94cc001dd 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -10,10 +10,6 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController before_action :authorize_update_pipeline_schedule!, except: [:index, :new, :create, :play] before_action :authorize_admin_pipeline_schedule!, only: [:destroy] - before_action do - push_frontend_feature_flag(:pipeline_schedules_with_tags, @project, default_enabled: :yaml) - end - feature_category :continuous_integration # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index 7b4d87a77e4..f849f36bf84 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -127,20 +127,6 @@ module BoardsHelper can?(current_user, :admin_issue, current_board_parent) end - def board_list_data - include_descendant_groups = @group&.present? - - { - toggle: "dropdown", - list_labels_path: labels_filter_path_with_defaults(only_group_labels: true, include_ancestor_groups: true), - labels: labels_filter_path_with_defaults(only_group_labels: true, include_descendant_groups: include_descendant_groups), - labels_endpoint: @labels_endpoint, - namespace_path: @namespace_path, - project_path: @project&.path, - group_path: @group&.path - } - end - def serializer CurrentBoardSerializer.new end diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb index 01075862618..20d40626449 100644 --- a/app/helpers/packages_helper.rb +++ b/app/helpers/packages_helper.rb @@ -46,7 +46,7 @@ module PackagesHelper ::Gitlab::Tracking.event(category, event_name.to_s, **args) end - def show_cleanup_policy_on_alert(project) + def show_cleanup_policy_link(project) Gitlab.com? && Gitlab.config.registry.enabled && project.feature_available?(:container_registry, current_user) && diff --git a/app/models/ci/namespace_mirror.rb b/app/models/ci/namespace_mirror.rb index b4b9574942e..d5cbbb96134 100644 --- a/app/models/ci/namespace_mirror.rb +++ b/app/models/ci/namespace_mirror.rb @@ -11,8 +11,6 @@ module Ci end scope :contains_any_of_namespaces, -> (ids) do - return none if ids.empty? - where('traversal_ids && ARRAY[?]::int[]', ids) end diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb index 03ac5c8e5df..28b4b0dcca1 100644 --- a/app/models/container_repository.rb +++ b/app/models/container_repository.rb @@ -174,7 +174,7 @@ class ContainerRepository < ApplicationRecord end end - before_transition %i[importing import_aborted] => :import_done do |container_repository| + before_transition any => :import_done do |container_repository| container_repository.migration_import_done_at = Time.zone.now end diff --git a/app/models/project_import_state.rb b/app/models/project_import_state.rb index 42914b0d668..fabbd5b49cb 100644 --- a/app/models/project_import_state.rb +++ b/app/models/project_import_state.rb @@ -6,6 +6,8 @@ class ProjectImportState < ApplicationRecord self.table_name = "project_mirror_data" + after_commit :expire_etag_cache + belongs_to :project, inverse_of: :import_state validates :project, presence: true @@ -76,6 +78,23 @@ class ProjectImportState < ApplicationRecord end end + def expire_etag_cache + if realtime_changes_path + Gitlab::EtagCaching::Store.new.tap do |store| + store.touch(realtime_changes_path) + rescue Gitlab::EtagCaching::Store::InvalidKeyError + # no-op: not every realtime changes endpoint is using etag caching + end + end + end + + def realtime_changes_path + Gitlab::Routing.url_helpers.polymorphic_path([:realtime_changes_import, project.import_type.to_sym], format: :json) + rescue NoMethodError + # polymorphic_path throws NoMethodError when no such path exists + nil + end + def relation_hard_failures(limit:) project.import_failures.hard_failures_by_correlation_id(correlation_id).limit(limit) end diff --git a/app/models/user.rb b/app/models/user.rb index c683f5dd252..281e52b90b0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2220,27 +2220,43 @@ class User < ApplicationRecord end def ci_owned_project_runners_from_project_members - Ci::RunnerProject - .select('ci_runners.*') - .joins(:runner) - .where(project: project_members.where('access_level >= ?', Gitlab::Access::MAINTAINER).pluck(:source_id)) + project_ids = project_members.where('access_level >= ?', Gitlab::Access::MAINTAINER).pluck(:source_id) + + Ci::Runner + .joins(:runner_projects) + .where(runner_projects: { project: project_ids }) end def ci_owned_project_runners_from_group_members - Ci::RunnerProject - .select('ci_runners.*') - .joins(:runner) - .joins('JOIN ci_project_mirrors ON ci_project_mirrors.project_id = ci_runner_projects.project_id') - .joins('JOIN ci_namespace_mirrors ON ci_namespace_mirrors.namespace_id = ci_project_mirrors.namespace_id') - .merge(ci_namespace_mirrors_for_group_members(Gitlab::Access::MAINTAINER)) + cte_namespace_ids = Gitlab::SQL::CTE.new( + :cte_namespace_ids, + ci_namespace_mirrors_for_group_members(Gitlab::Access::MAINTAINER).select(:namespace_id) + ) + + cte_project_ids = Gitlab::SQL::CTE.new( + :cte_project_ids, + Ci::ProjectMirror + .select(:project_id) + .where('ci_project_mirrors.namespace_id IN (SELECT namespace_id FROM cte_namespace_ids)') + ) + + Ci::Runner + .with(cte_namespace_ids.to_arel) + .with(cte_project_ids.to_arel) + .joins(:runner_projects) + .where('ci_runner_projects.project_id IN (SELECT project_id FROM cte_project_ids)') end def ci_owned_group_runners - Ci::RunnerNamespace - .select('ci_runners.*') - .joins(:runner) - .joins('JOIN ci_namespace_mirrors ON ci_namespace_mirrors.namespace_id = ci_runner_namespaces.namespace_id') - .merge(ci_namespace_mirrors_for_group_members(Gitlab::Access::OWNER)) + cte_namespace_ids = Gitlab::SQL::CTE.new( + :cte_namespace_ids, + ci_namespace_mirrors_for_group_members(Gitlab::Access::OWNER).select(:namespace_id) + ) + + Ci::Runner + .with(cte_namespace_ids.to_arel) + .joins(:runner_namespaces) + .where('ci_runner_namespaces.namespace_id IN (SELECT namespace_id FROM cte_namespace_ids)') end def ci_namespace_mirrors_for_group_members(level) diff --git a/app/services/incident_management/issuable_escalation_statuses/build_service.rb b/app/services/incident_management/issuable_escalation_statuses/build_service.rb new file mode 100644 index 00000000000..9ebcf72a0c9 --- /dev/null +++ b/app/services/incident_management/issuable_escalation_statuses/build_service.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module IncidentManagement + module IssuableEscalationStatuses + class BuildService < ::BaseProjectService + def initialize(issue) + @issue = issue + @alert = issue.alert_management_alert + + super(project: issue.project) + end + + def execute + return issue.escalation_status if issue.escalation_status + + issue.build_incident_management_issuable_escalation_status(alert_params) + end + + private + + attr_reader :issue, :alert + + def alert_params + return {} unless alert + + { + status_event: alert.status_event_for(alert.status_name) + } + end + end + end +end + +IncidentManagement::IssuableEscalationStatuses::BuildService.prepend_mod diff --git a/app/services/incident_management/issuable_escalation_statuses/create_service.rb b/app/services/incident_management/issuable_escalation_statuses/create_service.rb index e28debf0fa3..9b22fb97e0d 100644 --- a/app/services/incident_management/issuable_escalation_statuses/create_service.rb +++ b/app/services/incident_management/issuable_escalation_statuses/create_service.rb @@ -2,14 +2,15 @@ module IncidentManagement module IssuableEscalationStatuses - class CreateService < BaseService + class CreateService < ::BaseProjectService def initialize(issue) @issue = issue - @alert = issue.alert_management_alert + + super(project: issue.project) end def execute - escalation_status = ::IncidentManagement::IssuableEscalationStatus.new(issue: issue, **alert_params) + escalation_status = BuildService.new(issue).execute if escalation_status.save ServiceResponse.success(payload: { escalation_status: escalation_status }) @@ -20,17 +21,7 @@ module IncidentManagement private - attr_reader :issue, :alert - - def alert_params - return {} unless alert - - { - status_event: alert.status_event_for(alert.status_name) - } - end + attr_reader :issue end end end - -IncidentManagement::IssuableEscalationStatuses::CreateService.prepend_mod diff --git a/app/services/incident_management/issuable_escalation_statuses/prepare_update_service.rb b/app/services/incident_management/issuable_escalation_statuses/prepare_update_service.rb index 8f591b375ee..1d0504a6e80 100644 --- a/app/services/incident_management/issuable_escalation_statuses/prepare_update_service.rb +++ b/app/services/incident_management/issuable_escalation_statuses/prepare_update_service.rb @@ -31,9 +31,7 @@ module IncidentManagement attr_reader :issuable, :param_errors def available? - issuable.supports_escalation? && - user_has_permissions? && - escalation_status.present? + issuable.supports_escalation? && user_has_permissions? end def user_has_permissions? @@ -42,7 +40,7 @@ module IncidentManagement def escalation_status strong_memoize(:escalation_status) do - issuable.escalation_status + issuable.escalation_status || BuildService.new(issuable).execute end end diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml index 0818c3d5cff..e00d8e2c050 100644 --- a/app/views/projects/pipeline_schedules/_form.html.haml +++ b/app/views/projects/pipeline_schedules/_form.html.haml @@ -15,7 +15,7 @@ = f.text_field :cron_timezone, value: @schedule.cron_timezone, id: 'schedule_cron_timezone', class: 'hidden', name: 'schedule[cron_timezone]', required: true .form-group.row .col-md-9 - = f.label :ref, Feature.enabled?(:pipeline_schedules_with_tags, default_enabled: :yaml) ? _('Target branch or tag') : _('Target branch'), class: 'label-bold' + = f.label :ref, _('Target branch or tag'), class: 'label-bold' %div{ data: { testid: 'schedule-target-ref' } } .js-target-ref-dropdown{ data: { project_id: @project.id, default_branch: @project.default_branch } } = f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml index aab5e9fca98..51f0b6319a1 100644 --- a/app/views/projects/registry/repositories/index.html.haml +++ b/app/views/projects/registry/repositories/index.html.haml @@ -19,7 +19,7 @@ "project_path": @project.full_path, "gid_prefix": container_repository_gid_prefix, "is_admin": current_user&.admin.to_s, - "show_cleanup_policy_on_alert": show_cleanup_policy_on_alert(@project).to_s, + "show_cleanup_policy_link": show_cleanup_policy_link(@project).to_s, "cleanup_policies_settings_path": project_settings_packages_and_registries_path(@project), connection_error: (!!@connection_error).to_s, invalid_path_error: (!!@invalid_path_error).to_s, diff --git a/app/views/projects/settings/packages_and_registries/show.html.haml b/app/views/projects/settings/packages_and_registries/show.html.haml index 658b2f2e65c..378bb0f9306 100644 --- a/app/views/projects/settings/packages_and_registries/show.html.haml +++ b/app/views/projects/settings/packages_and_registries/show.html.haml @@ -11,5 +11,5 @@ admin_settings_path: ci_cd_admin_application_settings_path(anchor: 'js-registry-settings'), enable_historic_entries: container_expiration_policies_historic_entry_enabled?.to_s, help_page_path: help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'cleanup-policy'), - show_cleanup_policy_on_alert: show_cleanup_policy_on_alert(@project).to_s, + show_cleanup_policy_link: show_cleanup_policy_link(@project).to_s, tags_regex_help_page_path: help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'regex-pattern-examples') } } diff --git a/app/views/shared/boards/_switcher.html.haml b/app/views/shared/boards/_switcher.html.haml deleted file mode 100644 index c667b3a4626..00000000000 --- a/app/views/shared/boards/_switcher.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -- parent = board.resource_parent -- milestone_filter_opts = { format: :json } -- milestone_filter_opts = milestone_filter_opts.merge(only_group_milestones: true) if board.group_board? -- weights = Gitlab.ee? ? ([Issue::WEIGHT_ANY] + Issue.weight_options) : [] - -#js-multiple-boards-switcher.inline.boards-switcher{ data: { milestone_path: milestones_filter_path(milestone_filter_opts), - board_base_url: board_base_url, - has_missing_boards: (!multiple_boards_available? && current_board_parent.boards.size > 1).to_s, - can_admin_board: can?(current_user, :admin_issue_board, parent).to_s, - multiple_issue_boards_available: parent.multiple_issue_boards_available?.to_s, - scoped_issue_board_feature_enabled: Gitlab.ee? && parent.feature_available?(:scoped_issue_board) ? 'true' : 'false', - weights: weights.to_json } } diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 37a79a50fb1..7fdf8ea7796 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -1,22 +1,12 @@ - type = local_assigns.fetch(:type) -- board = local_assigns.fetch(:board, nil) - show_sorting_dropdown = local_assigns.fetch(:show_sorting_dropdown, true) - disable_target_branch = local_assigns.fetch(:disable_target_branch, false) - placeholder = local_assigns[:placeholder] || _('Search or filter results...') - block_css_class = type != :productivity_analytics ? 'row-content-block second-block' : '' -- is_epic_board = board&.to_type == "EpicBoard" - -- if is_epic_board - - user_can_admin_list = can?(current_user, :admin_epic_board_list, board.resource_parent) -- elsif board - - user_can_admin_list = can?(current_user, :admin_issue_board_list, board.resource_parent) .issues-filters .issues-details-filters.filtered-search-block.d-flex.flex-column.flex-lg-row{ class: block_css_class } .d-flex.flex-column.flex-md-row.flex-grow-1.mb-lg-0.mb-md-2.mb-sm-0.w-100 - - if type == :boards - = render "shared/boards/switcher", board: board - .js-new-board{ data: { multiple_issue_boards_available: parent.multiple_issue_boards_available?.to_s, can_admin_board: can?(current_user, :admin_issue_board, parent).to_s, } } = form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form w-100' do - if params[:search].present? = hidden_field_tag :search, params[:search] @@ -25,201 +15,188 @@ - checkbox_id = 'check-all-issues' %label.gl-sr-only{ for: checkbox_id }= _('Select all') = check_box_tag checkbox_id, nil, false, class: "check-all-issues left" - - if is_epic_board - #js-board-filtered-search{ data: { full_path: @group&.full_path } } - - elsif board - #js-issue-board-filtered-search - - else - .issues-other-filters.filtered-search-wrapper.d-flex.flex-column.flex-md-row - .filtered-search-box - - if type != :boards - - text = tag.span(sprite_icon('history'), class: "d-md-none") + tag.span(_('Recent searches'), class: "d-none d-md-inline") - = dropdown_tag(text, - options: { wrapper_class: "filtered-search-history-dropdown-wrapper", - toggle_class: "gl-button btn btn-default filtered-search-history-dropdown-toggle-button", - dropdown_class: "filtered-search-history-dropdown", - content_class: "filtered-search-history-dropdown-content" }) do - .js-filtered-search-history-dropdown{ data: { full_path: search_history_storage_prefix } } - .filtered-search-box-input-container.droplab-dropdown - .scroll-container - %ul.tokens-container.list-unstyled - %li.input-token - %input.form-control.filtered-search{ search_filter_input_options(type, placeholder) } - #js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown - %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } - %li.filter-dropdown-item{ data: {hint: "#{'{{hint}}'}", tag: "#{'{{tag}}'}", action: "#{'{{hint === \'search\' ? \'submit\' : \'\' }}'}" } } - %button.gl-button.btn.btn-link{ type: 'button' } - -# Encapsulate static class name `{{icon}}` inside #{} to bypass - -# haml lint's ClassAttributeWithStaticValue - %svg - %use{ 'xlink:href': "#{'{{icon}}'}" } - %span.js-filter-hint - {{formattedKey}} - #js-dropdown-operator.filtered-search-input-dropdown-menu.dropdown-menu - %ul.filter-dropdown{ data: { dropdown: true, dynamic: true } } - %li.filter-dropdown-item{ data: { value: "{{ title }}" } } - %button.gl-button.btn.btn-link{ type: 'button' } - {{ title }} - %span.btn-helptext - {{ help }} - #js-dropdown-author.filtered-search-input-dropdown-menu.dropdown-menu + .issues-other-filters.filtered-search-wrapper.d-flex.flex-column.flex-md-row + .filtered-search-box + - if type != :boards + - text = tag.span(sprite_icon('history'), class: "d-md-none") + tag.span(_('Recent searches'), class: "d-none d-md-inline") + = dropdown_tag(text, + options: { wrapper_class: "filtered-search-history-dropdown-wrapper", + toggle_class: "gl-button btn btn-default filtered-search-history-dropdown-toggle-button", + dropdown_class: "filtered-search-history-dropdown", + content_class: "filtered-search-history-dropdown-content" }) do + .js-filtered-search-history-dropdown{ data: { full_path: search_history_storage_prefix } } + .filtered-search-box-input-container.droplab-dropdown + .scroll-container + %ul.tokens-container.list-unstyled + %li.input-token + %input.form-control.filtered-search{ search_filter_input_options(type, placeholder) } + #js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + %li.filter-dropdown-item{ data: {hint: "#{'{{hint}}'}", tag: "#{'{{tag}}'}", action: "#{'{{hint === \'search\' ? \'submit\' : \'\' }}'}" } } + %button.gl-button.btn.btn-link{ type: 'button' } + -# Encapsulate static class name `{{icon}}` inside #{} to bypass + -# haml lint's ClassAttributeWithStaticValue + %svg + %use{ 'xlink:href': "#{'{{icon}}'}" } + %span.js-filter-hint + {{formattedKey}} + #js-dropdown-operator.filtered-search-input-dropdown-menu.dropdown-menu + %ul.filter-dropdown{ data: { dropdown: true, dynamic: true } } + %li.filter-dropdown-item{ data: { value: "{{ title }}" } } + %button.gl-button.btn.btn-link{ type: 'button' } + {{ title }} + %span.btn-helptext + {{ help }} + #js-dropdown-author.filtered-search-input-dropdown-menu.dropdown-menu + - if current_user + %ul{ data: { dropdown: true } } + = render 'shared/issuable/user_dropdown_item', + user: current_user + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + = render 'shared/issuable/user_dropdown_item', + user: User.new(username: '{{username}}', name: '{{name}}'), + avatar: { lazy: true, url: '{{avatar_url}}' } + #js-dropdown-assignee.filtered-search-input-dropdown-menu.dropdown-menu + %ul{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'None' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('None') + %li.filter-dropdown-item{ data: { value: 'Any' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('Any') + %li.divider.droplab-item-ignore - if current_user - %ul{ data: { dropdown: true } } - = render 'shared/issuable/user_dropdown_item', - user: current_user - %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } = render 'shared/issuable/user_dropdown_item', - user: User.new(username: '{{username}}', name: '{{name}}'), - avatar: { lazy: true, url: '{{avatar_url}}' } - #js-dropdown-assignee.filtered-search-input-dropdown-menu.dropdown-menu - %ul{ data: { dropdown: true } } - %li.filter-dropdown-item{ data: { value: 'None' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('None') - %li.filter-dropdown-item{ data: { value: 'Any' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('Any') - %li.divider.droplab-item-ignore - - if current_user - = render 'shared/issuable/user_dropdown_item', - user: current_user - %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + user: current_user + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + = render 'shared/issuable/user_dropdown_item', + user: User.new(username: '{{username}}', name: '{{name}}'), + avatar: { lazy: true, url: '{{avatar_url}}' } + #js-dropdown-reviewer.filtered-search-input-dropdown-menu.dropdown-menu + %ul{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'None' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('None') + %li.filter-dropdown-item{ data: { value: 'Any' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('Any') + %li.divider.droplab-item-ignore + - if current_user = render 'shared/issuable/user_dropdown_item', - user: User.new(username: '{{username}}', name: '{{name}}'), - avatar: { lazy: true, url: '{{avatar_url}}' } - #js-dropdown-reviewer.filtered-search-input-dropdown-menu.dropdown-menu - %ul{ data: { dropdown: true } } - %li.filter-dropdown-item{ data: { value: 'None' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('None') - %li.filter-dropdown-item{ data: { value: 'Any' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('Any') - %li.divider.droplab-item-ignore - - if current_user + user: current_user + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + = render 'shared/issuable/user_dropdown_item', + user: User.new(username: '{{username}}', name: '{{name}}'), + avatar: { lazy: true, url: '{{avatar_url}}' } + - if Feature.enabled?(:mr_attention_requests, default_enabled: :yaml) + #js-dropdown-attention-requested.filtered-search-input-dropdown-menu.dropdown-menu + - if current_user + %ul{ data: { dropdown: true } } = render 'shared/issuable/user_dropdown_item', user: current_user %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } = render 'shared/issuable/user_dropdown_item', user: User.new(username: '{{username}}', name: '{{name}}'), avatar: { lazy: true, url: '{{avatar_url}}' } - - if Feature.enabled?(:mr_attention_requests, default_enabled: :yaml) - #js-dropdown-attention-requested.filtered-search-input-dropdown-menu.dropdown-menu - - if current_user - %ul{ data: { dropdown: true } } - = render 'shared/issuable/user_dropdown_item', - user: current_user - %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } - = render 'shared/issuable/user_dropdown_item', - user: User.new(username: '{{username}}', name: '{{name}}'), - avatar: { lazy: true, url: '{{avatar_url}}' } - = render_if_exists 'shared/issuable/approver_dropdown' - = render_if_exists 'shared/issuable/approved_by_dropdown' - #js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu - %ul{ data: { dropdown: true } } - %li.filter-dropdown-item{ data: { value: 'None' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('None') - %li.filter-dropdown-item{ data: { value: 'Any' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('Any') - %li.filter-dropdown-item{ data: { value: 'Upcoming' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('Upcoming') - %li.filter-dropdown-item{ data: { value: 'Started' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('Started') - %li.divider.droplab-item-ignore - %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } - %li.filter-dropdown-item - %button.gl-button.btn.btn-link.js-data-value{ type: 'button' } + = render_if_exists 'shared/issuable/approver_dropdown' + = render_if_exists 'shared/issuable/approved_by_dropdown' + #js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu + %ul{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'None' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('None') + %li.filter-dropdown-item{ data: { value: 'Any' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('Any') + %li.filter-dropdown-item{ data: { value: 'Upcoming' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('Upcoming') + %li.filter-dropdown-item{ data: { value: 'Started' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('Started') + %li.divider.droplab-item-ignore + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + %li.filter-dropdown-item + %button.gl-button.btn.btn-link.js-data-value{ type: 'button' } + {{title}} + = render_if_exists 'shared/issuable/filter_iteration', type: type + #js-dropdown-release.filtered-search-input-dropdown-menu.dropdown-menu + %ul{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'None' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('None') + %li.filter-dropdown-item{ data: { value: 'Any' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('Any') + %li.divider.droplab-item-ignore + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + %li.filter-dropdown-item + %button.gl-button.btn.btn-link.js-data-value{ type: 'button' } + {{title}} + #js-dropdown-label.filtered-search-input-dropdown-menu.dropdown-menu + %ul{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'None' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('None') + %li.filter-dropdown-item{ data: { value: 'Any' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('Any') + %li.divider.droplab-item-ignore + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + %li.filter-dropdown-item + %button.gl-button.btn.btn-link{ type: 'button' } + %span.dropdown-label-box{ style: 'background: {{color}}' } + %span.label-title.js-data-value {{title}} - = render_if_exists 'shared/issuable/filter_iteration', type: type - #js-dropdown-release.filtered-search-input-dropdown-menu.dropdown-menu - %ul{ data: { dropdown: true } } - %li.filter-dropdown-item{ data: { value: 'None' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('None') - %li.filter-dropdown-item{ data: { value: 'Any' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('Any') - %li.divider.droplab-item-ignore - %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } - %li.filter-dropdown-item - %button.gl-button.btn.btn-link.js-data-value{ type: 'button' } - {{title}} - #js-dropdown-label.filtered-search-input-dropdown-menu.dropdown-menu - %ul{ data: { dropdown: true } } - %li.filter-dropdown-item{ data: { value: 'None' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('None') - %li.filter-dropdown-item{ data: { value: 'Any' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('Any') - %li.divider.droplab-item-ignore - %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } - %li.filter-dropdown-item - %button.gl-button.btn.btn-link{ type: 'button' } - %span.dropdown-label-box{ style: 'background: {{color}}' } - %span.label-title.js-data-value - {{title}} - #js-dropdown-my-reaction.filtered-search-input-dropdown-menu.dropdown-menu - %ul{ data: { dropdown: true } } - %li.filter-dropdown-item{ data: { value: 'None' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('None') - %li.filter-dropdown-item{ data: { value: 'Any' } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('Any') - %li.divider.droplab-item-ignore - %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } - %li.filter-dropdown-item - %button.gl-button.btn.btn-link{ type: 'button' } - %gl-emoji - %span.js-data-value.gl-ml-3 - {{name}} - #js-dropdown-wip.filtered-search-input-dropdown-menu.dropdown-menu - %ul.filter-dropdown{ data: { dropdown: true } } - %li.filter-dropdown-item{ data: { value: 'yes', capitalize: true } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('Yes') - %li.filter-dropdown-item{ data: { value: 'no', capitalize: true } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('No') - #js-dropdown-confidential.filtered-search-input-dropdown-menu.dropdown-menu - %ul.filter-dropdown{ data: { dropdown: true } } - %li.filter-dropdown-item{ data: { value: 'yes', capitalize: true } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('Yes') - %li.filter-dropdown-item{ data: { value: 'no', capitalize: true } } - %button.gl-button.btn.btn-link{ type: 'button' } - = _('No') - - unless disable_target_branch - #js-dropdown-target-branch.filtered-search-input-dropdown-menu.dropdown-menu - %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } - %li.filter-dropdown-item - %button.gl-button.btn.btn-link.js-data-value.monospace - {{title}} - #js-dropdown-environment.filtered-search-input-dropdown-menu.dropdown-menu + #js-dropdown-my-reaction.filtered-search-input-dropdown-menu.dropdown-menu + %ul{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'None' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('None') + %li.filter-dropdown-item{ data: { value: 'Any' } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('Any') + %li.divider.droplab-item-ignore + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + %li.filter-dropdown-item + %button.gl-button.btn.btn-link{ type: 'button' } + %gl-emoji + %span.js-data-value.gl-ml-3 + {{name}} + #js-dropdown-wip.filtered-search-input-dropdown-menu.dropdown-menu + %ul.filter-dropdown{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'yes', capitalize: true } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('Yes') + %li.filter-dropdown-item{ data: { value: 'no', capitalize: true } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('No') + #js-dropdown-confidential.filtered-search-input-dropdown-menu.dropdown-menu + %ul.filter-dropdown{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'yes', capitalize: true } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('Yes') + %li.filter-dropdown-item{ data: { value: 'no', capitalize: true } } + %button.gl-button.btn.btn-link{ type: 'button' } + = _('No') + - unless disable_target_branch + #js-dropdown-target-branch.filtered-search-input-dropdown-menu.dropdown-menu %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %li.filter-dropdown-item - %button.gl-button.btn.btn-link.js-data-value{ type: 'button' } + %button.gl-button.btn.btn-link.js-data-value.monospace {{title}} + #js-dropdown-environment.filtered-search-input-dropdown-menu.dropdown-menu + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + %li.filter-dropdown-item + %button.gl-button.btn.btn-link.js-data-value{ type: 'button' } + {{title}} - = render_if_exists 'shared/issuable/filter_weight', type: type + = render_if_exists 'shared/issuable/filter_weight', type: type - = render_if_exists 'shared/issuable/filter_epic', type: type + = render_if_exists 'shared/issuable/filter_epic', type: type - %button.clear-search.hidden{ type: 'button' } - = sprite_icon('close', size: 16, css_class: 'clear-search-icon') + %button.clear-search.hidden{ type: 'button' } + = sprite_icon('close', size: 16, css_class: 'clear-search-icon') .filter-dropdown-container.gl-display-flex.gl-flex-direction-column.gl-md-flex-direction-row.gl-align-items-flex-start - - if type == :boards - #js-board-labels-toggle - - if current_user - #js-board-epics-swimlanes-toggle - .js-board-config{ data: { can_admin_list: user_can_admin_list.to_s, has_scope: board.scoped?.to_s } } - - if user_can_admin_list - .js-create-column-trigger{ data: board_list_data } - #js-toggle-focus-btn - - elsif type != :productivity_analytics && show_sorting_dropdown + - if type != :productivity_analytics && show_sorting_dropdown = render 'shared/issuable/sort_dropdown' |