Merge pull request #436 from ONLYOFFICE/bugfix/data-import
Bugfix/data import
This commit is contained in:
commit
af59952251
@ -94,6 +94,8 @@ const HeaderContainer = styled.div`
|
||||
}
|
||||
|
||||
.arrow-button {
|
||||
flex-shrink: 0;
|
||||
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
|
@ -107,7 +107,7 @@ const ImportCompleteStep = ({
|
||||
setIsSaving(true);
|
||||
setTimeout(() => {
|
||||
setIsSaving(false);
|
||||
navigate(-1);
|
||||
navigate("/portal-settings/data-import/migration");
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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} />
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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));
|
||||
|
@ -96,7 +96,7 @@ const ImportCompleteStep = ({
|
||||
setIsSaving(true);
|
||||
setTimeout(() => {
|
||||
setIsSaving(false);
|
||||
navigate(-1);
|
||||
navigate("/portal-settings/data-import/migration");
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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} />
|
||||
|
||||
|
@ -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
|
||||
|
@ -107,7 +107,7 @@ const ImportCompleteStep = ({
|
||||
setIsSaving(true);
|
||||
setTimeout(() => {
|
||||
setIsSaving(false);
|
||||
navigate(-1);
|
||||
navigate("/portal-settings/data-import/migration")
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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} />
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 };
|
||||
};
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user