UNPKG

passbolt-styleguide

Version:

Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.

361 lines (338 loc) 12 kB
/** * Passbolt ~ Open source password manager for teams * Copyright (c) 2022 Passbolt SA (https://www.passbolt.com) * * Licensed under GNU Affero General Public License version 3 of the or any later version. * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) 2022 Passbolt SA (https://www.passbolt.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.passbolt.com Passbolt(tm) * @since 3.8.0 */ import React from "react"; import PropTypes from "prop-types"; import { Trans, withTranslation } from "react-i18next"; import { withAppContext } from "../../../../shared/context/AppContext/AppContext"; import DialogWrapper from "../../Common/Dialog/DialogWrapper/DialogWrapper"; import { withAdminSmtpSettings } from "../../../contexts/AdminSmtpSettingsContext"; import FormCancelButton from "../../Common/Inputs/FormSubmitButton/FormCancelButton"; import FormSubmitButton from "../../Common/Inputs/FormSubmitButton/FormSubmitButton"; import AppEmailValidatorService from "../../../../shared/services/validator/AppEmailValidatorService"; import CaretDownSVG from "../../../../img/svg/caret_down.svg"; import CaretRightSVG from "../../../../img/svg/caret_right.svg"; const uiStateEnum = { FORM: "form", ERROR: "error", SUCCESS: "success", }; class SendTestMailDialog extends React.Component { /** * Constructor * @param {Object} props */ constructor(props) { super(props); this.state = this.defaultState; this.bindCallbacks(); } /** * Get default state * @returns {Object} */ get defaultState() { return { uiState: uiStateEnum.FORM, recipient: this.props.context.loggedInUser.username, processing: false, displayLogs: true, }; } /** * Bind callbacks */ bindCallbacks() { this.handleRetryClick = this.handleRetryClick.bind(this); this.handleError = this.handleError.bind(this); this.handleFormSubmit = this.handleFormSubmit.bind(this); this.handleInputChange = this.handleInputChange.bind(this); this.handleDisplayLogsClick = this.handleDisplayLogsClick.bind(this); } /** * Handles the form submission * @param {Event} event * @returns {Promise<void>} */ async handleFormSubmit(event) { event.preventDefault(); if (!this.validateForm()) { return; } try { this.setState({ processing: true }); const details = await this.props.adminSmtpSettingsContext.sendTestMailTo(this.state.recipient); this.setState({ uiState: uiStateEnum.SUCCESS, debugDetails: this.formatDebug(details.debug), displayLogs: false, }); } catch (e) { this.handleError(e); } this.setState({ processing: false }); } /** * Handles form input changes * @param {Event} event * @returns {Promise<void>} */ async handleInputChange(event) { this.setState({ recipient: event.target.value }); } /** * Validates the current form * @returns {Promise<Boolean>} true if the form is valid, false otherwise */ validateForm() { const isValid = AppEmailValidatorService.validate(this.state.recipient, this.props.context.siteSettings); this.setState({ recipientError: isValid ? "" : this.translate("Recipient must be a valid email"), }); return isValid; } /** * Format the JSON debug message for the log details. * @param {object} data a serializable JS object * @returns {string} a human readable JSON */ formatDebug(data) { return JSON.stringify(data, null, 4); } /** * Handles error by setting the debug info from the error message and setting the UI state to ERROR * @param {Error} error the error to parse */ handleError(error) { //If an SMTP timeout errors happens, no debug is available but we have a clear error message in the header. const debugInfo = error.data?.body?.debug; const errorMessage = debugInfo?.length > 0 ? debugInfo : error?.message; this.setState({ uiState: uiStateEnum.ERROR, debugDetails: this.formatDebug(errorMessage), displayLogs: true, }); } /** * Handle the click on "Logs" button by toggling the log details display state. */ handleDisplayLogsClick() { this.setState({ displayLogs: !this.state.displayLogs }); } /** * Handle the click on "Retry" button by setting the UI to FORM state. */ handleRetryClick() { this.setState({ uiState: uiStateEnum.FORM }); } /** * Returns true if the input must be disabled. * It's true when the data is under process in the admin SMTP context. * @returns {boolean} */ hasAllInputDisabled() { return this.state.processing; } /** * Returns the title string to display on the dialog based on the UI state. * @returns {string} */ get title() { const stateTitles = { form: this.translate("Send test email"), error: this.translate("Something went wrong!"), success: this.translate("Email sent"), }; return stateTitles[this.state.uiState] || ""; } /** * Get the translate function * @returns {function(...[*]=)} */ get translate() { return this.props.t; } /** * Render the component * @returns {JSX} */ render() { return ( <DialogWrapper className="send-test-email-dialog" title={this.title} onClose={this.props.handleClose} disabled={this.hasAllInputDisabled()} > {this.state.uiState === uiStateEnum.FORM && ( <form onSubmit={this.handleFormSubmit} noValidate> <div className="form-content"> <div className={`input text required ${this.state.recipientError ? "error" : ""} ${this.hasAllInputDisabled() ? "disabled" : ""}`} > <label> <Trans>Recipient</Trans> </label> <input id="recipient" type="text" name="recipient" required="required" className="required fluid form-element ready" placeholder="name@email.com" onChange={this.handleInputChange} value={this.state.recipient} disabled={this.hasAllInputDisabled()} /> {this.state.recipientError && ( <div className="recipient error-message">{this.state.recipientError}</div> )} </div> <div className="message notice no-margin"> <strong> <Trans>Pro tip</Trans>: </strong>{" "} <Trans> after clicking on send, a test email will be sent to the recipient email in order to check that your configuration is correct. </Trans> </div> </div> <div className="submit-wrapper clearfix"> <FormCancelButton disabled={this.hasAllInputDisabled()} onClick={this.props.handleClose} /> <FormSubmitButton disabled={this.hasAllInputDisabled()} processing={this.state.processing} value={this.translate("Send")} /> </div> </form> )} {this.state.uiState === uiStateEnum.ERROR && ( <> <div className="dialog-body"> <p> <Trans>The test email could not be sent. Kindly check the logs below for more information.</Trans> <br /> <a className="faq-link" href="https://www.passbolt.com/docs/hosting/troubleshooting/email" rel="noopener noreferrer" target="_blank" > <Trans>FAQ: Why are my emails not sent?</Trans> </a> </p> <div className="accordion-header"> <button type="button" className="link no-border" onClick={this.handleDisplayLogsClick}> <span> <Trans>Logs</Trans> </span> {this.state.displayLogs ? ( <CaretDownSVG className="baseline svg-icon" /> ) : ( <CaretRightSVG className="baseline svg-icon" /> )} </button> </div> {this.state.displayLogs && ( <div className="accordion-content"> <textarea className="full_report" readOnly={true} value={this.state.debugDetails} /> </div> )} </div> <div className="dialog-footer clearfix"> <button type="button" className="cancel" disabled={this.hasAllInputDisabled()} onClick={this.handleRetryClick} > <Trans>Retry</Trans> </button> <button className="button primary" type="button" onClick={this.props.handleClose} disabled={this.isProcessing} > <span> <Trans>Close</Trans> </span> </button> </div> </> )} {this.state.uiState === uiStateEnum.SUCCESS && ( <> <div className="dialog-body"> <p> <Trans>The test email has been sent. Check your email box, you should receive it in a minute.</Trans> </p> <div className="accordion-header"> <button type="button" className="link no-border" onClick={this.handleDisplayLogsClick}> <span> <Trans>Logs</Trans> </span> {this.state.displayLogs ? ( <CaretDownSVG className="baseline svg-icon" /> ) : ( <CaretRightSVG className="baseline svg-icon" /> )} </button> </div> {this.state.displayLogs && ( <div className="accordion-content"> <textarea className="full_report" readOnly={true} value={this.state.debugDetails} /> </div> )} <div className="message notice no-margin"> <strong> <Trans>Pro tip</Trans>: </strong>{" "} <Trans>Check your spam folder if you do not hear from us after a while.</Trans> </div> </div> <div className="dialog-footer clearfix"> <button type="button" className="cancel" disabled={this.hasAllInputDisabled()} onClick={this.handleRetryClick} > <Trans>Retry</Trans> </button> <button className="button primary" type="button" onClick={this.props.handleClose} disabled={this.isProcessing} > <span> <Trans>Close</Trans> </span> </button> </div> </> )} </DialogWrapper> ); } } SendTestMailDialog.propTypes = { context: PropTypes.object, // Application context adminSmtpSettingsContext: PropTypes.object, // The administration SMTP settings context handleClose: PropTypes.func, // The close dialog callback t: PropTypes.func, // The translation function }; export default withAppContext(withAdminSmtpSettings(withTranslation("common")(SendTestMailDialog)));