diff --git a/client/src/App.tsx b/client/src/App.tsx
index ae23bb5..5deb500 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -18,7 +18,7 @@ import Loader from './components/Loader';
import { getHumanDateTime, isInTheFuture } from './Utils';
import NoteModal from './components/modals/NoteModal';
import { useEasterEgg } from './context/eggs';
-import { ClientData, Food, PizzaOrder, DepartureTime, PizzaDayState, Restaurant, RestaurantDayMenu, RestaurantDayMenuMap, LunchChoice, UserLunchChoice, PizzaVariant, getData, getEasterEggImage, addPizza, removePizza, updatePizzaDayNote, createPizzaDay, deletePizzaDay, lockPizzaDay, unlockPizzaDay, finishOrder, finishDelivery, addChoice, jdemeObed, removeChoices, removeChoice, updateNote, changeDepartureTime, setBuyer } from '../../types';
+import { ClientData, Food, PizzaOrder, DepartureTime, PizzaDayState, Restaurant, RestaurantDayMenu, RestaurantDayMenuMap, LunchChoice, UserLunchChoice, PizzaVariant, getData, getEasterEggImage, addPizza, removePizza, updatePizzaDayNote, createPizzaDay, deletePizzaDay, lockPizzaDay, unlockPizzaDay, finishOrder, finishDelivery, addChoice, jdemeObed, removeChoices, removeChoice, updateNote, changeDepartureTime, setBuyer, dismissQr } from '../../types';
import { getLunchChoiceName } from './enums';
// import FallingLeaves, { LEAF_PRESETS, LEAF_COLOR_THEMES } from './FallingLeaves';
// import './FallingLeaves.scss';
@@ -729,6 +729,32 @@ function App() {
}
+ {data.pendingQrs && data.pendingQrs.length > 0 &&
+
+
Nevyřízené platby
+
Máte neuhrazené QR kódy z předchozích Pizza day.
+ {data.pendingQrs.map(qr => (
+
+
+ {qr.date} — {qr.creator} ({qr.totalPrice} Kč)
+
+

+
+
+
+
+ ))}
+
+ }
>
{/* {
// Na víkendu zobrazíme pátek místo hlášky "Užívejte víkend"
date = getDateForWeekIndex(4);
}
- res.status(200).json(await getData(date));
+ const data = await getData(date);
+ // Připojíme nevyřízené QR kódy pro přihlášeného uživatele
+ try {
+ const login = getLogin(parseToken(req));
+ const pendingQrs = await getPendingQrs(login);
+ if (pendingQrs.length > 0) {
+ data.pendingQrs = pendingQrs;
+ }
+ } catch {
+ // Token nemusí být validní, ignorujeme
+ }
+ res.status(200).json(data);
});
// Ostatní routes
diff --git a/server/src/pizza.ts b/server/src/pizza.ts
index f1c7d48..8ffdf69 100644
--- a/server/src/pizza.ts
+++ b/server/src/pizza.ts
@@ -4,9 +4,10 @@ import { generateQr } from "./qr";
import getStorage from "./storage";
import { downloadPizzy } from "./chefie";
import { getClientData, getToday, initIfNeeded } from "./service";
-import { Pizza, ClientData, PizzaDayState, PizzaSize, PizzaOrder, PizzaVariant, UdalostEnum } from "../../types/gen/types.gen";
+import { Pizza, ClientData, PizzaDayState, PizzaSize, PizzaOrder, PizzaVariant, UdalostEnum, PendingQr } from "../../types/gen/types.gen";
const storage = getStorage();
+const PENDING_QR_PREFIX = 'pending_qr';
/**
* Vrátí seznam dostupných pizz pro dnešní den.
@@ -241,6 +242,12 @@ export async function finishPizzaDelivery(login: string, bankAccount?: string, b
let message = order.pizzaList!.map(pizza => `Pizza ${pizza.name} (${pizza.size})`).join(', ');
await generateQr(order.customer, bankAccount, bankAccountHolder, order.totalPrice, message);
order.hasQr = true;
+ // Uložíme nevyřízený QR kód pro persistentní zobrazení
+ await addPendingQr(order.customer, {
+ date: today,
+ creator: login,
+ totalPrice: order.totalPrice,
+ });
}
}
}
@@ -307,4 +314,41 @@ export async function updatePizzaFee(login: string, targetLogin: string, text?:
targetOrder.totalPrice = targetOrder.pizzaList.reduce((price, pizzaOrder) => price + pizzaOrder.price, 0) + (targetOrder.fee?.price ?? 0);
await storage.setData(today, clientData);
return clientData;
+}
+
+/**
+ * Vrátí klíč pro uložení nevyřízených QR kódů uživatele.
+ */
+function getPendingQrKey(login: string): string {
+ return `${PENDING_QR_PREFIX}_${login}`;
+}
+
+/**
+ * Přidá nevyřízený QR kód pro uživatele.
+ */
+async function addPendingQr(login: string, pendingQr: PendingQr): Promise {
+ const key = getPendingQrKey(login);
+ const existing = await storage.getData(key) ?? [];
+ // Nepřidáváme duplicity pro stejný den
+ if (!existing.some(qr => qr.date === pendingQr.date)) {
+ existing.push(pendingQr);
+ await storage.setData(key, existing);
+ }
+}
+
+/**
+ * Vrátí nevyřízené QR kódy pro uživatele.
+ */
+export async function getPendingQrs(login: string): Promise {
+ return await storage.getData(getPendingQrKey(login)) ?? [];
+}
+
+/**
+ * Označí QR kód pro daný den jako uhrazený (odstraní ho ze seznamu nevyřízených).
+ */
+export async function dismissPendingQr(login: string, date: string): Promise {
+ const key = getPendingQrKey(login);
+ const existing = await storage.getData(key) ?? [];
+ const filtered = existing.filter(qr => qr.date !== date);
+ await storage.setData(key, filtered);
}
\ No newline at end of file
diff --git a/server/src/routes/pizzaDayRoutes.ts b/server/src/routes/pizzaDayRoutes.ts
index 944518e..82a26c5 100644
--- a/server/src/routes/pizzaDayRoutes.ts
+++ b/server/src/routes/pizzaDayRoutes.ts
@@ -1,9 +1,9 @@
import express, { Request } from "express";
import { getLogin } from "../auth";
-import { createPizzaDay, deletePizzaDay, getPizzaList, addPizzaOrder, removePizzaOrder, lockPizzaDay, unlockPizzaDay, finishPizzaOrder, finishPizzaDelivery, updatePizzaDayNote, updatePizzaFee } from "../pizza";
+import { createPizzaDay, deletePizzaDay, getPizzaList, addPizzaOrder, removePizzaOrder, lockPizzaDay, unlockPizzaDay, finishPizzaOrder, finishPizzaDelivery, updatePizzaDayNote, updatePizzaFee, dismissPendingQr } from "../pizza";
import { parseToken } from "../utils";
import { getWebsocket } from "../websocket";
-import { AddPizzaData, FinishDeliveryData, RemovePizzaData, UpdatePizzaDayNoteData, UpdatePizzaFeeData } from "../../../types";
+import { AddPizzaData, DismissQrData, FinishDeliveryData, RemovePizzaData, UpdatePizzaDayNoteData, UpdatePizzaFeeData } from "../../../types";
const router = express.Router();
@@ -109,4 +109,16 @@ router.post("/updatePizzaFee", async (req: Request<{}, any, UpdatePizzaFeeData["
} catch (e: any) { next(e) }
});
+/** Označí QR kód jako uhrazený. */
+router.post("/dismissQr", async (req: Request<{}, any, DismissQrData["body"]>, res, next) => {
+ const login = getLogin(parseToken(req));
+ if (!req.body.date) {
+ return res.status(400).json({ error: "Nebyl předán datum" });
+ }
+ try {
+ await dismissPendingQr(login, req.body.date);
+ res.status(200).json({});
+ } catch (e: any) { next(e) }
+});
+
export default router;
\ No newline at end of file