v0.19.0 — Pantry: tag what you used items for
Quick-pick chips (Family dinner, Dinner party, Taking on a trip, Meal prep, Gave it away) in the Use dialog, alongside the existing freeform notes field — picking one fills the note, no schema change needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +1,33 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { Minus } from "lucide-react";
|
import { Minus } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import type { PantryItem } from "./pantry-client";
|
import type { PantryItem } from "./pantry-client";
|
||||||
|
|
||||||
|
const QUICK_REASONS = ["Family dinner", "Dinner party", "Taking on a trip", "Meal prep", "Gave it away"];
|
||||||
|
|
||||||
export function UseItemDialog({ item, onSuccess }: { item: PantryItem; onSuccess: () => void }) {
|
export function UseItemDialog({ item, onSuccess }: { item: PantryItem; onSuccess: () => void }) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [amount, setAmount] = useState("1");
|
const [amount, setAmount] = useState("1");
|
||||||
const [notes, setNotes] = useState("");
|
const [notes, setNotes] = useState("");
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [selectedReason, setSelectedReason] = useState<string | null>(null);
|
||||||
|
const notesRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
function pickReason(reason: string) {
|
||||||
|
if (selectedReason === reason) {
|
||||||
|
setSelectedReason(null);
|
||||||
|
setNotes("");
|
||||||
|
} else {
|
||||||
|
setSelectedReason(reason);
|
||||||
|
setNotes(reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent) {
|
async function handleSubmit(e: React.FormEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -28,6 +43,7 @@ export function UseItemDialog({ item, onSuccess }: { item: PantryItem; onSuccess
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
setAmount("1");
|
setAmount("1");
|
||||||
setNotes("");
|
setNotes("");
|
||||||
|
setSelectedReason(null);
|
||||||
onSuccess();
|
onSuccess();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(String(err).replace("Error: ", ""));
|
setError(String(err).replace("Error: ", ""));
|
||||||
@@ -44,7 +60,7 @@ export function UseItemDialog({ item, onSuccess }: { item: PantryItem; onSuccess
|
|||||||
<Minus className="h-3.5 w-3.5" />
|
<Minus className="h-3.5 w-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={o => { setOpen(o); if (!o) { setSelectedReason(null); setNotes(""); } }}>
|
||||||
<DialogContent className="max-w-sm">
|
<DialogContent className="max-w-sm">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Use {item.name}</DialogTitle>
|
<DialogTitle>Use {item.name}</DialogTitle>
|
||||||
@@ -66,9 +82,32 @@ export function UseItemDialog({ item, onSuccess }: { item: PantryItem; onSuccess
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1.5">
|
||||||
<Label>Notes (optional)</Label>
|
<Label>What's it for? (optional)</Label>
|
||||||
<input className={inputCls} value={notes} onChange={e => setNotes(e.target.value)} placeholder="e.g. used in chicken soup" />
|
<div className="flex flex-wrap gap-1.5">
|
||||||
|
{QUICK_REASONS.map(reason => (
|
||||||
|
<button
|
||||||
|
key={reason}
|
||||||
|
type="button"
|
||||||
|
onClick={() => pickReason(reason)}
|
||||||
|
className={cn(
|
||||||
|
"rounded-full border px-3 py-1 text-xs",
|
||||||
|
selectedReason === reason
|
||||||
|
? "bg-primary text-primary-foreground border-primary"
|
||||||
|
: "bg-background hover:bg-accent"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{reason}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
ref={notesRef}
|
||||||
|
className={inputCls}
|
||||||
|
value={notes}
|
||||||
|
onChange={e => { setNotes(e.target.value); setSelectedReason(null); }}
|
||||||
|
placeholder="e.g. used in chicken soup, or pick a tag above"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{error && <p className="text-sm text-destructive">{error}</p>}
|
{error && <p className="text-sm text-destructive">{error}</p>}
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
|
|||||||
@@ -8,6 +8,13 @@ export type ChangelogEntry = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CHANGELOG: ChangelogEntry[] = [
|
export const CHANGELOG: ChangelogEntry[] = [
|
||||||
|
{
|
||||||
|
version: "0.19.0",
|
||||||
|
date: "2026-06-29",
|
||||||
|
changes: [
|
||||||
|
"Pantry: when you use something, you can now tag why with one tap — Family dinner, Dinner party, Taking on a trip, Meal prep, Gave it away — or type your own note instead.",
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
version: "0.18.1",
|
version: "0.18.1",
|
||||||
date: "2026-06-29",
|
date: "2026-06-29",
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
// Bump on every user-visible release. Changelog entries live in `src/lib/changelog.ts`.
|
// Bump on every user-visible release. Changelog entries live in `src/lib/changelog.ts`.
|
||||||
export const APP_VERSION = "0.18.1";
|
export const APP_VERSION = "0.19.0";
|
||||||
|
|||||||
Reference in New Issue
Block a user