diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-07 15:09:00 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-07 15:09:00 +0300 |
commit | ba27dbddc7dbc42f2cc8d84e815a9ea19f87a81d (patch) | |
tree | e71fba864897fa78be7f0c40ded23d0f719abf84 /app | |
parent | 708815aefead73a61473c1a611aea169ab16a358 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
21 files changed, 279 insertions, 222 deletions
diff --git a/app/assets/javascripts/error_tracking/components/error_details.vue b/app/assets/javascripts/error_tracking/components/error_details.vue index 9f054b94de7..182090e64f9 100644 --- a/app/assets/javascripts/error_tracking/components/error_details.vue +++ b/app/assets/javascripts/error_tracking/components/error_details.vue @@ -237,7 +237,7 @@ export default { <template> <div> - <div v-if="errorLoading" class="py-3"> + <div v-if="errorLoading" class="gl-py-5"> <gl-loading-icon size="lg" /> </div> @@ -258,23 +258,25 @@ export default { {{ __('No stack trace for this error') }} </gl-alert> - <div class="error-details-header d-flex py-2 justify-content-between"> + <div + class="error-details-header gl-border-b gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-py-3 gl-justify-content-space-between" + > <div v-if="!loadingStacktrace && stacktrace" - class="error-details-meta my-auto" + class="gl-my-auto gl-text-truncate" data-qa-selector="reported_text" > <gl-sprintf :message="__('Reported %{timeAgo} by %{reportedBy}')"> <template #reportedBy> - <strong class="error-details-meta-culprit">{{ error.culprit }}</strong> + <strong>{{ error.culprit }}</strong> </template> <template #timeAgo> <time-ago-tooltip :time="stacktraceData.date_received" /> </template> </gl-sprintf> </div> - <div class="error-details-actions"> - <div class="d-inline-flex bv-d-sm-down-none"> + <div> + <div class="gl-display-none gl-md-display-inline-flex"> <gl-button :loading="updatingIgnoreStatus" data-testid="update-ignore-status-btn" @@ -283,7 +285,7 @@ export default { {{ ignoreBtnLabel }} </gl-button> <gl-button - class="ml-2" + class="gl-ml-3" category="secondary" variant="confirm" :loading="updatingResolveStatus" @@ -294,7 +296,7 @@ export default { </gl-button> <gl-button v-if="error.gitlabIssuePath" - class="ml-2" + class="gl-ml-3" data-testid="view_issue_button" :href="error.gitlabIssuePath" variant="confirm" @@ -305,7 +307,7 @@ export default { ref="sentryIssueForm" :action="projectIssuesPath" method="POST" - class="d-inline-block ml-2" + class="gl-display-inline-block gl-ml-3" > <gl-form-input class="hidden" name="issue[title]" :value="issueTitle" /> <input name="issue[description]" :value="issueDescription" type="hidden" /> @@ -329,7 +331,7 @@ export default { </div> <gl-dropdown text="Options" - class="error-details-options d-md-none" + class="gl-w-full gl-md-display-none" right :disabled="issueUpdateInProgress" > @@ -362,7 +364,7 @@ export default { </div> <div> <tooltip-on-truncate :title="error.title" truncate-target="child" placement="top"> - <h2 class="text-truncate">{{ error.title }}</h2> + <h2 class="gl-text-truncate">{{ error.title }}</h2> </tooltip-on-truncate> <template v-if="error.tags"> <gl-badge v-if="error.tags.level" :variant="errorSeverityVariant" class="gl-mr-3"> @@ -373,12 +375,12 @@ export default { <error-details-info :error="error" /> - <div v-if="loadingStacktrace" class="py-3"> + <div v-if="loadingStacktrace" class="gl-py-5"> <gl-loading-icon size="lg" /> </div> <template v-else-if="showStacktrace"> - <h3 class="my-4">{{ __('Stack trace') }}</h3> + <h3 class="gl-my-6">{{ __('Stack trace') }}</h3> <stacktrace :entries="stacktrace" /> </template> </div> diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue b/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue index a5e712f4fc2..35e8e26ecfb 100644 --- a/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue +++ b/app/assets/javascripts/error_tracking/components/error_tracking_actions.vue @@ -44,37 +44,45 @@ export default { <template> <div> - <gl-button-group class="gl-flex-direction-column flex-md-row gl-ml-0 ml-md-n4"> + <gl-button-group class="gl-flex-direction-column gl-md-flex-direction-row gl-ml-n6"> <gl-button :key="ignoreBtn.status" :ref="`${ignoreBtn.title.toLowerCase()}Error`" v-gl-tooltip.hover - class="gl-display-block gl-mb-4 mb-md-0 gl-w-full" + class="gl-display-block gl-mb-4 gl-md-mb-0 gl-w-full" :title="ignoreBtn.title" :aria-label="ignoreBtn.title" @click="$emit('update-issue-status', { errorId: error.id, status: ignoreBtn.status })" > - <gl-icon class="gl-display-none d-md-inline gl-m-0" :name="ignoreBtn.icon" :size="12" /> - <span class="d-md-none">{{ ignoreBtn.title }}</span> + <gl-icon + class="gl-display-none gl-md-display-inline gl-m-0" + :name="ignoreBtn.icon" + :size="12" + /> + <span class="gl-md-display-none">{{ ignoreBtn.title }}</span> </gl-button> <gl-button :key="resolveBtn.status" :ref="`${resolveBtn.title.toLowerCase()}Error`" v-gl-tooltip.hover - class="gl-display-block gl-mb-4 mb-md-0 gl-w-full" + class="gl-display-block gl-mb-4 gl-md-mb-0 gl-w-full" :title="resolveBtn.title" :aria-label="resolveBtn.title" @click="$emit('update-issue-status', { errorId: error.id, status: resolveBtn.status })" > - <gl-icon class="gl-display-none d-md-inline gl-m-0" :name="resolveBtn.icon" :size="12" /> - <span class="d-md-none">{{ resolveBtn.title }}</span> + <gl-icon + class="gl-display-none gl-md-display-inline gl-m-0" + :name="resolveBtn.icon" + :size="12" + /> + <span class="gl-md-display-none">{{ resolveBtn.title }}</span> </gl-button> </gl-button-group> <gl-button :href="detailsLink" category="primary" variant="confirm" - class="gl-display-block d-md-none gl-mb-4 mb-md-0" + class="gl-display-block gl-md-display-none! gl-mb-4 gl-md-mb-0" > {{ __('More details') }} </gl-button> diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue index 23783bb7bb1..e3784cc8b92 100644 --- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue +++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue @@ -31,8 +31,6 @@ import { import { I18N_ERROR_TRACKING_LIST } from '../constants'; import ErrorTrackingActions from './error_tracking_actions.vue'; -export const tableDataClass = 'table-col d-flex d-md-table-cell align-items-center'; - const isValidErrorId = (errorId) => { return /^[0-9]+$/.test(errorId); }; @@ -46,30 +44,29 @@ export default { key: 'error', label: __('Error'), thClass: 'w-60p', - tdClass: `${tableDataClass} px-3 rounded-top`, }, { key: 'events', label: __('Events'), - thClass: 'text-right', - tdClass: `${tableDataClass}`, + thClass: 'gl-text-right', + tdClass: 'gl-text-right', }, { key: 'users', label: __('Users'), - thClass: 'text-right', - tdClass: `${tableDataClass}`, + thClass: 'gl-text-right', + tdClass: 'gl-text-right', }, { key: 'lastSeen', label: __('Last seen'), - thClass: 'w-15p', - tdClass: `${tableDataClass}`, + thClass: 'gl-w-15p', + tdClass: 'gl-text-left', }, { key: 'status', label: '', - tdClass: `${tableDataClass}`, + tdClass: 'gl-text-center', }, ], statusFilters: { @@ -275,6 +272,7 @@ export default { <template> <div class="error-list"> <div v-if="errorTrackingEnabled"> + <!-- Enable ET --> <gl-alert v-if="showIntegratedDisabledAlert" variant="danger" @@ -303,18 +301,20 @@ export default { </gl-button> </div> </gl-alert> + + <!-- Search / Filter Bar --> <div - class="row flex-column flex-md-row align-items-md-center m-0 mt-sm-2 p-3 p-sm-3 bg-secondary border" + class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-md-align-items-center gl-m-0 gl-p-5 gl-bg-gray-50 gl-border" > - <div class="search-box flex-fill mb-1 mb-md-0"> - <div class="filtered-search-box mb-0"> + <div class="search-box gl-display-flex gl-flex-grow-1 gl-mb-2 gl-md-mb-0"> + <div class="filtered-search-box gl-mb-0"> <gl-dropdown :text="__('Recent searches')" class="filtered-search-history-dropdown-wrapper" toggle-class="filtered-search-history-dropdown-toggle-button gl-shadow-none! gl-border-r-gray-200! gl-border-1! gl-rounded-0!" :disabled="loading" > - <div v-if="!$options.hasLocalStorage" class="px-3"> + <div v-if="!$options.hasLocalStorage" class="gl-px-5"> {{ __('This feature requires local storage to be enabled') }} </div> <template v-else-if="recentSearches.length > 0"> @@ -329,12 +329,12 @@ export default { >{{ __('Clear recent searches') }} </gl-dropdown-item> </template> - <div v-else class="px-3">{{ __("You don't have any recent searches") }}</div> + <div v-else class="gl-px-5">{{ __("You don't have any recent searches") }}</div> </gl-dropdown> <div class="filtered-search-input-container gl-flex-grow-1"> <gl-form-input v-model="errorSearchQuery" - class="pl-2 filtered-search" + class="gl-pl-3! filtered-search" :disabled="loading" :placeholder="__('Search or filter results…')" autofocus @@ -346,7 +346,7 @@ export default { v-if="errorSearchQuery.length > 0" v-gl-tooltip.hover :title="__('Clear')" - class="clear-search text-secondary" + class="clear-search gl-text-secondary" name="clear" icon="close" @click="errorSearchQuery = ''" @@ -357,7 +357,7 @@ export default { <gl-dropdown :text="$options.statusFilters[statusFilter]" - class="status-dropdown mx-md-1 mb-1 mb-md-0" + class="status-dropdown gl-md-ml-2 gl-md-mr-2 gl-mb-2 gl-md-mb-0" :disabled="loading" right > @@ -366,7 +366,7 @@ export default { :key="status" @click="filterErrors(status, label)" > - <span class="d-flex"> + <span class="gl-display-flex"> <gl-icon class="gl-dropdown-item-check-icon" :class="{ invisible: !isCurrentStatusFilter(status) }" @@ -383,7 +383,7 @@ export default { :key="field" @click="sortErrorsByField(field)" > - <span class="d-flex"> + <span class="gl-display-flex"> <gl-icon class="gl-dropdown-item-check-icon" :class="{ invisible: !isCurrentSortField(field) }" @@ -395,58 +395,65 @@ export default { </gl-dropdown> </div> - <div v-if="loading" class="py-3"> + <div v-if="loading" class="gl-py-5"> <gl-loading-icon size="lg" /> </div> + <!-- Results Table --> <template v-else> - <h4 class="d-block d-md-none my-3">{{ __('Open errors') }}</h4> + <h4 class="gl-display-block gl-md-display-none! gl-my-5">{{ __('Open errors') }}</h4> <gl-table - class="error-list-table mt-3" + class="error-list-table gl-mt-5" :items="errors" :fields="$options.fields" :show-empty="true" fixed stacked="md" - tbody-tr-class="table-row mb-4" + tbody-tr-class="table-row" > + <!-- table head --> <template #head(error)> - <div class="d-none d-md-block">{{ __('Open errors') }}</div> + <div class="gl-display-none gl-md-display-block">{{ __('Open errors') }}</div> </template> <template #head(events)="data"> - <div class="text-md-right">{{ data.label }}</div> + {{ data.label }} </template> <template #head(users)="data"> - <div class="text-md-right">{{ data.label }}</div> + {{ data.label }} </template> + <!-- table row --> <template #cell(error)="errors"> - <div class="d-flex flex-column"> - <gl-link class="d-flex mw-100 text-dark" :href="getDetailsLink(errors.item.id)"> - <strong class="text-truncate">{{ errors.item.title.trim() }}</strong> + <div class="gl-display-flex gl-flex-direction-column"> + <gl-link + class="gl-display-flex gl-max-w-full gl-text-body" + :href="getDetailsLink(errors.item.id)" + > + <strong class="gl-text-truncate">{{ errors.item.title.trim() }}</strong> </gl-link> - <span class="text-secondary text-truncate mw-100"> + <span class="gl-text-secondary gl-text-truncate gl-max-w-full"> {{ errors.item.culprit }} </span> </div> </template> + <template #cell(events)="errors"> - <div class="text-right">{{ errors.item.count }}</div> + {{ errors.item.count }} </template> <template #cell(users)="errors"> - <div class="text-right">{{ errors.item.userCount }}</div> + {{ errors.item.userCount }} </template> <template #cell(lastSeen)="errors"> - <div class="text-lg-left text-right"> - <time-ago :time="errors.item.lastSeen" class="text-secondary" /> - </div> + <time-ago :time="errors.item.lastSeen" class="gl-text-secondary" /> </template> + <template #cell(status)="errors"> <error-tracking-actions :error="errors.item" @update-issue-status="updateErrosStatus" /> </template> + <template #empty> {{ __('No errors to display.') }} <gl-link class="js-try-again" @click="restartPolling"> @@ -465,6 +472,7 @@ export default { /> </template> </div> + <!-- Get Started with ET --> <div v-else> <gl-empty-state :title="__('Get started with error tracking')" :svg-path="illustrationPath"> <template #description> diff --git a/app/assets/javascripts/labels/components/promote_label_modal.vue b/app/assets/javascripts/labels/components/promote_label_modal.vue index 298cc20ab35..752fda83d6b 100644 --- a/app/assets/javascripts/labels/components/promote_label_modal.vue +++ b/app/assets/javascripts/labels/components/promote_label_modal.vue @@ -4,6 +4,7 @@ import { createAlert } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { visitUrl } from '~/lib/utils/url_utility'; import { s__, __, sprintf } from '~/locale'; +import { stripQuotes } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils'; import eventHub from '../event_hub'; export default { @@ -52,6 +53,12 @@ export default { }, ); }, + cleanedLabelColor() { + return stripQuotes(this.labelColor); + }, + cleanedLabelTextColor() { + return stripQuotes(this.labelTextColor); + }, }, methods: { onSubmit() { @@ -97,7 +104,7 @@ export default { <template #labelTitle> <span class="label color-label" - :style="`background-color: ${labelColor}; color: ${labelTextColor};`" + :style="`background-color: ${cleanedLabelColor}; color: ${cleanedLabelTextColor};`" > {{ labelTitle }} </span> diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue index 20c5248052b..747e92b9e85 100644 --- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue +++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/components/candidate_detail_row.vue @@ -1,25 +1,11 @@ <script> -import { GlLink } from '@gitlab/ui'; - export default { name: 'CandidateDetailRow', - components: { - GlLink, - }, props: { label: { type: String, required: true, }, - text: { - type: [String, Number], - required: true, - }, - href: { - type: String, - required: false, - default: '', - }, sectionLabel: { type: String, required: false, @@ -34,8 +20,7 @@ export default { <td class="gl-text-secondary gl-font-weight-bold">{{ sectionLabel }}</td> <td class="gl-font-weight-bold">{{ label }}</td> <td> - <gl-link v-if="href" :href="href">{{ text }}</gl-link> - <template v-else>{{ text }}</template> + <slot></slot> </td> </tr> </template> diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue index b7a612a9688..a68fb7d340a 100644 --- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue +++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue @@ -1,4 +1,5 @@ <script> +import { GlAvatarLabeled, GlLink } from '@gitlab/ui'; import ModelExperimentsHeader from '~/ml/experiment_tracking/components/model_experiments_header.vue'; import DeleteButton from '~/ml/experiment_tracking/components/delete_button.vue'; import DetailRow from './components/candidate_detail_row.vue'; @@ -29,6 +30,8 @@ export default { ModelExperimentsHeader, DeleteButton, DetailRow, + GlAvatarLabeled, + GlLink, }, props: { candidate: { @@ -94,52 +97,51 @@ export default { <tbody> <tr class="divider"></tr> - <detail-row - :label="$options.i18n.ID_LABEL" - :section-label="$options.i18n.INFO_LABEL" - :text="info.iid" - /> + <detail-row :label="$options.i18n.ID_LABEL" :section-label="$options.i18n.INFO_LABEL"> + {{ info.iid }} + </detail-row> - <detail-row :label="$options.i18n.MLFLOW_ID_LABEL" :text="info.eid" /> + <detail-row :label="$options.i18n.MLFLOW_ID_LABEL">{{ info.eid }}</detail-row> - <detail-row :label="$options.i18n.STATUS_LABEL" :text="info.status" /> + <detail-row :label="$options.i18n.STATUS_LABEL">{{ info.status }}</detail-row> - <detail-row - :label="$options.i18n.EXPERIMENT_LABEL" - :text="info.experiment_name" - :href="info.path_to_experiment" - /> + <detail-row :label="$options.i18n.EXPERIMENT_LABEL"> + <gl-link :href="info.path_to_experiment"> + {{ info.experiment_name }} + </gl-link> + </detail-row> - <detail-row - v-if="info.path_to_artifact" - :label="$options.i18n.ARTIFACTS_LABEL" - :href="info.path_to_artifact" - :text="$options.i18n.ARTIFACTS_LABEL" - /> + <detail-row v-if="info.path_to_artifact" :label="$options.i18n.ARTIFACTS_LABEL"> + <gl-link :href="info.path_to_artifact"> + {{ $options.i18n.ARTIFACTS_LABEL }} + </gl-link> + </detail-row> <template v-if="ciJob"> <tr class="divider"></tr> <detail-row :label="$options.i18n.JOB_LABEL" - :text="ciJob.name" - :href="ciJob.path" :section-label="$options.i18n.CI_SECTION_LABEL" - /> + > + <gl-link :href="ciJob.path"> + {{ ciJob.name }} + </gl-link> + </detail-row> - <detail-row - v-if="ciJob.user" - :label="$options.i18n.CI_USER_LABEL" - :href="ciJob.user.path" - :text="ciJob.user.username" - /> + <detail-row v-if="ciJob.user" :label="$options.i18n.CI_USER_LABEL"> + <gl-avatar-labeled label="" :size="24" :src="ciJob.user.avatar"> + <gl-link :href="ciJob.user.path"> + {{ ciJob.user.name }} + </gl-link> + </gl-avatar-labeled> + </detail-row> - <detail-row - v-if="ciJob.merge_request" - :label="$options.i18n.CI_MR_LABEL" - :text="ciJob.merge_request.title" - :href="ciJob.merge_request.path" - /> + <detail-row v-if="ciJob.merge_request" :label="$options.i18n.CI_MR_LABEL"> + <gl-link :href="ciJob.merge_request.path"> + !{{ ciJob.merge_request.iid }} {{ ciJob.merge_request.title }} + </gl-link> + </detail-row> </template> <template v-for="{ sectionName, sectionValues } in sections"> @@ -150,8 +152,9 @@ export default { :key="item.name" :label="item.name" :section-label="index === 0 ? sectionName : ''" - :text="item.value" - /> + > + {{ item.value }} + </detail-row> </template> </tbody> </table> diff --git a/app/assets/javascripts/notes/components/discussion_notes.vue b/app/assets/javascripts/notes/components/discussion_notes.vue index 3e8cddc3174..a3034e550ed 100644 --- a/app/assets/javascripts/notes/components/discussion_notes.vue +++ b/app/assets/javascripts/notes/components/discussion_notes.vue @@ -82,6 +82,9 @@ export default { url: this.discussion.discussion_path, }; }, + isDiscussionInternal() { + return this.discussion.notes[0]?.internal; + }, }, methods: { ...mapActions(['toggleDiscussion', 'setSelectedCommentPositionHover']), @@ -139,6 +142,7 @@ export default { :discussion-resolve-path="discussion.resolve_path" :is-overview-tab="isOverviewTab" :should-scroll-to-note="shouldScrollToNote" + :internal-note="isDiscussionInternal" @handleDeleteNote="$emit('deleteNote')" @startReplying="$emit('startReplying')" > @@ -171,6 +175,7 @@ export default { :note="componentData(note)" :help-page-path="helpPagePath" :line="line" + :internal-note="isDiscussionInternal" @handleDeleteNote="$emit('deleteNote')" /> </template> @@ -190,6 +195,7 @@ export default { :discussion-resolve-path="discussion.resolve_path" :is-overview-tab="isOverviewTab" :should-scroll-to-note="shouldScrollToNote" + :internal-note="isDiscussionInternal" @handleDeleteNote="$emit('deleteNote')" > <template #avatar-badge> diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index f5fe2cc1284..2feca0bd7a5 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -163,6 +163,15 @@ export default { return true; }, + isDiscussionInternal() { + return this.discussion.notes[0]?.internal; + }, + discussionHolderClass() { + return { + 'is-replying gl-pt-0!': this.isReplying, + 'internal-note': this.isDiscussionInternal, + }; + }, }, created() { eventHub.$on('startReplying', this.onStartReplying); @@ -318,8 +327,9 @@ export default { /> <li v-else-if="canShowReplyActions && showReplies" - :class="{ 'is-replying gl-bg-white! gl-pt-0!': isReplying }" + data-testid="reply-wrapper" class="discussion-reply-holder gl-border-t-0! clearfix" + :class="discussionHolderClass" > <discussion-actions v-if="!isReplying && userCanReply" diff --git a/app/assets/javascripts/sidebar/components/status/status_dropdown.vue b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue index 7763ec00091..69ec4214712 100644 --- a/app/assets/javascripts/sidebar/components/status/status_dropdown.vue +++ b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue @@ -1,39 +1,35 @@ <script> -import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { GlCollapsibleListbox } from '@gitlab/ui'; import { __ } from '~/locale'; import { statusDropdownOptions } from '../../constants'; export default { components: { - GlDropdown, - GlDropdownItem, + GlCollapsibleListbox, }, data() { return { status: null, + selectedValue: undefined, }; }, computed: { dropdownText() { - return this.status?.text ?? this.$options.i18n.defaultDropdownText; - }, - selectedValue() { - return this.status?.value; + const selected = this.$options.statusDropdownOptions.find( + (option) => option.value === this.selectedValue, + ); + return selected?.text || this.$options.i18n.defaultDropdownText; }, }, methods: { - onDropdownItemClick(statusOption) { - // clear status if the currently checked status is clicked again - if (this.status?.value === statusOption.value) { - this.status = null; - } else { - this.status = statusOption; - } + handleReset() { + this.selectedValue = undefined; }, }, i18n: { dropdownTitle: __('Change status'), defaultDropdownText: __('Select status'), + resetText: __('Reset'), }, statusDropdownOptions, }; @@ -41,17 +37,14 @@ export default { <template> <div> <input type="hidden" name="update[state_event]" :value="selectedValue" /> - <gl-dropdown :text="dropdownText" :title="$options.i18n.dropdownTitle" class="gl-w-full"> - <gl-dropdown-item - v-for="statusOption in $options.statusDropdownOptions" - :key="statusOption.value" - :is-checked="selectedValue === statusOption.value" - is-check-item - :title="statusOption.text" - @click="onDropdownItemClick(statusOption)" - > - {{ statusOption.text }} - </gl-dropdown-item> - </gl-dropdown> + <gl-collapsible-listbox + v-model="selectedValue" + block + :header-text="$options.i18n.dropdownTitle" + :reset-button-label="$options.i18n.resetText" + :toggle-text="dropdownText" + :items="$options.statusDropdownOptions" + @reset="handleReset" + /> </div> </template> diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue index 4c3ba76d12d..bacbe5d46a6 100644 --- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions_dropdown.vue @@ -1,5 +1,5 @@ <script> -import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import { GlCollapsibleListbox } from '@gitlab/ui'; import { __ } from '~/locale'; import { subscriptionsDropdownOptions } from '../../constants'; @@ -8,27 +8,27 @@ export default { i18n: { defaultDropdownText: __('Select subscription'), headerText: __('Change subscription'), + resetText: __('Reset'), }, components: { - GlDropdown, - GlDropdownItem, + GlCollapsibleListbox, }, data() { return { - subscription: undefined, + selectedValue: undefined, }; }, computed: { dropdownText() { - return this.subscription?.text ?? this.$options.i18n.defaultDropdownText; - }, - selectedValue() { - return this.subscription?.value; + const selected = this.$options.subscriptionsDropdownOptions.find( + (option) => option.value === this.selectedValue, + ); + return selected?.text || this.$options.i18n.defaultDropdownText; }, }, methods: { - handleClick(option) { - this.subscription = option.value === this.subscription?.value ? undefined : option; + handleReset() { + this.selectedValue = undefined; }, }, }; @@ -36,16 +36,14 @@ export default { <template> <div> <input type="hidden" name="update[subscription_event]" :value="selectedValue" /> - <gl-dropdown class="gl-w-full" :header-text="$options.i18n.headerText" :text="dropdownText"> - <gl-dropdown-item - v-for="subscriptionsOption in $options.subscriptionsDropdownOptions" - :key="subscriptionsOption.value" - is-check-item - :is-checked="selectedValue === subscriptionsOption.value" - @click="handleClick(subscriptionsOption)" - > - {{ subscriptionsOption.text }} - </gl-dropdown-item> - </gl-dropdown> + <gl-collapsible-listbox + v-model="selectedValue" + block + :header-text="$options.i18n.headerText" + :reset-button-label="$options.i18n.resetText" + :toggle-text="dropdownText" + :items="$options.subscriptionsDropdownOptions" + @reset="handleReset" + /> </div> </template> diff --git a/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js b/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js index 1a359533435..2687ea5ccf8 100644 --- a/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js +++ b/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js @@ -24,7 +24,7 @@ export const toggleSuperSidebarCollapsed = (collapsed, saveCookie) => { findPage().classList.toggle(SIDEBAR_COLLAPSED_CLASS, collapsed); sidebarState.isPeek = false; - sidebarState.isPeekable = Boolean(gon.features?.superSidebarPeek) && collapsed; + sidebarState.isPeekable = collapsed; sidebarState.isCollapsed = collapsed; if (saveCookie && isDesktopBreakpoint()) { diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue index 748d6082abd..57b19620c10 100644 --- a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue @@ -45,18 +45,31 @@ export default { required: false, default: false, }, + internalNote: { + type: Boolean, + required: false, + default: false, + }, }, computed: { ...mapGetters(['getUserData']), renderedNote() { return renderMarkdown(this.note.body); }, + internalNoteClass() { + return { + 'internal-note': this.internalNote, + }; + }, }, }; </script> <template> - <timeline-entry-item class="note note-wrapper note-comment being-posted fade-in-half"> + <timeline-entry-item + class="note note-wrapper note-comment being-posted fade-in-half" + :class="internalNoteClass" + > <div class="timeline-avatar gl-float-left"> <gl-avatar-link :href="getUserData.path"> <gl-avatar diff --git a/app/assets/stylesheets/page_bundles/error_tracking_details.scss b/app/assets/stylesheets/page_bundles/error_tracking_details.scss index a47c5cc9b3e..9b93fa7f6d8 100644 --- a/app/assets/stylesheets/page_bundles/error_tracking_details.scss +++ b/app/assets/stylesheets/page_bundles/error_tracking_details.scss @@ -1,36 +1,5 @@ @import 'page_bundles/mixins_and_variables_and_functions'; -.error-details { - li { - @include gl-line-height-32; - } - - .btn-outline-info { - color: var(--blue-500, $blue-500); - border-color: var(--blue-500, $blue-500); - } - - .error-details-header { - border-bottom: 1px solid var(--border-color, $border-color); - - @include media-breakpoint-down(xs) { - flex-flow: column; - - .error-details-meta-culprit { - display: flex; - } - - .error-details-options { - width: 100%; - - .dropdown-toggle { - text-align: center; - } - } - } - } -} - .stacktrace { .file-title { svg { diff --git a/app/assets/stylesheets/page_bundles/error_tracking_index.scss b/app/assets/stylesheets/page_bundles/error_tracking_index.scss index 5c49bcc0348..4baab693aed 100644 --- a/app/assets/stylesheets/page_bundles/error_tracking_index.scss +++ b/app/assets/stylesheets/page_bundles/error_tracking_index.scss @@ -1,29 +1,13 @@ @import 'page_bundles/mixins_and_variables_and_functions'; .error-list { - .dropdown { - min-width: auto; - } - .filtered-search-box .form-control { min-width: unset; } - .sort-control { - .btn { - padding-right: 2rem; - } - - .gl-dropdown-caret { - position: absolute; - right: 0.5rem; - top: 0.5rem; - } - } - @include media-breakpoint-down(sm) { .error-list-table { - .table-col { + td { min-height: 68px; &:last-child { diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 8d2146cddc5..3c96e49499f 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -87,12 +87,14 @@ class Admin::UsersController < Admin::ApplicationController end def activate - if user.blocked? - return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user must be unblocked to be activated")) - end + activate_service = Users::ActivateService.new(current_user) + result = activate_service.execute(user) - user.activate - redirect_back_or_admin_user(notice: _("Successfully activated")) + if result.success? + redirect_back_or_admin_user(notice: _("Successfully activated")) + else + redirect_back_or_admin_user(alert: result.message) + end end def deactivate diff --git a/app/controllers/jira_connect/app_descriptor_controller.rb b/app/controllers/jira_connect/app_descriptor_controller.rb index 3c50d54fa10..2c498820a1e 100644 --- a/app/controllers/jira_connect/app_descriptor_controller.rb +++ b/app/controllers/jira_connect/app_descriptor_controller.rb @@ -8,7 +8,7 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController skip_before_action :verify_atlassian_jwt! def show - render json: { + result = { name: Atlassian::JiraConnect.app_name, description: 'Integrate commits, branches and merge requests from GitLab into Jira', key: Atlassian::JiraConnect.app_key, @@ -36,10 +36,15 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController gdpr: true } } + + result[:links][:feedback] = URI.join(HOME_URL, FEEDBACK_URL) if Feature.enabled?(:jira_for_cloud_app_feedback_link) + + render json: result end private + FEEDBACK_URL = '/gitlab-org/gitlab/-/issues/413652' HOME_URL = 'https://gitlab.com' DOC_URL = 'https://docs.gitlab.com/ee/integration/jira/' diff --git a/app/models/concerns/packages/downloadable.rb b/app/models/concerns/packages/downloadable.rb new file mode 100644 index 00000000000..011f5ddda9c --- /dev/null +++ b/app/models/concerns/packages/downloadable.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Packages + module Downloadable + extend ActiveSupport::Concern + + def touch_last_downloaded_at + ::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do + update_column(:last_downloaded_at, Time.zone.now) + end + end + end +end + +Packages::Downloadable.prepend_mod diff --git a/app/models/packages/npm/metadata_cache.rb b/app/models/packages/npm/metadata_cache.rb index 7a7c66d7a45..02efeda69cb 100644 --- a/app/models/packages/npm/metadata_cache.rb +++ b/app/models/packages/npm/metadata_cache.rb @@ -4,6 +4,7 @@ module Packages module Npm class MetadataCache < ApplicationRecord include FileStoreMounter + include Packages::Downloadable belongs_to :project, inverse_of: :npm_metadata_caches diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb index f1044f865aa..9888a139760 100644 --- a/app/models/packages/package.rb +++ b/app/models/packages/package.rb @@ -6,6 +6,7 @@ class Packages::Package < ApplicationRecord include UsageStatistics include Gitlab::Utils::StrongMemoize include Packages::Installable + include Packages::Downloadable DISPLAYABLE_STATUSES = [:default, :error].freeze INSTALLABLE_STATUSES = [:default, :hidden].freeze @@ -359,12 +360,6 @@ class Packages::Package < ApplicationRecord name.gsub(/#{Gitlab::Regex::Packages::PYPI_NORMALIZED_NAME_REGEX_STRING}/o, '-').downcase end - def touch_last_downloaded_at - ::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do - update_column(:last_downloaded_at, Time.zone.now) - end - end - def publish_creation_event ::Gitlab::EventStore.publish( ::Packages::PackageCreatedEvent.new(data: { @@ -437,5 +432,3 @@ class Packages::Package < ApplicationRecord end end end - -Packages::Package.prepend_mod diff --git a/app/presenters/ml/candidate_details_presenter.rb b/app/presenters/ml/candidate_details_presenter.rb index 58ec2aee471..7f0bd9d6c11 100644 --- a/app/presenters/ml/candidate_details_presenter.rb +++ b/app/presenters/ml/candidate_details_presenter.rb @@ -53,7 +53,9 @@ module Ml { user: { path: user_path(user), - username: user.username + username: user.username, + name: user.name, + avatar: user.avatar_url } } end @@ -64,6 +66,7 @@ module Ml { merge_request: { path: project_merge_request_path(mr.project, mr), + iid: mr.iid, title: mr.title } } diff --git a/app/services/users/activate_service.rb b/app/services/users/activate_service.rb new file mode 100644 index 00000000000..dfc2996bcce --- /dev/null +++ b/app/services/users/activate_service.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Users + class ActivateService < BaseService + def initialize(current_user) + @current_user = current_user + end + + def execute(user) + return error(_('You are not authorized to perform this action'), :forbidden) unless allowed? + + return error(_('Error occurred. A blocked user must be unblocked to be activated'), :forbidden) if user.blocked? + + return success(_('Successfully activated')) if user.active? + + if user.activate + after_activate_hook(user) + log_event(user) + success(_('Successfully activated')) + else + error(user.errors.full_messages.to_sentence, :unprocessable_entity) + end + end + + private + + attr_reader :current_user + + def allowed? + can?(current_user, :admin_all_resources) + end + + def after_activate_hook(user) + # overridden by EE module + end + + def log_event(user) + Gitlab::AppLogger.info(message: 'User activated', user: user.username.to_s, email: user.email.to_s, + activated_by: current_user.username.to_s, ip_address: current_user.current_sign_in_ip.to_s) + end + + def success(message) + ::ServiceResponse.success(message: message) + end + + def error(message, reason) + ::ServiceResponse.error(message: message, reason: reason) + end + end +end + +Users::ActivateService.prepend_mod_with('Users::ActivateService') # rubocop: disable Cop/InjectEnterpriseEditionModule |