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