fix: oprava pádu při zdvojeném menu
CI / Generate TypeScript types (push) Successful in 13s
CI / Server unit tests (push) Successful in 23s
CI / Build server (push) Successful in 26s
CI / Build client (push) Successful in 38s
CI / Playwright E2E tests (push) Successful in 1m29s
CI / Build and push Docker image (push) Successful in 43s
CI / Notify (push) Successful in 2s

This commit is contained in:
2026-07-20 11:33:14 +02:00
parent 0232e6ea26
commit 146c31b775
3 changed files with 102 additions and 3 deletions
+45 -1
View File
@@ -4,7 +4,7 @@ import { getMenuSladovnicka, getMenuTechTower, getMenuZastavkaUmichala, getMenuS
import { getTodayMock } from "./mock";
import { removeAllUserPizzas } from "./pizza";
import { getStores } from "./stores";
import { ClientData, DepartureTime, LunchChoice, MealSlot, PizzaDayState, Restaurant, RestaurantDayMenu, WeekMenu } from "../../types/gen/types.gen";
import { ClientData, DepartureTime, LunchChoice, LunchChoices, MealSlot, PizzaDayState, Restaurant, RestaurantDayMenu, RestaurantDayMenuMap, WeekMenu } from "../../types/gen/types.gen";
const storage = getStorage();
const MENU_PREFIX = 'menu';
@@ -62,10 +62,54 @@ export async function getData(date?: Date, slot?: MealSlot): Promise<ClientData>
ZASTAVKAUMICHALA: await getRestaurantMenu('ZASTAVKAUMICHALA', date),
SENKSERIKOVA: await getRestaurantMenu('SENKSERIKOVA', date),
}
// Očistí volby jídel odkazující na indexy, které v aktuálním menu již neexistují.
// Pokud se v odpovědi něco změnilo, promítne očistu i do uložených dat (self-heal).
if (pruneStaleFoodChoices(clientData.choices, clientData.menus)) {
await storage.updateData<ClientData>(getDataKey(date ?? getToday(), slot), (current) => {
const data = current ?? getEmptyData(date);
pruneStaleFoodChoices(data.choices, clientData.menus!);
return data;
});
}
}
return clientData;
}
/**
* Odstraní z voleb uživatelů indexy jídel, které v aktuálním menu daného podniku již neexistují.
*
* Nastává typicky poté, co podnik ze svého webu odebere menu předchozího týdne: aplikace pak
* pro daný den vyparsuje méně jídel než dříve a dříve uložený index (např. z „budoucího“ týdne)
* se stane neplatným. Bez očištění by klient spadl při přístupu na neexistující jídlo a data
* by zůstala nekonzistentní.
*
* Očista se provádí pouze u podniků, které mají v menu alespoň jedno jídlo prázdné menu
* (zavřeno / neúspěšné načtení) volby záměrně nemaže, aby nedošlo ke ztrátě platných voleb.
*
* @param choices volby uživatelů
* @param menus menu podniků pro daný den
* @returns true, pokud došlo k odebrání alespoň jednoho indexu
*/
export function pruneStaleFoodChoices(choices: LunchChoices, menus: RestaurantDayMenuMap): boolean {
let mutated = false;
for (const restaurant of Object.keys(Restaurant) as Restaurant[]) {
const foodCount = menus[restaurant]?.food?.length ?? 0;
if (foodCount === 0) continue; // prázdné menu volby nemažeme
const locationChoices = choices[restaurant];
if (!locationChoices) continue;
for (const userChoice of Object.values(locationChoices)) {
const selected = userChoice.selectedFoods;
if (!selected?.length) continue;
const filtered = selected.filter(index => index < foodCount);
if (filtered.length !== selected.length) {
userChoice.selectedFoods = filtered;
mutated = true;
}
}
}
return mutated;
}
/**
* Vrátí klíč, pod kterým je uloženo menu pro týden příslušící předanému datu.
*