From 82171b652ee32df88c0301660f050ac63cdba4cf Mon Sep 17 00:00:00 2001 From: meskio <meskio@sindominio.net> Date: Tue, 16 Mar 2021 17:01:46 +0100 Subject: [PATCH] Separate the product editor out of the list --- src/Panel.js | 4 + src/product/ProductList.js | 194 ++++++------------------------------- src/product/ShowProduct.js | 144 +++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 162 deletions(-) create mode 100644 src/product/ShowProduct.js diff --git a/src/Panel.js b/src/Panel.js index ab0633f..ffa697b 100644 --- a/src/Panel.js +++ b/src/Panel.js @@ -5,6 +5,7 @@ import MemberAdder from "./member/MemberAdder"; import MemberEditer from "./member/MemberEditer"; import MemberList from "./member/MemberList"; import ProductList from "./product/ProductList"; +import ShowProduct from "./product/ShowProduct"; import CreateProduct from "./product/CreateProduct"; import CreateInventary from "./inventary/CreateInventary"; import InventaryList from "./inventary/InventaryList"; @@ -57,6 +58,9 @@ function LogedPanel(props) { <Route path="/product/add"> <CreateProduct /> </Route> + <Route path="/product/:code"> + <ShowProduct /> + </Route> <Route path="/inventary/add"> <CreateInventary /> </Route> diff --git a/src/product/ProductList.js b/src/product/ProductList.js index d3a4ef7..62e2f51 100644 --- a/src/product/ProductList.js +++ b/src/product/ProductList.js @@ -1,170 +1,40 @@ -import React from "react"; -import { Table, Button, Alert, Modal } from "react-bootstrap"; +import React, { useState } from "react"; +import { Table } from "react-bootstrap"; +import { LinkContainer } from "react-router-bootstrap"; import Fetcher from "../Fetcher"; -import EditableCell from "../EditableCell"; -import AuthContext from "../AuthContext"; -import { url } from "../util"; +import { printMoney } from "../util"; -class ProductList extends React.Component { - static contextType = AuthContext; - - constructor(props) { - super(props); - this.state = { - delete: { - name: null, - }, - products: [], - error: null, - }; - - this.modalClose = this.modalClose.bind(this); - this.delProduct = this.delProduct.bind(this); - } - - update(row, key, value) { - this.setState({ error: null }); - - let products = this.state.products; - const code = products[row].code; - - switch (key) { - case "code": - case "stock": - value = parseInt(value); - break; - default: - break; - } - products[row][key] = value; - this.setState({ products }); - - const body = JSON.stringify(products[row]); - - fetch(url("/api/product/" + code), { - headers: { "x-authentication": this.context.token }, - method: "PUT", - body, - }).then((response) => { - if (!response.ok) { - this.setState({ - error: response.status.toString() + " " + response.statusText, - }); - } - }); - } - - delProduct() { - const code = this.state.delete.code; - let products = this.state.products; - const index = products.findIndex((p) => p.code === code); - products.splice(index, 1); - this.setState({ products }); - this.modalClose(); - - fetch(url("/api/product/" + code), { - headers: { "x-authentication": this.context.token }, - method: "DELETE", - }).then((response) => { - if (!response.ok) { - this.setState({ - error: response.status.toString() + " " + response.statusText, - }); - } - }); - } - - modalClose() { - this.setState({ delete: { name: null } }); - } - - render() { - let alert = null; - if (this.state.error !== null) { - alert = ( - <Alert variant="danger"> - Ha ocurrido un error enviando cambios: {this.state.error} - </Alert> - ); - } - - const isAdmin = this.context.role === "admin"; - const entries = this.state.products.map((product, row) => { - return ( - <tr key={product.code}> - <EditableCell - onChange={(v) => this.update(row, "code", v)} - value={product.code} - ro={!isAdmin} - /> - <EditableCell - onChange={(v) => this.update(row, "name", v)} - value={product.name} - ro={!isAdmin} - /> - <EditableCell - onChange={(v) => this.update(row, "price", v)} - value={product.price} - ro={!isAdmin} - price - /> - <EditableCell - onChange={(v) => this.update(row, "stock", v)} - value={product.stock} - ro={!isAdmin} - /> - {isAdmin && ( - <td sm={1}> - <Button - variant="danger" - onClick={() => this.setState({ delete: product })} - > - - - </Button> - </td> - )} - </tr> - ); - }); +function ProductList() { + const [products, setProducts] = useState([]); + const entries = products.map((product) => { return ( - <Fetcher - url="/api/product" - onFetch={(products) => this.setState({ products })} - > - {alert} - <Table striped bordered hover responsive> - <thead> - <tr> - <th>codigo</th> - <th>Nombre</th> - <th>Precio</th> - <th>Existencias</th> - {isAdmin && <th sm={1}></th>} - </tr> - </thead> - <tbody>{entries}</tbody> - </Table> - - <Modal show={this.state.delete.name != null} onHide={this.modalClose}> - <Modal.Header closeButton> - <Modal.Title>Confirmar la elminicacion</Modal.Title> - </Modal.Header> - <Modal.Body> - ¿Borrar permanentemente el producto {this.state.delete.name}? - </Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={this.modalClose}> - Cancelar - </Button> - <Button variant="danger" onClick={this.delProduct}> - Eliminar - </Button> - </Modal.Footer> - </Modal> - </Fetcher> + <LinkContainer key={product.code} to={"/product/" + product.code}> + <tr> + <td>{product.code}</td> + <td>{product.name}</td> + <td>{printMoney(product.price)}</td> + <td>{product.stock}</td> + </tr> + </LinkContainer> ); - } + }); + + return ( + <Fetcher url="/api/product" onFetch={setProducts}> + <Table striped bordered hover responsive> + <thead> + <tr> + <th>codigo</th> + <th>Nombre</th> + <th>Precio</th> + <th>Existencias</th> + </tr> + </thead> + <tbody>{entries}</tbody> + </Table> + </Fetcher> + ); } export default ProductList; diff --git a/src/product/ShowProduct.js b/src/product/ShowProduct.js new file mode 100644 index 0000000..cbd9710 --- /dev/null +++ b/src/product/ShowProduct.js @@ -0,0 +1,144 @@ +import React, { useState, useContext } from "react"; +import { Redirect, useParams } from "react-router-dom"; +import { LinkContainer } from "react-router-bootstrap"; +import { Form, Col, Spinner, Alert, Button, Modal } from "react-bootstrap"; +import Sender from "../Sender"; +import Fetcher from "../Fetcher"; +import AuthContext from "../AuthContext"; +import { url, printMoney } from "../util"; + +function ShowProduct() { + const { code } = useParams(); + const auth = useContext(AuthContext); + const [product, _setProduct] = useState({ + price: 0, + stock: 0, + }); + const [name, setName] = useState(""); + const [newCode, setNewCode] = useState(code); + const [error, setError] = useState(""); + const [showDelete, setShowDelete] = useState(false); + const [loading, setLoading] = useState(false); + const [redirect, setRedirect] = useState(false); + + const setProduct = (p) => { + _setProduct(p); + setName(p.name); + }; + + if (redirect) { + return <Redirect to="/products" />; + } + + if (loading) { + return <Spinner animation="border" />; + } + + if (error) { + return <Alert variant="danger">{error}</Alert>; + } + + const handleClose = () => setShowDelete(false); + + const delProduct = () => { + setLoading(true); + fetch(url("/api/product/" + code), { + headers: { "x-authentication": auth.token }, + method: "DELETE", + }).then((response) => { + if (!response.ok) { + setError( + "No pudo eliminar el producto: " + + response.status.toString() + + " " + + response.statusText + ); + } else { + setRedirect(true); + } + }); + }; + + const invalid = !name; + + const body = { + name, + code: parseInt(newCode), + price: product.price, + stock: -1, + }; + + return ( + <Fetcher url={"/api/product/" + code} onFetch={setProduct} oneShot> + <Sender + url={"/api/product/" + code} + method="PUT" + body={body} + onSuccess={() => setRedirect(true)} + > + <Form.Row> + <Form.Group as={Col}> + <Form.Label>Codigo:</Form.Label> + <Form.Control + type="number" + placeholder="codigo" + value={newCode} + onChange={(e) => setNewCode(e.target.value)} + /> + </Form.Group> + <Form.Group as={Col} xs={12} sm={10}> + <Form.Label>Producto:</Form.Label> + <Form.Control + placeholder="nombre" + value={name} + onChange={(e) => setName(e.target.value)} + /> + </Form.Group> + </Form.Row> + <Form.Row> + <Form.Group as={Col}> + <Form.Label>Precio:</Form.Label> + <p>{printMoney(product.price)}</p> + </Form.Group> + <Form.Group as={Col}> + <Form.Label>Cantidad:</Form.Label> + <p>{product.stock}</p> + </Form.Group> + </Form.Row> + <Form.Row> + <Button type="submit" disabled={invalid}> + Guardar + </Button> + + <LinkContainer to="/products"> + <Button variant="secondary">Cancelar</Button> + </LinkContainer> + <Col className="text-right"> + <Button variant="danger" onClick={() => setShowDelete(true)}> + Eliminar + </Button> + </Col> + </Form.Row> + + <Modal show={showDelete} onHide={handleClose}> + <Modal.Header closeButton> + <Modal.Title>Confirmar la elminicacion</Modal.Title> + </Modal.Header> + <Modal.Body> + ¿Borrar permanentemente el producto {product.name}? + </Modal.Body> + <Modal.Footer> + <Button variant="secondary" onClick={handleClose}> + Cancelar + </Button> + <Button variant="danger" onClick={delProduct}> + Eliminar + </Button> + </Modal.Footer> + </Modal> + </Sender> + </Fetcher> + ); +} + +export default ShowProduct; -- GitLab