diff --git a/packages/client/public/locales/en/Settings.json b/packages/client/public/locales/en/Settings.json index 54063be3f9..126af986fb 100644 --- a/packages/client/public/locales/en/Settings.json +++ b/packages/client/public/locales/en/Settings.json @@ -199,6 +199,7 @@ "SuccessfullySaveGreetingSettingsMessage": "Welcome Page settings have been successfully saved", "SuccessfullySavePortalNameMessage": "Space has been renamed successfully", "SuccessfullySaveSettingsMessage": "Settings have been successfully updated", + "DNSSettingsHint": "Enter domain name like this:", "TemporaryStorage": "Temporary storage", "TemporaryStorageDescription": "Backup is stored in the 'Backup' section, you will be able to download it within 24 hours after creating.", "ThirdPartyAuthorization": "Third-party Authorization", diff --git a/packages/client/public/locales/ru/Settings.json b/packages/client/public/locales/ru/Settings.json index 8950798aa9..5499b4d818 100644 --- a/packages/client/public/locales/ru/Settings.json +++ b/packages/client/public/locales/ru/Settings.json @@ -183,6 +183,7 @@ "SuccessfullySaveGreetingSettingsMessage": "Настройки страницы приветствия успешно сохранены", "SuccessfullySavePortalNameMessage": "Портал успешно переименован", "SuccessfullySaveSettingsMessage": "Настройки успешно обновлены", + "DNSSettingsHint": "Введите доменное имя следующим образом:", "TemporaryStorage": "Временное хранилище", "TemporaryStorageDescription": "Резервная копия будет храниться в разделе 'Резервное копирование', вы сможете скачать её в течение 24 часов с момента создания.", "ThirdPartyAuthorization": "Сторонние сервисы", diff --git a/packages/client/src/pages/PortalSettings/categories/common/Customization/dns-settings.js b/packages/client/src/pages/PortalSettings/categories/common/Customization/dns-settings.js index 7950ef3a98..b1c23fd8c8 100644 --- a/packages/client/src/pages/PortalSettings/categories/common/Customization/dns-settings.js +++ b/packages/client/src/pages/PortalSettings/categories/common/Customization/dns-settings.js @@ -20,7 +20,7 @@ import { ToggleButton } from "@docspace/shared/components/toggle-button"; import { Text } from "@docspace/shared/components/text"; import { Link } from "@docspace/shared/components/link"; import { DeviceType } from "@docspace/shared/enums"; -import { validatePortalName } from "@docspace/shared/utils/common"; +import { parseDomain } from "@docspace/shared/utils/common"; const toggleStyle = { position: "static", @@ -61,7 +61,7 @@ const DNSSettings = (props) => { isDefaultDNS, dnsSettingsUrl, currentDeviceType, - domainValidator, + portalName, } = props; const [hasScroll, setHasScroll] = useState(false); const isLoadedSetting = isLoaded && tReady; @@ -110,19 +110,8 @@ const DNSSettings = (props) => { setIsLoading(true); }, [200]); - const isValidPortalName = validatePortalName( - dnsName || "", - domainValidator, - setErrorText, - t, - ); - - if (isValidPortalName) { - await saveDNSSettings(); - toastr.success(t("Settings:SuccessfullySaveSettingsMessage")); - } else { - setIsError(true); - } + await saveDNSSettings(); + toastr.success(t("Settings:SuccessfullySaveSettingsMessage")); } catch (e) { setIsError(true); toastr.error(e); @@ -131,6 +120,8 @@ const DNSSettings = (props) => { clearTimeout(timerId); timerId = null; setIsLoading(false); + + setIsError(false); }; const onClickToggle = (e) => { const checked = e.currentTarget.checked; @@ -138,10 +129,18 @@ const DNSSettings = (props) => { }; const onChangeTextInput = (e) => { - const { value } = e.target; isError && setIsError(false); - errorText && setErrorText(""); - setDNSName(value); + setErrorText(""); + + const { value } = e.target; + const dns = portalName ? value.slice(portalName.length + 1) : value; + + const isValidDomain = parseDomain(dns || "", setErrorText, t); + + if (!isValidDomain) { + setIsError(true); + } + setDNSName(dns); }; const checkInnerWidth = useCallback(() => { if (!isMobile()) { @@ -162,6 +161,8 @@ const DNSSettings = (props) => { } }, [isMobile, setIsCustomizationView]); + const domainExampleText = " team.ourcompany.com"; + const settingsBlock = (
{standalone ? ( @@ -177,13 +178,33 @@ const DNSSettings = (props) => {
- - {errorText} + {errorText && + errorText.map((err, index) => ( + + {err} + + ))} +
+
+ + {`${t("Settings:DNSSettingsHint")}${domainExampleText}`}
@@ -215,7 +236,7 @@ const DNSSettings = (props) => { size={currentDeviceType === DeviceType.desktop ? "small" : "normal"} label={t("Common:SaveButton")} onClick={onSaveSettings} - isDisabled={isLoading || isDefaultDNS} + isDisabled={isLoading || isDefaultDNS || isError} isLoading={isLoading} /> ) : ( @@ -224,7 +245,7 @@ const DNSSettings = (props) => { size={currentDeviceType === DeviceType.desktop ? "small" : "normal"} label={t("Common:SendRequest")} onClick={onSendRequest} - isDisabled={!isSettingPaid} + isDisabled={!isSettingPaid || isError} /> ); @@ -278,7 +299,6 @@ export default inject(({ settingsStore, common, currentQuotaStore }) => { standalone, dnsSettingsUrl, currentDeviceType, - domainValidator, } = settingsStore; const { isLoaded, @@ -290,6 +310,7 @@ export default inject(({ settingsStore, common, currentQuotaStore }) => { setDNSName, saveDNSSettings, isDefaultDNS, + portalName, } = common; const { isBrandingAndCustomizationAvailable } = currentQuotaStore; @@ -313,6 +334,6 @@ export default inject(({ settingsStore, common, currentQuotaStore }) => { saveDNSSettings, dnsSettingsUrl, currentDeviceType, - domainValidator, + portalName, }; })(withLoading(withTranslation(["Settings", "Common"])(observer(DNSSettings)))); diff --git a/packages/client/src/pages/PortalSettings/categories/common/Customization/portal-renaming.js b/packages/client/src/pages/PortalSettings/categories/common/Customization/portal-renaming.js index 18a1c8e8e8..fa3fb18514 100644 --- a/packages/client/src/pages/PortalSettings/categories/common/Customization/portal-renaming.js +++ b/packages/client/src/pages/PortalSettings/categories/common/Customization/portal-renaming.js @@ -33,6 +33,8 @@ const PortalRenaming = (props) => { currentColorScheme, renamingSettingsUrl, domainValidator, + setPortalName, + portalName, } = props; const navigate = useNavigate(); @@ -54,10 +56,8 @@ const PortalRenaming = (props) => { ? tenantAlias : portalNameDefaultFromSessionStorage; - const [portalName, setPortalName] = useState(portalNameInitially); - const [portalNameDefault, setPortalNameDefault] = useState( - portalNameDefaultInitially + portalNameDefaultInitially, ); const [isLoadingPortalNameSave, setIsLoadingPortalNameSave] = useState(false); @@ -78,6 +78,7 @@ const PortalRenaming = (props) => { useEffect(() => { setDocumentTitle(t("PortalRenaming")); + setPortalName(portalNameInitially); const page = isMobileView ? "language-and-time-zone" : "general"; if (!isLoaded) initSettings(page).then(() => setIsLoaded(true)); @@ -193,14 +194,14 @@ const PortalRenaming = (props) => { t("PortalNameLength", { minLength: domainValidator.minLength, maxLength: domainValidator.maxLength, - }) + }), ); saveToSessionStorage( "errorValue", t("PortalNameLength", { minLength: domainValidator.minLength, maxLength: domainValidator.maxLength, - }) + }), ); break; case !validDomain.test(value): @@ -242,7 +243,7 @@ const PortalRenaming = (props) => { const currentUrl = window.location.href.replace( window.location.origin, - "" + "", ); const newUrl = "/portal-settings/customization/general"; @@ -353,8 +354,14 @@ export default inject(({ settingsStore, setup, common }) => { domainValidator, } = settingsStore; const { setPortalRename } = setup; - const { isLoaded, setIsLoadedPortalRenaming, initSettings, setIsLoaded } = - common; + const { + isLoaded, + setIsLoadedPortalRenaming, + initSettings, + setIsLoaded, + setPortalName, + portalName, + } = common; return { theme, @@ -368,7 +375,11 @@ export default inject(({ settingsStore, setup, common }) => { currentColorScheme, renamingSettingsUrl, domainValidator, + portalName, + setPortalName, }; })( - withLoading(withTranslation(["Settings", "Common"])(observer(PortalRenaming))) + withLoading( + withTranslation(["Settings", "Common"])(observer(PortalRenaming)), + ), ); diff --git a/packages/client/src/store/CommonStore.js b/packages/client/src/store/CommonStore.js index 6dadd623c8..6b462e572d 100644 --- a/packages/client/src/store/CommonStore.js +++ b/packages/client/src/store/CommonStore.js @@ -13,6 +13,7 @@ class CommonStore { whiteLabelLogoText = null; defaultLogoTextWhiteLabel = null; + portalName = null; dnsSettings = { defaultObj: {}, customObj: {}, @@ -63,14 +64,14 @@ class CommonStore { requests.push( this.getWhiteLabelLogoUrls(), this.getWhiteLabelLogoText(), - this.getIsDefaultWhiteLabel() + this.getIsDefaultWhiteLabel(), ); break; } case "language-and-time-zone": requests.push( this.settingsStore.getPortalTimezones(), - this.settingsStore.getPortalCultures() + this.settingsStore.getPortalCultures(), ); break; case "dns-settings": @@ -88,7 +89,7 @@ class CommonStore { { requests.push( this.settingsStore.getPortalTimezones(), - this.settingsStore.getPortalCultures() + this.settingsStore.getPortalCultures(), ); if (standalone) { @@ -100,7 +101,7 @@ class CommonStore { requests.push( this.getWhiteLabelLogoUrls(), this.getWhiteLabelLogoText(), - this.getIsDefaultWhiteLabel() + this.getIsDefaultWhiteLabel(), ); break; @@ -123,7 +124,7 @@ class CommonStore { setWhiteLabelSettings = async (data) => { const response = await api.settings.setWhiteLabelSettings( data, - isManagement() + isManagement(), ); return Promise.resolve(response); }; @@ -187,6 +188,10 @@ class CommonStore { this.dnsSettings.customObj.dnsName = value; }; + setPortalName = (value) => { + this.portalName = value; + }; + setDNSSettings = (data) => { this.dnsSettings = { defaultObj: data, customObj: data }; }; diff --git a/packages/management/src/categories/spaces/sub-components/ConfigurationSection.tsx b/packages/management/src/categories/spaces/sub-components/ConfigurationSection.tsx index 2d43403a73..6059f4933e 100644 --- a/packages/management/src/categories/spaces/sub-components/ConfigurationSection.tsx +++ b/packages/management/src/categories/spaces/sub-components/ConfigurationSection.tsx @@ -5,11 +5,10 @@ import { TextInput } from "@docspace/shared/components/text-input"; import { Text } from "@docspace/shared/components/text"; import { ConfigurationWrapper } from "../StyledSpaces"; import { useStore } from "SRC_DIR/store"; -import { parseDomain } from "SRC_DIR/utils"; import { isMobile } from "react-device-detect"; import { toastr } from "@docspace/shared/components/toast"; import { TranslationType } from "SRC_DIR/types/spaces"; -import { validatePortalName } from "@docspace/shared/utils/common"; +import { parseDomain, validatePortalName } from "@docspace/shared/utils/common"; type TConfigurationSection = { t: TranslationType; diff --git a/packages/management/src/categories/spaces/sub-components/dialogs/ChangeDomainDialog/index.tsx b/packages/management/src/categories/spaces/sub-components/dialogs/ChangeDomainDialog/index.tsx index ef9ace99d7..0153e3865a 100644 --- a/packages/management/src/categories/spaces/sub-components/dialogs/ChangeDomainDialog/index.tsx +++ b/packages/management/src/categories/spaces/sub-components/dialogs/ChangeDomainDialog/index.tsx @@ -8,8 +8,8 @@ import { useTranslation } from "react-i18next"; import { observer } from "mobx-react"; import { TextInput } from "@docspace/shared/components/text-input"; import { useStore } from "SRC_DIR/store"; -import { parseDomain } from "SRC_DIR/utils"; import { toastr } from "@docspace/shared/components/toast"; +import { parseDomain } from "@docspace/shared/utils/common"; const StyledModal = styled(ModalDialogContainer)` .create-docspace-input-block { diff --git a/packages/management/src/utils/index.ts b/packages/management/src/utils/index.ts index 999ef23fc3..c7c8b71003 100644 --- a/packages/management/src/utils/index.ts +++ b/packages/management/src/utils/index.ts @@ -1,8 +1,5 @@ import { settingsTree } from "./settingsTree"; import { translations } from "../autoGeneratedTranslations"; -import { parseAddress } from "@docspace/shared/utils"; -import { ErrorKeys } from "@docspace/shared/enums"; -import { TranslationType } from "SRC_DIR/types/spaces"; export const getItemByLink = (path: string) => { const resultPath = path.split("/")[1]; @@ -63,41 +60,3 @@ export function loadLanguagePath(homepage: string, fixedNS = null) { return path; }; } - -export const parseDomain = ( - domain: string, - setError: Function, - t: TranslationType -) => { - let parsedDomain = parseAddress("test@" + domain); - - if (parsedDomain?.parseErrors.length > 0) { - const translatedErrors = parsedDomain.parseErrors.map((error) => { - switch (error.errorKey) { - case ErrorKeys.LocalDomain: - return t("Common:LocalDomain"); - case ErrorKeys.IncorrectDomain: - case ErrorKeys.IncorrectEmail: - return t("Common:IncorrectDomain"); - case ErrorKeys.DomainIpAddress: - return t("Common:DomainIpAddress"); - case ErrorKeys.PunycodeDomain: - return t("Common:PunycodeDomain"); - case ErrorKeys.PunycodeLocalPart: - return t("Common:PunycodeLocalPart"); - case ErrorKeys.IncorrectLocalPart: - return t("Common:IncorrectLocalPart"); - case ErrorKeys.SpacesInLocalPart: - return t("Common:SpacesInLocalPart"); - case ErrorKeys.MaxLengthExceeded: - return t("Common:MaxLengthExceeded"); - default: - throw new Error("Unknown translation key"); - } - }); - - setError(translatedErrors); - } - - return parsedDomain.isValid(); -}; diff --git a/packages/shared/utils/common.ts b/packages/shared/utils/common.ts index 8cc9944c43..f0642bfbfc 100644 --- a/packages/shared/utils/common.ts +++ b/packages/shared/utils/common.ts @@ -23,8 +23,14 @@ import BackgroundPatternRedReactSvgUrl from "PUBLIC_DIR/images/background.patter import BackgroundPatternPurpleReactSvgUrl from "PUBLIC_DIR/images/background.pattern.purple.react.svg?url"; import BackgroundPatternLightBlueReactSvgUrl from "PUBLIC_DIR/images/background.pattern.lightBlue.react.svg?url"; import BackgroundPatternBlackReactSvgUrl from "PUBLIC_DIR/images/background.pattern.black.react.svg?url"; - -import { FolderType, RoomsType, ShareAccessRights, ThemeKeys } from "../enums"; +import { parseAddress } from "@docspace/shared/utils"; +import { + FolderType, + RoomsType, + ShareAccessRights, + ThemeKeys, + ErrorKeys, +} from "../enums"; import { LANGUAGE, RTL_LANGUAGES } from "../constants"; import { TI18n } from "../types"; @@ -106,6 +112,44 @@ export const getUserTypeLabel = ( } }; +export const parseDomain = ( + domain: string, + setError: Function, + t: (key: string) => string, +) => { + const parsedDomain = parseAddress("test@" + domain); + + if (parsedDomain?.parseErrors.length > 0) { + const translatedErrors = parsedDomain.parseErrors.map((error) => { + switch (error.errorKey) { + case ErrorKeys.LocalDomain: + return t("Common:LocalDomain"); + case ErrorKeys.IncorrectDomain: + case ErrorKeys.IncorrectEmail: + return t("Common:IncorrectDomain"); + case ErrorKeys.DomainIpAddress: + return t("Common:DomainIpAddress"); + case ErrorKeys.PunycodeDomain: + return t("Common:PunycodeDomain"); + case ErrorKeys.PunycodeLocalPart: + return t("Common:PunycodeLocalPart"); + case ErrorKeys.IncorrectLocalPart: + return t("Common:IncorrectLocalPart"); + case ErrorKeys.SpacesInLocalPart: + return t("Common:SpacesInLocalPart"); + case ErrorKeys.MaxLengthExceeded: + return t("Common:MaxLengthExceeded"); + default: + return t("Common:IncorrectDomain"); + } + }); + + setError(translatedErrors); + } + + return parsedDomain.isValid(); +}; + export const validatePortalName = ( value: string, nameValidator: { minLength: number; maxLength: number; regex: RegExp },