diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/index.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/index.tsx index 3d418b1446..bfe85eb21e 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/index.tsx +++ b/packages/client/src/pages/PortalSettings/categories/data-import/GoogleWorkspace/Stepper/index.tsx @@ -30,9 +30,9 @@ import { Text } from "@docspace/shared/components/text"; import { HelpButton } from "@docspace/shared/components/help-button"; import SelectFileStep from "../../components/SelectFileStep"; - import SelectUsersStep from "../../components/SelectUsersStep"; -import SelectUsersTypeStep from "./SelectUsersTypeStep"; +import SelectUsersTypeStep from "../../components/SelectUsersTypeStep"; + import ImportStep from "./ImportStep"; import ImportProcessingStep from "./ImportProcessingStep"; import ImportCompleteStep from "./ImportCompleteStep"; diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/NextcloudWorkspace/Stepper/index.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/NextcloudWorkspace/Stepper/index.tsx index 2c07f1d862..9669175ffc 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/NextcloudWorkspace/Stepper/index.tsx +++ b/packages/client/src/pages/PortalSettings/categories/data-import/NextcloudWorkspace/Stepper/index.tsx @@ -32,8 +32,8 @@ import { HelpButton } from "@docspace/shared/components/help-button"; import SelectFileStep from "../../components/SelectFileStep"; import SelectUsersStep from "../../components/SelectUsersStep"; import AddEmailsStep from "../../components/AddEmailsStep"; +import SelectUsersTypeStep from "../../components/SelectUsersTypeStep"; -import SelectUsersTypeStep from "./SelectUsersTypeStep"; import ImportStep from "./ImportStep"; import ImportProcessingStep from "./ImportProcessingStep"; import ImportCompleteStep from "./ImportCompleteStep"; @@ -113,13 +113,7 @@ export const getStepsData = ( /> ), - component: ( - - ), + component: , }, { title: t("Settings:DataImport"), diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/Stepper/index.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/Stepper/index.tsx index 2db4d347ea..63c4e1ecba 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/Stepper/index.tsx +++ b/packages/client/src/pages/PortalSettings/categories/data-import/OnlyofficeWorkspace/Stepper/index.tsx @@ -31,8 +31,8 @@ import { HelpButton } from "@docspace/shared/components/help-button"; import SelectFileStep from "../../components/SelectFileStep"; import SelectUsersStep from "../../components/SelectUsersStep"; +import SelectUsersTypeStep from "../../components/SelectUsersTypeStep"; -import SelectUsersTypeStep from "./SelectUsersTypeStep"; import ImportStep from "./ImportStep"; import ImportProcessingStep from "./ImportProcessingStep"; import ImportCompleteStep from "./ImportCompleteStep"; diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/StyledDataImport.ts b/packages/client/src/pages/PortalSettings/categories/data-import/StyledDataImport.ts index 18fd935942..e08bbb31f1 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/StyledDataImport.ts +++ b/packages/client/src/pages/PortalSettings/categories/data-import/StyledDataImport.ts @@ -119,6 +119,10 @@ export const Wrapper = styled.div` .save-cancel-buttons { margin-bottom: 16px; + + @media ${mobile} { + margin-bottom: 0; + } } .mt-8 { diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/components/AddEmailsStep/index.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/components/AddEmailsStep/index.tsx index b5c8956cc2..c57429c0c2 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/components/AddEmailsStep/index.tsx +++ b/packages/client/src/pages/PortalSettings/categories/data-import/components/AddEmailsStep/index.tsx @@ -45,6 +45,7 @@ import { parseQuota } from "../../utils"; import { AddEmailsStepProps, InjectedAddEmailsStepProps } from "../../types"; const PAGE_SIZE = 25; +const REFRESH_TIMEOUT = 100; const AddEmailsStep = (props: AddEmailsStepProps) => { const { @@ -85,6 +86,7 @@ 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()), ); @@ -151,7 +153,7 @@ const AddEmailsStep = (props: AddEmailsStepProps) => { placeholder={t("Common:Search")} value={searchValue} onChange={onChangeInput} - refreshTimeout={100} + refreshTimeout={REFRESH_TIMEOUT} onClearSearch={onClearSearchInput} size={InputSize.base} /> @@ -179,6 +181,8 @@ const AddEmailsStep = (props: AddEmailsStepProps) => { export default inject(({ importAccountsStore, currentQuotaStore }) => { const { + incrementStep, + decrementStep, searchValue, setSearchValue, users, @@ -190,6 +194,8 @@ export default inject(({ importAccountsStore, currentQuotaStore }) => { const { quotaCharacteristics } = currentQuotaStore; return { + incrementStep, + decrementStep, searchValue, setSearchValue, users, diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/RowView/UsersRow.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/RowView/UsersRow.tsx new file mode 100644 index 0000000000..7e45dd3c16 --- /dev/null +++ b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/RowView/UsersRow.tsx @@ -0,0 +1,65 @@ +// (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 { useRef } from "react"; +import { Row } from "@docspace/shared/components/row"; +import UsersRowContent from "./UsersRowContent"; +import { TypeSelectUsersRowProps } from "../../../../types"; + +const UsersRow = (props: TypeSelectUsersRowProps) => { + const { data, sectionWidth, typeOptions, isChecked, toggleAccount } = props; + + const roleSelectorRef = useRef(null); + + const handleAccountToggle = (e: React.ChangeEvent) => { + if (!roleSelectorRef.current?.contains(e.target)) { + toggleAccount(); + } + }; + + return ( + + + + ); +}; + +export default UsersRow; diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/RowView/UsersRowContent.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/RowView/UsersRowContent.tsx new file mode 100644 index 0000000000..3ca0f72761 --- /dev/null +++ b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/RowView/UsersRowContent.tsx @@ -0,0 +1,165 @@ +// (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 styled, { css } 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"; +import { + ComboBox, + ComboBoxSize, + TOption, +} from "@docspace/shared/components/combobox"; +import { + TypeSelectRowContentProps, + InjectedTypeSelectRowContentProps, +} from "../../../../types"; + +const StyledRowContent = styled(RowContent)` + display: flex; + + .row-main-container-wrapper { + width: 100%; + ${(props) => + props.theme.interfaceDirection === "rtl" + ? css` + margin-left: 0px; + ` + : css` + margin-right: 0px; + `} + } + + .rowMainContainer { + height: 100%; + width: 100%; + } + + .username { + font-size: 14px; + font-weight: 600; + color: ${(props) => props.theme.client.settings.migration.subtitleColor}; + } + + .user-email { + margin-right: 5px; + font-size: 12px; + font-weight: 600; + color: ${(props) => + props.theme.client.settings.migration.tableRowTextColor}; + } + + .user-type { + .combo-button { + border: none; + padding: 4px 8px; + justify-content: flex-end; + background-color: transparent; + } + + .combo-button-label { + color: ${(props) => + props.theme.client.settings.migration.tableRowTextColor}; + } + + .combo-buttons_arrow-icon { + flex: initial; + margin-right: 0px; + } + + svg { + path { + fill: ${(props) => + props.theme.client.settings.migration.tableRowTextColor}; + } + } + } +`; + +const UsersRowContent = (props: TypeSelectRowContentProps) => { + const { + id, + sectionWidth, + displayName, + email, + typeOptions, + roleSelectorRef, + type, + changeUserType, + } = props as InjectedTypeSelectRowContentProps; + + const onSelectUser = (option: TOption) => { + changeUserType(id, String(option.key)); + }; + + const selectedOption: TOption = typeOptions.find( + (option) => option.key === type, + ) || { key: "", label: "" }; + + const contentData = [ + + + {displayName} + {email} + + +
+ +
+
, + ]; + + return ( + + {contentData} + + ); +}; + +export default inject(({ importAccountsStore }) => { + const { changeUserType } = importAccountsStore; + + return { + changeUserType, + }; +})(observer(UsersRowContent)); diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/RowView/index.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/RowView/index.tsx new file mode 100644 index 0000000000..2f8a83a357 --- /dev/null +++ b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/RowView/index.tsx @@ -0,0 +1,246 @@ +// (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 { 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 { 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 UsersRow from "./UsersRow"; +import { + InjectedTypeSelectRowViewProps, + TypeSelectRowViewProps, +} from "../../../../types"; + +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: -31.5px; + top: 53px; + margin-bottom: -29.5px; + + .table-container_group-menu { + padding: 0px 16px; + border-image-slice: 0; + box-shadow: rgba(4, 15, 27, 0.07) 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 = (props: TypeSelectRowViewProps) => { + const { + t, + sectionWidth, + accountsData, + typeOptions, + + filteredUsers, + checkedUsers, + toggleAccount, + toggleAllAccounts, + isAccountChecked, + setSearchValue, + } = props as InjectedTypeSelectRowViewProps; + + const isIndeterminate = + checkedUsers.result.length > 0 && + checkedUsers.result.length !== filteredUsers.length; + + const toggleAll = (isChecked: boolean) => + toggleAllAccounts(isChecked, filteredUsers, checkedAccountType); + + const onClearFilter = () => setSearchValue(""); + + const headerMenu = [ + { + id: "change-type", + label: t("ChangeUserTypeDialog:ChangeUserTypeButton"), + disabled: false, + withDropDown: true, + options: typeOptions, + iconUrl: ChangeTypeReactSvgUrl, + onClick: () => {}, + title: t("ChangeUserTypeDialog:ChangeUserTypeButton"), + }, + ]; + + return ( + + {checkedUsers.result.length > 0 && ( +
+ +
+ )} + {accountsData.length > 0 ? ( + <> + + {t("Common:Name")} + + + {accountsData.map((data) => ( + toggleAccount(data, checkedAccountType)} + /> + ))} + + ) : ( + + + + {t("Common:ClearFilter")} + + + } + /> + )} +
+ ); +}; + +export default inject(({ importAccountsStore }) => { + const { + checkedUsers, + toggleAccount, + toggleAllAccounts, + isAccountChecked, + setSearchValue, + filteredUsers, + } = importAccountsStore; + + return { + checkedUsers, + toggleAccount, + toggleAllAccounts, + isAccountChecked, + setSearchValue, + filteredUsers, + }; +})(observer(RowView)); diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/TableView/UsersTableHeader.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/TableView/UsersTableHeader.tsx new file mode 100644 index 0000000000..74b6b96a9d --- /dev/null +++ b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/TableView/UsersTableHeader.tsx @@ -0,0 +1,137 @@ +// (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, useEffect } from "react"; + +import { TableHeader, TTableColumn } from "@docspace/shared/components/table"; +import { UsersTableHeaderProps } from "../../../../types"; + +const TABLE_VERSION = "6"; +const TABLE_COLUMNS = `nextcloudFourthColumns_ver-${TABLE_VERSION}`; + +const getColumns = (defaultColumns: TTableColumn[], userId?: string) => { + const storageColumns = localStorage.getItem(`${TABLE_COLUMNS}=${userId}`); + + if (storageColumns) { + const splitColumns = storageColumns?.split(","); + + return defaultColumns.map((col) => ({ + ...col, + enable: splitColumns.includes(col.key), + })); + } + + return defaultColumns; +}; + +const UsersTableHeader = (props: UsersTableHeaderProps) => { + const { + t, + sectionWidth, + userId, + tableRef, + columnStorageName, + columnInfoPanelStorageName, + isIndeterminate, + isChecked, + } = props; + + const [columns, setColumns] = useState([ + { + key: "Name", + title: t("Common:Name"), + resizable: true, + enable: true, + default: true, + active: true, + minWidth: 180, + }, + ]); + + function onColumnChange(key: string) { + const columnIndex = columns.findIndex((c) => c.key === key); + + if (columnIndex === -1) return; + + setColumns((prevColumns: TTableColumn[]) => + prevColumns.map((item, index) => + index === columnIndex ? { ...item, enable: !item.enable } : item, + ), + ); + + const tableColumns = columns.map((c) => c.enable && c.key); + localStorage.setItem(`${TABLE_COLUMNS}=${userId}`, tableColumns.toString()); + } + + const defaultColumns = [ + { + key: "Name", + title: t("Common:Name"), + resizable: true, + enable: true, + default: true, + active: true, + minWidth: 180, + onChange: onColumnChange, + }, + { + key: "Type", + title: t("Common:Type"), + enable: true, + resizable: true, + minWidth: 100, + onChange: onColumnChange, + }, + { + key: "Email", + title: t("Common:Email"), + enable: true, + resizable: true, + onChange: onColumnChange, + }, + ]; + + useEffect(() => { + setColumns(getColumns(defaultColumns)); + }, [isIndeterminate, isChecked]); + + return ( + + ); +}; + +export default UsersTableHeader; diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/TableView/UsersTableRow.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/TableView/UsersTableRow.tsx new file mode 100644 index 0000000000..baaa052143 --- /dev/null +++ b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/TableView/UsersTableRow.tsx @@ -0,0 +1,161 @@ +// (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 { useRef } from "react"; +import { inject, observer } from "mobx-react"; +import styled from "styled-components"; + +import { TableRow, TableCell } from "@docspace/shared/components/table"; + +import { Text } from "@docspace/shared/components/text"; +import { Checkbox } from "@docspace/shared/components/checkbox"; +import { + ComboBox, + ComboBoxSize, + TOption, +} from "@docspace/shared/components/combobox"; +import { + InjectedTypeSelectTableRowProps, + TypeSelectTableRowProps, +} from "../../../../types"; + +const StyledTableRow = styled(TableRow)` + .table-container_cell { + padding-right: 30px; + text-overflow: ellipsis; + } + + .username { + font-size: 13px; + font-weight: 600; + color: ${(props) => props.theme.client.settings.migration.subtitleColor}; + } + + .user-email { + margin-right: 5px; + font-size: 13px; + font-weight: 600; + color: ${(props) => + props.theme.client.settings.migration.tableRowTextColor}; + } + + .user-type { + .combo-button { + border: none; + padding: 4px 8px; + justify-content: flex-start; + background-color: transparent; + } + + .combo-button-label { + color: ${(props) => + props.theme.client.settings.migration.comboBoxLabelColor}; + } + + .combo-buttons_arrow-icon { + flex: initial; + margin-right: 0px; + } + + svg { + path { + fill: ${(props) => + props.theme.client.settings.migration.comboBoxLabelColor}; + } + } + } +`; + +const UsersTableRow = (props: TypeSelectTableRowProps) => { + const { + id, + displayName, + email, + typeOptions, + isChecked, + toggleAccount, + type, + changeUserType, + } = props as InjectedTypeSelectTableRowProps; + const userTypeRef = useRef(null); + + const onSelectUser = (option: TOption) => { + changeUserType(id, String(option.key)); + }; + + const selectedOption: TOption = typeOptions.find( + (option) => option.key === type, + ) || { key: "", label: "" }; + + const handleAccountToggle = (e: React.ChangeEvent) => { + e.preventDefault(); + e.stopPropagation(); + + if ( + !e.target.closest(".dropdown-container") && + !userTypeRef.current?.contains(e.target) + ) { + toggleAccount(); + } + }; + + return ( + + + + {displayName} + + + +
+ +
+
+ + + {email} + +
+ ); +}; + +export default inject(({ importAccountsStore }) => { + const { changeUserType } = importAccountsStore; + + return { + changeUserType, + }; +})(observer(UsersTableRow)); diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/TableView/index.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/TableView/index.tsx new file mode 100644 index 0000000000..d7b370ffbd --- /dev/null +++ b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/TableView/index.tsx @@ -0,0 +1,246 @@ +// (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 { useRef } from "react"; +import { inject, observer } from "mobx-react"; +import { Base } from "@docspace/shared/themes"; +import styled, { css } 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 { + TableGroupMenu, + TableBody, + TGroupMenuItem, +} from "@docspace/shared/components/table"; + +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 { StyledTableContainer } from "../../../../StyledDataImport"; +import UsersTableRow from "./UsersTableRow"; +import UsersTableHeader from "./UsersTableHeader"; +import { + TypeSelectTableViewProps, + InjectedTypeSelectTableViewProps, +} from "../../../../types"; + +const UserSelectTableContainer = styled(StyledTableContainer)` + .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: rgba(4, 15, 27, 0.07) 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; + `} + } + } +`; + +UserSelectTableContainer.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 = (props: TypeSelectTableViewProps) => { + const { + t, + sectionWidth, + accountsData, + typeOptions, + + userId, + checkedUsers, + toggleAccount, + toggleAllAccounts, + isAccountChecked, + setSearchValue, + filteredUsers, + } = props as InjectedTypeSelectTableViewProps; + const tableRef = useRef(null); + const columnStorageName = `${COLUMNS_SIZE}=${userId}`; + const columnInfoPanelStorageName = `${INFO_PANEL_COLUMNS_SIZE}=${userId}`; + + const isIndeterminate = + checkedUsers.result.length > 0 && + checkedUsers.result.length !== filteredUsers.length; + + const toggleAll = (isChecked: boolean) => + toggleAllAccounts(isChecked, filteredUsers, checkedAccountType); + + const onClearFilter = () => { + setSearchValue(""); + }; + + const headerMenu: TGroupMenuItem[] = [ + { + id: "change-type", + label: t("ChangeUserTypeDialog:ChangeUserTypeButton"), + disabled: false, + withDropDown: true, + options: typeOptions, + iconUrl: ChangeTypeReactSvgUrl, + onClick: () => {}, + title: t("ChangeUserTypeDialog:ChangeUserTypeButton"), + }, + ]; + + return ( + + {checkedUsers.result.length > 0 && ( +
+ +
+ )} + {accountsData.length > 0 ? ( + <> + + {}} + > + {accountsData.map((data) => ( + toggleAccount(data, checkedAccountType)} + /> + ))} + + + ) : ( + + + + {t("Common:ClearFilter")} + + + } + /> + )} +
+ ); +}; + +export default inject(({ userStore, importAccountsStore }) => { + const userId = userStore.user?.id; + const { + checkedUsers, + toggleAccount, + toggleAllAccounts, + isAccountChecked, + setSearchValue, + filteredUsers, + } = importAccountsStore; + + return { + userId, + checkedUsers, + toggleAccount, + toggleAllAccounts, + isAccountChecked, + setSearchValue, + filteredUsers, + }; +})(observer(TableView)); diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/index.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/index.tsx new file mode 100644 index 0000000000..1a8e86174b --- /dev/null +++ b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/AccountsTable/index.tsx @@ -0,0 +1,115 @@ +// (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 { withTranslation } from "react-i18next"; +import { Consumer } from "@docspace/shared/utils/context"; + +import TableView from "./TableView"; +import RowView from "./RowView"; +import { + AccountsTableProps, + InjectedTypeSelectTableProps, +} from "../../../types"; + +const checkedAccountType = "result"; + +const AccountsTable = (props: AccountsTableProps) => { + const { + t, + accountsData, + viewAs, + changeGroupType, + UserTypes, + toggleAllAccounts, + } = props as InjectedTypeSelectTableProps; + + const setTypeDocspaceAdmin = () => { + changeGroupType(UserTypes.DocSpaceAdmin); + toggleAllAccounts(false, [], checkedAccountType); + }; + const setTypeRoomAdmin = () => { + changeGroupType(UserTypes.RoomAdmin); + toggleAllAccounts(false, [], checkedAccountType); + }; + const setTypeUser = () => { + changeGroupType(UserTypes.User); + toggleAllAccounts(false, [], checkedAccountType); + }; + + const typeOptions = [ + { + key: UserTypes.DocSpaceAdmin, + label: t(`Common:${UserTypes.DocSpaceAdmin}`), + onClick: setTypeDocspaceAdmin, + }, + { + key: UserTypes.RoomAdmin, + label: t(`Common:${UserTypes.RoomAdmin}`), + onClick: setTypeRoomAdmin, + }, + { + key: UserTypes.User, + label: t(`Common:PowerUser`), + onClick: setTypeUser, + }, + ]; + + return ( + + {(context) => + viewAs === "table" ? ( + + ) : ( + + ) + } + + ); +}; +export default inject(({ setup, importAccountsStore }) => { + const { viewAs } = setup; + const { changeGroupType, UserTypes, toggleAllAccounts } = importAccountsStore; + + return { + viewAs, + changeGroupType, + UserTypes, + toggleAllAccounts, + }; +})( + withTranslation(["ChangeUserTypeDialog", "People"])(observer(AccountsTable)), +); diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/index.tsx b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/index.tsx new file mode 100644 index 0000000000..48e3b072c3 --- /dev/null +++ b/packages/client/src/pages/PortalSettings/categories/data-import/components/SelectUsersTypeStep/index.tsx @@ -0,0 +1,153 @@ +// (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, useEffect } from "react"; +import { inject, observer } from "mobx-react"; + +import { SaveCancelButtons } from "@docspace/shared/components/save-cancel-buttons"; +import { SearchInput } from "@docspace/shared/components/search-input"; +import { InputSize } from "@docspace/shared/components/text-input"; +import AccountsTable from "./AccountsTable"; +import AccountsPaging from "../../sub-components/AccountsPaging"; + +import { Wrapper } from "../../StyledDataImport"; +import { InjectedTypeSelectProps, TypeSelectProps } from "../../types"; + +const PAGE_SIZE = 25; +const REFRESH_TIMEOUT = 100; + +const SelectUsersTypeStep = (props: TypeSelectProps) => { + const { + t, + + incrementStep, + decrementStep, + users, + searchValue, + setSearchValue, + filteredUsers, + } = props as InjectedTypeSelectProps; + + const [boundaries, setBoundaries] = useState([0, PAGE_SIZE]); + const [dataPortion, setDataPortion] = useState( + filteredUsers.slice(...boundaries), + ); + + const handleDataChange = (leftBoundary: number, rightBoundary: number) => { + setBoundaries([leftBoundary, rightBoundary]); + setDataPortion(filteredUsers.slice(leftBoundary, rightBoundary)); + }; + + const onChangeInput = (value: string) => { + setSearchValue(value); + }; + + const onClearSearchInput = () => { + setSearchValue(""); + }; + + const filteredAccounts = dataPortion.filter( + (data) => + data.firstName?.toLowerCase().startsWith(searchValue.toLowerCase()) || + data.displayName?.toLowerCase().startsWith(searchValue.toLowerCase()) || + data.email?.toLowerCase().startsWith(searchValue.toLowerCase()), + ); + + useEffect(() => { + setDataPortion(filteredUsers.slice(...boundaries)); + }, [users]); + + return ( + + + + {filteredUsers.length > 0 && ( + <> + + + + + {filteredUsers.length > PAGE_SIZE && filteredAccounts.length > 0 && ( + + )} + + {filteredAccounts.length > 0 && ( + + )} + + )} + + ); +}; + +export default inject(({ importAccountsStore }) => { + const { + users, + incrementStep, + decrementStep, + searchValue, + setSearchValue, + filteredUsers, + } = importAccountsStore; + + return { + users, + incrementStep, + decrementStep, + searchValue, + setSearchValue, + filteredUsers, + }; +})(observer(SelectUsersTypeStep)); diff --git a/packages/client/src/pages/PortalSettings/categories/data-import/types/index.ts b/packages/client/src/pages/PortalSettings/categories/data-import/types/index.ts index 2a5a88df90..936581e956 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-import/types/index.ts +++ b/packages/client/src/pages/PortalSettings/categories/data-import/types/index.ts @@ -32,6 +32,8 @@ import { } from "@docspace/shared/api/settings/types"; import { TPaymentFeature } from "@docspace/shared/api/portal/types"; +import { TOption } from "@docspace/shared/components/combobox"; + export type TFunciton = ReturnType["t"]; export interface ProvidersProps {} @@ -174,7 +176,7 @@ export interface UsersTableHeaderProps { columnInfoPanelStorageName: string; isIndeterminate: boolean; isChecked: boolean; - toggleAll: (e: React.ChangeEvent) => void; + toggleAll?: (e: React.ChangeEvent) => void; } export interface UsersTableRowProps { @@ -283,3 +285,94 @@ export interface InjectedAddEmailRowContentProps extends AddEmailRowContentProps { changeEmail: TStore["importAccountsStore"]["changeEmail"]; } + +export interface TypeSelectProps { + t: TFunciton; +} + +export interface InjectedTypeSelectProps extends TypeSelectProps { + incrementStep: TStore["importAccountsStore"]["incrementStep"]; + decrementStep: TStore["importAccountsStore"]["decrementStep"]; + users: TStore["importAccountsStore"]["users"]; + searchValue: TStore["importAccountsStore"]["searchValue"]; + setSearchValue: TStore["importAccountsStore"]["setSearchValue"]; + filteredUsers: TStore["importAccountsStore"]["filteredUsers"]; +} + +export interface InjectedTypeSelectTableProps extends AccountsTableProps { + viewAs: TStore["setup"]["viewAs"]; + changeGroupType: TStore["importAccountsStore"]["changeGroupType"]; + UserTypes: TStore["importAccountsStore"]["UserTypes"]; + toggleAllAccounts: TStore["importAccountsStore"]["toggleAllAccounts"]; +} + +export interface TypeSelectTableViewProps { + t: TFunciton; + sectionWidth?: number; + accountsData: TEnhancedMigrationUser[]; + typeOptions: TOption[]; +} +export interface InjectedTypeSelectTableViewProps + extends TypeSelectTableViewProps { + userId?: string; + checkedUsers: TStore["importAccountsStore"]["checkedUsers"]; + toggleAccount: TStore["importAccountsStore"]["toggleAccount"]; + toggleAllAccounts: TStore["importAccountsStore"]["toggleAllAccounts"]; + isAccountChecked: TStore["importAccountsStore"]["isAccountChecked"]; + setSearchValue: TStore["importAccountsStore"]["setSearchValue"]; + filteredUsers: TStore["importAccountsStore"]["filteredUsers"]; +} + +export interface TypeSelectTableRowProps { + id: string; + displayName: string; + email: string; + typeOptions: TOption[]; + isChecked: boolean; + type: string; + toggleAccount: () => void; +} + +export interface InjectedTypeSelectTableRowProps + extends TypeSelectTableRowProps { + changeUserType: TStore["importAccountsStore"]["changeUserType"]; +} + +export interface TypeSelectRowViewProps { + t: TFunciton; + sectionWidth?: number; + accountsData: TEnhancedMigrationUser[]; + typeOptions: TOption[]; +} + +export interface InjectedTypeSelectRowViewProps extends TypeSelectRowViewProps { + filteredUsers: TStore["importAccountsStore"]["filteredUsers"]; + checkedUsers: TStore["importAccountsStore"]["checkedUsers"]; + toggleAccount: TStore["importAccountsStore"]["toggleAccount"]; + toggleAllAccounts: TStore["importAccountsStore"]["toggleAllAccounts"]; + isAccountChecked: TStore["importAccountsStore"]["isAccountChecked"]; + setSearchValue: TStore["importAccountsStore"]["setSearchValue"]; +} + +export interface TypeSelectUsersRowProps { + data: TEnhancedMigrationUser; + sectionWidth?: number; + typeOptions: TOption[]; + isChecked: boolean; + toggleAccount: () => void; +} + +export interface TypeSelectRowContentProps { + id: string; + sectionWidth?: number; + displayName: string; + email: string; + typeOptions: TOption[]; + roleSelectorRef: React.RefObject; + type: string; +} + +export interface InjectedTypeSelectRowContentProps + extends TypeSelectRowContentProps { + changeUserType: TStore["importAccountsStore"]["changeUserType"]; +}