Compare commits
1 Commits
20f4ee0427
...
feat/refak
| Author | SHA1 | Date | |
|---|---|---|---|
|
0179afca75
|
@@ -24,7 +24,6 @@
|
||||
"react-router": "^7.9.5",
|
||||
"react-router-dom": "^7.9.5",
|
||||
"react-select-search": "^4.1.6",
|
||||
"react-snow-overlay": "^1.0.14",
|
||||
"react-snowfall": "^2.3.0",
|
||||
"react-toastify": "^11.0.5",
|
||||
"recharts": "^3.4.1",
|
||||
|
||||
@@ -15,13 +15,13 @@ import { useSettings } from './context/settings';
|
||||
import Footer from './components/Footer';
|
||||
import { faChainBroken, faChevronLeft, faChevronRight, faGear, faSatelliteDish, faSearch } from '@fortawesome/free-solid-svg-icons';
|
||||
import Loader from './components/Loader';
|
||||
import { getHumanDateTime, isInTheFuture } from './Utils';
|
||||
import { getDayOfWeekIndex, getHumanDate, getHumanDateTime, getIsWeekend, 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 } from '../../types';
|
||||
import { getLunchChoiceName } from './enums';
|
||||
// import FallingLeaves, { LEAF_PRESETS, LEAF_COLOR_THEMES } from './FallingLeaves';
|
||||
// import './FallingLeaves.scss';
|
||||
import FallingLeaves, { LEAF_PRESETS, LEAF_COLOR_THEMES } from './FallingLeaves';
|
||||
import './FallingLeaves.scss';
|
||||
|
||||
const EVENT_CONNECT = "connect"
|
||||
|
||||
@@ -71,7 +71,10 @@ function App() {
|
||||
const departureChoiceRef = useRef<HTMLSelectElement>(null);
|
||||
const pizzaPoznamkaRef = useRef<HTMLInputElement>(null);
|
||||
const [failure, setFailure] = useState<boolean>(false);
|
||||
const [dayIndex, setDayIndex] = useState<number>();
|
||||
const [dayIndex, setDayIndex] = useState<number>(); // Index zobrazovaného dne
|
||||
// TODO berka zde je nutné dořešit mocking pro testování
|
||||
const [todayDayIndex, setTodayDayIndex] = useState<number>(getDayOfWeekIndex(new Date())); // Index dnešního dne
|
||||
const [isTodayWeekend, setIsTodayWeekend] = useState<boolean>(getIsWeekend(new Date()));
|
||||
const [loadingPizzaDay, setLoadingPizzaDay] = useState<boolean>(false);
|
||||
const [noteModalOpen, setNoteModalOpen] = useState<boolean>(false);
|
||||
const [eggImage, setEggImage] = useState<Blob>();
|
||||
@@ -89,8 +92,9 @@ function App() {
|
||||
const data = response.data
|
||||
if (data) {
|
||||
setData(data);
|
||||
setDayIndex(data.dayIndex);
|
||||
dayIndexRef.current = data.dayIndex;
|
||||
const dayIndex = getDayOfWeekIndex(new Date(data.date));
|
||||
setDayIndex(dayIndex);
|
||||
dayIndexRef.current = dayIndex;
|
||||
setFood(data.menus);
|
||||
}
|
||||
}).catch(e => {
|
||||
@@ -103,6 +107,8 @@ function App() {
|
||||
if (!auth?.login) {
|
||||
return
|
||||
}
|
||||
setTodayDayIndex(getDayOfWeekIndex(new Date()));
|
||||
setIsTodayWeekend(getIsWeekend(new Date()));
|
||||
getData({ query: { dayIndex: dayIndex } }).then(response => {
|
||||
const data = response.data;
|
||||
setData(data);
|
||||
@@ -125,7 +131,7 @@ function App() {
|
||||
socket.on(EVENT_MESSAGE, (newData: ClientData) => {
|
||||
// console.log("Přijata nová data ze socketu", newData);
|
||||
// Aktualizujeme pouze, pokud jsme dostali data pro den, který máme aktuálně zobrazený
|
||||
if (dayIndexRef.current == null || newData.dayIndex === dayIndexRef.current) {
|
||||
if (dayIndexRef.current == null || getDayOfWeekIndex(new Date(newData.date)) === dayIndexRef.current) {
|
||||
setData(newData);
|
||||
}
|
||||
});
|
||||
@@ -439,7 +445,7 @@ function App() {
|
||||
}
|
||||
|
||||
const noOrders = data?.pizzaDay?.orders?.length === 0;
|
||||
const canChangeChoice = dayIndex == null || data.todayDayIndex == null || dayIndex >= data.todayDayIndex;
|
||||
const canChangeChoice = dayIndex == null || dayIndex >= todayDayIndex;
|
||||
|
||||
const { path, url, startOffset, endOffset, duration, ...style } = easterEgg || {};
|
||||
|
||||
@@ -448,15 +454,15 @@ function App() {
|
||||
{easterEgg && eggImage && <img ref={eggRef} alt='' src={URL.createObjectURL(eggImage)} style={{ position: 'absolute', ...EASTER_EGG_STYLE, ...style, animationDuration: `${duration ?? EASTER_EGG_DEFAULT_DURATION}s` }} />}
|
||||
<Header />
|
||||
<div className='wrapper'>
|
||||
{data.isWeekend ? <h4>Užívejte víkend :)</h4> : <>
|
||||
{isTodayWeekend ? <h4>Užívejte víkend :)</h4> : <>
|
||||
<Alert variant={'primary'}>
|
||||
<img alt="" src='hat.png' style={{ position: "absolute", width: "70px", rotate: "-45deg", left: -40, top: -58 }} />
|
||||
<img alt="" src='snowman.png' style={{ position: "absolute", height: "110px", right: 10, top: 5 }} />
|
||||
{/* <img alt="" src='hat.png' style={{ position: "absolute", width: "70px", rotate: "-45deg", left: -40, top: -58 }} />
|
||||
<img alt="" src='snowman.png' style={{ position: "absolute", height: "110px", right: 10, top: 5 }} /> */}
|
||||
Poslední změny:
|
||||
<ul>
|
||||
<li>Oprava parsování Sladovnické</li>
|
||||
<li>Oprava parsování cen TechTower</li>
|
||||
<li>Zimní atmosféra</li>
|
||||
<li>Zobrazení alergenu při najetí myší a proklik na seznam alergenů</li>
|
||||
<li>Přesun přenačtení menu do samostatného dialogu</li>
|
||||
<li>Podzimní atmosféra</li>
|
||||
</ul>
|
||||
</Alert>
|
||||
{dayIndex != null &&
|
||||
@@ -464,7 +470,7 @@ function App() {
|
||||
<span title='Předchozí den'>
|
||||
<FontAwesomeIcon icon={faChevronLeft} style={{ cursor: "pointer", visibility: dayIndex > 0 ? "initial" : "hidden" }} onClick={() => handleDayChange(dayIndex - 1)} />
|
||||
</span>
|
||||
<h1 className='title' style={{ color: dayIndex === data.todayDayIndex ? 'black' : 'gray' }}>{data.date}</h1>
|
||||
<h1 className='title' style={{ color: dayIndex === todayDayIndex ? 'black' : 'gray' }}>{getHumanDate(new Date(data.date))}</h1>
|
||||
<span title="Následující den">
|
||||
<FontAwesomeIcon icon={faChevronRight} style={{ cursor: "pointer", visibility: dayIndex < 4 ? "initial" : "hidden" }} onClick={() => handleDayChange(dayIndex + 1)} />
|
||||
</span>
|
||||
@@ -480,7 +486,7 @@ function App() {
|
||||
<div className='content-wrapper'>
|
||||
<div className='content'>
|
||||
{canChangeChoice && <>
|
||||
<p>{`Jak to ${dayIndex == null || dayIndex === data.todayDayIndex ? 'dnes' : 'tento den'} vidíš s obědem?`}</p>
|
||||
<p>{`Jak to ${dayIndex == null || dayIndex === todayDayIndex ? 'dnes' : 'tento den'} vidíš s obědem?`}</p>
|
||||
<Form.Select ref={choiceRef} onChange={doAddChoice}>
|
||||
<option></option>
|
||||
{Object.entries(LunchChoice)
|
||||
@@ -589,7 +595,7 @@ function App() {
|
||||
: <div className='mt-5'><i>Zatím nikdo nehlasoval...</i></div>
|
||||
}
|
||||
</div>
|
||||
{dayIndex === data.todayDayIndex &&
|
||||
{dayIndex === todayDayIndex &&
|
||||
<div className='mt-5'>
|
||||
{!data.pizzaDay &&
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
@@ -711,10 +717,10 @@ function App() {
|
||||
</div>
|
||||
</> || "Jejda, něco se nám nepovedlo :("}
|
||||
</div>
|
||||
{/* <FallingLeaves
|
||||
<FallingLeaves
|
||||
numLeaves={LEAF_PRESETS.NORMAL}
|
||||
leafVariants={LEAF_COLOR_THEMES.AUTUMN}
|
||||
/> */}
|
||||
/>
|
||||
<Footer />
|
||||
<NoteModal isOpen={noteModalOpen} onClose={() => setNoteModalOpen(false)} onSave={saveNote} />
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Routes, Route } from "react-router-dom";
|
||||
import { ProvideSettings } from "./context/settings";
|
||||
// import Snowfall from "react-snowfall";
|
||||
import { SnowOverlay } from 'react-snow-overlay';
|
||||
import { ToastContainer } from "react-toastify";
|
||||
import { SocketContext, socket } from "./context/socket";
|
||||
import StatsPage from "./pages/StatsPage";
|
||||
@@ -23,7 +22,6 @@ export default function AppRoutes() {
|
||||
width: '100vw',
|
||||
height: '100vh'
|
||||
}} /> */}
|
||||
<SnowOverlay color={'rgba(240, 240, 240, 0.9)'} disabledOnSingleCpuDevices={true} />
|
||||
<App />
|
||||
</>
|
||||
<ToastContainer />
|
||||
|
||||
@@ -73,6 +73,12 @@ export const getDayOfWeekIndex = (date: Date) => {
|
||||
return (((date.getDay() - 1) % 7) + 7) % 7;
|
||||
}
|
||||
|
||||
/** Vrátí true, pokud je předané datum o víkendu. */
|
||||
export function getIsWeekend(date: Date) {
|
||||
const index = getDayOfWeekIndex(date);
|
||||
return index == 5 || index == 6;
|
||||
}
|
||||
|
||||
/** Vrátí první pracovní den v týdnu předaného data. */
|
||||
export function getFirstWorkDayOfWeek(date: Date) {
|
||||
const firstDay = new Date(date.getTime());
|
||||
|
||||
@@ -1706,11 +1706,6 @@ react-select-search@^4.1.6:
|
||||
resolved "https://registry.yarnpkg.com/react-select-search/-/react-select-search-4.1.8.tgz#435bdd7893685d45332813ad65b000e0dafcfbac"
|
||||
integrity sha512-mzHhYzpaAJ3iYDjayydfb+grvvP59VWlLUWLqP26Trvm4xj2rzLnksopWZdkwWORB3dhAneqmXos3RWOMjFOxQ==
|
||||
|
||||
react-snow-overlay@^1.0.14:
|
||||
version "1.0.14"
|
||||
resolved "https://registry.yarnpkg.com/react-snow-overlay/-/react-snow-overlay-1.0.14.tgz#1080ab24018bc9f86e7ae0e341b7d1b94ec80cc8"
|
||||
integrity sha512-i178E9PVcMobSYxGiV+L2SMKcVNKytOwTG6Sz3dNw53Eq3Q0NC8BbVL4ewXXd/QCGX5YBVpv8R5+bSBxpUHeRw==
|
||||
|
||||
react-snowfall@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-snowfall/-/react-snowfall-2.3.0.tgz#3c7af4fa8c3786d3d3b7ccdc36ddb73108d23b07"
|
||||
|
||||
@@ -100,7 +100,7 @@ export const getMenuSladovnicka = async (firstDayOfWeek: Date, mock: boolean = f
|
||||
const html = await getHtml(SLADOVNICKA_URL);
|
||||
const $ = load(html);
|
||||
|
||||
// Zjistíme, které dny jsou k dispozici z tab elementů
|
||||
// Nejdříve zjistíme, které dny jsou k dispozici z tab elementů
|
||||
const tabElements = $('#daily-menu-tab-list').children('button[id^="daily-menu-tab-"]');
|
||||
const availableDays: { [dayIndex: number]: number } = {}; // mapování dayIndex -> contentIndex
|
||||
|
||||
@@ -112,7 +112,7 @@ export const getMenuSladovnicka = async (firstDayOfWeek: Date, mock: boolean = f
|
||||
}
|
||||
});
|
||||
|
||||
const menuContentElements = $('#daily-menu-content-list').children('.daily-menu-content__content').not('.daily-menu-content__content--static');
|
||||
const menuContentElements = $('#daily-menu-content-list').children('[id^="daily-menu-content-"]');
|
||||
|
||||
const result: Food[][] = [];
|
||||
|
||||
@@ -130,32 +130,59 @@ export const getMenuSladovnicka = async (firstDayOfWeek: Date, mock: boolean = f
|
||||
continue; // Přeskočíme, pokud content element neexistuje
|
||||
}
|
||||
|
||||
const contentElement = $(menuContentElements[contentIndexNum]);
|
||||
const itemElement = contentElement.find('.daily-menu-content__item');
|
||||
const table = itemElement.find('table.daily-menu-content__table tbody');
|
||||
const rows = table.children('tr');
|
||||
const dayChildren = $(menuContentElements[contentIndexNum]).children();
|
||||
|
||||
// Ověříme, že má element očekávanou strukturu
|
||||
if (dayChildren.length < 2) {
|
||||
console.warn(`Neočekávaný počet children v menu Sladovnické pro den ${dayIndexNum}: ${dayChildren.length}, očekávány alespoň 2 (polévka a hlavní jídlo)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parsování polévky
|
||||
const soupElement = dayChildren.get(0);
|
||||
const soupTable = $(soupElement).find('table tbody tr');
|
||||
const soupCells = soupTable.children('td');
|
||||
if (soupCells.length !== 3) {
|
||||
console.warn(`Neočekávaný počet buněk v tabulce polévky pro den ${dayIndexNum}: ${soupCells.length}, ale očekávány byly 3`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const soupAmount = sanitizeText($(soupCells.get(0)).text());
|
||||
const soupNameRaw = sanitizeText($(soupCells.get(1)).text());
|
||||
const soupPrice = sanitizeText($(soupCells.get(2)).text().replace(' ', '\xA0'));
|
||||
const soupParsed = parseAllergens(soupNameRaw);
|
||||
|
||||
// Parsování hlavních jídel
|
||||
const mainCourseElement = dayChildren.get(1);
|
||||
const mainCourseTable = $(mainCourseElement).find('table tbody');
|
||||
const mainCourseRows = mainCourseTable.children('tr');
|
||||
|
||||
const currentDayFood: Food[] = [];
|
||||
|
||||
// Projdeme všechny řádky - první je polévka, zbytek jsou hlavní jídla
|
||||
rows.each((i, row) => {
|
||||
// Přidáme polévku do seznamu jídel
|
||||
currentDayFood.push({
|
||||
amount: soupAmount,
|
||||
name: soupParsed.cleanName,
|
||||
price: soupPrice,
|
||||
isSoup: true,
|
||||
allergens: soupParsed.allergens.length > 0 ? soupParsed.allergens : undefined,
|
||||
});
|
||||
|
||||
// Projdeme všechny řádky hlavních jídel
|
||||
mainCourseRows.each((i, row) => {
|
||||
const cells = $(row).children('td');
|
||||
if (cells.length !== 3) {
|
||||
return; // Přeskočíme řádky s nesprávnou strukturou
|
||||
}
|
||||
|
||||
const amount = sanitizeText($(cells.get(0)).text());
|
||||
const nameRaw = sanitizeText($(cells.get(1)).text());
|
||||
const price = sanitizeText($(cells.get(2)).text().replace(' ', '\xA0'));
|
||||
const parsed = parseAllergens(nameRaw);
|
||||
|
||||
// Přeskočíme prázdné řádky
|
||||
// Přeskočíme prázdné řádky (první řádek může být prázdný)
|
||||
if (parsed.cleanName.trim().length > 0) {
|
||||
currentDayFood.push({
|
||||
amount,
|
||||
name: parsed.cleanName,
|
||||
price,
|
||||
isSoup: i === 0, // První řádek je polévka
|
||||
isSoup: false,
|
||||
allergens: parsed.allergens.length > 0 ? parsed.allergens : undefined,
|
||||
});
|
||||
}
|
||||
@@ -324,11 +351,6 @@ export const getMenuTechTower = async (firstDayOfWeek: Date, mock: boolean = fal
|
||||
const split = [tmp.slice(0, -2).join(' ')].concat(tmp.slice(-2));
|
||||
price = `${split.slice(1)[0]}\xA0Kč`
|
||||
nameRaw = split[0].replace('•', '');
|
||||
} else if (text.toLowerCase().endsWith(',-')) {
|
||||
const tmp = text.replace('\xA0', ' ').split(' ');
|
||||
const split = [tmp.slice(0, -1).join(' ')].concat(tmp.slice(-1));
|
||||
price = `${split.slice(1)[0].replace(',-', '')}\xA0Kč`
|
||||
nameRaw = split[0].replace('•', '');
|
||||
}
|
||||
if (nameRaw.endsWith('–')) {
|
||||
nameRaw = nameRaw.slice(0, -1).trim();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { InsufficientPermissions, formatDate, getDayOfWeekIndex, getFirstWorkDayOfWeek, getHumanDate, getIsWeekend, getWeekNumber } from "./utils";
|
||||
import { InsufficientPermissions, formatDate, getDayOfWeekIndex, getFirstWorkDayOfWeek, getIsWeekend, getWeekNumber } from "./utils";
|
||||
import getStorage from "./storage";
|
||||
import { getMenuSladovnicka, getMenuTechTower, getMenuZastavkaUmichala, getMenuSenkSerikova } from "./restaurants";
|
||||
import { getTodayMock } from "./mock";
|
||||
@@ -31,10 +31,7 @@ export const getDateForWeekIndex = (index: number) => {
|
||||
function getEmptyData(date?: Date): ClientData {
|
||||
const usedDate = date || getToday();
|
||||
return {
|
||||
todayDayIndex: getDayOfWeekIndex(getToday()),
|
||||
date: getHumanDate(usedDate),
|
||||
isWeekend: getIsWeekend(usedDate),
|
||||
dayIndex: getDayOfWeekIndex(usedDate),
|
||||
date: usedDate.toISOString().split('T')[0],
|
||||
choices: {},
|
||||
};
|
||||
}
|
||||
@@ -486,9 +483,5 @@ export async function updateDepartureTime(login: string, time?: string, date?: D
|
||||
export async function getClientData(date?: Date): Promise<ClientData> {
|
||||
const targetDate = date ?? getToday();
|
||||
const dateString = formatDate(targetDate);
|
||||
const clientData = await storage.getData<ClientData>(dateString) || getEmptyData(date);
|
||||
return {
|
||||
...clientData,
|
||||
todayDayIndex: getDayOfWeekIndex(getToday()),
|
||||
}
|
||||
return await storage.getData<ClientData>(dateString) || getEmptyData(date);
|
||||
}
|
||||
@@ -21,23 +21,13 @@ ClientData:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- todayDayIndex
|
||||
- date
|
||||
- isWeekend
|
||||
- choices
|
||||
properties:
|
||||
todayDayIndex:
|
||||
description: Index dnešního dne v týdnu
|
||||
$ref: "#/DayIndex"
|
||||
date:
|
||||
description: Human-readable datum dne
|
||||
description: Datum konkrétního dne
|
||||
type: string
|
||||
isWeekend:
|
||||
description: Příznak, zda je tento den víkend
|
||||
type: boolean
|
||||
dayIndex:
|
||||
description: Index dne v týdnu, ke kterému se vztahují tato data
|
||||
$ref: "#/DayIndex"
|
||||
format: date
|
||||
choices:
|
||||
$ref: "#/LunchChoices"
|
||||
menus:
|
||||
|
||||
Reference in New Issue
Block a user