v0.21.1 — Pantry: delete items with inline confirm
Trash icon on each row; clicking shows "Delete? Yes / No" inline (no dialog needed). Soft-deletes via active=false. Removed the admin-only guard on DELETE /api/pantry/[id] since anyone logged in should be able to manage their own pantry. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { ItemSource } from "@prisma/client";
|
import { ItemSource } from "@prisma/client";
|
||||||
import { db } from "@/lib/db";
|
import { db } from "@/lib/db";
|
||||||
import { requireAuth, isAdmin } from "@/lib/auth";
|
import { requireAuth } from "@/lib/auth";
|
||||||
|
|
||||||
const PANTRY_SELECT = {
|
const PANTRY_SELECT = {
|
||||||
id: true, name: true, description: true, quantity: true, unit: true,
|
id: true, name: true, description: true, quantity: true, unit: true,
|
||||||
@@ -96,7 +96,6 @@ export async function PATCH(req: Request, { params }: { params: { id: string } }
|
|||||||
export async function DELETE(_req: Request, { params }: { params: { id: string } }) {
|
export async function DELETE(_req: Request, { params }: { params: { id: string } }) {
|
||||||
try {
|
try {
|
||||||
const session = await requireAuth();
|
const session = await requireAuth();
|
||||||
if (!isAdmin(session.user.role)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
|
||||||
await db.item.update({ where: { id: params.id }, data: { active: false } });
|
await db.item.update({ where: { id: params.id }, data: { active: false } });
|
||||||
await db.auditEvent.create({
|
await db.auditEvent.create({
|
||||||
data: { action: "pantry.delete", entityId: params.id, actorId: session.user.id },
|
data: { action: "pantry.delete", entityId: params.id, actorId: session.user.id },
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { AlertTriangle, ChevronDown, ChevronRight, UtensilsCrossed } from "lucide-react";
|
import { AlertTriangle, ChevronDown, ChevronRight, Trash2, UtensilsCrossed } from "lucide-react";
|
||||||
import { PantryItemDialog } from "./pantry-item-dialog";
|
import { PantryItemDialog } from "./pantry-item-dialog";
|
||||||
import { PullDialog } from "./pull-dialog";
|
import { PullDialog } from "./pull-dialog";
|
||||||
import { UseItemDialog } from "./use-item-dialog";
|
import { UseItemDialog } from "./use-item-dialog";
|
||||||
@@ -51,6 +51,20 @@ function ExpiryBadge({ date }: { date: Date }) {
|
|||||||
function ItemRow({ item, locations, plants, onRefresh }: {
|
function ItemRow({ item, locations, plants, onRefresh }: {
|
||||||
item: PantryItem; locations: LocationOption[]; plants: PlantOption[]; onRefresh: () => void;
|
item: PantryItem; locations: LocationOption[]; plants: PlantOption[]; onRefresh: () => void;
|
||||||
}) {
|
}) {
|
||||||
|
const [confirming, setConfirming] = useState(false);
|
||||||
|
const [deleting, setDeleting] = useState(false);
|
||||||
|
|
||||||
|
async function handleDelete() {
|
||||||
|
setDeleting(true);
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/pantry/${item.id}`, { method: "DELETE" });
|
||||||
|
if (res.ok) onRefresh();
|
||||||
|
} finally {
|
||||||
|
setDeleting(false);
|
||||||
|
setConfirming(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const priceStr = item.pricePaid != null
|
const priceStr = item.pricePaid != null
|
||||||
? `$${item.pricePaid.toFixed(2)}${item.priceUnit && item.priceUnit !== "package" ? `/${item.priceUnit}` : ""}`
|
? `$${item.pricePaid.toFixed(2)}${item.priceUnit && item.priceUnit !== "package" ? `/${item.priceUnit}` : ""}`
|
||||||
: null;
|
: null;
|
||||||
@@ -83,8 +97,37 @@ function ItemRow({ item, locations, plants, onRefresh }: {
|
|||||||
{item.notes && <p className="text-xs text-muted-foreground mt-0.5 italic">{item.notes}</p>}
|
{item.notes && <p className="text-xs text-muted-foreground mt-0.5 italic">{item.notes}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1 shrink-0">
|
<div className="flex items-center gap-1 shrink-0">
|
||||||
<UseItemDialog item={item} onSuccess={onRefresh} />
|
{confirming ? (
|
||||||
<PantryItemDialog item={item} locations={locations} plants={plants} onSuccess={onRefresh} />
|
<>
|
||||||
|
<span className="text-xs text-muted-foreground mr-1">Delete?</span>
|
||||||
|
<button
|
||||||
|
onClick={handleDelete}
|
||||||
|
disabled={deleting}
|
||||||
|
className="text-xs text-destructive font-medium hover:underline disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{deleting ? "…" : "Yes"}
|
||||||
|
</button>
|
||||||
|
<span className="text-muted-foreground text-xs">/</span>
|
||||||
|
<button
|
||||||
|
onClick={() => setConfirming(false)}
|
||||||
|
className="text-xs text-muted-foreground hover:underline"
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<UseItemDialog item={item} onSuccess={onRefresh} />
|
||||||
|
<PantryItemDialog item={item} locations={locations} plants={plants} onSuccess={onRefresh} />
|
||||||
|
<button
|
||||||
|
onClick={() => setConfirming(true)}
|
||||||
|
className="h-8 w-8 flex items-center justify-center rounded-md hover:bg-destructive/10 hover:text-destructive text-muted-foreground transition-colors"
|
||||||
|
title="Delete"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,6 +8,13 @@ export type ChangelogEntry = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CHANGELOG: ChangelogEntry[] = [
|
export const CHANGELOG: ChangelogEntry[] = [
|
||||||
|
{
|
||||||
|
version: "0.21.1",
|
||||||
|
date: "2026-06-29",
|
||||||
|
changes: [
|
||||||
|
"Pantry: trash icon on each item — click it, confirm with Yes/No. Removes the item from the inventory.",
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
version: "0.21.0",
|
version: "0.21.0",
|
||||||
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.21.0";
|
export const APP_VERSION = "0.21.1";
|
||||||
|
|||||||
Reference in New Issue
Block a user