Přechody mezi stavy Pizza Day
This commit is contained in:
parent
d5d0b88d3c
commit
dfce063de7
@ -38,6 +38,22 @@ export const deletePizzaDay = async (login) => {
|
||||
return await api.post<any, any>('/api/deletePizzaDay', JSON.stringify({ login }));
|
||||
}
|
||||
|
||||
export const lockPizzaDay = async (login) => {
|
||||
return await api.post<any, any>('/api/lockPizzaDay', JSON.stringify({ login }));
|
||||
}
|
||||
|
||||
export const unlockPizzaDay = async (login) => {
|
||||
return await api.post<any, any>('/api/unlockPizzaDay', JSON.stringify({ login }));
|
||||
}
|
||||
|
||||
export const finishOrder = async (login) => {
|
||||
return await api.post<any, any>('/api/finishOrder', JSON.stringify({ login }));
|
||||
}
|
||||
|
||||
export const finishDelivery = async (login) => {
|
||||
return await api.post<any, any>('/api/finishDelivery', JSON.stringify({ login }));
|
||||
}
|
||||
|
||||
export const updateChoice = async (name: string, choice: number | null) => {
|
||||
return await api.post<any, any>('/api/updateChoice', JSON.stringify({ name, choice }));
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import { EVENT_DISCONNECT, EVENT_MESSAGE, SocketContext } from './context/socket';
|
||||
import { addPizza, createPizzaDay, deletePizzaDay, getData, getFood, getPizzy, removePizza, updateChoice } from './Api';
|
||||
import { addPizza, createPizzaDay, deletePizzaDay, finishDelivery, finishOrder, getData, getFood, getPizzy, lockPizzaDay, removePizza, unlockPizzaDay, updateChoice } from './Api';
|
||||
import { useAuth } from './context/auth';
|
||||
import Login from './Login';
|
||||
import { Locations, ClientData, Pizza, PizzaOrder } from './Types';
|
||||
import { Locations, ClientData, Pizza, PizzaOrder, State } from './Types';
|
||||
import { Alert, Button, Col, Form, Row, Table } from 'react-bootstrap';
|
||||
import Header from './components/Header';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
@ -125,6 +125,30 @@ function App() {
|
||||
}
|
||||
}
|
||||
|
||||
const addToCart = async () => {
|
||||
// TODO aktuálně nefunkční - nedokážeme poslat PHPSESSIONID cookie
|
||||
// if (data?.pizzaDay?.orders) {
|
||||
// for (const order of data?.pizzaDay?.orders) {
|
||||
// for (const pizzaOrder of order.pizzaList) {
|
||||
// const url = 'https://www.pizzachefie.cz/pridat.html';
|
||||
// const payload = new URLSearchParams();
|
||||
// payload.append('varId', pizzaOrder.varId.toString());
|
||||
// await fetch(url, {
|
||||
// method: "POST",
|
||||
// mode: "no-cors",
|
||||
// cache: "no-cache",
|
||||
// credentials: "same-origin",
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/x-www-form-urlencoded',
|
||||
// },
|
||||
// body: payload,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// // TODO otevřít košík v nové záložce
|
||||
// }
|
||||
}
|
||||
|
||||
const renderFoodTable = (name, food) => {
|
||||
return <Col md={12} lg={4}>
|
||||
<h3>{name}</h3>
|
||||
@ -150,6 +174,8 @@ function App() {
|
||||
return <div>Načítám data...</div>
|
||||
}
|
||||
|
||||
const noOrders = data?.pizzaDay?.orders?.length == 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
@ -158,10 +184,7 @@ function App() {
|
||||
<Alert variant={'primary'}>
|
||||
Poslední změny:
|
||||
<ul>
|
||||
<li>Možnost odhlášení pomocí menu</li>
|
||||
<li>Přihlásit se lze i stiskem Enter</li>
|
||||
<li>Základ pro pizza day - našeptávač pizz z Pizza Chefie, možnost naklikat si objednávku</li>
|
||||
<li>Možnost uložit si číslo účtu pro budoucí generování QR kódů</li>
|
||||
<li>Přechody mezi stavy Pizza Day: Vytvořeno -> Uzamčeno -> Objednáno -> Doručeno</li>
|
||||
</ul>
|
||||
</Alert>
|
||||
<h1 className='title'>Dnes je {data.date}</h1>
|
||||
@ -220,11 +243,54 @@ function App() {
|
||||
{data.pizzaDay &&
|
||||
<div>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<p>Pizza Day je založen uživatelem {data.pizzaDay.creator}</p>
|
||||
{
|
||||
data.pizzaDay.creator === auth.login && <Button className='danger mb-3' onClick={async () => {
|
||||
await deletePizzaDay(auth.login);
|
||||
}}>Smazat Pizza day</Button>
|
||||
data.pizzaDay.state === State.CREATED && data.pizzaDay.creator === auth.login &&
|
||||
<div>
|
||||
<p>Pizza Day je založen a spravován uživatelem {data.pizzaDay.creator}.<br />Můžete upravovat své objednávky.</p>
|
||||
<Button className='danger mb-3' title="Smaže kompletně pizza day, včetně dosud zadaných objednávek." onClick={async () => {
|
||||
await deletePizzaDay(auth.login);
|
||||
}}>Smazat Pizza day</Button>
|
||||
<Button className='mb-3' style={{ marginLeft: '20px' }} title={noOrders ? "Nelze uzamknout - neexistuje žádná objednávka" : "Zamezí přidávat/odebírat objednávky. Použij před samotným objednáním, aby již nemohlo docházet ke změnám."} disabled={noOrders} onClick={async () => {
|
||||
await lockPizzaDay(auth.login);
|
||||
}}>Uzamknout</Button>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
data.pizzaDay.state === State.LOCKED && data.pizzaDay.creator === auth.login &&
|
||||
<div>
|
||||
<p>Objednávky jsou uzamčeny uživatelem {data.pizzaDay.creator}</p>
|
||||
<Button className='danger mb-3' title="Umožní znovu editovat objednávky." onClick={async () => {
|
||||
await unlockPizzaDay(auth.login);
|
||||
}}>Odemknout</Button>
|
||||
{/* <Button className='danger mb-3' style={{ marginLeft: '20px' }} onClick={async () => {
|
||||
await addToCart();
|
||||
}}>Přidat vše do košíku</Button> */}
|
||||
<Button className='danger mb-3' style={{ marginLeft: '20px' }} title={noOrders ? "Nelze objednat - neexistuje žádná objednávka" : "Použij po objednání. Objednávky zůstanou zamčeny."} disabled={noOrders} onClick={async () => {
|
||||
await finishOrder(auth.login);
|
||||
}}>Objednáno</Button>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
data.pizzaDay.state === State.ORDERED &&
|
||||
<div>
|
||||
<p>Pizzy byly objednány uživatelem {data.pizzaDay.creator}</p>
|
||||
{data.pizzaDay.creator === auth.login &&
|
||||
<div>
|
||||
<Button className='danger mb-3' title="Vrátí stav do předchozího kroku (před objednáním)." onClick={async () => {
|
||||
await lockPizzaDay(auth.login);
|
||||
}}>Vrátit do "uzamčeno"</Button>
|
||||
<Button className='danger mb-3' style={{ marginLeft: '20px' }} title="Nastaví stav na 'Doručeno' - koncový stav." onClick={async () => {
|
||||
await finishDelivery(auth.login);
|
||||
}}>Doručeno</Button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
data.pizzaDay.state === State.DELIVERED &&
|
||||
<div>
|
||||
<p>Pizzy byly doručeny.</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<SelectSearch
|
||||
@ -232,8 +298,9 @@ function App() {
|
||||
options={pizzaSuggestions}
|
||||
placeholder='Vyhledat pizzu...'
|
||||
onChange={handlePizzaChange}
|
||||
disabled={data.pizzaDay.state !== State.CREATED}
|
||||
/>
|
||||
<PizzaOrderList orders={data.pizzaDay.orders} onDelete={handlePizzaDelete} />
|
||||
<PizzaOrderList state={data.pizzaDay.state} orders={data.pizzaDay.orders} onDelete={handlePizzaDelete} />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
// TODO všechno v tomto souboru jsou duplicity se serverem, ale aktuálně nevím jaký je nejlepší způsob jejich sdílení
|
||||
|
||||
export interface PizzaSize {
|
||||
varId: number, // unikátní ID varianty pizzy
|
||||
size: string, // velikost pizzy, např. "30cm"
|
||||
pizzaPrice: number, // cena samotné pizzy
|
||||
boxPrice: number, // cena krabice
|
||||
@ -16,6 +17,7 @@ export interface Pizza {
|
||||
|
||||
/** Objednávka jedné konkrétní pizzy */
|
||||
export interface PizzaOrder {
|
||||
varId: number, // unikátní ID varianty pizzy
|
||||
name: string, // název pizzy
|
||||
size: string, // velikost pizzy jako string (30cm)
|
||||
price: number, // cena pizzy v Kč, včetně krabice
|
||||
@ -59,5 +61,7 @@ export enum Locations {
|
||||
export enum State {
|
||||
NOT_CREATED, // Pizza day nebyl založen
|
||||
CREATED, // Pizza day je založen
|
||||
LOCKED // Objednávky uzamčeny
|
||||
LOCKED, // Objednávky uzamčeny
|
||||
ORDERED, // Objednáno
|
||||
DELIVERED, // Doručeno
|
||||
}
|
@ -1,19 +1,23 @@
|
||||
import React from "react";
|
||||
import { Table } from "react-bootstrap";
|
||||
import { Order, PizzaOrder } from "../Types";
|
||||
import { Order, PizzaOrder, State } from "../Types";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faTrashCan } from "@fortawesome/free-regular-svg-icons";
|
||||
import { useAuth } from "../context/auth";
|
||||
|
||||
export default function PizzaOrderList({ orders, onDelete }: { orders: Order[], onDelete: (pizzaOrder: PizzaOrder) => void }) {
|
||||
export default function PizzaOrderList({ state, orders, onDelete }: { state: State, orders: Order[], onDelete: (pizzaOrder: PizzaOrder) => void }) {
|
||||
const auth = useAuth();
|
||||
|
||||
if (!orders?.length) {
|
||||
return <p><i>Zatím žádné objednávky...</i></p>
|
||||
}
|
||||
|
||||
return <Table className="mt-3" striped bordered hover>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Jméno</th>
|
||||
<th>Objednávka</th>
|
||||
<th>Celkem</th>
|
||||
<th>Cena</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -22,13 +26,13 @@ export default function PizzaOrderList({ orders, onDelete }: { orders: Order[],
|
||||
<td>{order.pizzaList.map<React.ReactNode>((pizzaOrder, index) =>
|
||||
<span key={index}>
|
||||
{`${pizzaOrder.name}, ${pizzaOrder.size} (${pizzaOrder.price} Kč)`}
|
||||
{auth?.login === order.customer &&
|
||||
{auth?.login === order.customer && state === State.CREATED &&
|
||||
<FontAwesomeIcon onClick={() => {
|
||||
onDelete(pizzaOrder);
|
||||
}} title='Odstranit' className='trash-icon' icon={faTrashCan} />
|
||||
}
|
||||
</span>)
|
||||
.reduce((prev, curr) => [prev, <br />, curr])}
|
||||
.reduce((prev, curr, index) => [prev, <br key={`br-${index}`} />, curr])}
|
||||
</td>
|
||||
<td>{order.totalPrice} Kč</td>
|
||||
</tr>)}
|
||||
|
@ -5,6 +5,7 @@ import fs from 'fs';
|
||||
import axios from 'axios';
|
||||
|
||||
type PizzaSize = {
|
||||
varId: number,
|
||||
size: string,
|
||||
pizzaPrice: number,
|
||||
boxPrice: number,
|
||||
@ -67,9 +68,10 @@ const downloadPizzy = async () => {
|
||||
const sizes: PizzaSize[] = [];
|
||||
const a = $('.varianty > li > a', pizzaHtml);
|
||||
a.each((i, elm) => {
|
||||
const varId = Number.parseInt(elm.attribs.href.split('?varianta=')[1].trim());
|
||||
const size = $($(elm).contents().get(0)).text().trim();
|
||||
const price = Number.parseInt($($(elm).contents().get(1)).text().trim().split(" Kč")[0]);
|
||||
sizes.push({ size: size, pizzaPrice: price, boxPrice: boxPrices[size], price: price + boxPrices[size] });
|
||||
sizes.push({ varId, size, pizzaPrice: price, boxPrice: boxPrices[size], price: price + boxPrices[size] });
|
||||
})
|
||||
result.push({
|
||||
name: name,
|
||||
|
@ -3,7 +3,7 @@ import { Server } from "socket.io";
|
||||
import bodyParser from "body-parser";
|
||||
import { fetchPizzy } from "./chefie";
|
||||
import cors from 'cors';
|
||||
import { addPizzaOrder, createPizzaDay, deletePizzaDay, getData, removePizzaOrder, updateChoice } from "./service";
|
||||
import { addPizzaOrder, createPizzaDay, deletePizzaDay, finishPizzaDelivery, finishPizzaOrder, getData, lockPizzaDay, removePizzaOrder, unlockPizzaDay, updateChoice } from "./service";
|
||||
import dotenv from 'dotenv';
|
||||
import path from 'path';
|
||||
import { fetchMenus } from "./restaurants";
|
||||
@ -103,6 +103,42 @@ app.post("/api/removePizza", (req, res) => {
|
||||
res.status(200).json({});
|
||||
});
|
||||
|
||||
app.post("/api/lockPizzaDay", (req, res) => {
|
||||
if (!req.body?.login) {
|
||||
throw Error("Nebyl předán login");
|
||||
}
|
||||
const data = lockPizzaDay(req.body.login);
|
||||
io.emit("message", data);
|
||||
res.status(200).json({});
|
||||
});
|
||||
|
||||
app.post("/api/unlockPizzaDay", (req, res) => {
|
||||
if (!req.body?.login) {
|
||||
throw Error("Nebyl předán login");
|
||||
}
|
||||
const data = unlockPizzaDay(req.body.login);
|
||||
io.emit("message", data);
|
||||
res.status(200).json({});
|
||||
});
|
||||
|
||||
app.post("/api/finishOrder", (req, res) => {
|
||||
if (!req.body?.login) {
|
||||
throw Error("Nebyl předán login");
|
||||
}
|
||||
const data = finishPizzaOrder(req.body.login);
|
||||
io.emit("message", data);
|
||||
res.status(200).json({});
|
||||
});
|
||||
|
||||
app.post("/api/finishDelivery", (req, res) => {
|
||||
if (!req.body?.login) {
|
||||
throw Error("Nebyl předán login");
|
||||
}
|
||||
const data = finishPizzaDelivery(req.body.login);
|
||||
io.emit("message", data);
|
||||
res.status(200).json({});
|
||||
});
|
||||
|
||||
app.post("/api/updateChoice", (req, res) => {
|
||||
if (!req.body.hasOwnProperty('name')) {
|
||||
res.status(400).json({});
|
||||
|
@ -69,6 +69,9 @@ export function addPizzaOrder(login: string, pizza: Pizza, size: PizzaSize) {
|
||||
if (!clientData.pizzaDay) {
|
||||
throw Error("Pizza day pro dnešní den neexistuje");
|
||||
}
|
||||
if (clientData.pizzaDay.state !== PizzaDayState.CREATED) {
|
||||
throw Error("Pizza day není ve stavu " + PizzaDayState.CREATED);
|
||||
}
|
||||
let order: Order | undefined = clientData.pizzaDay.orders.find(o => o.customer === login);
|
||||
if (!order) {
|
||||
order = {
|
||||
@ -79,6 +82,7 @@ export function addPizzaOrder(login: string, pizza: Pizza, size: PizzaSize) {
|
||||
clientData.pizzaDay.orders.push(order);
|
||||
}
|
||||
const pizzaOrder: PizzaOrder = {
|
||||
varId: size.varId,
|
||||
name: pizza.name,
|
||||
size: size.size,
|
||||
price: size.price,
|
||||
@ -120,6 +124,98 @@ export function removePizzaOrder(login: string, pizzaOrder: PizzaOrder) {
|
||||
return clientData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uzamkne možnost editovat objednávky pizzy.
|
||||
*
|
||||
* @param login login uživatele
|
||||
* @returns aktuální data pro uživatele
|
||||
*/
|
||||
export function lockPizzaDay(login: string) {
|
||||
const today = formatDate(getToday());
|
||||
const clientData: ClientData = db.get(today);
|
||||
if (!clientData.pizzaDay) {
|
||||
throw Error("Pizza day pro dnešní den neexistuje");
|
||||
}
|
||||
if (clientData.pizzaDay.creator !== login) {
|
||||
throw Error("Pizza day není spravován uživatelem " + login);
|
||||
}
|
||||
if (clientData.pizzaDay.state !== PizzaDayState.CREATED && clientData.pizzaDay.state !== PizzaDayState.ORDERED) {
|
||||
throw Error("Pizza day není ve stavu " + PizzaDayState.CREATED + " nebo " + PizzaDayState.ORDERED);
|
||||
}
|
||||
clientData.pizzaDay.state = PizzaDayState.LOCKED;
|
||||
db.set(today, clientData);
|
||||
return clientData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Odekmne možnost editovat objednávky pizzy.
|
||||
*
|
||||
* @param login login uživatele
|
||||
* @returns aktuální data pro uživatele
|
||||
*/
|
||||
export function unlockPizzaDay(login: string) {
|
||||
const today = formatDate(getToday());
|
||||
const clientData: ClientData = db.get(today);
|
||||
if (!clientData.pizzaDay) {
|
||||
throw Error("Pizza day pro dnešní den neexistuje");
|
||||
}
|
||||
if (clientData.pizzaDay.creator !== login) {
|
||||
throw Error("Pizza day není spravován uživatelem " + login);
|
||||
}
|
||||
if (clientData.pizzaDay.state !== PizzaDayState.LOCKED) {
|
||||
throw Error("Pizza day není ve stavu " + PizzaDayState.LOCKED);
|
||||
}
|
||||
clientData.pizzaDay.state = PizzaDayState.CREATED;
|
||||
db.set(today, clientData);
|
||||
return clientData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nastaví stav pizza day na "pizzy objednány".
|
||||
*
|
||||
* @param login login uživatele
|
||||
* @returns aktuální data pro uživatele
|
||||
*/
|
||||
export function finishPizzaOrder(login: string) {
|
||||
const today = formatDate(getToday());
|
||||
const clientData: ClientData = db.get(today);
|
||||
if (!clientData.pizzaDay) {
|
||||
throw Error("Pizza day pro dnešní den neexistuje");
|
||||
}
|
||||
if (clientData.pizzaDay.creator !== login) {
|
||||
throw Error("Pizza day není spravován uživatelem " + login);
|
||||
}
|
||||
if (clientData.pizzaDay.state !== PizzaDayState.LOCKED) {
|
||||
throw Error("Pizza day není ve stavu " + PizzaDayState.LOCKED);
|
||||
}
|
||||
clientData.pizzaDay.state = PizzaDayState.ORDERED;
|
||||
db.set(today, clientData);
|
||||
return clientData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nastaví stav pizza day na "pizzy doručeny".
|
||||
*
|
||||
* @param login login uživatele
|
||||
* @returns aktuální data pro uživatele
|
||||
*/
|
||||
export function finishPizzaDelivery(login: string) {
|
||||
const today = formatDate(getToday());
|
||||
const clientData: ClientData = db.get(today);
|
||||
if (!clientData.pizzaDay) {
|
||||
throw Error("Pizza day pro dnešní den neexistuje");
|
||||
}
|
||||
if (clientData.pizzaDay.creator !== login) {
|
||||
throw Error("Pizza day není spravován uživatelem " + login);
|
||||
}
|
||||
if (clientData.pizzaDay.state !== PizzaDayState.ORDERED) {
|
||||
throw Error("Pizza day není ve stavu " + PizzaDayState.ORDERED);
|
||||
}
|
||||
clientData.pizzaDay.state = PizzaDayState.DELIVERED;
|
||||
db.set(today, clientData);
|
||||
return clientData;
|
||||
}
|
||||
|
||||
export function initIfNeeded() {
|
||||
const today = formatDate(getToday());
|
||||
if (!db.has(today)) {
|
||||
|
@ -4,6 +4,7 @@ export interface Choices {
|
||||
|
||||
/** Velikost konkrétní pizzy */
|
||||
export interface PizzaSize {
|
||||
varId: number, // unikátní ID varianty pizzy
|
||||
size: string, // velikost pizzy, např. "30cm"
|
||||
pizzaPrice: number, // cena samotné pizzy
|
||||
boxPrice: number, // cena krabice
|
||||
@ -19,6 +20,7 @@ export interface Pizza {
|
||||
|
||||
/** Objednávka jedné konkrétní pizzy */
|
||||
export interface PizzaOrder {
|
||||
varId: number, // unikátní ID varianty pizzy
|
||||
name: string, // název pizzy
|
||||
size: string, // velikost pizzy jako string (30cm)
|
||||
price: number, // cena pizzy v Kč, včetně krabice
|
||||
@ -35,7 +37,9 @@ export interface Order {
|
||||
export enum PizzaDayState {
|
||||
NOT_CREATED, // Pizza day nebyl založen
|
||||
CREATED, // Pizza day je založen
|
||||
LOCKED // Objednávky uzamčeny
|
||||
LOCKED, // Objednávky uzamčeny
|
||||
ORDERED, // Pizzy objednány
|
||||
DELIVERED // Pizzy doručeny
|
||||
}
|
||||
|
||||
/** Informace o pizza day pro dnešní den */
|
||||
|
Loading…
x
Reference in New Issue
Block a user