Merge pull request #436 from ONLYOFFICE/bugfix/data-import

Bugfix/data import
This commit is contained in:
Alexey Safronov 2024-06-06 11:40:01 +04:00 committed by GitHub
commit af59952251
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 363 additions and 157 deletions

View File

@ -94,6 +94,8 @@ const HeaderContainer = styled.div`
}
.arrow-button {
flex-shrink: 0;
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`

View File

@ -107,7 +107,7 @@ const ImportCompleteStep = ({
setIsSaving(true);
setTimeout(() => {
setIsSaving(false);
navigate(-1);
navigate("/portal-settings/data-import/migration");
}, 1000);
};

View File

@ -89,6 +89,8 @@ const ErrorBlock = styled.div`
}
`;
const FAILS_TRIES = 1;
const SelectFileStep = ({
t,
onNextStep,
@ -117,9 +119,10 @@ const SelectFileStep = ({
const uploadInterval = useRef(null);
const navigate = useNavigate();
const [failTries, setFailsTries] = useState(FAILS_TRIES);
const goBack = () => {
cancelMigration();
setTimeout(() => navigate(-1), 100);
navigate("/portal-settings/data-import/migration");
};
const checkMigrationStatusAndUpdate = async () => {
@ -187,7 +190,6 @@ const SelectFileStep = ({
};
const onUploadFile = async (file) => {
setProgress(0);
setIsVisible(true);
try {
if (file.length) {
@ -195,23 +197,18 @@ const SelectFileStep = ({
} else {
await singleFileUploading(file, setProgress, isAbort);
}
if (isAbort.current) return;
await initMigrationName(searchParams.get("service"));
uploadInterval.current = setInterval(async () => {
try {
const res = await getMigrationStatus();
setProgress(res?.progress);
if (res.progress > 10) {
setIsVisible(false);
} else {
setIsVisible(true);
}
if (res.error || res.parseResult.failedArchives.length > 0) {
setShowErrorText(true);
} else {
setShowErrorText(false);
if (!res && failTries) {
setFailsTries((tries) => tries - 1);
return;
}
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
@ -219,12 +216,14 @@ const SelectFileStep = ({
setIsFileError(true);
setIsFileLoading(false);
clearInterval(uploadInterval.current);
} else if (res.isCompleted || res.parseResult.progress === 100) {
setShowErrorText(true);
return;
}
if (res.isCompleted || res.parseResult.progress === 100) {
clearInterval(uploadInterval.current);
setIsFileLoading(false);
setIsVisible(false);
setProgress(100);
if (
res.parseResult.users.length +
res.parseResult.existUsers.length +
@ -239,12 +238,23 @@ const SelectFileStep = ({
cancelMigration();
}
}
setProgress(res?.progress);
if (res.progress > 10) {
setIsVisible(false);
} else {
setIsVisible(true);
}
setShowErrorText(false);
} catch (error) {
toastr.error(error || t("Common:SomethingWentWrong"));
setIsFileError(true);
setIsFileLoading(false);
setIsError(true);
clearInterval(uploadInterval.current);
} finally {
isAbort.current = false;
}
}, 1000);
} catch (error) {
@ -259,6 +269,7 @@ const SelectFileStep = ({
setIsFileError(false);
setShowReminder(false);
setIsFileLoading(true);
setFailsTries(FAILS_TRIES);
try {
onUploadFile(file);
} catch (error) {
@ -286,7 +297,6 @@ const SelectFileStep = ({
});
} catch (error) {
toastr.error(error);
console.log(error);
}
};

View File

@ -32,9 +32,9 @@ import { SearchInput } from "@docspace/shared/components/search-input";
import AccountsTable from "./AccountsTable";
import AccountsPaging from "../../../sub-components/AccountsPaging";
// import UsersInfoBlock from "./../../../sub-components/UsersInfoBlock";
import UsersInfoBlock from "./../../../sub-components/UsersInfoBlock";
// const LICENSE_LIMIT = 100;
import { parseQuota } from "../../../utils";
const SelectUsersStep = ({
t,
@ -46,11 +46,16 @@ const SelectUsersStep = ({
setResultUsers,
areCheckedUsersEmpty,
cancelMigration,
checkedUsers,
quotaCharacteristics,
}) => {
const [dataPortion, setDataPortion] = useState(withEmailUsers.slice(0, 25));
const [quota, setQuota] = useState({ used: 0, max: 0 });
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
setSearchValue("");
setQuota(parseQuota(quotaCharacteristics[1]));
}, []);
const handleDataChange = (leftBoundary, rightBoundary) => {
@ -79,9 +84,17 @@ const SelectUsersStep = ({
const goBack = () => {
cancelMigration();
setTimeout(onPrevStep, 100);
setIsSaving(true);
setTimeout(() => {
setIsSaving(false);
onPrevStep();
}, 1000);
};
const totalUsedUsers =
quota.used +
checkedUsers.withEmail.filter((user) => !user.isDuplicate).length;
return (
<>
<SaveCancelButtons
@ -92,15 +105,21 @@ const SelectUsersStep = ({
saveButtonLabel={t("Settings:NextStep")}
cancelButtonLabel={t("Common:Back")}
displaySettings={true}
saveButtonDisabled={areCheckedUsersEmpty}
saveButtonDisabled={
areCheckedUsersEmpty || (quota.max && totalUsedUsers > quota.max)
}
isSaving={isSaving}
/>
{/* <UsersInfoBlock
t={t}
selectedUsers={100}
totalUsers={withEmailUsers.length}
totalLicenceLimit={LICENSE_LIMIT}
/> */}
{quota.max && (
<UsersInfoBlock
t={t}
totalUsedUsers={totalUsedUsers}
selectedUsers={checkedUsers.withEmail.length}
totalUsers={withEmailUsers.length}
totalLicenceLimit={quota.max}
/>
)}
<SearchInput
id="search-withEmailUsers-input"
@ -131,14 +150,17 @@ const SelectUsersStep = ({
saveButtonLabel={t("Settings:NextStep")}
cancelButtonLabel={t("Common:Back")}
displaySettings={true}
saveButtonDisabled={areCheckedUsersEmpty}
saveButtonDisabled={
areCheckedUsersEmpty || (quota.max && totalUsedUsers > quota.max)
}
isSaving={isSaving}
/>
)}
</>
);
};
export default inject(({ importAccountsStore }) => {
export default inject(({ importAccountsStore, currentQuotaStore }) => {
const {
withEmailUsers,
searchValue,
@ -146,7 +168,9 @@ export default inject(({ importAccountsStore }) => {
areCheckedUsersEmpty,
setResultUsers,
cancelMigration,
checkedUsers,
} = importAccountsStore;
const { quotaCharacteristics } = currentQuotaStore;
return {
withEmailUsers,
@ -155,5 +179,7 @@ export default inject(({ importAccountsStore }) => {
areCheckedUsersEmpty,
setResultUsers,
cancelMigration,
checkedUsers,
quotaCharacteristics,
};
})(observer(SelectUsersStep));

View File

@ -51,11 +51,11 @@ const StyledTableContainer = styled(TableContainer)`
position: sticky;
z-index: 201;
width: calc(100% + 40px);
margin-top: 20px;
margin-top: -33px;
margin-left: -20px;
top: 0;
margin-bottom: -37.5px;
margin-bottom: -36px;
.table-container_group-menu {
border-image-slice: 0;

View File

@ -92,16 +92,14 @@ const SelectUsersTypeStep = ({
{filteredUsers.length > 0 && (
<>
{!checkedUsers.result.length > 0 && (
<StyledSearchInput
id="search-users-type-input"
placeholder={t("Common:Search")}
value={searchValue}
onChange={onChangeInput}
refreshTimeout={100}
onClearSearch={onClearSearchInput}
/>
)}
<StyledSearchInput
id="search-users-type-input"
placeholder={t("Common:Search")}
value={searchValue}
onChange={onChangeInput}
refreshTimeout={100}
onClearSearch={onClearSearchInput}
/>
<AccountsTable t={t} accountsData={filteredAccounts} />

View File

@ -28,7 +28,10 @@ import { useState, useEffect } from "react";
import { inject, observer } from "mobx-react";
import { Trans, withTranslation } from "react-i18next";
import { getStepTitle, getGoogleStepDescription } from "../../../utils";
import { tablet } from "@docspace/shared/utils/device";
import {
tablet,
isMobile as isMobileBreakpoint,
} from "@docspace/shared/utils/device";
import { isMobile } from "react-device-detect";
import useViewEffect from "SRC_DIR/Hooks/useViewEffect";
@ -215,7 +218,7 @@ const GoogleWorkspace = ({
return clearCheckedAccounts;
}, []);
if (isMobile)
if (isMobile || isMobileBreakpoint())
return (
<BreakpointWarning
isMobileUnavailableOnly

View File

@ -178,7 +178,7 @@ const UsersTableRow = ({
{isEmailOpen ? (
<EmailInputWrapper ref={emailInputRef}>
<EmailInput
placeholder={t("Settings:NoEmail")}
placeholder={t("EnterEmail")}
className="import-email-input"
value={tempEmail}
onChange={handleEmailChange}
@ -197,7 +197,7 @@ const UsersTableRow = ({
<span onClick={openEmail} className="user-email" ref={emailTextRef}>
<EditSvg />
<Text fontWeight={600} color="#A3A9AE" className="textOverflow">
{prevEmail !== "" ? prevEmail : t("EnterEmail")}
{prevEmail !== "" ? prevEmail : t("Settings:NoEmail")}
</Text>
</span>
)}

View File

@ -24,7 +24,7 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { useState } from "react";
import { useState, useEffect } from "react";
import { inject, observer } from "mobx-react";
import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons";
@ -36,10 +36,10 @@ import { Text } from "@docspace/shared/components/text";
import { Wrapper } from "../StyledStepper";
// import UsersInfoBlock from "../../../sub-components/UsersInfoBlock";
import UsersInfoBlock from "../../../sub-components/UsersInfoBlock";
import { NoEmailUsersBlock } from "../../../sub-components/NoEmailUsersBlock";
// const LICENSE_LIMIT = 100;
import { parseQuota } from "../../../utils";
const AddEmailsStep = (props) => {
const {
@ -51,11 +51,15 @@ const AddEmailsStep = (props) => {
setSearchValue,
setResultUsers,
areCheckedUsersEmpty,
checkedUsers,
quotaCharacteristics,
withEmailUsers,
} = props;
const [dataPortion, setDataPortion] = useState(
users.withoutEmail.slice(0, 25),
);
const [quota, setQuota] = useState({ used: 0, max: 0 });
const handleDataChange = (leftBoundary, rightBoundary) => {
setDataPortion(users.withoutEmail.slice(leftBoundary, rightBoundary));
@ -80,6 +84,18 @@ const AddEmailsStep = (props) => {
incrementStep();
};
const numberOfSelectedUsers =
checkedUsers.withEmail.length + checkedUsers.withoutEmail.length;
useEffect(() => {
setQuota(parseQuota(quotaCharacteristics[1]));
}, []);
const totalUsedUsers =
quota.used +
checkedUsers.withEmail.filter((user) => !user.isDuplicate).length +
checkedUsers.withoutEmail.length;
return (
<Wrapper>
{users.withoutEmail.length > 0 && (
@ -100,15 +116,20 @@ const AddEmailsStep = (props) => {
cancelButtonLabel={t("Common:Back")}
showReminder
displaySettings
saveButtonDisabled={areCheckedUsersEmpty}
saveButtonDisabled={
areCheckedUsersEmpty || (quota.max && totalUsedUsers > quota.max)
}
/>
{/* <UsersInfoBlock
t={t}
selectedUsers={numberOfCheckedAccounts}
totalUsers={users.withoutEmail.length}
totalLicenceLimit={LICENSE_LIMIT}
/> */}
{quota.max && (
<UsersInfoBlock
t={t}
totalUsedUsers={totalUsedUsers}
selectedUsers={numberOfSelectedUsers}
totalUsers={withEmailUsers.length + users.withoutEmail.length}
totalLicenceLimit={quota.max}
/>
)}
<SearchInput
id="search-users-input"
@ -143,13 +164,15 @@ const AddEmailsStep = (props) => {
cancelButtonLabel={t("Common:Back")}
showReminder
displaySettings
saveButtonDisabled={areCheckedUsersEmpty}
saveButtonDisabled={
areCheckedUsersEmpty || (quota.max && totalUsedUsers > quota.max)
}
/>
</Wrapper>
);
};
export default inject(({ setup, importAccountsStore }) => {
export default inject(({ setup, importAccountsStore, currentQuotaStore }) => {
const { viewAs } = setup;
const {
searchValue,
@ -157,7 +180,10 @@ export default inject(({ setup, importAccountsStore }) => {
users,
setResultUsers,
areCheckedUsersEmpty,
checkedUsers,
withEmailUsers,
} = importAccountsStore;
const { quotaCharacteristics } = currentQuotaStore;
return {
viewAs,
@ -166,5 +192,8 @@ export default inject(({ setup, importAccountsStore }) => {
users,
setResultUsers,
areCheckedUsersEmpty,
checkedUsers,
quotaCharacteristics,
withEmailUsers,
};
})(observer(AddEmailsStep));

View File

@ -96,7 +96,7 @@ const ImportCompleteStep = ({
setIsSaving(true);
setTimeout(() => {
setIsSaving(false);
navigate(-1);
navigate("/portal-settings/data-import/migration");
}, 1000);
};

View File

@ -39,7 +39,6 @@ import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-butto
import { Box } from "@docspace/shared/components/box";
import { Link } from "@docspace/shared/components/link";
import { toastr } from "@docspace/shared/components/toast";
// import { mockRes } from "./tempMock";
const Wrapper = styled.div`
max-width: 700px;
@ -101,6 +100,8 @@ const ErrorBlock = styled.div`
}
`;
const FAILS_TRIES = 1;
const SelectFileStep = ({
t,
incrementStep,
@ -125,9 +126,10 @@ const SelectFileStep = ({
const uploadInterval = useRef(null);
const navigate = useNavigate();
const [failTries, setFailsTries] = useState(FAILS_TRIES);
const goBack = () => {
cancelMigration();
setTimeout(() => navigate(-1), 100);
navigate("/portal-settings/data-import/migration");
};
const checkMigrationStatusAndUpdate = async () => {
@ -188,15 +190,41 @@ const SelectFileStep = ({
};
const onUploadFile = async (file) => {
setProgress(0);
setIsVisible(true);
try {
if (Array.isArray(file)) throw new Error(t("Common:SomethingWentWrong"));
await singleFileUploading(file, setProgress, isAbort);
if (isAbort.current) return;
await initMigrationName(searchParams.get("service"));
uploadInterval.current = setInterval(async () => {
try {
const res = await getMigrationStatus();
if (!res && failTries) {
setFailsTries((tries) => tries - 1);
return;
}
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
clearInterval(uploadInterval.current);
setIsFileError(true);
setIsFileLoading(false);
toastr.error(res.error);
return;
}
if (res.isCompleted || res.parseResult.progress === 100) {
clearInterval(uploadInterval.current);
setIsFileLoading(false);
setIsVisible(false);
setUsers(res.parseResult);
setIsSaveDisabled(true);
}
setProgress(res.progress);
if (res.progress > 10) {
@ -204,26 +232,14 @@ const SelectFileStep = ({
} else {
setIsVisible(true);
}
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
toastr.error(res.error);
setIsFileError(true);
setIsFileLoading(false);
clearInterval(uploadInterval.current);
} else if (res.isCompleted || res.parseResult.progress === 100) {
clearInterval(uploadInterval.current);
setIsFileLoading(false);
setIsVisible(false);
setProgress(100);
setUsers(res.parseResult);
setIsSaveDisabled(true);
}
} catch (error) {
toastr.error(error || error.message);
setIsFileError(true);
setIsFileLoading(false);
setIsError(true);
clearInterval(uploadInterval.current);
} finally {
isAbort.current = false;
}
}, 1000);
} catch (error) {
@ -238,6 +254,7 @@ const SelectFileStep = ({
setIsFileError(false);
setIsSaveDisabled(false);
setIsFileLoading(true);
setFailsTries(FAILS_TRIES);
try {
onUploadFile(file);
} catch (error) {
@ -264,7 +281,6 @@ const SelectFileStep = ({
window.URL.revokeObjectURL(url);
});
} catch (error) {
console.log(error);
toastr.error(error);
}
};

View File

@ -34,10 +34,10 @@ import { Text } from "@docspace/shared/components/text";
import AccountsTable from "./AccountsTable";
import AccountsPaging from "../../../sub-components/AccountsPaging";
// import UsersInfoBlock from "../../../sub-components/UsersInfoBlock";
import UsersInfoBlock from "../../../sub-components/UsersInfoBlock";
import { Wrapper } from "../StyledStepper";
// const LICENSE_LIMIT = 100;
import { parseQuota } from "../../../utils";
const SelectUsersStep = (props) => {
const {
@ -48,12 +48,18 @@ const SelectUsersStep = (props) => {
searchValue,
setSearchValue,
cancelMigration,
checkedUsers,
quotaCharacteristics,
users,
} = props;
const [dataPortion, setDataPortion] = useState(withEmailUsers.slice(0, 25));
const [quota, setQuota] = useState({ used: 0, max: 0 });
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
setSearchValue("");
setQuota(parseQuota(quotaCharacteristics[1]));
}, []);
const handleDataChange = (leftBoundary, rightBoundary) => {
@ -77,9 +83,21 @@ const SelectUsersStep = (props) => {
const goBack = () => {
cancelMigration();
setTimeout(decrementStep, 100);
setIsSaving(true);
setTimeout(() => {
setIsSaving(false);
decrementStep();
}, 1000);
};
const numberOfSelectedUsers =
checkedUsers.withEmail.length + checkedUsers.withoutEmail.length;
const totalUsedUsers =
quota.used +
checkedUsers.withEmail.filter((user) => !user.isDuplicate).length +
checkedUsers.withoutEmail.length;
return (
<Wrapper>
{withEmailUsers.length > 0 ? (
@ -92,14 +110,18 @@ const SelectUsersStep = (props) => {
cancelButtonLabel={t("Common:Back")}
showReminder
displaySettings
isSaving={isSaving}
/>
{/* <UsersInfoBlock
t={t}
selectedUsers={numberOfCheckedAccounts}
totalUsers={users.length}
totalLicenceLimit={LICENSE_LIMIT}
/> */}
{quota.max && (
<UsersInfoBlock
t={t}
totalUsedUsers={totalUsedUsers}
selectedUsers={numberOfSelectedUsers}
totalUsers={withEmailUsers.length + users.withoutEmail.length}
totalLicenceLimit={quota.max}
/>
)}
<SearchInput
id="search-users-input"
@ -135,20 +157,23 @@ const SelectUsersStep = (props) => {
cancelButtonLabel={t("Common:Back")}
showReminder
displaySettings
isSaving={isSaving}
/>
)}
</Wrapper>
);
};
export default inject(({ importAccountsStore }) => {
export default inject(({ importAccountsStore, currentQuotaStore }) => {
const {
users,
withEmailUsers,
searchValue,
setSearchValue,
cancelMigration,
checkedUsers,
} = importAccountsStore;
const { quotaCharacteristics } = currentQuotaStore;
return {
users,
@ -156,5 +181,7 @@ export default inject(({ importAccountsStore }) => {
searchValue,
setSearchValue,
cancelMigration,
checkedUsers,
quotaCharacteristics,
};
})(observer(SelectUsersStep));

View File

@ -49,11 +49,11 @@ const UserSelectTableContainer = styled(StyledTableContainer)`
position: sticky;
z-index: 201;
width: calc(100% + 40px);
margin-top: 20px;
margin-top: -33px;
margin-left: -20px;
top: 0;
margin-bottom: -37.5px;
margin-bottom: -36px;
.table-container_group-menu {
border-image-slice: 0;

View File

@ -89,17 +89,15 @@ const SelectUsersTypeStep = (props) => {
{filteredUsers.length > 0 && (
<>
{!checkedUsers.result.length > 0 && (
<SearchInput
id="search-checkedUsers-type-input"
className="importUsersSearch"
placeholder={t("Common:Search")}
value={searchValue}
onChange={onChangeInput}
refreshTimeout={100}
onClearSearch={onClearSearchInput}
/>
)}
<SearchInput
id="search-checkedUsers-type-input"
className="importUsersSearch"
placeholder={t("Common:Search")}
value={searchValue}
onChange={onChangeInput}
refreshTimeout={100}
onClearSearch={onClearSearchInput}
/>
<AccountsTable t={t} accountsData={filteredAccounts} />

View File

@ -27,6 +27,7 @@
import { useState, useEffect } from "react";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import { isMobile as isMobileBreakpoint } from "@docspace/shared/utils/device";
import { isMobile } from "react-device-detect";
import { useNavigate } from "react-router-dom";
import useViewEffect from "SRC_DIR/Hooks/useViewEffect";
@ -131,7 +132,7 @@ const NextcloudWorkspace = (props) => {
return clearCheckedAccounts;
}, []);
if (isMobile)
if (isMobile || isMobileBreakpoint())
return (
<BreakpointWarning
isMobileUnavailableOnly

View File

@ -107,7 +107,7 @@ const ImportCompleteStep = ({
setIsSaving(true);
setTimeout(() => {
setIsSaving(false);
navigate(-1);
navigate("/portal-settings/data-import/migration")
}, 1000);
};

View File

@ -89,6 +89,8 @@ const ErrorBlock = styled.div`
}
`;
const FAILS_TRIES = 1;
const SelectFileStep = ({
t,
onNextStep,
@ -115,9 +117,10 @@ const SelectFileStep = ({
const uploadInterval = useRef(null);
const navigate = useNavigate();
const [failTries, setFailsTries] = useState(FAILS_TRIES);
const goBack = () => {
cancelMigration();
setTimeout(() => navigate(-1), 100);
navigate("/portal-settings/data-import/migration");
};
const checkMigrationStatusAndUpdate = async () => {
@ -178,16 +181,45 @@ const SelectFileStep = ({
};
const onUploadFile = async (file) => {
setProgress(0);
setIsVisible(true);
try {
if (Array.isArray(file)) {
setShowErrorText(true);
throw new Error(t("Common:SomethingWentWrong"));
}
await singleFileUploading(file, setProgress, isAbort);
if (isAbort.current) return;
await initMigrationName(searchParams.get("service"));
uploadInterval.current = setInterval(async () => {
try {
const res = await getMigrationStatus();
setProgress(res?.progress);
if (!res && failTries) {
setFailsTries((tries) => tries - 1);
return;
}
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
toastr.error(res.error || t("Common:SomethingWentWrong"));
setIsFileError(true);
setIsFileLoading(false);
clearInterval(uploadInterval.current);
return;
}
if (res.isCompleted || res.parseResult.progress === 100) {
clearInterval(uploadInterval.current);
setIsFileLoading(false);
setIsVisible(false);
setUsers(res.parseResult);
setShowReminder(true);
}
setProgress(res.progress);
if (res.progress > 10) {
setIsVisible(false);
@ -200,26 +232,14 @@ const SelectFileStep = ({
} else {
setShowErrorText(false);
}
if (!res || res.parseResult.failedArchives.length > 0 || res.error) {
toastr.error(res.error || t("Common:SomethingWentWrong"));
setIsFileError(true);
setIsFileLoading(false);
clearInterval(uploadInterval.current);
} else if (res.isCompleted || res.parseResult.progress === 100) {
clearInterval(uploadInterval.current);
setIsFileLoading(false);
setIsVisible(false);
setProgress(100);
setUsers(res.parseResult);
setShowReminder(true);
}
} catch (error) {
toastr.error(error || t("Common:SomethingWentWrong"));
setIsFileError(true);
setIsFileLoading(false);
setIsError(true);
clearInterval(uploadInterval.current);
} finally {
isAbort.current = false;
}
}, 1000);
} catch (error) {
@ -234,6 +254,7 @@ const SelectFileStep = ({
setIsFileError(false);
setShowReminder(false);
setIsFileLoading(true);
setFailsTries(FAILS_TRIES);
try {
onUploadFile(file);
} catch (error) {
@ -261,7 +282,6 @@ const SelectFileStep = ({
});
} catch (error) {
toastr.error(error);
console.log(error);
}
};

View File

@ -32,9 +32,9 @@ import { SearchInput } from "@docspace/shared/components/search-input";
import AccountsTable from "./AccountsTable";
import AccountsPaging from "../../../sub-components/AccountsPaging";
// import UsersInfoBlock from "./../../../sub-components/UsersInfoBlock";
import UsersInfoBlock from "./../../../sub-components/UsersInfoBlock";
// const LICENSE_LIMIT = 100;
import { parseQuota } from "../../../utils";
const SelectUsersStep = ({
t,
@ -46,11 +46,16 @@ const SelectUsersStep = ({
searchValue,
setSearchValue,
cancelMigration,
checkedUsers,
quotaCharacteristics,
}) => {
const [dataPortion, setDataPortion] = useState(withEmailUsers.slice(0, 25));
const [quota, setQuota] = useState({ used: 0, max: 0 });
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
setSearchValue("");
setQuota(parseQuota(quotaCharacteristics[1]));
}, []);
const handleDataChange = (leftBoundary, rightBoundary) => {
@ -79,9 +84,17 @@ const SelectUsersStep = ({
const goBack = () => {
cancelMigration();
setTimeout(onPrevStep, 100);
setIsSaving(true);
setTimeout(() => {
setIsSaving(false);
onPrevStep();
}, 1000);
};
const totalUsedUsers =
quota.used +
checkedUsers.withEmail.filter((user) => !user.isDuplicate).length;
return (
<>
<SaveCancelButtons
@ -92,15 +105,21 @@ const SelectUsersStep = ({
saveButtonLabel={t("Settings:NextStep")}
cancelButtonLabel={t("Common:Back")}
displaySettings
saveButtonDisabled={areCheckedUsersEmpty}
saveButtonDisabled={
areCheckedUsersEmpty || (quota.max && totalUsedUsers > quota.max)
}
isSaving={isSaving}
/>
{/* <UsersInfoBlock
t={t}
selectedUsers={numberOfCheckedAccounts}
totalUsers={users.length}
totalLicenceLimit={LICENSE_LIMIT}
/> */}
{quota.max && (
<UsersInfoBlock
t={t}
totalUsedUsers={totalUsedUsers}
selectedUsers={checkedUsers.withEmail.length}
totalUsers={withEmailUsers.length}
totalLicenceLimit={quota.max}
/>
)}
<SearchInput
id="search-users-input"
@ -131,14 +150,17 @@ const SelectUsersStep = ({
saveButtonLabel={t("Settings:NextStep")}
cancelButtonLabel={t("Common:Back")}
displaySettings
saveButtonDisabled={areCheckedUsersEmpty}
saveButtonDisabled={
areCheckedUsersEmpty || (quota.max && totalUsedUsers > quota.max)
}
isSaving={isSaving}
/>
)}
</>
);
};
export default inject(({ importAccountsStore }) => {
export default inject(({ importAccountsStore, currentQuotaStore }) => {
const {
withEmailUsers,
searchValue,
@ -146,7 +168,9 @@ export default inject(({ importAccountsStore }) => {
setResultUsers,
areCheckedUsersEmpty,
cancelMigration,
checkedUsers,
} = importAccountsStore;
const { quotaCharacteristics } = currentQuotaStore;
return {
setResultUsers,
@ -155,5 +179,7 @@ export default inject(({ importAccountsStore }) => {
searchValue,
setSearchValue,
cancelMigration,
checkedUsers,
quotaCharacteristics,
};
})(observer(SelectUsersStep));

View File

@ -51,11 +51,11 @@ const StyledTableContainer = styled(TableContainer)`
position: sticky;
z-index: 201;
width: calc(100% + 40px);
margin-top: 20px;
margin-top: -33px;
margin-left: -20px;
top: 0;
margin-bottom: -37.5px;
margin-bottom: -36px;
.table-container_group-menu {
border-image-slice: 0;

View File

@ -86,17 +86,15 @@ const SelectUsersTypeStep = ({
{filteredUsers.length > 0 && (
<>
{!checkedUsers.result.length > 0 && (
<SearchInput
id="search-users-type-input"
placeholder={t("Common:Search")}
style={{ marginTop: "20px" }}
value={searchValue}
onChange={onChangeInput}
refreshTimeout={100}
onClearSearch={onClearSearchInput}
/>
)}
<SearchInput
id="search-users-type-input"
placeholder={t("Common:Search")}
style={{ marginTop: "20px" }}
value={searchValue}
onChange={onChangeInput}
refreshTimeout={100}
onClearSearch={onClearSearchInput}
/>
<AccountsTable t={t} accountsData={filteredAccounts} />

View File

@ -28,7 +28,10 @@ import { useState, useEffect } from "react";
import { inject, observer } from "mobx-react";
import { Trans, withTranslation } from "react-i18next";
import { getStepTitle, getWorkspaceStepDescription } from "../../../utils";
import { tablet } from "@docspace/shared/utils/device";
import {
tablet,
isMobile as isMobileBreakpoint,
} from "@docspace/shared/utils/device";
import { isMobile } from "react-device-detect";
import useViewEffect from "SRC_DIR/Hooks/useViewEffect";
import styled, { css } from "styled-components";
@ -212,7 +215,7 @@ const OnlyofficeWorkspace = ({
return clearCheckedAccounts;
}, []);
if (isMobile) {
if (isMobile || isMobileBreakpoint()) {
return (
<BreakpointWarning
isMobileUnavailableOnly

View File

@ -87,6 +87,7 @@ const UsersInfoBlock = ({
t,
selectedUsers,
totalUsers,
totalUsedUsers,
totalLicenceLimit,
}) => {
return (
@ -98,7 +99,7 @@ const UsersInfoBlock = ({
)}
<UsersInfoWrapper
selectedUsers={selectedUsers}
selectedUsers={totalUsedUsers}
totalLicenceLimit={totalLicenceLimit}
>
<Text className="selected-users-count" truncate>
@ -107,7 +108,7 @@ const UsersInfoBlock = ({
<Text as="div" className="selected-admins-count" truncate>
{t("Settings:LicenseLimitCounter")}
<Text as="span">
{selectedUsers}/{totalLicenceLimit}
{totalUsedUsers}/{totalLicenceLimit}
</Text>
</Text>
<HelpButton

View File

@ -0,0 +1,50 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import { PortalFeaturesLimitations } from "@docspace/shared/enums";
import { getConvertedSize } from "@docspace/shared/utils/common";
export const parseQuota = (quotaCharacteristics) => {
const maxValue = quotaCharacteristics.value;
const usedValue = quotaCharacteristics.used.value;
if (maxValue === PortalFeaturesLimitations.Unavailable) return;
const isExistsMaxValue = maxValue !== PortalFeaturesLimitations.Limitless;
const resultingMaxValue =
quotaCharacteristics.type === "size" && isExistsMaxValue
? getConvertedSize(t, maxValue)
: isExistsMaxValue
? maxValue
: null;
const resultingUsedValue =
quotaCharacteristics.type === "size"
? getConvertedSize(t, usedValue)
: usedValue;
return { used: resultingUsedValue, max: resultingMaxValue };
};

View File

@ -102,7 +102,9 @@ class ImportAccountsStore {
get filteredUsers() {
return this.users.result.filter(
(user) =>
!this.users.existing.some((existingUser) => existingUser.key === user.key),
!this.users.existing.some(
(existingUser) => existingUser.key === user.key,
),
);
}
@ -290,8 +292,6 @@ class ImportAccountsStore {
}
} catch (e) {
console.error(e);
} finally {
isAbort.current = false;
}
};
@ -331,8 +331,6 @@ class ImportAccountsStore {
}
} catch (e) {
console.error(e);
} finally {
isAbort.current = false;
}
};