feat(pump): link pump events to milk stock, fix expiry units
- PUMP modal: "Ajouter au stock" toggle with location selector - Merge option: pick existing today's lot to add volume to (expiry kept) - On save: POST new MilkStock or PATCH existing volume - Milk page: expiry input now in hours/days/months per location - Milk page: "soon" alert threshold scales per location (freezer=7d, fridge=24h, room=2h) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+37
-10
@@ -37,7 +37,13 @@ function expiryStatus(stock: Stock): "expired" | "soon" | "ok" {
|
||||
const now = Date.now();
|
||||
const exp = new Date(stock.expiresAt).getTime();
|
||||
if (exp <= now) return "expired";
|
||||
if (exp - now < 6 * 60 * 60 * 1000) return "soon"; // < 6h
|
||||
// "soon" threshold scales with location: room <2h, fridge <24h, freezer <7d
|
||||
const remaining = exp - now;
|
||||
const soonMs =
|
||||
stock.location === "freezer" ? 7 * 24 * 3600_000 :
|
||||
stock.location === "fridge" ? 24 * 3600_000 :
|
||||
2 * 3600_000;
|
||||
if (remaining < soonMs) return "soon";
|
||||
return "ok";
|
||||
}
|
||||
|
||||
@@ -51,7 +57,7 @@ export default function MilkPage() {
|
||||
volume: "",
|
||||
storedAt: localNowString(),
|
||||
notes: "",
|
||||
expiryHours: "",
|
||||
expiryCustom: "", // in hours (room), days (fridge), months (freezer)
|
||||
});
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
@@ -77,7 +83,13 @@ export default function MilkPage() {
|
||||
e.preventDefault();
|
||||
if (!selectedBaby) return;
|
||||
setSaving(true);
|
||||
const expiryHours = form.expiryHours ? parseFloat(form.expiryHours) : undefined;
|
||||
let expiryCustom: number | undefined;
|
||||
if (form.expiryCustom) {
|
||||
const v = parseFloat(form.expiryCustom);
|
||||
if (location === "fridge") expiryCustom = v * 24;
|
||||
else if (location === "freezer") expiryCustom = v * 24 * 30;
|
||||
else expiryCustom = v;
|
||||
}
|
||||
await fetch("/api/milk", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -87,11 +99,11 @@ export default function MilkPage() {
|
||||
storedAt: form.storedAt,
|
||||
location,
|
||||
notes: form.notes || null,
|
||||
expiryHours,
|
||||
expiryCustom,
|
||||
}),
|
||||
});
|
||||
qc.invalidateQueries({ queryKey: ["milk"] });
|
||||
setForm({ volume: "", storedAt: localNowString(), notes: "", expiryHours: "" });
|
||||
setForm({ volume: "", storedAt: localNowString(), notes: "", expiryCustom: "" });
|
||||
setShowForm(false);
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -177,7 +189,7 @@ export default function MilkPage() {
|
||||
return (
|
||||
<button key={loc} type="button" onClick={() => {
|
||||
setLocation(loc);
|
||||
setForm((f) => ({ ...f, expiryHours: "" }));
|
||||
setForm((f) => ({ ...f, expiryCustom: "" }));
|
||||
}}
|
||||
className={`flex-1 flex flex-col items-center gap-1 py-2 px-1 border rounded-xl text-xs font-medium transition ${
|
||||
location === loc
|
||||
@@ -191,7 +203,12 @@ export default function MilkPage() {
|
||||
})}
|
||||
</div>
|
||||
<p className="text-[10px] text-slate-400 dark:text-slate-500 mt-1">
|
||||
Durée par défaut : {defaultExpiryHours >= 24 ? `${defaultExpiryHours / 24}j` : `${defaultExpiryHours}h`}
|
||||
Durée par défaut :{" "}
|
||||
{location === "freezer"
|
||||
? `${defaultExpiryHours / 24 / 30} mois`
|
||||
: location === "fridge"
|
||||
? `${defaultExpiryHours / 24} jours`
|
||||
: `${defaultExpiryHours}h`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -202,9 +219,19 @@ export default function MilkPage() {
|
||||
required className={inputCls} placeholder="80" min="1" max="2000" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-slate-600 dark:text-slate-400 mb-1.5">Durée péremption (h)</label>
|
||||
<input type="number" value={form.expiryHours} onChange={(e) => setForm((f) => ({ ...f, expiryHours: e.target.value }))}
|
||||
className={inputCls} placeholder={String(defaultExpiryHours)} min="1" />
|
||||
<label className="block text-xs font-medium text-slate-600 dark:text-slate-400 mb-1.5">
|
||||
Péremption ({location === "freezer" ? "mois" : location === "fridge" ? "jours" : "heures"})
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={form.expiryCustom}
|
||||
onChange={(e) => setForm((f) => ({ ...f, expiryCustom: e.target.value }))}
|
||||
className={inputCls}
|
||||
placeholder={
|
||||
location === "freezer" ? "6" : location === "fridge" ? "4" : "4"
|
||||
}
|
||||
min="1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user