Skip to content

Case study · web

Kitchen OS

A mobile-first PWA for kitchen inventory, expiry tracking and AI-assisted meal planning — auth, row-level-secured data, and an installable app shell.

login-page.png
Kitchen OS sign-in page with the tagline: Know what you have. Cook before it expires.
01

Overview

Groceries went bad before anyone remembered they existed, and knowing what to cook on a given night meant opening the fridge and guessing. There was no record of what was actually on hand, when it needed to be used, or what could be made from it without a shopping trip.

02

Objective

Build a personal kitchen inventory that's fast enough to actually keep updated — log a receipt instead of typing every item by hand, surface what's about to expire before it's wasted, and turn what's on hand into a cookable plan instead of a mental exercise.

03

Approach

The stack is Next.js App Router on top of Supabase (Postgres + Auth) via Prisma, with middleware refreshing the session and guarding every authenticated route. Authorization is enforced twice: the service layer scopes every query to the signed-in user, and Postgres Row-Level Security policies (auth.uid() = user_id) sit underneath as defense-in-depth for anything hitting Supabase directly — Prisma itself runs with a privileged role and doesn't depend on RLS to stay safe.

Receipt parsing and recipe generation go through a provider-abstracted AI layer (Anthropic or Gemini, picked by an environment variable) with Zod schemas validating whatever comes back before it touches the database — model output is treated as an untrusted suggestion, not trusted structure. Unit conversion, expiry estimation and inventory deduction are pure functions, covered by around 75 Vitest cases, deliberately kept independent of the database so they run fast and don't need a live Supabase connection.

04

Solution

The dashboard leads with what needs attention — items expiring soon or already past their (estimated) date — above two quick actions: scan a receipt or add an item by hand. The inventory list supports search, sort, and filtering by freshness or storage location (fridge, freezer, pantry), with a freshness pill on every row and a one-tap "use" action that deducts it and logs what was actually consumed.

Scanning a receipt is deliberately narrow in scope: the photo goes to the AI only to read the line items back, stays private to the account, and can be deleted at any time. On the cooking side, "What's for dinner?" suggests ideas from what's already in the kitchen — saved recipes take priority over anything AI-generated — and a recipe's missing ingredients can be sent straight to the shopping list from its own page. Settings covers household and dietary preferences, plus a "recalculate expiry estimates" action that only touches dates the AI estimated; anything set by hand is left alone.

This is the real product, live at kitchen-os.mcabello.dev — the screens above aren't mockups.

dashboard.png
Kitchen OS dashboard showing items expiring soon and quick actions to scan a receipt or add an item
Dashboard — what needs attention first, with expiry estimates marked as such.
scan-receipt.png
Scan receipt screen with a note that the photo is used only to read items and can be deleted any time
Receipt scanning — the AI reads the line items; nothing is retained beyond what review needs.
05

Technology

  • Next.js
  • React
  • TypeScript
  • Tailwind CSS
  • Prisma
  • PostgreSQL
  • Supabase
  • Anthropic API
  • Vitest
06

Outcome

Live at kitchen-os.mcabello.dev and in real personal use — the inventory above is an actual kitchen's, not seed data: groceries get logged from a receipt, dinners get planned from what's actually on hand, and nothing here was staged for this write-up.

07
inventory.png
Inventory list with search, sort, and freshness filters
cook-idea.png
Cook page with AI-suggested recipes and a weekly meal-plan action
shopping.png
Shopping list empty state, which can also receive a recipe's missing ingredients
settings.png
Settings page with household preferences and an expiry re-estimate action