feat(pump): show all available lots for merge, add expiry input

- Replace today-only lot filter with all available lots (fridge + freezer)
- Show lot date (d MMM HH:mm) not just time for clarity
- Add expiry input when creating new lot (hidden when merging)
- Expiry unit label adapts: heures/jours/mois per storage location

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 15:42:16 +02:00
parent 9fb1a726dd
commit 8152c02995
+46 -30
View File
@@ -140,8 +140,9 @@ export function EventModal({ type, babyId, onClose, onSaved, initialEvent }: Pro
// Pump → stock state // Pump → stock state
const [addToStock, setAddToStock] = useState(false); const [addToStock, setAddToStock] = useState(false);
const [pumpStockLocation, setPumpStockLocation] = useState<"room" | "fridge" | "freezer">("fridge"); const [pumpStockLocation, setPumpStockLocation] = useState<"room" | "fridge" | "freezer">("fridge");
const [todayLots, setTodayLots] = useState<MilkStock[]>([]); const [availableLots, setAvailableLots] = useState<MilkStock[]>([]);
const [mergeLotId, setMergeLotId] = useState<string | null>(null); const [mergeLotId, setMergeLotId] = useState<string | null>(null);
const [pumpExpiryCustom, setPumpExpiryCustom] = useState("");
const selectedProfile = profiles.find((p) => p.id === selectedProfileId) ?? null; const selectedProfile = profiles.find((p) => p.id === selectedProfileId) ?? null;
@@ -162,13 +163,10 @@ export function EventModal({ type, babyId, onClose, onSaved, initialEvent }: Pro
.then((r) => r.json()) .then((r) => r.json())
.then((stocks) => { .then((stocks) => {
if (!Array.isArray(stocks)) return; if (!Array.isArray(stocks)) return;
const today = new Date().toDateString(); const lots = stocks
const lots = stocks.filter( .filter((s: MilkStock & { used?: boolean }) => !s.used)
(s: MilkStock & { used?: boolean }) => .sort((a: MilkStock, b: MilkStock) => new Date(b.storedAt).getTime() - new Date(a.storedAt).getTime());
!s.used && new Date(s.storedAt).toDateString() === today setAvailableLots(lots);
);
lots.sort((a: MilkStock, b: MilkStock) => new Date(b.storedAt).getTime() - new Date(a.storedAt).getTime());
setTodayLots(lots);
}); });
}, [type, babyId]); }, [type, babyId]);
@@ -353,17 +351,24 @@ export function EventModal({ type, babyId, onClose, onSaved, initialEvent }: Pro
const vol = parseFloat(form.pumpVolume); const vol = parseFloat(form.pumpVolume);
if (vol > 0) { if (vol > 0) {
if (mergeLotId) { if (mergeLotId) {
const existing = todayLots.find((l) => l.id === mergeLotId); const existing = availableLots.find((l) => l.id === mergeLotId);
await fetch(`/api/milk/${mergeLotId}`, { await fetch(`/api/milk/${mergeLotId}`, {
method: "PATCH", method: "PATCH",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ volume: (existing?.volume ?? 0) + vol }), body: JSON.stringify({ volume: (existing?.volume ?? 0) + vol }),
}); });
} else { } else {
let expiryHours: number | undefined;
if (pumpExpiryCustom) {
const v = parseFloat(pumpExpiryCustom);
expiryHours = pumpStockLocation === "freezer" ? v * 24 * 30
: pumpStockLocation === "fridge" ? v * 24
: v;
}
await fetch("/api/milk", { await fetch("/api/milk", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ babyId, volume: vol, location: pumpStockLocation }), body: JSON.stringify({ babyId, volume: vol, location: pumpStockLocation, expiryHours }),
}); });
} }
} }
@@ -536,27 +541,38 @@ export function EventModal({ type, babyId, onClose, onSaved, initialEvent }: Pro
))} ))}
</div> </div>
</div> </div>
{todayLots.length > 0 && ( <div>
<div> <label className="block text-xs font-medium text-slate-600 dark:text-slate-300 mb-1.5">Fusionner avec lot existant</label>
<label className="block text-xs font-medium text-slate-600 dark:text-slate-300 mb-1.5">Fusionner avec lot du jour</label> <div className="flex flex-col gap-1.5 max-h-36 overflow-y-auto">
<div className="flex flex-col gap-1.5"> <button type="button" onClick={() => setMergeLotId(null)}
<button type="button" onClick={() => setMergeLotId(null)} className={`flex items-center gap-2 px-3 py-2 rounded-lg border text-xs transition ${mergeLotId === null ? "border-indigo-500 bg-indigo-50 dark:bg-indigo-950 text-indigo-700 dark:text-indigo-300" : "border-slate-200 dark:border-slate-600 text-slate-500 dark:text-slate-400"}`}>
className={`flex items-center gap-2 px-3 py-2 rounded-lg border text-xs transition ${mergeLotId === null ? "border-indigo-500 bg-indigo-50 dark:bg-indigo-950 text-indigo-700 dark:text-indigo-300" : "border-slate-200 dark:border-slate-600 text-slate-500 dark:text-slate-400"}`}> Nouveau lot
Nouveau lot </button>
{availableLots.map((lot) => (
<button key={lot.id} type="button" onClick={() => setMergeLotId(lot.id)}
className={`flex items-center gap-2 px-3 py-2 rounded-lg border text-xs transition ${mergeLotId === lot.id ? "border-indigo-500 bg-indigo-50 dark:bg-indigo-950 text-indigo-700 dark:text-indigo-300" : "border-slate-200 dark:border-slate-600 text-slate-500 dark:text-slate-400"}`}>
{lot.volume} mL · {lot.location === "fridge" ? "frigo" : lot.location === "freezer" ? "congélo" : "ambiant"}
{" · "}{format(new Date(lot.storedAt), "d MMM HH:mm", { locale: fr })}
</button> </button>
{todayLots.map((lot) => ( ))}
<button key={lot.id} type="button" onClick={() => setMergeLotId(lot.id)} </div>
className={`flex items-center gap-2 px-3 py-2 rounded-lg border text-xs transition ${mergeLotId === lot.id ? "border-indigo-500 bg-indigo-50 dark:bg-indigo-950 text-indigo-700 dark:text-indigo-300" : "border-slate-200 dark:border-slate-600 text-slate-500 dark:text-slate-400"}`}> {mergeLotId && (
{lot.volume} mL · {lot.location === "fridge" ? "frigo" : lot.location === "freezer" ? "congélo" : "ambiant"} <p className="text-[11px] text-indigo-500 mt-1.5">Volume ajouté. Péremption inchangée.</p>
{" · "}tirage {format(new Date(lot.storedAt), "HH:mm", { locale: fr })} )}
</button> </div>
))} {!mergeLotId && (
</div> <div>
{mergeLotId && ( <label className="block text-xs font-medium text-slate-600 dark:text-slate-300 mb-1.5">
<p className="text-[11px] text-indigo-500 mt-1.5"> Péremption ({pumpStockLocation === "freezer" ? "mois" : pumpStockLocation === "fridge" ? "jours" : "heures"})
Volume sera ajouté à ce lot. Péremption inchangée. </label>
</p> <input
)} type="number"
value={pumpExpiryCustom}
onChange={(e) => setPumpExpiryCustom(e.target.value)}
className={inputCls}
placeholder={pumpStockLocation === "freezer" ? "6" : pumpStockLocation === "fridge" ? "4" : "4"}
min="1"
/>
</div> </div>
)} )}
</div> </div>