diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-27 12:14:36 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-12-27 12:14:36 +0300 |
commit | 7596630be2bb8febb34e782817c8339ba6ec7b2c (patch) | |
tree | e2cbab17c8792538df117050b09ac0a587f1f8b7 /app | |
parent | 0878b072b015ae4db00f4824e2f5444d66d5ae5c (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
6 files changed, 187 insertions, 19 deletions
diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue index dead90eeb71..4f6ca5c5638 100644 --- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue +++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue @@ -7,11 +7,7 @@ import Api from '~/api'; import Tracking from '~/tracking'; import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants'; import { n__, s__, sprintf } from '~/locale'; -import { - memberName, - triggerExternalAlert, - inviteMembersTrackingOptions, -} from 'ee_else_ce/invite_members/utils/member_utils'; +import { memberName, triggerExternalAlert } from 'ee_else_ce/invite_members/utils/member_utils'; import { captureException } from '~/ci/runner/sentry_utils'; import { USERS_FILTER_ALL, @@ -142,9 +138,6 @@ export default { isCelebration() { return this.mode === 'celebrate'; }, - baseTrackingDetails() { - return { label: this.source, celebrate: this.isCelebration }; - }, isTextForAdmin() { return this.isCurrentUserAdmin && Boolean(this.newUsersUrl); }, @@ -265,7 +258,7 @@ export default { this.source = source; this.$root.$emit(BV_SHOW_MODAL, this.modalId); - this.track('render', inviteMembersTrackingOptions(this.baseTrackingDetails)); + this.track('render', { label: this.source }); }, closeModal() { this.$root.$emit(BV_HIDE_MODAL, this.modalId); @@ -335,10 +328,10 @@ export default { return this.newUsersToInvite.find((member) => memberName(member) === username)?.name; }, onCancel() { - this.track('click_cancel', inviteMembersTrackingOptions(this.baseTrackingDetails)); + this.track('click_cancel', { label: this.source }); }, onClose() { - this.track('click_x', inviteMembersTrackingOptions(this.baseTrackingDetails)); + this.track('click_x', { label: this.source }); }, resetFields() { this.clearValidation(); @@ -347,7 +340,7 @@ export default { this.newUsersToInvite = []; }, onInviteSuccess() { - this.track('invite_successful', inviteMembersTrackingOptions(this.baseTrackingDetails)); + this.track('invite_successful', { label: this.source }); if (this.reloadPageOnSubmit) { reloadOnInvitationSuccess(); diff --git a/app/assets/javascripts/invite_members/utils/member_utils.js b/app/assets/javascripts/invite_members/utils/member_utils.js index 52fb5e98f27..7998cb69445 100644 --- a/app/assets/javascripts/invite_members/utils/member_utils.js +++ b/app/assets/javascripts/invite_members/utils/member_utils.js @@ -6,7 +6,3 @@ export function memberName(member) { export function triggerExternalAlert() { return false; } - -export function inviteMembersTrackingOptions(options) { - return { label: options.label }; -} diff --git a/app/assets/javascripts/observability/client.js b/app/assets/javascripts/observability/client.js index 3a793c9dc14..b7e4ae8e8ea 100644 --- a/app/assets/javascripts/observability/client.js +++ b/app/assets/javascripts/observability/client.js @@ -1,3 +1,4 @@ +import { isValidDate } from '~/lib/utils/datetime_utility'; import * as Sentry from '~/sentry/sentry_browser_wrapper'; import axios from '~/lib/utils/axios_utils'; import { logError } from '~/lib/logger'; @@ -128,6 +129,24 @@ function handleAttributeFilter(filterValue, filterOperator, searchParams) { } } +function handlePeriodFilter(rawValue, filterName, filterParams) { + if (rawValue.trim().indexOf(' ') < 0) { + filterParams.append(filterName, rawValue.trim()); + return; + } + + const dateParts = rawValue.split(' - '); + if (dateParts.length === 2) { + const [start, end] = dateParts; + const startDate = new Date(start); + const endDate = new Date(end); + if (isValidDate(startDate) && isValidDate(endDate)) { + filterParams.append('start_time', startDate.toISOString()); + filterParams.append('end_time', endDate.toISOString()); + } + } +} + /** * Builds URLSearchParams from a filter object of type { [filterName]: undefined | null | Array<{operator: String, value: any} } * e.g: @@ -154,6 +173,8 @@ function filterObjToQueryParams(filterObj) { validFilters.forEach(({ operator, value: rawValue }) => { if (filterName === 'attribute') { handleAttributeFilter(rawValue, operator, filterParams); + } else if (filterName === 'period') { + handlePeriodFilter(rawValue, filterName, filterParams); } else { const paramName = getFilterParamName(filterName, operator); let value = rawValue; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/daterange_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/daterange_token.vue new file mode 100644 index 00000000000..8da5f0cca4a --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/daterange_token.vue @@ -0,0 +1,158 @@ +<script> +import { + GlIcon, + GlDaterangePicker, + GlFilteredSearchToken, + GlFilteredSearchSuggestion, + GlOutsideDirective, +} from '@gitlab/ui'; +import { __ } from '~/locale'; +import { formatDate } from '~/lib/utils/datetime/date_format_utility'; + +const CUSTOM_DATE_FILTER_TYPE = 'custom-date'; + +export default { + directives: { Outside: GlOutsideDirective }, + components: { + GlIcon, + GlDaterangePicker, + GlFilteredSearchToken, + GlFilteredSearchSuggestion, + }, + props: { + active: { + type: Boolean, + required: true, + }, + value: { + type: Object, + required: true, + }, + config: { + type: Object, + required: true, + }, + }, + data() { + return { + datePickerShown: false, + }; + }, + computed: { + isActive() { + return this.datePickerShown || this.active; + }, + computedValue() { + if (this.datePickerShown) { + return { + ...this.value, + data: '', + }; + } + return this.value; + }, + dataSegmentInputAttributes() { + const id = 'time_range_data_segment_input'; + if (this.datePickerShown) { + return { + id, + placeholder: 'YYYY-MM-DD - YYYY-MM-DD', // eslint-disable-line @gitlab/require-i18n-strings + style: 'padding-left: 23px;', + }; + } + return { + id, + }; + }, + computedConfig() { + return { + ...this.config, + options: undefined, // remove options from config to avoid default options being rendered + }; + }, + suggestions() { + const suggestions = this.config.options.map((option) => ({ + value: option.value, + text: option.title, + })); + suggestions.push({ value: CUSTOM_DATE_FILTER_TYPE, text: __('Custom') }); + return suggestions; + }, + defaultStartDate() { + return new Date(); + }, + }, + methods: { + hideDatePicker() { + this.datePickerShown = false; + }, + showDatePicker() { + this.datePickerShown = true; + }, + handleClickOutside() { + this.hideDatePicker(); + }, + handleComplete(value) { + if (value === CUSTOM_DATE_FILTER_TYPE) { + this.showDatePicker(); + } + }, + selectValue(inputValue, submitValue) { + let value = inputValue; + if (typeof inputValue === 'object' && inputValue.startDate && inputValue.endDate) { + const { startDate, endDate } = inputValue; + const format = 'yyyy-mm-dd'; + value = `${formatDate(startDate, format)} - ${formatDate(endDate, format)}`; + } + submitValue(value); + this.hideDatePicker(); + }, + }, + CUSTOM_DATE_FILTER_TYPE: 'custom-date', +}; +</script> + +<template> + <gl-filtered-search-token + :data-segment-input-attributes="dataSegmentInputAttributes" + v-bind="{ ...$props, ...$attrs }" + :view-only="datePickerShown" + :active="isActive" + :value="computedValue" + :config="computedConfig" + v-on="$listeners" + @complete="handleComplete" + > + <template #before-data-segment-input="{ submitValue }"> + <gl-icon + v-if="datePickerShown" + class="gl-text-gray-500" + name="calendar" + style="margin-left: 5px; margin-right: -15px; z-index: 1; pointer-events: none" + /> + <div + v-if="datePickerShown" + v-outside="handleClickOutside" + class="gl-absolute gl-z-index-1 gl-bg-white gl-border-1 gl-border-gray-200 gl-my-2 gl-p-4 gl-rounded-base gl-shadow-x0-y2-b4-s0 gl-top-full" + > + <gl-daterange-picker + start-opened + :default-start-date="defaultStartDate" + @input="selectValue($event, submitValue)" + /> + </div> + </template> + + <template #suggestions> + <div v-if="!datePickerShown"> + <gl-filtered-search-suggestion + v-for="token in suggestions" + :key="token.value" + :value="token.value" + > + {{ token.text }} + </gl-filtered-search-suggestion> + </div> + </template> + </gl-filtered-search-token> +</template> diff --git a/app/views/groups/_import_group_from_file_panel.html.haml b/app/views/groups/_import_group_from_file_panel.html.haml index c39f5cf87c7..43a8ccdaae4 100644 --- a/app/views/groups/_import_group_from_file_panel.html.haml +++ b/app/views/groups/_import_group_from_file_panel.html.haml @@ -10,14 +10,14 @@ alert_options: { class: 'gl-mb-5' }, dismissible: false) do |c| - c.with_body do - - docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index', anchor: 'migrate-groups-by-direct-transfer-recommended') } + - docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index') } - link_end = '</a>'.html_safe = s_('GroupsNew|This feature is deprecated and replaced by group migration by direct transfer. %{docs_link_start}Learn more%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: link_end } = render 'shared/groups/group_name_and_path_fields', f: f .form-group = f.label :file, s_('GroupsNew|Upload file') .gl-font-weight-normal - - import_export_link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path('user/group/import/index') } + - import_export_link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path('user/project/settings/import_export', anchor: 'migrate-groups-by-uploading-an-export-file-deprecated') } = s_('GroupsNew|To import a group, navigate to the group settings for the GitLab source instance, %{link_start}generate an export file%{link_end}, and upload it here.').html_safe % { link_start: import_export_link_start, link_end: '</a>'.html_safe } .gl-mt-3 = render 'shared/file_picker_button', f: f, field: :file, help_text: nil diff --git a/app/views/groups/settings/_export.html.haml b/app/views/groups/settings/_export.html.haml index ff1d76f470c..c7909a7c249 100644 --- a/app/views/groups/settings/_export.html.haml +++ b/app/views/groups/settings/_export.html.haml @@ -10,7 +10,7 @@ - c.with_body do = render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mb-4' }) do |c| - c.with_body do - - docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index', anchor: 'migrate-groups-by-direct-transfer-recommended') } + - docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index') } - docs_link_end = '</a>'.html_safe = s_('GroupsNew|This feature is deprecated and replaced by group migration by direct transfer. %{docs_link_start}Learn more%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: docs_link_end } %p |