diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue index 5a60fefdc..34f1e003f 100644 --- a/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue @@ -7,8 +7,8 @@ import { useStore, useStoreGetters } from 'dashboard/composables/store'; import { uploadFile } from 'dashboard/helper/uploadHelper'; import { checkFileSizeLimit } from 'shared/helpers/FileHelper'; import { useVuelidate } from '@vuelidate/core'; -import { required, minLength } from '@vuelidate/validators'; -import { shouldBeUrl } from 'shared/helpers/Validators'; +import { required, minLength, helpers } from '@vuelidate/validators'; +import { shouldBeUrl, isValidSlug } from 'shared/helpers/Validators'; import Button from 'dashboard/components-next/button/Button.vue'; import Input from 'dashboard/components-next/input/Input.vue'; @@ -61,7 +61,16 @@ const liveChatWidgets = computed(() => { const rules = { name: { required, minLength: minLength(2) }, - slug: { required }, + slug: { + required: helpers.withMessage( + () => t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.ERROR'), + required + ), + isValidSlug: helpers.withMessage( + () => t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.FORMAT_ERROR'), + isValidSlug + ), + }, homePageLink: { shouldBeUrl }, }; @@ -71,9 +80,9 @@ const nameError = computed(() => v$.value.name.$error ? t('HELP_CENTER.CREATE_PORTAL_DIALOG.NAME.ERROR') : '' ); -const slugError = computed(() => - v$.value.slug.$error ? t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.ERROR') : '' -); +const slugError = computed(() => { + return v$.value.slug.$errors[0]?.$message || ''; +}); const homePageLinkError = computed(() => v$.value.homePageLink.$error diff --git a/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/CreatePortalDialog.vue b/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/CreatePortalDialog.vue index d245656d0..70c30a241 100644 --- a/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/CreatePortalDialog.vue +++ b/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/CreatePortalDialog.vue @@ -6,8 +6,9 @@ import { useAlert, useTrack } from 'dashboard/composables'; import { PORTALS_EVENTS } from 'dashboard/helper/AnalyticsHelper/events'; import { convertToCategorySlug } from 'dashboard/helper/commons.js'; import { useVuelidate } from '@vuelidate/core'; -import { required, minLength } from '@vuelidate/validators'; +import { required, minLength, helpers } from '@vuelidate/validators'; import { buildPortalURL } from 'dashboard/helper/portalHelper'; +import { isValidSlug } from 'shared/helpers/Validators'; import Dialog from 'dashboard/components-next/dialog/Dialog.vue'; import Input from 'dashboard/components-next/input/Input.vue'; @@ -31,7 +32,16 @@ const state = reactive({ const rules = { name: { required, minLength: minLength(2) }, - slug: { required }, + slug: { + required: helpers.withMessage( + () => t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.ERROR'), + required + ), + isValidSlug: helpers.withMessage( + () => t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.FORMAT_ERROR'), + isValidSlug + ), + }, }; const v$ = useVuelidate(rules, state); @@ -40,9 +50,9 @@ const nameError = computed(() => v$.value.name.$error ? t('HELP_CENTER.CREATE_PORTAL_DIALOG.NAME.ERROR') : '' ); -const slugError = computed(() => - v$.value.slug.$error ? t('HELP_CENTER.CREATE_PORTAL_DIALOG.SLUG.ERROR') : '' -); +const slugError = computed(() => { + return v$.value.slug.$errors[0]?.$message || ''; +}); const isSubmitDisabled = computed(() => v$.value.$invalid); @@ -131,6 +141,7 @@ defineExpose({ dialogRef }); :message=" nameError || t('HELP_CENTER.CREATE_PORTAL_DIALOG.NAME.MESSAGE') " + @blur="v$.name.$touch()" /> diff --git a/app/javascript/dashboard/i18n/locale/en/helpCenter.json b/app/javascript/dashboard/i18n/locale/en/helpCenter.json index 8fa64108b..f437b83d9 100644 --- a/app/javascript/dashboard/i18n/locale/en/helpCenter.json +++ b/app/javascript/dashboard/i18n/locale/en/helpCenter.json @@ -696,7 +696,8 @@ "SLUG": { "LABEL": "Slug", "PLACEHOLDER": "user-guide", - "ERROR": "Slug is required" + "ERROR": "Slug is required", + "FORMAT_ERROR": "Please enter a valid slug, for eg: user-guide" } }, "PORTAL_SETTINGS": { diff --git a/app/javascript/shared/helpers/Validators.js b/app/javascript/shared/helpers/Validators.js index 6502fef39..1c10121f1 100644 --- a/app/javascript/shared/helpers/Validators.js +++ b/app/javascript/shared/helpers/Validators.js @@ -100,3 +100,10 @@ export const getRegexp = regexPatternValue => { regexPatternValue.slice(lastSlash + 1) ); }; + +/** + * Checks if a string is a valid slug (letters, numbers, hyphens only, no spaces or other symbols). + * @param {string} value - The slug to validate. + * @returns {boolean} True if the slug is valid, false otherwise. + */ +export const isValidSlug = value => /^[a-zA-Z0-9-]+$/.test(value); diff --git a/app/javascript/shared/helpers/specs/ValidatorsHelper.spec.js b/app/javascript/shared/helpers/specs/ValidatorsHelper.spec.js index fc15b772c..4546b43a8 100644 --- a/app/javascript/shared/helpers/specs/ValidatorsHelper.spec.js +++ b/app/javascript/shared/helpers/specs/ValidatorsHelper.spec.js @@ -9,6 +9,7 @@ import { isNumber, isDomain, getRegexp, + isValidSlug, } from '../Validators'; describe('#shouldBeUrl', () => { @@ -153,3 +154,24 @@ describe('#getRegexp', () => { expect(regex.test('12-34-5678')).toBe(false); }); }); + +describe('#isValidSlug', () => { + it('should return true for valid slugs', () => { + expect(isValidSlug('abc')).toEqual(true); + expect(isValidSlug('abc-123')).toEqual(true); + expect(isValidSlug('a-b-c')).toEqual(true); + expect(isValidSlug('123')).toEqual(true); + expect(isValidSlug('abc123-def')).toEqual(true); + }); + it('should return false for invalid slugs', () => { + expect(isValidSlug('abc_def')).toEqual(false); + expect(isValidSlug('abc def')).toEqual(false); + expect(isValidSlug('abc@def')).toEqual(false); + expect(isValidSlug('abc.def')).toEqual(false); + expect(isValidSlug('abc/def')).toEqual(false); + expect(isValidSlug('abc!def')).toEqual(false); + expect(isValidSlug('abc--def!')).toEqual(false); + expect(isValidSlug('abc-def ')).toEqual(false); + expect(isValidSlug(' abc-def')).toEqual(false); + }); +});