diff --git a/client/src/Api.ts b/client/src/Api.ts index 8dfe2c1..2c3782b 100644 --- a/client/src/Api.ts +++ b/client/src/Api.ts @@ -38,6 +38,22 @@ export const deletePizzaDay = async (login) => { return await api.post('/api/deletePizzaDay', JSON.stringify({ login })); } +export const lockPizzaDay = async (login) => { + return await api.post('/api/lockPizzaDay', JSON.stringify({ login })); +} + +export const unlockPizzaDay = async (login) => { + return await api.post('/api/unlockPizzaDay', JSON.stringify({ login })); +} + +export const finishOrder = async (login) => { + return await api.post('/api/finishOrder', JSON.stringify({ login })); +} + +export const finishDelivery = async (login) => { + return await api.post('/api/finishDelivery', JSON.stringify({ login })); +} + export const updateChoice = async (name: string, choice: number | null) => { return await api.post('/api/updateChoice', JSON.stringify({ name, choice })); } diff --git a/client/src/App.tsx b/client/src/App.tsx index 2051841..0767b65 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -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

{name}

@@ -150,6 +174,8 @@ function App() { return
Načítám data...
} + const noOrders = data?.pizzaDay?.orders?.length == 0; + return ( <>
@@ -158,10 +184,7 @@ function App() { Poslední změny:
    -
  • Možnost odhlášení pomocí menu
  • -
  • Přihlásit se lze i stiskem Enter
  • -
  • Základ pro pizza day - našeptávač pizz z Pizza Chefie, možnost naklikat si objednávku
  • -
  • Možnost uložit si číslo účtu pro budoucí generování QR kódů
  • +
  • Přechody mezi stavy Pizza Day: Vytvořeno -> Uzamčeno -> Objednáno -> Doručeno

Dnes je {data.date}

@@ -220,11 +243,54 @@ function App() { {data.pizzaDay &&
-

Pizza Day je založen uživatelem {data.pizzaDay.creator}

{ - data.pizzaDay.creator === auth.login && + data.pizzaDay.state === State.CREATED && data.pizzaDay.creator === auth.login && +
+

Pizza Day je založen a spravován uživatelem {data.pizzaDay.creator}.
Můžete upravovat své objednávky.

+ + +
+ } + { + data.pizzaDay.state === State.LOCKED && data.pizzaDay.creator === auth.login && +
+

Objednávky jsou uzamčeny uživatelem {data.pizzaDay.creator}

+ + {/* */} + +
+ } + { + data.pizzaDay.state === State.ORDERED && +
+

Pizzy byly objednány uživatelem {data.pizzaDay.creator}

+ {data.pizzaDay.creator === auth.login && +
+ + +
+ } +
+ } + { + data.pizzaDay.state === State.DELIVERED && +
+

Pizzy byly doručeny.

+
}
- +
} diff --git a/client/src/Types.tsx b/client/src/Types.tsx index ae5caf4..aa23b5b 100644 --- a/client/src/Types.tsx +++ b/client/src/Types.tsx @@ -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 } \ No newline at end of file diff --git a/client/src/components/PizzaOrderList.tsx b/client/src/components/PizzaOrderList.tsx index a268671..cc8d710 100644 --- a/client/src/components/PizzaOrderList.tsx +++ b/client/src/components/PizzaOrderList.tsx @@ -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

Zatím žádné objednávky...

+ } + return - + @@ -22,13 +26,13 @@ export default function PizzaOrderList({ orders, onDelete }: { orders: Order[], )} diff --git a/server/src/chefie.ts b/server/src/chefie.ts index ceda484..8001c0b 100644 --- a/server/src/chefie.ts +++ b/server/src/chefie.ts @@ -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, diff --git a/server/src/index.ts b/server/src/index.ts index d351d50..78ae639 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -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({}); diff --git a/server/src/service.ts b/server/src/service.ts index 16f2ea6..41b2bac 100644 --- a/server/src/service.ts +++ b/server/src/service.ts @@ -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)) { diff --git a/server/src/types.ts b/server/src/types.ts index 07785bf..dbdddca 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -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 */
Jméno ObjednávkaCelkemCena
{order.pizzaList.map((pizzaOrder, index) => {`${pizzaOrder.name}, ${pizzaOrder.size} (${pizzaOrder.price} Kč)`} - {auth?.login === order.customer && + {auth?.login === order.customer && state === State.CREATED && { onDelete(pizzaOrder); }} title='Odstranit' className='trash-icon' icon={faTrashCan} /> } ) - .reduce((prev, curr) => [prev,
, curr])} + .reduce((prev, curr, index) => [prev,
, curr])}
{order.totalPrice} Kč