Deux retours utilisateur distincts, meme cause : le popover de correction presentait la selection de technique et l'edition des ingredients/ ustensiles comme deux etapes successives (liste plate -> "Valider" pour la selection, puis un ecran separe pour les metadonnees), et cette liste de techniques elle-meme n'etait qu'un flot de ~74 boutons sans recherche ni tri, illisible en pratique. TechStepCorrectionPopover.tsx : les deux fonctions fusionnent en un seul ecran — la technique se choisit desormais via le meme CatalogSearchPicker (recherche + liste filtrée) deja utilise pour les ingredients/ustensiles, avec le choix courant marque visuellement (nouveau prop `selectedId`), et les sections Ingredients/Ustensiles restent affichees en permanence a cote plutot que masquees tant qu'aucune technique n'est choisie. "Valider" reste desactive tant qu'aucune technique n'est selectionnee. CatalogSearchPicker.tsx : nouveau prop optionnel `selectedId` pour marquer visuellement l'item courant dans la liste (utilise par le picker de technique, pas par les sous-flux ingredient/ustensile qui n'ont pas de notion de "choix courant"). Tests Cypress (component + e2e) et traductions mis a jour pour ce nouvel ecran unique. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
71 lines
2.8 KiB
TypeScript
71 lines
2.8 KiB
TypeScript
import { useState } from "react";
|
|
|
|
/**
|
|
* Small search-and-pick list — a lighter alternative to `IngredientPicker.tsx`
|
|
* (category/subcategory grid + allergen/diet toggles) for a context that
|
|
* doesn't have room for that: `TechStepCorrectionPopover.tsx`'s ingredient/
|
|
* utensil/**technique** pickers, all embedded in a small popover rather than
|
|
* a full recipe form. Reused for all three — an ingredient, a utensil, and a
|
|
* technique are all "search a reference list by translated label, pick one"
|
|
* from this component's point of view, the only difference is which
|
|
* `items`/labels the caller passes in. The technique catalog in particular
|
|
* (~74 entries) is exactly the case a plain unfiltered list stops being
|
|
* readable at — the original motivation for adding search here at all.
|
|
*
|
|
* Deliberately just `{ id, label }` in, `id` out — no `IngredientView`/
|
|
* `UtensilView`/`TechStepView` dependency here, so this stays reusable for
|
|
* any future "search this small reference catalog" need without growing a
|
|
* new prop per catalog shape.
|
|
*/
|
|
export function CatalogSearchPicker({
|
|
items,
|
|
selectedId,
|
|
onSelect,
|
|
placeholder,
|
|
emptyLabel,
|
|
}: {
|
|
items: { id: number; label: string }[];
|
|
/** The currently-picked item, if any — marked with a distinct modifier class so it stays visible at a glance while browsing/filtering a longer list (e.g. `TechStepCorrectionPopover`'s ~74-entry technique catalog), not just implied by whatever's selected elsewhere on screen. Omit for a picker with no notion of a "current" pick (the ingredient/utensil span sub-flows — each `onSelect` there just appends a brand-new mention, nothing to mark as already chosen). */
|
|
selectedId?: number;
|
|
onSelect: (id: number) => void;
|
|
placeholder: string;
|
|
emptyLabel: string;
|
|
}) {
|
|
const [query, setQuery] = useState("");
|
|
const normalizedQuery = query.trim().toLowerCase();
|
|
const visible =
|
|
normalizedQuery.length === 0
|
|
? items
|
|
: items.filter((item) => item.label.toLowerCase().includes(normalizedQuery));
|
|
|
|
return (
|
|
<div className="catalog-search-picker">
|
|
<input
|
|
type="text"
|
|
value={query}
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
placeholder={placeholder}
|
|
className="catalog-search-picker__input"
|
|
/>
|
|
{visible.length === 0 ? (
|
|
<p className="catalog-search-picker__empty">{emptyLabel}</p>
|
|
) : (
|
|
<ul className="catalog-search-picker__list">
|
|
{visible.map((item) => (
|
|
<li key={item.id}>
|
|
<button
|
|
type="button"
|
|
className={
|
|
item.id === selectedId ? "catalog-search-picker__item--selected" : undefined
|
|
}
|
|
onClick={() => onSelect(item.id)}
|
|
>
|
|
{item.label}
|
|
</button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|