599 lines
26 KiB
TypeScript
599 lines
26 KiB
TypeScript
import React, { useContext, useEffect, useMemo, useRef, useState, useCallback } from 'react';
|
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
import { EVENT_DISCONNECT, EVENT_MESSAGE, SocketContext } from './context/socket';
|
|
import { addPizza, createPizzaDay, deletePizzaDay, finishDelivery, finishOrder, lockPizzaDay, removePizza, unlockPizzaDay, updatePizzaDayNote } from './api/PizzaDayApi';
|
|
import { useAuth } from './context/auth';
|
|
import Login from './Login';
|
|
import { Alert, Button, Col, Form, Row, Table } from 'react-bootstrap';
|
|
import Header from './components/Header';
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
import PizzaOrderList from './components/PizzaOrderList';
|
|
import SelectSearch, { SelectedOptionValue } from 'react-select-search';
|
|
import 'react-select-search/style.css';
|
|
import './App.css';
|
|
import { SelectSearchOption } from 'react-select-search';
|
|
import { faCircleCheck, faTrashCan } from '@fortawesome/free-regular-svg-icons';
|
|
import { useSettings } from './context/settings';
|
|
import { ClientData, Restaurants, Food, Order, Locations, PizzaOrder, PizzaDayState, FoodChoices, DayMenu, DepartureTime } from './types';
|
|
import Footer from './components/Footer';
|
|
import { faChainBroken, faChevronLeft, faChevronRight, faGear, faSatelliteDish, faSearch } from '@fortawesome/free-solid-svg-icons';
|
|
import Loader from './components/Loader';
|
|
import { getData, errorHandler, getQrUrl } from './api/Api';
|
|
import { addChoice, removeChoices, removeChoice, changeDepartureTime, jdemeObed } from './api/FoodApi';
|
|
import { getHumanDateTime } from './Utils';
|
|
|
|
const EVENT_CONNECT = "connect"
|
|
|
|
function App() {
|
|
const auth = useAuth();
|
|
const settings = useSettings();
|
|
const [isConnected, setIsConnected] = useState<boolean>(false);
|
|
const [data, setData] = useState<ClientData>();
|
|
const [food, setFood] = useState<{ [key in Restaurants]?: DayMenu }>();
|
|
const [myOrder, setMyOrder] = useState<Order>();
|
|
const [foodChoiceList, setFoodChoiceList] = useState<Food[]>();
|
|
const [closed, setClosed] = useState<boolean>(false);
|
|
const socket = useContext(SocketContext);
|
|
const choiceRef = useRef<HTMLSelectElement>(null);
|
|
const foodChoiceRef = useRef<HTMLSelectElement>(null);
|
|
const departureChoiceRef = useRef<HTMLSelectElement>(null);
|
|
const poznamkaRef = useRef<HTMLInputElement>(null);
|
|
const [failure, setFailure] = useState<boolean>(false);
|
|
const [dayIndex, setDayIndex] = useState<number>();
|
|
const [loadingPizzaDay, setLoadingPizzaDay] = useState<boolean>(false);
|
|
// Prazvláštní workaround, aby socket.io listener viděl aktuální hodnotu
|
|
// https://medium.com/@kishorkrishna/cant-access-latest-state-inside-socket-io-listener-heres-how-to-fix-it-1522a5abebdb
|
|
const dayIndexRef = useRef<number | undefined>(dayIndex);
|
|
|
|
// Načtení dat po přihlášení
|
|
useEffect(() => {
|
|
if (!auth || !auth.login) {
|
|
return
|
|
}
|
|
getData().then((data: ClientData) => {
|
|
setData(data);
|
|
setDayIndex(data.weekIndex);
|
|
dayIndexRef.current = data.weekIndex;
|
|
setFood(data.menus);
|
|
}).catch(e => {
|
|
setFailure(true);
|
|
})
|
|
}, [auth, auth?.login]);
|
|
|
|
// Přenačtení pro zvolený den
|
|
useEffect(() => {
|
|
if (!auth || !auth.login) {
|
|
return
|
|
}
|
|
getData(dayIndex).then((data: ClientData) => {
|
|
setData(data);
|
|
setFood(data.menus);
|
|
}).catch(e => {
|
|
setFailure(true);
|
|
})
|
|
}, [dayIndex, auth]);
|
|
|
|
// Registrace socket eventů
|
|
useEffect(() => {
|
|
socket.on(EVENT_CONNECT, () => {
|
|
// console.log("Connected!");
|
|
setIsConnected(true);
|
|
});
|
|
socket.on(EVENT_DISCONNECT, () => {
|
|
// console.log("Disconnected!");
|
|
setIsConnected(false);
|
|
});
|
|
socket.on(EVENT_MESSAGE, (newData: ClientData) => {
|
|
// console.log("Přijata nová data ze socketu", newData);
|
|
// Aktualizujeme pouze, pokud jsme dostali data pro den, který máme aktuálně zobrazený
|
|
if (dayIndexRef.current == null || newData.weekIndex === dayIndexRef.current) {
|
|
setData(newData);
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
socket.off(EVENT_CONNECT);
|
|
socket.off(EVENT_DISCONNECT);
|
|
socket.off(EVENT_MESSAGE);
|
|
}
|
|
}, [socket]);
|
|
|
|
useEffect(() => {
|
|
if (!auth || !auth.login) {
|
|
return
|
|
}
|
|
// TODO tohle občas náhodně nezafunguje, nutno přepsat, viz https://medium.com/@teh_builder/ref-objects-inside-useeffect-hooks-eb7c15198780
|
|
// TODO nutno opravit
|
|
// if (data?.choices && choiceRef.current) {
|
|
// for (let entry of Object.entries(data.choices)) {
|
|
// if (entry[1].includes(auth.login)) {
|
|
// const value = entry[0] as any as number; // TODO tohle je absurdní
|
|
// choiceRef.current.value = Object.values(Locations)[value];
|
|
// }
|
|
// }
|
|
// }
|
|
}, [auth, auth?.login, data?.choices])
|
|
|
|
// Reference na mojí objednávku
|
|
useEffect(() => {
|
|
if (data?.pizzaDay?.orders) {
|
|
const myOrder = data.pizzaDay.orders.find(o => o.customer === auth?.login);
|
|
setMyOrder(myOrder);
|
|
}
|
|
}, [auth?.login, data?.pizzaDay?.orders])
|
|
|
|
useEffect(() => {
|
|
if (choiceRef?.current?.value && choiceRef.current.value !== "") {
|
|
// TODO: wtf, cos pil, když jsi tohle psal?
|
|
const key = choiceRef?.current?.value;
|
|
const locationIndex = Object.keys(Locations).indexOf(key as unknown as Locations);
|
|
const locationsKey = Object.keys(Locations)[locationIndex];
|
|
const restaurantKey = Object.keys(Restaurants).indexOf(locationsKey);
|
|
if (restaurantKey > -1 && food) {
|
|
const restaurant = Object.values(Restaurants)[restaurantKey];
|
|
setFoodChoiceList(food[restaurant]?.food);
|
|
setClosed(food[restaurant]?.closed ?? false);
|
|
} else {
|
|
setFoodChoiceList(undefined);
|
|
setClosed(false);
|
|
}
|
|
} else {
|
|
setFoodChoiceList(undefined);
|
|
setClosed(false);
|
|
}
|
|
}, [choiceRef.current?.value, food])
|
|
|
|
// Navigace mezi dny pomocí klávesových šípek
|
|
const handleKeyDown = useCallback((e: any) => {
|
|
if (e.keyCode === 37 && dayIndex != null && dayIndex > 0) {
|
|
handleDayChange(dayIndex - 1);
|
|
} else if (e.keyCode === 39 && dayIndex != null && dayIndex < 4) {
|
|
handleDayChange(dayIndex + 1);
|
|
}
|
|
}, [dayIndex]);
|
|
|
|
useEffect(() => {
|
|
document.addEventListener('keydown', handleKeyDown);
|
|
return () => {
|
|
document.removeEventListener('keydown', handleKeyDown);
|
|
}
|
|
}, [handleKeyDown]);
|
|
|
|
const doAddChoice = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
const index = Object.keys(Locations).indexOf(event.target.value as unknown as Locations);
|
|
if (auth?.login) {
|
|
await errorHandler(() => addChoice(index, undefined, dayIndex));
|
|
if (foodChoiceRef.current?.value) {
|
|
foodChoiceRef.current.value = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
const doJdemeObed = async () => {
|
|
if (auth?.login) {
|
|
await jdemeObed();
|
|
}
|
|
}
|
|
|
|
const doAddFoodChoice = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
if (event.target.value && foodChoiceList?.length && choiceRef.current?.value) {
|
|
const restaurantKey = choiceRef.current.value;
|
|
if (auth?.login) {
|
|
const locationIndex = Object.keys(Locations).indexOf(restaurantKey as unknown as Locations);
|
|
await errorHandler(() => addChoice(locationIndex, Number(event.target.value), dayIndex));
|
|
}
|
|
}
|
|
}
|
|
|
|
const doRemoveChoices = async (locationKey: string) => {
|
|
if (auth?.login) {
|
|
await errorHandler(() => removeChoices(Number(locationKey), dayIndex));
|
|
// Vyresetujeme výběr, aby bylo jasné pro který případně vybíráme jídlo
|
|
if (choiceRef?.current?.value) {
|
|
choiceRef.current.value = "";
|
|
}
|
|
if (foodChoiceRef?.current?.value) {
|
|
foodChoiceRef.current.value = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
const doRemoveFoodChoice = async (locationKey: string, foodIndex: number) => {
|
|
if (auth?.login) {
|
|
await errorHandler(() => removeChoice(Number(locationKey), foodIndex, dayIndex));
|
|
if (choiceRef?.current?.value) {
|
|
choiceRef.current.value = "";
|
|
}
|
|
if (foodChoiceRef?.current?.value) {
|
|
foodChoiceRef.current.value = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
const pizzaSuggestions = useMemo(() => {
|
|
if (!data?.pizzaList) {
|
|
return [];
|
|
}
|
|
const suggestions: SelectSearchOption[] = [];
|
|
data.pizzaList.forEach((pizza, index) => {
|
|
const group: SelectSearchOption = { name: pizza.name, type: "group", items: [] }
|
|
pizza.sizes.forEach((size, sizeIndex) => {
|
|
const name = `${size.size} (${size.price} Kč)`;
|
|
const value = `${index}|${sizeIndex}`;
|
|
group.items?.push({ name, value });
|
|
})
|
|
suggestions.push(group);
|
|
})
|
|
return suggestions;
|
|
}, [data?.pizzaList]);
|
|
|
|
const handlePizzaChange = async (value: SelectedOptionValue | SelectedOptionValue[]) => {
|
|
if (auth?.login && data?.pizzaList) {
|
|
if (!(typeof value === 'string')) {
|
|
throw Error('Nepodporovaný typ hodnoty');
|
|
}
|
|
const s = value.split('|');
|
|
const pizzaIndex = Number.parseInt(s[0]);
|
|
const pizzaSizeIndex = Number.parseInt(s[1]);
|
|
await addPizza(pizzaIndex, pizzaSizeIndex);
|
|
}
|
|
}
|
|
|
|
const handlePizzaDelete = async (pizzaOrder: PizzaOrder) => {
|
|
await removePizza(pizzaOrder);
|
|
}
|
|
|
|
const handlePoznamkaChange = async () => {
|
|
if (poznamkaRef.current?.value && poznamkaRef.current.value.length > 100) {
|
|
alert("Poznámka může mít maximálně 100 znaků");
|
|
return;
|
|
}
|
|
updatePizzaDayNote(poznamkaRef.current?.value);
|
|
}
|
|
|
|
// 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 handleChangeDepartureTime = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
if (foodChoiceList?.length && choiceRef.current?.value) {
|
|
await changeDepartureTime(event.target.value, dayIndex);
|
|
}
|
|
}
|
|
|
|
const handleDayChange = async (dayIndex: number) => {
|
|
setDayIndex(dayIndex);
|
|
dayIndexRef.current = dayIndex;
|
|
if (choiceRef?.current?.value) {
|
|
choiceRef.current.value = "";
|
|
}
|
|
if (foodChoiceRef?.current?.value) {
|
|
foodChoiceRef.current.value = "";
|
|
}
|
|
if (departureChoiceRef?.current?.value) {
|
|
departureChoiceRef.current.value = "";
|
|
}
|
|
}
|
|
|
|
const renderFoodTable = (name: string, menu: DayMenu) => {
|
|
let content;
|
|
if (menu?.closed) {
|
|
content = <h3>Zavřeno</h3>
|
|
} else if (menu?.food?.length > 0) {
|
|
content = <Table striped bordered hover>
|
|
<tbody>
|
|
{menu.food.filter(f => (settings?.hideSoups ? !f.isSoup : true)).map((f: any, index: number) =>
|
|
<tr key={index}>
|
|
<td>{f.amount}</td>
|
|
<td>{f.name}</td>
|
|
<td>{f.price}</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</Table>
|
|
} else {
|
|
content = <h3>Chyba načtení dat</h3>
|
|
}
|
|
return <Col md={12} lg={4}>
|
|
<h3>{name}</h3>
|
|
{menu?.lastUpdate && <small>Poslední aktualizace: {getHumanDateTime(new Date(menu.lastUpdate))}</small>}
|
|
{content}
|
|
</Col>
|
|
}
|
|
|
|
if (!auth || !auth.login) {
|
|
return <Login />;
|
|
}
|
|
|
|
if (!isConnected) {
|
|
return <Loader
|
|
icon={faSatelliteDish}
|
|
description={'Zdá se, že máme problémy se spojením se serverem. Pokud problém přetrvává, kontaktujte správce systému.'}
|
|
animation={'fa-beat-fade'}
|
|
/>
|
|
}
|
|
|
|
if (failure) {
|
|
return <Loader
|
|
title="Něco se nám nepovedlo :("
|
|
icon={faChainBroken}
|
|
description={'Ale to nevadí. To se stává, takový je život. Kontaktujte správce systému, který zajistí nápravu.'}
|
|
/>
|
|
}
|
|
|
|
if (!data || !food) {
|
|
return <Loader
|
|
icon={faSearch}
|
|
description={'Hledáme, co dobrého je aktuálně v nabídce'}
|
|
animation={'fa-bounce'}
|
|
/>
|
|
}
|
|
|
|
const noOrders = data?.pizzaDay?.orders?.length === 0;
|
|
const canChangeChoice = dayIndex == null || data.todayWeekIndex == null || dayIndex >= data.todayWeekIndex;
|
|
|
|
return (
|
|
<>
|
|
<Header />
|
|
<div className='wrapper'>
|
|
{data.isWeekend ? <h4>Užívejte víkend :)</h4> : <>
|
|
<Alert variant={'primary'}>
|
|
Poslední změny:
|
|
<ul>
|
|
<li>Možnost skrytí polévek</li>
|
|
<li>Možnost "Rozhoduji se"</li>
|
|
</ul>
|
|
</Alert>
|
|
{dayIndex != null &&
|
|
<div className='day-navigator'>
|
|
{dayIndex > 0 && <FontAwesomeIcon title="Předchozí den" icon={faChevronLeft} style={{ cursor: "pointer" }} onClick={() => handleDayChange(dayIndex - 1)} />}
|
|
<h1 className='title' style={{ color: dayIndex === data.todayWeekIndex ? 'black' : 'gray' }}>{data.date}</h1>
|
|
{dayIndex < 4 && <FontAwesomeIcon title="Následující den" icon={faChevronRight} style={{ cursor: "pointer" }} onClick={() => handleDayChange(dayIndex + 1)} />}
|
|
</div>
|
|
}
|
|
<Row className='food-tables'>
|
|
{food[Restaurants.SLADOVNICKA] && renderFoodTable('Sladovnická', food[Restaurants.SLADOVNICKA])}
|
|
{food[Restaurants.UMOTLIKU] && renderFoodTable('U Motlíků', food[Restaurants.UMOTLIKU])}
|
|
{food[Restaurants.TECHTOWER] && renderFoodTable('TechTower', food[Restaurants.TECHTOWER])}
|
|
</Row>
|
|
<div className='content-wrapper'>
|
|
<div className='content'>
|
|
{canChangeChoice && <>
|
|
<p>{`Jak to ${dayIndex == null || dayIndex === data.todayWeekIndex ? 'dnes' : 'tento den'} vidíš s obědem?`}</p>
|
|
<Form.Select ref={choiceRef} onChange={doAddChoice}>
|
|
<option></option>
|
|
{Object.entries(Locations)
|
|
.filter(entry => {
|
|
// TODO: wtf, cos pil, když jsi tohle psal? v2
|
|
const key = entry[0];
|
|
const locationIndex = Object.keys(Locations).indexOf(key as unknown as Locations);
|
|
const locationsKey = Object.keys(Locations)[locationIndex];
|
|
const restaurantKey = Object.keys(Restaurants).indexOf(locationsKey);
|
|
const v = Object.values(Restaurants)[restaurantKey];
|
|
return v == null || !food[v]?.closed;
|
|
})
|
|
.map(entry => <option key={entry[0]} value={entry[0]}>{entry[1]}</option>)}
|
|
</Form.Select>
|
|
<small>Je možné vybrat jen jednu možnost. Výběr jiné odstraní předchozí.</small>
|
|
{foodChoiceList && !closed && <>
|
|
<p style={{ marginTop: "10px" }}>Na co dobrého? <small>(nepovinné)</small></p>
|
|
<Form.Select ref={foodChoiceRef} onChange={doAddFoodChoice}>
|
|
<option></option>
|
|
{foodChoiceList.map((food, index) => <option key={index} value={index}>{food.name}</option>)}
|
|
</Form.Select>
|
|
</>}
|
|
{foodChoiceList && !closed && <>
|
|
<p style={{ marginTop: "10px" }}>V kolik hodin preferuješ odchod?</p>
|
|
<Form.Select ref={departureChoiceRef} onChange={handleChangeDepartureTime}>
|
|
<option></option>
|
|
{Object.values(DepartureTime).map(time => <option key={time} value={time}>{time}</option>)}
|
|
</Form.Select>
|
|
</>}
|
|
</>}
|
|
{Object.keys(data.choices).length > 0 ?
|
|
<Table bordered className='mt-5'>
|
|
<tbody>
|
|
{Object.keys(data.choices).map((locationKey: string) => {
|
|
const locationName = Object.values(Locations)[Number(locationKey)];
|
|
const locationLoginList = Object.entries(data.choices[Number(locationKey)]);
|
|
return (
|
|
<tr key={locationKey}>
|
|
<td>{locationName}</td>
|
|
<td className='p-0'>
|
|
<Table>
|
|
<tbody>
|
|
{locationLoginList.map((entry: [string, FoodChoices], index) => {
|
|
const login = entry[0];
|
|
const userPayload = entry[1];
|
|
const userChoices = userPayload?.options;
|
|
const trusted = userPayload?.trusted || false;
|
|
return <tr key={index}>
|
|
<td className='text-nowrap'>
|
|
{trusted && <span className='trusted-icon'>
|
|
<FontAwesomeIcon title='Uživatel ověřený doménovým přihlášením' icon={faCircleCheck} style={{ cursor: "help" }} />
|
|
</span>}
|
|
{login}
|
|
{userPayload.departureTime && <small> ({userPayload.departureTime})</small>}
|
|
{login === auth.login && canChangeChoice && <FontAwesomeIcon onClick={() => {
|
|
doRemoveChoices(locationKey);
|
|
}} title={`Odstranit volbu ${locationName}, včetně případných zvolených jídel`} className='action-icon' icon={faTrashCan} />}
|
|
</td>
|
|
{userChoices?.length && food ? <td className='w-100'>
|
|
<ul>
|
|
{userChoices?.map(foodIndex => {
|
|
const locationsKey = Object.keys(Locations)[Number(locationKey)]
|
|
const restaurantKey = Object.keys(Restaurants).indexOf(locationsKey);
|
|
const restaurant = Object.values(Restaurants)[restaurantKey];
|
|
const foodName = food[restaurant]?.food[foodIndex].name;
|
|
return <li key={foodIndex}>
|
|
{foodName}
|
|
{login === auth.login && canChangeChoice && <FontAwesomeIcon onClick={() => {
|
|
doRemoveFoodChoice(locationKey, foodIndex);
|
|
}} title={`Odstranit ${foodName}`} className='action-icon' icon={faTrashCan} />}
|
|
</li>
|
|
})}
|
|
</ul>
|
|
</td> : null}
|
|
</tr>
|
|
}
|
|
)}
|
|
</tbody>
|
|
</Table>
|
|
</td>
|
|
</tr>)
|
|
}
|
|
)}
|
|
</tbody>
|
|
</Table>
|
|
: <div className='mt-5'><i>Zatím nikdo nehlasoval...</i></div>
|
|
}
|
|
</div>
|
|
{dayIndex === data.todayWeekIndex &&
|
|
<div className='mt-5'>
|
|
{!data.pizzaDay &&
|
|
<div style={{ textAlign: 'center' }}>
|
|
<p>Pro dnešní den není aktuálně založen Pizza day.</p>
|
|
{loadingPizzaDay ?
|
|
<span>
|
|
<FontAwesomeIcon icon={faGear} className='fa-spin' /> Zjišťujeme dostupné pizzy
|
|
</span>
|
|
:
|
|
<>
|
|
<Button onClick={async () => {
|
|
setLoadingPizzaDay(true);
|
|
await createPizzaDay().then(() => setLoadingPizzaDay(false));
|
|
}}>Založit Pizza day</Button>
|
|
<Button onClick={doJdemeObed} style={{ marginLeft: "14px" }}>Jdeme na oběd !</Button>
|
|
</>
|
|
}
|
|
</div>
|
|
}
|
|
{data.pizzaDay &&
|
|
<div>
|
|
<div style={{ textAlign: 'center' }}>
|
|
<h3>Pizza day</h3>
|
|
{
|
|
data.pizzaDay.state === PizzaDayState.CREATED &&
|
|
<div>
|
|
<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 &&
|
|
<>
|
|
<Button className='danger mb-3' title="Smaže kompletně pizza day, včetně dosud zadaných objednávek." onClick={async () => {
|
|
await deletePizzaDay();
|
|
}}>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();
|
|
}}>Uzamknout</Button>
|
|
</>
|
|
}
|
|
</div>
|
|
}
|
|
{
|
|
data.pizzaDay.state === PizzaDayState.LOCKED &&
|
|
<div>
|
|
<p>Objednávky jsou uzamčeny uživatelem {data.pizzaDay.creator}</p>
|
|
{data.pizzaDay.creator === auth.login &&
|
|
<>
|
|
<Button className='danger mb-3' title="Umožní znovu editovat objednávky." onClick={async () => {
|
|
await unlockPizzaDay();
|
|
}}>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();
|
|
}}>Objednáno</Button>
|
|
</>
|
|
}
|
|
</div>
|
|
}
|
|
{
|
|
data.pizzaDay.state === PizzaDayState.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();
|
|
}}>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(settings?.bankAccount, settings?.holderName);
|
|
}}>Doručeno</Button>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
{
|
|
data.pizzaDay.state === PizzaDayState.DELIVERED &&
|
|
<div>
|
|
<p>{`Pizzy byly doručeny.${myOrder?.hasQr ? ` Objednávku můžete uživateli ${data.pizzaDay.creator} uhradit pomocí QR kódu níže.` : ''}`}</p>
|
|
</div>
|
|
}
|
|
</div>
|
|
{data.pizzaDay.state === PizzaDayState.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} creator={data.pizzaDay.creator} />
|
|
{
|
|
data.pizzaDay.state === PizzaDayState.DELIVERED && myOrder?.hasQr &&
|
|
<div className='qr-code'>
|
|
<h3>QR platba</h3>
|
|
<img src={getQrUrl(auth.login)} alt='QR kód' />
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
</>}
|
|
</div>
|
|
<Footer />
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default App;
|