From 59096570ee114cbc181ab7b40fb9afff4974dc17 Mon Sep 17 00:00:00 2001 From: meskio <meskio@sindominio.net> Date: Tue, 29 Sep 2020 16:51:41 +0200 Subject: [PATCH] Topup UI --- api/topup.go | 2 +- src/App.js | 4 ++ src/Head.js | 1 + src/ShowTransaction.js | 7 +- src/Topup.js | 153 +++++++++++++++++++++++++++++++++++++++++ src/TransactionList.js | 10 ++- src/member.js | 1 - 7 files changed, 173 insertions(+), 5 deletions(-) create mode 100644 src/Topup.js diff --git a/api/topup.go b/api/topup.go index cc6397e..74ed855 100644 --- a/api/topup.go +++ b/api/topup.go @@ -43,7 +43,7 @@ func (a *api) AddTopup(adminNum int, w http.ResponseWriter, req *http.Request) { MemberNum: adminNum, Comment: topup.Comment, }, - Type: "toupup", + Type: "topup", Total: topup.Ammount, } err = a.db.Create(&transaction).Error diff --git a/src/App.js b/src/App.js index cc381c5..b9e81ad 100644 --- a/src/App.js +++ b/src/App.js @@ -4,6 +4,7 @@ import MemberList from './member'; import ProductList from './ProductList'; import Dashboard from './Dashboard'; import Purchase from './purchase/Purchase'; +import Topup from './Topup'; import ShowTransaction from './ShowTransaction'; import AuthContext from './AuthContext'; import SignIn from './SignIn'; @@ -27,6 +28,9 @@ function Panel(props) { <Route path="/purchase"> <Purchase /> </Route> + <Route path="/topup"> + <Topup /> + </Route> <Route path="/"> <Dashboard /> </Route> diff --git a/src/Head.js b/src/Head.js index f300d89..acb1efb 100644 --- a/src/Head.js +++ b/src/Head.js @@ -11,6 +11,7 @@ function Head(props) { adminNav = ( <NavDropdown title="Admin" id="admin"> <Nav.Link href="/members">Socias</Nav.Link> + <Nav.Link href="/topup">Recarga</Nav.Link> </NavDropdown> ); } diff --git a/src/ShowTransaction.js b/src/ShowTransaction.js index 304f0b6..6a48ae1 100644 --- a/src/ShowTransaction.js +++ b/src/ShowTransaction.js @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { useParams } from 'react-router-dom'; -import { Row, Col } from 'react-bootstrap'; +import { Container, Row, Col } from 'react-bootstrap'; import Fetcher from './Fetcher'; import ShowPurchase from './purchase/ShowPurchase'; import { printMoney, printDate } from './util'; @@ -14,12 +14,16 @@ function ShowTransaction() { case "purchase": show_list = <ShowPurchase purchase={transaction.purchase} />; break; + case "topup": + show_list = <p>{transaction.topup.comment}</p>; + break; default: show_list = null; } return ( <Fetcher url={"/api/transaction/"+id} onFetch={setTransaction} > + <Container> {show_list} <Row> <Col> @@ -29,6 +33,7 @@ function ShowTransaction() { <p className="text-right">{printDate(transaction.date)}</p> </Col> </Row> + </Container> </Fetcher> ); } diff --git a/src/Topup.js b/src/Topup.js new file mode 100644 index 0000000..ecf4fbc --- /dev/null +++ b/src/Topup.js @@ -0,0 +1,153 @@ +import React from 'react'; +import { Redirect } from 'react-router-dom'; +import Fetcher from './Fetcher'; +import { Container, Form, Col, Row, Button, Alert, Spinner, InputGroup } from 'react-bootstrap'; +import { Typeahead } from 'react-bootstrap-typeahead'; +import AuthContext from './AuthContext'; + +class Topup extends React.Component { + static contextType = AuthContext; + + constructor(props) { + super(props); + this.state = { + members: [], + numInvalid: false, + ammount: 0, + num: null, + name: "", + comment: "", + transactionId: null, + isLoading: false, + badAuth: false, + error: null + }; + } + + setNum(numStr) { + const num = parseInt(numStr); + const member = this.state.members.find(p => p.num === num); + let name = ""; + if (member) { + name = member.name; + } + this.setState({ + num: numStr, + name: name, + numInvalid: name === undefined + }); + } + + setName(member) { + this.setState({ + num: member.num, + name: member.name, + numInvalid: false, + }); + } + + send(e) { + e.preventDefault(); + + this.setState({isLoading: true, error: null}); + const body = JSON.stringify({ + member: parseInt(this.state.num), + comment: this.state.comment, + ammount: parseInt(this.state.ammount)*100 + }); + fetch("/api/topup", {headers: {'x-authentication': this.context.token}, method: "POST", body}) + .then(response => { + if (!response.ok) { + throw new Error(response.status.toString()+' '+response.statusText); + } + return response.json(); + }) + .then(transaction => { + this.setState({transactionId: transaction.ID, isLoading: false}); + }) + .catch(error => { + this.setState({isLoading: false, error: error.message}); + }); + } + + render() { + if (this.state.isLoading) { + return <Spinner animation="border" />; + } + + let alert; + if (this.state.error != null) { + alert = ( + <Alert variant="danger"> + Ha ocurrido un error enviando la compra: {this.state.error} + </Alert> + ); + } + if (this.state.transactionId !== null) { + return <Redirect to={"/transaction/"+this.state.transactionId} />; + } + + return ( + <Fetcher url="/api/member" onFetch={members => this.setState({ members })} > + <Container> + {alert} + <Form onSubmit={e => this.send(e)}> + <Form.Group as={Row}> + <Form.Label as="legend" column sm={2}>Socia</Form.Label> + <Col sm={5}> + <Form.Control + placeholder="numero de socia" + value={this.state.num} + onChange={e => this.setNum(e.target.value)} + isInvalid={this.state.numInvalid} + /> + </Col> + <Col sm={5}> + <Typeahead + id="name" + labelKey="name" + options={this.state.members} + onChange={m => this.setName(m[0])} + selected={this.state.name? [this.state.name]:[]} + /> + </Col> + </Form.Group> + <Form.Group as={Row}> + <Form.Label as="legend" column sm={2}>Recarga</Form.Label> + <Col sm={10}> + <InputGroup> + <Form.Control + type="number" + placeholder="euros" + value={this.state.ammount} + onChange={e => this.setState({ammount: e.target.value})} + /> + <InputGroup.Append> + <InputGroup.Text>.00 €</InputGroup.Text> + </InputGroup.Append> + </InputGroup> + </Col> + </Form.Group> + <Form.Group as={Row}> + <Form.Label as="legend" column sm={2}>Comentario</Form.Label> + <Col sm={10}> + <Form.Control + placeholder="..." + value={this.state.comment} + onChange={e => this.setState({comment: e.target.value})} + /> + </Col> + </Form.Group> + <Form.Group as={Row}> + <Col sm={{ span: 10, offset: 2 }}> + <Button type="submit">Envia</Button> + </Col> + </Form.Group> + </Form> + </Container> + </Fetcher> + ); + } +} + +export default Topup; diff --git a/src/TransactionList.js b/src/TransactionList.js index 9684dcc..98b1e2e 100644 --- a/src/TransactionList.js +++ b/src/TransactionList.js @@ -2,15 +2,21 @@ import React, { useState } from 'react'; import { Redirect } from 'react-router-dom'; import BootstrapTable from 'react-bootstrap-table-next'; import { FaShoppingBasket, FaMoneyBillAlt } from 'react-icons/fa'; +import { GiPayMoney, GiReceiveMoney } from 'react-icons/gi'; import Fetcher from './Fetcher'; import { printMoney, printDate } from './util'; const columns = [ {dataField: 'type', text: '', align: 'center', - formatter: cell => { + formatter: (cell, row) => { switch (cell) { case "purchase": return <FaShoppingBasket />; + case "topup": + if (row.total < 0) { + return <GiReceiveMoney />; + } + return <GiPayMoney />; default: return <FaMoneyBillAlt />; }; @@ -20,7 +26,7 @@ const columns = [ ] function rowStyle(row) { - if (["purchase"].includes(row.type)) { + if (row.total < 0) { return { backgroundColor: "#fcbabf" }; diff --git a/src/member.js b/src/member.js index 454b0cd..85cd9af 100644 --- a/src/member.js +++ b/src/member.js @@ -4,7 +4,6 @@ import Fetcher from './Fetcher'; import { printMoney } from './util'; class MemberList extends React.Component { - constructor(props) { super(props); this.state = { -- GitLab