Merge branch 'develop' into refactoring/global-colors
This commit is contained in:
commit
49c9cc687e
@ -51,11 +51,7 @@ const GoogleWorkspace = (props: WorkspaceProps) => {
|
||||
"Common, SMTPSettings, Settings",
|
||||
]);
|
||||
|
||||
const StepsData = getStepsData(
|
||||
t,
|
||||
filteredUsers.length === 0,
|
||||
t("Common:OrganizationName"),
|
||||
);
|
||||
const StepsData = getStepsData(t, filteredUsers.length === 0);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (migratingWorkspace === "GoogleWorkspace" && !isMigrationInit) {
|
||||
@ -68,6 +64,7 @@ const GoogleWorkspace = (props: WorkspaceProps) => {
|
||||
}
|
||||
setIsMigrationInit(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!ready) return <SelectFileLoader />;
|
||||
@ -81,7 +78,6 @@ const GoogleWorkspace = (props: WorkspaceProps) => {
|
||||
title={StepsData[step - 1].title}
|
||||
description={StepsData[step - 1].description}
|
||||
component={StepsData[step - 1].component}
|
||||
organizationName={t("Common:OrganizationName")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -64,6 +64,7 @@ const NextcloudWorkspace = (props: WorkspaceProps) => {
|
||||
}
|
||||
setIsMigrationInit(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!ready) return <SelectFileLoader />;
|
||||
|
@ -1,246 +0,0 @@
|
||||
// (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 { inject, observer } from "mobx-react";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import UsersTypeRow from "./UsersTypeRow";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { TableGroupMenu } from "@docspace/shared/components/table";
|
||||
import { RowContainer } from "@docspace/shared/components/row-container";
|
||||
import { Row } from "@docspace/shared/components/row";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import ChangeTypeReactSvgUrl from "PUBLIC_DIR/images/change.type.react.svg?url";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
import { globalColors } from "@docspace/shared/themes";
|
||||
|
||||
const StyledRowContainer = styled(RowContainer)`
|
||||
margin: 0 0 20px;
|
||||
|
||||
.table-group-menu {
|
||||
height: 61px;
|
||||
position: sticky;
|
||||
z-index: 201;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: -16px;
|
||||
`
|
||||
: css`
|
||||
margin-left: -16px;
|
||||
`}
|
||||
width: 100%;
|
||||
|
||||
margin-top: 20px;
|
||||
top: 61px;
|
||||
margin-bottom: -29.5px;
|
||||
|
||||
.table-container_group-menu {
|
||||
padding: 0px 16px;
|
||||
border-image-slice: 0;
|
||||
box-shadow: ${globalColors.menuShadow} 0px 15px 20px;
|
||||
}
|
||||
|
||||
.table-container_group-menu-checkbox {
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
margin-right: 8px;
|
||||
`
|
||||
: css`
|
||||
margin-left: 8px;
|
||||
`}
|
||||
}
|
||||
|
||||
.table-container_group-menu-separator {
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-container-text {
|
||||
font-size: 12px;
|
||||
color: ${(props) =>
|
||||
props.theme.client.settings.migration.tableRowTextColor};
|
||||
}
|
||||
|
||||
.table-container_header {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ec-desc {
|
||||
max-width: 348px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledRow = styled(Row)`
|
||||
box-sizing: border-box;
|
||||
min-height: 40px;
|
||||
|
||||
.row-header-title {
|
||||
color: ${(props) => props.theme.client.settings.migration.tableHeaderText};
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
.row_content {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const checkedAccountType = "result";
|
||||
|
||||
const RowView = ({
|
||||
t,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
typeOptions,
|
||||
filteredUsers,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
}) => {
|
||||
const isIndeterminate =
|
||||
checkedUsers.result.length > 0 &&
|
||||
checkedUsers.result.length !== filteredUsers.length;
|
||||
|
||||
const isChecked = checkedUsers.result.length === filteredUsers.length;
|
||||
|
||||
const toggleAll = (isChecked) =>
|
||||
toggleAllAccounts(isChecked, filteredUsers, checkedAccountType);
|
||||
|
||||
const onClearFilter = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const headerMenu = [
|
||||
{
|
||||
id: "change-type",
|
||||
key: "change-type",
|
||||
label: t("ChangeUserTypeDialog:ChangeUserTypeButton"),
|
||||
disabled: false,
|
||||
withDropDown: true,
|
||||
options: typeOptions,
|
||||
iconUrl: ChangeTypeReactSvgUrl,
|
||||
onClick: () => {},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<StyledRowContainer useReactWindow={false}>
|
||||
{checkedUsers.result.length > 0 && (
|
||||
<div className="table-group-menu">
|
||||
<TableGroupMenu
|
||||
sectionWidth={sectionWidth}
|
||||
headerMenu={headerMenu}
|
||||
withoutInfoPanelToggler
|
||||
withComboBox={false}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={isChecked}
|
||||
onChange={toggleAll}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<StyledRow key="Name" sectionWidth={sectionWidth} onClick={toggleAll}>
|
||||
<Text className="row-header-title">{t("Common:Name")}</Text>
|
||||
</StyledRow>
|
||||
|
||||
{accountsData.map((data) => (
|
||||
<UsersTypeRow
|
||||
key={data.key}
|
||||
data={data}
|
||||
sectionWidth={sectionWidth}
|
||||
typeOptions={typeOptions}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={() => toggleAccount(data, checkedAccountType)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size="12"
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered={true}
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledRowContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ importAccountsStore }) => {
|
||||
const {
|
||||
filteredUsers,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
filteredUsers,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
};
|
||||
})(observer(RowView));
|
@ -1,298 +0,0 @@
|
||||
// (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 { useState, useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Base, globalColors } from "@docspace/shared/themes";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import UsersTypeTableHeader from "./UsersTypeTableHeader";
|
||||
import UsersTypeTableRow from "./UsersTypeTableRow";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { TableGroupMenu } from "@docspace/shared/components/table";
|
||||
import { TableContainer } from "@docspace/shared/components/table";
|
||||
import { TableBody } from "@docspace/shared/components/table/TableBody";
|
||||
import ChangeTypeReactSvgUrl from "PUBLIC_DIR/images/change.type.react.svg?url";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
|
||||
const StyledTableContainer = styled(TableContainer)`
|
||||
margin: 0 0 20px;
|
||||
|
||||
.table-group-menu {
|
||||
height: 69px;
|
||||
position: sticky;
|
||||
z-index: 201;
|
||||
width: calc(100% + 40px);
|
||||
margin-top: -33px;
|
||||
margin-left: -20px;
|
||||
top: 0;
|
||||
|
||||
margin-bottom: -36px;
|
||||
|
||||
.table-container_group-menu {
|
||||
border-image-slice: 0;
|
||||
border-image-source: none;
|
||||
border-bottom: ${(props) =>
|
||||
props.theme.client.settings.migration.workspaceBorder};
|
||||
box-shadow: ${globalColors.menuShadow} 0px 15px 20px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.table-container_group-menu-separator {
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container_header {
|
||||
position: absolute;
|
||||
${(props) =>
|
||||
props.theme.interfaceDirection === "rtl"
|
||||
? css`
|
||||
padding: 0px 28px 0 15px;
|
||||
`
|
||||
: css`
|
||||
padding: 0px 15px 0 28px;
|
||||
`}
|
||||
}
|
||||
|
||||
.header-container-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.checkboxWrapper {
|
||||
padding: 0;
|
||||
padding-inline-start: 8px;
|
||||
}
|
||||
|
||||
.table-list-item {
|
||||
cursor: pointer;
|
||||
|
||||
padding-left: 20px;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
props.theme.filesSection.tableView.row.backgroundActive};
|
||||
|
||||
.table-container_cell {
|
||||
margin-top: -1px;
|
||||
border-top: ${(props) =>
|
||||
`1px solid ${props.theme.filesSection.tableView.row.borderColor}`};
|
||||
|
||||
margin-left: -24px;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.checkboxWrapper {
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
.table-container_row-context-menu-wrapper {
|
||||
margin-right: -20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-list-item:has(.selected-table-row) {
|
||||
background-color: ${(props) =>
|
||||
props.theme.filesSection.tableView.row.backgroundActive};
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
margin-right: 8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.ec-desc {
|
||||
max-width: 618px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledTableContainer.defaultProps = { theme: Base };
|
||||
|
||||
const TABLE_VERSION = "6";
|
||||
const COLUMNS_SIZE = `nextcloudFourthColumnsSize_ver-${TABLE_VERSION}`;
|
||||
const INFO_PANEL_COLUMNS_SIZE = `infoPanelNextcloudFourthColumnsSize_ver-${TABLE_VERSION}`;
|
||||
|
||||
const checkedAccountType = "result";
|
||||
|
||||
const TableView = ({
|
||||
t,
|
||||
userId,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
typeOptions,
|
||||
filteredUsers,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
}) => {
|
||||
const tableRef = useRef(null);
|
||||
const [hideColumns, setHideColumns] = useState(false);
|
||||
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
|
||||
const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`;
|
||||
|
||||
const isIndeterminate =
|
||||
checkedUsers.result.length > 0 &&
|
||||
checkedUsers.result.length !== filteredUsers.length;
|
||||
|
||||
const isChecked = checkedUsers.result.length === filteredUsers.length;
|
||||
|
||||
const toggleAll = (isChecked) => {
|
||||
toggleAllAccounts(isChecked, filteredUsers, checkedAccountType);
|
||||
};
|
||||
|
||||
const onClearFilter = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const headerMenu = [
|
||||
{
|
||||
id: "change-type",
|
||||
key: "change-type",
|
||||
label: t("ChangeUserTypeDialog:ChangeUserTypeButton"),
|
||||
disabled: false,
|
||||
withDropDown: true,
|
||||
options: typeOptions,
|
||||
iconUrl: ChangeTypeReactSvgUrl,
|
||||
onClick: () => {},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<StyledTableContainer forwardedRef={tableRef} useReactWindow>
|
||||
{checkedUsers.result.length > 0 && (
|
||||
<div className="table-group-menu">
|
||||
<TableGroupMenu
|
||||
checkboxOptions={[]}
|
||||
sectionWidth={sectionWidth}
|
||||
headerMenu={headerMenu}
|
||||
withoutInfoPanelToggler
|
||||
withComboBox={false}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={isChecked}
|
||||
onChange={toggleAll}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<UsersTypeTableHeader
|
||||
t={t}
|
||||
sectionWidth={sectionWidth}
|
||||
tableRef={tableRef}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={isChecked}
|
||||
toggleAll={toggleAll}
|
||||
setHideColumns={setHideColumns}
|
||||
/>
|
||||
<TableBody
|
||||
itemHeight={49}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
filesLength={accountsData.length}
|
||||
hasMoreFiles={false}
|
||||
itemCount={accountsData.length}
|
||||
fetchMoreFiles={() => {}}
|
||||
>
|
||||
{accountsData.map((data) => (
|
||||
<UsersTypeTableRow
|
||||
key={data.key}
|
||||
id={data.key}
|
||||
type={data.userType}
|
||||
displayName={data.displayName}
|
||||
email={data.email}
|
||||
typeOptions={typeOptions}
|
||||
hideColumns={hideColumns}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={() => toggleAccount(data, checkedAccountType)}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size="12"
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered={true}
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ userStore, importAccountsStore }) => {
|
||||
const { id: userId } = userStore.user;
|
||||
const {
|
||||
filteredUsers,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
userId,
|
||||
filteredUsers,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
};
|
||||
})(observer(TableView));
|
@ -64,6 +64,7 @@ const OnlyofficeWorkspace = (props: WorkspaceProps) => {
|
||||
}
|
||||
setIsMigrationInit(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!ready) return <SelectFileLoader />;
|
||||
|
@ -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 styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
import { tablet, mobile } from "@docspace/shared/utils/device";
|
||||
|
||||
import { TableContainer } from "@docspace/shared/components/table";
|
||||
|
@ -29,16 +29,25 @@ import { inject, observer } from "mobx-react";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link, LinkType } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { RowContainer } from "@docspace/shared/components/row-container";
|
||||
import { Row } from "@docspace/shared/components/row";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { globalColors } from "@docspace/shared/themes";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
import { TEnhancedMigrationUser } from "@docspace/shared/api/settings/types";
|
||||
import UsersRow from "./UsersRow";
|
||||
import { AddEmailRowProps, RowViewProps } from "../../../../types";
|
||||
|
||||
const StyledRowContainer = styled(RowContainer)`
|
||||
margin: 0 0 20px;
|
||||
|
||||
.clear-icon {
|
||||
margin-inline-end: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledRow = styled(Row)`
|
||||
@ -72,6 +81,7 @@ const RowView = (props: RowViewProps) => {
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = props as AddEmailRowProps;
|
||||
|
||||
const [openedEmailKey, setOpenedEmailKey] = useState("");
|
||||
@ -86,6 +96,8 @@ const RowView = (props: RowViewProps) => {
|
||||
const handleToggle = (user: TEnhancedMigrationUser) =>
|
||||
toggleAccount(user, checkedAccountType);
|
||||
|
||||
const onClearFilter = () => setSearchValue("");
|
||||
|
||||
const isIndeterminate =
|
||||
checkedUsers.withoutEmail.length > 0 &&
|
||||
checkedUsers.withoutEmail.length !== usersWithFilledEmails.length;
|
||||
@ -96,33 +108,56 @@ const RowView = (props: RowViewProps) => {
|
||||
|
||||
return (
|
||||
<StyledRowContainer useReactWindow={false}>
|
||||
<StyledRow
|
||||
checked={isChecked}
|
||||
onSelect={toggleAll}
|
||||
indeterminate={isIndeterminate}
|
||||
isDisabled={usersWithFilledEmails.length === 0}
|
||||
>
|
||||
<Text
|
||||
className="row-header-title"
|
||||
color={globalColors.gray}
|
||||
fontWeight={600}
|
||||
fontSize="12px"
|
||||
>
|
||||
{t("Common:Name")}
|
||||
</Text>
|
||||
</StyledRow>
|
||||
{accountsData.map((data) => (
|
||||
<UsersRow
|
||||
t={t}
|
||||
key={data.key}
|
||||
data={data}
|
||||
sectionWidth={sectionWidth}
|
||||
toggleAccount={() => handleToggle(data)}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
isEmailOpen={openedEmailKey === data.key}
|
||||
setOpenedEmailKey={setOpenedEmailKey}
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<StyledRow
|
||||
checked={isChecked}
|
||||
onSelect={toggleAll}
|
||||
indeterminate={isIndeterminate}
|
||||
isDisabled={usersWithFilledEmails.length === 0}
|
||||
>
|
||||
<Text className="row-header-title">{t("Common:Name")}</Text>
|
||||
</StyledRow>
|
||||
{accountsData.map((data) => (
|
||||
<UsersRow
|
||||
t={t}
|
||||
key={data.key}
|
||||
data={data}
|
||||
sectionWidth={sectionWidth}
|
||||
toggleAccount={() => handleToggle(data)}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
isEmailOpen={openedEmailKey === data.key}
|
||||
setOpenedEmailKey={setOpenedEmailKey}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size={12}
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type={LinkType.action}
|
||||
isHovered
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
)}
|
||||
</StyledRowContainer>
|
||||
);
|
||||
};
|
||||
@ -134,6 +169,7 @@ export default inject<TStore>(({ importAccountsStore }) => {
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
@ -142,5 +178,6 @@ export default inject<TStore>(({ importAccountsStore }) => {
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
};
|
||||
})(observer(RowView));
|
||||
|
@ -27,7 +27,13 @@
|
||||
import { useState, useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
import { Link, LinkType } from "@docspace/shared/components/link";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { TableBody } from "@docspace/shared/components/table";
|
||||
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
|
||||
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
|
||||
import UsersTableHeader from "./UsersTableHeader";
|
||||
import UsersTableRow from "./UsersTableRow";
|
||||
import { StyledTableContainer } from "../../../../StyledDataImport";
|
||||
@ -45,13 +51,13 @@ const TableView = (props: TableViewProps) => {
|
||||
t,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
|
||||
userId,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
users,
|
||||
setSearchValue,
|
||||
} = props as AddEmailTableProps;
|
||||
const [openedEmailKey, setOpenedEmailKey] = useState<string>("");
|
||||
const tableRef = useRef(null);
|
||||
@ -67,6 +73,10 @@ const TableView = (props: TableViewProps) => {
|
||||
checkedAccountType,
|
||||
);
|
||||
|
||||
const onClearFilter = () => {
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
|
||||
const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`;
|
||||
|
||||
@ -76,44 +86,74 @@ const TableView = (props: TableViewProps) => {
|
||||
|
||||
return (
|
||||
<StyledTableContainer forwardedRef={tableRef} useReactWindow>
|
||||
<UsersTableHeader
|
||||
t={t}
|
||||
sectionWidth={sectionWidth}
|
||||
tableRef={tableRef}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={
|
||||
usersWithFilledEmails.length > 0 &&
|
||||
checkedUsers.withoutEmail.length === usersWithFilledEmails.length
|
||||
}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
<TableBody
|
||||
itemHeight={49}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
filesLength={accountsData.length}
|
||||
hasMoreFiles={false}
|
||||
itemCount={accountsData.length}
|
||||
fetchMoreFiles={() => {}}
|
||||
>
|
||||
{accountsData.map((data) => (
|
||||
<UsersTableRow
|
||||
{accountsData.length > 0 ? (
|
||||
<>
|
||||
<UsersTableHeader
|
||||
t={t}
|
||||
key={data.key}
|
||||
id={data.key}
|
||||
email={data.email || ""}
|
||||
displayName={data.displayName}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={() => toggleAccount(data, checkedAccountType)}
|
||||
isEmailOpen={openedEmailKey === data.key}
|
||||
setOpenedEmailKey={setOpenedEmailKey}
|
||||
sectionWidth={sectionWidth}
|
||||
tableRef={tableRef}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
isIndeterminate={isIndeterminate}
|
||||
isChecked={
|
||||
usersWithFilledEmails.length > 0 &&
|
||||
checkedUsers.withoutEmail.length === usersWithFilledEmails.length
|
||||
}
|
||||
toggleAll={toggleAll}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableBody
|
||||
itemHeight={49}
|
||||
useReactWindow
|
||||
infoPanelVisible={false}
|
||||
columnStorageName={columnStorageName}
|
||||
columnInfoPanelStorageName={columnInfoPanelStorageName}
|
||||
filesLength={accountsData.length}
|
||||
hasMoreFiles={false}
|
||||
itemCount={accountsData.length}
|
||||
fetchMoreFiles={() => {}}
|
||||
>
|
||||
{accountsData.map((data) => (
|
||||
<UsersTableRow
|
||||
t={t}
|
||||
key={data.key}
|
||||
id={data.key}
|
||||
email={data.email || ""}
|
||||
displayName={data.displayName}
|
||||
isChecked={isAccountChecked(data.key, checkedAccountType)}
|
||||
toggleAccount={() => toggleAccount(data, checkedAccountType)}
|
||||
isEmailOpen={openedEmailKey === data.key}
|
||||
setOpenedEmailKey={setOpenedEmailKey}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc={EmptyScreenUserReactSvgUrl}
|
||||
imageAlt="Empty Screen user image"
|
||||
headerText={t("Common:NotFoundUsers")}
|
||||
descriptionText={t("Common:NotFoundUsersDescription")}
|
||||
buttons={
|
||||
<Box displayProp="flex" alignItems="center">
|
||||
<IconButton
|
||||
className="clear-icon"
|
||||
isFill
|
||||
size={12}
|
||||
onClick={onClearFilter}
|
||||
iconName={ClearEmptyFilterSvgUrl}
|
||||
/>
|
||||
<Link
|
||||
type={LinkType.action}
|
||||
isHovered
|
||||
fontWeight="600"
|
||||
onClick={onClearFilter}
|
||||
>
|
||||
{t("Common:ClearFilter")}
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
@ -126,6 +166,7 @@ export default inject<TStore>(({ userStore, importAccountsStore }) => {
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
} = importAccountsStore;
|
||||
|
||||
return {
|
||||
@ -135,5 +176,6 @@ export default inject<TStore>(({ userStore, importAccountsStore }) => {
|
||||
toggleAccount,
|
||||
toggleAllAccounts,
|
||||
isAccountChecked,
|
||||
setSearchValue,
|
||||
};
|
||||
})(observer(TableView));
|
||||
|
@ -51,7 +51,6 @@ const REFRESH_TIMEOUT = 100;
|
||||
const AddEmailsStep = (props: AddEmailsStepProps) => {
|
||||
const {
|
||||
t,
|
||||
|
||||
incrementStep,
|
||||
decrementStep,
|
||||
users,
|
||||
@ -67,10 +66,8 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
|
||||
setWorkspace,
|
||||
setMigratingWorkspace,
|
||||
setMigrationPhase,
|
||||
|
||||
cancelUploadDialogVisible,
|
||||
setCancelUploadDialogVisible,
|
||||
|
||||
quotaCharacteristics,
|
||||
} = props as InjectedAddEmailsStepProps;
|
||||
|
||||
@ -97,8 +94,8 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
|
||||
const filteredAccounts = dataPortion.filter(
|
||||
(data) =>
|
||||
data.firstName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.displayName.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.email.toLowerCase().startsWith(searchValue.toLowerCase()),
|
||||
data.displayName?.toLowerCase().startsWith(searchValue.toLowerCase()) ||
|
||||
data.email?.toLowerCase().startsWith(searchValue.toLowerCase()),
|
||||
);
|
||||
|
||||
const handleStepIncrement = () => {
|
||||
@ -190,12 +187,15 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Text fontWeight={600} lineHeight="20px" className="mb-17">
|
||||
{t("Settings:WithoutEmailHint")}
|
||||
</Text>
|
||||
<>
|
||||
<Text fontWeight={600} lineHeight="20px" className="mb-17">
|
||||
{t("Settings:WithoutEmailHint")}
|
||||
</Text>
|
||||
{Buttons}
|
||||
</>
|
||||
)}
|
||||
|
||||
{Buttons}
|
||||
{filteredAccounts.length > 0 && Buttons}
|
||||
|
||||
{cancelUploadDialogVisible && (
|
||||
<CancelUploadDialog
|
||||
|
@ -42,7 +42,6 @@ const ImportProcessingStep = (props: ImportProcessingStepProps) => {
|
||||
const {
|
||||
t,
|
||||
migratorName,
|
||||
|
||||
incrementStep,
|
||||
setIsLoading,
|
||||
proceedFileMigration,
|
||||
@ -50,8 +49,7 @@ const ImportProcessingStep = (props: ImportProcessingStepProps) => {
|
||||
} = props as InjectedImportProcessingStepProps;
|
||||
|
||||
const [percent, setPercent] = useState(0);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
const [isVisibleProgress, setIsVisibleProgress] = useState(false);
|
||||
const [failTries, setFailTries] = useState(FAIL_TRIES);
|
||||
|
||||
const uploadInterval = useRef<number>();
|
||||
@ -59,7 +57,7 @@ const ImportProcessingStep = (props: ImportProcessingStepProps) => {
|
||||
const handleFileMigration = async () => {
|
||||
setIsLoading(true);
|
||||
setPercent(0);
|
||||
setIsVisible(true);
|
||||
setIsVisibleProgress(true);
|
||||
try {
|
||||
await proceedFileMigration(migratorName);
|
||||
|
||||
@ -78,17 +76,12 @@ const ImportProcessingStep = (props: ImportProcessingStepProps) => {
|
||||
}
|
||||
|
||||
setPercent(res.progress);
|
||||
|
||||
if (res.progress > 10) {
|
||||
setIsVisible(false);
|
||||
} else {
|
||||
setIsVisible(true);
|
||||
}
|
||||
setIsVisibleProgress(res.progress <= 10);
|
||||
|
||||
if (res.isCompleted || res.progress === 100) {
|
||||
clearInterval(uploadInterval.current);
|
||||
setIsLoading(false);
|
||||
setIsVisible(false);
|
||||
setIsVisibleProgress(false);
|
||||
setPercent(100);
|
||||
setTimeout(() => {
|
||||
incrementStep();
|
||||
@ -111,7 +104,7 @@ const ImportProcessingStep = (props: ImportProcessingStepProps) => {
|
||||
<Wrapper>
|
||||
<ProgressBar
|
||||
percent={percent}
|
||||
isInfiniteProgress={isVisible}
|
||||
isInfiniteProgress={isVisibleProgress}
|
||||
className="data-import-progress-bar"
|
||||
/>
|
||||
</Wrapper>
|
||||
|
@ -180,6 +180,7 @@ const ImportStep = (props: ImportStepProps) => {
|
||||
workspace: t("Common:ProductName"),
|
||||
sectionIcon: RoomsIcon,
|
||||
}}
|
||||
isDisabled={false}
|
||||
/>
|
||||
<ImportSection
|
||||
isChecked={importOptions.importCommonFiles}
|
||||
|
@ -86,20 +86,22 @@ const Wrapper = styled.div`
|
||||
const FileUploadContainer = styled.div`
|
||||
max-width: 350px;
|
||||
|
||||
.cancel-btn {
|
||||
@media ${mobile} {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.cancelUploadButton {
|
||||
@media ${mobile} {
|
||||
margin-bottom: 0;
|
||||
|
||||
width: auto;
|
||||
|
||||
position: fixed;
|
||||
inset-inline: 0px;
|
||||
bottom: 0px;
|
||||
|
||||
padding: 16px;
|
||||
|
||||
background: white;
|
||||
|
||||
background: ${(props) =>
|
||||
props.theme.client.settings.migration.workspaceBackground};
|
||||
gap: 0;
|
||||
}
|
||||
}
|
||||
@ -133,19 +135,15 @@ const FAIL_TRIES = 2;
|
||||
|
||||
const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
const {
|
||||
t,
|
||||
isMultipleUpload,
|
||||
migratorName,
|
||||
acceptedExtensions,
|
||||
|
||||
t,
|
||||
incrementStep,
|
||||
setWorkspace,
|
||||
|
||||
cancelUploadDialogVisible,
|
||||
setCancelUploadDialogVisible,
|
||||
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
initMigrations,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
cancelMigration,
|
||||
@ -153,9 +151,9 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
setLoadingStatus,
|
||||
files,
|
||||
setFiles,
|
||||
multipleFileUploading,
|
||||
migratingWorkspace,
|
||||
setMigratingWorkspace,
|
||||
uploadFiles,
|
||||
} = props as InjectedSelectFileStepProps;
|
||||
|
||||
const [isSaveDisabled, setIsSaveDisabled] = useState(
|
||||
@ -174,9 +172,18 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
const [chunkSize, setChunkSize] = useState(0);
|
||||
|
||||
const [failTries, setFailTries] = useState(FAIL_TRIES);
|
||||
|
||||
const uploadInterval = useRef<number>();
|
||||
|
||||
const handleError = useCallback(
|
||||
(message?: string) => {
|
||||
toastr.error(message || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setLoadingStatus("none");
|
||||
clearInterval(uploadInterval.current);
|
||||
},
|
||||
[t, setLoadingStatus],
|
||||
);
|
||||
|
||||
const poolStatus = useCallback(async () => {
|
||||
try {
|
||||
const res = await getMigrationStatus();
|
||||
@ -187,29 +194,24 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
toastr.error(t("Common:SomethingWentWrong"));
|
||||
setLoadingStatus("none");
|
||||
clearInterval(uploadInterval.current);
|
||||
handleError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.parseResult.failedArchives.length > 0 || res.error) {
|
||||
toastr.error(res.error);
|
||||
setIsFileError(true);
|
||||
setLoadingStatus("none");
|
||||
clearInterval(uploadInterval.current);
|
||||
handleError(res.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.isCompleted || res.progress === 100) {
|
||||
clearInterval(uploadInterval.current);
|
||||
if (
|
||||
|
||||
const totalUsers =
|
||||
res.parseResult.users.length +
|
||||
res.parseResult.existUsers.length +
|
||||
res.parseResult.withoutEmailUsers.length >
|
||||
0
|
||||
) {
|
||||
setUsers(res.parseResult);
|
||||
res.parseResult.existUsers.length +
|
||||
res.parseResult.withoutEmailUsers.length;
|
||||
|
||||
if (totalUsers > 0) {
|
||||
setIsBackupEmpty(false);
|
||||
setLoadingStatus("done");
|
||||
setUsers(res.parseResult);
|
||||
@ -228,15 +230,8 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
setIsInfiniteProgress(false);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
if (error.message === "Network Error") {
|
||||
setIsNetworkError(true);
|
||||
}
|
||||
toastr.error(error || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setLoadingStatus("none");
|
||||
clearInterval(uploadInterval.current);
|
||||
}
|
||||
handleError(error as string);
|
||||
setIsNetworkError(true);
|
||||
}
|
||||
}, [
|
||||
failTries,
|
||||
@ -244,49 +239,31 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
isInfiniteProgress,
|
||||
setLoadingStatus,
|
||||
setUsers,
|
||||
t,
|
||||
handleError,
|
||||
]);
|
||||
|
||||
const onUploadFile = async (file: File | File[]) => {
|
||||
try {
|
||||
if (file instanceof Array) {
|
||||
setFiles(file.map((item) => item.name));
|
||||
await multipleFileUploading(
|
||||
file,
|
||||
setProgress,
|
||||
isAbort,
|
||||
setChunk,
|
||||
startChunk,
|
||||
setChunkSize,
|
||||
chunkSize,
|
||||
);
|
||||
} else {
|
||||
setFiles([file.name]);
|
||||
await singleFileUploading(
|
||||
file,
|
||||
setProgress,
|
||||
isAbort,
|
||||
setChunk,
|
||||
startChunk,
|
||||
setChunkSize,
|
||||
chunkSize,
|
||||
);
|
||||
}
|
||||
const filesData = Array.isArray(file) ? file : [file];
|
||||
setFiles(filesData.map((item) => item.name));
|
||||
|
||||
await uploadFiles(
|
||||
filesData,
|
||||
setProgress,
|
||||
isAbort,
|
||||
setChunk,
|
||||
startChunk,
|
||||
setChunkSize,
|
||||
chunkSize,
|
||||
);
|
||||
|
||||
if (isAbort.current) return;
|
||||
|
||||
await initMigrationName(migratorName);
|
||||
await initMigrations(migratorName);
|
||||
setLoadingStatus("proceed");
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
if (error.message === "Network Error") {
|
||||
setIsNetworkError(true);
|
||||
}
|
||||
|
||||
toastr.error(error || t("Common:SomethingWentWrong"));
|
||||
setIsFileError(true);
|
||||
setLoadingStatus("none");
|
||||
}
|
||||
handleError(error as string);
|
||||
setIsNetworkError(true);
|
||||
} finally {
|
||||
isAbort.current = false;
|
||||
}
|
||||
@ -300,6 +277,7 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
|
||||
setProgress(0);
|
||||
setIsFileError(false);
|
||||
setIsBackupEmpty(false);
|
||||
setIsSaveDisabled(true);
|
||||
setLoadingStatus("upload");
|
||||
setFailTries(FAIL_TRIES);
|
||||
@ -405,6 +383,7 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
}
|
||||
accept={acceptedExtensions}
|
||||
size={InputSize.base}
|
||||
isMultiple={migratorName === "GoogleWorkspace"}
|
||||
/>
|
||||
</FileUploadContainer>
|
||||
{fileLoadingStatus === "upload" || fileLoadingStatus === "proceed" ? (
|
||||
@ -418,6 +397,7 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
<div className="cancelUploadButton">
|
||||
<Button
|
||||
size={isTablet() ? ButtonSize.medium : ButtonSize.small}
|
||||
className="cancel-btn"
|
||||
label={t("Common:CancelButton")}
|
||||
onClick={onCancel}
|
||||
scale={isMobile()}
|
||||
@ -503,8 +483,7 @@ const SelectFileStep = (props: SelectFileStepProps) => {
|
||||
|
||||
export default inject<TStore>(({ dialogsStore, importAccountsStore }) => {
|
||||
const {
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
initMigrations,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
fileLoadingStatus,
|
||||
@ -514,16 +493,15 @@ export default inject<TStore>(({ dialogsStore, importAccountsStore }) => {
|
||||
incrementStep,
|
||||
files,
|
||||
setFiles,
|
||||
multipleFileUploading,
|
||||
migratingWorkspace,
|
||||
setMigratingWorkspace,
|
||||
uploadFiles,
|
||||
} = importAccountsStore;
|
||||
const { cancelUploadDialogVisible, setCancelUploadDialogVisible } =
|
||||
dialogsStore;
|
||||
|
||||
return {
|
||||
initMigrationName,
|
||||
singleFileUploading,
|
||||
initMigrations,
|
||||
getMigrationStatus,
|
||||
setUsers,
|
||||
fileLoadingStatus,
|
||||
@ -535,8 +513,8 @@ export default inject<TStore>(({ dialogsStore, importAccountsStore }) => {
|
||||
incrementStep,
|
||||
files,
|
||||
setFiles,
|
||||
multipleFileUploading,
|
||||
migratingWorkspace,
|
||||
setMigratingWorkspace,
|
||||
uploadFiles,
|
||||
};
|
||||
})(observer(SelectFileStep));
|
||||
|
@ -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 styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
import { Text } from "@docspace/shared/components/text";
|
||||
import { Box } from "@docspace/shared/components/box";
|
||||
import { RowContent } from "@docspace/shared/components/row-content";
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
@ -142,7 +142,6 @@ const RowView = (props: RowViewProps) => {
|
||||
t,
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
|
||||
checkedUsers,
|
||||
withEmailUsers,
|
||||
toggleAccount,
|
||||
|
@ -196,9 +196,12 @@ const SelectUsersStep = (props: SelectUsersStepProps) => {
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Text fontWeight={600} lineHeight="20px" className="mb-17">
|
||||
{t("Settings:AddEmailsWarning")}
|
||||
</Text>
|
||||
<>
|
||||
<Text fontWeight={600} lineHeight="20px" className="mb-17">
|
||||
{t("Settings:AddEmailsWarning")}
|
||||
</Text>
|
||||
{Buttons}
|
||||
</>
|
||||
)}
|
||||
|
||||
{filteredAccounts.length > 0 && Buttons}
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { tablet } from "@docspace/shared/utils/device";
|
||||
import styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
@ -56,7 +56,7 @@ const StyledRowContainer = styled(RowContainer)`
|
||||
margin-inline-start: -16px;
|
||||
width: 100%;
|
||||
|
||||
margin-top: -31.5px;
|
||||
margin-top: -32.5px;
|
||||
top: 53px;
|
||||
margin-bottom: -29.5px;
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
import { useRef } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Base, globalColors } from "@docspace/shared/themes";
|
||||
import styled, { css } from "styled-components";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { EmptyScreenContainer } from "@docspace/shared/components/empty-screen-container";
|
||||
import { IconButton } from "@docspace/shared/components/icon-button";
|
||||
@ -97,7 +97,6 @@ const TableView = (props: TypeSelectTableViewProps) => {
|
||||
sectionWidth,
|
||||
accountsData,
|
||||
typeOptions,
|
||||
|
||||
userId,
|
||||
checkedUsers,
|
||||
toggleAccount,
|
||||
|
@ -27,9 +27,9 @@
|
||||
import { useEffect, useCallback } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import useViewEffect from "SRC_DIR/Hooks/useViewEffect";
|
||||
import { setDocumentTitle } from "SRC_DIR/helpers/utils";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { setDocumentTitle } from "SRC_DIR/helpers/utils";
|
||||
import useViewEffect from "SRC_DIR/Hooks/useViewEffect";
|
||||
|
||||
import { DataImportProps, InjectedDataImportProps } from "./types";
|
||||
|
||||
@ -43,7 +43,6 @@ const DataImport = (props: DataImportProps) => {
|
||||
viewAs,
|
||||
setViewAs,
|
||||
currentDeviceType,
|
||||
|
||||
getMigrationStatus,
|
||||
isMigrationInit,
|
||||
setUsers,
|
||||
@ -74,15 +73,14 @@ const DataImport = (props: DataImportProps) => {
|
||||
|
||||
const { parseResult, error, isCompleted } = response;
|
||||
|
||||
if (
|
||||
error ||
|
||||
parseResult.failedArchives.length > 0 ||
|
||||
const isErrorOrFailedParse = error || parseResult.failedArchives.length > 0;
|
||||
const isNoUsersParsed =
|
||||
parseResult.users.length +
|
||||
parseResult.existUsers.length +
|
||||
parseResult.withoutEmailUsers.length ===
|
||||
0
|
||||
)
|
||||
return;
|
||||
0;
|
||||
|
||||
if (isErrorOrFailedParse || isNoUsersParsed) return;
|
||||
|
||||
if (parseResult.operation === "parse") {
|
||||
setWorkspace(parseResult.migratorName);
|
||||
@ -140,7 +138,7 @@ const DataImport = (props: DataImportProps) => {
|
||||
};
|
||||
|
||||
export default inject<TStore>(
|
||||
({ authStore, settingsStore, setup, importAccountsStore }) => {
|
||||
({ settingsStore, setup, importAccountsStore }) => {
|
||||
const {
|
||||
getMigrationStatus,
|
||||
isMigrationInit,
|
||||
@ -160,7 +158,6 @@ export default inject<TStore>(
|
||||
viewAs,
|
||||
setViewAs,
|
||||
currentDeviceType,
|
||||
|
||||
getMigrationStatus,
|
||||
isMigrationInit,
|
||||
setUsers,
|
||||
|
@ -59,8 +59,7 @@ export interface InjectedSelectFileStepProps extends SelectFileStepProps {
|
||||
setWorkspace: TStore["importAccountsStore"]["setWorkspace"];
|
||||
cancelUploadDialogVisible: TStore["dialogsStore"]["cancelUploadDialogVisible"];
|
||||
setCancelUploadDialogVisible: TStore["dialogsStore"]["setCancelUploadDialogVisible"];
|
||||
initMigrationName: TStore["importAccountsStore"]["initMigrationName"];
|
||||
singleFileUploading: TStore["importAccountsStore"]["singleFileUploading"];
|
||||
initMigrations: TStore["importAccountsStore"]["initMigrations"];
|
||||
getMigrationStatus: TStore["importAccountsStore"]["getMigrationStatus"];
|
||||
setUsers: TStore["importAccountsStore"]["setUsers"];
|
||||
fileLoadingStatus: TStore["importAccountsStore"]["fileLoadingStatus"];
|
||||
@ -68,15 +67,14 @@ export interface InjectedSelectFileStepProps extends SelectFileStepProps {
|
||||
cancelMigration: TStore["importAccountsStore"]["cancelMigration"];
|
||||
files: TStore["importAccountsStore"]["files"];
|
||||
setFiles: TStore["importAccountsStore"]["setFiles"];
|
||||
multipleFileUploading: TStore["importAccountsStore"]["multipleFileUploading"];
|
||||
migratingWorkspace: TStore["importAccountsStore"]["migratingWorkspace"];
|
||||
setMigratingWorkspace: TStore["importAccountsStore"]["setMigratingWorkspace"];
|
||||
uploadFiles: TStore["importAccountsStore"]["uploadFiles"];
|
||||
}
|
||||
|
||||
export interface DataImportProps {}
|
||||
|
||||
export interface InjectedDataImportProps extends DataImportProps {
|
||||
setDocumentTitle: TStore["authStore"]["setDocumentTitle"];
|
||||
getMigrationStatus: TStore["importAccountsStore"]["getMigrationStatus"];
|
||||
viewAs: TStore["setup"]["viewAs"];
|
||||
setViewAs: TStore["setup"]["setViewAs"];
|
||||
@ -102,8 +100,6 @@ export interface InjectedWorkspaceProps extends WorkspaceProps {
|
||||
migrationPhase: TStore["importAccountsStore"]["migrationPhase"];
|
||||
isMigrationInit: TStore["importAccountsStore"]["isMigrationInit"];
|
||||
setIsMigrationInit: TStore["importAccountsStore"]["setIsMigrationInit"];
|
||||
|
||||
setDocumentTitle: TStore["authStore"]["setDocumentTitle"];
|
||||
}
|
||||
|
||||
export interface LayoutProps {
|
||||
@ -172,6 +168,7 @@ export interface InjectedTableViewProps extends TableViewProps {
|
||||
|
||||
export interface AddEmailTableProps extends InjectedTableViewProps {
|
||||
users: TStore["importAccountsStore"]["users"];
|
||||
setSearchValue: TStore["importAccountsStore"]["setSearchValue"];
|
||||
}
|
||||
|
||||
export interface SelectUserTableProps extends InjectedTableViewProps {
|
||||
@ -223,6 +220,7 @@ export interface AddEmailRowProps extends RowViewProps {
|
||||
toggleAccount: TStore["importAccountsStore"]["toggleAccount"];
|
||||
toggleAllAccounts: TStore["importAccountsStore"]["toggleAllAccounts"];
|
||||
isAccountChecked: TStore["importAccountsStore"]["isAccountChecked"];
|
||||
setSearchValue: TStore["importAccountsStore"]["setSearchValue"];
|
||||
}
|
||||
|
||||
export interface UsersRowProps {
|
||||
|
@ -27,6 +27,7 @@
|
||||
import axios from "axios";
|
||||
import { uploadFile } from "@docspace/shared/api/files";
|
||||
import { combineUrl } from "@docspace/shared/utils/combineUrl";
|
||||
import { toastr } from "@docspace/shared/components/toast";
|
||||
import { makeAutoObservable, runInAction } from "mobx";
|
||||
import {
|
||||
migrationList,
|
||||
@ -328,8 +329,8 @@ class ImportAccountsStore {
|
||||
};
|
||||
};
|
||||
|
||||
multipleFileUploading = async (
|
||||
files: File[],
|
||||
uploadFiles = async (
|
||||
filesData: File | File[],
|
||||
setProgress: (progress: number) => void,
|
||||
isAbort: React.MutableRefObject<boolean>,
|
||||
setChunk: React.Dispatch<React.SetStateAction<number>>,
|
||||
@ -338,29 +339,34 @@ class ImportAccountsStore {
|
||||
chunkSize: number,
|
||||
) => {
|
||||
let chunk = 0;
|
||||
const location = combineUrl(
|
||||
window.location.origin,
|
||||
"migrationFileUpload.ashx",
|
||||
);
|
||||
|
||||
try {
|
||||
const location = combineUrl(
|
||||
window.location.origin,
|
||||
"migrationFileUpload.ashx",
|
||||
);
|
||||
const requestsDataArray: { formData: FormData; fileName: string }[] = [];
|
||||
let chunkUploadSize = chunkSize;
|
||||
|
||||
let chunkUploadSize = 0;
|
||||
if (!chunkSize) {
|
||||
const res = await axios.post<{
|
||||
Success: boolean;
|
||||
ChunkSize: number;
|
||||
Message: string;
|
||||
}>(`${location}?Init=${startChunk === 0}`);
|
||||
|
||||
if (chunkSize) {
|
||||
chunkUploadSize = chunkSize;
|
||||
} else {
|
||||
const res: { data: { ChunkSize: number } } = await axios.post(
|
||||
`${location}?Init=${startChunk === 0}`,
|
||||
);
|
||||
if (!res.data.Success) {
|
||||
toastr.error(res.data.Message);
|
||||
throw new Error(res.data.Message);
|
||||
}
|
||||
|
||||
chunkUploadSize = res.data.ChunkSize;
|
||||
setChunkSize(chunkUploadSize);
|
||||
}
|
||||
|
||||
if (!chunkUploadSize) return;
|
||||
if (!chunkUploadSize || isAbort.current) return;
|
||||
|
||||
if (isAbort.current) return;
|
||||
const requestsDataArray: { formData: FormData; fileName: string }[] = [];
|
||||
const files = Array.isArray(filesData) ? filesData : [filesData];
|
||||
|
||||
const chunksNumber = files
|
||||
.map((file) => Math.ceil(file.size / chunkUploadSize))
|
||||
@ -396,74 +402,9 @@ class ImportAccountsStore {
|
||||
setProgress(Math.ceil(progress));
|
||||
chunk += 1;
|
||||
}
|
||||
} catch (e) {
|
||||
setChunk(chunk);
|
||||
}
|
||||
};
|
||||
|
||||
singleFileUploading = async (
|
||||
file: File,
|
||||
setProgress: (progress: number) => void,
|
||||
isAbort: React.MutableRefObject<boolean>,
|
||||
setChunk: React.Dispatch<React.SetStateAction<number>>,
|
||||
startChunk: number,
|
||||
setChunkSize: React.Dispatch<React.SetStateAction<number>>,
|
||||
chunkSize: number,
|
||||
) => {
|
||||
let chunk = 0;
|
||||
try {
|
||||
const location = combineUrl(
|
||||
window.location.origin,
|
||||
"migrationFileUpload.ashx",
|
||||
);
|
||||
|
||||
let chunkUploadSize = 0;
|
||||
|
||||
if (chunkSize) {
|
||||
chunkUploadSize = chunkSize;
|
||||
} else {
|
||||
const res: { data: { ChunkSize: number } } = await axios.post(
|
||||
`${location}?Init=${startChunk === 0}`,
|
||||
);
|
||||
|
||||
chunkUploadSize = res.data.ChunkSize;
|
||||
setChunkSize(chunkUploadSize);
|
||||
}
|
||||
|
||||
if (!chunkUploadSize) return;
|
||||
|
||||
const requestsDataArray = [];
|
||||
|
||||
const chunks = Math.ceil(file.size / chunkUploadSize);
|
||||
|
||||
if (isAbort.current) return;
|
||||
|
||||
while (chunk < chunks) {
|
||||
const offset = chunk * chunkUploadSize;
|
||||
const formData = new FormData();
|
||||
formData.append("file", file.slice(offset, offset + chunkUploadSize));
|
||||
requestsDataArray.push(formData);
|
||||
chunk += 1;
|
||||
}
|
||||
|
||||
chunk = startChunk || 0;
|
||||
while (
|
||||
chunk < chunks &&
|
||||
(this.fileLoadingStatus === "upload" ||
|
||||
this.fileLoadingStatus === "proceed")
|
||||
) {
|
||||
if (isAbort.current) return;
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await uploadFile(
|
||||
`${location}?Name=${file.name}`,
|
||||
requestsDataArray[chunk],
|
||||
);
|
||||
const progress = (chunk / chunks) * 100;
|
||||
setProgress(Math.ceil(progress));
|
||||
chunk += 1;
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
setChunk(chunk);
|
||||
throw new Error(error as string);
|
||||
}
|
||||
};
|
||||
|
||||
@ -481,7 +422,7 @@ class ImportAccountsStore {
|
||||
};
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
initMigrationName = (name: TWorkspaceService) => {
|
||||
initMigrations = (name: TWorkspaceService) => {
|
||||
return initMigration(name);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user