import { useEffect } from "react"; import type { Bootstrap, Item, Product } from "../types.js"; import { TYPES, helpers } from "../types.js"; import { getToday, getStoredTimezone } from "../tz.js"; import { fmt, TYPE_GLYPHS } from "../format.js"; import { Btn, Pill, Icon } from "./primitives/index.js"; // Right-side drawer for an inventory instance. Shows the asset id and // product context up top, then per-batch fields (price, THC, weight), // audit history, and full detail rows. export function ProductDetail({ item, data, onClose, onConsume, onMarkGone, onAudit, onEdit, onCheckout, onCheckin, }: { item: Item; data: Bootstrap; onClose: () => void; onConsume: (i: Item) => void; onMarkGone: (i: Item) => void; onAudit: (i: Item) => void; onEdit: (i: Item) => void; onCheckout: (i: Item) => void; onCheckin: (i: Item) => void; }) { const bin = data.bins.find((b) => b.id === item.binId); const cfg = TYPES.find((t) => t.id === item.type); const product = data.products.find((p) => p.id === item.productId); const pctRemaining = helpers.pctRemaining(item, getToday(getStoredTimezone())); const est = helpers.estimatedRemaining(item, getToday(getStoredTimezone())); const last = helpers.lastAudit(item); const overdue = helpers.auditOverdue(item, getToday(getStoredTimezone())); const sinceCheck = helpers.daysSinceCheck(item, getToday(getStoredTimezone())); const isActive = item.status === "active"; const isCheckedOut = item.status === "checked-out"; useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [onClose]); // Sibling instances of the same product (excluding this one) — useful for // seeing previous purchases of the same SKU. const siblings = data.inventoryItems.filter( (i) => i.productId === item.productId && i.id !== item.id, ); const detailRows: [string, React.ReactNode][] = [ ["Asset id", {item.assetId}], ["SKU", {item.sku}], ["Type", `${item.type} · ${item.kind}`], ["Strain", item.name], ["Brand", helpers.brandName(data, item.brandId)], ["Shop", helpers.shopName(data, item.shopId)], ...(cfg?.showCannabinoidPct !== false ? [["Total cannabinoids", `${item.totalCannabinoids.toFixed(1)}%`] as [string, React.ReactNode]] : []), ["Purchase date", fmt.date(item.purchaseDate, getStoredTimezone())], ["Bin", isCheckedOut ? "In your custody" : bin ? bin.name : —], ["Audit cadence", `Every ${cfg?.cadenceDays ?? "—"} days · ${cfg?.auditMode ?? "—"}`], [ "Cost per gram", item.kind === "bulk" && item.weight > 0 ? fmt.money(item.price / item.weight) : item.kind === "discrete" && item.unitWeight > 0 ? `${fmt.money(item.price / (item.countOriginal * item.unitWeight))} (effective)` : "—", ], ]; if (item.status === "checked-out") { detailRows.push(["Checked out", fmt.date(item.checkoutDate, getStoredTimezone())]); } if (item.status === "consumed") { detailRows.push( ["Date finished", fmt.date(item.consumedDate, getStoredTimezone())], [ "Lasted", `${Math.round((+new Date(item.consumedDate!) - +new Date(item.purchaseDate)) / 86_400_000)} days`, ], ); } if (item.status === "gone") { detailRows.push( ["Date gone", fmt.date(item.goneDate, getStoredTimezone())], [ "After", `${Math.round((+new Date(item.goneDate!) - +new Date(item.purchaseDate)) / 86_400_000)} days`, ], ); } return (