diff --git a/client/src/Api.ts b/client/src/Api.ts index 219ae63..d3d10d3 100644 --- a/client/src/Api.ts +++ b/client/src/Api.ts @@ -1,4 +1,4 @@ -import { PizzaOrder } from "./Types"; +import { Locations, PizzaOrder } from "./Types"; import { getBaseUrl } from "./Utils"; async function request( @@ -58,7 +58,7 @@ export const finishDelivery = async (login, bankAccount, bankAccountHolder) => { return await api.post('/api/finishDelivery', JSON.stringify({ login, bankAccount, bankAccountHolder })); } -export const updateChoice = async (name: string, choice: number | null) => { +export const updateChoice = async (name: string, choice?: Locations | null) => { return await api.post('/api/updateChoice', JSON.stringify({ name, choice })); } @@ -72,4 +72,8 @@ export const removePizza = async (login: string, pizzaOrder: PizzaOrder) => { export const updateNote = async (login: string, note?: string) => { return await api.post('/api/updateNote', JSON.stringify({ login, note })); +} + +export const updateFoodChoice = async (login: string, choice: Locations, foodName?: string) => { + return await api.post('/api/updateFoodChoice', JSON.stringify({ login, choice, foodName })); } \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx index dafa7e6..18ecf1d 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, finishDelivery, finishOrder, getData, getFood, getPizzy, getQrUrl, lockPizzaDay, removePizza, unlockPizzaDay, updateChoice, updateNote } from './Api'; +import { addPizza, createPizzaDay, deletePizzaDay, finishDelivery, finishOrder, getData, getFood, getPizzy, getQrUrl, lockPizzaDay, removePizza, unlockPizzaDay, updateChoice, updateFoodChoice, updateNote } from './Api'; import { useAuth } from './context/auth'; import Login from './Login'; -import { Locations, ClientData, Pizza, PizzaOrder, State, Order, Food, Restaurants } from './Types'; +import { Locations, ClientData, Pizza, PizzaOrder, State, Order, Food, Restaurants, FoodChoice } from './Types'; import { Alert, Button, Col, Form, Row, Table } from 'react-bootstrap'; import Header from './components/Header'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' @@ -30,6 +30,8 @@ function App() { const socket = useContext(SocketContext); const choiceRef = useRef(null); const poznamkaRef = useRef(null); + const [foodSelection, setFoodSelection] = useState(); + const foodChoiceRef = useRef(null); // Prvotní načtení aktuálního stavu useEffect(() => { @@ -73,8 +75,8 @@ function App() { // TODO tohle občas náhodně nezafunguje, nutno přepsat, viz https://medium.com/@teh_builder/ref-objects-inside-useeffect-hooks-eb7c15198780 if (data?.choices && choiceRef.current) { for (let entry of Object.entries(data.choices)) { - if (entry[1].includes(auth.login)) { - choiceRef.current.value = Object.values(Locations)[entry[0]] + if (entry[1].find(ch => ch.login === auth.login)) { + choiceRef.current.value = entry[0]; } } } @@ -88,10 +90,64 @@ function App() { } }, [auth?.login, data?.pizzaDay?.orders]) + const locationToRestaurant = (location: Locations): Restaurants | undefined => { + switch (location) { + case Locations.SLADOVNICKA: + return Restaurants.SLADOVNICKA; + case Locations.UMOTLIKU: + return Restaurants.UMOTLIKU; + case Locations.TECHTOWER: + return Restaurants.TECHTOWER; + } + } + + const renderLocation = (location: Locations) => { + switch (location) { + case Locations.SLADOVNICKA: + return 'Sladovnická'; + case Locations.UMOTLIKU: + return 'U Motlíků'; + case Locations.TECHTOWER: + return 'TechTower'; + case Locations.SPSE: + return 'SPŠE'; + case Locations.PIZZA: + return 'Pizza day'; + case Locations.OBJEDNAVAM: + return 'Objednávám (mimo pizza day)'; + case Locations.NEOBEDVAM: + return 'Vlastní/Home office/Neobědvám'; + } + } + + const restaurantToLocation = (restaurant: Restaurants): Locations | undefined => { + switch (restaurant) { + case Restaurants.SLADOVNICKA: + return Locations.SLADOVNICKA; + case Restaurants.UMOTLIKU: + return Locations.UMOTLIKU; + case Restaurants.TECHTOWER: + return Locations.TECHTOWER; + } + } + const changeChoice = async (event: React.ChangeEvent) => { - const index = Object.values(Locations).indexOf(event.target.value as unknown as Locations); + const choice: Locations | undefined = Locations[event.target.value]; + console.log("Vybráno", choice); // TODO smazat if (auth?.login) { - await updateChoice(auth.login, index > -1 ? index : null); + await updateChoice(auth.login, choice); + // if (index >= 0) { + // const choice = Object.values(Locations)[index]; + // if ([Locations.SLADOVNICKA, Locations.UMOTLIKU, Locations.TECHTOWER].includes(choice)) { + // const restaurant = locationToRestaurant(choice); + // if (restaurant) { + // const selection = food?.[restaurant]; + // console.log("Choice and restaurant", choice, restaurant); + // setFoodSelection(selection); + // } + // } + } else { + setFoodSelection(undefined); } } @@ -104,6 +160,19 @@ function App() { } } + const changeFoodChoice = async (event: React.ChangeEvent) => { + console.log("Přidání jídla", choiceRef.current?.value, event.target.value); + let key: string | undefined = undefined; + // TODO pokračovat + // if (choiceRef.current?.value) { + // const location = choiceRef.current.value as Locations; + // key = Object.keys(Locations)[Object.values(Locations).indexOf(location)]; + // } + // if (auth?.login) { + // await updateFoodChoice(auth.login, key, event.target.value); + // } + } + const pizzaSuggestions = useMemo(() => { if (!pizzy) { return []; @@ -222,27 +291,32 @@ function App() {

Jak to dnes vidíš s obědem?

- - - - - - - + {Object.values(Locations).map(key => )}

Aktuálně je možné vybrat pouze jednu variantu.

+ {foodSelection && + <> +

Na jaké hlavní jídlo?

+ + + {foodSelection?.filter(food => !food.isSoup).map(food => + + )} + + + } {Object.keys(data.choices).length > 0 ? {Object.keys(data.choices).map((key: string, index: number) => - +
{Object.values(Locations)[Number(key)]}{renderLocation(Locations[key])}
    - {data.choices[Number(key)].map((p: string, index: number) => -
  • {p} {p === auth.login && { + {data.choices[key].map((p: FoodChoice, index: number) => +
  • {p.login} {p.login === auth.login && { removeChoice(key); }} title='Odstranit' className='trash-icon' icon={faTrashCan} />}
  • )} diff --git a/client/src/Types.tsx b/client/src/Types.tsx index 13c6bd4..8d9c32b 100644 --- a/client/src/Types.tsx +++ b/client/src/Types.tsx @@ -32,8 +32,13 @@ export interface Order { note?: string, // volitelná poznámka uživatele k objednávce } +export interface FoodChoice { + login: string, // jméno uživatele + foodName?: string, // název vybraného jídla +} + export interface Choices { - [location: string]: string[], + [location: string]: FoodChoice[], } /** Údaje o Pizza day. */ @@ -51,13 +56,13 @@ export interface ClientData { } export enum Locations { - SLADOVNICKA = 'Sladovnická', - UMOTLIKU = 'U Motlíků', - TECHTOWER = 'TechTower', - SPSE = 'SPŠE', - PIZZA = 'Pizza day', - OBJEDNAVAM = 'Budu objednávat', - NEOBEDVAM = 'Mám vlastní/neobědvám', + SLADOVNICKA = 'SLADOVNICKA', + UMOTLIKU = 'UMOTLIKU', + TECHTOWER = 'TECHTOWER', + SPSE = 'SPSE', + PIZZA = 'PIZZA', + OBJEDNAVAM = 'OBJEDNAVAM', + NEOBEDVAM = 'NEOBEDVAM', } /** Jídlo z obědového menu restaurace. */ diff --git a/server/src/index.ts b/server/src/index.ts index 3993e3d..c712050 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, finishPizzaDelivery, finishPizzaOrder, getData, lockPizzaDay, removePizzaOrder, unlockPizzaDay, updateChoice, updateNote } from "./service"; +import { addPizzaOrder, createPizzaDay, deletePizzaDay, finishPizzaDelivery, finishPizzaOrder, getData, lockPizzaDay, removePizzaOrder, unlockPizzaDay, updateChoice, updateFoodChoice, updateNote } from "./service"; import dotenv from 'dotenv'; import path from 'path'; import { getMenuSladovnicka, getMenuTechTower, getMenuUMotliku } from "./restaurants"; @@ -179,6 +179,18 @@ app.post("/api/updateNote", (req, res) => { res.status(200).json(data); }); +app.post("/api/updateFoodChoice", (req, res) => { + if (!req.body.login) { + throw Error("Nebyl předán login"); + } + if (!req.body.choice) { + throw Error("Nebyla předána vybraná restaurace"); + } + const data = updateFoodChoice(req.body.login, req.body.choice, req.body.food); + io.emit("message", data); + res.status(200).json(data); +}); + io.on("connection", (socket) => { console.log(`New client connected: ${socket.id}`); diff --git a/server/src/service.ts b/server/src/service.ts index 6711358..d1d6004 100644 --- a/server/src/service.ts +++ b/server/src/service.ts @@ -240,11 +240,13 @@ export function initIfNeeded() { 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]; + if (data.choices[key]) { + const index = data.choices[key].findIndex(choice => choice.login === login); + if (index >= 0) { + data.choices[key].splice(index, 1); + if (data.choices[key].length == 0) { + delete data.choices[key]; + } } } } @@ -256,11 +258,11 @@ export function updateChoice(login: string, choice: Locations | null) { const today = formatDate(getToday()); let data: ClientData = db.get(today); data = removeChoice(login, data); - if (choice !== null) { + if (choice) { if (!data.choices?.[choice]) { data.choices[choice] = []; } - data.choices[choice].push(login); + data.choices[choice].push({ login }); } db.set(today, data); return data; @@ -282,4 +284,24 @@ export function updateNote(login: string, note?: string) { myOrder.note = note; db.set(today, clientData); return clientData; +} + +export function updateFoodChoice(login: string, location: Locations, foodName?: string) { + const today = formatDate(getToday()); + let clientData: ClientData = db.get(today); + + // TODO smazat + console.log("Location", location); + console.log("Choices", clientData.choices); + const myChoice = clientData.choices[location].find(o => o.login === login); + if (!myChoice) { + throw Error("V datech nebyla nalezena volba pro uživatele " + login); + } + if (foodName) { + myChoice.foodName = foodName; + } else { + delete myChoice.foodName; + } + db.set(today, clientData); + return clientData; } \ No newline at end of file diff --git a/server/src/types.ts b/server/src/types.ts index 0a52a9b..d3005e2 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -1,7 +1,13 @@ import exp from "constants"; +/** Výběr jídla z restaurace jednoho uživatele. */ +export interface FoodChoice { + login: string, // jméno uživatele + foodName?: string, // název vybraného jídla +} + export interface Choices { - [location: string]: string[], + [location: string]: FoodChoice[], } /** Velikost konkrétní pizzy */ @@ -77,13 +83,13 @@ export enum Restaurants { } export enum Locations { - SLADOVNICKA = 'Sladovnická', - UMOTLIKU = 'U Motlíků', - TECHTOWER = 'TechTower', - SPSE = 'SPŠE', - PIZZA = 'Pizza day', - OBJEDNAVAM = 'Budu objednávat', - NEOBEDVAM = 'Mám vlastní/neobědvám', + SLADOVNICKA = 'SLADOVNICKA', + UMOTLIKU = 'UMOTLIKU', + TECHTOWER = 'TECHTOWER', + SPSE = 'SPSE', + PIZZA = 'PIZZA', + OBJEDNAVAM = 'OBJEDNAVAM', + NEOBEDVAM = 'NEOBEDVAM', } export enum UdalostEnum {