Web: Client: Data Import: add addEmail dialog

This commit is contained in:
Vladimir Khvan 2024-07-03 17:16:41 +05:00
parent db00964816
commit b01b9915ca
4 changed files with 203 additions and 64 deletions

View File

@ -10,6 +10,7 @@
"AccountsWithoutEmails": "We found <1>{{users}} users</1> without emails. You can fill their emails or continue without this action.", "AccountsWithoutEmails": "We found <1>{{users}} users</1> without emails. You can fill their emails or continue without this action.",
"AccountsWithoutEmailsNextStep": "We found <1>{{users}} users</1> without emails. You can add necessary data to their accounts on the next step.", "AccountsWithoutEmailsNextStep": "We found <1>{{users}} users</1> without emails. You can add necessary data to their accounts on the next step.",
"AddAllowedIP": "Add allowed IP address", "AddAllowedIP": "Add allowed IP address",
"AddEmail": "Add e-mail",
"AddEmails": "Add emails to incomplete accounts", "AddEmails": "Add emails to incomplete accounts",
"AddEmailsDescription": "Check list of unimported users to import into {{organizationName}} {{productName}}.", "AddEmailsDescription": "Check list of unimported users to import into {{organizationName}} {{productName}}.",
"AddEmailsWarning": "You don'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.",

View File

