summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/pages/projects
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-20 12:26:25 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-20 12:26:25 +0000
commita09983ae35713f5a2bbb100981116d31ce99826e (patch)
tree2ee2af7bd104d57086db360a7e6d8c9d5d43667a /app/assets/javascripts/pages/projects
parent18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff)
downloadgitlab-ce-a09983ae35713f5a2bbb100981116d31ce99826e.tar.gz
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'app/assets/javascripts/pages/projects')
-rw-r--r--app/assets/javascripts/pages/projects/clusters/show/cluster_health.js18
-rw-r--r--app/assets/javascripts/pages/projects/clusters/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/commit/pipelines/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/edit/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue91
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue147
-rw-r--r--app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue20
-rw-r--r--app/assets/javascripts/pages/projects/integrations/jira/issues/index/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/issues/service_desk/filtered_search.js30
-rw-r--r--app/assets/javascripts/pages/projects/issues/service_desk/index.js11
-rw-r--r--app/assets/javascripts/pages/projects/issues/show.js20
-rw-r--r--app/assets/javascripts/pages/projects/metrics_dashboard/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue215
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/index/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/project_members/index.js26
-rw-r--r--app/assets/javascripts/pages/projects/releases/new/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/settings/operations/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue2
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js27
-rw-r--r--app/assets/javascripts/pages/projects/tree/show/index.js45
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();
});