transferring changes from Google to NextCloud

This commit is contained in:
Elyor Djalilov 2023-09-29 18:14:24 +05:00
parent 851a305d85
commit b92a25719d
24 changed files with 1105 additions and 431 deletions

View File

@ -8,7 +8,7 @@
"AccessRightsUsersFromList": "{{users}} from the list",
"AccountsWithoutEmails": "We found <1>{{users}} users</1> without emails. You can add necessary data to their accounts on the next step.",
"AddEmails": "Add emails to incomplete accounts",
"AddEmailsWarning": "You don<EFBFBD>t have users with emails. Please proceed to the next step to add them.",
"AddEmailsWarning": "You don't have users with emails. Please proceed to the next step to add them.",
"AddAllowedIP": "Add allowed IP address",
"AdditionalResources": "Additional resources",
"AdditionalResourcesDescription": "Choose whether you want to display links to additional resources in DocSpace menu.",

View File

@ -6,6 +6,7 @@ import ModalDialogContainer from "../ModalDialogContainer";
const CancelUploadDialog = ({
isFifthStep,
isSixthStep,
visible,
onClose,
loading,
@ -14,12 +15,13 @@ const CancelUploadDialog = ({
const { t } = useTranslation(["Settings", "Common"]);
const navigate = useNavigate();
const modalBodyText = isFifthStep
? t("Settings:WantToCancelDataImport")
: t("Settings:WantToCancelUpload");
const modalBodyText =
isFifthStep || isSixthStep
? t("Settings:WantToCancelDataImport")
: t("Settings:WantToCancelUpload");
const onCancelProcess = () => {
if (isFifthStep) {
if (isFifthStep || isSixthStep) {
navigate(-1);
cancelMigration();
} else {

View File

@ -1,11 +1,11 @@
import React, { useState } from "react";
import { useState } from "react";
import { inject, observer } from "mobx-react";
import styled from "styled-components";
import ImportSection from "../../../sub-components/ImportSection";
import SaveCancelButtons from "@docspace/components/save-cancel-buttons";
import PeopleIcon from "PUBLIC_DIR/images/catalog.accounts.react.svg";
import UserIcon from "PUBLIC_DIR/images/catalog.user.react.svg";
import SaveCancelButtons from "@docspace/components/save-cancel-buttons";
const SectionsWrapper = styled.div`
display: flex;
@ -17,27 +17,28 @@ const SectionsWrapper = styled.div`
}
`;
const FifthStep = ({ t, incrementStep, decrementStep }) => {
const [isChecked, setIsChecked] = useState({
users: true,
pFiles: true,
sFiles: true,
});
const FifthStep = ({
t,
incrementStep,
decrementStep,
toggles,
setToggles,
}) => {
const [isChecked, setIsChecked] = useState(true);
const onChange = (name) => {
setIsChecked((prevIsChecked) => ({
...prevIsChecked,
[name]: !prevIsChecked[name],
}));
const onChange = (e, name) => {
const checked = e.target.checked;
setToggles({ [name]: checked });
};
const users = t("Settings:Employees")[0].toUpperCase() + t("Settings:Employees").slice(1);
const users =
t("Settings:Employees")[0].toUpperCase() + t("Settings:Employees").slice(1);
return (
<SectionsWrapper>
<ImportSection
isChecked={isChecked.users}
onChange={() => onChange("users")}
isChecked={isChecked}
onChange={() => setIsChecked((prev) => !prev)}
sectionName={users}
description={t("Settings:UsersSectionDescription")}
exportSection={{ sectionName: users, workspace: "NextCloud" }}
@ -49,10 +50,12 @@ const FifthStep = ({ t, incrementStep, decrementStep }) => {
isDisabled
/>
<ImportSection
isChecked={isChecked.pFiles}
onChange={() => onChange("pFiles")}
isChecked={toggles.importPersonalFiles}
onChange={(e) => onChange(e, "importPersonalFiles")}
sectionName={t("Settings:PersonalFiles")}
description={t("Settings:PersonalFilesDescription", { serviceName: "Nextcloud" })}
description={t("Settings:PersonalFilesDescription", {
serviceName: "Nextcloud",
})}
exportSection={{
sectionName: t("Settings:UsersFiles"),
workspace: "NextCloud",
@ -64,10 +67,12 @@ const FifthStep = ({ t, incrementStep, decrementStep }) => {
}}
/>
<ImportSection
isChecked={isChecked.sFiles}
onChange={() => onChange("sFiles")}
isChecked={toggles.importSharedFiles}
onChange={(e) => onChange(e, "importSharedFiles")}
sectionName={t("Settings:SharedFiles")}
description={t("Settings:SharedFilesDescription", { serviceName: "Nextcloud" })}
description={t("Settings:SharedFilesDescription", {
serviceName: "Nextcloud",
})}
exportSection={{
sectionName: t("Settings:SharedFiles"),
workspace: "NextCloud",
@ -91,4 +96,12 @@ const FifthStep = ({ t, incrementStep, decrementStep }) => {
</SectionsWrapper>
);
};
export default FifthStep;
export default inject(({ importAccountsStore }) => {
const { toggles, setToggles } = importAccountsStore;
return {
toggles,
setToggles,
};
})(observer(FifthStep));

View File

@ -1,16 +1,14 @@
import React, { useState } from "react";
import { useState } from "react";
import { inject, observer } from "mobx-react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
import styled from "styled-components";
import { inject, observer } from "mobx-react";
import Text from "@docspace/components/text";
import FileInput from "@docspace/components/file-input";
import SaveCancelButtons from "@docspace/components/save-cancel-buttons";
import ProgressBar from "@docspace/components/progress-bar";
import Button from "@docspace/components/button";
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
import { useSearchParams } from "react-router-dom";
import FileInput from "@docspace/components/file-input";
import ProgressBar from "@docspace/components/progress-bar";
import SaveCancelButtons from "@docspace/components/save-cancel-buttons";
const Wrapper = styled.div`
max-width: 350px;
@ -43,19 +41,21 @@ const FirstStep = ({
t,
incrementStep,
decrementStep,
cancelUploadDialogVisible,
setCancelUploadDialogVisible,
cancelDialogVisble,
setCancelDialogVisbile,
initMigrationName,
singleFileUploading,
getMigrationStatus,
setUsers,
setData,
isFileLoading,
setIsFileLoading,
cancelMigration,
}) => {
const [isSaveDisabled, setIsSaveDisabled] = useState(false);
const [searchParams] = useSearchParams();
const [progress, setProgress] = useState(0);
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const onUploadFile = async (file) => {
await singleFileUploading(file, setProgress);
@ -64,12 +64,12 @@ const FirstStep = ({
const res = await getMigrationStatus();
if (!res || res.parseResult.failedArchives.length > 0) {
console.error("something went wrong");
setIsFileLoading(false);
clearInterval(interval);
} else if (res.isCompleted) {
setIsFileLoading(false);
clearInterval(interval);
setData(res);
setUsers(res);
setIsSaveDisabled(true);
}
@ -87,31 +87,44 @@ const FirstStep = ({
};
const onCancel = () => {
setCancelUploadDialogVisible(true);
setCancelDialogVisbile(true);
setProgress(0);
setIsFileLoading(false);
};
return (
<Wrapper>
<Text className="choose-backup-file">{t("Settings:ChooseBackupFile")}</Text>
<Text className="choose-backup-file">
{t("Settings:ChooseBackupFile")}
</Text>
<FileInput
scale
onInput={onSelectFile}
className="upload-backup-input"
placeholder={t("Settings:BackupFile")}
scale
isDisabled={isFileLoading}
accept=".zip"
/>
{isFileLoading ? (
<>
<Text className="select-file-progress-text">{t("Settings:BackupFileUploading")}</Text>
<ProgressBar percent={progress} className="select-file-progress-bar" />
<Button size="small" label={t("Common:CancelButton")} onClick={onCancel} />
<Text className="select-file-progress-text">
{t("Settings:BackupFileUploading")}
</Text>
<ProgressBar
percent={progress}
className="select-file-progress-bar"
/>
<Button
size="small"
label={t("Common:CancelButton")}
onClick={onCancel}
/>
</>
) : (
<SaveCancelButtons
className="upload-back-buttons"
onSaveClick={incrementStep}
onCancelClick={decrementStep}
onCancelClick={() => navigate(-1)}
saveButtonLabel={t("Settings:UploadToServer")}
cancelButtonLabel={t("Common:Back")}
displaySettings
@ -120,11 +133,12 @@ const FirstStep = ({
/>
)}
{cancelUploadDialogVisible && (
{cancelDialogVisble && (
<CancelUploadDialog
visible={cancelUploadDialogVisible}
visible={cancelDialogVisble}
loading={isFileLoading}
onClose={() => setCancelUploadDialogVisible(false)}
onClose={() => setCancelDialogVisbile(false)}
cancelMigration={cancelMigration}
/>
)}
</Wrapper>
@ -137,19 +151,24 @@ export default inject(({ dialogsStore, importAccountsStore }) => {
singleFileUploading,
getMigrationStatus,
setUsers,
setData,
isFileLoading,
setIsFileLoading,
cancelMigration,
} = importAccountsStore;
const { cancelUploadDialogVisible, setCancelUploadDialogVisible } = dialogsStore;
const { cancelUploadDialogVisible, setCancelUploadDialogVisible } =
dialogsStore;
return {
setUsers,
initMigrationName,
singleFileUploading,
getMigrationStatus,
initMigrationName,
cancelUploadDialogVisible,
setCancelUploadDialogVisible,
setUsers,
setData,
isFileLoading,
setIsFileLoading,
cancelMigration,
cancelDialogVisble: cancelUploadDialogVisible,
setCancelDialogVisbile: setCancelUploadDialogVisible,
};
})(observer(FirstStep));

View File

@ -1,9 +1,9 @@
import React, { useRef } from "react";
import { useRef } from "react";
import Row from "@docspace/components/row";
import UsersRowContent from "./UsersRowContent";
const UserskRow = (props) => {
const { t, data, sectionWidth, isChecked, toggleAccount } = props;
const { data, sectionWidth, typeOptions, isChecked, toggleAccount } = props;
const roleSelectorRef = useRef();
@ -18,15 +18,20 @@ const UserskRow = (props) => {
return (
<Row
sectionWidth={sectionWidth}
key={data.key}
data={data}
checked={isChecked}
checkbox
onClick={handleAccountToggle}>
checkbox={isChecked}
onClick={handleAccountToggle}
contextButtonSpacerWidth="0"
>
<UsersRowContent
t={t}
id={data.key}
sectionWidth={sectionWidth}
displayName={data.displayName}
email={data.email}
type={data.userType}
typeOptions={typeOptions}
roleSelectorRef={roleSelectorRef}
/>
</Row>

View File

@ -1,9 +1,10 @@
import React, { useState } from "react";
import { inject, observer } from "mobx-react";
import styled from "styled-components";
import Text from "@docspace/components/text";
import RowContent from "@docspace/components/row-content";
import AccessRightSelect from "@docspace/components/access-right-select";
import Text from "@docspace/components/text";
import Box from "@docspace/components/box";
import RowContent from "@docspace/components/row-content";
import ComboBox from "@docspace/components/combobox";
const StyledRowContent = styled(RowContent)`
display: flex;
@ -54,22 +55,22 @@ const StyledRowContent = styled(RowContent)`
}
`;
const UsersRowContent = ({ t, sectionWidth, displayName, email, roleSelectorRef }) => {
const data = [
{
key: "role-DocSpace-admin",
label: t("Settings:DocSpaceAdmin"),
},
{
key: "role-Room-admin",
label: t("Settings:RoomAdmin"),
},
{
key: "role-Power-user",
label: t("Settings:PowerUser"),
},
];
const [selectedType, setSelectedType] = useState(data[2]);
const UsersRowContent = ({
id,
sectionWidth,
displayName,
email,
typeOptions,
roleSelectorRef,
type,
changeType,
}) => {
const onSelectUser = (e) => {
changeType(id, e.key);
};
const selectedOption =
typeOptions.find((option) => option.key === type) || {};
return (
<StyledRowContent sectionWidth={sectionWidth}>
@ -82,18 +83,26 @@ const UsersRowContent = ({ t, sectionWidth, displayName, email, roleSelectorRef
</Text>
</div>
<div ref={roleSelectorRef}>
<AccessRightSelect
accessOptions={data}
selectedOption={selectedType}
scaledOptions={false}
scaled={false}
<ComboBox
className="user-type"
selectedOption={selectedOption}
options={typeOptions}
onSelect={onSelectUser}
scaled
size="content"
displaySelectedOption
modernView
manualWidth="fit-content"
className="role-type-selector"
onSelect={setSelectedType}
/>
</div>
</StyledRowContent>
);
};
export default UsersRowContent;
export default inject(({ importAccountsStore }) => {
const { changeType } = importAccountsStore;
return {
changeType,
};
})(observer(UsersRowContent));

View File

@ -4,21 +4,77 @@ import { isMobile } from "react-device-detect";
import { tablet } from "@docspace/components/utils/device";
import styled from "styled-components";
import RowContainer from "@docspace/components/row-container";
import UsersRow from "./UsersRow";
import EmptyScreenContainer from "@docspace/components/empty-screen-container";
import IconButton from "@docspace/components/icon-button";
import Link from "@docspace/components/link";
import Box from "@docspace/components/box";
import TableGroupMenu from "@docspace/components/table-container/TableGroupMenu";
import RowContainer from "@docspace/components/row-container";
import Row from "@docspace/components/row";
import Text from "@docspace/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 { mockData } from "../../mockData";
// import { mockData } from "../../mockData";
const StyledRowContainer = styled(RowContainer)`
margin: 20px 0;
margin: 0 0 20px;
.table-group-menu {
height: 60px;
position: relative;
z-index: 201;
left: -20px;
top: 25px;
width: 100%;
.table-container_group-menu {
padding: 0px 20px;
border-image-slice: 0;
box-shadow: rgba(4, 15, 27, 0.07) 0px 15px 20px;
}
.table-container_group-menu-checkbox {
margin-left: 7px;
}
.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;
@ -29,18 +85,30 @@ const StyledRow = styled(Row)`
const RowView = (props) => {
const {
t,
users,
sectionWidth,
viewAs,
setViewAs,
sectionWidth,
accountsData,
typeOptions,
checkedAccounts,
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
onCheckAccounts,
setSearchValue,
} = props;
const rowRef = useRef(null);
const toggleAll = (e) => toggleAllAccounts({ target: { checked: !e.target.checked } }, mockData);
const toggleAll = (checked) => {
onCheckAccounts(checked, users);
};
const onClearFilter = () => {
setSearchValue("");
};
const isIndeterminate =
checkedAccounts.length > 0 && checkedAccounts.length !== users.length;
useEffect(() => {
if (viewAs !== "table" && viewAs !== "row") return;
@ -50,32 +118,79 @@ const RowView = (props) => {
} else {
viewAs !== "table" && setViewAs("table");
}
return cleanCheckedAccounts;
}, [sectionWidth]);
const headerMenu = [
{
id: "change-type",
key: "change-type",
label: t("ChangeUserTypeDialog:ChangeUserTypeButton"),
disabled: false,
withDropDown: true,
options: typeOptions,
iconUrl: ChangeTypeReactSvgUrl,
},
];
return (
<StyledRowContainer useReactWindow={false}>
<StyledRow
sectionWidth={sectionWidth}
checkbox
checked={checkedAccounts.length === mockData.length}
onClick={toggleAll}
indeterminate={checkedAccounts.length > 0 && checkedAccounts.length !== mockData.length}>
<Text color="#a3a9ae" fontWeight={600} fontSize="12px">
{t("Common:Name")}
</Text>
</StyledRow>
{accountsData.map((data) => (
<UsersRow
t={t}
key={data.id}
data={data}
sectionWidth={sectionWidth}
toggleAccount={() => toggleAccount(data.id)}
isChecked={isAccountChecked(data.id)}
<StyledRowContainer forwardedRef={rowRef} useReactWindow={false}>
{checkedAccounts.length > 0 && (
<div className="table-group-menu">
<TableGroupMenu
sectionWidth={sectionWidth}
headerMenu={headerMenu}
withoutInfoPanelToggler
withComboBox={false}
isIndeterminate={isIndeterminate}
isChecked={checkedAccounts.length === users.length}
onChange={toggleAll}
/>
</div>
)}
{accountsData.length > 0 ? (
<>
<StyledRow key="Name" sectionWidth={sectionWidth} onClick={toggleAll}>
<Text className="row-header-title">{t("Common:Name")}</Text>
</StyledRow>
{users.map((data) => (
<UsersRow
key={data.key}
data={data}
sectionWidth={sectionWidth}
typeOptions={typeOptions}
isChecked={isAccountChecked(data.key)}
toggleAccount={() => toggleAccount(data.key)}
/>
))}
</>
) : (
<EmptyScreenContainer
imageSrc={EmptyScreenUserReactSvgUrl}
imageAlt="Empty Screen user image"
headerText={t("People:NotFoundUsers")}
descriptionText={t("People:NotFoundUsersDesc")}
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>
);
};
@ -83,20 +198,24 @@ const RowView = (props) => {
export default inject(({ setup, importAccountsStore }) => {
const { viewAs, setViewAs } = setup;
const {
users,
checkedAccounts,
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
onCheckAccounts,
setSearchValue,
} = importAccountsStore;
return {
users,
viewAs,
setViewAs,
checkedAccounts,
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
onCheckAccounts,
setSearchValue,
};
})(observer(RowView));

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import { useState, useEffect } from "react";
import { inject, observer } from "mobx-react";
import TableHeader from "@docspace/components/table-container/TableHeader";
@ -81,8 +81,8 @@ const UsersTableHeader = (props) => {
setColumns((prevColumns) =>
prevColumns.map((item, index) =>
index === columnIndex ? { ...item, enable: !item.enable } : item,
),
index === columnIndex ? { ...item, enable: !item.enable } : item
)
);
const tableColumns = columns.map((c) => c.enable && c.key);

View File

@ -1,12 +1,12 @@
import React, { useState, useRef } from "react";
import { useRef } from "react";
import { inject, observer } from "mobx-react";
import styled from "styled-components";
import TableRow from "@docspace/components/table-container/TableRow";
import TableCell from "@docspace/components/table-container/TableCell";
import AccessRightSelect from "@docspace/components/access-right-select";
import Text from "@docspace/components/text";
import Checkbox from "@docspace/components/checkbox";
import ComboBox from "@docspace/components/combobox";
const StyledTableRow = styled(TableRow)`
.table-container_cell {
@ -57,24 +57,24 @@ const StyledTableRow = styled(TableRow)`
}
`;
const UsersTableRow = ({ t, displayName, email, isChecked, toggleAccount }) => {
const data = [
{
key: "DocSpaceAdmin",
label: t("Common:DocSpaceAdmin"),
},
{
key: "RoomAdmin",
label: t("Common:RoomAdmin"),
},
{
key: "PowerUser",
label: t("Common:PowerUser"),
},
];
const UsersTableRow = ({
id,
displayName,
email,
typeOptions,
isChecked,
toggleAccount,
type,
changeType,
}) => {
const roleSelectorRef = useRef();
const [selectedType, setSelectedType] = useState(data[2]);
const onSelectUser = (e) => {
changeType(id, e.key);
};
const selectedOption =
typeOptions.find((option) => option.key === type) || {};
const handleAccountToggle = (e) => {
e.preventDefault();
@ -87,7 +87,7 @@ const UsersTableRow = ({ t, displayName, email, isChecked, toggleAccount }) => {
return (
<StyledTableRow checked={isChecked} onClick={handleAccountToggle}>
<TableCell>
<Checkbox onChange={handleAccountToggle} isChecked={isChecked} />
<Checkbox isChecked={isChecked} onChange={handleAccountToggle} />
<Text fontWeight={600} className="textOverflow">
{displayName}
</Text>
@ -95,19 +95,26 @@ const UsersTableRow = ({ t, displayName, email, isChecked, toggleAccount }) => {
<TableCell>
<div ref={roleSelectorRef}>
<AccessRightSelect
accessOptions={data}
selectedOption={selectedType}
scaledOptions={false}
scaled={false}
<ComboBox
className="user-type"
selectedOption={selectedOption}
options={typeOptions}
onSelect={onSelectUser}
scaled
size="content"
displaySelectedOption
modernView
manualWidth="fit-content"
className="role-type-selector"
onSelect={setSelectedType}
/>
</div>
</TableCell>
<TableCell>
<Text lineHeight="20px" fontWeight={600} color="#A3A9AE" className="textOverflow">
<Text
lineHeight="20px"
fontWeight={600}
color="#A3A9AE"
className="textOverflow"
>
{email}
</Text>
</TableCell>
@ -115,4 +122,10 @@ const UsersTableRow = ({ t, displayName, email, isChecked, toggleAccount }) => {
);
};
export default UsersTableRow;
export default inject(({ importAccountsStore }) => {
const { changeType } = importAccountsStore;
return {
changeType,
};
})(observer(UsersTableRow));

View File

@ -3,23 +3,53 @@ import { inject, observer } from "mobx-react";
import { isMobile } from "react-device-detect";
import { Base } from "@docspace/components/themes";
import styled from "styled-components";
import UsersTableHeader from "./UsersTableHeader";
import UsersTableRow from "./UsersTableRow";
import EmptyScreenContainer from "@docspace/components/empty-screen-container";
import IconButton from "@docspace/components/icon-button";
import Link from "@docspace/components/link";
import Box from "@docspace/components/box";
import TableGroupMenu from "@docspace/components/table-container/TableGroupMenu";
import TableContainer from "@docspace/components/table-container/TableContainer";
import TableBody from "@docspace/components/table-container/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";
import { mockData } from "../../mockData";
const TABLE_VERSION = "6";
const COLUMNS_SIZE = `nextcloudFourthColumnsSize_ver-${TABLE_VERSION}`;
const INFO_PANEL_COLUMNS_SIZE = `infoPanelNextcloudFourthColumnsSize_ver-${TABLE_VERSION}`;
// import { mockData } from "../../mockData";
const StyledTableContainer = styled(TableContainer)`
margin: 0.5px 0px 20px;
margin: 0 0 20px;
.table-group-menu {
height: 69px;
position: relative;
z-index: 201;
left: -20px;
top: 28px;
width: 100%;
.table-container_group-menu {
border-image-slice: 0;
border-image-source: none;
border-bottom: ${(props) =>
props.theme.client.settings.migration.workspaceBorder};
box-shadow: rgba(4, 15, 27, 0.07) 0px 15px 20px;
}
.table-container_group-menu-checkbox {
margin-left: 0;
}
.table-container_group-menu-separator {
margin: 0 16px;
}
}
.header-container-text {
font-size: 12px;
color: ${(props) => props.theme.client.settings.migration.tableHeaderText};
}
.table-container_header {
@ -30,36 +60,64 @@ const StyledTableContainer = styled(TableContainer)`
margin-top: -1px;
&:hover {
cursor: pointer;
background-color: ${(props) => (props.theme.isBase ? "#F8F9F9" : "#282828")};
background: ${(props) =>
props.theme.client.settings.migration.tableRowHoverColor};
}
}
.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 TableView = (props) => {
const {
t,
users,
userId,
viewAs,
setViewAs,
sectionWidth,
accountsData,
typeOptions,
checkedAccounts,
toggleAccount,
toggleAllAccounts,
onCheckAccounts,
isAccountChecked,
cleanCheckedAccounts,
setSearchValue,
} = props;
const [hideColumns, setHideColumns] = useState(false);
const tableRef = useRef(null);
const [hideColumns, setHideColumns] = useState(false);
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`;
const isIndeterminate =
checkedAccounts.length > 0 && checkedAccounts.length !== users.length;
const toggleAll = (checked) => {
onCheckAccounts(checked, users);
};
const toggleAll = (e) => toggleAllAccounts(e, mockData);
const handleToggle = (e, id) => {
e.stopPropagation();
toggleAccount(id);
};
const onClearFilter = () => {
setSearchValue("");
};
useEffect(() => {
if (!sectionWidth) return;
if (sectionWidth < 1025 || isMobile) {
@ -67,48 +125,101 @@ const TableView = (props) => {
} else {
viewAs !== "table" && setViewAs("table");
}
return cleanCheckedAccounts;
}, [sectionWidth]);
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`;
const headerMenu = [
{
id: "change-type",
key: "change-type",
label: t("ChangeUserTypeDialog:ChangeUserTypeButton"),
disabled: false,
withDropDown: true,
options: typeOptions,
iconUrl: ChangeTypeReactSvgUrl,
},
];
return (
<StyledTableContainer forwardedRef={tableRef} useReactWindow>
<UsersTableHeader
t={t}
sectionWidth={sectionWidth}
tableRef={tableRef}
columnStorageName={columnStorageName}
columnInfoPanelStorageName={columnInfoPanelStorageName}
setHideColumns={setHideColumns}
isIndeterminate={checkedAccounts.length > 0 && checkedAccounts.length !== mockData.length}
isChecked={checkedAccounts.length === mockData.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
t={t}
key={data.id}
displayName={data.displayName}
email={data.email}
hideColumns={hideColumns}
isChecked={isAccountChecked(data.id)}
toggleAccount={(e) => handleToggle(e, data.id)}
{checkedAccounts.length > 0 && (
<div className="table-group-menu">
<TableGroupMenu
sectionWidth={sectionWidth}
headerMenu={headerMenu}
withoutInfoPanelToggler
withComboBox={false}
isIndeterminate={isIndeterminate}
isChecked={checkedAccounts.length === users.length}
onChange={toggleAll}
/>
))}
</TableBody>
</div>
)}
{accountsData.length > 0 ? (
<>
<UsersTableHeader
t={t}
sectionWidth={sectionWidth}
tableRef={tableRef}
columnStorageName={columnStorageName}
columnInfoPanelStorageName={columnInfoPanelStorageName}
isIndeterminate={isIndeterminate}
isChecked={checkedAccounts.length === users.length}
toggleAll={toggleAll}
setHideColumns={setHideColumns}
/>
<TableBody
itemHeight={49}
useReactWindow
infoPanelVisible={false}
columnStorageName={columnStorageName}
columnInfoPanelStorageName={columnInfoPanelStorageName}
filesLength={accountsData.length}
hasMoreFiles={false}
itemCount={accountsData.length}
fetchMoreFiles={() => {}}
>
{users.map((data) => (
<UsersTableRow
key={data.key}
id={data.key}
type={data.userType}
displayName={data.displayName}
email={data.email}
typeOptions={typeOptions}
hideColumns={hideColumns}
isChecked={isAccountChecked(data.key)}
toggleAccount={(e) => handleToggle(e, data.key)}
/>
))}
</TableBody>
</>
) : (
<EmptyScreenContainer
imageSrc={EmptyScreenUserReactSvgUrl}
imageAlt="Empty Screen user image"
headerText={t("People:NotFoundUsers")}
descriptionText={t("People:NotFoundUsersDesc")}
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>
);
};
@ -117,14 +228,17 @@ export default inject(({ setup, auth, importAccountsStore }) => {
const { viewAs, setViewAs } = setup;
const { id: userId } = auth.userStore.user;
const {
users,
checkedAccounts,
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
onCheckAccounts,
setSearchValue,
} = importAccountsStore;
return {
users,
viewAs,
setViewAs,
userId,
@ -132,6 +246,7 @@ export default inject(({ setup, auth, importAccountsStore }) => {
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
onCheckAccounts,
setSearchValue,
};
})(observer(TableView));

View File

@ -1,30 +1,65 @@
import React from "react";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import { Consumer } from "@docspace/components/utils/context";
import TableView from "./TableView";
import RowView from "./RowView";
const AccountsTable = (props) => {
const { t, viewAs, accountsData } = props;
const { t, viewAs, accountsData, changeTypeGroup } = props;
const onChangeType = (key) => {
changeTypeGroup(key);
};
const typeOptions = [
{
key: "DocSpaceAdmin",
label: t("Common:DocSpaceAdmin"),
onClick: () => onChangeType("DocSpaceAdmin"),
},
{
key: "RoomAdmin",
label: t("Common:RoomAdmin"),
onClick: () => onChangeType("RoomAdmin"),
},
{
key: "User",
label: t("Common:PowerUser"),
onClick: () => onChangeType("User"),
},
];
return (
<Consumer>
{(context) =>
viewAs === "table" ? (
<TableView t={t} sectionWidth={context.sectionWidth} accountsData={accountsData} />
<TableView
t={t}
sectionWidth={context.sectionWidth}
accountsData={accountsData}
typeOptions={typeOptions}
/>
) : (
<RowView t={t} sectionWidth={context.sectionWidth} accountsData={accountsData} />
<RowView
t={t}
sectionWidth={context.sectionWidth}
accountsData={accountsData}
typeOptions={typeOptions}
/>
)
}
</Consumer>
);
};
export default inject(({ setup }) => {
export default inject(({ setup, importAccountsStore }) => {
const { viewAs } = setup;
const { changeTypeGroup } = importAccountsStore;
return {
viewAs,
changeTypeGroup,
};
})(observer(AccountsTable));
})(
withTranslation(["ChangeUserTypeDialog", "People"])(observer(AccountsTable))
);

View File

@ -1,25 +1,45 @@
import React, { useState } from "react";
import { useState } from "react";
import { inject, observer } from "mobx-react";
import SaveCancelButtons from "@docspace/components/save-cancel-buttons";
import SearchInput from "@docspace/components/search-input";
import AccountsTable from "./AccountsTable";
import AccountsPaging from "../../../sub-components/AccountsPaging";
import { Wrapper } from "../StyledStepper";
import { mockData } from "./mockData";
// import { mockData } from "./mockData";
const FourthStep = (props) => {
const { t, incrementStep, decrementStep } = props;
const {
t,
incrementStep,
decrementStep,
checkedAccounts,
users,
searchValue,
setSearchValue,
} = props;
const [dataPortion, setDataPortion] = useState(mockData.slice(0, 25));
const [dataPortion, setDataPortion] = useState(users.slice(0, 25));
const handleDataChange = (leftBoundary, rightBoundary) => {
setDataPortion(mockData.slice(leftBoundary, rightBoundary));
setDataPortion(users.slice(leftBoundary, rightBoundary));
};
const onChangeInput = (value) => {
setSearchValue(value);
};
const onClearSearchInput = () => {
setSearchValue("");
};
const filteredAccounts = dataPortion.filter(
(data) =>
data.displayName.toLowerCase().startsWith(searchValue.toLowerCase()) ||
data.email.toLowerCase().startsWith(searchValue.toLowerCase())
);
return (
<Wrapper>
<SaveCancelButtons
@ -32,35 +52,50 @@ const FourthStep = (props) => {
displaySettings={true}
/>
<SearchInput
id="search-users-input"
onChange={() => console.log("changed")}
onClearSearch={() => console.log("cleared")}
placeholder={t("Common:Search")}
/>
<AccountsTable t={t} accountsData={dataPortion} />
{mockData.length > 25 && (
<AccountsPaging t={t} numberOfItems={mockData.length} setDataPortion={handleDataChange} />
{!checkedAccounts.length > 0 && (
<SearchInput
id="search-users-type-input"
placeholder={t("Common:Search")}
value={searchValue}
onChange={onChangeInput}
refreshTimeout={100}
onClearSearch={onClearSearchInput}
/>
)}
<SaveCancelButtons
className="save-cancel-buttons"
onSaveClick={incrementStep}
onCancelClick={decrementStep}
showReminder={true}
saveButtonLabel={t("Settings:NextStep")}
cancelButtonLabel={t("Common:Back")}
displaySettings={true}
/>
<AccountsTable t={t} accountsData={filteredAccounts} />
{users.length > 25 && (
<AccountsPaging
t={t}
numberOfItems={users.length}
setDataPortion={handleDataChange}
/>
)}
{filteredAccounts.length > 0 && (
<SaveCancelButtons
className="save-cancel-buttons"
onSaveClick={incrementStep}
onCancelClick={decrementStep}
showReminder={true}
saveButtonLabel={t("Settings:NextStep")}
cancelButtonLabel={t("Common:Back")}
displaySettings={true}
/>
)}
</Wrapper>
);
};
export default inject(({ setup }) => {
const { viewAs } = setup;
export default inject(({ importAccountsStore }) => {
const { checkedAccounts, users, searchValue, setSearchValue } =
importAccountsStore;
return {
viewAs,
checkedAccounts,
users,
searchValue,
setSearchValue,
};
})(observer(FourthStep));

View File

@ -1,4 +1,3 @@
import React from "react";
import Row from "@docspace/components/row";
import UsersRowContent from "./UsersRowContent";
@ -9,10 +8,12 @@ const UsersRow = (props) => {
<Row
sectionWidth={sectionWidth}
data={data}
checked={isChecked}
checkbox
checked={isChecked}
onClick={toggleAccount}
onSelect={toggleAccount}>
onSelect={toggleAccount}
contextButtonSpacerWidth="0"
>
<UsersRowContent
t={t}
sectionWidth={sectionWidth}

View File

@ -1,4 +1,3 @@
import React from "react";
import styled from "styled-components";
import Text from "@docspace/components/text";
import RowContent from "@docspace/components/row-content";
@ -33,13 +32,21 @@ const StyledRowContent = styled(RowContent)`
}
`;
const UsersRowContent = ({ t, sectionWidth, displayName, email, isDuplicate }) => {
const UsersRowContent = ({
t,
sectionWidth,
displayName,
email,
isDuplicate,
}) => {
return (
<StyledRowContent sectionWidth={sectionWidth}>
<div className="import-accounts-name">
{displayName}
{isDuplicate && (
<span className="import-account-duplicate">&nbsp;({t("Settings:ExistingAccount")})</span>
<span className="import-account-duplicate">
&nbsp;({t("Settings:ExistingAccount")})
</span>
)}
</div>
<Text fontSize="12px" color="#a3a9ae" className="user-email">

View File

@ -1,24 +1,52 @@
import { useEffect } from "react";
import { useEffect, useRef } from "react";
import { inject, observer } from "mobx-react";
import { isMobile } from "react-device-detect";
import { tablet } from "@docspace/components/utils/device";
import styled from "styled-components";
import EmptyScreenContainer from "@docspace/components/empty-screen-container";
import IconButton from "@docspace/components/icon-button";
import Link from "@docspace/components/link";
import Box from "@docspace/components/box";
import RowContainer from "@docspace/components/row-container";
import UsersRow from "./UsersRow";
import Row from "@docspace/components/row";
import Checkbox from "@docspace/components/checkbox";
import Text from "@docspace/components/text";
import UsersRow from "./UsersRow";
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
import { mockData } from "../../mockData";
// import { mockData } from "../../mockData";
const StyledRowContainer = styled(RowContainer)`
margin: 20px 0;
.clear-icon {
margin-right: 8px;
}
.ec-desc {
max-width: 348px;
}
`;
const StyledRow = styled(Row)`
box-sizing: border-box;
height: 40px;
min-height: 40px;
.row-header-item {
display: flex;
align-items: center;
margin-left: 7px;
}
.row-header-title {
color: ${(props) => props.theme.client.settings.migration.tableHeaderText};
font-weight: 600;
font-size: 12px;
}
@media ${tablet} {
.row_content {
height: auto;
@ -37,10 +65,17 @@ const RowView = (props) => {
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
setSearchValue,
} = props;
const rowRef = useRef(null);
const toggleAll = (e) => toggleAllAccounts({ target: { checked: !e.target.checked } }, mockData);
const toggleAll = (e) => toggleAllAccounts(e, users);
const handleToggle = (id) => toggleAccount(id);
const onClearFilter = () => {
setSearchValue("");
};
useEffect(() => {
if (viewAs !== "table" && viewAs !== "row") return;
@ -50,32 +85,62 @@ const RowView = (props) => {
} else {
viewAs !== "table" && setViewAs("table");
}
return cleanCheckedAccounts;
}, [sectionWidth]);
return (
<StyledRowContainer useReactWindow={false}>
<StyledRow
sectionWidth={sectionWidth}
checkbox
checked={checkedAccounts.length === mockData.length}
onClick={toggleAll}
indeterminate={checkedAccounts.length > 0 && checkedAccounts.length !== mockData.length}>
<Text color="#a3a9ae" fontWeight={600} fontSize="12px">
{t("Common:Name")}
</Text>
</StyledRow>
{accountsData.map((data) => (
<UsersRow
t={t}
key={data.id}
data={data}
sectionWidth={sectionWidth}
toggleAccount={() => toggleAccount(data.id)}
isChecked={isAccountChecked(data.id)}
<StyledRowContainer forwardedRef={rowRef} useReactWindow={false}>
{accountsData.length > 0 ? (
<>
<StyledRow sectionWidth={sectionWidth}>
<div className="row-header-item">
{checkedAccounts.length > 0 && (
<Checkbox
isChecked={checkedAccounts.length === users.length}
isIndeterminate={isIndeterminate}
onChange={toggleAll}
/>
)}
<Text className="row-header-title">{t("Common:Name")}</Text>
</div>
</StyledRow>
{accountsData.map((data) => (
<UsersRow
t={t}
key={data.key}
data={data}
sectionWidth={sectionWidth}
isChecked={isAccountChecked(data.key)}
toggleAccount={() => handleToggle(data.key)}
/>
))}
</>
) : (
<EmptyScreenContainer
imageSrc={EmptyScreenUserReactSvgUrl}
imageAlt="Empty Screen user image"
headerText={t("People:NotFoundUsers")}
descriptionText={t("People:NotFoundUsersDesc")}
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>
);
};
@ -87,7 +152,7 @@ export default inject(({ setup, importAccountsStore }) => {
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
setSearchValue,
} = importAccountsStore;
return {
@ -97,6 +162,6 @@ export default inject(({ setup, importAccountsStore }) => {
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
setSearchValue,
};
})(observer(RowView));

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import { useState, useEffect } from "react";
import { inject, observer } from "mobx-react";
import TableHeader from "@docspace/components/table-container/TableHeader";
@ -80,8 +80,8 @@ const UsersTableHeader = (props) => {
setColumns((prevColumns) =>
prevColumns.map((item, index) =>
index === columnIndex ? { ...item, enable: !item.enable } : item,
),
index === columnIndex ? { ...item, enable: !item.enable } : item
)
);
const tableColumns = columns.map((c) => c.enable && c.key);

View File

@ -1,4 +1,3 @@
import React from "react";
import TableRow from "@docspace/components/table-container/TableRow";
import TableCell from "@docspace/components/table-container/TableCell";
import Text from "@docspace/components/text";
@ -22,7 +21,16 @@ const StyledTableRow = styled(TableRow)`
}
`;
const UsersTableRow = ({ t, displayName, email, isDuplicate, isChecked, toggleAccount }) => {
const NOT_EXIST = "—";
const UsersTableRow = ({
t,
displayName,
email,
isDuplicate,
isChecked,
toggleAccount,
}) => {
return (
<StyledTableRow checked={isChecked} onClick={toggleAccount}>
<TableCell>
@ -33,7 +41,11 @@ const UsersTableRow = ({ t, displayName, email, isDuplicate, isChecked, toggleAc
</TableCell>
<TableCell>
<Text fontWeight={600} color="#a3a9ae" className="user-email textOverflow">
<Text
fontWeight={600}
color="#a3a9ae"
className="user-email textOverflow"
>
{email}
</Text>
</TableCell>
@ -45,7 +57,7 @@ const UsersTableRow = ({ t, displayName, email, isDuplicate, isChecked, toggleAc
</Text>
) : (
<Text fontWeight={600} color="#a3a9ae" className="textOverflow">
-
{NOT_EXIST}
</Text>
)}
</TableCell>

View File

@ -4,11 +4,17 @@ import { isMobile } from "react-device-detect";
import { Base } from "@docspace/components/themes";
import styled from "styled-components";
import EmptyScreenContainer from "@docspace/components/empty-screen-container";
import IconButton from "@docspace/components/icon-button";
import Link from "@docspace/components/link";
import Box from "@docspace/components/box";
import UsersTableHeader from "./UsersTableHeader";
import UsersTableRow from "./UsersTableRow";
import TableContainer from "@docspace/components/table-container/TableContainer";
import TableBody from "@docspace/components/table-container/TableBody";
import { mockData } from "../../mockData";
import EmptyScreenUserReactSvgUrl from "PUBLIC_DIR/images/empty_screen_user.react.svg?url";
import ClearEmptyFilterSvgUrl from "PUBLIC_DIR/images/clear.empty.filter.svg?url";
// import { mockData } from "../../mockData";
const TABLE_VERSION = "6";
const COLUMNS_SIZE = `nextcloudSecondColumnsSize_ver-${TABLE_VERSION}`;
@ -29,9 +35,18 @@ const StyledTableContainer = styled(TableContainer)`
margin-top: -1px;
&:hover {
cursor: pointer;
background-color: ${(props) => (props.theme.isBase ? "#F8F9F9" : "#282828")};
background-color: ${(props) =>
props.theme.isBase ? "#F8F9F9" : "#282828"};
}
}
.clear-icon {
margin-right: 8px;
margin-top: 2px;
}
.ec-desc {
max-width: 618px;
}
`;
StyledTableContainer.defaultProps = { theme: Base };
@ -39,6 +54,7 @@ StyledTableContainer.defaultProps = { theme: Base };
const TableView = (props) => {
const {
t,
users,
userId,
viewAs,
setViewAs,
@ -48,17 +64,25 @@ const TableView = (props) => {
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
setSearchValue,
} = props;
const [hideColumns, setHideColumns] = useState(false);
const tableRef = useRef(null);
const toggleAll = (e) => toggleAllAccounts(e, mockData);
const toggleAll = (e) => toggleAllAccounts(e, users);
const handleToggle = (e, id) => {
e.stopPropagation();
toggleAccount(id);
};
const onClearFilter = () => {
setSearchValue("");
};
const isIndeterminate =
checkedAccounts.length > 0 && checkedAccounts.length !== users.length;
useEffect(() => {
if (!sectionWidth) return;
if (sectionWidth < 1025 || isMobile) {
@ -66,8 +90,6 @@ const TableView = (props) => {
} else {
viewAs !== "table" && setViewAs("table");
}
return cleanCheckedAccounts;
}, [sectionWidth]);
const columnStorageName = `${COLUMNS_SIZE}=${userId}`;
@ -75,40 +97,72 @@ const TableView = (props) => {
return (
<StyledTableContainer forwardedRef={tableRef} useReactWindow>
<UsersTableHeader
t={t}
sectionWidth={sectionWidth}
tableRef={tableRef}
columnStorageName={columnStorageName}
columnInfoPanelStorageName={columnInfoPanelStorageName}
setHideColumns={setHideColumns}
isIndeterminate={checkedAccounts.length > 0 && checkedAccounts.length !== mockData.length}
isChecked={checkedAccounts.length === mockData.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.id}
displayName={data.displayName}
email={data.email}
isDuplicate={data.isDuplicate}
hideColumns={hideColumns}
isChecked={isAccountChecked(data.id)}
toggleAccount={(e) => handleToggle(e, data.id)}
sectionWidth={sectionWidth}
tableRef={tableRef}
userId={userId}
columnStorageName={columnStorageName}
columnInfoPanelStorageName={columnInfoPanelStorageName}
setHideColumns={setHideColumns}
isIndeterminate={isIndeterminate}
isChecked={checkedAccounts.length === users.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}
displayName={data.displayName}
email={data.email}
isDuplicate={data.isDuplicate}
hideColumns={hideColumns}
isChecked={isAccountChecked(data.key)}
toggleAccount={(e) => handleToggle(e, data.key)}
/>
))}
</TableBody>
</>
) : (
<EmptyScreenContainer
imageSrc={EmptyScreenUserReactSvgUrl}
imageAlt="Empty Screen user image"
headerText={t("People:NotFoundUsers")}
descriptionText={t("People:NotFoundUsersDesc")}
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>
);
};
@ -117,14 +171,16 @@ export default inject(({ setup, auth, importAccountsStore }) => {
const { viewAs, setViewAs } = setup;
const { id: userId } = auth.userStore.user;
const {
users,
checkedAccounts,
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
setSearchValue,
} = importAccountsStore;
return {
users,
viewAs,
setViewAs,
userId,
@ -132,6 +188,6 @@ export default inject(({ setup, auth, importAccountsStore }) => {
toggleAccount,
toggleAllAccounts,
isAccountChecked,
cleanCheckedAccounts,
setSearchValue,
};
})(observer(TableView));

View File

@ -1,6 +1,5 @@
import React from "react";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import { Consumer } from "@docspace/components/utils/context";
import TableView from "./TableView";
@ -13,9 +12,17 @@ const AccountsTable = (props) => {
<Consumer>
{(context) =>
viewAs === "table" ? (
<TableView sectionWidth={context.sectionWidth} accountsData={accountsData} t={t} />
<TableView
t={t}
sectionWidth={context.sectionWidth}
accountsData={accountsData}
/>
) : (
<RowView sectionWidth={context.sectionWidth} accountsData={accountsData} t={t} />
<RowView
t={t}
sectionWidth={context.sectionWidth}
accountsData={accountsData}
/>
)
}
</Consumer>
@ -27,4 +34,4 @@ export default inject(({ setup }) => {
return {
viewAs,
};
})(observer(AccountsTable));
})(withTranslation(["People"])(observer(AccountsTable)));

View File

@ -1,38 +1,57 @@
import React, { useState } from "react";
import { useState } from "react";
import { inject, observer } from "mobx-react";
import SaveCancelButtons from "@docspace/components/save-cancel-buttons";
import SearchInput from "@docspace/components/search-input";
import AccountsTable from "./AccountsTable";
import AccountsPaging from "../../../sub-components/AccountsPaging";
import UsersInfoBlock from "../../../sub-components/UsersInfoBlock";
import Text from "@docspace/components/text";
import SearchInput from "@docspace/components/search-input";
import { Wrapper } from "../StyledStepper";
import UsersInfoBlock from "../../../sub-components/UsersInfoBlock";
import { mockData as nextStepData } from "../ThirdStep/mockData";
import { mockData } from "./mockData";
// import { mockData as nextStepData } from "../ThirdStep/mockData";
// import { mockData } from "./mockData";
import { NoEmailUsersBlock } from "../../../sub-components/NoEmailUsersBlock";
const LICENSE_LIMIT = 100;
const SecondStep = (props) => {
const { t, incrementStep, decrementStep, numberOfCheckedAccounts } = props;
const {
t,
incrementStep,
decrementStep,
numberOfCheckedAccounts,
users,
searchValue,
setSearchValue,
} = props;
const [dataPortion, setDataPortion] = useState(mockData.slice(0, 25));
const [dataPortion, setDataPortion] = useState(users.slice(0, 25));
const handleDataChange = (leftBoundary, rightBoundary) => {
setDataPortion(mockData.slice(leftBoundary, rightBoundary));
setDataPortion(users.slice(leftBoundary, rightBoundary));
};
const onChangeInput = (value) => {
setSearchValue(value);
};
const onClearSearchInput = () => {
setSearchValue("");
};
const filteredAccounts = dataPortion.filter(
(data) =>
data.displayName.toLowerCase().startsWith(searchValue.toLowerCase()) ||
data.email.toLowerCase().startsWith(searchValue.toLowerCase())
);
return (
<Wrapper>
{nextStepData.length > 0 && <NoEmailUsersBlock users={nextStepData.length} t={t} />}
{users.length > 0 && <NoEmailUsersBlock users={users.length} t={t} />}
{mockData.length > 0 ? (
{users.length > 0 ? (
<>
<SaveCancelButtons
className="save-cancel-buttons"
@ -48,23 +67,25 @@ const SecondStep = (props) => {
<UsersInfoBlock
t={t}
selectedUsers={numberOfCheckedAccounts}
totalUsers={mockData.length}
totalUsers={users.length}
totalLicenceLimit={LICENSE_LIMIT}
/>
<SearchInput
id="search-users-input"
onChange={() => console.log("changed")}
onClearSearch={() => console.log("cleared")}
placeholder={t("Common:Search")}
value={searchValue}
onChange={onChangeInput}
refreshTimeout={100}
onClearSearch={onClearSearchInput}
/>
<AccountsTable accountsData={dataPortion} t={t} />
<AccountsTable t={t} accountsData={filteredAccounts} />
{mockData.length > 25 && (
{users.length > 25 && (
<AccountsPaging
t={t}
numberOfItems={mockData.length}
numberOfItems={users.length}
setDataPortion={handleDataChange}
/>
)}
@ -75,26 +96,30 @@ const SecondStep = (props) => {
</Text>
)}
<SaveCancelButtons
className="save-cancel-buttons"
onSaveClick={incrementStep}
onCancelClick={decrementStep}
saveButtonLabel={t("Settings:NextStep")}
cancelButtonLabel={t("Common:Back")}
showReminder
displaySettings
saveButtonDisabled={numberOfCheckedAccounts > LICENSE_LIMIT}
/>
{filteredAccounts.length > 0 && (
<SaveCancelButtons
className="save-cancel-buttons"
onSaveClick={incrementStep}
onCancelClick={decrementStep}
saveButtonLabel={t("Settings:NextStep")}
cancelButtonLabel={t("Common:Back")}
showReminder
displaySettings
saveButtonDisabled={numberOfCheckedAccounts > LICENSE_LIMIT}
/>
)}
</Wrapper>
);
};
export default inject(({ setup, importAccountsStore }) => {
const { viewAs } = setup;
const { numberOfCheckedAccounts } = importAccountsStore;
export default inject(({ importAccountsStore }) => {
const { numberOfCheckedAccounts, users, searchValue, setSearchValue } =
importAccountsStore;
return {
viewAs,
numberOfCheckedAccounts,
users,
searchValue,
setSearchValue,
};
})(observer(SecondStep));

View File

@ -1,6 +1,7 @@
import React, { useState } from "react";
import styled from "styled-components";
import { useState } from "react";
import { inject, observer } from "mobx-react";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import Button from "@docspace/components/button";
import Text from "@docspace/components/text";
@ -18,18 +19,52 @@ const ButtonsWrapper = styled.div`
column-gap: 8px;
`;
const SeventhStep = ({ t }) => {
const SeventhStep = ({
t,
selectedUsers,
importedUsers,
getMigrationLog,
cleanCheckedAccounts,
sendWelcomeLetter,
}) => {
const [isChecked, setIsChecked] = useState(false);
const navigate = useNavigate();
const onDownloadLog = async () => {
try {
await getMigrationLog()
.then((response) => new Blob([response]))
.then((blob) => {
let a = document.createElement("a");
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = "migration.log";
a.click();
window.URL.revokeObjectURL(url);
});
} catch (error) {
console.log(error);
}
};
const onChangeCheckbox = () => {
setIsChecked((prev) => !prev);
};
const onFinishClick = () => {
if (isChecked) {
sendWelcomeLetter({ isSendWelcomeEmail: true });
}
setTimeout(() => {
navigate(-1);
cleanCheckedAccounts();
}, 300);
};
return (
<Wrapper>
<Text fontSize="12px">
{t("Settings:ImportedUsers", { selectedUsers: 67, importedUsers: 70 })}
{t("Settings:ImportedUsers", { selectedUsers, importedUsers })}
</Text>
<Text fontSize="12px" color="#F21C0E" className="mt-8">
{t("Settings:ErrorsWereFound", { errors: 3 })}
@ -45,17 +80,43 @@ const SeventhStep = ({ t }) => {
place="right"
offsetRight={0}
style={{ marginLeft: "4px" }}
tooltipContent={<Text fontSize="12px">{t("Settings:WelcomeLetterTooltip")}</Text>}
tooltipContent={
<Text fontSize="12px">{t("Settings:WelcomeLetterTooltip")}</Text>
}
/>
</Box>
<ButtonsWrapper>
<Button size="small" label={t("Common:Finish")} primary onClick={() => navigate(-1)} />
<Button size="small" label={t("Settings:DownloadLog")} />
<Button size="small" label={t("Settings:DeleteTemporaryFile")} />
<Button
size="small"
label={t("Common:Finish")}
primary
onClick={onFinishClick}
/>
<Button
size="small"
label={t("Settings:DownloadLog")}
onClick={onDownloadLog}
/>
</ButtonsWrapper>
</Wrapper>
);
};
export default SeventhStep;
export default inject(({ importAccountsStore }) => {
const {
users,
getMigrationLog,
numberOfCheckedAccounts,
cleanCheckedAccounts,
sendWelcomeLetter,
} = importAccountsStore;
return {
importedUsers: users.length,
selectedUsers: numberOfCheckedAccounts,
getMigrationLog,
cleanCheckedAccounts,
sendWelcomeLetter,
};
})(observer(SeventhStep));

View File

@ -1,67 +1,88 @@
import React, { useState, useRef, useEffect } from "react";
import SaveCancelButtons from "@docspace/components/save-cancel-buttons";
import ProgressBar from "@docspace/components/progress-bar";
import { useState, useRef, useEffect } from "react";
import { inject, observer } from "mobx-react";
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
import Button from "@docspace/components/button";
import { Wrapper } from "../StyledStepper";
const SixthStep = ({ t, incrementStep, decrementStep }) => {
const [isCancelVisible, setIsCancelVisible] = useState(false);
import ProgressBar from "@docspace/components/progress-bar";
import Button from "@docspace/components/button";
const PERCENT_STEP = 5;
const SixthStep = ({
t,
incrementStep,
isSixthStep,
setIsLoading,
migrationFile,
cancelMigration,
data,
toggles,
}) => {
const [isVisble, setIsVisble] = useState(false);
const [percent, setPercent] = useState(0);
const percentRef = useRef(0);
const PERCENT_STEP = 5;
useEffect(() => {
const interval = setInterval(() => {
if (percentRef.current < 100) {
setPercent((prevPercent) => prevPercent + PERCENT_STEP);
percentRef.current += PERCENT_STEP;
} else {
clearInterval(interval);
incrementStep();
}
}, 200);
return () => {
clearInterval(interval);
};
try {
const interval = setInterval(() => {
if (percentRef.current < 100) {
setIsLoading(true);
setPercent((prev) => prev + PERCENT_STEP);
percentRef.current += PERCENT_STEP;
} else {
clearInterval(interval);
setIsLoading(false);
incrementStep();
}
}, 1000);
migrationFile({ ...data, ...toggles });
} catch (error) {
console.log(error);
setIsLoading(false);
}
}, []);
const onClickButton = () => {
setIsCancelVisible(true);
const onCancel = () => {
setIsVisble(true);
setIsLoading(false);
};
return (
<Wrapper>
{percent < 102 ? (
{percent < 102 && (
<>
<ProgressBar percent={percent} className="data-import-progress-bar" />
<Button size="small" label={t("Common:CancelButton")} onClick={onClickButton} />
<Button
size="small"
className="cancel-button"
label={t("Common:CancelButton")}
onClick={onCancel}
/>
</>
) : (
<SaveCancelButtons
className="save-cancel-buttons"
onSaveClick={incrementStep}
onCancelClick={decrementStep}
saveButtonLabel={t("Settings:NextStep")}
cancelButtonLabel={t("Common:Back")}
displaySettings
showReminder
/>
)}
{isCancelVisible && (
{isVisble && (
<CancelUploadDialog
visible={isCancelVisible}
visible={isVisble}
loading={false}
onClose={() => setIsCancelVisible(false)}
isSixthStep={isSixthStep}
cancelMigration={cancelMigration}
onClose={() => setIsVisble(false)}
/>
)}
</Wrapper>
);
};
export default SixthStep;
export default inject(({ importAccountsStore }) => {
const { data, setIsLoading, migrationFile, cancelMigration, toggles } =
importAccountsStore;
return {
data,
toggles,
setIsLoading,
migrationFile,
cancelMigration,
};
})(observer(SixthStep));

View File

@ -10,6 +10,8 @@ import HelpButton from "@docspace/components/help-button";
import Text from "@docspace/components/text";
export const getStepsData = (t, currentStep, setCurrentStep) => {
const isSixthStep = currentStep === 6;
const incrementStep = () => {
if (currentStep !== 6) {
setCurrentStep((prev) => prev + 1);
@ -26,17 +28,35 @@ export const getStepsData = (t, currentStep, setCurrentStep) => {
{
title: t("Common:SelectFile"),
description: t("Settings:SelectFileDescriptionNextcloud"),
component: <FirstStep t={t} incrementStep={incrementStep} decrementStep={decrementStep} />,
component: (
<FirstStep
t={t}
incrementStep={incrementStep}
decrementStep={decrementStep}
/>
),
},
{
title: t("Settings:SelectUsers"),
description: t("Settings:SelectUsersDescriptionNextcloud"),
component: <SecondStep t={t} incrementStep={incrementStep} decrementStep={decrementStep} />,
component: (
<SecondStep
t={t}
incrementStep={incrementStep}
decrementStep={decrementStep}
/>
),
},
{
title: t("Settings:AddEmails"),
description: t("Settings:SelectUsersDescriptionNextcloud"),
component: <ThirdStep t={t} incrementStep={incrementStep} decrementStep={decrementStep} />,
component: (
<ThirdStep
t={t}
incrementStep={incrementStep}
decrementStep={decrementStep}
/>
),
},
{
title: t("Settings:SelectUserTypes"),
@ -56,22 +76,47 @@ export const getStepsData = (t, currentStep, setCurrentStep) => {
/>
</>
),
component: <FourthStep t={t} incrementStep={incrementStep} decrementStep={decrementStep} />,
component: (
<FourthStep
t={t}
incrementStep={incrementStep}
decrementStep={decrementStep}
/>
),
},
{
title: t("Settings:DataImport"),
description: t("Settings:ImportSectionDescription"),
component: <FifthStep t={t} incrementStep={incrementStep} decrementStep={decrementStep} />,
component: (
<FifthStep
t={t}
incrementStep={incrementStep}
decrementStep={decrementStep}
/>
),
},
{
title: t("Settings:DataImportProcessing"),
description: t("Settings:ImportProcessingDescription"),
component: <SixthStep t={t} incrementStep={incrementStep} decrementStep={decrementStep} />,
component: (
<SixthStep
t={t}
incrementStep={incrementStep}
decrementStep={decrementStep}
isSixthStep={isSixthStep}
/>
),
},
{
title: t("Settings:DataImportComplete"),
description: t("Settings:ImportCompleteDescriptionNextcloud"),
component: <SeventhStep t={t} incrementStep={incrementStep} decrementStep={decrementStep} />,
component: (
<SeventhStep
t={t}
incrementStep={incrementStep}
decrementStep={decrementStep}
/>
),
},
];
};

View File

@ -1,13 +1,11 @@
import React, { useState } from "react";
import styled from "styled-components";
import { withTranslation } from "react-i18next";
import { useState } from "react";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import { isMobileOnly } from "react-device-detect";
import styled from "styled-components";
import Text from "@docspace/components/text";
import { isMobileOnly } from "react-device-detect";
import BreakpointWarning from "SRC_DIR/components/BreakpointWarning";
import { getStepsData } from "./Stepper";
const NextcloudWrapper = styled.div`
@ -30,12 +28,11 @@ const NextcloudWrapper = styled.div`
const NextcloudWorkspace = (props) => {
const { t, tReady, theme } = props;
const [currentStep, setCurrentStep] = useState(0);
const StepsData = getStepsData(t, currentStep, setCurrentStep);
if (isMobileOnly) return <BreakpointWarning sectionName={t("Settings:DataImport")} />;
if (isMobileOnly)
return <BreakpointWarning sectionName={t("Settings:DataImport")} />;
if (!tReady) return;
@ -45,13 +42,21 @@ const NextcloudWorkspace = (props) => {
<Text
className="data-import-description"
lineHeight="20px"
color={theme.isBase ? "#657077" : "#ADADAD"}>
color={theme.isBase ? "#657077" : "#ADADAD"}
>
{t("Settings:AboutDataImport")}
</Text>
<Text className="data-import-counter" fontSize="16px" fontWeight={700} lineHeight="22px">
<Text
className="data-import-counter"
fontSize="16px"
fontWeight={700}
lineHeight="22px"
>
{currentStep + 1}/{StepsData.length}. {StepsData[currentStep].title}
</Text>
<div className="data-import-section-description">{StepsData[currentStep].description}</div>
<div className="data-import-section-description">
{StepsData[currentStep].description}
</div>
</NextcloudWrapper>
{StepsData[currentStep].component}
</>
@ -64,4 +69,8 @@ export default inject(({ setup, auth }) => {
initSettings,
theme: auth.settingsStore.theme,
};
})(withTranslation(["Common, Settings, SMTPSettings"])(observer(NextcloudWorkspace)));
})(
withTranslation(["Common, Settings, SMTPSettings"])(
observer(NextcloudWorkspace)
)
);