Merge branch 'develop' into feature/webhooks-ui

This commit is contained in:
Alexey Safronov 2023-03-10 15:16:17 +04:00
commit 22c944e865
46 changed files with 974 additions and 863 deletions

View File

@ -1,2 +1,5 @@
PUSHD %~dp0..
dotnet test common\Tests\Frontend.Translations.Tests\Frontend.Translations.Tests.csproj --filter Name~SpellCheckTest -l:html -r TestsResults
set dir=%~dp0..
echo %dir%
dotnet test common\Tests\Frontend.Translations.Tests\Frontend.Translations.Tests.csproj --filter Name~SpellCheckTest -l:html --environment "BASE_DIR=%dir%" --results-directory "%dir%/TestsResults"

View File

@ -0,0 +1,8 @@
rd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
echo "Run script directory:" $dir
dir=$(builtin cd $rd/../; pwd)
echo "Root directory:" $dir
dotnet test $dir/common/Tests/Frontend.Translations.Tests/Frontend.Translations.Tests.csproj --filter Name~SpellCheckTest -l:html --results-directory "$dir/TestsResults" --environment "BASE_DIR=$dir"

View File

@ -1,4 +1,4 @@
PUSHD %~dp0..
set dir=%~dp0..
echo %dir%
dotnet test common\Tests\Frontend.Translations.Tests\Frontend.Translations.Tests.csproj --filter "TestCategory=FastRunning" -l:html --environment "BASE_DIR=%dir%" --results-directory "%dir%/TestsResults"
dotnet test common\Tests\Frontend.Translations.Tests\Frontend.Translations.Tests.csproj --filter "TestCategory=Locales" -l:html --environment "BASE_DIR=%dir%" --results-directory "%dir%/TestsResults"

View File

@ -5,4 +5,4 @@ dir=$(builtin cd $rd/../; pwd)
echo "Root directory:" $dir
dotnet test $dir/common/Tests/Frontend.Translations.Tests/Frontend.Translations.Tests.csproj --filter "TestCategory=FastRunning" -l:html --results-directory "$dir/TestsResults" --environment "BASE_DIR=$dir"
dotnet test $dir/common/Tests/Frontend.Translations.Tests/Frontend.Translations.Tests.csproj --filter "TestCategory=Locales" -l:html --results-directory "$dir/TestsResults" --environment "BASE_DIR=$dir"

View File

@ -181,7 +181,7 @@ public class ImagesTest
}
[Test]
[Category("FastRunning")]
[Category("Images")]
public void ParseMd5Test()
{
Assert.AreEqual(0, HashErrorFiles.Count, string.Join("\r\n",
@ -189,7 +189,7 @@ public class ImagesTest
}
[Test]
[Category("FastRunning")]
[Category("Images")]
public void DublicatesFilesByMD5HashTest()
{
var duplicatesByMD5 = ImageFiles
@ -205,7 +205,7 @@ public class ImagesTest
}
[Test]
[Category("FastRunning")]
[Category("Images")]
public void DublicatesFilesByFileNameButDifferentByMD5HashTest()
{
var duplicatesByNameWithDifMD5 = ImageFiles
@ -222,7 +222,7 @@ public class ImagesTest
}
[Test]
[Category("FastRunning")]
[Category("Images")]
public void UselessImagesTest()
{
var usedImages = SourceImageFiles
@ -371,487 +371,5 @@ public class ImagesTest
}
Assert.AreEqual(0, 0);
}
/*[Test]
[Category("FastRunning")]
public void NotTranslatedKeysTest()
{
var message = $"Next languages are not equal 'en' by translated keys count:\r\n\r\n";
var exists = false;
var i = 0;
foreach (var module in ModuleFolders)
{
if (module.AvailableLanguages == null)
continue;
var enLanguages = module.AvailableLanguages.Where(l => l.Language == "en").ToList();
var otherLanguages = module.AvailableLanguages.Where(l => l.Language != "en").ToList();
foreach (var lng in otherLanguages)
{
var lngKeys = lng.Translations.Select(f => f.Key).ToList();
var enKeys = enLanguages.Where(l => l.Path == lng.Path.Replace(Utils.ConvertPathToOS($"/{lng.Language}/"), Utils.ConvertPathToOS("/en/")))
.SelectMany(l => l.Translations.Select(f => f.Key))
.ToList();
var notFoundKeys = enKeys.Except(lngKeys).ToList();
if (!notFoundKeys.Any())
continue;
exists = true;
message += $"{++i}. Language ('{lng.Language}'={notFoundKeys.Count}/'en'={enKeys.Count}). Path '{lng.Path}' " +
$"Not found keys:\r\n\r\n";
message += string.Join("\r\n", notFoundKeys) + "\r\n\r\n";
// Save empty not found keys
//SaveNotFoundKeys(lng.Path, notFoundKeys);
}
}
Assert.AreEqual(false, exists, message);
}
[Test]
[Category("FastRunning")]
public void NotFoundKeysTest()
{
var allEnKeys = TranslationFiles
.Where(file => file.Language == "en")
.SelectMany(item => item.Translations)
.Select(item => item.Key);
var allJsTranslationKeys = JavaScriptFiles
.Where(f => !f.Path.Contains("Banner.js")) // skip Banner.js (translations from firebase)
.SelectMany(j => j.TranslationKeys)
.Select(k => k.Substring(k.IndexOf(":") + 1))
.Distinct();
var notFoundJsKeys = allJsTranslationKeys.Except(allEnKeys);
Assert.AreEqual(0, notFoundJsKeys.Count(),
"Some i18n-keys are not exist in translations in 'en' language: Keys:\r\n{0}",
string.Join("\r\n", notFoundJsKeys));
}
[Test]
[Category("FastRunning")]
public void UselessTranslationKeysTest()
{
var allEnKeys = TranslationFiles
.Where(file => file.Language == "en")
.SelectMany(item => item.Translations)
.Select(item => item.Key)
.Where(k => !k.StartsWith("Culture_"))
.OrderBy(t => t);
var allJsTranslationKeys = JavaScriptFiles
.SelectMany(j => j.TranslationKeys)
.Select(k => k.Substring(k.IndexOf(":") + 1))
.Where(k => !k.StartsWith("Culture_"))
.Distinct()
.OrderBy(t => t);
var notFoundi18nKeys = allEnKeys.Except(allJsTranslationKeys);
Assert.AreEqual(0, notFoundi18nKeys.Count(),
"Some i18n-keys are not found in js keys:\r\n{0}",
string.Join("\r\n", notFoundi18nKeys));
}
[Test]
[Category("FastRunning")]
public void UselessModuleTranslationKeysTest()
{
var notFoundi18nKeys = new List<KeyValuePair<string, List<string>>>();
var message = $"Some i18n-keys are not found in Module or Common translations: \r\nKeys: \r\n\r\n";
var index = 0;
for (int i = 0; i < ModuleFolders.Count; i++)
{
var module = ModuleFolders[i];
if (module.AppliedJsTranslationKeys == null && module.AvailableLanguages != null)
{
message += $"{++index}. 'ANY LANGUAGES' '{module.Path}' NOT USED\r\n";
var list = module.AvailableLanguages
.SelectMany(l => l.Translations.Select(t => t.Key).ToList())
.ToList();
notFoundi18nKeys.Add(new KeyValuePair<string, List<string>>("ANY LANGUAGES", list));
continue;
}
var notCommonKeys = module.AppliedJsTranslationKeys
.Where(k => !k.StartsWith("Common:"))
.OrderBy(t => t)
.ToList();
var onlyCommonKeys = module.AppliedJsTranslationKeys
.Except(notCommonKeys)
.Select(k => k.Replace("Common:", ""))
.OrderBy(t => t)
.ToList();
notCommonKeys = notCommonKeys.Select(k => k.Substring(k.IndexOf(":") + 1)).ToList();
if (onlyCommonKeys.Any())
{
foreach (var lng in CommonTranslations)
{
var list = onlyCommonKeys
.Except(lng.Translations.Select(t => t.Key))
.ToList();
if (!list.Any())
continue;
message += $"{++index}. '{lng.Language}' '{module.Path}' \r\n {string.Join("\r\n", list)} \r\n";
notFoundi18nKeys.Add(new KeyValuePair<string, List<string>>(lng.Language, list));
}
}
if (module.AvailableLanguages == null)
{
if (notCommonKeys.Any())
{
message += $"{++index}. 'ANY LANGUAGES' '{module.Path}' \r\n {string.Join("\r\n", notCommonKeys)} \r\n";
notFoundi18nKeys.Add(new KeyValuePair<string, List<string>>("ANY LANGUAGES", notCommonKeys));
}
continue;
}
foreach (var lng in module.AvailableLanguages)
{
var list = lng.Translations
.Select(t => t.Key)
.Except(notCommonKeys)
.ToList();
if (!list.Any())
continue;
message += $"{++index}. '{lng.Language}' '{module.Path}' \r\n {string.Join("\r\n", list)} \r\n";
notFoundi18nKeys.Add(new KeyValuePair<string, List<string>>(lng.Language, list));
}
}
Assert.AreEqual(0, notFoundi18nKeys.Count, message);
}
[Test]
[Category("FastRunning")]
public void NotTranslatedCommonKeysTest()
{
var message = $"Some i18n-keys are not found in COMMON translations: \r\nKeys: \r\n\r\n";
var enLanguageKeys = CommonTranslations
.Where(l => l.Language == "en")
.FirstOrDefault()
.Translations
.Select(k => k.Key)
.ToList();
var otherCommonLanguages = CommonTranslations.Where(l => l.Language != "en");
var exists = false;
var i = 0;
foreach (var lng in otherCommonLanguages)
{
var list = enLanguageKeys
.Except(lng.Translations.Select(t => t.Key))
.ToList();
if (!list.Any())
continue;
message += $"{++i}. '{lng.Language}' Keys: \r\n {string.Join("\r\n", list)} \r\n";
exists = true;
// Save empty not found keys
//SaveNotFoundKeys(lng.Path, list);
}
Assert.AreEqual(false, exists, message);
}
public static void UpdateKeys(string pathToJson, List<TranslationItem> newKeys)
{
if (!File.Exists(pathToJson) || !newKeys.Any())
return;
var jsonTranslation = JObject.Parse(File.ReadAllText(pathToJson));
var keys = newKeys.Select(k => k.Key).ToList();
var properties = jsonTranslation.Properties().ToList();
properties.ForEach(p =>
{
var newKey = newKeys.Where(k => k.Key == p.Name).FirstOrDefault();
if (newKey != null)
p.Value = newKey.Value;
});
var result = new JObject(properties);
var sortedJsonString = JsonConvert.SerializeObject(result, Formatting.Indented);
File.WriteAllText(pathToJson, sortedJsonString, Encoding.UTF8);
}
public static void RemoveEmptyKeys(string pathToJson, List<string> emptyKeys)
{
if (!File.Exists(pathToJson) || !emptyKeys.Any())
return;
var jsonTranslation = JObject.Parse(File.ReadAllText(pathToJson));
var properties = jsonTranslation.Properties().Where(p => !emptyKeys.Contains(p.Name)).ToList();
var result = new JObject(properties);
var sortedJsonString = JsonConvert.SerializeObject(result, Formatting.Indented);
File.WriteAllText(pathToJson, sortedJsonString, Encoding.UTF8);
}
public string GetWorkspace(string path)
{
var folderName = Directory.GetParent(Path.GetDirectoryName(path)).Name;
switch (folderName)
{
case "Client":
return Workspaces.Find(w => w.Contains("client"));
case "Editor":
return Workspaces.Find(w => w.Contains("editor"));
case "Login":
return Workspaces.Find(w => w.Contains("login"));
default:
return Path.Combine(BasePath, Utils.ConvertPathToOS("public\\locales"));
}
}
[Test]
[Category("FastRunning")]
public void EmptyValueKeysTest()
{
var message = $"Next files have empty keys:\r\n\r\n";
var exists = false;
var i = 0;
foreach (var module in ModuleFolders)
{
if (module.AvailableLanguages == null)
continue;
foreach (var lng in module.AvailableLanguages)
{
var emptyTranslationItems = lng.Translations.Where(f => string.IsNullOrEmpty(f.Value)).ToList();
if (!emptyTranslationItems.Any())
continue;
exists = true;
message += $"{++i}. Language '{lng.Language}' (Count: {emptyTranslationItems.Count}). Path '{lng.Path}' " +
$"Empty keys:\r\n\r\n";
var emptyKeys = emptyTranslationItems.Select(t => t.Key).ToList();
message += string.Join("\r\n", emptyKeys) + "\r\n\r\n";
}
}
foreach (var lng in CommonTranslations)
{
var emptyTranslationItems = lng.Translations.Where(f => string.IsNullOrEmpty(f.Value)).ToList();
if (!emptyTranslationItems.Any())
continue;
exists = true;
message += $"{++i}. Language '{lng.Language}' (Count: {emptyTranslationItems.Count}). Path '{lng.Path}' " +
$"Empty keys:\r\n\r\n";
var emptyKeys = emptyTranslationItems.Select(t => t.Key).ToList();
message += string.Join("\r\n", emptyKeys) + "\r\n\r\n";
}
Assert.AreEqual(false, exists, message);
}
[Test]
[Category("FastRunning")]
public void LanguageTranslatedPercentTest()
{
var message = $"Next languages translated less then 100%:\r\n\r\n";
var groupedByLng = TranslationFiles
.GroupBy(t => t.Language)
.Select(g => new
{
Language = g.Key,
AllTranslated = g.ToList()
.SelectMany(t => t.Translations)
.ToList()
})
.Select(t => new
{
t.Language,
TotalKeysCount = t.AllTranslated.LongCount(),
EmptyKeysCount = t.AllTranslated
.Where(t => string.IsNullOrEmpty(t.Value))
.LongCount()
})
.ToList();
var i = 0;
var exists = false;
var expectedTotalKeysCount = groupedByLng.Where(t => t.Language == "en").Single().TotalKeysCount;
foreach (var lng in groupedByLng)
{
if (lng.EmptyKeysCount == 0 && lng.TotalKeysCount == expectedTotalKeysCount)
continue;
exists = true;
var translated = lng.TotalKeysCount == expectedTotalKeysCount
? Math.Round(100f - (lng.EmptyKeysCount * 100f / expectedTotalKeysCount), 1)
: Math.Round(lng.TotalKeysCount * 100f / expectedTotalKeysCount, 1);
message += $"{++i}. Language '{lng.Language}' translated by '{translated}%'\r\n";
}
Assert.AreEqual(false, exists, message);
}
[Test]
[Category("FastRunning")]
public void NotTranslatedToastsTest()
{
var message = $"Next text not translated in toasts:\r\n\r\n";
var i = 0;
NotTranslatedToasts.GroupBy(t => t.Key)
.Select(g => new
{
FilePath = g.Key,
Values = g.ToList()
})
.ToList()
.ForEach(t =>
{
message += $"{++i}. Path='{t.FilePath}'\r\n\r\n{string.Join("\r\n", t.Values.Select(v => v.Value))}\r\n\r\n";
});
Assert.AreEqual(0, NotTranslatedToasts.Count, message);
}
[Test]
[Category("FastRunning")]
public void WrongTranslationVariablesTest()
{
var message = $"Next keys have wrong variables:\r\n\r\n";
var regVariables = new Regex("\\{\\{([^\\{].?[^\\}]+)\\}\\}", RegexOptions.Compiled | RegexOptions.Multiline);
var groupedByLng = TranslationFiles
.GroupBy(t => t.Language)
.Select(g => new
{
Language = g.Key,
TranslationsWithVariables = g.ToList()
.SelectMany(t => t.Translations)
.Where(k => k.Value.IndexOf("{{") != -1)
.Select(t => new
{
t.Key,
t.Value,
Variables = regVariables.Matches(t.Value)
.Select(m => m.Groups[1]?.Value?.Trim().Replace(", lowercase", ""))
.ToList()
})
.ToList()
})
.ToList();
var enWithVariables = groupedByLng
.Where(t => t.Language == "en")
.SelectMany(t => t.TranslationsWithVariables)
.ToList();
var otherLanguagesWithVariables = groupedByLng
.Where(t => t.Language != "en")
.ToList();
var i = 0;
var errorsCount = 0;
foreach (var lng in otherLanguagesWithVariables)
{
foreach (var t in lng.TranslationsWithVariables)
{
var enKey = enWithVariables
.Where(en => en.Key == t.Key)
.FirstOrDefault();
if (enKey == null)
{
// wrong
message += $"{++i}. lng='{lng.Language}' key='{t.Key}' has no 'en' language variant (!!!useless key!!!)\r\n\r\n";
errorsCount++;
continue;
}
if (enKey.Variables.Count != t.Variables.Count)
{
// wrong
message += $"{++i}. lng='{lng.Language}' key='{t.Key}' has less variables then 'en' language have " +
$"(en={enKey.Variables.Count}|{lng.Language}={t.Variables.Count})\r\n" +
$"'en': '{enKey.Value}'\r\n'{lng.Language}': '{t.Value}'\r\n\r\n";
errorsCount++;
}
if (!t.Variables.All(v => enKey.Variables.Contains(v)))
{
// wrong
errorsCount++;
message += $"{++i}. lng='{lng.Language}' key='{t.Key}' has not equals variables of 'en' language have\r\n\r\n" +
$"Have to be:\r\n'{enKey.Value}'\r\n\r\n{string.Join("\r\n", enKey.Variables)}\r\n\r\n" +
$"But in real:\r\n'{t.Value}'\r\n\r\n{string.Join("\r\n", t.Variables)} \r\n\r\n";
}
}
}
Assert.AreEqual(0, errorsCount, message);
}*/
}
}

