Migrace na OpenAPI - TypeScript typy
This commit is contained in:
@@ -8,12 +8,11 @@ import { Alert, Button, Col, Form, Row, Table } from 'react-bootstrap';
|
||||
import Header from './components/Header';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import PizzaOrderList from './components/PizzaOrderList';
|
||||
import SelectSearch, {SelectedOptionValue, SelectSearchOption} from 'react-select-search';
|
||||
import SelectSearch, { SelectedOptionValue, SelectSearchOption } from 'react-select-search';
|
||||
import 'react-select-search/style.css';
|
||||
import './App.scss';
|
||||
import { faCircleCheck, faNoteSticky, faTrashCan } from '@fortawesome/free-regular-svg-icons';
|
||||
import { useSettings } from './context/settings';
|
||||
import { ClientData, Restaurants, Food, Order, Locations, PizzaOrder, PizzaDayState, FoodChoices, DayMenu, DepartureTime, LocationKey } from '../../types';
|
||||
import Footer from './components/Footer';
|
||||
import { faChainBroken, faChevronLeft, faChevronRight, faGear, faSatelliteDish, faSearch } from '@fortawesome/free-solid-svg-icons';
|
||||
import Loader from './components/Loader';
|
||||
@@ -25,6 +24,7 @@ import { useEasterEgg } from './context/eggs';
|
||||
import { getImage } from './api/EasterEggApi';
|
||||
import { Link } from 'react-router';
|
||||
import { STATS_URL } from './AppRoutes';
|
||||
import { ClientData, Food, PizzaOrder, DepartureTime, PizzaDayState, Restaurant, RestaurantDayMenu, RestaurantDayMenuMap, LunchChoice, LunchChoices, UserLunchChoice, PizzaVariant } from '../../types';
|
||||
|
||||
const EVENT_CONNECT = "connect"
|
||||
|
||||
@@ -44,8 +44,8 @@ function App() {
|
||||
const [easterEgg, easterEggLoading] = useEasterEgg(auth);
|
||||
const [isConnected, setIsConnected] = useState<boolean>(false);
|
||||
const [data, setData] = useState<ClientData>();
|
||||
const [food, setFood] = useState<{ [key in Restaurants]?: DayMenu }>();
|
||||
const [myOrder, setMyOrder] = useState<Order>();
|
||||
const [food, setFood] = useState<RestaurantDayMenuMap>();
|
||||
const [myOrder, setMyOrder] = useState<PizzaOrder>();
|
||||
const [foodChoiceList, setFoodChoiceList] = useState<Food[]>();
|
||||
const [closed, setClosed] = useState<boolean>(false);
|
||||
const socket = useContext(SocketContext);
|
||||
@@ -68,11 +68,13 @@ function App() {
|
||||
if (!auth || !auth.login) {
|
||||
return
|
||||
}
|
||||
getData().then((data: ClientData) => {
|
||||
setData(data);
|
||||
setDayIndex(data.weekIndex);
|
||||
dayIndexRef.current = data.weekIndex;
|
||||
setFood(data.menus);
|
||||
getData().then(({ data }) => {
|
||||
if (data) {
|
||||
setData(data);
|
||||
setDayIndex(data.weekIndex);
|
||||
dayIndexRef.current = data.weekIndex;
|
||||
setFood(data.menus);
|
||||
}
|
||||
}).catch(e => {
|
||||
setFailure(true);
|
||||
})
|
||||
@@ -104,7 +106,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.weekIndex === dayIndexRef.current) {
|
||||
if (dayIndexRef.current == null || newData.dayIndex === dayIndexRef.current) {
|
||||
setData(newData);
|
||||
}
|
||||
});
|
||||
@@ -142,10 +144,10 @@ function App() {
|
||||
|
||||
useEffect(() => {
|
||||
if (choiceRef?.current?.value && choiceRef.current.value !== "") {
|
||||
const locationKey = choiceRef.current.value as LocationKey;
|
||||
const restaurantKey = Object.keys(Restaurants).indexOf(locationKey);
|
||||
const locationKey = choiceRef.current.value as keyof typeof LunchChoice;
|
||||
const restaurantKey = Object.keys(Restaurant).indexOf(locationKey);
|
||||
if (restaurantKey > -1 && food) {
|
||||
const restaurant = Object.values(Restaurants)[restaurantKey];
|
||||
const restaurant = Object.keys(Restaurant)[restaurantKey] as keyof typeof Restaurant;
|
||||
setFoodChoiceList(food[restaurant]?.food);
|
||||
setClosed(food[restaurant]?.closed ?? false);
|
||||
} else {
|
||||
@@ -191,17 +193,16 @@ function App() {
|
||||
}
|
||||
}, [auth?.login, easterEgg?.duration, easterEgg?.url, eggImage]);
|
||||
|
||||
const doAddClickFoodChoice = async (location: Locations, foodIndex?: number) => {
|
||||
const doAddClickFoodChoice = async (location: keyof typeof LunchChoice, foodIndex?: number) => {
|
||||
if (document.getSelection()?.type !== 'Range') { // pouze pokud se nejedná o výběr textu
|
||||
const locationKey = Object.keys(Locations).find(key => Locations[key as keyof typeof Locations] === location) as LocationKey;
|
||||
if (auth?.login) {
|
||||
await errorHandler(() => addChoice(locationKey, foodIndex, dayIndex));
|
||||
await errorHandler(() => addChoice(location, foodIndex, dayIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const doAddChoice = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const locationKey = event.target.value as LocationKey;
|
||||
const locationKey = event.target.value as keyof typeof LunchChoice;
|
||||
if (auth?.login) {
|
||||
await errorHandler(() => addChoice(locationKey, undefined, dayIndex));
|
||||
if (foodChoiceRef.current?.value) {
|
||||
@@ -218,14 +219,14 @@ function App() {
|
||||
|
||||
const doAddFoodChoice = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
if (event.target.value && foodChoiceList?.length && choiceRef.current?.value) {
|
||||
const locationKey = choiceRef.current.value as LocationKey;
|
||||
const locationKey = choiceRef.current.value as keyof typeof LunchChoice;
|
||||
if (auth?.login) {
|
||||
await errorHandler(() => addChoice(locationKey, Number(event.target.value), dayIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const doRemoveChoices = async (locationKey: LocationKey) => {
|
||||
const doRemoveChoices = async (locationKey: keyof typeof LunchChoice) => {
|
||||
if (auth?.login) {
|
||||
await errorHandler(() => removeChoices(locationKey, dayIndex));
|
||||
// Vyresetujeme výběr, aby bylo jasné pro který případně vybíráme jídlo
|
||||
@@ -238,7 +239,7 @@ function App() {
|
||||
}
|
||||
}
|
||||
|
||||
const doRemoveFoodChoice = async (locationKey: LocationKey, foodIndex: number) => {
|
||||
const doRemoveFoodChoice = async (locationKey: keyof typeof LunchChoice, foodIndex: number) => {
|
||||
if (auth?.login) {
|
||||
await errorHandler(() => removeChoice(locationKey, foodIndex, dayIndex));
|
||||
if (choiceRef?.current?.value) {
|
||||
@@ -286,7 +287,7 @@ function App() {
|
||||
}
|
||||
}
|
||||
|
||||
const handlePizzaDelete = async (pizzaOrder: PizzaOrder) => {
|
||||
const handlePizzaDelete = async (pizzaOrder: PizzaVariant) => {
|
||||
await removePizza(pizzaOrder);
|
||||
}
|
||||
|
||||
@@ -342,11 +343,11 @@ function App() {
|
||||
}
|
||||
}
|
||||
|
||||
const renderFoodTable = (location: Locations, menu: DayMenu) => {
|
||||
const renderFoodTable = (location: keyof typeof Restaurant, menu: RestaurantDayMenu) => {
|
||||
let content;
|
||||
if (menu?.closed) {
|
||||
content = <h3>Zavřeno</h3>
|
||||
} else if (menu?.food?.length > 0) {
|
||||
} else if (menu?.food?.length && menu.food.length > 0) {
|
||||
const hideSoups = settings?.hideSoups;
|
||||
content = <Table striped bordered hover>
|
||||
<tbody style={{ cursor: 'pointer' }}>
|
||||
@@ -399,7 +400,7 @@ function App() {
|
||||
}
|
||||
|
||||
const noOrders = data?.pizzaDay?.orders?.length === 0;
|
||||
const canChangeChoice = dayIndex == null || data.todayWeekIndex == null || dayIndex >= data.todayWeekIndex;
|
||||
const canChangeChoice = dayIndex == null || data.todayDayIndex == null || dayIndex >= data.todayDayIndex;
|
||||
|
||||
const { path, url, startOffset, endOffset, duration, ...style } = easterEgg || {};
|
||||
|
||||
@@ -421,29 +422,28 @@ function App() {
|
||||
{dayIndex != null &&
|
||||
<div className='day-navigator'>
|
||||
<FontAwesomeIcon title="Předchozí den" icon={faChevronLeft} style={{ cursor: "pointer", visibility: dayIndex > 0 ? "initial" : "hidden" }} onClick={() => handleDayChange(dayIndex - 1)} />
|
||||
<h1 className='title' style={{ color: dayIndex === data.todayWeekIndex ? 'black' : 'gray' }}>{data.date}</h1>
|
||||
<h1 className='title' style={{ color: dayIndex === data.todayDayIndex ? 'black' : 'gray' }}>{data.date}</h1>
|
||||
<FontAwesomeIcon title="Následující den" icon={faChevronRight} style={{ cursor: "pointer", visibility: dayIndex < 4 ? "initial" : "hidden" }} onClick={() => handleDayChange(dayIndex + 1)} />
|
||||
</div>
|
||||
}
|
||||
<Row className='food-tables'>
|
||||
{food[Restaurants.SLADOVNICKA] && renderFoodTable(Locations.SLADOVNICKA, food[Restaurants.SLADOVNICKA])}
|
||||
{/* {food[Restaurants.UMOTLIKU] && renderFoodTable(food[Restaurants.UMOTLIKU])} */}
|
||||
{food[Restaurants.TECHTOWER] && renderFoodTable(Locations.TECHTOWER, food[Restaurants.TECHTOWER])}
|
||||
{food[Restaurants.ZASTAVKAUMICHALA] && renderFoodTable(Locations.ZASTAVKAUMICHALA, food[Restaurants.ZASTAVKAUMICHALA])}
|
||||
{food[Restaurants.SENKSERIKOVA] && renderFoodTable(Locations.SENKSERIKOVA, food[Restaurants.SENKSERIKOVA])}
|
||||
{/* TODO zjednodušit, stačí iterovat klíče typu Restaurant */}
|
||||
{food['SLADOVNICKA'] && renderFoodTable('SLADOVNICKA', food['SLADOVNICKA'])}
|
||||
{/* {food['UMOTLIKU'] && renderFoodTable('UMOTLIKU', food['UMOTLIKU'])} */}
|
||||
{food['TECHTOWER'] && renderFoodTable('TECHTOWER', food['TECHTOWER'])}
|
||||
{food['ZASTAVKAUMICHALA'] && renderFoodTable('ZASTAVKAUMICHALA', food['ZASTAVKAUMICHALA'])}
|
||||
{food['SENKSERIKOVA'] && renderFoodTable('SENKSERIKOVA', food['SENKSERIKOVA'])}
|
||||
</Row>
|
||||
<div className='content-wrapper'>
|
||||
<div className='content'>
|
||||
{canChangeChoice && <>
|
||||
<p>{`Jak to ${dayIndex == null || dayIndex === data.todayWeekIndex ? 'dnes' : 'tento den'} vidíš s obědem?`}</p>
|
||||
<p>{`Jak to ${dayIndex == null || dayIndex === data.todayDayIndex ? 'dnes' : 'tento den'} vidíš s obědem?`}</p>
|
||||
<Form.Select ref={choiceRef} onChange={doAddChoice}>
|
||||
<option></option>
|
||||
{Object.entries(Locations)
|
||||
{Object.keys(Restaurant)
|
||||
.filter(entry => {
|
||||
const locationKey = entry[0] as LocationKey;
|
||||
const restaurantKey = Object.keys(Restaurants).indexOf(locationKey);
|
||||
const v = Object.values(Restaurants)[restaurantKey];
|
||||
return v == null || !food[v]?.closed;
|
||||
const locationKey = entry as keyof typeof Restaurant;
|
||||
return !food[locationKey]?.closed;
|
||||
})
|
||||
.map(entry => <option key={entry[0]} value={entry[0]}>{entry[1]}</option>)}
|
||||
</Form.Select>
|
||||
@@ -460,8 +460,8 @@ function App() {
|
||||
<Form.Select ref={departureChoiceRef} onChange={handleChangeDepartureTime}>
|
||||
<option></option>
|
||||
{Object.values(DepartureTime)
|
||||
.filter(time => isInTheFuture(time))
|
||||
.map(time => <option key={time} value={time}>{time}</option>)}
|
||||
.filter(time => isInTheFuture(time))
|
||||
.map(time => <option key={time} value={time}>{time}</option>)}
|
||||
</Form.Select>
|
||||
</>}
|
||||
</>}
|
||||
@@ -469,8 +469,8 @@ function App() {
|
||||
<Table bordered className='mt-5'>
|
||||
<tbody>
|
||||
{Object.keys(data.choices).map(key => {
|
||||
const locationKey = key as LocationKey;
|
||||
const locationName = Locations[locationKey];
|
||||
const locationKey = key as keyof typeof LunchChoice;
|
||||
const locationName = LunchChoice[locationKey];
|
||||
const loginObject = data.choices[locationKey];
|
||||
if (!loginObject) {
|
||||
return;
|
||||
@@ -479,17 +479,17 @@ function App() {
|
||||
const locationPickCount = locationLoginList.length
|
||||
return (
|
||||
<tr key={key}>
|
||||
{(locationPickCount?? 0) > 1 ? (
|
||||
{(locationPickCount ?? 0) > 1 ? (
|
||||
<td>{locationName} ({locationPickCount})</td>
|
||||
) : (
|
||||
<td>{locationName}</td>)}
|
||||
<td>{locationName}</td>)}
|
||||
<td className='p-0'>
|
||||
<Table>
|
||||
<tbody>
|
||||
{locationLoginList.map((entry: [string, FoodChoices], index) => {
|
||||
{locationLoginList.map((entry: [string, UserLunchChoice], index) => {
|
||||
const login = entry[0];
|
||||
const userPayload = entry[1];
|
||||
const userChoices = userPayload?.options;
|
||||
const userChoices = userPayload?.selectedFoods;
|
||||
const trusted = userPayload?.trusted || false;
|
||||
return <tr key={index}>
|
||||
<td>
|
||||
@@ -503,20 +503,18 @@ function App() {
|
||||
setNoteModalOpen(true);
|
||||
}} title='Upravit poznámku' className='action-icon' icon={faNoteSticky} />}
|
||||
{login === auth.login && canChangeChoice && <FontAwesomeIcon onClick={() => {
|
||||
doRemoveChoices(key as LocationKey);
|
||||
doRemoveChoices(key as keyof typeof LunchChoice);
|
||||
}} title={`Odstranit volbu ${locationName}, včetně případných zvolených jídel`} className='action-icon' icon={faTrashCan} />}
|
||||
</td>
|
||||
{userChoices?.length && food ? <td>
|
||||
<ul>
|
||||
{userChoices?.map(foodIndex => {
|
||||
// TODO narovnat, tohle je zbytečně složité
|
||||
const restaurantKey = Object.keys(Restaurants).indexOf(key);
|
||||
const restaurant = Object.values(Restaurants)[restaurantKey];
|
||||
const foodName = food[restaurant]?.food[foodIndex].name;
|
||||
const restaurantKey = key as keyof typeof Restaurant;
|
||||
const foodName = food[restaurantKey]?.food?.[foodIndex].name;
|
||||
return <li key={foodIndex}>
|
||||
{foodName}
|
||||
{login === auth.login && canChangeChoice && <FontAwesomeIcon onClick={() => {
|
||||
doRemoveFoodChoice(key as LocationKey, foodIndex);
|
||||
doRemoveFoodChoice(restaurantKey, foodIndex);
|
||||
}} title={`Odstranit ${foodName}`} className='action-icon' icon={faTrashCan} />}
|
||||
</li>
|
||||
})}
|
||||
@@ -536,7 +534,7 @@ function App() {
|
||||
: <div className='mt-5'><i>Zatím nikdo nehlasoval...</i></div>
|
||||
}
|
||||
</div>
|
||||
{dayIndex === data.todayWeekIndex &&
|
||||
{dayIndex === data.todayDayIndex &&
|
||||
<div className='mt-5'>
|
||||
{!data.pizzaDay &&
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
@@ -646,13 +644,13 @@ function App() {
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
<PizzaOrderList state={data.pizzaDay.state} orders={data.pizzaDay.orders} onDelete={handlePizzaDelete} creator={data.pizzaDay.creator} />
|
||||
<PizzaOrderList state={data.pizzaDay.state!} orders={data.pizzaDay.orders!} onDelete={handlePizzaDelete} creator={data.pizzaDay.creator!} />
|
||||
{
|
||||
data.pizzaDay.state === PizzaDayState.DELIVERED && myOrder?.hasQr &&
|
||||
<div className='qr-code'>
|
||||
<h3>QR platba</h3>
|
||||
<img src={getQrUrl(auth.login)} alt='QR kód' />
|
||||
</div>
|
||||
data.pizzaDay.state === PizzaDayState.DELIVERED && myOrder?.hasQr ?
|
||||
<div className='qr-code'>
|
||||
<h3>QR platba</h3>
|
||||
<img src={getQrUrl(auth.login)} alt='QR kód' />
|
||||
</div> : null
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user