diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
commit | a09983ae35713f5a2bbb100981116d31ce99826e (patch) | |
tree | 2ee2af7bd104d57086db360a7e6d8c9d5d43667a /app/assets/javascripts/pages/projects | |
parent | 18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff) | |
download | gitlab-ce-a09983ae35713f5a2bbb100981116d31ce99826e.tar.gz |
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'app/assets/javascripts/pages/projects')
20 files changed, 457 insertions, 228 deletions
diff --git a/app/assets/javascripts/pages/projects/clusters/show/cluster_health.js b/app/assets/javascripts/pages/projects/clusters/show/cluster_health.js new file mode 100644 index 00000000000..382d39645a9 --- /dev/null +++ b/app/assets/javascripts/pages/projects/clusters/show/cluster_health.js @@ -0,0 +1,18 @@ +import monitoringApp from '~/monitoring/monitoring_app'; + +export default () => { + const el = document.getElementById('prometheus-graphs'); + + if (el && el.dataset) { + monitoringApp({ + ...el.dataset, + showLegend: false, + showHeader: false, + showPanels: false, + forceSmallGraph: true, + smallEmptyState: true, + currentEnvironmentName: '', + hasMetrics: true, + }); + } +}; diff --git a/app/assets/javascripts/pages/projects/clusters/show/index.js b/app/assets/javascripts/pages/projects/clusters/show/index.js index 397f9faf6fe..d20e2c19583 100644 --- a/app/assets/javascripts/pages/projects/clusters/show/index.js +++ b/app/assets/javascripts/pages/projects/clusters/show/index.js @@ -1,7 +1,9 @@ import ClustersBundle from '~/clusters/clusters_bundle'; import initGkeNamespace from '~/create_cluster/gke_cluster_namespace'; +import initClusterHealth from './cluster_health'; document.addEventListener('DOMContentLoaded', () => { new ClustersBundle(); // eslint-disable-line no-new initGkeNamespace(); + initClusterHealth(); }); diff --git a/app/assets/javascripts/pages/projects/commit/pipelines/index.js b/app/assets/javascripts/pages/projects/commit/pipelines/index.js index 9f08260c3d6..1415a6f60c8 100644 --- a/app/assets/javascripts/pages/projects/commit/pipelines/index.js +++ b/app/assets/javascripts/pages/projects/commit/pipelines/index.js @@ -1,6 +1,7 @@ import $ from 'jquery'; import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown'; import initPipelines from '~/commit/pipelines/pipelines_bundle'; +import { fetchCommitMergeRequests } from '~/commit_merge_requests'; document.addEventListener('DOMContentLoaded', () => { new MiniPipelineGraph({ @@ -8,5 +9,6 @@ document.addEventListener('DOMContentLoaded', () => { }).bindEvents(); // eslint-disable-next-line no-jquery/no-load $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); + fetchCommitMergeRequests(); initPipelines(); }); diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js index 9fb07917f9b..e65c18c07a9 100644 --- a/app/assets/javascripts/pages/projects/edit/index.js +++ b/app/assets/javascripts/pages/projects/edit/index.js @@ -7,13 +7,20 @@ import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory'; import initFilePickers from '~/file_pickers'; import initProjectLoadingSpinner from '../shared/save_project_loader'; import initProjectPermissionsSettings from '../shared/permissions'; +import initProjectRemoveModal from '~/projects/project_remove_modal'; +import UserCallout from '~/user_callout'; +import initServiceDesk from '~/projects/settings_service_desk'; document.addEventListener('DOMContentLoaded', () => { initFilePickers(); initConfirmDangerModal(); initSettingsPanels(); + initProjectRemoveModal(); mountBadgeSettings(PROJECT_BADGE); + new UserCallout({ className: 'js-service-desk-callout' }); // eslint-disable-line no-new + initServiceDesk(); + initProjectLoadingSpinner(); initProjectPermissionsSettings(); setupTransferEdit('.js-project-transfer-form', 'select.select2'); diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue new file mode 100644 index 00000000000..77753521342 --- /dev/null +++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue @@ -0,0 +1,91 @@ +<script> +import { GlTabs, GlTab, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui'; +import axios from '~/lib/utils/axios_utils'; +import { __ } from '~/locale'; +import createFlash from '~/flash'; +import ForkGroupsListItem from './fork_groups_list_item.vue'; + +export default { + components: { + GlTabs, + GlTab, + GlLoadingIcon, + GlSearchBoxByType, + ForkGroupsListItem, + }, + props: { + hasReachedProjectLimit: { + type: Boolean, + required: true, + }, + endpoint: { + type: String, + required: true, + }, + }, + data() { + return { + namespaces: null, + filter: '', + }; + }, + computed: { + filteredNamespaces() { + return this.namespaces.filter(n => n.name.toLowerCase().includes(this.filter.toLowerCase())); + }, + }, + + mounted() { + this.loadGroups(); + }, + + methods: { + loadGroups() { + axios + .get(this.endpoint) + .then(response => { + this.namespaces = response.data.namespaces; + }) + .catch(() => createFlash(__('There was a problem fetching groups.'))); + }, + }, + + i18n: { + searchPlaceholder: __('Search by name'), + }, +}; +</script> +<template> + <gl-tabs class="fork-groups"> + <gl-tab :title="__('Groups and subgroups')"> + <gl-loading-icon v-if="!namespaces" size="md" class="gl-mt-3" /> + <template v-else-if="namespaces.length === 0"> + <div class="gl-text-center"> + <div class="h5">{{ __('No available groups to fork the project.') }}</div> + <p class="gl-mt-5"> + {{ __('You must have permission to create a project in a group before forking.') }} + </p> + </div> + </template> + <div v-else-if="filteredNamespaces.length === 0" class="gl-text-center gl-mt-3"> + {{ s__('GroupsTree|No groups matched your search') }} + </div> + <ul v-else class="groups-list group-list-tree"> + <fork-groups-list-item + v-for="(namespace, index) in filteredNamespaces" + :key="index" + :group="namespace" + :has-reached-project-limit="hasReachedProjectLimit" + /> + </ul> + </gl-tab> + <template #tabs-end> + <gl-search-box-by-type + v-if="namespaces && namespaces.length" + v-model="filter" + :placeholder="$options.i18n.searchPlaceholder" + class="gl-align-self-center gl-ml-auto fork-filtered-search" + /> + </template> + </gl-tabs> +</template> diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue new file mode 100644 index 00000000000..792c2f3db34 --- /dev/null +++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue @@ -0,0 +1,147 @@ +<script> +import { + GlLink, + GlButton, + GlIcon, + GlAvatar, + GlTooltipDirective, + GlTooltip, + GlBadge, +} from '@gitlab/ui'; +import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '~/groups/constants'; +import { __ } from '~/locale'; +import csrf from '~/lib/utils/csrf'; + +export default { + components: { + GlIcon, + GlAvatar, + GlBadge, + GlButton, + GlTooltip, + GlLink, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + props: { + group: { + type: Object, + required: true, + }, + hasReachedProjectLimit: { + type: Boolean, + required: true, + }, + }, + data() { + return { namespaces: null }; + }, + + computed: { + rowClass() { + return { + 'has-description': this.group.description, + 'being-removed': this.isGroupPendingRemoval, + }; + }, + isGroupPendingRemoval() { + return this.group.marked_for_deletion; + }, + hasForkedProject() { + return Boolean(this.group.forked_project_path); + }, + visibilityIcon() { + return VISIBILITY_TYPE_ICON[this.group.visibility]; + }, + visibilityTooltip() { + return GROUP_VISIBILITY_TYPE[this.group.visibility]; + }, + isSelectButtonDisabled() { + return this.hasReachedProjectLimit || !this.group.can_create_project; + }, + selectButtonDisabledTooltip() { + return this.hasReachedProjectLimit + ? this.$options.i18n.hasReachedProjectLimitMessage + : this.$options.i18n.insufficientPermissionsMessage; + }, + }, + + i18n: { + hasReachedProjectLimitMessage: __('You have reached your project limit'), + insufficientPermissionsMessage: __( + 'You must have permission to create a project in a namespace before forking.', + ), + }, + + csrf, +}; +</script> +<template> + <li :class="rowClass" class="group-row"> + <div class="group-row-contents gl-display-flex gl-align-items-center gl-py-3 gl-pr-5"> + <div class="folder-toggle-wrap gl-mr-2 gl-display-flex gl-align-items-center"> + <gl-icon name="folder-o" /> + </div> + <gl-link + :href="group.relative_path" + class="gl-display-none gl-flex-shrink-0 gl-display-sm-flex gl-mr-3" + > + <gl-avatar :size="32" shape="rect" :entity-name="group.name" :src="group.avatarUrl" /> + </gl-link> + <div class="gl-min-w-0 gl-display-flex gl-flex-grow-1 gl-flex-shrink-1 gl-align-items-center"> + <div class="gl-min-w-0 gl-flex-grow-1 flex-shrink-1"> + <div class="title gl-display-flex gl-align-items-center gl-flex-wrap gl-mr-3"> + <gl-link :href="group.relative_path" class="gl-mt-3 gl-mr-3 gl-text-gray-900!">{{ + group.full_name + }}</gl-link> + <gl-icon + v-gl-tooltip.hover.bottom + class="gl-mr-0 gl-inline-flex gl-mt-3 text-secondary" + :name="visibilityIcon" + :title="visibilityTooltip" + /> + <gl-badge + v-if="isGroupPendingRemoval" + variant="warning" + class="gl-display-none gl-display-sm-flex gl-mt-3 gl-mr-1" + >{{ __('pending removal') }}</gl-badge + > + <span v-if="group.permission" class="user-access-role gl-mt-3"> + {{ group.permission }} + </span> + </div> + <div v-if="group.description" class="description"> + <span v-html="group.markdown_description"> </span> + </div> + </div> + <div class="gl-display-flex gl-flex-shrink-0"> + <gl-button + v-if="hasForkedProject" + class="gl-h-7 gl-text-decoration-none!" + :href="group.forked_project_path" + >{{ __('Go to fork') }}</gl-button + > + <template v-else> + <div ref="selectButtonWrapper"> + <form method="POST" :action="group.fork_path"> + <input type="hidden" name="authenticity_token" :value="$options.csrf.token" /> + <gl-button + type="submit" + class="gl-h-7 gl-text-decoration-none!" + :data-qa-name="group.full_name" + variant="success" + :disabled="isSelectButtonDisabled" + >{{ __('Select') }}</gl-button + > + </form> + </div> + <gl-tooltip v-if="isSelectButtonDisabled" :target="() => $refs.selectButtonWrapper"> + {{ selectButtonDisabledTooltip }} + </gl-tooltip> + </template> + </div> + </div> + </div> + </li> +</template> diff --git a/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue index af8fb032c22..39d6df33a85 100644 --- a/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue +++ b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue @@ -63,17 +63,19 @@ export default { selectedDailyCoverageName() { return this.selectedDailyCoverage?.group_name; }, - formattedData() { - if (this.selectedDailyCoverage?.data) { - return this.selectedDailyCoverage.data.map(value => [ - dateFormat(value.date, 'mmm dd'), - value.coverage, - ]); - } - + sortedData() { // If the fetching failed, we return an empty array which // allow the graph to render while empty - return []; + if (!this.selectedDailyCoverage?.data) { + return []; + } + + return [...this.selectedDailyCoverage.data].sort( + (a, b) => new Date(a.date) - new Date(b.date), + ); + }, + formattedData() { + return this.sortedData.map(value => [dateFormat(value.date, 'mmm dd'), value.coverage]); }, chartData() { return [ diff --git a/app/assets/javascripts/pages/projects/integrations/jira/issues/index/index.js b/app/assets/javascripts/pages/projects/integrations/jira/issues/index/index.js new file mode 100644 index 00000000000..260ba69b4bc --- /dev/null +++ b/app/assets/javascripts/pages/projects/integrations/jira/issues/index/index.js @@ -0,0 +1,5 @@ +import initIssuablesList from '~/issuables_list'; + +document.addEventListener('DOMContentLoaded', () => { + initIssuablesList(); +}); diff --git a/app/assets/javascripts/pages/projects/issues/service_desk/filtered_search.js b/app/assets/javascripts/pages/projects/issues/service_desk/filtered_search.js new file mode 100644 index 00000000000..72003b61c8a --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/service_desk/filtered_search.js @@ -0,0 +1,30 @@ +/* eslint-disable class-methods-use-this */ +import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys'; +import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager'; + +const AUTHOR_PARAM_KEY = 'author_username'; + +export default class FilteredSearchServiceDesk extends FilteredSearchManager { + constructor(supportBotData) { + super({ + page: 'service_desk', + filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys, + }); + + this.supportBotData = supportBotData; + } + + canEdit(tokenName) { + return tokenName !== 'author'; + } + + modifyUrlParams(paramsArray) { + const supportBotParamPair = `${AUTHOR_PARAM_KEY}=${this.supportBotData.username}`; + const onlyValidParams = paramsArray.filter(param => param.indexOf(AUTHOR_PARAM_KEY) === -1); + + // unshift ensures author param is always first token element + onlyValidParams.unshift(supportBotParamPair); + + return onlyValidParams; + } +} diff --git a/app/assets/javascripts/pages/projects/issues/service_desk/index.js b/app/assets/javascripts/pages/projects/issues/service_desk/index.js new file mode 100644 index 00000000000..56054f5fc80 --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/service_desk/index.js @@ -0,0 +1,11 @@ +import FilteredSearchServiceDesk from './filtered_search'; + +document.addEventListener('DOMContentLoaded', () => { + const supportBotData = JSON.parse( + document.querySelector('.js-service-desk-issues').dataset.supportBot, + ); + + const filteredSearchManager = new FilteredSearchServiceDesk(supportBotData); + + filteredSearchManager.setup(); +}); diff --git a/app/assets/javascripts/pages/projects/issues/show.js b/app/assets/javascripts/pages/projects/issues/show.js index 46c9b2fe0af..32f77465347 100644 --- a/app/assets/javascripts/pages/projects/issues/show.js +++ b/app/assets/javascripts/pages/projects/issues/show.js @@ -3,7 +3,7 @@ import Issue from '~/issue'; import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable'; import ZenMode from '~/zen_mode'; import '~/notes/index'; -import initIssueableApp from '~/issue_show'; +import initIssueableApp, { issuableHeaderWarnings } from '~/issue_show'; import initSentryErrorStackTraceApp from '~/sentry_error_stack_trace'; import initRelatedMergeRequestsApp from '~/related_merge_requests'; import initVueIssuableSidebarApp from '~/issuable_sidebar/sidebar_bundle'; @@ -12,15 +12,17 @@ export default function() { initIssueableApp(); initSentryErrorStackTraceApp(); initRelatedMergeRequestsApp(); + issuableHeaderWarnings(); - // .js-design-management is currently EE-only. - // This will be moved to CE as part of https://gitlab.com/gitlab-org/gitlab/-/issues/212566#frontend - // at which point this conditional can be removed. - if (document.querySelector('.js-design-management')) { - import(/* webpackChunkName: 'design_management' */ '~/design_management') - .then(module => module.default()) - .catch(() => {}); - } + import(/* webpackChunkName: 'design_management' */ '~/design_management') + .then(module => module.default()) + .catch(() => {}); + + // This will be removed when we remove the `design_management_moved` feature flag + // See https://gitlab.com/gitlab-org/gitlab/-/issues/223197 + import(/* webpackChunkName: 'design_management' */ '~/design_management_new') + .then(module => module.default()) + .catch(() => {}); new Issue(); // eslint-disable-line no-new new ShortcutsIssuable(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/pages/projects/metrics_dashboard/index.js b/app/assets/javascripts/pages/projects/metrics_dashboard/index.js new file mode 100644 index 00000000000..d3028aec313 --- /dev/null +++ b/app/assets/javascripts/pages/projects/metrics_dashboard/index.js @@ -0,0 +1,3 @@ +import monitoringApp from '~/monitoring/monitoring_app'; + +document.addEventListener('DOMContentLoaded', monitoringApp); diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue index 4efabcb7df3..5ef1f959b2c 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue @@ -1,12 +1,19 @@ <script> -import { GlSprintf, GlLink } from '@gitlab/ui'; +import { GlFormRadio, GlFormRadioGroup, GlLink, GlSprintf } from '@gitlab/ui'; import { s__, sprintf } from '~/locale'; import { getWeekdayNames } from '~/lib/utils/datetime_utility'; +const KEY_EVERY_DAY = 'everyDay'; +const KEY_EVERY_WEEK = 'everyWeek'; +const KEY_EVERY_MONTH = 'everyMonth'; +const KEY_CUSTOM = 'custom'; + export default { components: { - GlSprintf, + GlFormRadio, + GlFormRadioGroup, GlLink, + GlSprintf, }, props: { initialCronInterval: { @@ -22,6 +29,7 @@ export default { randomWeekDayIndex: this.generateRandomWeekDayIndex(), randomDay: this.generateRandomDay(), inputNameAttribute: 'schedule[cron]', + radioValue: this.initialCronInterval ? KEY_CUSTOM : KEY_EVERY_DAY, cronInterval: this.initialCronInterval, cronSyntaxUrl: 'https://en.wikipedia.org/wiki/Cron', }; @@ -29,14 +37,11 @@ export default { computed: { cronIntervalPresets() { return { - everyDay: `0 ${this.randomHour} * * *`, - everyWeek: `0 ${this.randomHour} * * ${this.randomWeekDayIndex}`, - everyMonth: `0 ${this.randomHour} ${this.randomDay} * *`, + [KEY_EVERY_DAY]: `0 ${this.randomHour} * * *`, + [KEY_EVERY_WEEK]: `0 ${this.randomHour} * * ${this.randomWeekDayIndex}`, + [KEY_EVERY_MONTH]: `0 ${this.randomHour} ${this.randomDay} * *`, }; }, - intervalIsPreset() { - return Object.values(this.cronIntervalPresets).includes(this.cronInterval); - }, formattedTime() { if (this.randomHour > 12) { return `${this.randomHour - 12}:00pm`; @@ -45,24 +50,36 @@ export default { } return `${this.randomHour}:00am`; }, + radioOptions() { + return [ + { + value: KEY_EVERY_DAY, + text: sprintf(s__(`Every day (at %{time})`), { time: this.formattedTime }), + }, + { + value: KEY_EVERY_WEEK, + text: sprintf(s__('Every week (%{weekday} at %{time})'), { + weekday: this.weekday, + time: this.formattedTime, + }), + }, + { + value: KEY_EVERY_MONTH, + text: sprintf(s__('Every month (Day %{day} at %{time})'), { + day: this.randomDay, + time: this.formattedTime, + }), + }, + { + value: KEY_CUSTOM, + text: s__('PipelineScheduleIntervalPattern|Custom (%{linkStart}Cron syntax%{linkEnd})'), + link: this.cronSyntaxUrl, + }, + ]; + }, weekday() { return getWeekdayNames()[this.randomWeekDayIndex]; }, - everyDayText() { - return sprintf(s__(`Every day (at %{time})`), { time: this.formattedTime }); - }, - everyWeekText() { - return sprintf(s__('Every week (%{weekday} at %{time})'), { - weekday: this.weekday, - time: this.formattedTime, - }); - }, - everyMonthText() { - return sprintf(s__('Every month (Day %{day} at %{time})'), { - day: this.randomDay, - time: this.formattedTime, - }); - }, }, watch: { cronInterval() { @@ -72,38 +89,18 @@ export default { gl.pipelineScheduleFieldErrors.updateFormValidityState(); }); }, - }, - // If at the mounting stage the default is still an empty string, we - // know we are not editing an existing field so we update it so - // that the default is the first radio option - mounted() { - if (this.cronInterval === '') { - this.cronInterval = this.cronIntervalPresets.everyDay; - } + radioValue: { + immediate: true, + handler(val) { + if (val !== KEY_CUSTOM) { + this.cronInterval = this.cronIntervalPresets[val]; + } + }, + }, }, methods: { - setCustomInput(e) { - if (!this.isEditingCustom) { - this.isEditingCustom = true; - this.$refs.customInput.click(); - // Because we need to manually trigger the click on the radio btn, - // it will add a space to update the v-model. If the user is typing - // and the space is added, it will feel very unituitive so we reset - // the value to the original - this.cronInterval = e.target.value; - } - if (this.intervalIsPreset) { - this.isEditingCustom = false; - } - }, - toggleCustomInput(shouldEnable) { - this.isEditingCustom = shouldEnable; - - if (shouldEnable) { - // We need to change the value so other radios don't remain selected - // because the model (cronInterval) hasn't changed. The server trims it. - this.cronInterval = `${this.cronInterval} `; - } + onCustomInput() { + this.radioValue = KEY_CUSTOM; }, generateRandomHour() { return Math.floor(Math.random() * 23); @@ -119,89 +116,33 @@ export default { </script> <template> - <div class="interval-pattern-form-group"> - <div class="cron-preset-radio-input"> - <input - id="every-day" - v-model="cronInterval" - :name="inputNameAttribute" - :value="cronIntervalPresets.everyDay" - class="label-bold" - type="radio" - @click="toggleCustomInput(false)" - /> - - <label class="label-bold" for="every-day"> - {{ everyDayText }} - </label> - </div> - - <div class="cron-preset-radio-input"> - <input - id="every-week" - v-model="cronInterval" - :name="inputNameAttribute" - :value="cronIntervalPresets.everyWeek" - class="label-bold" - type="radio" - @click="toggleCustomInput(false)" - /> - - <label class="label-bold" for="every-week"> - {{ everyWeekText }} - </label> - </div> - - <div class="cron-preset-radio-input"> - <input - id="every-month" - v-model="cronInterval" - :name="inputNameAttribute" - :value="cronIntervalPresets.everyMonth" - class="label-bold" - type="radio" - @click="toggleCustomInput(false)" - /> - - <label class="label-bold" for="every-month"> - {{ everyMonthText }} - </label> - </div> - - <div class="cron-preset-radio-input"> - <input - id="custom" - ref="customInput" - v-model="cronInterval" - :name="inputNameAttribute" - :value="cronInterval" - class="label-bold" - type="radio" - @click="toggleCustomInput(true)" - /> - - <label for="custom"> {{ s__('PipelineSheduleIntervalPattern|Custom') }} </label> - - <gl-sprintf :message="__('(%{linkStart}Cron syntax%{linkEnd})')"> - <template #link="{content}"> - <gl-link :href="cronSyntaxUrl" target="_blank" class="gl-font-sm"> - {{ content }} - </gl-link> - </template> - </gl-sprintf> - </div> - - <div class="cron-interval-input-wrapper"> - <input - id="schedule_cron" - v-model="cronInterval" - :placeholder="__('Define a custom pattern with cron syntax')" - :name="inputNameAttribute" - class="form-control inline cron-interval-input" - type="text" - required="true" - @input="setCustomInput" - /> - </div> + <div> + <gl-form-radio-group v-model="radioValue" :name="inputNameAttribute"> + <gl-form-radio + v-for="option in radioOptions" + :key="option.value" + :value="option.value" + :data-testid="option.value" + > + <gl-sprintf v-if="option.link" :message="option.text"> + <template #link="{content}"> + <gl-link :href="option.link" target="_blank" class="gl-font-sm"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + <template v-else>{{ option.text }}</template> + </gl-form-radio> + </gl-form-radio-group> + <input + id="schedule_cron" + v-model="cronInterval" + :placeholder="__('Define a custom pattern with cron syntax')" + :name="inputNameAttribute" + class="form-control inline cron-interval-input" + type="text" + required="true" + @input="onCustomInput" + /> </div> </template> diff --git a/app/assets/javascripts/pages/projects/pipelines/index/index.js b/app/assets/javascripts/pages/projects/pipelines/index/index.js index 2c37d7da4a7..bed9a751d4c 100644 --- a/app/assets/javascripts/pages/projects/pipelines/index/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/index/index.js @@ -8,7 +8,7 @@ import { } from '~/lib/utils/common_utils'; import { __ } from '~/locale'; import PipelinesStore from '../../../../pipelines/stores/pipelines_store'; -import pipelinesComponent from '../../../../pipelines/components/pipelines.vue'; +import pipelinesComponent from '../../../../pipelines/components/pipelines_list/pipelines.vue'; import Translate from '../../../../vue_shared/translate'; Vue.use(Translate); @@ -40,6 +40,7 @@ document.addEventListener( props: { store: this.store, endpoint: this.dataset.endpoint, + pipelineScheduleUrl: this.dataset.pipelineScheduleUrl, helpPagePath: this.dataset.helpPagePath, emptyStateSvgPath: this.dataset.emptyStateSvgPath, errorStateSvgPath: this.dataset.errorStateSvgPath, diff --git a/app/assets/javascripts/pages/projects/project_members/index.js b/app/assets/javascripts/pages/projects/project_members/index.js index f39765818e7..e146592e134 100644 --- a/app/assets/javascripts/pages/projects/project_members/index.js +++ b/app/assets/javascripts/pages/projects/project_members/index.js @@ -1,12 +1,30 @@ +import Vue from 'vue'; import Members from 'ee_else_ce/members'; -import memberExpirationDate from '../../../member_expiration_date'; -import UsersSelect from '../../../users_select'; -import groupsSelect from '../../../groups_select'; +import memberExpirationDate from '~/member_expiration_date'; +import UsersSelect from '~/users_select'; +import groupsSelect from '~/groups_select'; +import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue'; + +function mountRemoveMemberModal() { + const el = document.querySelector('.js-remove-member-modal'); + if (!el) { + return false; + } + + return new Vue({ + el, + render(createComponent) { + return createComponent(RemoveMemberModal); + }, + }); +} document.addEventListener('DOMContentLoaded', () => { - memberExpirationDate('.js-access-expiration-date-groups'); groupsSelect(); memberExpirationDate(); + memberExpirationDate('.js-access-expiration-date-groups'); + mountRemoveMemberModal(); + new Members(); // eslint-disable-line no-new new UsersSelect(); // eslint-disable-line no-new }); diff --git a/app/assets/javascripts/pages/projects/releases/new/index.js b/app/assets/javascripts/pages/projects/releases/new/index.js new file mode 100644 index 00000000000..0e314aacf8a --- /dev/null +++ b/app/assets/javascripts/pages/projects/releases/new/index.js @@ -0,0 +1,7 @@ +import ZenMode from '~/zen_mode'; +import initNewRelease from '~/releases/mount_new'; + +document.addEventListener('DOMContentLoaded', () => { + new ZenMode(); // eslint-disable-line no-new + initNewRelease(); +}); diff --git a/app/assets/javascripts/pages/projects/settings/operations/show/index.js b/app/assets/javascripts/pages/projects/settings/operations/show/index.js index 721d4a31fe4..1b9ec44ed4a 100644 --- a/app/assets/javascripts/pages/projects/settings/operations/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/operations/show/index.js @@ -1,13 +1,17 @@ import mountErrorTrackingForm from '~/error_tracking_settings'; +import mountAlertsSettings from '~/alerts_settings'; import mountOperationSettings from '~/operation_settings'; import mountGrafanaIntegration from '~/grafana_integration'; import initSettingsPanels from '~/settings_panels'; +import initIncidentsSettings from '~/incidents_settings'; document.addEventListener('DOMContentLoaded', () => { + initIncidentsSettings(); mountErrorTrackingForm(); mountOperationSettings(); mountGrafanaIntegration(); if (!IS_EE) { initSettingsPanels(); } + mountAlertsSettings(document.querySelector('.js-alerts-settings')); }); diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue index 7181332a1d6..a95f0af46cd 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue @@ -426,7 +426,7 @@ export default { v-if="lfsAvailable" ref="git-lfs-settings" :help-path="lfsHelpPath" - :label="s__('ProjectSettings|Git Large File Storage')" + :label="s__('ProjectSettings|Git Large File Storage (LFS)')" :help-text=" s__('ProjectSettings|Manages large files such as audio, video, and graphics files') " diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 3c44053e2b2..c65cc3e4c57 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -1,25 +1,18 @@ -import $ from 'jquery'; -import 'jquery.waitforimages'; - import initBlob from '~/blob_edit/blob_bundle'; import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import NotificationsForm from '~/notifications_form'; import UserCallout from '~/user_callout'; -import TreeView from '~/tree'; import BlobViewer from '~/blob/viewer/index'; import Activities from '~/activities'; -import { ajaxGet } from '~/lib/utils/common_utils'; -import GpgBadges from '~/gpg_badges'; import initReadMore from '~/read_more'; import leaveByUrl from '~/namespaces/leave_by_url'; import Star from '../../../star'; import notificationsDropdown from '../../../notifications_dropdown'; -import initNamespaceStorageLimitAlert from '~/namespace_storage_limit_alert'; import { showLearnGitLabProjectPopover } from '~/onboarding_issues'; +import initTree from 'ee_else_ce/repository'; document.addEventListener('DOMContentLoaded', () => { initReadMore(); - initNamespaceStorageLimitAlert(); new Star(); // eslint-disable-line no-new notificationsDropdown(); new ShortcutsNavigation(); // eslint-disable-line no-new @@ -31,10 +24,10 @@ document.addEventListener('DOMContentLoaded', () => { }); // Project show page loads different overview content based on user preferences - const treeSlider = document.querySelector('#tree-slider'); + const treeSlider = document.getElementById('js-tree-list'); if (treeSlider) { - new TreeView(); // eslint-disable-line no-new initBlob(); + initTree(); } if (document.querySelector('.blob-viewer')) { @@ -45,21 +38,7 @@ document.addEventListener('DOMContentLoaded', () => { new Activities(); // eslint-disable-line no-new } - $(treeSlider).waitForImages(() => { - ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); - }); - - GpgBadges.fetch(); leaveByUrl('project'); - if (document.getElementById('js-tree-list')) { - initBlob(); - import('ee_else_ce/repository') - .then(m => m.default()) - .catch(e => { - throw e; - }); - } - showLearnGitLabProjectPopover(); }); diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index 0d1d32317fe..78a4ea23f1a 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -1,53 +1,12 @@ import $ from 'jquery'; -import 'jquery.waitforimages'; - -import Vue from 'vue'; import initBlob from '~/blob_edit/blob_bundle'; -import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; -import GpgBadges from '~/gpg_badges'; -import TreeView from '../../../../tree'; import ShortcutsNavigation from '../../../../behaviors/shortcuts/shortcuts_navigation'; -import BlobViewer from '../../../../blob/viewer'; import NewCommitForm from '../../../../new_commit_form'; -import { ajaxGet } from '../../../../lib/utils/common_utils'; +import initTree from 'ee_else_ce/repository'; document.addEventListener('DOMContentLoaded', () => { new ShortcutsNavigation(); // eslint-disable-line no-new - new TreeView(); // eslint-disable-line no-new - new BlobViewer(); // eslint-disable-line no-new new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new - $('#tree-slider').waitForImages(() => - ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath), - ); - initBlob(); - const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status'); - const statusLink = document.querySelector('.commit-actions .ci-status-link'); - if (statusLink != null) { - statusLink.remove(); - // eslint-disable-next-line no-new - new Vue({ - el: commitPipelineStatusEl, - components: { - commitPipelineStatus, - }, - render(createElement) { - return createElement('commit-pipeline-status', { - props: { - endpoint: commitPipelineStatusEl.dataset.endpoint, - }, - }); - }, - }); - } - - GpgBadges.fetch(); - - if (document.getElementById('js-tree-list')) { - import('ee_else_ce/repository') - .then(m => m.default()) - .catch(e => { - throw e; - }); - } + initTree(); }); |