diff --git a/src/App.js b/src/App.js index a436f4b870033218137b4d0b70e3e375b398b1fa..6b2514c80a9d5ccc92b51c4a5d09a61f5564b9a6 100644 --- a/src/App.js +++ b/src/App.js @@ -3,6 +3,7 @@ import { BrowserRouter, Switch, Route } from "react-router-dom"; import MemberList from "./MemberList"; import ProductList from "./ProductList"; import Dashboard from "./Dashboard"; +import OwnPassword from "./Password"; import Purchase from "./purchase/Purchase"; import Topup from "./Topup"; import ShowTransaction from "./ShowTransaction"; @@ -27,6 +28,9 @@ function Panel(props) { <Route path="/transaction/:id"> <ShowTransaction /> </Route> + <Route path="/password"> + <OwnPassword /> + </Route> <Route path="/purchase"> <Purchase /> </Route> diff --git a/src/Head.js b/src/Head.js index 63d458216bc4cb6d7c7f691cadf9b3a1d52de926..740778cf1d21dc11051a39cbee88ec2e9184810c 100644 --- a/src/Head.js +++ b/src/Head.js @@ -35,6 +35,11 @@ function Head(props) { <Nav.Link href="/purchase">Comprar</Nav.Link> <Nav.Link href="/order/create">Abrir pedido</Nav.Link> </Nav> + + <NavDropdown title="Ajustes" id="ajustes"> + <Nav.Link href="/password">Cambiar contraseña</Nav.Link> + </NavDropdown> + {adminNav} <Form inline> <Button variant="outline-success" onClick={props.onLogout}> diff --git a/src/Password.js b/src/Password.js new file mode 100644 index 0000000000000000000000000000000000000000..ef0a7f376655e27468a3d4122a84e72a42dddf0e --- /dev/null +++ b/src/Password.js @@ -0,0 +1,166 @@ +import React from "react"; +import { Container, Form, Col, Row, Button, Alert } from "react-bootstrap"; +import AuthContext from "./AuthContext"; + +class OwnPassword extends React.Component { + static contextType = AuthContext; + + constructor(props) { + super(props); + this.state = { + newPassword: "", + newPassword2: "", + formErrors: "", + isNewPasswordValid: false, + isNewPassword2Valid: false, + isFormValid: false, + error: null, + passwordChanged: false, + }; + this.handleSubmit = this.handleSubmit.bind(this); + } + + /** Check whether the new password is valid, and matches with its confirmation. */ + checkNewPassword() { + let isNewPasswordValid = this.state.newPassword.length >= 6; + let isNewPassword2Valid = + this.state.newPassword === this.state.newPassword2; + + let formErrors = isNewPasswordValid + ? isNewPassword2Valid + ? "" + : "Las contraseñas no coinciden." + : "La contraseña es demasiado corta."; + + this.setState( + { + formErrors: formErrors, + isNewPasswordValid: isNewPasswordValid, + isNewPassword2Valid: isNewPassword2Valid, + }, + this.validateForm + ); + } + + /** Check all the fields are valid, so the form may be submitted. */ + validateForm() { + this.setState({ + isFormValid: + this.state.isNewPasswordValid && this.state.isNewPassword2Valid, + }); + } + + handleSubmit(event) { + event.preventDefault(); + + this.setState({ isLoading: true, error: null }); + const body = JSON.stringify({ + password: this.state.newPassword, + }); + fetch("/api/member/me", { + headers: { "x-authentication": this.context.token }, + method: "PUT", + body, + }) + .then((response) => { + if (!response.ok) { + throw new Error( + response.status.toString() + " " + response.statusText + ); + } + return response.json(); + }) + .then((json) => { + // console.log(json); + this.setState({ + isLoading: false, + passwordChanged: true, + }); + }) + .catch((error) => { + this.setState({ isLoading: false, error: error.message }); + }); + } + + render() { + let alert; + + if (this.state.formErrors) { + alert = <Alert variant="warning">{this.state.formErrors}</Alert>; + } + + if (this.state.error) { + alert = ( + <Alert variant="danger"> + Se produjo un error al intentar cambiar la contraseña:{" "} + {this.state.error} + </Alert> + ); + } + + if (this.state.passwordChanged) { + alert = ( + <Alert variant="success">La contraseña se ha cambiado con éxito.</Alert> + ); + this.state.passwordChanged = false; + } + + return ( + <Container> + <h2>Cambio de contraseña</h2> + + {alert} + + <Form onSubmit={this.handleSubmit}> + <Form.Group as={Row}> + <Form.Label as="legend" column sm={4}> + Nueva contraseña + </Form.Label> + <Col sm={8}> + <Form.Control + placeholder="Nueva contraseña" + type="password" + onChange={(e) => + this.setState({ newPassword: e.target.value }, () => { + this.checkNewPassword(); + }) + } + /> + </Col> + </Form.Group> + + <Form.Group as={Row}> + <Form.Label as="legend" column sm={4}> + Confirme la nueva contraseña + </Form.Label> + <Col sm={8}> + <Form.Control + placeholder="Repita la nueva contraseña" + type="password" + onChange={(e) => + this.setState({ newPassword2: e.target.value }, () => { + this.checkNewPassword(); + }) + } + /> + </Col> + </Form.Group> + + <Form.Group as={Row}> + <Col sm={{ offset: 4, span: 8 }}> + <Button + type="submit" + variant="primary" + disabled={!this.state.isFormValid} + > + Cambiar la contraseña + </Button> + </Col> + </Form.Group> + </Form> + </Container> + ); + } +} + +export default OwnPassword;