Možnost přidat k objednávce poznámku

This commit is contained in:
Martin Berka 2023-06-17 10:44:12 +02:00
parent 26337121de
commit 67242c48df
9 changed files with 97 additions and 24 deletions

View File

@ -48,7 +48,7 @@ Aplikace sestává ze tří (čtyř) modulů.
- [x] https://qr-platba.cz/pro-vyvojare/restful-api/
- [ ] Zobrazovat celkovou cenu objednávky pod tabulkou objednávek
- [ ] Zobrazit upozornění před smazáním/zamknutím/odemknutím pizza day
- [ ] Umožnit přidat k objednávce poznámku (např. "bez oliv")
- [x] Umožnit přidat k objednávce poznámku (např. "bez oliv")
- [ ] Negenerovat QR kód pro objednávajícího
- [ ] Pizzy se samy budou při naklikání přidávat do košíku
- [ ] Nutno nejprve vyřešit předávání PHPSESSIONID cookie na pizzachefie.cz pomocí fetch()

View File

@ -69,3 +69,7 @@ export const addPizza = async (login: string, pizzaIndex: number, pizzaSizeIndex
export const removePizza = async (login: string, pizzaOrder: PizzaOrder) => {
return await api.post<any, any>('/api/removePizza', JSON.stringify({ login, pizzaOrder }));
}
export const updateNote = async (login: string, note?: string) => {
return await api.post<any, any>('/api/updateNote', JSON.stringify({ login, note }));
}

View File

@ -76,3 +76,7 @@
.qr-code {
text-align: center;
}
.select-search-container {
margin: auto;
}

View File

@ -1,7 +1,7 @@
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, finishDelivery, finishOrder, getData, getFood, getPizzy, getQrUrl, lockPizzaDay, removePizza, unlockPizzaDay, updateChoice } from './Api';
import { addPizza, createPizzaDay, deletePizzaDay, finishDelivery, finishOrder, getData, getFood, getPizzy, getQrUrl, lockPizzaDay, removePizza, unlockPizzaDay, updateChoice, updateNote } from './Api';
import { useAuth } from './context/auth';
import Login from './Login';
import { Locations, ClientData, Pizza, PizzaOrder, State, Order } from './Types';
@ -29,6 +29,7 @@ function App() {
const [myOrder, setMyOrder] = useState<Order>();
const socket = useContext(SocketContext);
const choiceRef = useRef<HTMLSelectElement>(null);
const poznamkaRef = useRef<HTMLInputElement>(null);
// Prvotní načtení aktuálního stavu
useEffect(() => {
@ -83,9 +84,7 @@ function App() {
useEffect(() => {
if (data?.pizzaDay?.orders) {
const myOrder = data.pizzaDay.orders.find(o => o.customer === auth?.login);
if (myOrder) {
setMyOrder(myOrder);
}
setMyOrder(myOrder);
}
}, [data?.pizzaDay?.orders])
@ -131,9 +130,19 @@ function App() {
}
}
const handlePizzaDelete = (pizzaOrder: PizzaOrder) => {
const handlePizzaDelete = async (pizzaOrder: PizzaOrder) => {
if (auth?.login) {
removePizza(auth?.login, pizzaOrder);
await removePizza(auth?.login, pizzaOrder);
}
}
const handlePoznamkaChange = async () => {
if (auth?.login) {
if (poznamkaRef.current?.value && poznamkaRef.current.value.length > 100) {
alert("Poznámka může mít maximálně 100 znaků");
return;
}
updateNote(auth.login, poznamkaRef.current?.value);
}
}
@ -198,6 +207,7 @@ function App() {
<ul>
<li>Nová žárovka zatím funguje</li>
<li>Funkční generování a zobrazení QR kódů pro Pizza day</li>
<li>Možnost zadat k Pizza day objednávce poznámku</li>
</ul>
</Alert>
<h1 className='title'>Dnes je {data.date}</h1>
@ -260,7 +270,10 @@ function App() {
{
data.pizzaDay.state === State.CREATED &&
<div>
<p>Pizza Day je založen a spravován uživatelem {data.pizzaDay.creator}.<br />Můžete upravovat své objednávky.</p>
<p>
Pizza Day je založen a spravován uživatelem {data.pizzaDay.creator}.<br />
Můžete upravovat své objednávky.
</p>
{
data.pizzaDay.creator === auth.login &&
<>
@ -275,18 +288,22 @@ function App() {
</div>
}
{
data.pizzaDay.state === State.LOCKED && data.pizzaDay.creator === auth.login &&
data.pizzaDay.state === State.LOCKED &&
<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 () => {
{data.pizzaDay.creator === auth.login &&
<>
<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>
<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>
}
{
@ -312,13 +329,27 @@ function App() {
</div>
}
</div>
<SelectSearch
search={true}
options={pizzaSuggestions}
placeholder='Vyhledat pizzu...'
onChange={handlePizzaChange}
disabled={data.pizzaDay.state !== State.CREATED}
/>
{data.pizzaDay.state === State.CREATED &&
<div style={{ textAlign: 'center' }}>
<SelectSearch
search={true}
options={pizzaSuggestions}
placeholder='Vyhledat pizzu...'
onChange={handlePizzaChange}
/>
Poznámka: <input ref={poznamkaRef} className='mt-3' type="text" onKeyDown={event => {
if (event.key === 'Enter') {
handlePoznamkaChange();
}
}} />
<Button
style={{ marginLeft: '20px' }}
disabled={!myOrder?.pizzaList?.length}
onClick={handlePoznamkaChange}>
Uložit
</Button>
</div>
}
<PizzaOrderList state={data.pizzaDay.state} orders={data.pizzaDay.orders} onDelete={handlePizzaDelete} />
{
data.pizzaDay.state === State.DELIVERED && myOrder?.hasQr &&

View File

@ -29,6 +29,7 @@ export interface Order {
pizzaList: PizzaOrder[], // seznam objednaných pizz
totalPrice: number, // celková cena všech objednaných pizz a krabic
hasQr?: boolean, // zda je pro objednávku vygenerován QR kód pro platbu
note?: string, // volitelná poznámka uživatele k objednávce
}
export interface Choices {

View File

@ -17,6 +17,7 @@ export default function PizzaOrderList({ state, orders, onDelete }: { state: Sta
<tr>
<th>Jméno</th>
<th>Objednávka</th>
<th>Poznámka</th>
<th>Cena</th>
</tr>
</thead>
@ -34,6 +35,7 @@ export default function PizzaOrderList({ state, orders, onDelete }: { state: Sta
</span>)
.reduce((prev, curr, index) => [prev, <br key={`br-${index}`} />, curr])}
</td>
<td>{order.note || '-'}</td>
<td>{order.totalPrice} </td>
</tr>)}
</tbody>

View File

@ -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, finishPizzaDelivery, finishPizzaOrder, getData, lockPizzaDay, removePizzaOrder, unlockPizzaDay, updateChoice } from "./service";
import { addPizzaOrder, createPizzaDay, deletePizzaDay, finishPizzaDelivery, finishPizzaOrder, getData, lockPizzaDay, removePizzaOrder, unlockPizzaDay, updateChoice, updateNote } from "./service";
import dotenv from 'dotenv';
import path from 'path';
import { fetchMenus } from "./restaurants";
@ -161,6 +161,18 @@ app.get("/api/qr", (req, res) => {
res.end(img);
});
app.post("/api/updateNote", (req, res) => {
if (!req.body.login) {
throw Error("Nebyl předán login");
}
if (req.body.note && req.body.note.length > 100) {
throw Error("Poznámka může mít maximálně 100 znaků");
}
const data = updateNote(req.body.login, req.body.note);
io.emit("message", data);
res.status(200).json(data);
});
io.on("connection", (socket) => {
console.log(`New client connected: ${socket.id}`);

View File

@ -263,3 +263,21 @@ export function updateChoice(login: string, choice: Locations | null) {
db.set(today, data);
return data;
}
export function updateNote(login: string, note?: string) {
const today = formatDate(getToday());
let clientData: ClientData = db.get(today);
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);
}
const myOrder = clientData.pizzaDay.orders.find(o => o.customer === login);
if (!myOrder || !myOrder.pizzaList.length) {
throw Error("Pizza day neobsahuje žádné objednávky uživatele " + login);
}
myOrder.note = note;
db.set(today, clientData);
return clientData;
}

View File

@ -34,6 +34,7 @@ export interface Order {
pizzaList: PizzaOrder[], // seznam objednaných pizz
totalPrice: number, // celková cena všech objednaných pizz a krabic
hasQr?: boolean, // true, pokud je k objednávce vygenerován QR kód pro platbu
note?: string, // volitelná uživatelská poznámka k objednávce
}
/** Stav pizza dne */