Merge pull request 'feat/odflaknutyRefreshDat' (#17) from feat/odflaknutyRefreshDat into master
All checks were successful
ci/woodpecker/push/workflow Pipeline was successful
All checks were successful
ci/woodpecker/push/workflow Pipeline was successful
Reviewed-on: #17
This commit is contained in:
commit
d2845f7d0f
@ -414,8 +414,7 @@ function App() {
|
||||
<img alt="" src='snowman.png' style={{ position: "absolute", height: "110px", right: 10, top: 5 }} /> */}
|
||||
Poslední změny:
|
||||
<ul>
|
||||
<li>Migrace na generované <Link target='_blank' to="https://www.openapis.org">OpenAPI</Link></li>
|
||||
<li>Odebrání zimní atmosféry</li>
|
||||
<li>Podpora ručního refresh týdne</li>
|
||||
</ul>
|
||||
</Alert>
|
||||
{dayIndex != null &&
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useRef } from "react";
|
||||
import { Modal, Button } from "react-bootstrap"
|
||||
import { useRef, useState } from "react";
|
||||
import { Modal, Button, Alert } from "react-bootstrap"
|
||||
import { useSettings } from "../../context/settings";
|
||||
|
||||
type Props = {
|
||||
@ -15,6 +15,41 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
|
||||
const nameRef = useRef<HTMLInputElement>(null);
|
||||
const hideSoupsRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// Pro refresh jidel
|
||||
const refreshPassRef = useRef<HTMLInputElement>(null);
|
||||
const refreshTypeRef = useRef<HTMLSelectElement>(null);
|
||||
const [refreshLoading, setRefreshLoading] = useState(false);
|
||||
const [refreshMessage, setRefreshMessage] = useState<{ type: 'success' | 'error', text: string } | null>(null);
|
||||
|
||||
const handleRefresh = async () => {
|
||||
const password = refreshPassRef.current?.value;
|
||||
const type = refreshTypeRef.current?.value;
|
||||
if (!password || !type) {
|
||||
setRefreshMessage({ type: 'error', text: 'Zadejte heslo a typ refresh.' });
|
||||
return;
|
||||
}
|
||||
|
||||
setRefreshLoading(true);
|
||||
setRefreshMessage(null);
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/food/refresh?type=${type}&heslo=${encodeURIComponent(password)}`);
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
setRefreshMessage({ type: 'success', text: 'Uspesny fetch' });
|
||||
if (refreshPassRef.current) {
|
||||
// Clean hesla xd
|
||||
refreshPassRef.current.value = '';
|
||||
}
|
||||
} else {
|
||||
setRefreshMessage({ type: 'error', text: data.error || 'Chyba při obnovování jídelníčku.' });
|
||||
}
|
||||
} catch (error) { }
|
||||
finally {
|
||||
setRefreshLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return <Modal show={isOpen} onHide={onClose} size="lg">
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title><h2>Nastavení</h2></Modal.Title>
|
||||
@ -24,6 +59,48 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
|
||||
<span title="V nabídkách nebudou zobrazovány polévky. Tato funkce je experimentální, a zejména u TechTower bývá často problém polévky spolehlivě rozeznat. V případě využití této funkce průběžně nahlašujte stále se zobrazující polévky." style={{ "cursor": "help" }}>
|
||||
<input ref={hideSoupsRef} type="checkbox" defaultChecked={settings?.hideSoups} /> Skrýt polévky
|
||||
</span>
|
||||
<hr />
|
||||
<h4>Obnovit jídelníček</h4>
|
||||
<p>Ruční refresh dat z restaurací.</p>
|
||||
|
||||
{refreshMessage && (
|
||||
<Alert variant={refreshMessage.type === 'success' ? 'success' : 'danger'}>
|
||||
{refreshMessage.text}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className="mb-3">
|
||||
Heslo: <input
|
||||
ref={refreshPassRef}
|
||||
type="password"
|
||||
placeholder="Zadejte heslo"
|
||||
className="form-control d-inline-block"
|
||||
style={{ width: 'auto', marginLeft: '10px' }}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
Typ refreshe: <select
|
||||
ref={refreshTypeRef}
|
||||
className="form-select d-inline-block"
|
||||
style={{ width: 'auto', marginLeft: '10px' }}
|
||||
defaultValue="week"
|
||||
>
|
||||
<option value="week">Týden</option>
|
||||
<option value="day">Den</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
variant="info"
|
||||
onClick={handleRefresh}
|
||||
disabled={refreshLoading}
|
||||
className="mb-3"
|
||||
>
|
||||
{refreshLoading ? 'Refreshing...' : 'Refresh'}
|
||||
</Button>
|
||||
|
||||
<hr />
|
||||
<h4>Bankovní účet</h4>
|
||||
<p>Nastavením čísla účtu umožníte automatické generování QR kódů pro úhradu za vámi provedené objednávky v rámci Pizza day.<br />Pokud vaše číslo účtu neobsahuje předčíslí, je možné ho zcela vynechat.<br /><br />Číslo účtu není ukládáno na serveru, posílá se na něj pouze za účelem vygenerování QR kódů.</p>
|
||||
|
@ -9,7 +9,7 @@ import { generateToken, verify } from "./auth";
|
||||
import { InsufficientPermissions } from "./utils";
|
||||
import { initWebsocket } from "./websocket";
|
||||
import pizzaDayRoutes from "./routes/pizzaDayRoutes";
|
||||
import foodRoutes from "./routes/foodRoutes";
|
||||
import foodRoutes, { refreshMetoda } from "./routes/foodRoutes";
|
||||
import votingRoutes from "./routes/votingRoutes";
|
||||
import easterEggRoutes from "./routes/easterEggRoutes";
|
||||
import statsRoutes from "./routes/statsRoutes";
|
||||
@ -96,6 +96,9 @@ app.get("/api/qr", (req, res) => {
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
// Přeskočení auth pro refresh dat xd
|
||||
app.use("/api/food/refresh", refreshMetoda);
|
||||
|
||||
/** Middleware ověřující JWT token */
|
||||
app.use("/api/", (req, res, next) => {
|
||||
if (HTTP_REMOTE_USER_ENABLED) {
|
||||
|
@ -1,11 +1,52 @@
|
||||
import express, { Request, Response } from "express";
|
||||
import { getLogin, getTrusted } from "../auth";
|
||||
import { addChoice, getDateForWeekIndex, getToday, removeChoice, removeChoices, updateDepartureTime, updateNote, getRestaurantMenu } from "../service";
|
||||
import { addChoice, getDateForWeekIndex, getToday, removeChoice, removeChoices, updateDepartureTime, updateNote, getRestaurantMenu, fetchRestaurantWeekMenuData, saveRestaurantWeekMenu } from "../service";
|
||||
import { getDayOfWeekIndex, parseToken, getFirstWorkDayOfWeek } from "../utils";
|
||||
import { getWebsocket } from "../websocket";
|
||||
import { callNotifikace } from "../notifikace";
|
||||
import { AddChoiceData, ChangeDepartureTimeData, RemoveChoiceData, RemoveChoicesData, UdalostEnum, UpdateNoteData } from "../../../types/gen/types.gen";
|
||||
|
||||
|
||||
// RateLimit na refresh endpoint
|
||||
interface RateLimitEntry {
|
||||
count: number;
|
||||
resetTime: number;
|
||||
}
|
||||
const rateLimits: Record<string, RateLimitEntry> = {};
|
||||
const RATE_LIMIT = 1; // maximální počet požadavků za minutu
|
||||
const RATE_LIMIT_WINDOW = 30 * 60 * 1000; // je to v ms (x * 1min)
|
||||
|
||||
// Kontrola ratelimitu
|
||||
function checkRateLimit(key: string, limit: number = RATE_LIMIT): boolean {
|
||||
const now = Date.now();
|
||||
|
||||
// Vyčištění starých záznamů
|
||||
Object.keys(rateLimits).forEach(k => {
|
||||
if (rateLimits[k].resetTime < now) {
|
||||
delete rateLimits[k];
|
||||
}
|
||||
});
|
||||
|
||||
// Kontrola, že záznam existuje a platí
|
||||
if (rateLimits[key] && rateLimits[key].resetTime > now) {
|
||||
// Záznam platí a kontroluje se limit
|
||||
if (rateLimits[key].count >= limit) {
|
||||
return false; // Překročen limit
|
||||
}
|
||||
|
||||
// ++ xd
|
||||
rateLimits[key].count++;
|
||||
return true;
|
||||
} else {
|
||||
// + klic
|
||||
rateLimits[key] = {
|
||||
count: 1,
|
||||
resetTime: now + RATE_LIMIT_WINDOW
|
||||
};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ověří a vrátí index dne v týdnu z požadavku, za předpokladu, že byl předán, a je zároveň
|
||||
* roven nebo vyšší indexu dnešního dne.
|
||||
@ -142,26 +183,84 @@ router.post("/jdemeObed", async (req, res, next) => {
|
||||
});
|
||||
|
||||
// /api/food/refresh?type=week&heslo=docasnyheslo
|
||||
router.get("/refresh", async (req: Request, res: Response) => {
|
||||
export const refreshMetoda = async (req: Request, res: Response) => {
|
||||
const { type, heslo } = req.query as { type?: string; heslo?: string };
|
||||
if (heslo !== "docasnyheslo") {
|
||||
if (heslo !== "docasnyheslo" && heslo !== "tohleheslopavelnesmizjistit123") {
|
||||
return res.status(403).json({ error: "Neplatné heslo" });
|
||||
}
|
||||
if (type !== "week") {
|
||||
if (!checkRateLimit("refresh") && heslo !== "tohleheslopavelnesmizjistit123") {
|
||||
return res.status(429).json({ error: "Refresh už se zavolal, chvíli počkej :))" });
|
||||
}
|
||||
if (type !== "week" && type !== "day") {
|
||||
return res.status(400).json({ error: "Neznámý typ refresh" });
|
||||
}
|
||||
if (type === "day") {
|
||||
return res.status(400).json({ error: "ještě neumim TODO..." });
|
||||
}
|
||||
try {
|
||||
// Pro všechny restaurace refreshni menu na aktuální týden
|
||||
const restaurants = ["SLADOVNICKA", "TECHTOWER", "ZASTAVKAUMICHALA", "SENKSERIKOVA"] as const;
|
||||
const firstDay = getFirstWorkDayOfWeek(getToday());
|
||||
const results: Record<string, any> = {};
|
||||
const successfulRestaurants: string[] = [];
|
||||
const failedRestaurants: string[] = [];
|
||||
|
||||
// Nejdříve načíst všechna data bez ukládání
|
||||
for (const rest of restaurants) {
|
||||
results[rest] = await getRestaurantMenu(rest, firstDay, true);
|
||||
try {
|
||||
const weekData = await fetchRestaurantWeekMenuData(rest, firstDay);
|
||||
results[rest] = weekData;
|
||||
|
||||
// Kontrola validity dat
|
||||
if (weekData && weekData.length > 0 &&
|
||||
weekData.some(dayMenu => dayMenu && dayMenu.length > 0)) {
|
||||
successfulRestaurants.push(rest);
|
||||
} else {
|
||||
failedRestaurants.push(rest);
|
||||
results[rest] = { error: "Žádná validní data" };
|
||||
}
|
||||
} catch (error) {
|
||||
failedRestaurants.push(rest);
|
||||
results[rest] = { error: `Chyba při načítání: ${error}` };
|
||||
}
|
||||
}
|
||||
res.status(200).json({ ok: true, refreshed: results });
|
||||
|
||||
// Pokud se nepodařilo načíst žádnou restauraci
|
||||
if (successfulRestaurants.length === 0) {
|
||||
return res.status(400).json({
|
||||
error: "Nepodařilo se získat validní data z žádné restaurace",
|
||||
failed: failedRestaurants,
|
||||
results: results
|
||||
});
|
||||
}
|
||||
|
||||
// Uložit pouze validní data
|
||||
for (const rest of successfulRestaurants) {
|
||||
try {
|
||||
await saveRestaurantWeekMenu(rest as any, firstDay, results[rest]);
|
||||
} catch (error) {
|
||||
console.error(`Chyba při ukládání dat pro ${rest}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Připravit odpověď
|
||||
const response: any = {
|
||||
ok: true,
|
||||
refreshed: results,
|
||||
successful: successfulRestaurants
|
||||
};
|
||||
|
||||
if (failedRestaurants.length > 0) {
|
||||
response.warning = `Nepodařilo se načíst: ${failedRestaurants.join(', ')}`;
|
||||
response.failed = failedRestaurants;
|
||||
}
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (e: any) {
|
||||
res.status(500).json({ error: e?.message || "Chyba při refreshi" });
|
||||
}
|
||||
});
|
||||
}
|
||||
router.get("/refresh", refreshMetoda);
|
||||
|
||||
|
||||
export default router;
|
@ -76,13 +76,105 @@ async function getMenu(date: Date): Promise<WeekMenu | undefined> {
|
||||
}
|
||||
|
||||
// TODO přesun do restaurants.ts
|
||||
/**
|
||||
* Načte menu dané restaurace pro celý týden bez ukládání do storage.
|
||||
* Používá se pro validaci dat před uložením.
|
||||
*
|
||||
* @param restaurant restaurace
|
||||
* @param firstDay první pracovní den týdne
|
||||
* @returns pole menu pro jednotlivé dny týdne
|
||||
*/
|
||||
export async function fetchRestaurantWeekMenuData(restaurant: Restaurant, firstDay: Date): Promise<any[]> {
|
||||
return await fetchRestaurantWeekMenu(restaurant, firstDay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uloží týdenní menu restaurace do storage.
|
||||
*
|
||||
* @param restaurant restaurace
|
||||
* @param date datum z týdne, pro který ukládat menu
|
||||
* @param weekData data týdenního menu
|
||||
*/
|
||||
export async function saveRestaurantWeekMenu(restaurant: Restaurant, date: Date, weekData: any[]): Promise<void> {
|
||||
const now = new Date().getTime();
|
||||
let weekMenu = await getMenu(date);
|
||||
weekMenu ??= [{}, {}, {}, {}, {}];
|
||||
|
||||
// Inicializace struktury pro restauraci
|
||||
for (let i = 0; i < 5; i++) {
|
||||
weekMenu[i] ??= {};
|
||||
weekMenu[i][restaurant] ??= {
|
||||
lastUpdate: now,
|
||||
closed: false,
|
||||
food: [],
|
||||
};
|
||||
}
|
||||
|
||||
// Uložení dat pro všechny dny
|
||||
for (let i = 0; i < weekData.length && i < weekMenu.length; i++) {
|
||||
weekMenu[i][restaurant]!.food = weekData[i];
|
||||
weekMenu[i][restaurant]!.lastUpdate = now;
|
||||
|
||||
// Detekce uzavření pro každou restauraci
|
||||
switch (restaurant) {
|
||||
case 'SLADOVNICKA':
|
||||
if (weekData[i].length === 1 && weekData[i][0].name.toLowerCase() === 'pro daný den nebyla nalezena denní nabídka') {
|
||||
weekMenu[i][restaurant]!.closed = true;
|
||||
}
|
||||
break;
|
||||
case 'TECHTOWER':
|
||||
if (weekData[i]?.length === 1 && weekData[i][0].name.toLowerCase() === 'svátek') {
|
||||
weekMenu[i][restaurant]!.closed = true;
|
||||
}
|
||||
break;
|
||||
case 'ZASTAVKAUMICHALA':
|
||||
if (weekData[i]?.length === 1 && weekData[i][0].name === 'Pro tento den není uveřejněna nabídka jídel.') {
|
||||
weekMenu[i][restaurant]!.closed = true;
|
||||
}
|
||||
break;
|
||||
case 'SENKSERIKOVA':
|
||||
if (weekData[i]?.length === 1 && weekData[i][0].name === 'Pro tento den nebylo zadáno menu.') {
|
||||
weekMenu[i][restaurant]!.closed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Uložení do storage
|
||||
await storage.setData(getMenuKey(date), weekMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Načte menu dané restaurace pro celý týden bez ukládání do storage.
|
||||
*
|
||||
* @param restaurant restaurace
|
||||
* @param firstDay první pracovní den týdne
|
||||
* @returns pole menu pro jednotlivé dny týdne
|
||||
*/
|
||||
async function fetchRestaurantWeekMenu(restaurant: Restaurant, firstDay: Date): Promise<any[]> {
|
||||
const mock = process.env.MOCK_DATA === 'true';
|
||||
|
||||
switch (restaurant) {
|
||||
case 'SLADOVNICKA':
|
||||
return await getMenuSladovnicka(firstDay, mock);
|
||||
case 'TECHTOWER':
|
||||
return await getMenuTechTower(firstDay, mock);
|
||||
case 'ZASTAVKAUMICHALA':
|
||||
return await getMenuZastavkaUmichala(firstDay, mock);
|
||||
case 'SENKSERIKOVA':
|
||||
return await getMenuSenkSerikova(firstDay, mock);
|
||||
default:
|
||||
throw new Error(`Nepodporovaná restaurace: ${restaurant}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí menu dané restaurace pro předaný den.
|
||||
* Pokud neexistuje, provede stažení menu pro příslušný týden a uložení do DB.
|
||||
*
|
||||
* @param restaurant restaurace
|
||||
* @param date datum, ke kterému získat menu
|
||||
* @param mock příznak, zda chceme pouze mock data
|
||||
* @param forceRefresh příznak vynuceného obnovení
|
||||
*/
|
||||
export async function getRestaurantMenu(restaurant: Restaurant, date?: Date, forceRefresh = false): Promise<RestaurantDayMenu> {
|
||||
const usedDate = date ?? getToday();
|
||||
@ -108,83 +200,45 @@ export async function getRestaurantMenu(restaurant: Restaurant, date?: Date, for
|
||||
}
|
||||
if (forceRefresh || !weekMenu[dayOfWeekIndex][restaurant]?.food?.length) {
|
||||
const firstDay = getFirstWorkDayOfWeek(usedDate);
|
||||
const mock = process.env.MOCK_DATA === 'true';
|
||||
switch (restaurant) {
|
||||
case 'SLADOVNICKA':
|
||||
try {
|
||||
const sladovnickaFood = await getMenuSladovnicka(firstDay, mock);
|
||||
for (let i = 0; i < sladovnickaFood.length; i++) {
|
||||
weekMenu[i][restaurant]!.food = sladovnickaFood[i];
|
||||
weekMenu[i][restaurant]!.lastUpdate = now;
|
||||
// Velice chatrný a nespolehlivý způsob detekce uzavření...
|
||||
if (sladovnickaFood[i].length === 1 && sladovnickaFood[i][0].name.toLowerCase() === 'pro daný den nebyla nalezena denní nabídka') {
|
||||
|
||||
try {
|
||||
const restaurantWeekFood = await fetchRestaurantWeekMenu(restaurant, firstDay);
|
||||
|
||||
// Aktualizace menu pro všechny dny
|
||||
for (let i = 0; i < restaurantWeekFood.length && i < weekMenu.length; i++) {
|
||||
weekMenu[i][restaurant]!.food = restaurantWeekFood[i];
|
||||
weekMenu[i][restaurant]!.lastUpdate = now;
|
||||
|
||||
// Detekce uzavření pro každou restauraci
|
||||
switch (restaurant) {
|
||||
case 'SLADOVNICKA':
|
||||
if (restaurantWeekFood[i].length === 1 && restaurantWeekFood[i][0].name.toLowerCase() === 'pro daný den nebyla nalezena denní nabídka') {
|
||||
weekMenu[i][restaurant]!.closed = true;
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error("Selhalo načtení jídel pro podnik Sladovnická", e);
|
||||
}
|
||||
break;
|
||||
// case 'UMOTLIKU':
|
||||
// try {
|
||||
// const uMotlikuFood = await getMenuUMotliku(firstDay, mock);
|
||||
// for (let i = 0; i < uMotlikuFood.length; i++) {
|
||||
// menus[i][restaurant]!.food = uMotlikuFood[i];
|
||||
// if (uMotlikuFood[i].length === 1 && uMotlikuFood[i][0].name.toLowerCase() === 'zavřeno') {
|
||||
// menus[i][restaurant]!.closed = true;
|
||||
// }
|
||||
// }
|
||||
// } catch (e: any) {
|
||||
// console.error("Selhalo načtení jídel pro podnik U Motlíků", e);
|
||||
// }
|
||||
// break;
|
||||
case 'TECHTOWER':
|
||||
try {
|
||||
const techTowerFood = await getMenuTechTower(firstDay, mock);
|
||||
for (let i = 0; i < techTowerFood.length; i++) {
|
||||
weekMenu[i][restaurant]!.food = techTowerFood[i];
|
||||
weekMenu[i][restaurant]!.lastUpdate = now;
|
||||
if (techTowerFood[i]?.length === 1 && techTowerFood[i][0].name.toLowerCase() === 'svátek') {
|
||||
break;
|
||||
case 'TECHTOWER':
|
||||
if (restaurantWeekFood[i]?.length === 1 && restaurantWeekFood[i][0].name.toLowerCase() === 'svátek') {
|
||||
weekMenu[i][restaurant]!.closed = true;
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error("Selhalo načtení jídel pro podnik TechTower", e);
|
||||
}
|
||||
break;
|
||||
case 'ZASTAVKAUMICHALA':
|
||||
try {
|
||||
const zastavkaUmichalaFood = await getMenuZastavkaUmichala(firstDay, mock);
|
||||
for (let i = 0; i < zastavkaUmichalaFood.length; i++) {
|
||||
weekMenu[i][restaurant]!.food = zastavkaUmichalaFood[i];
|
||||
weekMenu[i][restaurant]!.lastUpdate = now;
|
||||
if (zastavkaUmichalaFood[i]?.length === 1 && zastavkaUmichalaFood[i][0].name === 'Pro tento den není uveřejněna nabídka jídel.') {
|
||||
break;
|
||||
case 'ZASTAVKAUMICHALA':
|
||||
if (restaurantWeekFood[i]?.length === 1 && restaurantWeekFood[i][0].name === 'Pro tento den není uveřejněna nabídka jídel.') {
|
||||
weekMenu[i][restaurant]!.closed = true;
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error("Selhalo načtení jídel pro podnik Zastávka u Michala", e);
|
||||
}
|
||||
break;
|
||||
case 'SENKSERIKOVA':
|
||||
try {
|
||||
const senkSerikovaFood = await getMenuSenkSerikova(firstDay, mock);
|
||||
for (let i = 0; i < senkSerikovaFood.length; i++) {
|
||||
if (i >= weekMenu.length) {
|
||||
break;
|
||||
}
|
||||
weekMenu[i][restaurant]!.food = senkSerikovaFood[i];
|
||||
weekMenu[i][restaurant]!.lastUpdate = now;
|
||||
if (senkSerikovaFood[i]?.length === 1 && senkSerikovaFood[i][0].name === 'Pro tento den nebylo zadáno menu.') {
|
||||
break;
|
||||
case 'SENKSERIKOVA':
|
||||
if (restaurantWeekFood[i]?.length === 1 && restaurantWeekFood[i][0].name === 'Pro tento den nebylo zadáno menu.') {
|
||||
weekMenu[i][restaurant]!.closed = true;
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error("Selhalo načtení jídel pro podnik Pivovarský šenk Šeříková", e);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Uložení do storage
|
||||
await storage.setData(getMenuKey(usedDate), weekMenu);
|
||||
} catch (e: any) {
|
||||
console.error(`Selhalo načtení jídel pro podnik ${restaurant}`, e);
|
||||
}
|
||||
await storage.setData(getMenuKey(usedDate), weekMenu);
|
||||
}
|
||||
return weekMenu[dayOfWeekIndex][restaurant]!;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user