UNPKG

passbolt-styleguide

Version:

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

289 lines (266 loc) 10.3 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 "../../../contexts/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 Icon from "../../../../shared/components/Icons/Icon"; import AppEmailValidatorService from "../../../../shared/services/validator/AppEmailValidatorService"; 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> <div className="message notice"> <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 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://help.passbolt.com/faq/hosting/why-email-not-sent" rel="noopener noreferrer" target="_blank"><Trans>FAQ: Why are my emails not sent?</Trans></a> </p> <div className="accordion-header"> <a onClick={this.handleDisplayLogsClick}> <Icon name={this.state.displayLogs ? "caret-down" : "caret-right"}/> <Trans>Logs</Trans> </a> </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"> <a className="cancel" role="button" disabled={this.hasAllInputDisabled()} onClick={this.handleRetryClick}><Trans>Retry</Trans></a> <button className="button primary" role="button" 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"> <a onClick={this.handleDisplayLogsClick}> <Icon name={this.state.displayLogs ? "caret-down" : "caret-right"}/> <Trans>Logs</Trans> </a> </div> {this.state.displayLogs && <div className="accordion-content"> <textarea className="full_report" readOnly={true} value={this.state.debugDetails}/> </div> } </div> <div className="message notice"> <strong><Trans>Pro tip</Trans>:</strong> <Trans>Check your spam folder if you do not hear from us after a while.</Trans> </div> <div className="dialog-footer clearfix"> <a className="cancel" role="button" disabled={this.hasAllInputDisabled()} onClick={this.handleRetryClick}><Trans>Retry</Trans></a> <button className="button primary" role="button" 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)));