feat: spin the wheel, objednavani scrape menu, historie cen
CI / Generate TypeScript types (push) Successful in 11s
CI / Server unit tests (push) Successful in 25s
CI / Build server (push) Successful in 29s
CI / Build client (push) Successful in 39s
CI / Playwright E2E tests (push) Successful in 1m52s
CI / Build and push Docker image (push) Successful in 51s
CI / Notify (push) Successful in 2s
CI / Generate TypeScript types (push) Successful in 11s
CI / Server unit tests (push) Successful in 25s
CI / Build server (push) Successful in 29s
CI / Build client (push) Successful in 39s
CI / Playwright E2E tests (push) Successful in 1m52s
CI / Build and push Docker image (push) Successful in 51s
CI / Notify (push) Successful in 2s
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { useMemo } from "react";
|
||||
import { OverlayTrigger, Tooltip } from "react-bootstrap";
|
||||
import SelectSearch, { SelectedOptionValue, SelectSearchOption } from "react-select-search";
|
||||
import { StoreMenuDish } from "../../../types";
|
||||
import { matchNoteToMenu } from "../utils/menuMatch";
|
||||
import "./MenuHintCell.scss";
|
||||
|
||||
type Props = {
|
||||
/** Celá nabídka podniku (stažená z Bolt Food/Wolt), undefined = načítá se / není */
|
||||
menu?: StoreMenuDish[],
|
||||
note?: string,
|
||||
/** Zadaná částka člena v haléřích */
|
||||
amount?: number,
|
||||
canEdit: boolean,
|
||||
onPick: (dish: StoreMenuDish) => void,
|
||||
onUsePrice: (priceKc: number) => void,
|
||||
};
|
||||
|
||||
const formatDish = (d: StoreMenuDish) => d.price != null ? `${d.name} (${d.price} Kč)` : d.name;
|
||||
|
||||
/**
|
||||
* Buňka „Z nabídky" ve skupinové objednávce: výběr položky ze stažené nabídky podniku
|
||||
* (doplní poznámku i částku) a kontrola ceny rozpoznané z poznámky proti zadané částce.
|
||||
*/
|
||||
export default function MenuHintCell({ menu, note, amount, canEdit, onPick, onUsePrice }: Readonly<Props>) {
|
||||
const options = useMemo<SelectSearchOption[]>(() => {
|
||||
if (!menu?.length) return [];
|
||||
const groups = new Map<string, SelectSearchOption[]>();
|
||||
menu.forEach((d, index) => {
|
||||
const category = d.category || 'Ostatní';
|
||||
if (!groups.has(category)) groups.set(category, []);
|
||||
groups.get(category)!.push({ name: formatDish(d), value: String(index) });
|
||||
});
|
||||
return [...groups.entries()].map(([name, items]) => ({ name, type: 'group', items }));
|
||||
}, [menu]);
|
||||
|
||||
const match = useMemo(() => menu ? matchNoteToMenu(note, menu) : undefined, [menu, note]);
|
||||
|
||||
if (!menu?.length) {
|
||||
return <small className="text-muted">—</small>;
|
||||
}
|
||||
|
||||
const menuPrice = match?.total;
|
||||
const differs = menuPrice != null && amount != null && Math.round(menuPrice * 100) !== amount;
|
||||
const unmatched = match?.parts.filter(p => !p.dish).map(p => p.text) ?? [];
|
||||
|
||||
return (
|
||||
<div className="menu-hint-cell">
|
||||
{canEdit && (
|
||||
<SelectSearch
|
||||
search
|
||||
options={options}
|
||||
placeholder="Přidat z nabídky…"
|
||||
value=""
|
||||
onBlur={() => { }}
|
||||
onFocus={() => { }}
|
||||
onChange={(value: SelectedOptionValue | SelectedOptionValue[]) => {
|
||||
const dish = menu[Number(value)];
|
||||
if (dish) onPick(dish);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{match && (
|
||||
<OverlayTrigger
|
||||
placement="top"
|
||||
overlay={
|
||||
<Tooltip>
|
||||
{match.parts.map((p, i) => (
|
||||
<div key={i}>
|
||||
{p.dish
|
||||
? `${p.quantity > 1 ? `${p.quantity}× ` : ''}${formatDish(p.dish)}`
|
||||
: `„${p.text}" — v nabídce nenalezeno`}
|
||||
</div>
|
||||
))}
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<small className={`menu-hint-price ${differs ? 'text-warning' : 'text-muted'}`}>
|
||||
{menuPrice != null
|
||||
? <>Dle nabídky: <strong>{menuPrice} Kč</strong></>
|
||||
: <>Nalezeno {match.parts.length - unmatched.length}/{match.parts.length} položek</>}
|
||||
{differs && canEdit && (
|
||||
<button type="button" className="menu-hint-use" onClick={() => onUsePrice(menuPrice!)}
|
||||
title="Nastavit částku podle ceny v nabídce">
|
||||
použít
|
||||
</button>
|
||||
)}
|
||||
</small>
|
||||
</OverlayTrigger>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user