Oddělení přenačtení menu do vlastní komponenty
All checks were successful
ci/woodpecker/push/workflow Pipeline was successful
All checks were successful
ci/woodpecker/push/workflow Pipeline was successful
This commit is contained in:
parent
523bbbfb0f
commit
331a890cc5
@ -5,6 +5,7 @@ import SettingsModal from "./modals/SettingsModal";
|
|||||||
import { useSettings } from "../context/settings";
|
import { useSettings } from "../context/settings";
|
||||||
import FeaturesVotingModal from "./modals/FeaturesVotingModal";
|
import FeaturesVotingModal from "./modals/FeaturesVotingModal";
|
||||||
import PizzaCalculatorModal from "./modals/PizzaCalculatorModal";
|
import PizzaCalculatorModal from "./modals/PizzaCalculatorModal";
|
||||||
|
import RefreshMenuModal from "./modals/RefreshMenuModal";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import { STATS_URL } from "../AppRoutes";
|
import { STATS_URL } from "../AppRoutes";
|
||||||
import { FeatureRequest, getVotes, updateVote } from "../../../types";
|
import { FeatureRequest, getVotes, updateVote } from "../../../types";
|
||||||
@ -16,6 +17,7 @@ export default function Header() {
|
|||||||
const [settingsModalOpen, setSettingsModalOpen] = useState<boolean>(false);
|
const [settingsModalOpen, setSettingsModalOpen] = useState<boolean>(false);
|
||||||
const [votingModalOpen, setVotingModalOpen] = useState<boolean>(false);
|
const [votingModalOpen, setVotingModalOpen] = useState<boolean>(false);
|
||||||
const [pizzaModalOpen, setPizzaModalOpen] = useState<boolean>(false);
|
const [pizzaModalOpen, setPizzaModalOpen] = useState<boolean>(false);
|
||||||
|
const [refreshMenuModalOpen, setRefreshMenuModalOpen] = useState<boolean>(false);
|
||||||
const [featureVotes, setFeatureVotes] = useState<FeatureRequest[] | undefined>([]);
|
const [featureVotes, setFeatureVotes] = useState<FeatureRequest[] | undefined>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -38,6 +40,10 @@ export default function Header() {
|
|||||||
setPizzaModalOpen(false);
|
setPizzaModalOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const closeRefreshMenuModal = () => {
|
||||||
|
setRefreshMenuModalOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
const isValidInteger = (str: string) => {
|
const isValidInteger = (str: string) => {
|
||||||
str = str.trim();
|
str = str.trim();
|
||||||
if (!str) {
|
if (!str) {
|
||||||
@ -114,6 +120,7 @@ export default function Header() {
|
|||||||
<Nav className="nav">
|
<Nav className="nav">
|
||||||
<NavDropdown align="end" title={auth?.login} id="basic-nav-dropdown">
|
<NavDropdown align="end" title={auth?.login} id="basic-nav-dropdown">
|
||||||
<NavDropdown.Item onClick={() => setSettingsModalOpen(true)}>Nastavení</NavDropdown.Item>
|
<NavDropdown.Item onClick={() => setSettingsModalOpen(true)}>Nastavení</NavDropdown.Item>
|
||||||
|
<NavDropdown.Item onClick={() => setRefreshMenuModalOpen(true)}>Přenačtení menu</NavDropdown.Item>
|
||||||
<NavDropdown.Item onClick={() => setVotingModalOpen(true)}>Hlasovat o nových funkcích</NavDropdown.Item>
|
<NavDropdown.Item onClick={() => setVotingModalOpen(true)}>Hlasovat o nových funkcích</NavDropdown.Item>
|
||||||
<NavDropdown.Item onClick={() => setPizzaModalOpen(true)}>Pizza kalkulačka</NavDropdown.Item>
|
<NavDropdown.Item onClick={() => setPizzaModalOpen(true)}>Pizza kalkulačka</NavDropdown.Item>
|
||||||
<NavDropdown.Item onClick={() => navigate(STATS_URL)}>Statistiky</NavDropdown.Item>
|
<NavDropdown.Item onClick={() => navigate(STATS_URL)}>Statistiky</NavDropdown.Item>
|
||||||
@ -123,6 +130,7 @@ export default function Header() {
|
|||||||
</Nav>
|
</Nav>
|
||||||
</Navbar.Collapse>
|
</Navbar.Collapse>
|
||||||
<SettingsModal isOpen={settingsModalOpen} onClose={closeSettingsModal} onSave={saveSettings} />
|
<SettingsModal isOpen={settingsModalOpen} onClose={closeSettingsModal} onSave={saveSettings} />
|
||||||
|
<RefreshMenuModal isOpen={refreshMenuModalOpen} onClose={closeRefreshMenuModal} />
|
||||||
<FeaturesVotingModal isOpen={votingModalOpen} onClose={closeVotingModal} onChange={saveFeatureVote} initialValues={featureVotes} />
|
<FeaturesVotingModal isOpen={votingModalOpen} onClose={closeVotingModal} onChange={saveFeatureVote} initialValues={featureVotes} />
|
||||||
<PizzaCalculatorModal isOpen={pizzaModalOpen} onClose={closePizzaModal} />
|
<PizzaCalculatorModal isOpen={pizzaModalOpen} onClose={closePizzaModal} />
|
||||||
</Navbar>
|
</Navbar>
|
||||||
|
|||||||
105
client/src/components/modals/RefreshMenuModal.tsx
Normal file
105
client/src/components/modals/RefreshMenuModal.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { useRef, useState } from "react";
|
||||||
|
import { Modal, Button, Alert } from "react-bootstrap";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Modální dialog pro přenačtení menu z restaurací. */
|
||||||
|
export default function RefreshMenuModal({ isOpen, onClose }: Readonly<Props>) {
|
||||||
|
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) {
|
||||||
|
console.error('Error refreshing menu:', error);
|
||||||
|
setRefreshMessage({ type: 'error', text: 'Chyba při obnovování jídelníčku.' });
|
||||||
|
} finally {
|
||||||
|
setRefreshLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setRefreshMessage(null);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal show={isOpen} onHide={handleClose} size="lg">
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title><h2>Přenačtení menu</h2></Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<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>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<Button variant="secondary" onClick={handleClose}>
|
||||||
|
Zavřít
|
||||||
|
</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { useRef, useState } from "react";
|
import { useRef } from "react";
|
||||||
import { Modal, Button, Alert } from "react-bootstrap"
|
import { Modal, Button } from "react-bootstrap"
|
||||||
import { useSettings } from "../../context/settings";
|
import { useSettings } from "../../context/settings";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -15,41 +15,6 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
|
|||||||
const nameRef = useRef<HTMLInputElement>(null);
|
const nameRef = useRef<HTMLInputElement>(null);
|
||||||
const hideSoupsRef = 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">
|
return <Modal show={isOpen} onHide={onClose} size="lg">
|
||||||
<Modal.Header closeButton>
|
<Modal.Header closeButton>
|
||||||
<Modal.Title><h2>Nastavení</h2></Modal.Title>
|
<Modal.Title><h2>Nastavení</h2></Modal.Title>
|
||||||
@ -59,48 +24,6 @@ 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" }}>
|
<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
|
<input ref={hideSoupsRef} type="checkbox" defaultChecked={settings?.hideSoups} /> Skrýt polévky
|
||||||
</span>
|
</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 />
|
<hr />
|
||||||
<h4>Bankovní účet</h4>
|
<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>
|
<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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user