Compare commits
	
		
			No commits in common. "bbf1cf18505d8599a41ddd584f5e0f304a5a9861" and "bc6035862a855ae28abe74958a6ac62cb606ef21" have entirely different histories.
		
	
	
		
			bbf1cf1850
			...
			bc6035862a
		
	
		
| @ -60,16 +60,8 @@ export const finishDelivery = async (bankAccount?: string, bankAccountHolder?: s | ||||
|     return await api.post<any, any>('/api/finishDelivery', JSON.stringify({ bankAccount, bankAccountHolder })); | ||||
| } | ||||
| 
 | ||||
| export const addChoice = async (locationIndex: number, foodIndex?: number) => { | ||||
|     return await api.post<any, any>('/api/addChoice', JSON.stringify({ locationIndex, foodIndex })); | ||||
| } | ||||
| 
 | ||||
| export const removeChoices = async (locationIndex: number) => { | ||||
|     return await api.post<any, any>('/api/removeChoices', JSON.stringify({ locationIndex })); | ||||
| } | ||||
| 
 | ||||
| export const removeChoice = async (locationIndex: number, foodIndex: number) => { | ||||
|     return await api.post<any, any>('/api/removeChoice', JSON.stringify({ locationIndex, foodIndex })); | ||||
| export const updateChoice = async (choice: number | null) => { | ||||
|     return await api.post<any, any>('/api/updateChoice', JSON.stringify({ choice })); | ||||
| } | ||||
| 
 | ||||
| export const addPizza = async (pizzaIndex: number, pizzaSizeIndex: number) => { | ||||
|  | ||||
| @ -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 { addChoice, addPizza, createPizzaDay, deletePizzaDay, finishDelivery, finishOrder, getData, getFood, getPizzy, getQrUrl, lockPizzaDay, removeChoice, removeChoices, removePizza, unlockPizzaDay, updateNote } 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 { Alert, Button, Col, Form, Row, Table } from 'react-bootstrap'; | ||||
| @ -27,10 +27,8 @@ function App() { | ||||
|   const [food, setFood] = useState<{ [key in Restaurants]: Food[] }>(); | ||||
|   const [pizzy, setPizzy] = useState<Pizza[]>(); | ||||
|   const [myOrder, setMyOrder] = useState<Order>(); | ||||
|   const [foodChoiceList, setFoodChoiceList] = useState<Food[]>(); | ||||
|   const socket = useContext(SocketContext); | ||||
|   const choiceRef = useRef<HTMLSelectElement>(null); | ||||
|   const foodChoiceRef = useRef<HTMLSelectElement>(null); | ||||
|   const poznamkaRef = useRef<HTMLInputElement>(null); | ||||
| 
 | ||||
|   // Načtení dat po přihlášení
 | ||||
| @ -76,15 +74,14 @@ function App() { | ||||
|       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];
 | ||||
|     //     }
 | ||||
|     //   }
 | ||||
|     // }
 | ||||
|     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
 | ||||
| @ -95,64 +92,19 @@ function App() { | ||||
|     } | ||||
|   }, [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]) | ||||
| 
 | ||||
|   const doAddChoice = async (event: React.ChangeEvent<HTMLSelectElement>) => { | ||||
|   const changeChoice = async (event: React.ChangeEvent<HTMLSelectElement>) => { | ||||
|     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 = ""; | ||||
|       } | ||||
|       await updateChoice(index > -1 ? index : null); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   const doAddFoodChoice = async (event: React.ChangeEvent<HTMLSelectElement>) => { | ||||
|     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) => { | ||||
|   const removeChoice = async (key: string) => { | ||||
|     if (auth?.login) { | ||||
|       await removeChoices(Number(locationKey)); | ||||
|       // Vyresetujeme výběr, aby bylo jasné pro který případně vybíráme jídlo
 | ||||
|       await updateChoice(null); | ||||
|       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 = ""; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -256,9 +208,8 @@ function App() { | ||||
|           <Alert variant={'primary'}> | ||||
|             Poslední změny: | ||||
|             <ul> | ||||
|               <li>Bliká mi žárovka v lednici :(</li> | ||||
|               <li>Možnost výběru konkrétních jídel (u podporovaných podniků)</li> | ||||
|               <li>Velmi chatrná, experimentální podpora přihlašování přes <a href="https://www.authelia.com">Authelia</a></li> | ||||
|               <li>Oprava parsování při neočekávané velikosti písmen v ceně</li> | ||||
|               <li>Přidání nedělitelných mezer k cenám</li> | ||||
|             </ul> | ||||
|           </Alert> | ||||
|           <h1 className='title'>Dnes je {data.date}</h1> | ||||
| @ -270,7 +221,7 @@ function App() { | ||||
|           <div className='content-wrapper'> | ||||
|             <div className='content'> | ||||
|               <p>Jak to dnes vidíš s obědem?</p> | ||||
|               <Form.Select ref={choiceRef} onChange={doAddChoice}> | ||||
|               <Form.Select ref={choiceRef} onChange={changeChoice}> | ||||
|                 <option></option> | ||||
|                 <option value={Locations.SLADOVNICKA}>Sladovnická</option> | ||||
|                 <option value={Locations.UMOTLIKU}>U Motlíků</option> | ||||
| @ -280,51 +231,25 @@ function App() { | ||||
|                 <option value={Locations.OBJEDNAVAM}>Budu objednávat (mimo pizzu)</option> | ||||
|                 <option value={Locations.NEOBEDVAM}>Mám vlastní/neobědvám</option> | ||||
|               </Form.Select> | ||||
|               {foodChoiceList && <> | ||||
|                 <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> | ||||
|               </>} | ||||
|               <p style={{ fontSize: "12px", marginTop: "5px" }}> | ||||
|                 Aktuálně je možné vybrat pouze jednu variantu. | ||||
|               </p> | ||||
|               {Object.keys(data.choices).length > 0 ? | ||||
|                 <Table striped bordered hover className='results-table 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> | ||||
|                             <ul> | ||||
|                               {locationLoginList.map((entry: [string, number[]], index) => { | ||||
|                                 const login = entry[0]; | ||||
|                                 const userChoices = entry[1]; | ||||
|                                 return <li key={index}>{login} {login === auth.login && <FontAwesomeIcon onClick={() => { | ||||
|                                   doRemoveChoices(locationKey); | ||||
|                                 }} title='Odstranit, včetně případných podřízených jídel' className='trash-icon' icon={faTrashCan} />} | ||||
|                                   {userChoices?.length && food ? <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][foodIndex].name; | ||||
|                                       return <li key={foodIndex}> | ||||
|                                         {foodName} | ||||
|                                         {login === auth.login && <FontAwesomeIcon onClick={() => { | ||||
|                                           doRemoveFoodChoice(locationKey, foodIndex); | ||||
|                                         }} title={`Odstranit ${foodName}`} className='trash-icon' icon={faTrashCan} />} | ||||
|                                       </li> | ||||
|                                     })} | ||||
|                                   </ul> : null} | ||||
|                                 </li> | ||||
|                               } | ||||
|                               )} | ||||
|                             </ul> | ||||
|                           </td> | ||||
|                         </tr>) | ||||
|                     } | ||||
|                     {Object.keys(data.choices).map((key: string, index: number) => | ||||
|                       <tr key={index}> | ||||
|                         <td>{Object.values(Locations)[Number(key)]}</td> | ||||
|                         <td> | ||||
|                           <ul> | ||||
|                             {data.choices[Number(key)].map((p: string, index: number) => | ||||
|                               <li key={index}>{p} {p === auth.login && <FontAwesomeIcon onClick={() => { | ||||
|                                 removeChoice(key); | ||||
|                               }} title='Odstranit' className='trash-icon' icon={faTrashCan} />}</li> | ||||
|                             )} | ||||
|                           </ul> | ||||
|                         </td> | ||||
|                       </tr> | ||||
|                     )} | ||||
|                   </tbody> | ||||
|                 </Table> | ||||
|  | ||||
| @ -3,13 +3,13 @@ import { Server } from "socket.io"; | ||||
| import bodyParser from "body-parser"; | ||||
| import { fetchPizzy } from "./chefie"; | ||||
| import cors from 'cors'; | ||||
| import { addChoice, addPizzaOrder, createPizzaDay, deletePizzaDay, finishPizzaDelivery, finishPizzaOrder, getData, lockPizzaDay, removeChoice, removeChoices, removePizzaOrder, unlockPizzaDay, updateNote } 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 { getMenuSladovnicka, getMenuTechTower, getMenuUMotliku } from "./restaurants"; | ||||
| import { getQr } from "./qr"; | ||||
| import { generateToken, getLogin, verify } from "./auth"; | ||||
| import { Locations, Restaurants } from "../../types"; | ||||
| import { Restaurants } from "../../types"; | ||||
| 
 | ||||
| const ENVIRONMENT = process.env.NODE_ENV || 'production'; | ||||
| dotenv.config({ path: path.resolve(__dirname, `./.env.${ENVIRONMENT}`) }); | ||||
| @ -35,15 +35,6 @@ app.use(cors({ | ||||
|     origin: '*' | ||||
| })); | ||||
| 
 | ||||
| // app.use((req, res, next) => {
 | ||||
| //     console.log("--- Request ---")
 | ||||
| //     console.log(req.url);
 | ||||
| //     console.log(req.baseUrl);
 | ||||
| //     console.log(req.originalUrl);
 | ||||
| //     console.log(req.path);
 | ||||
| //     next();
 | ||||
| // });
 | ||||
| 
 | ||||
| app.use(express.static('public')) | ||||
| 
 | ||||
| const parseToken = (req: any) => { | ||||
| @ -205,26 +196,9 @@ app.post("/api/finishDelivery", (req, res) => { | ||||
|     res.status(200).json({}); | ||||
| }); | ||||
| 
 | ||||
| app.post("/api/addChoice", (req, res) => { | ||||
| app.post("/api/updateChoice", (req, res) => { | ||||
|     const login = getLogin(parseToken(req)); | ||||
|     if (req.body.locationIndex > -1) { | ||||
|         const data = addChoice(login, req.body.locationIndex, req.body.foodIndex); | ||||
|         io.emit("message", data); | ||||
|         res.status(200).json(data); | ||||
|     } | ||||
|     res.status(400); // TODO přidat popis chyby
 | ||||
| }); | ||||
| 
 | ||||
| app.post("/api/removeChoices", (req, res) => { | ||||
|     const login = getLogin(parseToken(req)); | ||||
|     const data = removeChoices(login, req.body.locationIndex); | ||||
|     io.emit("message", data); | ||||
|     res.status(200).json(data); | ||||
| }); | ||||
| 
 | ||||
| app.post("/api/removeChoice", (req, res) => { | ||||
|     const login = getLogin(parseToken(req)); | ||||
|     const data = removeChoice(login, req.body.locationIndex, req.body.foodIndex); | ||||
|     const data = updateChoice(login, req.body.choice); | ||||
|     io.emit("message", data); | ||||
|     res.status(200).json(data); | ||||
| }); | ||||
|  | ||||
| @ -239,78 +239,34 @@ export function initIfNeeded() { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Odstraní kompletně volbu uživatele (včetně případných podřízených jídel). | ||||
|  *  | ||||
|  * @param login login uživatele | ||||
|  * @param location vybrané "umístění" | ||||
|  * @returns  | ||||
|  */ | ||||
| export function removeChoices(login: string, location: Locations) { | ||||
|     const today = formatDate(getToday()); | ||||
|     let data: ClientData = db.get(today); | ||||
|     if (location in data.choices) { | ||||
|         if (login in data.choices[location]) { | ||||
|             delete data.choices[location][login] | ||||
|             if (Object.keys(data.choices[location]).length === 0) { | ||||
|                 delete data.choices[location] | ||||
|             } | ||||
|             db.set(today, data); | ||||
|         } | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Odstraní konkrétní volbu jídla uživatele. | ||||
|  * Neodstraňuje volbu samotnou, k tomu slouží {@link removeChoices}. | ||||
|  *  | ||||
|  * @param login login uživatele | ||||
|  * @param location vybrané "umístění" | ||||
|  * @param foodIndex index jídla v jídelním lístku daného umístění, pokud existuje | ||||
|  * @returns  | ||||
|  */ | ||||
| export function removeChoice(login: string, location: Locations, foodIndex: number) { | ||||
|     const today = formatDate(getToday()); | ||||
|     let data: ClientData = db.get(today); | ||||
|     if (location in data.choices) { | ||||
|         if (login in data.choices[location]) { | ||||
|             const index = data.choices[location][login].indexOf(foodIndex); | ||||
|             if (index > -1) { | ||||
|                 data.choices[location][login].splice(index, 1) | ||||
|                 db.set(today, data); | ||||
| export function removeChoice(login: string, data: ClientData) { | ||||
|     for (let key of Object.keys(data.choices)) { | ||||
|         if (data.choices[key] && data.choices[key].includes(login)) { | ||||
|             const index = data.choices[key].indexOf(login); | ||||
|             data.choices[key].splice(index, 1); | ||||
|             if (data.choices[key].length == 0) { | ||||
|                 delete data.choices[key]; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Přidá volbu uživatele. | ||||
|  *  | ||||
|  * @param login login uživatele | ||||
|  * @param location vybrané "umístění" | ||||
|  * @param foodIndex volitelný index jídla v daném umístění | ||||
|  * @returns aktuální data | ||||
|  */ | ||||
| export function addChoice(login: string, location: Locations, foodIndex?: number) { | ||||
| export function updateChoice(login: string, choice: Locations | null) { | ||||
|     initIfNeeded(); | ||||
|     const today = formatDate(getToday()); | ||||
|     let data: ClientData = db.get(today); | ||||
|     if (!(location in data.choices)) { | ||||
|         data.choices[location] = {}; | ||||
|     } | ||||
|     if (!(login in data.choices[location])) { | ||||
|         data.choices[location][login] = []; | ||||
|     } | ||||
|     if (foodIndex != null && !data.choices[location][login].includes(foodIndex)) { | ||||
|         data.choices[location][login].push(foodIndex); | ||||
|     data = removeChoice(login, data); | ||||
|     if (choice !== null) { | ||||
|         if (!data.choices?.[choice]) { | ||||
|             data.choices[choice] = []; | ||||
|         } | ||||
|         data.choices[choice].push(login); | ||||
|     } | ||||
|     db.set(today, data); | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| // TODO přejmenovat, ať je jasné že to patří k pizza day
 | ||||
| export function updateNote(login: string, note?: string) { | ||||
|     const today = formatDate(getToday()); | ||||
|     let clientData: ClientData = db.get(today); | ||||
|  | ||||
| @ -6,9 +6,7 @@ export enum Restaurants { | ||||
| } | ||||
| 
 | ||||
| export interface Choices { | ||||
|     [location: string]: { | ||||
|         [login: string]: number[] | ||||
|     }, | ||||
|     [location: string]: string[], | ||||
| } | ||||
| 
 | ||||
| /** Velikost konkrétní pizzy */ | ||||
| @ -76,7 +74,6 @@ export interface Food { | ||||
|     isSoup: boolean, // příznak, zda se jedná o polévku
 | ||||
| } | ||||
| 
 | ||||
| // TODO tohle je dost špatné pojmenování, vzhledem k tomu co to obsahuje
 | ||||
| export enum Locations { | ||||
|     SLADOVNICKA = 'Sladovnická', | ||||
|     UMOTLIKU = 'U Motlíků', | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user