Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • meskio/cicer
  • quique/cicer
2 results
Show changes
Showing
with 1679 additions and 358 deletions
import React, { useState } from "react";
import { Form, Row, Col } from "react-bootstrap";
import ProductPicker from "../product/ProductPicker";
import Fetcher from "../Fetcher";
import { date2string, time2string, daysAfterNow } from "../util";
function order2picks(order) {
return order.products.map((p) => {
const name = p.product !== null ? p.product.name : p.code;
return {
code: p.code,
name: name,
price: p.price,
};
});
}
function genBody(name, description, date, time, picks) {
const products = picks.map((p) => {
return { code: p.code, price: p.price };
});
let deadline = new Date(date);
const timeParts = time.split(":");
deadline.setHours(timeParts[0], timeParts[1]);
return { name, description, deadline, products };
}
function OrderEditor(props) {
const [name, _setName] = useState(props.order ? props.order.name : "");
const [description, _setDescription] = useState(
props.order ? props.order.description : ""
);
const [deadlineDate, _setDeadlineDate] = useState(
props.order
? date2string(new Date(props.order.deadline))
: date2string(daysAfterNow(3))
);
const [deadlineTime, _setDeadlineTime] = useState(
props.order ? time2string(new Date(props.order.deadline)) : "23:59"
);
const [picks, _setPicks] = useState(
props.order ? order2picks(props.order) : []
);
const [prevOrders, setPrevOrders] = useState(null);
const setName = (name) => {
_setName(name);
const body = genBody(name, description, deadlineDate, deadlineTime, picks);
props.onChange(body);
};
const setDescription = (description) => {
_setDescription(description);
const body = genBody(name, description, deadlineDate, deadlineTime, picks);
props.onChange(body);
};
const setDeadlineDate = (str) => {
_setDeadlineDate(str);
const body = genBody(name, description, str, deadlineTime, picks);
props.onChange(body);
};
const setDeadlineTime = (str) => {
_setDeadlineTime(str);
const body = genBody(name, description, deadlineDate, str, picks);
props.onChange(body);
};
const setPicks = (picks) => {
_setPicks(picks);
const body = genBody(name, description, deadlineDate, deadlineTime, picks);
props.onChange(body);
};
const fillOrder = (name) => {
if (!name) {
return;
}
const prevOrder = prevOrders.find((o) => o.name === name);
_setName(name);
_setDescription(prevOrder.description);
const prevPicks = order2picks(prevOrder);
setPicks(prevPicks);
const body = genBody(
name,
prevOrder.description,
deadlineDate,
deadlineTime,
prevPicks
);
props.onChange(body);
};
const orderOptions = prevOrders
? [<option key=""></option>].concat(
prevOrders.map((o) => <option key={o.name}>{o.name}</option>)
)
: "";
let order_picker;
if (!props.order) {
order_picker = (
<div>
<Form.Group as={Row}>
<Form.Label as="legend" column sm={3}>
Repetir pedido
</Form.Label>
<Col sm={9}>
<Fetcher url="/api/order/picks" onFetch={setPrevOrders}>
<Form.Select onChange={(e) => fillOrder(e.target.value)}>
{orderOptions}
</Form.Select>
</Fetcher>
</Col>
</Form.Group>
<hr />
</div>
);
}
return (
<div>
{order_picker}
<Form.Group as={Row}>
<Form.Label as="legend" column sm={3}>
Nombre
</Form.Label>
<Col sm={9}>
<Form.Control
placeholder="nombre del pedido"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</Col>
</Form.Group>
<Form.Group as={Row}>
<Form.Label as="legend" column sm={3}>
Descripción
</Form.Label>
<Col sm={9}>
<Form.Control
as="textarea"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</Col>
</Form.Group>
<Form.Group as={Row}>
<Form.Label as="legend" column sm={3}>
Fecha límite
</Form.Label>
<Col sm={5}>
<Form.Control
type="date"
value={deadlineDate}
onChange={(e) => setDeadlineDate(e.target.value)}
/>
</Col>
<Col sm={4}>
<Form.Control
type="time"
value={deadlineTime}
onChange={(e) => setDeadlineTime(e.target.value)}
/>
</Col>
</Form.Group>
<ProductPicker picks={picks} setPicks={setPicks} price />
</div>
);
}
export default OrderEditor;
import React, { useState } from "react";
import { Table, OverlayTrigger, Popover } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
import Fetcher from "../Fetcher";
import { Container, Card, Row, Col, Button } from "react-bootstrap";
import { printDate } from "../util";
function orderOverlay(order) {
const content = order.products.map((p) => {
if (p.product === null) {
return null;
}
return (
<div key={"OL" + order.ID + "-" + p.ID}>
{p.product.name}
<br />
</div>
);
});
return (
<Popover>
<Popover.Header>{order.description}</Popover.Header>
<Popover.Body>{content}</Popover.Body>
</Popover>
);
}
function OrderList() {
const [orders, setOrders] = useState([]);
const order_list = orders.map((o) => (
<Card as={Col} sm={4}>
<Card.Body>
<Card.Title>{o.name}</Card.Title>
<Card.Text>{o.description}</Card.Text>
<Button href={"/order/" + o.ID}>Realizar pedido</Button>
</Card.Body>
</Card>
const entries = orders.map((order) => (
<OverlayTrigger key={order.ID} overlay={orderOverlay(order)}>
<LinkContainer to={"/order/" + order.ID}>
<tr className={order.active ? "table-primary" : "table-secondary"}>
<td>{printDate(order.deadline)}</td>
<td>{order.name}</td>
</tr>
</LinkContainer>
</OverlayTrigger>
));
return (
<Container>
<Fetcher url={"/api/order/active"} onFetch={setOrders}>
<Row>{order_list}</Row>
</Fetcher>
</Container>
<Fetcher url="/api/order" onFetch={setOrders}>
<Table className="text-center" responsive>
<thead>
<tr>
<th>Fecha</th>
<th>Nombre</th>
</tr>
</thead>
<tbody>{entries}</tbody>
</Table>
</Fetcher>
);
}
......
import React from "react";
import {
Alert,
Spinner,
Form,
InputGroup,
Button,
Col,
Row,
} from "react-bootstrap";
import AuthContext from "../AuthContext";
import React, { useState } from "react";
import { Alert, Form, InputGroup, Button, Col, Row } from "react-bootstrap";
import { printMoney } from "../util";
import Sender from "../Sender";
class PurchaseOrder extends React.Component {
static contextType = AuthContext;
function PurchaseOrder({ order, purchase, onSend }) {
const [productAmounts, setProductAmounts] = useState(
listProductAmounts(order, purchase)
);
const [total, setTotal] = useState(calculateTotal(productAmounts));
const [noMoney, setNoMoney] = useState(false);
const [disabledError, setDisabledError] = useState(false);
constructor(props) {
super(props);
let order = props.order.products;
order = order.map((p) => {
p.amount = 0;
p.purchased = 0;
return p;
});
props.order.transactions.forEach((t) => {
t.order_purchase.forEach((purchase) => {
const i = order.findIndex((p) => p.code === purchase.product_code);
if (i) {
order[i].purchased += purchase.amount;
}
});
});
this.state = {
order: order,
total: 0,
isLoading: false,
noMoney: false,
error: null,
};
}
const onSuccess = (transaction) => {
onSend(transaction);
setNoMoney(false);
setDisabledError(false);
};
send(e) {
e.preventDefault();
const onError = (status) => {
if (status === 400) {
setNoMoney(true);
return true;
}
if (status === 402) {
setDisabledError(true);
return true;
}
return false;
};
this.setState({ isLoading: true, error: null, noMoney: false });
const purchase = this.state.order.map((p) => {
return {
product_code: p.code,
amount: parseInt(p.amount),
};
});
const body = JSON.stringify({
order: this.props.order.ID,
purchase,
});
console.log(body);
fetch("/api/order/purchase", {
headers: { "x-authentication": this.context.token },
method: "POST",
body,
}).then((response) => {
if (response.status === 400) {
this.setState({ isLoading: false, noMoney: true });
} else if (!response.ok) {
this.setState({
isLoading: false,
error: response.status.toString() + " " + response.statusText,
});
} else {
this.props.onSend(this.state.order, this.state.total);
}
});
let alert;
if (noMoney) {
alert = (
<Alert variant="warning">
No tienes suficiente dinero para realizar este pedido.
</Alert>
);
}
setAmount(index, amount) {
let order = this.state.order;
order[index].amount = amount;
const add = (acc, p) => acc + p.price * p.amount;
const total = order.reduce(add, 0);
this.setState({ order, total });
if (disabledError) {
alert = (
<Alert variant="warning">
No puedes realizar compras por que tu cuenta esta bloqueada.
</Alert>
);
}
render() {
if (this.state.isLoading) {
return <Spinner animation="border" />;
}
let alert;
if (this.state.noMoney) {
alert = (
<Alert variant="warning">
No tienes suficiente dinero para realizar este pedido.
</Alert>
);
} else if (this.state.error != null) {
alert = (
<Alert variant="danger">
Ha ocurrido un error enviando el pedido: {this.state.error}
</Alert>
);
}
const formEntries = listFormEntries(
productAmounts,
setProductAmounts,
setTotal
);
const formEntries = this.state.order.map((p, i) => (
<Form.Group key={p.code} as={Row}>
<Form.Label column className="text-right">
{p.name} ({p.code}):
</Form.Label>
return (
<Sender
url={"/api/order/" + order.ID + "/purchase"}
body={body(productAmounts)}
onSuccess={onSuccess}
onError={onError}
>
{alert}
{formEntries}
<Form.Group as={Row}>
<Col className="text-right">
<Button type="submit">
{purchase ? "Actualizar" : "Realizar"} pedido
</Button>
</Col>
<Col>
<InputGroup>
<Form.Control
type="number"
min="0"
placeholder="cantidad"
value={p.amount}
onChange={(e) => this.setAmount(i, e.target.value)}
/>
<InputGroup.Append>
<InputGroup.Text>{printMoney(p.price)}</InputGroup.Text>
</InputGroup.Append>
</InputGroup>
<h3>Total: {printMoney(total)}</h3>
</Col>
</Form.Group>
));
</Sender>
);
}
return (
<Form onSubmit={(e) => this.send(e)}>
{alert}
{formEntries}
<Form.Group as={Row}>
<Col className="text-right">
<Button type="submit">Realizar pedido</Button>
</Col>
<Col>
<h3>Total: {printMoney(this.state.total)}</h3>
</Col>
</Form.Group>
</Form>
);
}
function listFormEntries(productAmounts, setProductAmounts, setTotal) {
const setAmount = (index, amount) => {
const newProductAmount = { ...productAmounts[index], amount };
let newProductAmounts = [...productAmounts];
newProductAmounts[index] = newProductAmount;
const newTotal = calculateTotal(newProductAmounts);
setProductAmounts(newProductAmounts);
setTotal(newTotal);
};
return productAmounts.map((p, i) => (
<Form.Group key={p.code} as={Row}>
<Form.Label column className="text-right">
{p.product.name} ({p.code}):
</Form.Label>
<Col>
<InputGroup>
<Form.Control
type="number"
min="0"
placeholder="cantidad"
value={p.amount}
onChange={(e) => setAmount(i, e.target.value)}
/>
<InputGroup.Text>{printMoney(p.price)}</InputGroup.Text>
</InputGroup>
</Col>
</Form.Group>
));
}
function calculateTotal(productAmounts) {
const add = (acc, p) => acc + p.price * p.amount;
return productAmounts.reduce(add, 0);
}
function listProductAmounts(order, purchase) {
return order.products
.map((p) => {
p.amount = 0;
if (purchase) {
const my_purchase = purchase.find(
(e) => e.order_product.code === p.code
);
if (my_purchase) {
p.amount = my_purchase.amount;
}
}
return p;
})
.sort((p1, p2) => {
if (p1.product.name > p2.product.name) {
return 1;
}
if (p1.product.name < p2.product.name) {
return -1;
}
return 0;
});
}
function body(productAmounts) {
return productAmounts.map((p) => {
return {
order_product_id: p.ID,
amount: parseInt(p.amount),
};
});
}
export default PurchaseOrder;
import React from "react";
import React, { useState, useContext } from "react";
import { Navigate, useParams } from "react-router-dom";
import { LinkContainer } from "react-router-bootstrap";
import Fetcher from "../Fetcher";
import { Container, Row, Col, Badge } from "react-bootstrap";
import {
Row,
Col,
Badge,
Button,
ButtonGroup,
Spinner,
Alert,
Modal,
} from "react-bootstrap";
import PurchaseOrder from "./PurchaseOrder";
import { printDate } from "../util";
import Sender from "../Sender";
import { printDate, printMoney, url, deepEqual } from "../util";
import AuthContext from "../AuthContext";
import { printMoney } from "../util";
function getName(order_product) {
if (order_product.product !== null) {
return order_product.product.name;
}
return order_product.code;
}
function ShowOrderTransaction(props) {
const list = props.transaction.order_purchase.map((o) => (
<li key={o.product.code}>
{o.product.name} ({o.product.code}): {o.amount}
<li key={o.order_product.code}>
{getName(o.order_product)} ({o.order_product.code}): {o.amount}
</li>
));
return (
......@@ -23,22 +41,25 @@ function ShowOrderTransaction(props) {
function ShowOrderResults(props) {
let products = props.order.products.map((p) => {
p.amount = 0;
p.total = 0;
return p;
});
const transactions = props.order.transactions.map((t) => {
if (t.order_purchase === null) {
return null;
}
const list = t.order_purchase.map((purchase) => {
const i = products.findIndex((p) => p.code === purchase.product_code);
if (!i) {
const i = products.findIndex((p) => p.ID === purchase.order_product_id);
if (i === -1) {
return null;
}
products[i].amount += purchase.amount;
products[i].total += purchase.amount;
if (purchase.amount) {
const key =
t.member.num.toString() + "-" + purchase.product_code.toString();
t.member.num.toString() + "-" + purchase.order_product_id.toString();
return (
<li key={key}>
{products[i].name} {purchase.amount}
{getName(products[i])} {purchase.amount}
</li>
);
}
......@@ -47,6 +68,7 @@ function ShowOrderResults(props) {
return (
<li key={t.member.num}>
{t.member.name} ({t.member.num}):
{t.collected && <Badge bg="success">Recogido</Badge>}
<ul>{list}</ul>
</li>
);
......@@ -54,7 +76,7 @@ function ShowOrderResults(props) {
const product_list = products.map((p) => (
<li key={p.code}>
{p.name}: {p.amount}
{getName(p)}: {p.total}
</li>
));
return (
......@@ -68,84 +90,184 @@ function ShowOrderResults(props) {
);
}
class ShowOrder extends React.Component {
static contextType = AuthContext;
constructor(props) {
super(props);
this.state = {
order: {
products: [],
transactions: [],
},
transaction: null,
};
}
function ShowOrder() {
const { id } = useParams();
const [order, setOrder] = useState({
products: [],
transactions: [],
});
const [transaction, setTransaction] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [redirect, setRedirect] = useState(false);
const [refetch, setRefetch] = useState(0);
const [error, setError] = useState(null);
const [showDelete, setShowDelete] = useState(false);
const auth = useContext(AuthContext);
onSend(purchase, total) {
const order_purchase = purchase.map((p) => {
p.product = p;
return p;
});
const transaction = { order_purchase, total: -total };
this.setState({ transaction });
}
const onSend = (transaction) => {
setTransaction(transaction);
setRefetch(refetch + 1);
};
showTransaction() {
if (this.state.transaction) {
return (
<ShowOrderTransaction
order={this.state.order}
transaction={this.state.transaction}
/>
);
const setData = (data) => {
if (!deepEqual(data.order, order)) {
setOrder(data.order);
}
if (this.state.order.active) {
return (
<PurchaseOrder
order={this.state.order}
onSend={(p, t) => this.onSend(p, t)}
/>
);
if (!deepEqual(data.transaction, transaction)) {
setTransaction(data.transaction);
}
};
if (redirect) {
return <Navigate to="/" />;
}
setData(data) {
this.setState({ order: data.order, transaction: data.transaction });
let expired;
if (!order.active) {
expired = <Badge variant="info">finalizado</Badge>;
}
render() {
let expired;
if (!this.state.order.active) {
expired = <Badge variant="info">finalizado</Badge>;
let update_button;
let collect_button;
if (isLoading) {
update_button = <Spinner animation="border" />;
} else {
let deadline_week = new Date(order.deadline);
deadline_week.setDate(deadline_week.getDate() + 7);
if (deadline_week > Date.now()) {
if (order.arrived && transaction !== null && !transaction.collected) {
collect_button = (
<Sender
url={"/api/order/" + id + "/collected"}
method="PUT"
onSuccess={() => setRefetch(refetch + 1)}
>
<Button type="submit" variant="secondary">
Recogido
</Button>
</Sender>
);
}
if (order.member_num === parseInt(auth.num) || auth.role === "admin") {
let arrived_button;
if (!order.active && !order.arrived) {
arrived_button = (
<Sender
url={"/api/order/" + id + "/arrive"}
method="PUT"
onSuccess={() => setRefetch(refetch + 1)}
>
<Button type="submit" variant="dark">
Informar llegada
</Button>
</Sender>
);
}
update_button = (
<div>
<ButtonGroup>
<LinkContainer to={"/order/edit/" + id}>
<Button variant="info">Modificar</Button>
</LinkContainer>
<Button variant="danger" onClick={() => setShowDelete(true)}>
Cancelar
</Button>
</ButtonGroup>
<br />
{arrived_button}
</div>
);
}
}
}
const deadlineStr = printDate(order.deadline, true);
const handleClose = () => setShowDelete(false);
return (
<Fetcher url={"/api/order/" + id} onFetch={setData} refetch={refetch}>
{error && <Alert variant="danger">{error}</Alert>}
<Row>
<Col>
<h1>{order.name}</h1>
</Col>
<Col className="text-right">
<p>
Fecha limite:
<br />
{deadlineStr}
<br />
{expired}
</p>
{update_button}
{collect_button}
</Col>
</Row>
<p>{order.description}</p>
{showTransaction(order, transaction, auth.disabled, onSend)}
<ShowOrderResults order={order} />
const { id } = this.props.match.params;
<Modal show={showDelete} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Confirmar la elminicacion</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>¿Borrar permanentemente el pedido {order.name}?</p>
<p>Si aceptas devolvera el dinero a quien haya pedido productos.</p>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Cancelar
</Button>
<Button
variant="danger"
onClick={() =>
delorder(order, auth.token, setIsLoading, setError, setRedirect)
}
>
Eliminar
</Button>
</Modal.Footer>
</Modal>
</Fetcher>
);
}
function showTransaction(order, transaction, disabled, onSend) {
if (order.active && !disabled) {
return (
<Container>
<Fetcher
url={"/api/order/" + id}
onFetch={(data) => this.setData(data)}
>
<Row>
<Col>
<h1>{this.state.order.name}</h1>
</Col>
<Col>
<p className="text-right">
Fecha limite: {printDate(this.state.order.deadline)}
{expired}
</p>
</Col>
</Row>
<p>{this.state.order.description}</p>
{this.showTransaction()}
<ShowOrderResults order={this.state.order} />
</Fetcher>
</Container>
<PurchaseOrder
order={order}
purchase={transaction && transaction.order_purchase}
onSend={(t) => onSend(t)}
/>
);
}
if (transaction) {
return <ShowOrderTransaction order={order} transaction={transaction} />;
}
}
function delorder(order, token, setIsLoading, setError, setRedirect) {
setIsLoading(true);
fetch(url("/api/order/" + order.ID), {
method: "DELETE",
headers: { "x-authentication": token },
}).then((response) => {
if (!response.ok) {
setError(
"Ha ocurrido un error cancelando el pedido: " +
response.status.toString() +
" " +
response.statusText
);
setIsLoading(false);
} else {
setRedirect(true);
}
});
}
export default ShowOrder;
import React, { useState } from "react";
import Fetcher from "../Fetcher";
import { date2string } from "../util";
function transactionsPerMonth(transactions) {
let productsPerMonth = {};
const incProduct = (date, name, amount) => {
if (amount === 0) {
return;
}
const strDate = String(date.getFullYear()) + " - " + (date.getMonth() + 1);
if (productsPerMonth[strDate] === undefined) {
productsPerMonth[strDate] = {};
}
if (productsPerMonth[strDate][name] === undefined) {
productsPerMonth[strDate][name] = amount;
} else {
productsPerMonth[strDate][name] += amount;
}
};
transactions.forEach((t) => {
const date = new Date(Date.parse(t.date));
const monthDate = new Date(date.getFullYear(), date.getMonth());
switch (t.type) {
case "purchase":
t.purchase.forEach((e) =>
incProduct(monthDate, e.product.name, e.amount)
);
break;
case "order":
t.order_purchase.forEach((e) =>
incProduct(monthDate, e.order_product.product.name, e.amount)
);
break;
default:
return;
}
});
return productsPerMonth;
}
function AnnualReport() {
const [data, setData] = useState([]);
const setTransactions = (transactions) => {
if (data.length == 0) {
const d = transactionsPerMonth(transactions);
setData(d);
}
};
let dates = Object.keys(data);
dates = dates.sort((a, b) => b.localeCompare(a));
const months = dates.map((date) => {
let products = Object.keys(data[date]);
products = products.sort((a, b) =>
data[date][a] < data[date][b] ? 1 : -1
);
const productList = products.map((product) => (
<li key={date + "-" + product}>
{product}: {data[date][product]}
</li>
));
return (
<div>
<h3>{date}</h3>
<ul>{productList}</ul>
</div>
);
});
const now = new Date();
const end = date2string(now);
const start = date2string(new Date(now.getFullYear() - 1, now.getMonth()));
return (
<Fetcher
url={
"/api/transaction?start-date=" +
start +
"&end-date=" +
end +
"&type=purchase&type=order"
}
onFetch={setTransactions}
oneShot
>
{months}
</Fetcher>
);
}
export default AnnualReport;
import React, { useState } from "react";
import { Navigate } from "react-router-dom";
import { Col, Row, Form, Button } from "react-bootstrap";
import Sender from "../Sender";
import PriceEditor from "../PriceEditor";
function CreateProduct() {
const [code, setCode] = useState("");
const [name, setName] = useState("");
const [price, setPrice] = useState(0);
const [stock, setStock] = useState(0);
const [redirect, setRedirect] = useState(null);
if (redirect != null) {
return <Navigate to={"/product/" + redirect.code} />;
}
const body = () => {
return {
code: parseInt(code),
price: price,
stock: parseInt(stock),
name,
};
};
const disabled = isNaN(price) || isNaN(parseInt(code)) || !name;
return (
<div>
<h2 className="text-center">Crear un nuevo código de producto</h2>
<Sender url="/api/product" body={body} onSuccess={setRedirect}>
<Row>
<Form.Group as={Col}>
<Form.Label>Codigo:</Form.Label>
<Form.Control
type="number"
placeholder="codigo"
value={code}
onChange={(e) => setCode(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>
</Row>
<Row>
<Form.Group as={Col}>
<Form.Label>Precio:</Form.Label>
<PriceEditor value={price} onChange={setPrice} />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>Cantidad:</Form.Label>
<Form.Control
type="number"
placeholder="cantidad"
value={stock}
onChange={(e) => setStock(e.target.value)}
/>
</Form.Group>
</Row>
<Button variant="primary" disabled={disabled} type="submit">
Añadir
</Button>
</Sender>
</div>
);
}
export default CreateProduct;
import React from "react";
import React, { useState } from "react";
import { Table } from "react-bootstrap";
import Fetcher from "./Fetcher";
import { printMoney } from "./util";
import { LinkContainer } from "react-router-bootstrap";
import Fetcher from "../Fetcher";
import { printMoney } from "../util";
class ProductList extends React.Component {
constructor(props) {
super(props);
this.state = {
products: [],
};
}
function ProductList() {
const [products, setProducts] = useState([]);
render() {
const entries = this.state.products.map((product) => {
return (
<tr key={product.code}>
const entries = products.map((product) => {
return (
<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={(products) => this.setState({ products })}
>
<Table striped bordered hover>
return (
<div>
<h2 className="text-center">Productos en almacén</h2>
<Fetcher url="/api/product" onFetch={setProducts}>
<Table striped bordered hover responsive>
<thead>
<tr>
<th>codigo</th>
<th>Nombre</th>
<th>digo</th>
<th>Producto</th>
<th>Precio</th>
<th>Existencias</th>
</tr>
......@@ -40,8 +36,8 @@ class ProductList extends React.Component {
<tbody>{entries}</tbody>
</Table>
</Fetcher>
);
}
</div>
);
}
export default ProductList;
import React from "react";
import { Form, Row, Col, Button } from "react-bootstrap";
import { Typeahead } from "react-bootstrap-typeahead";
import Fetcher from "./Fetcher";
import { printMoney } from "./util";
import Fetcher from "../Fetcher";
import { printMoney } from "../util";
import PriceEditor from "../PriceEditor";
class ProductPicker extends React.Component {
constructor(props) {
......@@ -14,15 +15,23 @@ class ProductPicker extends React.Component {
}
delPick(index) {
let picks = this.props.picks;
let picks = [...this.props.picks];
picks.splice(index, 1);
this.props.setPicks(picks);
}
setAmount(index, amount) {
let picks = this.props.picks;
let picks = [...this.props.picks];
picks[index].amount = parseInt(amount);
this.props.setPicks(picks);
// trick it to redraw on each change
this.setState({ code: this.state.code });
}
setPrice(index, price) {
let picks = [...this.props.picks];
picks[index].price = price;
this.props.setPicks(picks);
}
setCode(codeStr) {
......@@ -36,40 +45,73 @@ class ProductPicker extends React.Component {
}
pickProduct(product) {
let picks = this.props.picks;
let picks = [...this.props.picks];
if (picks.find((p) => product.code === p.code)) {
return;
}
let amount = 0;
if (this.props.amount) {
amount = 1;
}
picks.push({
code: product.code,
name: product.name,
origPrice: product.price,
price: product.price,
amount: 1,
stock: product.stock,
amount,
});
this.props.setPicks(picks);
this.setState({ code: "" });
}
setProducts(products) {
if (this.props.amount) {
products = products.filter((p) => p.stock > 0);
}
this.setState({ products });
}
render() {
const rows = this.props.picks.map((p, i) => {
let price;
if (!this.props.noPrice) {
price = (
<Col>
{this.props.price ? (
<PriceEditor
value={p.price}
onChange={(price) => this.setPrice(i, price)}
/>
) : (
printMoney(p.price) + ""
)}
</Col>
);
}
return (
<Form.Group key={p.code} as={Row}>
<Col>
<p>{p.code}</p>
</Col>
<Col sm={4}>
<Col xs={4}>
<p>{p.name}</p>
</Col>
<Col>{printMoney(p.price) + ""}</Col>
{this.props.amount && (
{price}
{(this.props.amount || this.props.stock) && (
<Col>
<Form.Control
type="number"
min="1"
min={this.props.amount ? "1" : Number.MIN_SAFE_INTEGER}
max={this.props.amount ? p.stock : Number.MAX_SAFE_INTEGER}
placeholder="cantidad"
value={p.amount}
onChange={(e) => this.setAmount(i, e.target.value)}
/>
</Col>
)}
<Col sm={1}>
<Col xs={1}>
<Button variant="danger" onClick={() => this.delPick(i)}>
-
</Button>
......@@ -79,26 +121,25 @@ class ProductPicker extends React.Component {
});
return (
<Fetcher
url="/api/product"
onFetch={(products) => this.setState({ products })}
>
<Fetcher url="/api/product" onFetch={(p) => this.setProducts(p)}>
<Row>
<Col>
<h6>Código</h6>
</Col>
<Col sm={4}>
<Col xs={4}>
<h6>Nombre</h6>
</Col>
<Col>
<h6>Precio</h6>
</Col>
{this.props.amount && (
{!this.props.noPrice && (
<Col>
<h6>Precio</h6>
</Col>
)}
{(this.props.amount || this.props.stock) && (
<Col>
<h6>Cantidad</h6>
</Col>
)}
<Col sm={1}></Col>
<Col xs={1}></Col>
</Row>
{rows}
<Form.Group as={Row}>
......@@ -109,18 +150,19 @@ class ProductPicker extends React.Component {
onChange={(e) => this.setCode(e.target.value)}
/>
</Col>
<Col sm={4}>
<Col xs={12} sm={4}>
<Typeahead
id="product-name"
placeholder="nombre"
labelKey="name"
options={this.state.products}
onChange={(name) => this.pickProduct(name[0])}
selected={[]}
/>
</Col>
<Col></Col>
{this.props.amount && <Col></Col>}
<Col sm={1}></Col>
{!this.props.noPrice && <Col xs={false} sm></Col>}
{(this.props.amount || this.props.stock) && <Col xs={false} sm></Col>}
<Col xs={false} sm={1}></Col>
</Form.Group>
</Fetcher>
);
......
import React, { useState, useContext } from "react";
import { Navigate, useParams } from "react-router-dom";
import { LinkContainer } from "react-router-bootstrap";
import { Form, Col, Row, 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) => {
if (p.price == 0) {
_setProduct(p);
setName(p.name);
}
};
if (redirect) {
return <Navigate 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)}
>
<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>
</Row>
<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>
</Row>
<Row>
<Button type="submit" disabled={invalid}>
Guardar
</Button>
&nbsp;
<LinkContainer to="/products">
<Button variant="secondary">Cancelar</Button>
</LinkContainer>
<Col className="text-right">
<Button variant="danger" onClick={() => setShowDelete(true)}>
Eliminar
</Button>
</Col>
</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;
import React from "react";
import { Redirect } from "react-router-dom";
import { Container, Row, Col, Button, Alert, Spinner } from "react-bootstrap";
import ProductPicker from "../ProductPicker";
import AuthContext from "../AuthContext";
import React, { useState, useRef } from "react";
import { Navigate } from "react-router-dom";
import { Row, Col, Button, Alert, Modal } from "react-bootstrap";
import ShowPurchase from "./ShowPurchase";
import ProductPicker from "../product/ProductPicker";
import MemberPicker from "../member/MemberPicker";
import Sender from "../Sender";
import { printMoney } from "../util";
class Purchase extends React.Component {
static contextType = AuthContext;
function Purchase(props) {
const [purchase, setPurchase] = useState([]);
const [member, setMember] = useState(null);
const [total, setTotal] = useState(0);
const [transaction, setTransaction] = useState(null);
const [noMoney, setNoMoney] = useState(false);
const [disabledError, setDisabledError] = useState(false);
const [showConfirmation, setShowConfirmation] = useState(false);
const sender = useRef(null);
constructor(props) {
super(props);
this.state = {
purchase: [],
total: 0,
transactionId: null,
isLoading: false,
noMoney: false,
error: null,
};
const handleClose = () => setShowConfirmation(false);
const submit = () => {
handleClose();
sender.current.click();
};
if (transaction !== null) {
return <Navigate to={"/transaction/" + transaction.ID} />;
}
setPurchase(purchase) {
const url =
props.member && member
? "/api/member/" + member.num + "/purchase"
: "/api/purchase";
const updatePurchase = (newPurchase) => {
const add = (acc, p) => acc + p.price * p.amount;
const total = purchase.reduce(add, 0);
this.setState({ purchase, total });
}
const newTotal = newPurchase.reduce(add, 0);
setPurchase(newPurchase);
setTotal(newTotal);
};
send() {
this.setState({ isLoading: true, error: null, noMoney: false });
const body = JSON.stringify(
this.state.purchase.map((p) => {
return {
code: p.code,
amount: p.amount,
};
})
);
fetch("/api/purchase", {
headers: { "x-authentication": this.context.token },
method: "POST",
body,
})
.then((response) => {
if (response.status === 400) {
var error = new Error("Not enough money");
error.name = "not-money";
throw error;
} else if (!response.ok) {
throw new Error(
response.status.toString() + " " + response.statusText
);
}
return response.json();
})
.then((p) => {
this.setState({ transactionId: p.ID, isLoading: false });
})
.catch((error) => {
if (error.name === "not-money") {
this.setState({ isLoading: false, noMoney: true });
} else {
this.setState({ isLoading: false, error: error.message });
}
});
}
const body = () =>
purchase.map((p) => {
return {
code: p.code,
amount: p.amount,
};
});
render() {
if (this.state.isLoading) {
return <Spinner animation="border" />;
const onError = (status) => {
if (status === 400) {
setNoMoney(true);
return true;
}
if (status === 402) {
setDisabledError(true);
return true;
}
return false;
};
const disabled = (props.member && !member) || total === 0;
let alert;
if (this.state.noMoney) {
alert = (
const displayedPurchase = purchase.map((p) => {
return {
code: p.code,
product: p,
amount: p.amount,
price: p.price,
};
});
return (
<Sender url={url} body={body} onSuccess={setTransaction} onError={onError}>
<button style={{ display: "none" }} type="submit" ref={sender} />
{noMoney && (
<Alert variant="warning">
No tienes suficiente dinero para realizar esta compra.
</Alert>
);
} else if (this.state.error != null) {
alert = (
<Alert variant="danger">
Ha ocurrido un error enviando la compra: {this.state.error}
)}
{disabledError && (
<Alert variant="warning">
No puedes realizar compras por que tu cuenta esta bloqueada.
</Alert>
);
}
if (this.state.transactionId !== null) {
return <Redirect to={"/transaction/" + this.state.transactionId} />;
}
)}
return (
<Container>
{alert}
<ProductPicker
amount
picks={this.state.purchase}
setPicks={(purchase) => this.setPurchase(purchase)}
/>
<br />
<Row>
<Col>
<h3>Total: {printMoney(this.state.total)}</h3>
</Col>
<Col>
<div className="text-right">
<Button onClick={() => this.send()}>Finalizar compra</Button>
</div>
</Col>
</Row>
</Container>
);
}
{props.member && <MemberPicker member={member} onChange={setMember} />}
<ProductPicker amount picks={purchase} setPicks={updatePurchase} />
<br />
<Row>
<Col>
<h3>Total: {printMoney(total)}</h3>
</Col>
<Col>
<div className="text-right">
<Button
variant="primary"
disabled={disabled}
onClick={() => setShowConfirmation(true)}
>
Finalizar compra
</Button>
</div>
</Col>
</Row>
<Modal show={showConfirmation} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Confirmar la compra</Modal.Title>
</Modal.Header>
<Modal.Body>
<ShowPurchase purchase={displayedPurchase} />
<h3>Total: {printMoney(total)}</h3>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Cancelar
</Button>
<Button variant="primary" disabled={disabled} onClick={submit}>
Confirmar
</Button>
</Modal.Footer>
</Modal>
</Sender>
);
}
export default Purchase;
import React, { useState, useContext } from "react";
import { Table, Form, Row, Col, Button } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
import ProductPicker from "../product/ProductPicker";
import MemberPicker from "../member/MemberPicker";
import AuthContext from "../AuthContext";
import Fetcher from "../Fetcher";
import { date2string, daysAfterNow, printDate } from "../util";
function PurchaseList() {
const auth = useContext(AuthContext);
const [purchases, setPurchases] = useState([]);
const [startDate, setStartDate] = useState(date2string(daysAfterNow(-30)));
const [endDate, setEndDate] = useState(date2string(new Date()));
const [members, setMembers] = useState([]);
const [products, setProducts] = useState([]);
const setTransactions = (transactions) => {
const pur = transactions
.map((t) => {
let purchases = t.purchase;
if (products.length !== 0) {
purchases = purchases.filter(
(p) => products.find((prod) => p.code === prod.code) !== undefined
);
}
return purchases.map((p) => {
return {
ID: p.ID,
date: t.date,
product: p.product.name,
code: p.code,
amount: p.amount,
member: t.member.name,
num: t.member.num,
transaction: t.ID,
};
});
})
.flat();
setPurchases(pur);
};
const admin = auth.role === "admin";
let query = "type=purchase&start-date=" + startDate + "&end-date=" + endDate;
query += members.map((m) => "&member=" + m.num).join("");
query += products.map((p) => "&product=" + p.code).join("");
const entries = purchases.map((p) => {
return (
<LinkContainer to={"/transaction/" + p.transaction} key={p.ID}>
<tr>
<td>{printDate(p.date)}</td>
<td>{p.product}</td>
<td>{p.amount}</td>
{admin && <td>{p.member}</td>}
</tr>
</LinkContainer>
);
});
const setMember = (m) => {
let newMembers = [...members];
newMembers.push(m);
setMembers(newMembers);
};
const delMember = (index) => {
let newMembers = [...members];
newMembers.splice(index, 1);
setMembers(newMembers);
};
let memberPicker;
if (admin) {
const rows = members.map((member, i) => (
<Row key={member.num}>
<Col>
<p>{member.name}</p>
</Col>
<Col xs={1}>
<Button variant="danger" onClick={() => delMember(i)}>
-
</Button>
</Col>
</Row>
));
memberPicker = (
<Col xs={12} sm={6}>
{rows}
<MemberPicker onChange={setMember} allowDisabled />
</Col>
);
}
return (
<div>
<Form>
<Row>
<Form.Group as={Col}>
<Form.Label>Desde:</Form.Label>
<Form.Control
type="date"
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
max={endDate}
/>
</Form.Group>
<Form.Group as={Col}>
<Form.Label>Hasta:</Form.Label>
<Form.Control
type="date"
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
min={startDate}
max={Date.now()}
/>
</Form.Group>
</Row>
<Row>
<Col>
<ProductPicker picks={products} setPicks={setProducts} noPrice />
</Col>
{memberPicker}
</Row>
</Form>
<br />
<Fetcher
url={(admin ? "/api/transaction?" : "/api/transaction/mine?") + query}
onFetch={setTransactions}
>
<Table className="text-center" responsive>
<thead>
<tr>
<th>Fecha</th>
<th>Producto</th>
<th>Cantidad</th>
{admin && <th>Socia</th>}
</tr>
</thead>
<tbody>{entries}</tbody>
</Table>
</Fetcher>
</div>
);
}
export default PurchaseList;
import React from "react";
import BootstrapTable from "react-bootstrap-table-next";
import { Table } from "react-bootstrap";
import { printMoney } from "../util";
const columns = [
{ dataField: "code", text: "Codigo" },
{ dataField: "product.name", text: "Nombre" },
{
dataField: "price",
text: "Precio",
formatter: (cell) => printMoney(cell) + "",
},
{ dataField: "amount", text: "Cantidad" },
];
function ShowPurchase(props) {
const entries = props.purchase.map((p) => {
return (
<tr key={p.code}>
<td>{p.code}</td>
<td>{p.product !== null ? p.product.name : p.code}</td>
<td>{printMoney(p.price) + ""}</td>
<td>{p.amount}</td>
</tr>
);
});
return (
<BootstrapTable keyField="code" data={props.purchase} columns={columns} />
<Table striped bordered hover responsive>
<thead>
<tr>
<th>Codigo</th>
<th>Nombre</th>
<th>Precio</th>
<th>Cantidad</th>
</tr>
</thead>
<tbody>{entries}</tbody>
</Table>
);
}
......
import React, { useState } from "react";
import { Table } from "react-bootstrap";
import icon from "./icon";
import TransactionTr from "./TransactionTr";
import Fetcher from "../Fetcher";
import { printMoney, printDate } from "../util";
function MyTransactions() {
const [transactions, setTransactions] = useState([]);
const entries = transactions.map((transaction) => {
return (
<TransactionTr key={transaction.ID} transaction={transaction}>
<td>{icon(transaction)}</td>
<td>{printDate(transaction.date)}</td>
<td>{printMoney(transaction.total) + ""}</td>
</TransactionTr>
);
});
return (
<Fetcher url="/api/transaction/mine" onFetch={setTransactions}>
<Table className="text-center">
<thead>
<tr>
<th></th>
<th>Fecha</th>
<th>Cantidad</th>
</tr>
</thead>
<tbody>{entries}</tbody>
</Table>
</Fetcher>
);
}
export default MyTransactions;
import React, { useState } from "react";
import { useParams, Redirect } from "react-router-dom";
import { Container, Row, Col } from "react-bootstrap";
import Fetcher from "./Fetcher";
import ShowPurchase from "./purchase/ShowPurchase";
import { printMoney, printDate } from "./util";
import { useParams, Navigate } from "react-router-dom";
import { Row, Col } from "react-bootstrap";
import Fetcher from "../Fetcher";
import ShowPurchase from "../purchase/ShowPurchase";
import { printMoney, printDate, printTransactionID } from "../util";
function ShowTransaction() {
const { id } = useParams();
const [transaction, setTransaction] = useState({});
const [transaction, setTransaction] = useState({ ID: 0 });
let show_list;
switch (transaction.type) {
......@@ -18,26 +18,35 @@ function ShowTransaction() {
show_list = <p>{transaction.topup.comment}</p>;
break;
case "order":
return <Redirect to={"/order/" + transaction.order.ID} />;
return <Navigate to={"/order/" + transaction.order.ID} />;
case "refund":
return <Redirect to={"/order/" + transaction.refund.ID} />;
return <Navigate to={"/order/" + transaction.refund.ID} />;
case "dues":
return <Navigate to={"/dues"} />;
default:
show_list = null;
}
return (
<Fetcher url={"/api/transaction/" + id} onFetch={setTransaction}>
<Container>
{show_list}
<Row>
<Col>
<h3>Total: {printMoney(transaction.total)}</h3>
</Col>
<Col>
<p className="text-right">{printDate(transaction.date)}</p>
</Col>
</Row>
</Container>
{show_list}
<Row>
<Col>
<h3>Total: {printMoney(transaction.total)}</h3>
{transaction.member && <p>{transaction.member.name}</p>}
</Col>
<Col>
<p className="text-right">
{printDate(transaction.date)} {printTransactionID(transaction)}
{transaction.proxy && (
<span>
<br />
por: {transaction.proxy.name}
</span>
)}
</p>
</Col>
</Row>
</Fetcher>
);
}
......
import React, { useState } from "react";
import { Table, Form, Row, Col } from "react-bootstrap";
import icon from "./icon";
import TransactionTr from "./TransactionTr";
import MemberPicker from "../member/MemberPicker";
import Fetcher from "../Fetcher";
import {
date2string,
daysAfterNow,
printMoney,
printDate,
printTransactionID,
} from "../util";
const engType = {
compra: "purchase",
recarga: "topup",
pedido: "order",
devolucion: "refund",
cuota: "dues",
};
function TransactionList() {
const [transactions, setTransactions] = useState([]);
const [startDate, setStartDate] = useState(date2string(daysAfterNow(-30)));
const [endDate, setEndDate] = useState(date2string(new Date()));
const [member, setMember] = useState(null);
const [proxy, setProxy] = useState(null);
const [types, setTypes] = useState([]);
let query = "start-date=" + startDate + "&end-date=" + endDate;
if (member) {
query += "&member=" + member.num;
}
if (proxy) {
query += "&proxy=" + proxy.num;
}
query += types.map((t) => "&type=" + engType[t]).join("");
const onTypeChange = (e) => {
const newTypes = Array.from(e.target.selectedOptions, (o) => o.value);
setTypes(newTypes);
};
const entries = transactions.map((transaction) => {
let memberStr = "";
if (transaction.member) {
memberStr = transaction.member.name + " (" + transaction.member.num + ")";
}
return (
<TransactionTr key={transaction.ID} transaction={transaction}>
<td>{icon(transaction)}</td>
<td>{printTransactionID(transaction)}</td>
<td>{printDate(transaction.date)}</td>
<td>{memberStr}</td>
<td>{transaction.proxy ? transaction.proxy.name : ""}</td>
<td>{printMoney(transaction.total) + ""}</td>
</TransactionTr>
);
});
return (
<div>
<Form>
<Row>
<Form.Group as={Col}>
<Form.Label>Desde:</Form.Label>
<Form.Control
type="date"
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
max={endDate}
/>
</Form.Group>
<Form.Group as={Col}>
<Form.Label>Hasta:</Form.Label>
<Form.Control
type="date"
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
min={startDate}
max={Date.now()}
/>
</Form.Group>
<Form.Group as={Col}>
<Form.Label>Typo:</Form.Label>
<Form.Select value={types} onChange={onTypeChange} multiple>
<option>compra</option>
<option>recarga</option>
<option>pedido</option>
<option>devolucion</option>
<option>cuota</option>
</Form.Select>
</Form.Group>
</Row>
<MemberPicker member={member} onChange={setMember} allowDisabled />
<MemberPicker
member={proxy}
onChange={setProxy}
text="Por"
allowDisabled
/>
</Form>
<br />
<Fetcher url={"/api/transaction?" + query} onFetch={setTransactions}>
<Table className="text-center" responsive>
<thead>
<tr>
<th></th>
<th>ID</th>
<th>Fecha</th>
<th>Socia</th>
<th>Por</th>
<th>Cantidad</th>
</tr>
</thead>
<tbody>{entries}</tbody>
</Table>
</Fetcher>
</div>
);
}
export default TransactionList;
import React, { useState, useRef } from "react";
import { Overlay, Popover } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
function transactionOverlay(transaction) {
let title;
let content;
switch (transaction.type) {
case "purchase":
title = "compra";
content =
transaction.purchase
.map((p) => (p.product !== null ? p.product.name : p.code))
.join(",") + ".";
break;
case "topup":
if (transaction.total < 0) {
title = "devolución";
} else {
title = "recarga";
}
content = transaction.topup.comment;
break;
case "order":
if (!transaction.order) {
title = "pedido";
break;
}
title = "pedido de " + transaction.order.name;
content = transaction.order_purchase.map((p) => {
const name =
p.order_product.product !== null
? p.order_product.product.name
: p.order_product.code;
return (
<div key={"O" + transaction.ID + "-" + p.order_product.ID}>
{name + ": " + p.amount}
<br />
</div>
);
});
break;
case "refund":
if (!transaction.refund) {
title = "devolución";
break;
}
title = "devolución de " + transaction.refund.name;
break;
case "dues":
title = "cuota mensual";
break;
default:
title = "transacción";
}
return (
<Popover>
<Popover.Header>{title}</Popover.Header>
{content && <Popover.Body>{content}</Popover.Body>}
</Popover>
);
}
function TransactionTr({ transaction, children }) {
const [show, setShow] = useState(false);
const target = useRef(null);
const colorClass = transaction.total < 0 ? "table-danger" : "table-success";
return (
<>
<LinkContainer to={"/transaction/" + transaction.ID} key={transaction.ID}>
<tr
className={colorClass}
ref={target}
onMouseEnter={() => setShow(true)}
onMouseLeave={() => setShow(false)}
>
{children}
</tr>
</LinkContainer>
<Overlay target={target.current} show={show}>
{transactionOverlay(transaction)}
</Overlay>
</>
);
}
export default TransactionTr;
import React from "react";
import { FaShoppingBasket, FaMoneyBillAlt } from "react-icons/fa";
import { FaHouseUser } from "react-icons/fa6";
import { GiPayMoney, GiReceiveMoney } from "react-icons/gi";
import { HiClipboardCopy, HiClipboardList } from "react-icons/hi";
function icon(transaction) {
switch (transaction.type) {
case "purchase":
return <FaShoppingBasket />;
case "topup":
if (transaction.total < 0) {
return <GiReceiveMoney />;
}
return <GiPayMoney />;
case "order":
return <HiClipboardList />;
case "refund":
return <HiClipboardCopy />;
case "dues":
return <FaHouseUser />;
default:
return <FaMoneyBillAlt />;
}
}
export default icon;
......@@ -2,8 +2,94 @@ function printMoney(money) {
return (money / 100).toFixed(2);
}
function printDate(date) {
return new Date(date).toLocaleDateString();
function printDate(date, time) {
const d = new Date(date);
let dateStr = d.toLocaleDateString();
if (time) {
dateStr += " " + d.toLocaleTimeString();
}
return dateStr;
}
export { printMoney, printDate };
function printRole(role) {
switch (role) {
case "admin":
return "admin";
case "order":
return "pedidos";
default:
return "socia";
}
}
function printTransactionID(transaction) {
return printID("T", transaction);
}
function printInventaryID(inventary) {
return printID("I", inventary);
}
function printSupplierID(supplier) {
return printID("S", supplier);
}
function printID(pre, item) {
return pre + "-" + item.ID.toString().padStart(5, "0");
}
function printMember(member) {
if (member) {
return member.name + " (" + member.num + ")";
} else {
return "-";
}
}
function url(path) {
let api = process.env.REACT_APP_API;
if (!api) {
api = "";
}
return api + path;
}
function date2string(date) {
return date.toISOString().split("T")[0];
}
function daysAfterNow(days) {
let date = new Date();
date.setDate(date.getDate() + days);
return date;
}
function time2string(date) {
return date.toTimeString().slice(0, 5);
}
function deepEqual(obj1, obj2) {
if (obj1 instanceof Object) {
if (!(obj2 instanceof Object)) {
return false;
}
return Object.keys(obj1).every((key) => deepEqual(obj1[key], obj2[key]));
}
return obj1 === obj2;
}
export {
printMoney,
printDate,
printRole,
printMember,
url,
date2string,
time2string,
daysAfterNow,
printTransactionID,
printInventaryID,
printSupplierID,
deepEqual,
};