View File

@ -174,6 +174,7 @@ public class LocalesTest
let clientDir = Path.Combine(BasePath, wsPath)
from filePath in Utils.GetFiles(clientDir, searchPatern, SearchOption.AllDirectories)
where !filePath.Contains(Utils.ConvertPathToOS("dist/"))
&& !filePath.Contains(Utils.ConvertPathToOS("storybook-static/"))
&& !filePath.Contains(".test.js")
&& !filePath.Contains(".stories.js")
&& !filePath.Contains(".test.ts")
@ -316,12 +317,12 @@ public class LocalesTest
TestContext.Progress.WriteLine($"Found CommonTranslations = {CommonTranslations.Count()}. First path is '{CommonTranslations.FirstOrDefault()?.Path}'");
TestContext.Progress.WriteLine($"Found _md5Excludes = {_md5Excludes.Count()} Path to file '{_md5ExcludesPath}'");
TestContext.Progress.WriteLine($"Found _md5Excludes = {_md5Excludes.Count()} Path to file '{_md5ExcludesPath}'");
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void ParseJsonTest()
{
Assert.AreEqual(0, ParseJsonErrors.Count, string.Join("\r\n", ParseJsonErrors.Select(e => $"File path = '{e.Path}' failed to parse with error: '{e.Exception.Message}'")));
@ -350,7 +351,7 @@ public class LocalesTest
}
[Test]
[Category("LongRunning")]
[Category("SpellCheck")]
public void SpellCheckTest()
{
var i = 0;
@ -425,7 +426,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void SingleKeyFilesTest()
{
var singleKeyTranslationFiles = TranslationFiles
@ -436,7 +437,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void DublicatesFilesByMD5HashTest()
{
var duplicatesByMD5 = TranslationFiles
@ -452,7 +453,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void FullEnDublicatesTest()
{
var fullEnDuplicates = TranslationFiles
@ -469,7 +470,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void EnDublicatesByContentTest()
{
var allRuTranslations = TranslationFiles
@ -548,7 +549,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void NotAllLanguageTranslatedTest()
{
var groupedByLng = TranslationFiles
@ -625,7 +626,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void NotTranslatedKeysTest()
{
var message = $"Next languages are not equal 'en' by translated keys count:\r\n\r\n";
@ -672,7 +673,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void NotFoundKeysTest()
{
var allEnKeys = TranslationFiles
@ -694,7 +695,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void UselessTranslationKeysTest()
{
var allEnKeys = TranslationFiles
@ -719,7 +720,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void UselessModuleTranslationKeysTest()
{
var notFoundi18nKeys = new List<KeyValuePair<string, List<string>>>();
@ -807,7 +808,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void NotTranslatedCommonKeysTest()
{
var message = $"Some i18n-keys are not found in COMMON translations: \r\nKeys: \r\n\r\n";
@ -904,7 +905,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void EmptyValueKeysTest()
{
// Uncomment if new keys are available
@ -1008,7 +1009,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void LanguageTranslatedPercentTest()
{
var message = $"Next languages translated less then 100%:\r\n\r\n";
@ -1055,7 +1056,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void NotTranslatedToastsTest()
{
var message = $"Next text not translated in toasts:\r\n\r\n";
@ -1078,7 +1079,7 @@ public class LocalesTest
}
[Test]
[Category("FastRunning")]
[Category("Locales")]
public void WrongTranslationVariablesTest()
{
var message = $"Next keys have wrong variables:\r\n\r\n";
@ -1156,7 +1157,7 @@ public class LocalesTest
}
//[Test]
//[Category("FastRunning")]
//[Category("Locales")]
//public void TranslationsEncodingTest()
//{
// /*//Convert to UTF-8

View File

@ -50,10 +50,17 @@ const ArticleBodyContent = (props) => {
const [disableBadgeClick, setDisableBadgeClick] = React.useState(false);
let loadTimeout = null;
const campaigns = (localStorage.getItem("campaigns") || "")
.split(",")
.filter((campaign) => campaign.length > 0);
const cleanTimer = () => {
loadTimeout && clearTimeout(loadTimeout);
loadTimeout = null;
};
const onClick = React.useCallback(
(folderId) => {
const {
@ -72,7 +79,9 @@ const ArticleBodyContent = (props) => {
const filesSection = window.location.pathname.indexOf("/filter") > 0;
if (filesSection) {
setIsLoading(true);
loadTimeout = setTimeout(() => {
setIsLoading(true);
}, 200);
} else {
showLoader();
}
@ -88,6 +97,7 @@ const ArticleBodyContent = (props) => {
fetchRooms(folderId, filter).finally(() => {
if (filesSection) {
cleanTimer();
setIsLoading(false);
} else {
hideLoader();
@ -98,6 +108,7 @@ const ArticleBodyContent = (props) => {
.catch((err) => toastr.error(err))
.finally(() => {
if (filesSection) {
cleanTimer();
setIsLoading(false);
} else {
hideLoader();

View File

@ -18,7 +18,7 @@ class ConfirmRoute extends React.Component {
}
componentDidMount() {
const { forUnauthorized, history, isAuthenticated } = this.props;
const { forUnauthorized, isAuthenticated } = this.props;
if (forUnauthorized && isAuthenticated) {
this.props.logout();
@ -42,18 +42,25 @@ class ConfirmRoute extends React.Component {
.then((validationResult) => {
switch (validationResult) {
case ValidationResult.Ok:
const confirmHeader = `${confirmLinkData}&${search.slice(1)}`;
const confirmHeader = search.slice(1);
const linkData = {
...confirmLinkData,
confirmHeader,
};
console.log("checkConfirmLink", {
confirmLinkData,
validationResult,
linkData,
});
this.setState({
isLoaded: true,
linkData,
});
break;
case ValidationResult.Invalid:
console.error("invlid link");
console.error("invlid link", { confirmLinkData, validationResult });
window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url,
path,
@ -61,7 +68,10 @@ class ConfirmRoute extends React.Component {
);
break;
case ValidationResult.Expired:
console.error("expired link");
console.error("expired link", {
confirmLinkData,
validationResult,
});
window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url,
path,
@ -69,7 +79,10 @@ class ConfirmRoute extends React.Component {
);
break;
default:
console.error("unknown link");
console.error("unknown link", {
confirmLinkData,
validationResult,
});
window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url,
path,
@ -79,7 +92,7 @@ class ConfirmRoute extends React.Component {
}
})
.catch((error) => {
console.error(error);
console.error("FAILED checkConfirmLink", { error, confirmLinkData });
window.location.href = combineUrl(
window.DocSpaceConfig?.proxy?.url,
path,

View File

@ -6,17 +6,18 @@ import Section from "@docspace/common/components/Section";
import { combineUrl } from "@docspace/common/utils";
import tryRedirectTo from "@docspace/common/utils/tryRedirectTo";
import { inject, observer } from "mobx-react";
import { EmployeeActivationStatus } from "@docspace/common/constants";
class ActivateEmail extends React.PureComponent {
componentDidMount() {
const { logout, changeEmail, linkData } = this.props;
const { logout, updateEmailActivationStatus, linkData } = this.props;
const [email, uid, key] = [
linkData.email,
linkData.uid,
linkData.confirmHeader,
];
logout().then(() =>
changeEmail(uid, email, key)
updateEmailActivationStatus(EmployeeActivationStatus.Activated, uid, key)
.then((res) => {
tryRedirectTo(
combineUrl(
@ -70,6 +71,6 @@ export default inject(({ auth }) => {
const { logout, userStore } = auth;
return {
logout,
changeEmail: userStore.changeEmail,
updateEmailActivationStatus: userStore.updateEmailActivationStatus,
};
})(withRouter(observer(ActivateEmailForm)));

View File

@ -10,9 +10,11 @@ import {
} from "./StyledPreview";
import ButtonPlusIcon from "PUBLIC_DIR/images/actions.button.plus.react.svg";
import { saveToSessionStorage, getFromSessionStorage } from "../../../utils";
const Preview = (props) => {
const {
appliedColorAccent,
previewAccent,
themePreview,
selectThemeId,
@ -21,7 +23,6 @@ const Preview = (props) => {
floatingButtonClass,
colorCheckImg,
} = props;
const [colorPreview, setColorPreview] = useState(previewAccent);
const [isViewTablet, setIsViewTablet] = useState(false);
@ -31,10 +32,25 @@ const Preview = (props) => {
setIsViewTablet(tablet);
};
const getSettings = () => {
const selectColorAccent = getFromSessionStorage("selectColorAccent");
saveToSessionStorage("defaultColorAccent", appliedColorAccent);
if (selectColorAccent) {
setColorPreview(selectColorAccent);
} else {
setColorPreview(appliedColorAccent);
}
};
useEffect(() => {
setColorPreview(previewAccent);
getSettings();
}, [previewAccent]);
useEffect(() => {
saveToSessionStorage("selectColorAccent", colorPreview);
}, [colorPreview]);
useEffect(() => {
onCheckView();
window.addEventListener("resize", onCheckView);

View File

@ -8,6 +8,7 @@ import Checkbox from "@docspace/components/checkbox";
import toastr from "@docspace/components/toast/toastr";
import LoaderAdditionalResources from "../sub-components/loaderAdditionalResources";
import isEqual from "lodash/isEqual";
import { saveToSessionStorage, getFromSessionStorage } from "../../../utils";
const StyledComponent = styled.div`
margin-top: 40px;
@ -51,54 +52,60 @@ const AdditionalResources = (props) => {
isLoadedAdditionalResources,
} = props;
const [feedbackAndSupportEnabled, setShowFeedback] = useState(
additionalResourcesData?.feedbackAndSupportEnabled
);
const [videoGuidesEnabled, setShowVideoGuides] = useState(
additionalResourcesData?.videoGuidesEnabled
);
const [helpCenterEnabled, setShowHelpCenter] = useState(
additionalResourcesData?.helpCenterEnabled
);
const [additionalSettings, setAdditionalSettings] = useState({});
const [hasChange, setHasChange] = useState(false);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setShowFeedback(additionalResourcesData?.feedbackAndSupportEnabled);
setShowVideoGuides(additionalResourcesData?.videoGuidesEnabled);
setShowHelpCenter(additionalResourcesData?.helpCenterEnabled);
}, [additionalResourcesData]);
const {
feedbackAndSupportEnabled,
videoGuidesEnabled,
helpCenterEnabled,
} = additionalSettings;
useEffect(() => {
const settings = {
feedbackAndSupportEnabled,
videoGuidesEnabled,
helpCenterEnabled,
};
const getSettings = () => {
const additionalSettings = getFromSessionStorage("additionalSettings");
const dataAdditionalResources = {
const defaultData = {
feedbackAndSupportEnabled:
additionalResourcesData.feedbackAndSupportEnabled,
videoGuidesEnabled: additionalResourcesData.videoGuidesEnabled,
helpCenterEnabled: additionalResourcesData.helpCenterEnabled,
};
const hasСhange = !isEqual(settings, dataAdditionalResources);
saveToSessionStorage("defaultAdditionalSettings", defaultData);
if (hasСhange) {
setHasChange(true);
if (additionalSettings) {
setAdditionalSettings({
feedbackAndSupportEnabled: additionalSettings.feedbackAndSupportEnabled,
videoGuidesEnabled: additionalSettings.videoGuidesEnabled,
helpCenterEnabled: additionalSettings.helpCenterEnabled,
});
} else {
setHasChange(false);
setAdditionalSettings(defaultData);
}
}, [
feedbackAndSupportEnabled,
videoGuidesEnabled,
helpCenterEnabled,
additionalResourcesData,
]);
};
useEffect(() => {
getSettings();
}, [isLoading]);
useEffect(() => {
const defaultAdditionalSettings = getFromSessionStorage(
"defaultAdditionalSettings"
);
const newSettings = {
feedbackAndSupportEnabled: additionalSettings.feedbackAndSupportEnabled,
videoGuidesEnabled: additionalSettings.videoGuidesEnabled,
helpCenterEnabled: additionalSettings.helpCenterEnabled,
};
saveToSessionStorage("additionalSettings", newSettings);
if (isEqual(defaultAdditionalSettings, newSettings)) {
setHasChange(false);
} else {
setHasChange(true);
}
}, [additionalSettings, additionalResourcesData]);
useEffect(() => {
if (!(additionalResourcesData && tReady)) return;
@ -123,21 +130,29 @@ const AdditionalResources = (props) => {
await getAdditionalResources();
const data = {
feedbackAndSupportEnabled,
videoGuidesEnabled,
helpCenterEnabled,
};
saveToSessionStorage("additionalSettings", data);
saveToSessionStorage("defaultAdditionalSettings", data);
setIsLoading(false);
}, [
setIsLoading,
setAdditionalResources,
getAdditionalResources,
feedbackAndSupportEnabled,
videoGuidesEnabled,
helpCenterEnabled,
additionalSettings,
]);
const onRestore = useCallback(async () => {
setIsLoading(true);
await restoreAdditionalResources()
.then(() => {
.then((res) => {
setAdditionalSettings(res);
saveToSessionStorage("additionalSettings", res);
toastr.success(t("Settings:SuccessfullySaveSettingsMessage"));
})
.catch((error) => {
@ -147,14 +162,40 @@ const AdditionalResources = (props) => {
await getAdditionalResources();
setIsLoading(false);
}, [
setIsLoading,
restoreAdditionalResources,
getAdditionalResources,
feedbackAndSupportEnabled,
videoGuidesEnabled,
helpCenterEnabled,
]);
}, [setIsLoading, restoreAdditionalResources, getAdditionalResources]);
const onChangeFeedback = () => {
setAdditionalSettings({
...additionalSettings,
feedbackAndSupportEnabled: !feedbackAndSupportEnabled,
});
saveToSessionStorage("additionalSettings", {
...additionalSettings,
feedbackAndSupportEnabled: !feedbackAndSupportEnabled,
});
};
const onChangeVideoGuides = () => {
setAdditionalSettings({
...additionalSettings,
videoGuidesEnabled: !videoGuidesEnabled,
});
saveToSessionStorage("additionalSettings", {
...additionalSettings,
videoGuidesEnabled: !videoGuidesEnabled,
});
};
const onChangeHelpCenter = () => {
setAdditionalSettings({
...additionalSettings,
helpCenterEnabled: !helpCenterEnabled,
});
saveToSessionStorage("additionalSettings", {
...additionalSettings,
helpCenterEnabled: !helpCenterEnabled,
});
};
if (!isLoadedAdditionalResources) return <LoaderAdditionalResources />;
@ -176,7 +217,7 @@ const AdditionalResources = (props) => {
isDisabled={!isSettingPaid}
label={t("ShowFeedbackAndSupport")}
isChecked={feedbackAndSupportEnabled}
onChange={() => setShowFeedback(!feedbackAndSupportEnabled)}
onChange={onChangeFeedback}
/>
<Checkbox
@ -185,7 +226,7 @@ const AdditionalResources = (props) => {
isDisabled={!isSettingPaid}
label={t("ShowVideoGuides")}
isChecked={videoGuidesEnabled}
onChange={() => setShowVideoGuides(!videoGuidesEnabled)}
onChange={onChangeVideoGuides}
/>
<Checkbox
tabIndex={14}
@ -193,21 +234,20 @@ const AdditionalResources = (props) => {
isDisabled={!isSettingPaid}
label={t("ShowHelpCenter")}
isChecked={helpCenterEnabled}
onChange={() => setShowHelpCenter(!helpCenterEnabled)}
onChange={onChangeHelpCenter}
/>
</div>
{isSettingPaid && (
<SaveCancelButtons
tabIndex={15}
onSaveClick={onSave}
onCancelClick={onRestore}
saveButtonLabel={t("Common:SaveButton")}
cancelButtonLabel={t("Settings:RestoreDefaultButton")}
displaySettings={true}
showReminder={(isSettingPaid && hasChange) || isLoading}
disableRestoreToDefault={additionalResourcesIsDefault || isLoading}
/>
)}
<SaveCancelButtons
tabIndex={15}
onSaveClick={onSave}
onCancelClick={onRestore}
saveButtonLabel={t("Common:SaveButton")}
cancelButtonLabel={t("Settings:RestoreDefaultButton")}
displaySettings={true}
reminderTest={t("YouHaveUnsavedChanges")}
showReminder={(isSettingPaid && hasChange) || isLoading}
disableRestoreToDefault={additionalResourcesIsDefault || isLoading}
/>
</StyledComponent>
</>
);

View File

@ -11,6 +11,7 @@ import styled from "styled-components";
import Link from "@docspace/components/link";
import LoaderCompanyInfoSettings from "../sub-components/loaderCompanyInfoSettings";
import AboutDialog from "../../../../About/AboutDialog";
import { saveToSessionStorage, getFromSessionStorage } from "../../../utils";
const StyledComponent = styled.div`
.link {
@ -49,26 +50,28 @@ const CompanyInfoSettings = (props) => {
personal,
} = props;
const [companyName, setCompanyName] = useState(
companyInfoSettingsData.companyName
);
const [email, setEmail] = useState(companyInfoSettingsData.email);
const [phone, setPhone] = useState(companyInfoSettingsData.phone);
const [site, setSite] = useState(companyInfoSettingsData.site);
const [address, setAddress] = useState(companyInfoSettingsData.address);
const defaultCompanySettingsError = {
hasErrorAddress: false,
hasErrorCompanyName: false,
hasErrorEmail: false,
hasErrorPhone: false,
hasErrorSite: false,
};
const [hasErrorSite, setHasErrorSite] = useState(false);
const [hasErrorEmail, setHasErrorEmail] = useState(false);
const [hasErrorCompanyName, setHasErrorCompanyName] = useState(false);
const [hasErrorPhone, setHasErrorPhone] = useState(false);
const [hasErrorAddress, setHasErrorAddress] = useState(false);
const [isChangesSettings, setIsChangesSettings] = useState(false);
const [companySettings, setCompanySettings] = useState({});
const [companySettingsError, setCompanySettingsError] = useState(defaultCompanySettingsError);
const [showReminder, setShowReminder] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [showModal, setShowModal] = useState(false);
const previewData = { companyName, email, phone, site, address };
const { address, companyName, email, phone, site } = companySettings;
const {
hasErrorAddress,
hasErrorCompanyName,
hasErrorEmail,
hasErrorPhone,
hasErrorSite,
} = companySettingsError;
const link = t("Common:AboutCompanyTitle");
@ -78,24 +81,10 @@ const CompanyInfoSettings = (props) => {
setIsLoadedCompanyInfoSettingsData(true);
}, [companyInfoSettingsData, tReady]);
useEffect(() => {
setCompanyName(companyInfoSettingsData.companyName);
setEmail(companyInfoSettingsData.email);
setPhone(companyInfoSettingsData.phone);
setSite(companyInfoSettingsData.site);
setAddress(companyInfoSettingsData.address);
}, [companyInfoSettingsData]);
const getSettings = () => {
const companySettings = getFromSessionStorage("companySettings");
useEffect(() => {
const settings = {
address,
companyName,
email,
phone,
site,
};
const dataСompanyInfoSettings = {
const defaultData = {
address: companyInfoSettingsData.address,
companyName: companyInfoSettingsData.companyName,
email: companyInfoSettingsData.email,
@ -103,87 +92,119 @@ const CompanyInfoSettings = (props) => {
site: companyInfoSettingsData.site,
};
const hasError =
hasErrorSite ||
hasErrorEmail ||
hasErrorCompanyName ||
hasErrorPhone ||
hasErrorAddress;
saveToSessionStorage("defaultCompanySettings", defaultData);
const noСhange = isEqual(settings, dataСompanyInfoSettings);
if (!(hasError || noСhange)) {
setIsChangesSettings(true);
if (companySettings) {
setCompanySettings({
address: companySettings.address,
companyName: companySettings.companyName,
email: companySettings.email,
phone: companySettings.phone,
site: companySettings.site,
});
} else {
setIsChangesSettings(false);
setCompanySettings(defaultData);
}
}, [
address,
companyName,
email,
phone,
site,
hasErrorSite,
hasErrorEmail,
hasErrorCompanyName,
hasErrorPhone,
hasErrorAddress,
companyInfoSettingsData,
]);
};
const validateUrl = (url) => {
useEffect(() => {
getSettings();
}, [isLoading]);
useEffect(() => {
const defaultCompanySettings = getFromSessionStorage("defaultCompanySettings");
const newSettings = {
address: companySettings.address,
companyName: companySettings.companyName,
email: companySettings.email,
phone: companySettings.phone,
site: companySettings.site,
};
saveToSessionStorage("companySettings", newSettings);
if (isEqual(defaultCompanySettings, newSettings)) {
setShowReminder(false);
} else {
setShowReminder(true);
}
}, [companySettings, companyInfoSettingsData]);
const validateSite = (site) => {
const urlRegex = /^(ftp|http|https):\/\/[^ "]+$/;
const hasError = !urlRegex.test(url);
const hasErrorSite = !urlRegex.test(site);
setHasErrorSite(hasError);
setCompanySettingsError({ ...companySettingsError, hasErrorSite });
};
const validateEmail = (email) => {
const emailRegex = /.+@.+\..+/;
const hasError = !emailRegex.test(email);
const hasErrorEmail = !emailRegex.test(email);
setHasErrorEmail(hasError);
setCompanySettingsError({ ...companySettingsError, hasErrorEmail });
};
const validateEmpty = (value, type) => {
const hasError = value.trim() === "";
const phoneRegex = /^[\d\(\)\-+]+$/;
const hasErrorPhone = !phoneRegex.test(value);
if (type === "companyName") {
setHasErrorCompanyName(hasError);
setCompanySettingsError({
...companySettingsError,
hasErrorCompanyName: hasError,
});
}
if (type === "phone") {
setHasErrorPhone(hasError);
setCompanySettingsError({
...companySettingsError,
hasErrorPhone,
});
}
if (type === "address") {
setHasErrorAddress(hasError);
setCompanySettingsError({
...companySettingsError,
hasErrorAddress: hasError,
});
}
};
const onChangeSite = (url) => {
validateUrl(url);
setSite(url);
const onChangeSite = (e) => {
const site = e.target.value;
validateSite(site);
setCompanySettings({ ...companySettings, site });
saveToSessionStorage("companySettings", { ...companySettings, site });
};
const onChangeEmail = (email) => {
const onChangeEmail = (e) => {
const email = e.target.value;
validateEmail(email);
setEmail(email);
setCompanySettings({ ...companySettings, email });
saveToSessionStorage("companySettings", { ...companySettings, email });
};
const onChangeСompanyName = (companyName) => {
const onChangeСompanyName = (e) => {
const companyName = e.target.value;
validateEmpty(companyName, "companyName");
setCompanyName(companyName);
setCompanySettings({ ...companySettings, companyName });
saveToSessionStorage("companySettings", {...companySettings, companyName });
};
const onChangePhone = (phone) => {
const onChangePhone = (e) => {
const phone = e.target.value;
validateEmpty(phone, "phone");
setPhone(phone);
setCompanySettings({ ...companySettings, phone });
saveToSessionStorage("companySettings", { ...companySettings, phone });
};
const onChangeAddress = (address) => {
const onChangeAddress = (e) => {
const address = e.target.value;
validateEmpty(address, "address");
setAddress(address);
setCompanySettings({ ...companySettings, address });
saveToSessionStorage("companySettings", { ...companySettings, address });
};
const onSave = useCallback(async () => {
@ -199,24 +220,41 @@ const CompanyInfoSettings = (props) => {
await getCompanyInfoSettings();
const data = {
address,
companyName,
email,
phone,
site,
};
saveToSessionStorage("companySettings", data);
saveToSessionStorage("defaultCompanySettings", data);
setCompanySettingsError({
hasErrorAddress: false,
hasErrorCompanyName: false,
hasErrorEmail: false,
hasErrorPhone: false,
hasErrorSite: false,
});
setIsLoading(false);
}, [
setIsLoading,
setCompanyInfoSettings,
getCompanyInfoSettings,
address,
companyName,
email,
phone,
site,
companySettings,
]);
const onRestore = useCallback(async () => {
setIsLoading(true);
await restoreCompanyInfoSettings()
.then(() => {
.then((res) => {
toastr.success(t("Settings:SuccessfullySaveSettingsMessage"));
setCompanySettings(res);
saveToSessionStorage("companySettings", res);
})
.catch((error) => {
toastr.error(error);
@ -224,6 +262,14 @@ const CompanyInfoSettings = (props) => {
await getCompanyInfoSettings();
setCompanySettingsError({
hasErrorAddress: false,
hasErrorCompanyName: false,
hasErrorEmail: false,
hasErrorPhone: false,
hasErrorSite: false,
});
setIsLoading(false);
}, [setIsLoading, restoreCompanyInfoSettings, getCompanyInfoSettings]);
@ -246,7 +292,7 @@ const CompanyInfoSettings = (props) => {
onClose={onCloseModal}
buildVersionInfo={buildVersionInfo}
personal={personal}
previewData={previewData}
previewData={companySettings}
/>
<StyledComponent isSettingPaid={isSettingPaid}>
@ -280,7 +326,7 @@ const CompanyInfoSettings = (props) => {
scale={true}
value={companyName}
hasError={hasErrorCompanyName}
onChange={(e) => onChangeСompanyName(e.target.value)}
onChange={onChangeСompanyName}
tabIndex={5}
/>
</FieldContainer>
@ -298,7 +344,7 @@ const CompanyInfoSettings = (props) => {
scale={true}
value={email}
hasError={hasErrorEmail}
onChange={(e) => onChangeEmail(e.target.value)}
onChange={onChangeEmail}
tabIndex={6}
/>
</FieldContainer>
@ -315,7 +361,7 @@ const CompanyInfoSettings = (props) => {
scale={true}
value={phone}
hasError={hasErrorPhone}
onChange={(e) => onChangePhone(e.target.value)}
onChange={onChangePhone}
tabIndex={7}
/>
</FieldContainer>
@ -332,7 +378,7 @@ const CompanyInfoSettings = (props) => {
scale={true}
value={site}
hasError={hasErrorSite}
onChange={(e) => onChangeSite(e.target.value)}
onChange={onChangeSite}
tabIndex={8}
/>
</FieldContainer>
@ -349,7 +395,7 @@ const CompanyInfoSettings = (props) => {
scale={true}
value={address}
hasError={hasErrorAddress}
onChange={(e) => onChangeAddress(e.target.value)}
onChange={onChangeAddress}
tabIndex={9}
/>
</FieldContainer>
@ -361,8 +407,9 @@ const CompanyInfoSettings = (props) => {
onCancelClick={onRestore}
saveButtonLabel={t("Common:SaveButton")}
cancelButtonLabel={t("Settings:RestoreDefaultButton")}
reminderTest={t("YouHaveUnsavedChanges")}
displaySettings={true}
showReminder={(isSettingPaid && isChangesSettings) || isLoading}
showReminder={(isSettingPaid && showReminder) || isLoading}
disableRestoreToDefault={companyInfoSettingsIsDefault || isLoading}
/>
</StyledComponent>

View File

@ -10,7 +10,7 @@ import Button from "@docspace/components/button";
import Badge from "@docspace/components/badge";
import SaveCancelButtons from "@docspace/components/save-cancel-buttons";
import toastr from "@docspace/components/toast/toastr";
import { saveToSessionStorage, getFromSessionStorage } from "../../../utils";
import WhiteLabelWrapper from "./StyledWhitelabel";
import LoaderWhiteLabel from "../sub-components/loaderWhiteLabel";
@ -40,9 +40,15 @@ const WhiteLabel = (props) => {
const [logoUrlsWhiteLabel, setLogoUrlsWhiteLabel] = useState(null);
const [isSaving, setIsSaving] = useState(false);
const companyNameFromeSessionStorage = getFromSessionStorage("companyName");
useEffect(() => {
if (logoText) {
if (!companyNameFromeSessionStorage) {
setLogoTextWhiteLabel(logoText);
saveToSessionStorage("companyName", logoText);
} else {
setLogoTextWhiteLabel(companyNameFromeSessionStorage);
saveToSessionStorage("companyName", companyNameFromeSessionStorage);
}
}, [logoText]);
@ -61,6 +67,7 @@ const WhiteLabel = (props) => {
const onChangeCompanyName = (e) => {
const value = e.target.value;
setLogoTextWhiteLabel(value);
saveToSessionStorage("companyName", value);
};
const onUseTextAsLogo = () => {

View File

@ -84,7 +84,7 @@ const DNSSettings = (props) => {
const newUrl = combineUrl(
window.DocSpaceConfig?.proxy?.url,
config.homepage,
"/portal-settings/common/customization"
"/portal-settings/customization/general"
);
if (newUrl === currentUrl) return;

View File

@ -81,6 +81,7 @@ class LanguageAndTimeZone extends React.Component {
const {
i18n,
language,
timezone,
rawTimezones,
portalTimeZoneId,
isLoaded,
@ -153,7 +154,11 @@ class LanguageAndTimeZone extends React.Component {
});
}
if (timezoneDefault && languageDefault) {
if (timezoneDefault || timezone) {
this.checkChanges();
}
if (languageDefault || language) {
this.checkChanges();
}
}
@ -327,14 +332,14 @@ class LanguageAndTimeZone extends React.Component {
onCancelClick = () => {
settingNames.forEach((settingName) => {
const valueFromSessionStorage = getFromSessionStorage(settingName);
if (
valueFromSessionStorage &&
valueFromSessionStorage !== "none" &&
valueFromSessionStorage !== null &&
!this.settingIsEqualInitialValue(settingName, valueFromSessionStorage)
) {
const defaultValue = this.state[settingName + "Default"];
this.setState({ [settingName]: defaultValue });
this.setState({ [settingName]: defaultValue || null });
saveToSessionStorage(settingName, "");
}
});
@ -386,7 +391,7 @@ class LanguageAndTimeZone extends React.Component {
const newUrl = combineUrl(
window.DocSpaceConfig?.proxy?.url,
config.homepage,
"/portal-settings/common/customization"
"/portal-settings/customization/general"
);
if (newUrl === currentUrl) return;

View File

@ -19,6 +19,7 @@ import { saveToSessionStorage, getFromSessionStorage } from "../../../utils";
import { setDocumentTitle } from "SRC_DIR/helpers/utils";
import LoaderCustomization from "../sub-components/loaderCustomization";
import withLoading from "SRC_DIR/HOCs/withLoading";
const PortalRenaming = (props) => {
const {
t,
@ -251,7 +252,7 @@ const PortalRenaming = (props) => {
const newUrl = combineUrl(
window.DocSpaceConfig?.proxy?.url,
config.homepage,
"/portal-settings/common/customization"
"/portal-settings/customization/general"
);
if (newUrl === currentUrl) return;

View File

@ -255,7 +255,7 @@ class WelcomePageSettings extends React.Component {
const newUrl = combineUrl(
window.DocSpaceConfig?.proxy?.url,
config.homepage,
"/portal-settings/common/customization"
"/portal-settings/customization/general"
);
if (newUrl === currentUrl) return;

View File

@ -9,7 +9,7 @@ import Tooltip from "@docspace/components/tooltip";
import Text from "@docspace/components/text";
import TabContainer from "@docspace/components/tabs-container";
import Preview from "./Appearance/preview";
import { saveToSessionStorage, getFromSessionStorage } from "../../utils";
import ColorSchemeDialog from "./sub-components/colorSchemeDialog";
import DropDownItem from "@docspace/components/drop-down-item";
@ -117,6 +117,7 @@ const Appearance = (props) => {
title: t("Profile:LightTheme"),
content: (
<Preview
appliedColorAccent={appliedColorAccent}
previewAccent={previewAccent}
selectThemeId={selectThemeId}
colorCheckImg={colorCheckImg}
@ -129,6 +130,7 @@ const Appearance = (props) => {
title: t("Profile:DarkTheme"),
content: (
<Preview
appliedColorAccent={appliedColorAccent}
previewAccent={previewAccent}
selectThemeId={selectThemeId}
colorCheckImg={colorCheckImg}
@ -140,6 +142,25 @@ const Appearance = (props) => {
[previewAccent, selectThemeId, colorCheckImg, tReady]
);
const getSettings = () => {
const selectColorId = getFromSessionStorage("selectColorId");
const defaultColorId = selectedThemeId;
saveToSessionStorage("defaultColorId", defaultColorId);
if (selectColorId) {
setSelectThemeId(selectColorId);
} else {
setSelectThemeId(defaultColorId);
}
};
useEffect(() => {
getSettings();
}, []);
useEffect(() => {
saveToSessionStorage("selectColorId", selectThemeId);
}, [selectThemeId]);
useEffect(() => {
onCheckView();
window.addEventListener("resize", onCheckView);
@ -151,7 +172,7 @@ const Appearance = (props) => {
useEffect(() => {
if (!currentColorScheme) return;
setAppliedColorButtons(defaultAppliedColorButtons);
setAppliedColorAccent(defaultAppliedColorAccent);
}, [
@ -286,6 +307,8 @@ const Appearance = (props) => {
setPreviewAccent(accent);
setSelectThemeId(id);
saveToSessionStorage("selectColorId", id);
saveToSessionStorage("selectColorAccent", accent);
},
[appearanceTheme, setPreviewAccent, setSelectThemeId]
);
@ -298,11 +321,14 @@ const Appearance = (props) => {
try {
await sendAppearanceTheme({ selected: selectThemeId });
await getAppearanceTheme();
toastr.success(t("Settings:SuccessfullySaveSettingsMessage"));
} catch (error) {
toastr.error(error);
}
saveToSessionStorage("selectColorId", selectThemeId);
saveToSessionStorage("defaultColorId", selectThemeId);
saveToSessionStorage("selectColorAccent", previewAccent);
saveToSessionStorage("defaultColorAccent", previewAccent);
}, [
selectThemeId,
setIsDisabledSaveButton,
@ -336,6 +362,9 @@ const Appearance = (props) => {
setPreviewAccent(appearanceTheme[0].main.accent);
}
saveToSessionStorage("selectColorId", appearanceTheme[0].id);
saveToSessionStorage("selectColorAccent", appearanceTheme[0].main.accent);
onCloseDialogDelete();
toastr.success(t("Settings:SuccessfullySaveSettingsMessage"));

View File

@ -15,6 +15,7 @@ import LoaderBrandingDescription from "./sub-components/loaderBrandingDescriptio
import BreakpointWarning from "../../../../components/BreakpointWarning/index";
import { UnavailableStyles } from "../../utils/commonSettingsStyles";
import { resetSessionStorage } from "../../utils";
const StyledComponent = styled.div`
max-width: 700px;
@ -56,6 +57,14 @@ const StyledComponent = styled.div`
const Branding = ({ t, isLoadedCompanyInfoSettingsData, isSettingPaid }) => {
const [viewDesktop, setViewDesktop] = useState(false);
useEffect(() => {
return () => {
if (!window.location.pathname.includes("customization")) {
resetSessionStorage();
}
};
}, []);
useEffect(() => {
onCheckView();
window.addEventListener("resize", onCheckView);

View File

@ -91,7 +91,7 @@ const CustomizationNavbar = ({
truncate={true}
href={combineUrl(
window.DocSpaceConfig?.proxy?.url,
"/portal-settings/common/customization/language-and-time-zone"
"/portal-settings/customization/general/language-and-time-zone"
)}
>
{t("StudioTimeLanguageSettings")}
@ -121,7 +121,7 @@ const CustomizationNavbar = ({
onClick={onClickLink}
href={combineUrl(
window.DocSpaceConfig?.proxy?.url,
"/portal-settings/common/customization/welcome-page-settings"
"/portal-settings/customization/general/welcome-page-settings"
)}
>
{t("CustomTitlesWelcome")}
@ -142,7 +142,7 @@ const CustomizationNavbar = ({
onClick={onClickLink}
href={combineUrl(
window.DocSpaceConfig?.proxy?.url,
"/portal-settings/common/customization/dns-settings"
"/portal-settings/customization/general/dns-settings"
)}
>
{t("DNSSettings")}
@ -180,7 +180,7 @@ const CustomizationNavbar = ({
onClick={onClickLink}
href={combineUrl(
window.DocSpaceConfig?.proxy?.url,
"/portal-settings/common/customization/portal-renaming"
"/portal-settings/customization/general/portal-renaming"
)}
>
{t("PortalRenaming")}

View File

@ -10,6 +10,7 @@ import Branding from "./branding";
import Appearance from "./appearance";
import withLoading from "SRC_DIR/HOCs/withLoading";
import LoaderSubmenu from "./sub-components/loaderSubmenu";
import { resetSessionStorage } from "../../utils";
const SubmenuCommon = (props) => {
const {
@ -19,9 +20,17 @@ const SubmenuCommon = (props) => {
setIsLoadedSubmenu,
loadBaseInfo,
isLoadedSubmenu,
getWhiteLabelLogoUrls,
} = props;
const [currentTab, setCurrentTab] = useState(0);
useEffect(() => {
return () => {
resetSessionStorage();
getWhiteLabelLogoUrls();
};
}, []);
useEffect(() => {
const path = location.pathname;
const currentTab = data.findIndex((item) => path.includes(item.id));
@ -43,7 +52,7 @@ const SubmenuCommon = (props) => {
const data = [
{
id: "customization",
id: "general",
name: t("Common:SettingsGeneral"),
content: <Customization />,
},
@ -64,7 +73,7 @@ const SubmenuCommon = (props) => {
combineUrl(
window.DocSpaceConfig?.proxy?.url,
config.homepage,
`/portal-settings/common/${e.id}`
`/portal-settings/customization/${e.id}`
)
);
};
@ -86,6 +95,7 @@ export default inject(({ common }) => {
setIsLoadedSubmenu,
initSettings,
isLoadedSubmenu,
getWhiteLabelLogoUrls,
} = common;
return {
loadBaseInfo: async () => {
@ -94,6 +104,7 @@ export default inject(({ common }) => {
isLoaded,
setIsLoadedSubmenu,
isLoadedSubmenu,
getWhiteLabelLogoUrls,
};
})(
withLoading(withRouter(withTranslation("Settings")(observer(SubmenuCommon))))

View File

@ -0,0 +1,105 @@
import HelpReactSvgUrl from "PUBLIC_DIR/images/help.react.svg?url";
import React, { useEffect } from "react";
import { withTranslation, Trans } from "react-i18next";
import Submenu from "@docspace/components/submenu";
import Link from "@docspace/components/link";
import HelpButton from "@docspace/components/help-button";
import { combineUrl } from "@docspace/common/utils";
import { inject, observer } from "mobx-react";
import AutoBackup from "./auto-backup";
import ManualBackup from "./manual-backup";
import config from "PACKAGE_FILE";
import { removeLocalStorage } from "../../../utils";
const Backup = ({
helpUrlCreatingBackup,
buttonSize,
t,
history,
isNotPaidPeriod,
currentColorScheme,
}) => {
const renderTooltip = (helpInfo) => {
return (
<>
<HelpButton
place="bottom"
iconName={HelpReactSvgUrl}
tooltipContent={
<>
<Trans t={t} i18nKey={`${helpInfo}`} ns="Settings">
{helpInfo}
</Trans>
<div>
<Link
as="a"
href={helpUrlCreatingBackup}
target="_blank"
color={currentColorScheme.main.accent}
isBold
isHovered
>
{t("Common:LearnMore")}
</Link>
</div>
</>
}
/>
</>
);
};
const data = [
{
id: "data-backup",
name: t("DataBackup"),
content: (
<ManualBackup buttonSize={buttonSize} renderTooltip={renderTooltip} />
),
},
{
id: "auto-backup",
name: t("AutoBackup"),
content: (
<AutoBackup buttonSize={buttonSize} renderTooltip={renderTooltip} />
),
},
];
const onSelect = (e) => {
history.push(
combineUrl(
window.DocSpaceConfig?.proxy?.url,
config.homepage,
`/portal-settings/backup/${e.id}`
)
);
};
return isNotPaidPeriod ? (
<ManualBackup buttonSize={buttonSize} renderTooltip={renderTooltip} />
) : (
<Submenu data={data} startSelect={data[0]} onSelect={(e) => onSelect(e)} />
);
};
export default inject(({ auth }) => {
const { settingsStore, currentTariffStatusStore } = auth;
const { isNotPaidPeriod } = currentTariffStatusStore;
const {
helpUrlCreatingBackup,
isTabletView,
currentColorScheme,
} = settingsStore;
const buttonSize = isTabletView ? "normal" : "small";
return {
helpUrlCreatingBackup,
buttonSize,
isNotPaidPeriod,
currentColorScheme,
toDefault,
};
})(observer(withTranslation(["Settings", "Common"])(Backup)));

View File

@ -102,6 +102,20 @@ class ManualBackup extends React.Component {
componentDidMount() {
const { fetchTreeFolders, rootFoldersTitles, isNotPaidPeriod } = this.props;
const valueFromLocalStorage = getFromLocalStorage("LocalCopyStorageType");
if (valueFromLocalStorage) {
let newStateObj = {};
const name = valueFromLocalStorage;
newStateObj[name] = true;
const newState = this.switches.filter((el) => el !== name);
newState.forEach((name) => (newStateObj[name] = false));
this.setState({
...newStateObj,
});
} else {
saveToLocalStorage("LocalCopyStorageType", "isCheckedTemporaryStorage");
}
if (isNotPaidPeriod) {
this.setState({
@ -136,7 +150,6 @@ class ManualBackup extends React.Component {
const { TemporaryModuleType } = BackupStorageType;
clearLocalStorage();
saveToLocalStorage("LocalCopyStorageType", "TemporaryStorage");
try {
await startBackup(`${TemporaryModuleType}`, null);
@ -162,6 +175,7 @@ class ManualBackup extends React.Component {
this.setState({
...newStateObj,
});
saveToLocalStorage("LocalCopyStorageType", name);
};
onMakeCopy = async (
selectedFolder,

View File

@ -10,7 +10,7 @@ import Link from "@docspace/components/link";
import HelpButton from "@docspace/components/help-button";
import { combineUrl } from "@docspace/common/utils";
import AppLoader from "@docspace/common/components/AppLoader";
import { removeLocalStorage } from "../../utils";
import config from "../../../../../package.json";
import ManualBackup from "./backup/manual-backup";
import AutoBackup from "./backup/auto-backup";
@ -23,10 +23,19 @@ const DataManagementWrapper = (props) => {
history,
isNotPaidPeriod,
currentColorScheme,
toDefault,
} = props;
const [currentTab, setCurrentTab] = useState(0);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
return () => {
removeLocalStorage("LocalCopyStorageType");
toDefault();
};
}, []);
const renderTooltip = (helpInfo) => {
return (
<>
@ -106,11 +115,11 @@ const DataManagementWrapper = (props) => {
);
};
export default inject(({ auth, setup }) => {
export default inject(({ auth, setup, backup }) => {
const { initSettings } = setup;
const { settingsStore, currentTariffStatusStore } = auth;
const { isNotPaidPeriod } = currentTariffStatusStore;
const { toDefault } = backup;
const {
helpUrlCreatingBackup,
isTabletView,
@ -126,6 +135,7 @@ export default inject(({ auth, setup }) => {
buttonSize,
isNotPaidPeriod,
currentColorScheme,
toDefault,
};
})(
withTranslation(["Settings", "Common"])(

View File

@ -24,10 +24,6 @@ const SingleSignOn = (props) => {
if (isMobile)
return <BreakpointWarning sectionName={t("Settings:SingleSignOn")} />;
useEffect(() => {
load();
}, []);
return (
<StyledSsoPage
hideSettings={serviceProviderSettings}

View File

@ -10,6 +10,7 @@ const StyledSsoPage = styled.div`
.intro-text {
margin-bottom: 18px;
max-width: 700px;
color: ${(props) => props.theme.client.settings.common.descriptionColor};
}
.toggle {

View File

@ -32,7 +32,6 @@ const ToggleSSO = (props) => {
<Text
className="intro-text settings_unavailable"
lineHeight="20px"
color="#657077"
noSelect
>
{t("SsoIntro")}

View File

@ -16,6 +16,10 @@ const RootContainer = styled(Box)`
max-width: 700px;
width: 100%;
.third-party-description {
color: ${(props) => props.theme.client.settings.common.descriptionColor};
}
@media ${mobile} {
width: calc(100% - 8px);
}
@ -129,7 +133,9 @@ class ThirdPartyServices extends React.Component {
return (
<>
<RootContainer className="RootContainer">
<Text>{t("ThirdPartyTitleDescription")}</Text>
<Text className="third-party-description">
{t("ThirdPartyTitleDescription")}
</Text>
<Box marginProp="8px 0 24px 0">
<Link
color={currentColorScheme.main.accent}

View File

@ -15,10 +15,16 @@ import AppLoader from "@docspace/common/components/AppLoader";
import SSOLoader from "./sub-components/ssoLoader";
const IntegrationWrapper = (props) => {
const { t, tReady, history, loadBaseInfo, enablePlugins } = props;
const { t, tReady, history, loadBaseInfo, enablePlugins, toDefault } = props;
const [currentTab, setCurrentTab] = useState(0);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
return () => {
toDefault();
};
}, []);
const pluginData = {
id: "plugins",
name: "Plugins",
@ -71,9 +77,9 @@ const IntegrationWrapper = (props) => {
return <Submenu data={data} startSelect={currentTab} onSelect={onSelect} />;
};
export default inject(({ setup, auth }) => {
export default inject(({ setup, auth, ssoStore }) => {
const { initSettings } = setup;
const { load: toDefault } = ssoStore;
const { enablePlugins } = auth.settingsStore;
return {
@ -81,6 +87,7 @@ export default inject(({ setup, auth }) => {
await initSettings();
},
enablePlugins,
toDefault,
};
})(
withTranslation(["Settings", "SingleSignOn", "Translations"])(

View File

@ -12,6 +12,7 @@ import LoginHistory from "./login-history/index.js";
import MobileSecurityLoader from "./sub-components/loaders/mobile-security-loader";
import AccessLoader from "./sub-components/loaders/access-loader";
import AuditTrail from "./audit-trail/index.js";
import { resetSessionStorage } from "../../utils";
import { isMobile } from "react-device-detect";
@ -43,6 +44,12 @@ const SecurityWrapper = (props) => {
setIsLoading(true);
};
useEffect(() => {
return () => {
resetSessionStorage();
};
}, []);
useEffect(() => {
const path = location.pathname;
const currentTab = data.findIndex((item) => path.includes(item.id));

View File

@ -72,7 +72,7 @@ const LoginHistory = (props) => {
securityLifetime={securityLifetime}
setLifetimeAuditSettings={setLifetimeAuditSettings}
content={getContent()}
downloadReport={t("DownloadReportBtn")}
downloadReport={t("DownloadReportBtnText")}
getReport={getLoginHistoryReport}
isSettingNotPaid={!isAuditAvailable}
/>

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react";
import Text from "@docspace/components/text";
import { saveToSessionStorage, getFromSessionStorage } from "../../../utils";
import TextInput from "@docspace/components/text-input";
import SaveCancelButtons from "@docspace/components/save-cancel-buttons";
import styled from "styled-components";
@ -34,7 +34,6 @@ const MainContainer = styled.div`
.save-cancel {
padding: 0;
position: static;
display: block;
}
.login-subheader {
@ -128,14 +127,59 @@ const HistoryMainContent = (props) => {
isLoadingDownloadReport,
} = props;
const [lifeTime, setLifeTime] = useState(String(lifetime) || "180");
const [showReminder, setShowReminder] = useState(false);
const [loginLifeTime, setLoginLifeTime] = useState(String(lifetime) || "180");
const [auditLifeTime, setAuditLifeTime] = useState(String(lifetime) || "180");
const [loginLifeTimeReminder, setLoginLifeTimeReminder] = useState(false);
const [auditLifeTimeReminder, setAuditLifeTimeReminder] = useState(false);
const lifeTimeHandler = (e) => {
const reg = new RegExp(/^(\d){1,3}$/g);
const condition = e.target.value === "";
if ((e.target.value.match(reg) && e.target.value <= 180) || condition) {
setLifeTime(e.target.value);
const isLoginHistoryPage = window.location.pathname.includes("login-history");
useEffect(() => {
getSettings();
}, []);
useEffect(() => {
const newSettings = {
loginHistoryLifeTime: loginLifeTime,
auditTrailLifeTime: auditLifeTime,
};
saveToSessionStorage("storagePeriod", newSettings);
if (loginLifeTime === String(lifetime)) {
setLoginLifeTimeReminder(false);
} else {
setLoginLifeTimeReminder(true);
}
}, [loginLifeTime]);
useEffect(() => {
const newSettings = {
loginHistoryLifeTime: loginLifeTime,
auditTrailLifeTime: auditLifeTime,
};
saveToSessionStorage("storagePeriod", newSettings);
if (auditLifeTime === String(lifetime)) {
setAuditLifeTimeReminder(false);
} else {
setAuditLifeTimeReminder(true);
}
}, [auditLifeTime]);
const getSettings = () => {
const storagePeriodSettings = getFromSessionStorage("storagePeriod");
const defaultData = {
loginHistoryLifeTime: String(lifetime),
auditTrailLifeTime: String(lifetime),
};
saveToSessionStorage("defaultStoragePeriod", defaultData);
if (storagePeriodSettings) {
setLoginLifeTime(storagePeriodSettings.loginHistoryLifeTime);
setAuditLifeTime(storagePeriodSettings.auditTrailLifeTime);
} else {
setLoginLifeTime(String(lifetime));
setAuditLifeTime(String(lifetime));
}
};
@ -143,13 +187,17 @@ const HistoryMainContent = (props) => {
if (loginHistory) {
const data = {
settings: {
loginHistoryLifeTime: lifeTime,
loginHistoryLifeTime: loginLifeTime,
auditTrailLifeTime: securityLifetime.auditTrailLifeTime,
},
};
try {
await setLifetimeAuditSettings(data);
setShowReminder(false);
saveToSessionStorage("defaultStoragePeriod", {
loginHistoryLifeTime: loginLifeTime,
auditTrailLifeTime: securityLifetime.auditTrailLifeTime,
});
setLoginLifeTimeReminder(false);
toastr.success(t("SuccessfullySaveSettingsMessage"));
} catch (error) {
console.error(error);
@ -159,13 +207,17 @@ const HistoryMainContent = (props) => {
const data = {
settings: {
loginHistoryLifeTime: securityLifetime.loginHistoryLifeTime,
auditTrailLifeTime: lifeTime,
auditTrailLifeTime: auditLifeTime,
},
};
try {
await setLifetimeAuditSettings(data);
setShowReminder(false);
saveToSessionStorage("defaultStoragePeriod", {
loginHistoryLifeTime: securityLifetime.loginHistoryLifeTime,
auditTrailLifeTime: auditLifeTime,
});
setAuditLifeTimeReminder(false);
toastr.success(t("SuccessfullySaveSettingsMessage"));
} catch (error) {
console.error(error);
@ -174,13 +226,31 @@ const HistoryMainContent = (props) => {
}
};
useEffect(() => {
if (lifeTime === String(lifetime)) {
setShowReminder(false);
} else {
setShowReminder(true);
const onChangeLoginLifeTime = (e) => {
const reg = new RegExp(/^(\d){1,3}$/g);
const condition = e.target.value === "";
if ((e.target.value.match(reg) && e.target.value <= 180) || condition) {
setLoginLifeTime(e.target.value);
}
}, [lifeTime]);
};
const onChangeAuditLifeTime = (e) => {
const reg = new RegExp(/^(\d){1,3}$/g);
const condition = e.target.value === "";
if ((e.target.value.match(reg) && e.target.value <= 180) || condition) {
setAuditLifeTime(e.target.value);
}
};
const onCancelLoginLifeTime = () => {
const defaultSettings = getFromSessionStorage("defaultStoragePeriod");
setLoginLifeTime(String(defaultSettings.loginHistoryLifeTime));
};
const onCancelAuditLifeTime = () => {
const defaultSettings = getFromSessionStorage("defaultStoragePeriod");
setAuditLifeTime(String(defaultSettings.auditTrailLifeTime));
};
return (
<MainContainer isSettingNotPaid={isSettingNotPaid}>
@ -199,25 +269,53 @@ const HistoryMainContent = (props) => {
>
{storagePeriod}
</label>
<StyledTextInput
onChange={lifeTimeHandler}
value={lifeTime}
size="base"
id="storage-period"
type="text"
isDisabled={isSettingNotPaid}
/>
<SaveCancelButtons
className="save-cancel"
onSaveClick={setLifeTimeSettings}
onCancelClick={() => setLifeTime(String(lifetime))}
saveButtonLabel={saveButtonLabel}
cancelButtonLabel={cancelButtonLabel}
showReminder={showReminder}
displaySettings={true}
hasScroll={false}
isDisabled={isSettingNotPaid}
/>
{isLoginHistoryPage ? (
<>
<StyledTextInput
onChange={onChangeLoginLifeTime}
value={loginLifeTime}
size="base"
id="login-history-period"
type="text"
isDisabled={isSettingNotPaid}
/>
<SaveCancelButtons
className="save-cancel"
onSaveClick={setLifeTimeSettings}
onCancelClick={onCancelLoginLifeTime}
saveButtonLabel={saveButtonLabel}
cancelButtonLabel={cancelButtonLabel}
showReminder={loginLifeTimeReminder}
reminderTest={t("YouHaveUnsavedChanges")}
displaySettings={true}
hasScroll={false}
isDisabled={isSettingNotPaid}
/>
</>
) : (
<>
<StyledTextInput
onChange={onChangeAuditLifeTime}
value={auditLifeTime}
size="base"
id="audit-history-period"
type="text"
isDisabled={isSettingNotPaid}
/>
<SaveCancelButtons
className="save-cancel"
onSaveClick={setLifeTimeSettings}
onCancelClick={onCancelAuditLifeTime}
saveButtonLabel={saveButtonLabel}
cancelButtonLabel={cancelButtonLabel}
showReminder={auditLifeTimeReminder}
reminderTest={t("YouHaveUnsavedChanges")}
displaySettings={true}
hasScroll={false}
isDisabled={isSettingNotPaid}
/>
</>
)}
<Text className="latest-text settings_unavailable">{downloadText}</Text>
</div>
{content}

View File

@ -24,15 +24,12 @@ const SessionLifetimePage = lazy(() =>
import("./categories/security/access-portal/sessionLifetime")
);
const CommonSettings = lazy(() => import("./categories/common/index.js"));
const CustomizationSettings = lazy(() => import("./categories/common/index.js"));
const DeveloperTools = lazy(() =>
import("./categories/developer-tools/index.js")
);
const CustomizationSettings = lazy(() =>
import("./categories/common/customization")
);
const LanguageAndTimeZoneSettings = lazy(() =>
import("./categories/common/Customization/language-and-time-zone")
);
@ -74,12 +71,12 @@ const PROXY_BASE_URL = combineUrl(
"/portal-settings"
);
const COMMON_URLS = [
const CUSTOMIZATION_URLS = [
PROXY_BASE_URL,
combineUrl(PROXY_BASE_URL, "/common"),
combineUrl(PROXY_BASE_URL, "/common/customization"),
combineUrl(PROXY_BASE_URL, "/common/branding"),
combineUrl(PROXY_BASE_URL, "/common/appearance"),
combineUrl(PROXY_BASE_URL, "/customization"),
combineUrl(PROXY_BASE_URL, "/customization/general"),
combineUrl(PROXY_BASE_URL, "/customization/branding"),
combineUrl(PROXY_BASE_URL, "/customization/appearance"),
];
const DEVELOPER_URLS = [
@ -88,12 +85,6 @@ const DEVELOPER_URLS = [
combineUrl(PROXY_BASE_URL, "/developer/tools"),
];
const CUSTOMIZATION_URLS = [
combineUrl(PROXY_BASE_URL, "/common/customization"),
combineUrl(PROXY_BASE_URL, "/common"),
PROXY_BASE_URL,
];
const BACKUP_URLS = [
PROXY_BASE_URL,
combineUrl(PROXY_BASE_URL, "/backup"),
@ -105,25 +96,25 @@ const RESTORE_DATA_URL = combineUrl(PROXY_BASE_URL, "/restore");
const LTZ_URL = combineUrl(
PROXY_BASE_URL,
"/common/customization/language-and-time-zone"
"/customization/general/language-and-time-zone"
);
const WELCOME_PAGE_SETTINGS_URL = combineUrl(
PROXY_BASE_URL,
"/common/customization/welcome-page-settings"
"/customization/general/welcome-page-settings"
);
const DNS_SETTINGS = combineUrl(
PROXY_BASE_URL,
"/common/customization/dns-settings"
"/customization/general/dns-settings"
);
const PORTAL_RENAMING = combineUrl(
PROXY_BASE_URL,
"/common/customization/portal-renaming"
"/customization/general/portal-renaming"
);
const TEAM_TEMPLATE_URL = combineUrl(
PROXY_BASE_URL,
"/common/customization/team-template"
"/customization/general/team-template"
);
const WHITELABEL_URL = combineUrl(PROXY_BASE_URL, "/common/whitelabel");
const SECURITY_URLS = [
@ -188,64 +179,28 @@ const Settings = () => {
<Panels />
<Suspense fallback={null}>
<Switch>
<Route exact path={COMMON_URLS} component={CommonSettings} />
{/* <Route
exact
path={CUSTOMIZATION_URLS}
component={CustomizationSettings}
/> */}
<Route exact path={CUSTOMIZATION_URLS} component={CustomizationSettings} />
<Route exact path={LTZ_URL} component={LanguageAndTimeZoneSettings} />
<Route
exact
path={WELCOME_PAGE_SETTINGS_URL}
component={WelcomePageSettings}
/>
<Route exact path={WELCOME_PAGE_SETTINGS_URL} component={WelcomePageSettings} />
<Route exact path={DNS_SETTINGS} component={DNSSettings} />
<Route exact path={PORTAL_RENAMING} component={PortalRenaming} />
<Route exact path={WHITELABEL_URL} component={WhiteLabel} />
<Route exact path={SECURITY_URLS} component={SecuritySettings} />
<Route exact path={TFA_PAGE_URL} component={TfaPage} />
<Route
exact
path={PASSWORD_PAGE_URL}
component={PasswordStrengthPage}
/>
<Route
exact
path={TRUSTED_MAIL_PAGE_URL}
component={TrustedMailPage}
/>
<Route exact path={PASSWORD_PAGE_URL} component={PasswordStrengthPage} />
<Route exact path={TRUSTED_MAIL_PAGE_URL} component={TrustedMailPage} />
<Route exact path={IP_SECURITY_PAGE_URL} component={IpSecurityPage} />
<Route
exact
path={ADMIN_MESSAGE_PAGE_URL}
component={AdminMessagePage}
/>
<Route
exact
path={SESSION_LIFETIME_PAGE_URL}
component={SessionLifetimePage}
/>
<Route exact path={ADMIN_MESSAGE_PAGE_URL} component={AdminMessagePage} />
<Route exact path={SESSION_LIFETIME_PAGE_URL} component={SessionLifetimePage} />
<Route exact path={INTEGRATION_URLS} component={Integration} />
<Route exact path={PAYMENTS_URL} component={Payments} />
<Route exact path={THIRD_PARTY_URL} component={ThirdParty} />
<Route exact path={SSO_URL} component={SingleSignOn} />
<Route exact path={DEVELOPER_URLS} component={DeveloperTools} />
<Route exact path={BACKUP_URLS} component={Backup} />
<Route path={RESTORE_DATA_URL} component={RestoreBackup} />
<Route exact path={DELETE_DATA_URLS} component={DeleteDataPage} />
<Redirect
to={{
pathname: ERROR_404_URL,
}}
/>
<Route path={RESTORE_DATA_URL} component={RestoreBackup} />
<Redirect to={{pathname: ERROR_404_URL}} />
</Switch>
</Suspense>
</Layout>

View File

@ -11,3 +11,4 @@ export { getSettingsIndex } from "./getSettingsIndex";
export { getFromLocalStorage } from "./getFromLocalStorage.js";
export { saveToLocalStorage } from "./saveToLocalStorage.js";
export { removeLocalStorage } from "./removeLocalStorage.js";
export { resetSessionStorage } from "./resetSessionStorage";

View File

@ -0,0 +1,88 @@
import { saveToSessionStorage, getFromSessionStorage } from "../utils";
export const resetSessionStorage = () => {
const portalNameFromSessionStorage = getFromSessionStorage("portalName");
const portalNameDefaultFromSessionStorage = getFromSessionStorage("portalNameDefault");
const greetingTitleFromSessionStorage = getFromSessionStorage("greetingTitle");
const greetingTitleDefaultFromSessionStorage = getFromSessionStorage("greetingTitleDefault");
const languageFromSessionStorage = getFromSessionStorage("language");
const languageDefaultFromSessionStorage = getFromSessionStorage("languageDefault");
const timezoneFromSessionStorage = getFromSessionStorage("timezone");
const timezoneDefaultFromSessionStorage = getFromSessionStorage("timezoneDefault");
const selectColorId = getFromSessionStorage("selectColorId");
const defaultColorId = getFromSessionStorage("defaultColorId");
const selectColorAccent = getFromSessionStorage("selectColorAccent");
const defaultColorAccent = getFromSessionStorage("defaultColorAccent");
const currentPasswordSettings = getFromSessionStorage("currentPasswordSettings");
const defaultPasswordSettings = getFromSessionStorage("defaultPasswordSettings");
const currentTfaSettings = getFromSessionStorage("currentTfaSettings");
const defaultTfaSettings = getFromSessionStorage("defaultTfaSettings");
const currentTrustedMailSettings = getFromSessionStorage("currentTrustedMailSettings");
const defaultTrustedMailSettings = getFromSessionStorage("defaultTrustedMailSettings");
const currentIPSettings = getFromSessionStorage("currentIPSettings");
const defaultIPSettings = getFromSessionStorage("defaultIPSettings");
const currentAdminMessageSettings = getFromSessionStorage("currentAdminMessageSettings");
const defaultAdminMessageSettings = getFromSessionStorage("defaultAdminMessageSettings");
const currentSessionLifetimeSettings = getFromSessionStorage("currentSessionLifetimeSettings");
const defaultSessionLifetimeSettings = getFromSessionStorage("defaultSessionLifetimeSettings");
const storagePeriodSettings = getFromSessionStorage("storagePeriod");
const defaultStoragePeriodSettings = getFromSessionStorage("defaultStoragePeriod");
const companyNameFromeSessionStorage = getFromSessionStorage("companyName");
const companySettingsFromSessionStorage = getFromSessionStorage("companySettings");
const defaultCompanySettingsFromSessionStorage = getFromSessionStorage("defaultCompanySettings");
const additionalSettings = getFromSessionStorage("additionalSettings");
const defaultAdditionalSettings = getFromSessionStorage("defaultAdditionalSettings");
if (portalNameFromSessionStorage !== portalNameDefaultFromSessionStorage) {
saveToSessionStorage("portalName", "none");
saveToSessionStorage("errorValue", null);
}
if (greetingTitleFromSessionStorage !== greetingTitleDefaultFromSessionStorage) {
saveToSessionStorage("greetingTitle", "none");
}
if (languageFromSessionStorage !== languageDefaultFromSessionStorage) {
saveToSessionStorage("language", languageDefaultFromSessionStorage);
}
if (timezoneFromSessionStorage !== timezoneDefaultFromSessionStorage) {
saveToSessionStorage("timezone", timezoneDefaultFromSessionStorage);
}
if (currentPasswordSettings !== defaultPasswordSettings) {
saveToSessionStorage("currentPasswordSettings", defaultPasswordSettings);
}
if (currentTfaSettings !== defaultTfaSettings) {
saveToSessionStorage("currentTfaSettings", "none");
}
if (currentTrustedMailSettings !== defaultTrustedMailSettings) {
saveToSessionStorage("currentTrustedMailSettings", defaultTrustedMailSettings);
}
if (currentIPSettings !== defaultIPSettings) {
saveToSessionStorage("currentIPSettings", defaultIPSettings);
}
if (currentAdminMessageSettings !== defaultAdminMessageSettings) {
saveToSessionStorage("currentAdminMessageSettings", defaultAdminMessageSettings);
}
if (currentSessionLifetimeSettings !== defaultSessionLifetimeSettings) {
saveToSessionStorage("currentSessionLifetimeSettings", defaultSessionLifetimeSettings);
}
if (storagePeriodSettings !== defaultStoragePeriodSettings) {
saveToSessionStorage("storagePeriod", defaultStoragePeriodSettings);
}
if (companyNameFromeSessionStorage !== "ONLYOFFICE") {
saveToSessionStorage("companyName", "ONLYOFFICE");
}
if (companySettingsFromSessionStorage !== defaultCompanySettingsFromSessionStorage) {
saveToSessionStorage("companySettings", defaultCompanySettingsFromSessionStorage);
}
if (additionalSettings !== defaultAdditionalSettings) {
saveToSessionStorage("additionalSettings", defaultAdditionalSettings);
}
if (selectColorId !== defaultColorId) {
saveToSessionStorage("selectColorId", defaultColorId);
}
if (selectColorAccent !== defaultColorAccent) {
saveToSessionStorage("selectColorAccent", defaultColorAccent);
}
};

View File

@ -14,18 +14,18 @@ import DeveloperReactSvgUrl from "PUBLIC_DIR/images/catalog.developer.react.svg?
export const settingsTree = [
{
id: "portal-settings_catalog-common",
id: "portal-settings_catalog-customization",
key: "0",
icon: CommonSettingsSvgUrl,
link: "common",
link: "customization",
tKey: "Customization",
isHeader: true,
children: [
{
id: "portal-settings_catalog-customization",
id: "portal-settings_catalog-general",
key: "0-0",
icon: "",
link: "customization",
link: "general",
tKey: "SettingsGeneral",
isCategory: true,
children: [

View File

@ -90,13 +90,39 @@ const MainProfile = (props) => {
<Text as="div" className="label">
{t("Common:Email")}
</Text>
<Text
as="div"
className={"email-text-container"}
fontWeight={600}
>
{profile.email}
</Text>
<div className="email-container">
<div className="email-edit-container">
<Text
as="div"
className={"email-text-container"}
fontWeight={600}
>
{profile.email}
</Text>
<IconButton
className="edit-button email-edit-button"
iconName={PencilOutlineReactSvgUrl}
size="12"
onClick={() => setChangeEmailVisible(true)}
/>
</div>
{withActivationBar && (
<div
className="send-again-container send-again-desktop"
onClick={sendActivationLinkAction}
>
<ReactSVG
className="send-again-icon"
src={SendClockReactSvgUrl}
/>
<Text className="send-again-text" fontWeight={600} noSelect>
{t("SendAgain")}
</Text>
</div>
)}
</div>
{withActivationBar && (
<div
className="send-again-container send-again-mobile"
@ -113,25 +139,11 @@ const MainProfile = (props) => {
)}
</div>
<IconButton
className="edit-button"
className="edit-button email-edit-button-mobile"
iconName={PencilOutlineReactSvgUrl}
size="12"
onClick={() => setChangeEmailVisible(true)}
/>
{withActivationBar && (
<div
className="send-again-container send-again-desktop"
onClick={sendActivationLinkAction}
>
<ReactSVG
className="send-again-icon"
src={SendClockReactSvgUrl}
/>
<Text className="send-again-text" fontWeight={600} noSelect>
{t("SendAgain")}
</Text>
</div>
)}
</div>
<div className="row">
<div className="field">

View File

@ -89,6 +89,7 @@ const LanguagesCombo = (props) => {
/>
</Text>
<ComboBox
className="language-combo-box"
directionY="both"
options={cultureNames}
selectedOption={selectedLanguage}

View File

@ -3,6 +3,7 @@ import {
hugeMobile,
smallTablet,
desktop,
tablet,
} from "@docspace/components/utils/device";
export const StyledWrapper = styled.div`
@ -42,7 +43,13 @@ export const StyledInfo = styled.div`
display: flex;
flex-direction: column;
gap: 12px;
gap: 11px;
@media ${tablet} {
gap: 7px;
}
padding-top: 5px;
@media ${smallTablet} {
width: 100%;
@ -70,19 +77,23 @@ export const StyledInfo = styled.div`
.row {
display: flex;
align-items: center;
align-items: baseline;
gap: 8px;
line-height: 20px;
max-width: 100%;
@media ${desktop} {
height: 20px;
}
@media ${smallTablet} {
align-items: center;
}
.field {
display: flex;
gap: 16px;
align-items: baseline;
max-width: calc(100% - 28px);
& > p {
@ -91,8 +102,6 @@ export const StyledInfo = styled.div`
}
.email-text-container {
padding-left: 8px;
${(props) =>
props.withActivationBar &&
css`
@ -161,6 +170,31 @@ export const StyledInfo = styled.div`
.edit-button {
min-width: 12px;
svg path {
fill: ${(props) => props.theme.isBase && `#657077`};
}
}
.email-edit-button {
display: block;
padding-left: 8px;
}
.email-edit-container {
display: flex;
padding-right: 16px;
}
.email-container {
padding-left: 8px;
display: flex;
flex-wrap: wrap;
align-items: baseline;
}
.email-edit-button-mobile {
display: none;
}
@media ${smallTablet} {
@ -173,7 +207,7 @@ export const StyledInfo = styled.div`
flex-direction: column;
gap: 2px;
.email-text-container {
.email-container {
padding-left: 0px;
}
@ -191,6 +225,14 @@ export const StyledInfo = styled.div`
line-height: 16px !important;
}
.email-edit-button-mobile {
display: block;
}
.email-edit-button {
display: none;
}
.edit-button {
margin-left: auto;
min-width: 12px;
@ -227,6 +269,10 @@ export const StyledRow = styled.div`
white-space: nowrap;
}
.language-combo-box {
margin-left: -8px;
}
@media ${smallTablet} {
width: 100%;
flex-direction: column;

View File

@ -42,6 +42,7 @@ import { getCategoryType } from "SRC_DIR/helpers/utils";
import { CategoryType } from "SRC_DIR/helpers/constants";
import RoomsFilter from "@docspace/common/api/rooms/filter";
import { RoomSearchArea } from "@docspace/common/constants";
import { getObjectByLocation } from "@docspace/common/utils";
class FilesActionStore {
authStore;
@ -1905,12 +1906,12 @@ class FilesActionStore {
const categoryType = getCategoryType(location);
const isRoom = !!roomType;
setSelectedFolder(null);
if (
categoryType === CategoryType.SharedRoom ||
(categoryType === CategoryType.Archive && parentId !== 0)
) {
const urlFilter = getObjectByLocation(location);
const isArchivedRoom = !!(CategoryType.Archive && urlFilter?.folder);
if (categoryType === CategoryType.SharedRoom || isArchivedRoom) {
if (isRoom) {
return this.moveToRoomsPage();
}
@ -1920,7 +1921,7 @@ class FilesActionStore {
if (
categoryType === CategoryType.Shared ||
(categoryType === CategoryType.Archive && parentId === 0)
categoryType === CategoryType.Archive
) {
return this.moveToRoomsPage();
}
@ -1933,10 +1934,12 @@ class FilesActionStore {
}
if (categoryType === CategoryType.Settings) {
setSelectedFolder(null);
setSelectedNode(["common"]);
}
if (categoryType === CategoryType.Accounts) {
setSelectedFolder(null);
setSelectedNode(["accounts", "filter"]);
}
};
@ -1970,8 +1973,8 @@ class FilesActionStore {
let id = this.selectedFolderStore.parentId;
if (!id) {
const filterObj = FilesFilter.getDefault();
id = filterObj.folder;
const urlFilter = getObjectByLocation(location);
id = urlFilter.folder;
}
setIsLoading(true);

View File

@ -375,6 +375,7 @@ class SsoFormStore {
idpSettings,
idpCertificates,
idpCertificateAdvanced,
uploadXmlUrl,
spLoginLabel,
spCertificates,
spCertificateAdvanced,
@ -425,7 +426,10 @@ class SsoFormStore {
this.idpDecryptAlgorithm = decryptAlgorithm;
this.ipdDecryptAssertions = decryptAssertions;
this.spLoginLabel = spLoginLabel;
this.spLoginLabel = spLoginLabel || "";
this.uploadXmlUrl = uploadXmlUrl || "";
this.serviceProviderSettings = false;
//spCertificates
this.spCertificates = [...spCertificates];

View File

@ -580,7 +580,16 @@ class UploadDataStore {
len = filesToConversion.length;
}
if (this.uploaded) {
const allFilesIsUploaded =
this.files.findIndex(
(f) =>
f.action !== "uploaded" &&
f.action !== "convert" &&
f.action !== "converted" &&
!f.error
) === -1;
if (this.uploaded || (this.isParallel && allFilesIsUploaded)) {
this.setConversionPercent(100);
this.finishUploadFiles();
} else {
@ -599,15 +608,19 @@ class UploadDataStore {
...this.tempConversionFiles,
];
if (this.uploaded) {
const newUploadData = {
if (this.uploaded || this.isParallel) {
let newUploadData = {
files: this.files,
filesSize: this.convertFilesSize,
uploadedFiles: this.uploadedFiles,
percent: this.percent,
uploaded: false,
converted: false,
// converted: false,
};
if (!this.isParallel)
newUploadData = { ...newUploadData, converted: false };
this.tempConversionFiles = [];
this.setUploadData(newUploadData);
@ -688,7 +701,9 @@ class UploadDataStore {
convertSize += file.size;
}
const countUploadingFiles = newFiles.length;
const countUploadingFiles = this.isParallel
? this.removeDuplicate([...this.files, ...newFiles]).length
: newFiles.length;
const countConversionFiles = this.tempConversionFiles.length;
if (countUploadingFiles && !countConversionFiles) {
@ -712,14 +727,21 @@ class UploadDataStore {
this.uploadedFilesHistory = clearArray;
const newUploadData = {
files: newFiles,
let newUploadData = {
files: this.isParallel
? this.removeDuplicate([...this.files, ...newFiles])
: newFiles,
filesSize,
uploadedFiles: this.uploadedFiles,
percent: this.percent,
uploaded: false,
converted: !!this.tempConversionFiles.length,
// converted: !!this.tempConversionFiles.length,
};
if (!this.isParallel)
newUploadData = {
...newUploadData,
converted: !!this.tempConversionFiles.length,
};
const isParallel = this.isParallel ? true : this.uploaded;
@ -904,7 +926,8 @@ class UploadDataStore {
this.files[indexOfFile].action = "uploaded";
this.files[indexOfFile].fileId = fileId;
this.files[indexOfFile].fileInfo = fileInfo;
this.percent = newPercent;
if (!this.isParallel) this.percent = newPercent;
if (this.isParallel) {
this.currentUploadNumber -= 1;
@ -1164,7 +1187,11 @@ class UploadDataStore {
const allFilesIsUploaded =
this.files.findIndex(
(f) => f.action !== "uploaded" && f.action !== "convert" && !f.error
(f) =>
f.action !== "uploaded" &&
f.action !== "convert" &&
f.action !== "converted" &&
!f.error
) === -1;
if (allFilesIsUploaded) {

View File

@ -62,6 +62,19 @@ class UserStore {
this.setIsLoading(false);
};
updateEmailActivationStatus = async (activationStatus, userId, key) => {
this.setIsLoading(true);
const user = await api.people.updateActivationStatus(
activationStatus,
userId,
key
);
this.setUser(user);
this.setIsLoading(false);
};
changeTheme = async (key) => {
this.setIsLoading(true);

View File

@ -1,4 +1,3 @@
import history from "../history";
export default function (page) {
if (
window.location.pathname === page ||
@ -6,9 +5,8 @@ export default function (page) {
) {
return false;
}
//TODO: check if we already on default page
window.location.replace(page);
history.push(page); // SSR crash
return true;
}

View File

@ -102,7 +102,7 @@ const LoginForm: React.FC<ILoginFormProps> = ({
focusInput();
window.authCallback = authCallback;
}, []);
}, [message, confirmedEmail]);
const onChangeLogin = (e: React.ChangeEvent<HTMLInputElement>) => {
//console.log("onChangeLogin", e.target.value);

View File

@ -131,7 +131,7 @@
"Megabyte": "Мб",
"Member": "Участник",
"Members": "Участники",
"Name": "Название",
"Name": "Имя",
"NewDocument": "Новый документ",
"NewFolder": "Новая папка",
"NewMasterForm": "Новый шаблон формы",