Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-09-30 18:12:24 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-09-30 18:12:24 +0300
commiteaec42f9e37fe51f9c53fa7079639ec9f4c40efc (patch)
tree2abbab9659bc8e043b2dbb9dcf797a5aab717767 /app/assets
parent4e8c8922da341914b9fd5570ec9ce7a29ffdfebd (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/analytics/shared/components/projects_dropdown_filter.vue93
-rw-r--r--app/assets/javascripts/editor/schema/ci.json4
-rw-r--r--app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue12
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/components/group_settings_app.vue139
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue139
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/constants.js7
-rw-r--r--app/assets/javascripts/projects/project_new.js58
7 files changed, 294 insertions, 158 deletions
diff --git a/app/assets/javascripts/analytics/shared/components/projects_dropdown_filter.vue b/app/assets/javascripts/analytics/shared/components/projects_dropdown_filter.vue
index a490111e13b..8467ea12be1 100644
--- a/app/assets/javascripts/analytics/shared/components/projects_dropdown_filter.vue
+++ b/app/assets/javascripts/analytics/shared/components/projects_dropdown_filter.vue
@@ -15,6 +15,8 @@ import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { n__, s__, __ } from '~/locale';
import getProjects from '../graphql/projects.query.graphql';
+const sortByProjectName = (projects = []) => projects.sort((a, b) => a.name.localeCompare(b.name));
+
export default {
name: 'ProjectsDropdownFilter',
components: {
@@ -88,6 +90,9 @@ export default {
selectedProjectIds() {
return this.selectedProjects.map((p) => p.id);
},
+ hasSelectedProjects() {
+ return Boolean(this.selectedProjects.length);
+ },
availableProjects() {
return filterBySearchTerm(this.projects, this.searchTerm);
},
@@ -95,6 +100,14 @@ export default {
const { loading, availableProjects } = this;
return !loading && !availableProjects.length;
},
+ selectedItems() {
+ return sortByProjectName(
+ this.availableProjects.filter(({ id }) => this.selectedProjectIds.includes(id)),
+ );
+ },
+ unselectedItems() {
+ return this.availableProjects.filter(({ id }) => !this.selectedProjectIds.includes(id));
+ },
},
watch: {
searchTerm() {
@@ -105,44 +118,53 @@ export default {
this.search();
},
methods: {
+ handleUpdatedSelectedProjects() {
+ this.$emit('selected', this.selectedProjects);
+ },
search: debounce(function debouncedSearch() {
this.fetchData();
}, DEFAULT_DEBOUNCE_AND_THROTTLE_MS),
- getSelectedProjects(selectedProject, isMarking) {
- return isMarking
+ getSelectedProjects(selectedProject, isSelected) {
+ return isSelected
? this.selectedProjects.concat([selectedProject])
: this.selectedProjects.filter((project) => project.id !== selectedProject.id);
},
singleSelectedProject(selectedObj, isMarking) {
return isMarking ? [selectedObj] : [];
},
- setSelectedProjects(selectedObj, isMarking) {
+ setSelectedProjects(project) {
this.selectedProjects = this.multiSelect
- ? this.getSelectedProjects(selectedObj, isMarking)
- : this.singleSelectedProject(selectedObj, isMarking);
+ ? this.getSelectedProjects(project, !this.isProjectSelected(project))
+ : this.singleSelectedProject(project, !this.isProjectSelected(project));
},
- onClick({ project, isSelected }) {
- this.setSelectedProjects(project, !isSelected);
- this.$emit('selected', this.selectedProjects);
+ onClick(project) {
+ this.setSelectedProjects(project);
+ this.handleUpdatedSelectedProjects();
},
- onMultiSelectClick({ project, isSelected }) {
- this.setSelectedProjects(project, !isSelected);
+ onMultiSelectClick(project) {
+ this.setSelectedProjects(project);
this.isDirty = true;
},
- onSelected(ev) {
+ onSelected(project) {
if (this.multiSelect) {
- this.onMultiSelectClick(ev);
+ this.onMultiSelectClick(project);
} else {
- this.onClick(ev);
+ this.onClick(project);
}
},
onHide() {
if (this.multiSelect && this.isDirty) {
- this.$emit('selected', this.selectedProjects);
+ this.handleUpdatedSelectedProjects();
}
this.searchTerm = '';
this.isDirty = false;
},
+ onClearAll() {
+ if (this.hasSelectedProjects) {
+ this.isDirty = true;
+ }
+ this.selectedProjects = [];
+ },
fetchData() {
this.loading = true;
@@ -168,8 +190,8 @@ export default {
this.projects = nodes;
});
},
- isProjectSelected(id) {
- return this.selectedProjects ? this.selectedProjectIds.includes(id) : false;
+ isProjectSelected(project) {
+ return this.selectedProjectIds.includes(project.id);
},
getEntityId(project) {
return getIdFromGraphQLId(project.id);
@@ -182,6 +204,10 @@ export default {
ref="projectsDropdown"
class="dropdown dropdown-projects"
toggle-class="gl-shadow-none"
+ :show-clear-all="hasSelectedProjects"
+ show-highlighted-items-title
+ highlighted-items-title-class="gl-p-3"
+ @clear-all.stop="onClearAll"
@hide="onHide"
>
<template #button-content>
@@ -204,14 +230,37 @@ export default {
<gl-dropdown-section-header>{{ __('Projects') }}</gl-dropdown-section-header>
<gl-search-box-by-type v-model.trim="searchTerm" />
</template>
+ <template #highlighted-items>
+ <gl-dropdown-item
+ v-for="project in selectedItems"
+ :key="project.id"
+ is-check-item
+ :is-checked="isProjectSelected(project)"
+ @click.native.capture.stop="onSelected(project)"
+ >
+ <div class="gl-display-flex">
+ <gl-avatar
+ class="gl-mr-2 gl-vertical-align-middle"
+ :alt="project.name"
+ :size="16"
+ :entity-id="getEntityId(project)"
+ :entity-name="project.name"
+ :src="project.avatarUrl"
+ shape="rect"
+ />
+ <div>
+ <div data-testid="project-name">{{ project.name }}</div>
+ <div class="gl-text-gray-500" data-testid="project-full-path">
+ {{ project.fullPath }}
+ </div>
+ </div>
+ </div>
+ </gl-dropdown-item>
+ </template>
<gl-dropdown-item
- v-for="project in availableProjects"
+ v-for="project in unselectedItems"
:key="project.id"
- :is-check-item="true"
- :is-checked="isProjectSelected(project.id)"
- @click.native.capture.stop="
- onSelected({ project, isSelected: isProjectSelected(project.id) })
- "
+ @click.native.capture.stop="onSelected(project)"
>
<div class="gl-display-flex">
<gl-avatar
diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json
index b6473322b1f..0052bc00406 100644
--- a/app/assets/javascripts/editor/schema/ci.json
+++ b/app/assets/javascripts/editor/schema/ci.json
@@ -852,10 +852,6 @@
"description": "Retry if there is a runner system failure (for example, job setup failed)."
},
{
- "const": "missing_dependency_failure",
- "description": "Retry if a dependency is missing."
- },
- {
"const": "runner_unsupported",
"description": "Retry if the runner is unsupported."
},
diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue b/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
index e3c5aa53129..fbf0ba744bd 100644
--- a/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
+++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
@@ -1,9 +1,12 @@
<script>
import { GlAlert, GlFormGroup, GlFormInputGroup, GlSkeletonLoader, GlSprintf } from '@gitlab/ui';
-import { helpPagePath } from '~/helpers/help_page_helper';
import { __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
+import {
+ DEPENDENCY_PROXY_SETTINGS_DESCRIPTION,
+ DEPENDENCY_PROXY_DOCS_PATH,
+} from '~/packages_and_registries/settings/group/constants';
import getDependencyProxyDetailsQuery from '~/packages_and_registries/dependency_proxy/graphql/queries/get_dependency_proxy_details.query.graphql';
@@ -19,9 +22,6 @@ export default {
},
inject: ['groupPath', 'dependencyProxyAvailable'],
i18n: {
- subTitle: __(
- 'Create a local proxy for storing frequently used upstream images. %{docLinkStart}Learn more%{docLinkEnd} about dependency proxies.',
- ),
proxyNotAvailableText: __('Dependency proxy feature is limited to public groups for now.'),
proxyImagePrefix: __('Dependency proxy image prefix'),
copyImagePrefixText: __('Copy prefix'),
@@ -47,8 +47,8 @@ export default {
infoMessages() {
return [
{
- text: this.$options.i18n.subTitle,
- link: helpPagePath('user/packages/dependency_proxy/index'),
+ text: DEPENDENCY_PROXY_SETTINGS_DESCRIPTION,
+ link: DEPENDENCY_PROXY_DOCS_PATH,
},
];
},
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/components/group_settings_app.vue b/app/assets/javascripts/packages_and_registries/settings/group/components/group_settings_app.vue
index ec3be43196c..954a2aedf6b 100644
--- a/app/assets/javascripts/packages_and_registries/settings/group/components/group_settings_app.vue
+++ b/app/assets/javascripts/packages_and_registries/settings/group/components/group_settings_app.vue
@@ -1,108 +1,54 @@
<script>
-import { GlSprintf, GlLink, GlAlert } from '@gitlab/ui';
-import DuplicatesSettings from '~/packages_and_registries/settings/group/components/duplicates_settings.vue';
-import GenericSettings from '~/packages_and_registries/settings/group/components/generic_settings.vue';
-import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
+import { GlAlert } from '@gitlab/ui';
import {
- PACKAGE_SETTINGS_HEADER,
- PACKAGE_SETTINGS_DESCRIPTION,
- PACKAGES_DOCS_PATH,
ERROR_UPDATING_SETTINGS,
SUCCESS_UPDATING_SETTINGS,
} from '~/packages_and_registries/settings/group/constants';
-import updateNamespacePackageSettings from '~/packages_and_registries/settings/group/graphql/mutations/update_group_packages_settings.mutation.graphql';
+import PackagesSettings from '~/packages_and_registries/settings/group/components/packages_settings.vue';
+
import getGroupPackagesSettingsQuery from '~/packages_and_registries/settings/group/graphql/queries/get_group_packages_settings.query.graphql';
-import { updateGroupPackageSettings } from '~/packages_and_registries/settings/group/graphql/utils/cache_update';
-import { updateGroupPackagesSettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
-import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
export default {
name: 'GroupSettingsApp',
- i18n: {
- PACKAGE_SETTINGS_HEADER,
- PACKAGE_SETTINGS_DESCRIPTION,
- },
- links: {
- PACKAGES_DOCS_PATH,
- },
components: {
GlAlert,
- GlSprintf,
- GlLink,
- SettingsBlock,
- MavenSettings,
- GenericSettings,
- DuplicatesSettings,
+ PackagesSettings,
},
- inject: ['defaultExpanded', 'groupPath'],
+ inject: ['groupPath'],
apollo: {
- packageSettings: {
+ group: {
query: getGroupPackagesSettingsQuery,
variables() {
return {
fullPath: this.groupPath,
};
},
- update(data) {
- return data.group?.packageSettings;
- },
},
},
data() {
return {
- packageSettings: {},
- errors: {},
+ group: {},
alertMessage: null,
};
},
computed: {
+ packageSettings() {
+ return this.group?.packageSettings || {};
+ },
isLoading() {
- return this.$apollo.queries.packageSettings.loading;
+ return this.$apollo.queries.group.loading;
},
},
methods: {
dismissAlert() {
this.alertMessage = null;
},
- updateSettings(payload) {
- this.errors = {};
- return this.$apollo
- .mutate({
- mutation: updateNamespacePackageSettings,
- variables: {
- input: {
- namespacePath: this.groupPath,
- ...payload,
- },
- },
- update: updateGroupPackageSettings(this.groupPath),
- optimisticResponse: updateGroupPackagesSettingsOptimisticResponse({
- ...this.packageSettings,
- ...payload,
- }),
- })
- .then(({ data }) => {
- if (data.updateNamespacePackageSettings?.errors?.length > 0) {
- this.alertMessage = ERROR_UPDATING_SETTINGS;
- } else {
- this.dismissAlert();
- this.$toast.show(SUCCESS_UPDATING_SETTINGS);
- }
- })
- .catch((e) => {
- if (e.graphQLErrors) {
- e.graphQLErrors.forEach((error) => {
- const [
- {
- path: [key],
- message,
- },
- ] = error.extensions.problems;
- this.errors = { ...this.errors, [key]: message };
- });
- }
- this.alertMessage = ERROR_UPDATING_SETTINGS;
- });
+ handleSuccess() {
+ this.$toast.show(SUCCESS_UPDATING_SETTINGS);
+ this.dismissAlert();
+ },
+ handleError() {
+ this.alertMessage = ERROR_UPDATING_SETTINGS;
},
},
};
@@ -114,50 +60,11 @@ export default {
{{ alertMessage }}
</gl-alert>
- <settings-block
- :default-expanded="defaultExpanded"
- data-qa-selector="package_registry_settings_content"
- >
- <template #title> {{ $options.i18n.PACKAGE_SETTINGS_HEADER }}</template>
- <template #description>
- <span data-testid="description">
- <gl-sprintf :message="$options.i18n.PACKAGE_SETTINGS_DESCRIPTION">
- <template #link="{ content }">
- <gl-link :href="$options.links.PACKAGES_DOCS_PATH" target="_blank">{{
- content
- }}</gl-link>
- </template>
- </gl-sprintf>
- </span>
- </template>
- <template #default>
- <maven-settings data-testid="maven-settings">
- <template #default="{ modelNames }">
- <duplicates-settings
- :duplicates-allowed="packageSettings.mavenDuplicatesAllowed"
- :duplicate-exception-regex="packageSettings.mavenDuplicateExceptionRegex"
- :duplicate-exception-regex-error="errors.mavenDuplicateExceptionRegex"
- :model-names="modelNames"
- :loading="isLoading"
- toggle-qa-selector="allow_duplicates_toggle"
- label-qa-selector="allow_duplicates_label"
- @update="updateSettings"
- />
- </template>
- </maven-settings>
- <generic-settings class="gl-mt-6" data-testid="generic-settings">
- <template #default="{ modelNames }">
- <duplicates-settings
- :duplicates-allowed="packageSettings.genericDuplicatesAllowed"
- :duplicate-exception-regex="packageSettings.genericDuplicateExceptionRegex"
- :duplicate-exception-regex-error="errors.genericDuplicateExceptionRegex"
- :model-names="modelNames"
- :loading="isLoading"
- @update="updateSettings"
- />
- </template>
- </generic-settings>
- </template>
- </settings-block>
+ <packages-settings
+ :package-settings="packageSettings"
+ :is-loading="isLoading"
+ @success="handleSuccess"
+ @error="handleError"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue b/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue
new file mode 100644
index 00000000000..b7e88945dbd
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue
@@ -0,0 +1,139 @@
+<script>
+import { GlSprintf, GlLink } from '@gitlab/ui';
+import DuplicatesSettings from '~/packages_and_registries/settings/group/components/duplicates_settings.vue';
+import GenericSettings from '~/packages_and_registries/settings/group/components/generic_settings.vue';
+import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
+import {
+ PACKAGE_SETTINGS_HEADER,
+ PACKAGE_SETTINGS_DESCRIPTION,
+ PACKAGES_DOCS_PATH,
+} from '~/packages_and_registries/settings/group/constants';
+import updateNamespacePackageSettings from '~/packages_and_registries/settings/group/graphql/mutations/update_group_packages_settings.mutation.graphql';
+import { updateGroupPackageSettings } from '~/packages_and_registries/settings/group/graphql/utils/cache_update';
+import { updateGroupPackagesSettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
+import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
+
+export default {
+ name: 'PackageSettings',
+ i18n: {
+ PACKAGE_SETTINGS_HEADER,
+ PACKAGE_SETTINGS_DESCRIPTION,
+ },
+ links: {
+ PACKAGES_DOCS_PATH,
+ },
+ components: {
+ GlSprintf,
+ GlLink,
+ SettingsBlock,
+ MavenSettings,
+ GenericSettings,
+ DuplicatesSettings,
+ },
+ inject: ['defaultExpanded', 'groupPath'],
+ props: {
+ packageSettings: {
+ type: Object,
+ required: true,
+ },
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ errors: {},
+ };
+ },
+ methods: {
+ async updateSettings(payload) {
+ this.errors = {};
+ try {
+ const { data } = await this.$apollo.mutate({
+ mutation: updateNamespacePackageSettings,
+ variables: {
+ input: {
+ namespacePath: this.groupPath,
+ ...payload,
+ },
+ },
+ update: updateGroupPackageSettings(this.groupPath),
+ optimisticResponse: updateGroupPackagesSettingsOptimisticResponse({
+ ...this.packageSettings,
+ ...payload,
+ }),
+ });
+
+ if (data.updateNamespacePackageSettings?.errors?.length > 0) {
+ throw new Error();
+ } else {
+ this.$emit('success');
+ }
+ } catch (e) {
+ if (e.graphQLErrors) {
+ e.graphQLErrors.forEach((error) => {
+ const [
+ {
+ path: [key],
+ message,
+ },
+ ] = error.extensions.problems;
+ this.errors = { ...this.errors, [key]: message };
+ });
+ }
+ this.$emit('error');
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <settings-block
+ :default-expanded="defaultExpanded"
+ data-qa-selector="package_registry_settings_content"
+ >
+ <template #title> {{ $options.i18n.PACKAGE_SETTINGS_HEADER }}</template>
+ <template #description>
+ <span data-testid="description">
+ <gl-sprintf :message="$options.i18n.PACKAGE_SETTINGS_DESCRIPTION">
+ <template #link="{ content }">
+ <gl-link :href="$options.links.PACKAGES_DOCS_PATH" target="_blank">{{
+ content
+ }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </span>
+ </template>
+ <template #default>
+ <maven-settings data-testid="maven-settings">
+ <template #default="{ modelNames }">
+ <duplicates-settings
+ :duplicates-allowed="packageSettings.mavenDuplicatesAllowed"
+ :duplicate-exception-regex="packageSettings.mavenDuplicateExceptionRegex"
+ :duplicate-exception-regex-error="errors.mavenDuplicateExceptionRegex"
+ :model-names="modelNames"
+ :loading="isLoading"
+ toggle-qa-selector="allow_duplicates_toggle"
+ label-qa-selector="allow_duplicates_label"
+ @update="updateSettings"
+ />
+ </template>
+ </maven-settings>
+ <generic-settings class="gl-mt-6" data-testid="generic-settings">
+ <template #default="{ modelNames }">
+ <duplicates-settings
+ :duplicates-allowed="packageSettings.genericDuplicatesAllowed"
+ :duplicate-exception-regex="packageSettings.genericDuplicateExceptionRegex"
+ :duplicate-exception-regex-error="errors.genericDuplicateExceptionRegex"
+ :model-names="modelNames"
+ :loading="isLoading"
+ @update="updateSettings"
+ />
+ </template>
+ </generic-settings>
+ </template>
+ </settings-block>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/constants.js b/app/assets/javascripts/packages_and_registries/settings/group/constants.js
index d29489a0b33..2824d5e2776 100644
--- a/app/assets/javascripts/packages_and_registries/settings/group/constants.js
+++ b/app/assets/javascripts/packages_and_registries/settings/group/constants.js
@@ -23,8 +23,15 @@ export const ERROR_UPDATING_SETTINGS = s__(
'PackageRegistry|An error occurred while saving the settings',
);
+export const DEPENDENCY_PROXY_HEADER = s__('DependencyProxy|Dependency Proxy');
+export const DEPENDENCY_PROXY_SETTINGS_DESCRIPTION = s__(
+ 'DependencyProxy|Create a local proxy for storing frequently used upstream images. %{docLinkStart}Learn more%{docLinkEnd} about dependency proxies.',
+);
+
// Parameters
export const PACKAGES_DOCS_PATH = helpPagePath('user/packages');
export const MAVEN_DUPLICATES_ALLOWED = 'mavenDuplicatesAllowed';
export const MAVEN_DUPLICATE_EXCEPTION_REGEX = 'mavenDuplicateExceptionRegex';
+
+export const DEPENDENCY_PROXY_DOCS_PATH = helpPagePath('user/packages/dependency_proxy/index');
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index ebd20583a1c..b350db0c838 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -1,5 +1,7 @@
import $ from 'jquery';
+import { debounce } from 'lodash';
import DEFAULT_PROJECT_TEMPLATES from 'ee_else_ce/projects/default_project_templates';
+import axios from '../lib/utils/axios_utils';
import {
convertToTitleCase,
humanize,
@@ -9,6 +11,23 @@ import {
let hasUserDefinedProjectPath = false;
let hasUserDefinedProjectName = false;
+const invalidInputClass = 'gl-field-error-outline';
+
+const validateImportCredentials = (url, user, password) => {
+ const endpoint = `${gon.relative_url_root}/import/url/validate`;
+ return axios
+ .post(endpoint, {
+ url,
+ user,
+ password,
+ })
+ .then(({ data }) => data)
+ .catch(() => ({
+ // intentionally reporting success in case of validation error
+ // we do not want to block users from trying import in case of validation exception
+ success: true,
+ }));
+};
const onProjectNameChange = ($projectNameInput, $projectPathInput) => {
const slug = slugify(convertUnicodeToAscii($projectNameInput.val()));
@@ -85,7 +104,10 @@ const bindHowToImport = () => {
const bindEvents = () => {
const $newProjectForm = $('#new_project');
const $projectImportUrl = $('#project_import_url');
- const $projectImportUrlWarning = $('.js-import-url-warning');
+ const $projectImportUrlUser = $('#project_import_url_user');
+ const $projectImportUrlPassword = $('#project_import_url_password');
+ const $projectImportUrlError = $('.js-import-url-error');
+ const $projectImportForm = $('.project-import form');
const $projectPath = $('.tab-pane.active #project_path');
const $useTemplateBtn = $('.template-button > input');
const $projectFieldsForm = $('.project-fields-form');
@@ -139,12 +161,15 @@ const bindEvents = () => {
$projectPath.val($projectPath.val().trim());
});
- function updateUrlPathWarningVisibility() {
- const url = $projectImportUrl.val();
- const URL_PATTERN = /(?:git|https?):\/\/.*\/.*\.git$/;
- const isUrlValid = URL_PATTERN.test(url);
- $projectImportUrlWarning.toggleClass('hide', isUrlValid);
- }
+ const updateUrlPathWarningVisibility = debounce(async () => {
+ const { success: isUrlValid } = await validateImportCredentials(
+ $projectImportUrl.val(),
+ $projectImportUrlUser.val(),
+ $projectImportUrlPassword.val(),
+ );
+ $projectImportUrl.toggleClass(invalidInputClass, !isUrlValid);
+ $projectImportUrlError.toggleClass('hide', isUrlValid);
+ }, 500);
let isProjectImportUrlDirty = false;
$projectImportUrl.on('blur', () => {
@@ -153,9 +178,22 @@ const bindEvents = () => {
});
$projectImportUrl.on('keyup', () => {
deriveProjectPathFromUrl($projectImportUrl);
- // defer error message till first input blur
- if (isProjectImportUrlDirty) {
- updateUrlPathWarningVisibility();
+ });
+
+ [$projectImportUrl, $projectImportUrlUser, $projectImportUrlPassword].forEach(($f) => {
+ $f.on('input', () => {
+ if (isProjectImportUrlDirty) {
+ updateUrlPathWarningVisibility();
+ }
+ });
+ });
+
+ $projectImportForm.on('submit', (e) => {
+ const $invalidFields = $projectImportForm.find(`.${invalidInputClass}`);
+ if ($invalidFields.length > 0) {
+ $invalidFields[0].focus();
+ e.preventDefault();
+ e.stopPropagation();
}
});