Wochenplan: Gerichtsuche statt langer Dropdown-Liste, Beispieldaten zurück
WeekEditor: "Mahlzeit hinzufügen" zeigt jetzt ein Suchfeld mit gefilterter Trefferliste (wie die Live-Suche bei Gerichten) statt einer langen Dropdown-Liste zum Durchscrollen. Ausgewähltes Gericht lässt sich per ×-Button wieder ändern. Neues Skript db/seed-demo.sql (+ db/seed-demo.mjs, "npm run db:seed-demo"): spielt den ursprünglichen Beispieldatensatz ein (4 Essenszeiten, 14 Allergene, 11 Zusatzstoffe, 18 Testgerichte, zwei Beispielwochen) – nur für lokale Entwicklung/Demos, kein Teil der automatischen Migrationen, da frische Deployments weiterhin leer starten sollen.
This commit is contained in:
+72
-22
@@ -35,6 +35,19 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
null
|
||||
);
|
||||
const [selectedDish, setSelectedDish] = useState("");
|
||||
const [dishSearch, setDishSearch] = useState("");
|
||||
|
||||
function startAdding(iso: string, section: string) {
|
||||
setAdding({ iso, section });
|
||||
setSelectedDish("");
|
||||
setDishSearch("");
|
||||
}
|
||||
|
||||
function cancelAdding() {
|
||||
setAdding(null);
|
||||
setSelectedDish("");
|
||||
setDishSearch("");
|
||||
}
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setError(null);
|
||||
@@ -77,8 +90,7 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
: formatMessage(dict.weekEditor.saveError, { message: data.error ?? String(res.status) })
|
||||
);
|
||||
}
|
||||
setAdding(null);
|
||||
setSelectedDish("");
|
||||
cancelAdding();
|
||||
await load();
|
||||
}
|
||||
|
||||
@@ -90,6 +102,11 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
}
|
||||
|
||||
const [hintBefore, hintAfter] = dict.weekEditor.newDishesHint.split("{link}");
|
||||
const selectedDishObj = dishes.find((d) => d.id === selectedDish) ?? null;
|
||||
const query = dishSearch.trim().toLowerCase();
|
||||
const filteredDishes = query
|
||||
? dishes.filter((d) => localized(d, locale).toLowerCase().includes(query))
|
||||
: dishes;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -168,18 +185,57 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
})}
|
||||
{isAdding ? (
|
||||
<div className="mt-2 space-y-1 text-left">
|
||||
<select
|
||||
className="input"
|
||||
value={selectedDish}
|
||||
onChange={(e) => setSelectedDish(e.target.value)}
|
||||
>
|
||||
<option value="">{dict.weekEditor.selectDish}</option>
|
||||
{dishes.map((dish) => (
|
||||
<option key={dish.id} value={dish.id}>
|
||||
{localized(dish, locale)} ({formatPrice(dish.price)})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{selectedDishObj ? (
|
||||
<div className="flex items-center gap-1 text-sm">
|
||||
<span className="mr-auto">
|
||||
{localized(selectedDishObj, locale)} (
|
||||
{formatPrice(selectedDishObj.price)})
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
className="text-gray-400 hover:text-[var(--c-primary)]"
|
||||
onClick={() => {
|
||||
setSelectedDish("");
|
||||
setDishSearch("");
|
||||
}}
|
||||
aria-label={dict.weekEditor.changeDish}
|
||||
title={dict.weekEditor.changeDish}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<input
|
||||
type="search"
|
||||
className="input"
|
||||
autoFocus
|
||||
placeholder={dict.dishManager.searchPlaceholder}
|
||||
value={dishSearch}
|
||||
onChange={(e) => setDishSearch(e.target.value)}
|
||||
/>
|
||||
<ul className="max-h-32 overflow-y-auto border border-gray-200 rounded-md bg-white divide-y divide-gray-100">
|
||||
{filteredDishes.map((dish) => (
|
||||
<li key={dish.id}>
|
||||
<button
|
||||
type="button"
|
||||
className="w-full text-left px-2 py-1 text-sm hover:bg-[var(--c-row-bg-alt)]"
|
||||
onClick={() => setSelectedDish(dish.id)}
|
||||
>
|
||||
{localized(dish, locale)} ({formatPrice(dish.price)})
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
{filteredDishes.length === 0 && (
|
||||
<li className="px-2 py-1 text-sm text-gray-500">
|
||||
{formatMessage(dict.dishManager.noResults, {
|
||||
query: dishSearch,
|
||||
})}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
<div className="flex gap-1 justify-center">
|
||||
<button
|
||||
type="button"
|
||||
@@ -192,10 +248,7 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-neutral"
|
||||
onClick={() => {
|
||||
setAdding(null);
|
||||
setSelectedDish("");
|
||||
}}
|
||||
onClick={cancelAdding}
|
||||
>
|
||||
{dict.common.cancel}
|
||||
</button>
|
||||
@@ -205,10 +258,7 @@ export default function WeekEditor({ monday }: { monday: string }) {
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-add mt-1"
|
||||
onClick={() => {
|
||||
setAdding({ iso: d.iso, section: s.key });
|
||||
setSelectedDish("");
|
||||
}}
|
||||
onClick={() => startAdding(d.iso, s.key)}
|
||||
>
|
||||
{dict.weekEditor.addMeal}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user