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 { addChoice, addPizza, changeDepartureTime, createPizzaDay, deletePizzaDay, finishDelivery, finishOrder, getData, getFood, getQrUrl, lockPizzaDay, removeChoice, removeChoices, removePizza, unlockPizzaDay, updateNote } from './Api'; 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 { useBank } from './context/bank'; import { ClientData, Restaurants, Food, Order, Locations, PizzaOrder, PizzaDayState, FoodChoices } from './types'; import Footer from './components/Footer'; const EVENT_CONNECT = "connect" // TODO tohle má být kvůli bezpečnosti na serveru const DEPARTURE_TIMES = [ "10:00", "10:15", "10:30", "10:45", "11:00", "11:15", "11:30", "11:45", "12:00", "12:15", "12:30", "12:45", "13:00", ] function App() { const auth = useAuth(); const bank = useBank(); const [isConnected, setIsConnected] = useState(false); const [data, setData] = useState(); const [food, setFood] = useState<{ [key in Restaurants]: Food[] }>(); const [myOrder, setMyOrder] = useState(); const [foodChoiceList, setFoodChoiceList] = useState(); const socket = useContext(SocketContext); const choiceRef = useRef(null); const foodChoiceRef = useRef(null); const poznamkaRef = useRef(null); // Načtení dat po přihlášení useEffect(() => { if (!auth || !auth.login) { return } getData().then(data => { setData(data); }) getFood().then(food => { setFood(food); }) }, [auth, auth?.login]); // 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); 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 locationIndex = Object.values(Locations).indexOf(choiceRef?.current?.value 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]); } else { setFoodChoiceList(undefined); } } else { setFoodChoiceList(undefined); } }, [choiceRef.current?.value, food]) const doAddChoice = async (event: React.ChangeEvent) => { const index = Object.values(Locations).indexOf(event.target.value as unknown as Locations); if (auth?.login) { await addChoice(index); if (foodChoiceRef.current?.value) { foodChoiceRef.current.value = ""; } } } const doAddFoodChoice = async (event: React.ChangeEvent) => { if (event.target.value && foodChoiceList?.length && choiceRef.current?.value) { if (auth?.login) { const locationIndex = Object.values(Locations).indexOf(choiceRef.current.value as unknown as Locations); await addChoice(locationIndex, Number(event.target.value)); } } } const doRemoveChoices = async (locationKey: string) => { if (auth?.login) { await removeChoices(Number(locationKey)); // 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 removeChoice(Number(locationKey), foodIndex); 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; } updateNote(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) => { if (foodChoiceList?.length && choiceRef.current?.value) { if (auth?.login) { await changeDepartureTime(auth.login, event.target.value); } } } const renderFoodTable = (name: string, food: Food[]) => { return

{name}

{food?.length > 0 ? food.map((f: any, index: number) => ) :

Hmmmmm podivné.... nic se nevrátilo

}
{f.amount} {f.name} {f.price}
} if (!auth || !auth.login) { return ; } if (!data || !isConnected || !food) { return
Načítám data...
} const noOrders = data?.pizzaDay?.orders?.length === 0; return ( <>
{data.isWeekend ?

Užívejte víkend :)

: <> Poslední změny:
  • Podpora Redis
  • Možnost výběru preferovaného času odchodu

Dnes je {data.date}

{renderFoodTable('Sladovnická', food[Restaurants.SLADOVNICKA])} {renderFoodTable('U Motlíků', food[Restaurants.UMOTLIKU])} {renderFoodTable('TechTower', food[Restaurants.TECHTOWER])}

Jak to dnes vidíš s obědem?

Je možné vybrat jen jednu možnost. Výběr jiné odstraní předchozí. {foodChoiceList && <>

Na co dobrého? (nepovinné)

{foodChoiceList.map((food, index) => )} } {foodChoiceList && <>

V kolik hodin preferuješ odchod?

{DEPARTURE_TIMES.map(time => )} } {Object.keys(data.choices).length > 0 ? {Object.keys(data.choices).map((locationKey: string) => { const locationName = Object.values(Locations)[Number(locationKey)]; const locationLoginList = Object.entries(data.choices[Number(locationKey)]); return ( ) } )}
{locationName} {locationLoginList.map((entry: [string, FoodChoices], index) => { const login = entry[0]; const userPayload = entry[1]; const userChoices = userPayload?.options; const trusted = userPayload?.trusted || false; return {userChoices?.length && food ? : null} } )}
{trusted && } {login} {userPayload.departureTime && ({userPayload.departureTime})} {login === auth.login && { doRemoveChoices(locationKey); }} title={`Odstranit volbu ${locationName}, včetně případných zvolených jídel`} className='trash-icon' icon={faTrashCan} />}
    {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][foodIndex].name; return
  • {foodName} {login === auth.login && { doRemoveFoodChoice(locationKey, foodIndex); }} title={`Odstranit ${foodName}`} className='trash-icon' icon={faTrashCan} />}
  • })}
:
Zatím nikdo nehlasoval...
}
{!data.pizzaDay &&

Pro dnešní den není aktuálně založen Pizza day.

} {data.pizzaDay &&

Pizza day

{ data.pizzaDay.state === PizzaDayState.CREATED &&

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

{ data.pizzaDay.creator === auth.login && <> }
} { data.pizzaDay.state === PizzaDayState.LOCKED &&

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

{data.pizzaDay.creator === auth.login && <> {/* */} }
} { data.pizzaDay.state === PizzaDayState.ORDERED &&

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

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

Pizzy byly doručeny. Objednávku můžete uhradit pomocí QR kódu níže.

}
{data.pizzaDay.state === PizzaDayState.CREATED &&
Poznámka: { if (event.key === 'Enter') { handlePoznamkaChange(); } }} />
} { data.pizzaDay.state === PizzaDayState.DELIVERED && myOrder?.hasQr &&

QR platba

Částka: {myOrder.totalPrice} Kč
QR kód

Generování QR kódů je v experimentální fázi - doporučujeme si překontrolovat údaje před odesláním platby.

}
}
}