@ -0,0 +1,119 @@
// (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 styled from "styled-components";
import { useTranslation } from "react-i18next";
import {
ModalDialog,
ModalDialogType,
} from "@docspace/shared/components/modal-dialog";
import { Button, ButtonSize } from "@docspace/shared/components/button";
import { EmailInput } from "@docspace/shared/components/email-input";
import { InputType } from "@docspace/shared/components/text-input";
import { TValidate } from "@docspace/shared/components/email-input/EmailInput.types";
import { Text } from "@docspace/shared/components/text";
import ModalDialogContainer from "../ModalDialogContainer";
const DialogBodyWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 16px;
`;
interface EmailChangeDialogProps {
visible: boolean;
onClose: () => void;
tempEmail: string;
handleEmailChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
onValidateEmail: (res: TValidate) => {
isValid: boolean;
errors: string[];
};
hasError: boolean;
checkEmailValidity: () => void;
handleSave: () => void;
displayName: string;
}
const EmailChangeDialog = ({
visible,
onClose,
tempEmail,
handleEmailChange,
onValidateEmail,
hasError,
checkEmailValidity,
handleSave,
displayName,
}: EmailChangeDialogProps) => {
const { t } = useTranslation(["Settings", "SMTPSettings", "Common"]);
return (
<ModalDialogContainer
visible={visible}
onClose={onClose}
displayType={ModalDialogType.modal}
>
<ModalDialog.Header>{t("AddEmail")}</ModalDialog.Header>
<ModalDialog.Body>
<DialogBodyWrapper>
<Text>{displayName}</Text>
<EmailInput
placeholder={t("SMTPSettings:EnterEmail")}
className="import-email-input"
value={tempEmail}
onChange={handleEmailChange}
type={InputType.email}
onValidateInput={onValidateEmail}
hasError={hasError}
onBlur={checkEmailValidity}
isAutoFocussed
scale
/>
</DialogBodyWrapper>
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
label={t("Common:SaveButton")}
size={ButtonSize.normal}
scale
primary
onClick={handleSave}
isDisabled={hasError}
/>
<Button
label={t("Common:CancelButton")}
size={ButtonSize.normal}
scale
onClick={onClose}
/>
</ModalDialog.Footer>
</ModalDialogContainer>
);
};
export default EmailChangeDialog;

View File

@ -37,12 +37,15 @@ import { Button } from "@docspace/shared/components/button";
import { TValidate } from "@docspace/shared/components/email-input/EmailInput.types"; import { TValidate } from "@docspace/shared/components/email-input/EmailInput.types";
import { InputType } from "@docspace/shared/components/text-input"; import { InputType } from "@docspace/shared/components/text-input";
import { isMobile } from "@docspace/shared/utils";
import EditSvg from "PUBLIC_DIR/images/access.edit.react.svg"; import EditSvg from "PUBLIC_DIR/images/access.edit.react.svg";
import CrossSvg from "PUBLIC_DIR/images/cross.edit.react.svg"; import CrossSvg from "PUBLIC_DIR/images/cross.edit.react.svg";
import CheckSvg from "PUBLIC_DIR/images/check.edit.react.svg"; import CheckSvg from "PUBLIC_DIR/images/check.edit.react.svg";
import { Base } from "@docspace/shared/themes"; import { Base } from "@docspace/shared/themes";
import EmailChangeDialog from "SRC_DIR/components/dialogs/EmailChangeDialog";
import { import {
AddEmailRowContentProps, AddEmailRowContentProps,
InjectedAddEmailRowContentProps, InjectedAddEmailRowContentProps,
@ -181,26 +184,40 @@ const UsersRowContent = (props: AddEmailRowContentProps) => {
</Text> </Text>
</div> </div>
{isEmailOpen ? ( {isEmailOpen ? (
<EmailInputWrapper ref={emailInputRef}> isMobile() ? (
<EmailInput <EmailChangeDialog
placeholder={t("Settings:NoEmail")} visible={isEmailOpen}
className="import-email-input" onClose={clearEmail}
value={tempEmail} tempEmail={tempEmail}
onChange={handleEmailChange} handleEmailChange={handleEmailChange}
type={InputType.email} onValidateEmail={onValidateEmail}
onValidateInput={onValidateEmail}
hasError={hasError} hasError={hasError}
onBlur={checkEmailValidity} checkEmailValidity={checkEmailValidity}
isAutoFocussed handleSave={handleSaveClick}
displayName={displayName}
/> />
) : (
<EmailInputWrapper ref={emailInputRef}>
<EmailInput
placeholder={t("SMTPSettings:EnterEmail")}
className="import-email-input"
value={tempEmail}
onChange={handleEmailChange}
type={InputType.email}
onValidateInput={onValidateEmail}
hasError={hasError}
onBlur={checkEmailValidity}
isAutoFocussed
/>
<DecisionButton <DecisionButton
label="" label=""
icon={<CheckSvg />} icon={<CheckSvg />}
onClick={handleSaveClick} onClick={handleSaveClick}
/> />
<DecisionButton label="" icon={<CrossSvg />} onClick={clearEmail} /> <DecisionButton label="" icon={<CrossSvg />} onClick={clearEmail} />
</EmailInputWrapper> </EmailInputWrapper>
)
) : ( ) : (
<span onClick={openEmail} className="user-email" ref={emailTextRef}> <span onClick={openEmail} className="user-email" ref={emailTextRef}>
<EditSvg /> <EditSvg />

View File

@ -31,6 +31,7 @@ import { SearchInput } from "@docspace/shared/components/search-input";
import { Text } from "@docspace/shared/components/text"; import { Text } from "@docspace/shared/components/text";
import { InputSize } from "@docspace/shared/components/text-input"; import { InputSize } from "@docspace/shared/components/text-input";
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
import AccountsTable from "./AccountsTable"; import AccountsTable from "./AccountsTable";
import AccountsPaging from "../../sub-components/AccountsPaging"; import AccountsPaging from "../../sub-components/AccountsPaging";
@ -43,7 +44,6 @@ import { parseQuota } from "../../utils";
import { AddEmailsStepProps, InjectedAddEmailsStepProps } from "../../types"; import { AddEmailsStepProps, InjectedAddEmailsStepProps } from "../../types";
import { MigrationButtons } from "../../sub-components/MigrationButtons"; import { MigrationButtons } from "../../sub-components/MigrationButtons";
import { CancelUploadDialog } from "SRC_DIR/components/dialogs";
const PAGE_SIZE = 25; const PAGE_SIZE = 25;
const REFRESH_TIMEOUT = 100; const REFRESH_TIMEOUT = 100;
@ -67,7 +67,7 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
setWorkspace, setWorkspace,
setMigratingWorkspace, setMigratingWorkspace,
setMigrationPhase, setMigrationPhase,
cancelUploadDialogVisible, cancelUploadDialogVisible,
setCancelUploadDialogVisible, setCancelUploadDialogVisible,
@ -118,7 +118,7 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
quota.used + quota.used +
checkedUsers.withEmail.filter((user) => !user.isDuplicate).length + checkedUsers.withEmail.filter((user) => !user.isDuplicate).length +
checkedUsers.withoutEmail.length; checkedUsers.withoutEmail.length;
const onCancelMigration = () => { const onCancelMigration = () => {
cancelMigration(); cancelMigration();
clearCheckedAccounts(); clearCheckedAccounts();
@ -127,7 +127,7 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
setMigratingWorkspace(""); setMigratingWorkspace("");
setMigrationPhase(""); setMigrationPhase("");
}; };
const showCancelDialog = () => setCancelUploadDialogVisible(true); const showCancelDialog = () => setCancelUploadDialogVisible(true);
const hideCancelDialog = () => setCancelUploadDialogVisible(false); const hideCancelDialog = () => setCancelUploadDialogVisible(false);
@ -196,7 +196,7 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
)} )}
{Buttons} {Buttons}
{cancelUploadDialogVisible && ( {cancelUploadDialogVisible && (
<CancelUploadDialog <CancelUploadDialog
visible={cancelUploadDialogVisible} visible={cancelUploadDialogVisible}
@ -211,50 +211,52 @@ const AddEmailsStep = (props: AddEmailsStepProps) => {
); );
}; };
export default inject<TStore>(({ importAccountsStore, currentQuotaStore, dialogsStore }) => { export default inject<TStore>(
const { ({ importAccountsStore, currentQuotaStore, dialogsStore }) => {
incrementStep, const {
decrementStep, incrementStep,
searchValue, decrementStep,
setSearchValue, searchValue,
users, setSearchValue,
setResultUsers, users,
areCheckedUsersEmpty, setResultUsers,
checkedUsers, areCheckedUsersEmpty,
withEmailUsers, checkedUsers,
withEmailUsers,
cancelMigration, cancelMigration,
clearCheckedAccounts, clearCheckedAccounts,
setStep, setStep,
setWorkspace, setWorkspace,
setMigratingWorkspace, setMigratingWorkspace,
setMigrationPhase, setMigrationPhase,
} = importAccountsStore; } = importAccountsStore;
const { quotaCharacteristics } = currentQuotaStore; const { quotaCharacteristics } = currentQuotaStore;
const { cancelUploadDialogVisible, setCancelUploadDialogVisible } = const { cancelUploadDialogVisible, setCancelUploadDialogVisible } =
dialogsStore; dialogsStore;
return { return {
incrementStep, incrementStep,
decrementStep, decrementStep,
searchValue, searchValue,
setSearchValue, setSearchValue,
users, users,
setResultUsers, setResultUsers,
areCheckedUsersEmpty, areCheckedUsersEmpty,
checkedUsers, checkedUsers,
withEmailUsers, withEmailUsers,
cancelMigration, cancelMigration,
clearCheckedAccounts, clearCheckedAccounts,
setStep, setStep,
setWorkspace, setWorkspace,
setMigratingWorkspace, setMigratingWorkspace,
setMigrationPhase, setMigrationPhase,
quotaCharacteristics, quotaCharacteristics,
cancelUploadDialogVisible, cancelUploadDialogVisible,
setCancelUploadDialogVisible, setCancelUploadDialogVisible,
}; };
})(observer(AddEmailsStep)); },
)(observer(AddEmailsStep));