93 lines
3.3 KiB
TypeScript
93 lines
3.3 KiB
TypeScript
import { headers } from "next/headers";
|
|
import { auth } from "@/lib/auth/server";
|
|
import { db, pantryItems, eq, asc } from "@epicure/db";
|
|
import { PrintTrigger } from "@/components/recipe/print-trigger";
|
|
|
|
export default async function PantryPrintPage() {
|
|
const session = await auth.api.getSession({ headers: await headers() });
|
|
if (!session) return null;
|
|
|
|
const items = await db.query.pantryItems.findMany({
|
|
where: eq(pantryItems.userId, session.user.id),
|
|
orderBy: asc(pantryItems.rawName),
|
|
});
|
|
|
|
const now = new Date();
|
|
|
|
return (
|
|
<>
|
|
<style>{`
|
|
@media print {
|
|
body { margin: 0; }
|
|
.no-print { display: none !important; }
|
|
}
|
|
body {
|
|
font-family: system-ui, sans-serif;
|
|
color: #1a1a1a;
|
|
max-width: 680px;
|
|
margin: 40px auto;
|
|
padding: 0 24px;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
}
|
|
h1 { font-size: 1.8em; margin: 0 0 4px; }
|
|
.subtitle { color: #666; font-size: 0.9em; margin-bottom: 24px; }
|
|
table { width: 100%; border-collapse: collapse; }
|
|
th { text-align: left; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.06em; color: #888; padding: 4px 8px 4px 0; border-bottom: 1px solid #ccc; }
|
|
td { padding: 6px 8px 6px 0; border-bottom: 1px dotted #eee; font-size: 0.9em; vertical-align: top; }
|
|
tr:last-child td { border-bottom: none; }
|
|
.expiry-soon { color: #b45309; }
|
|
.expiry-expired { color: #dc2626; }
|
|
footer { margin-top: 40px; font-size: 0.75em; color: #aaa; text-align: center; }
|
|
.print-btn {
|
|
position: fixed; top: 16px; right: 16px; padding: 8px 16px;
|
|
background: #18181b; color: white; border: none; border-radius: 6px;
|
|
cursor: pointer; font-size: 13px;
|
|
}
|
|
.print-btn:hover { background: #3f3f46; }
|
|
`}</style>
|
|
|
|
<PrintTrigger />
|
|
|
|
<h1>Pantry</h1>
|
|
<p className="subtitle">{items.length} item{items.length !== 1 ? "s" : ""}</p>
|
|
|
|
{items.length === 0 ? (
|
|
<p style={{ color: "#888" }}>No items in pantry.</p>
|
|
) : (
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Item</th>
|
|
<th>Quantity</th>
|
|
<th>Expires</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{items.map((item) => {
|
|
const exp = item.expiresAt ? new Date(item.expiresAt) : null;
|
|
const daysLeft = exp ? Math.ceil((exp.getTime() - now.getTime()) / 86400000) : null;
|
|
const expiryClass = daysLeft === null ? "" : daysLeft < 0 ? "expiry-expired" : daysLeft <= 3 ? "expiry-soon" : "";
|
|
return (
|
|
<tr key={item.id}>
|
|
<td>{item.rawName}</td>
|
|
<td>{[item.quantity, item.unit].filter(Boolean).join(" ") || "—"}</td>
|
|
<td className={expiryClass}>
|
|
{exp
|
|
? daysLeft !== null && daysLeft < 0
|
|
? `Expired ${Math.abs(daysLeft)}d ago`
|
|
: exp.toLocaleDateString("en", { month: "short", day: "numeric", year: "numeric" })
|
|
: "—"}
|
|
</td>
|
|
</tr>
|
|
);
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
|
|
<footer>Printed from Epicure</footer>
|
|
</>
|
|
);
|
|
}
|