commit 41537fa84c913b756f140f5646c81ba9cf2abe14 Author: Claw Date: Sun Feb 8 18:47:48 2026 +0000 docs: Initial project documentation - README: Project overview, quick start, tech stack - PROJECT_PLAN: Vision, roadmap, 6-week MVP timeline - ARCHITECTURE: System design, data model, tech decisions Establishes foundation for Pantry - self-hosted kitchen inventory PWA with barcode scanning. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0189705 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# Pantry ๐Ÿณ + +Self-hosted kitchen inventory management โ€” family-friendly PWA with barcode scanning. + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) + +## ๐ŸŽฏ Vision + +A simple, modern kitchen inventory app that the whole family can actually use. Born from frustration with existing tools: +- **Grocy:** Powerful but overwhelming +- **KitchenOwl:** Nice UI but lacks features + +**Pantry** bridges the gap โ€” simple enough for daily use, powerful enough to be useful. + +## โœจ Key Features (Planned) + +- ๐Ÿ“ฑ **PWA** โ€” Install on phone, works offline +- ๐Ÿ“ธ **Barcode scanning** โ€” Scan โ†’ Add (3 taps) +- ๐Ÿท๏ธ **Tag-based organization** โ€” Flexible categories (fridge, dairy, etc.) +- ๐Ÿ“Š **Unit conversions** โ€” kg โ†” g, L โ†” mL +- ๐Ÿ‘ฅ **Multi-user** โ€” Shared inventory (no household complexity) +- ๐Ÿ”’ **Self-hosted** โ€” Your data stays yours +- ๐ŸŒ **Open Food Facts** โ€” Auto-fill product data from barcodes + +## ๐Ÿš€ Quick Start + +```bash +# Clone +git clone https://gitea.jeanlucmakiola.de/pantry-app/pantry.git + +# Start services (Docker Compose) +docker-compose up -d + +# Access at http://localhost:3000 +``` + +## ๐Ÿ“š Documentation + +- [**Project Plan**](docs/PROJECT_PLAN.md) โ€” Vision, roadmap, phases +- [**Architecture**](docs/ARCHITECTURE.md) โ€” Tech stack, data model, design decisions +- [**Database Schema**](docs/DATABASE.md) โ€” Tables, relationships, RLS policies +- [**API Reference**](docs/API.md) โ€” Endpoints, Supabase functions +- [**Development Guide**](docs/DEVELOPMENT.md) โ€” Setup, workflow, conventions +- [**Deployment**](docs/DEPLOYMENT.md) โ€” Docker, Coolify, production setup + +## ๐Ÿ› ๏ธ Tech Stack + +| Layer | Technology | +|-------|------------| +| Frontend | Nuxt 4 (Vue 3) | +| Styling | Tailwind CSS + Nuxt UI | +| Backend | Supabase (Postgres, Auth, Realtime) | +| Barcode | html5-qrcode | +| Runtime | Bun | +| Deployment | Docker Compose | +| External API | Open Food Facts | + +## ๐Ÿ—‚๏ธ Monorepo Structure + +``` +pantry/ +โ”œโ”€โ”€ app/ # Nuxt 4 PWA +โ”œโ”€โ”€ supabase/ # Database, migrations, functions +โ”œโ”€โ”€ docker/ # Docker Compose, configs +โ”œโ”€โ”€ docs/ # Documentation +โ””โ”€โ”€ scripts/ # Utilities +``` + +## ๐ŸŽฏ Principles + +1. **Family-friendly** โ€” If your parents can't use it, it's too complex +2. **Start simple** โ€” Ship small, iterate fast +3. **Extendable** โ€” Clean architecture for future features +4. **Self-hosted first** โ€” No SaaS plans, no lock-in + +## ๐Ÿ“‹ MVP Status + +**Target:** v0.1 (6-week sprint) + +See [PROJECT_PLAN.md](docs/PROJECT_PLAN.md) for detailed roadmap. + +- [ ] Foundation (Nuxt + Supabase + Auth) +- [ ] Core inventory (CRUD, tags, units) +- [ ] Barcode scanning (PWA camera + Open Food Facts) +- [ ] Mobile polish (PWA, offline) +- [ ] Docker deployment + +## ๐Ÿค Contributing + +This is an early-stage project. Contributions welcome once v0.1 ships. + +## ๐Ÿ“„ License + +MIT ยฉ 2026 Jean-Luc Makiola + +## ๐Ÿ™ Acknowledgments + +- Open Food Facts for product data +- Supabase for the backend platform +- Nuxt/Vue ecosystem diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..4804d74 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,655 @@ +# Pantry - Architecture + +**Version:** 1.0 +**Last Updated:** 2026-02-08 + +--- + +## ๐Ÿ—๏ธ System Overview + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ User Devices โ”‚ +โ”‚ (Phone, Tablet, Desktop - PWA) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ HTTPS + โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Nuxt 4 Frontend (SSR) โ”‚ +โ”‚ - Vue 3 Components โ”‚ +โ”‚ - Tailwind CSS + Nuxt UI โ”‚ +โ”‚ - PWA (offline-first) โ”‚ +โ”‚ - Barcode Scanner โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ WebSocket + REST + โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Supabase Platform โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ PostgreSQL (Database) โ”‚ โ”‚ +โ”‚ โ”‚ - Items, Products, Tags, Units โ”‚ โ”‚ +โ”‚ โ”‚ - Row Level Security (RLS) โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Auth (GoTrue) โ”‚ โ”‚ +โ”‚ โ”‚ - Email/Password โ”‚ โ”‚ +โ”‚ โ”‚ - OIDC (optional) โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Realtime (WebSocket) โ”‚ โ”‚ +โ”‚ โ”‚ - Live updates across devices โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ Edge Functions (Deno) โ”‚ โ”‚ +โ”‚ โ”‚ - Product lookup โ”‚ โ”‚ +โ”‚ โ”‚ - Barcode cache โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ HTTP + โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Open Food Facts API โ”‚ +โ”‚ (External - product data) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +--- + +## ๐Ÿ“ฆ Tech Stack + +### Frontend + +**Nuxt 4** (Vue 3) +- **Why:** Meta-framework, SSR, excellent DX +- **Alternatives considered:** Next.js (React), SvelteKit +- **Decision:** Vue ecosystem maturity + Nuxt UI + +**Tailwind CSS** +- **Why:** Utility-first, fast iteration +- **Alternatives:** CSS Modules, UnoCSS +- **Decision:** Industry standard, Nuxt UI built on it + +**Nuxt UI** +- **Why:** Pre-built components, accessible +- **Alternatives:** Headless UI, Shadcn +- **Decision:** First-party Nuxt support + +**html5-qrcode** +- **Why:** PWA camera support, multi-format +- **Alternatives:** ZXing, QuaggaJS +- **Decision:** Best mobile performance in tests + +### Backend + +**Supabase** +- **Why:** Postgres + Auth + Realtime in one +- **Alternatives:** Firebase, Appwrite, custom backend +- **Decision:** Self-hosted, SQL flexibility, mature + +**PostgreSQL 15+** +- **Why:** Robust, supports JSONB, full-text search +- **Alternatives:** MySQL, MongoDB +- **Decision:** Comes with Supabase, best for relational data + +**Deno Edge Functions** +- **Why:** Serverless, TypeScript-native +- **Alternatives:** Node.js functions, custom API +- **Decision:** Part of Supabase, fast cold starts + +### Deployment + +**Docker Compose** +- **Why:** Simple multi-container orchestration +- **Alternatives:** Kubernetes, bare metal +- **Decision:** Right complexity level for self-hosted + +**Bun** (runtime) +- **Why:** Fast, npm-compatible, built-in TypeScript +- **Alternatives:** Node.js, pnpm +- **Decision:** Modern, faster than Node + +--- + +## ๐Ÿ—„๏ธ Data Model + +### Core Entities + +```mermaid +erDiagram + INVENTORY_ITEMS ||--o| PRODUCTS : references + INVENTORY_ITEMS }o--o{ TAGS : tagged_with + INVENTORY_ITEMS ||--|| UNITS : uses + PRODUCTS ||--|| UNITS : default_unit + UNITS ||--o| UNITS : converts_to + ITEM_TAGS }|--|| INVENTORY_ITEMS : item + ITEM_TAGS }|--|| TAGS : tag + + INVENTORY_ITEMS { + uuid id PK + uuid product_id FK + string name + decimal quantity + uuid unit_id FK + date expiry_date + uuid added_by FK + timestamp created_at + timestamp updated_at + } + + PRODUCTS { + uuid id PK + string barcode UK + string name + string brand + string image_url + uuid default_unit_id FK + timestamp cached_at + } + + TAGS { + uuid id PK + string name + enum category + string icon + string color + uuid created_by FK + } + + UNITS { + uuid id PK + string name + string abbreviation + enum unit_type + uuid base_unit_id FK + decimal conversion_factor + boolean is_default + uuid created_by FK + } + + ITEM_TAGS { + uuid item_id FK + uuid tag_id FK + } +``` + +### Key Relationships + +1. **Items โ†” Products:** Many-to-one (multiple items from same product) +2. **Items โ†” Tags:** Many-to-many (items can have multiple tags) +3. **Units โ†” Units:** Self-referential (g โ†’ kg conversion) +4. **Items โ†’ Users:** Audit trail (who added) + +--- + +## ๐Ÿ” Security Model + +### Authentication + +**Supabase Auth (GoTrue)** + +**Email/Password (Default):** +```typescript +const { user, error } = await supabase.auth.signUp({ + email: 'user@example.com', + password: 'secure-password' +}) +``` + +**OIDC (Optional - Admin Config):** +```yaml +# Admin sets via environment +SUPABASE_AUTH_GOOGLE_ENABLED=true +SUPABASE_AUTH_GOOGLE_CLIENT_ID=... +SUPABASE_AUTH_GOOGLE_SECRET=... +``` + +### Authorization + +**Row Level Security (RLS)** + +**Inventory Items:** +```sql +-- Everyone can read (shared inventory) +CREATE POLICY "items_select" ON inventory_items + FOR SELECT USING (true); + +-- Authenticated users can insert +CREATE POLICY "items_insert" ON inventory_items + FOR INSERT WITH CHECK ( + auth.uid() IS NOT NULL + ); + +-- Users can update any item (trust model) +CREATE POLICY "items_update" ON inventory_items + FOR UPDATE USING ( + auth.uid() IS NOT NULL + ); + +-- Users can delete any item +CREATE POLICY "items_delete" ON inventory_items + FOR DELETE USING ( + auth.uid() IS NOT NULL + ); +``` + +**Products (read-only for users):** +```sql +-- Everyone reads +CREATE POLICY "products_select" ON products + FOR SELECT USING (true); + +-- Only service role writes (via Edge Functions) +-- (No user-facing policy needed) +``` + +**Tags (user-managed):** +```sql +-- Default tags: everyone reads +-- Custom tags: creator manages +CREATE POLICY "tags_select" ON tags + FOR SELECT USING (true); + +CREATE POLICY "tags_insert" ON tags + FOR INSERT WITH CHECK ( + auth.uid() IS NOT NULL + ); + +CREATE POLICY "tags_update" ON tags + FOR UPDATE USING ( + created_by = auth.uid() OR created_by IS NULL + ); +``` + +--- + +## ๐Ÿ”„ Data Flow + +### 1. Barcode Scan Flow + +``` +User taps "Scan" button + โ†“ +Camera permission requested (if first time) + โ†“ +Camera opens (html5-qrcode) + โ†“ +Barcode detected (e.g., "8000500310427") + โ†“ +Frontend calls Edge Function + POST /functions/v1/product-lookup + { barcode: "8000500310427" } + โ†“ +Edge Function checks products table + โ”œโ”€ Found: return cached product + โ””โ”€ Not found: + โ†“ + Fetch from Open Food Facts API + https://world.openfoodfacts.org/api/v2/product/{barcode} + โ†“ + Parse response (name, brand, image, etc.) + โ†“ + Cache in products table + โ†“ + Return product data + โ†“ +Frontend displays product card + โ†“ +User sets quantity, tags, expiry (optional) + โ†“ +Frontend inserts into inventory_items table + โ†“ +Realtime updates other devices (WebSocket) + โ†“ +Done! +``` + +### 2. Manual Add Flow + +``` +User taps "Add Manually" + โ†“ +Form: name, quantity, unit, tags, expiry + โ†“ +Frontend validates (required: name, quantity) + โ†“ +Insert into inventory_items + โ†“ +Realtime broadcast to other devices + โ†“ +Done! +``` + +### 3. Unit Conversion Flow + +``` +User selects unit (e.g., g) + โ†“ +Item has quantity (e.g., 500g) + โ†“ +User wants to view in kg + โ†“ +Frontend calls conversion logic: + convert(500, g, kg) + โ†“ + Find g.base_unit = kg + Find g.conversion_factor = 0.001 + โ†“ + result = 500 * 0.001 = 0.5 kg + โ†“ +Display: "0.5 kg (500 g)" +``` + +--- + +## ๐ŸŒ API Design + +### Supabase Client (Direct Access) + +**Inventory CRUD:** +```typescript +// List items +const { data } = await supabase + .from('inventory_items') + .select(` + *, + product:products(*), + unit:units(*), + tags:item_tags(tag:tags(*)) + `) + .order('created_at', { ascending: false }) + +// Add item +const { data, error } = await supabase + .from('inventory_items') + .insert({ + product_id: '...', + quantity: 1, + unit_id: '...', + added_by: user.id + }) + +// Update quantity (consume) +const { data } = await supabase + .from('inventory_items') + .update({ quantity: item.quantity - 1 }) + .eq('id', itemId) + +// Delete item +const { error } = await supabase + .from('inventory_items') + .delete() + .eq('id', itemId) +``` + +### Edge Functions + +**Product Lookup:** +```typescript +// POST /functions/v1/product-lookup +// Body: { barcode: string } + +interface Request { + barcode: string +} + +interface Response { + product: Product | null + cached: boolean +} + +// Returns cached product or fetches from Open Food Facts +``` + +**Batch Lookup (future):** +```typescript +// POST /functions/v1/products/batch +// Body: { barcodes: string[] } + +// For offline sync (scan multiple, upload batch) +``` + +--- + +## ๐Ÿ“ฑ PWA Architecture + +### Service Worker Strategy + +**Network-first with cache fallback:** + +```typescript +// App shell: cache-first +self.addEventListener('fetch', (event) => { + if (event.request.url.includes('/app')) { + event.respondWith( + caches.match(event.request) + .then(cached => cached || fetch(event.request)) + ) + } +}) + +// API calls: network-first +if (event.request.url.includes('/api')) { + event.respondWith( + fetch(event.request) + .catch(() => caches.match(event.request)) + ) +} +``` + +### Offline Support + +**Offline Queue:** +```typescript +// Queue scans when offline +if (!navigator.onLine) { + await db.offlineQueue.add({ + type: 'scan', + barcode: '123456', + timestamp: Date.now() + }) +} + +// Sync when online +window.addEventListener('online', async () => { + const queue = await db.offlineQueue.getAll() + for (const item of queue) { + await processQueueItem(item) + } +}) +``` + +--- + +## ๐ŸŽจ Frontend Architecture + +### Component Structure + +``` +app/ +โ”œโ”€โ”€ components/ +โ”‚ โ”œโ”€โ”€ inventory/ +โ”‚ โ”‚ โ”œโ”€โ”€ ItemList.vue # Main inventory view +โ”‚ โ”‚ โ”œโ”€โ”€ ItemCard.vue # Single item card +โ”‚ โ”‚ โ”œโ”€โ”€ QuickActions.vue # Consume, restock buttons +โ”‚ โ”‚ โ””โ”€โ”€ FilterBar.vue # Search & filter +โ”‚ โ”œโ”€โ”€ scan/ +โ”‚ โ”‚ โ”œโ”€โ”€ BarcodeScanner.vue # Camera + detection +โ”‚ โ”‚ โ”œโ”€โ”€ ScanButton.vue # Floating action button +โ”‚ โ”‚ โ””โ”€โ”€ ProductCard.vue # After scan confirmation +โ”‚ โ”œโ”€โ”€ tags/ +โ”‚ โ”‚ โ”œโ”€โ”€ TagPicker.vue # Multi-select tags +โ”‚ โ”‚ โ”œโ”€โ”€ TagBadge.vue # Display tag +โ”‚ โ”‚ โ””โ”€โ”€ TagManager.vue # Settings: manage tags +โ”‚ โ”œโ”€โ”€ units/ +โ”‚ โ”‚ โ”œโ”€โ”€ UnitSelector.vue # Dropdown with conversions +โ”‚ โ”‚ โ””โ”€โ”€ UnitDisplay.vue # Show quantity + unit +โ”‚ โ””โ”€โ”€ common/ +โ”‚ โ”œโ”€โ”€ AppHeader.vue +โ”‚ โ”œโ”€โ”€ AppFooter.vue +โ”‚ โ””โ”€โ”€ LoadingState.vue +โ”œโ”€โ”€ composables/ +โ”‚ โ”œโ”€โ”€ useBarcode.ts # Camera, detection logic +โ”‚ โ”œโ”€โ”€ useInventory.ts # CRUD operations +โ”‚ โ”œโ”€โ”€ useUnits.ts # Conversions +โ”‚ โ”œโ”€โ”€ useTags.ts # Tag filtering +โ”‚ โ””โ”€โ”€ useOpenFoodFacts.ts # API wrapper +โ”œโ”€โ”€ pages/ +โ”‚ โ”œโ”€โ”€ index.vue # Inventory list +โ”‚ โ”œโ”€โ”€ scan.vue # Full-screen scanner +โ”‚ โ”œโ”€โ”€ item/[id].vue # Item detail/edit +โ”‚ โ””โ”€โ”€ settings.vue # Tags, units, profile +โ””โ”€โ”€ utils/ + โ”œโ”€โ”€ conversions.ts # Unit conversion math + โ”œโ”€โ”€ barcode-formats.ts # Supported formats + โ””โ”€โ”€ offline-queue.ts # Offline sync +``` + +### State Management + +**No Vuex/Pinia needed (for MVP):** +- Supabase Realtime = reactive state +- Composables for shared logic +- Props/emits for component state + +**If needed later:** +- Pinia for complex UI state (filters, etc.) + +--- + +## ๐Ÿ”ง Development Workflow + +### Local Development + +```bash +# Terminal 1: Supabase +docker-compose -f docker/docker-compose.dev.yml up + +# Terminal 2: Nuxt dev server +cd app +bun run dev + +# Access: +# - App: http://localhost:3000 +# - Supabase Studio: http://localhost:54323 +``` + +### Database Migrations + +```bash +# Create migration +supabase migration new add_tags_table + +# Apply locally +supabase db reset + +# Push to production +supabase db push +``` + +### Testing + +```bash +# Unit tests +bun test + +# E2E tests (Playwright) +bun run test:e2e + +# Type check +bun run typecheck +``` + +--- + +## ๐Ÿš€ Deployment + +### Docker Compose (Production) + +```yaml +services: + supabase: + image: supabase/postgres:15 + environment: + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + + supabase-auth: + image: supabase/gotrue + environment: + SITE_URL: ${SITE_URL} + # OIDC config (optional) + + pantry: + build: ./app + environment: + SUPABASE_URL: http://supabase:8000 + SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY} + ports: + - "3000:3000" +``` + +### Coolify Deployment + +**Service Config:** +- Type: Docker Compose +- Repo: pantry-app/pantry +- Compose File: `docker/docker-compose.yml` +- Domain: `pantry.yourdomain.com` + +--- + +## ๐Ÿ“Š Performance Considerations + +### Image Optimization +- Open Food Facts images cached locally +- Nuxt Image module for responsive images +- WebP format with fallbacks + +### Database Indexing +```sql +-- Fast barcode lookup +CREATE INDEX idx_products_barcode ON products(barcode); + +-- Fast tag filtering +CREATE INDEX idx_item_tags_tag ON item_tags(tag_id); + +-- Fast user queries +CREATE INDEX idx_items_added_by ON inventory_items(added_by); +``` + +### Realtime Optimization +- Only subscribe to user's inventory (RLS filters) +- Debounce updates (don't broadcast every keystroke) + +--- + +## ๐Ÿ”ฎ Future Considerations + +### Scalability +- **Database:** Postgres scales to millions of rows +- **Storage:** Images stored in Supabase Storage (S3-compatible) +- **Caching:** Redis for product lookup cache (if Open Food Facts is slow) + +### Multi-Tenancy (v2.0+) +- Add `household_id` to all tables +- RLS policies filter by household +- Invite system for family members + +### Native Mobile App (v2.0+) +- Capacitor wrapper around Nuxt +- Native barcode scanner (faster than PWA) +- Push notifications (expiry alerts) + +--- + +## ๐Ÿ“š References + +- [Supabase Docs](https://supabase.com/docs) +- [Nuxt 4 Docs](https://nuxt.com) +- [Open Food Facts API](https://wiki.openfoodfacts.org/API) +- [html5-qrcode](https://github.com/mebjas/html5-qrcode) + +--- + +**Next:** [Database Schema](./DATABASE.md) diff --git a/docs/PROJECT_PLAN.md b/docs/PROJECT_PLAN.md new file mode 100644 index 0000000..c335769 --- /dev/null +++ b/docs/PROJECT_PLAN.md @@ -0,0 +1,323 @@ +# Pantry - Project Plan + +**Version:** 1.0 +**Last Updated:** 2026-02-08 +**Status:** Planning + +--- + +## ๐ŸŽฏ Vision & Goals + +### Problem Statement +Kitchen inventory management is either: +- Too complex (Grocy) โ€” intimidating for casual users +- Too simple (KitchenOwl) โ€” missing critical features + +**Gap:** No self-hosted solution that's both powerful AND easy enough for the whole family. + +### Solution +**Pantry** โ€” A PWA that makes inventory management invisible: +- **Barcode scanning** reduces data entry to 3 taps +- **Tag-based** organization (no rigid categories) +- **Unit conversions** handled automatically +- **Multi-user** without household complexity + +### Success Criteria +- [ ] Non-technical family member can add items solo +- [ ] Adding via barcode takes <10 seconds +- [ ] Zero onboarding docs needed (self-explanatory UI) +- [ ] Deployed and used in production by v0.2 + +--- + +## ๐ŸŽจ Core Principles + +### 1. Family-Friendly First +**Test:** Can your parents use it without help? + +- No technical jargon in UI +- Defaults for everything +- One primary action per screen +- Forgiving (easy undo) + +### 2. Barcode-First Workflow +**Most common action should be fastest:** + +``` +Scan โ†’ Confirm โ†’ Done (3 taps) +``` + +Everything else is secondary. + +### 3. Progressive Disclosure +**Simple by default, powerful when needed:** + +- Quick add: just quantity +- Advanced: tags, expiry, custom units +- Settings: hidden until needed + +### 4. Tag Everything +**No rigid hierarchies:** + +- Position tags: fridge, freezer, pantry +- Type tags: dairy, meat, vegan +- Custom tags: meal-prep, groceries-to-buy +- Multi-tag items (dairy + fridge) + +### 5. Self-Hosted Only +**No SaaS plans, ever:** + +- One-click Docker Compose deploy +- All data stays local +- No phone-home analytics +- MIT licensed + +--- + +## ๐Ÿ—บ๏ธ Roadmap + +### MVP (v0.1) โ€” 6 weeks + +**Target:** Usable for single household + +**Core features:** +- โœ… Barcode scanning (PWA camera) +- โœ… Auto-fill from Open Food Facts +- โœ… Tag-based organization +- โœ… Unit conversions (metric defaults) +- โœ… Multi-user (email/password auth) +- โœ… PWA (installable, offline-ready) + +**Out of scope for v0.1:** +- OIDC providers (added later) +- Shopping lists +- Recipe integration +- Expiry notifications +- Mobile app (PWA only) + +### v0.2 โ€” Polish (4 weeks) +- Quick actions (consume, restock) +- Search & advanced filters +- Expiry date tracking +- Low-stock alerts +- OIDC auth (Authentik, Google) + +### v0.3 โ€” Expansion (TBD) +- Shopping list generation +- Recipe integration +- Meal planning +- Barcode printer labels +- Import/export + +### v1.0 โ€” Production-Ready (TBD) +- Full test coverage +- Performance optimization +- Mobile app (React Native/Capacitor) +- Admin dashboard +- Multi-language support + +--- + +## ๐Ÿ“… MVP Timeline (6 Weeks) + +### Week 1: Foundation +**Goal:** Core infrastructure + auth + +- [x] Create organization + monorepo +- [ ] Nuxt 4 scaffold (Tailwind + Nuxt UI) +- [ ] Supabase local setup (Docker) +- [ ] Database schema (initial) +- [ ] Email/password auth +- [ ] Deploy to development environment + +**Deliverable:** Authenticated empty app + +### Week 2: Core Inventory +**Goal:** Manual inventory management + +- [ ] Database migrations (items, tags, units) +- [ ] Seed default tags & units +- [ ] Item CRUD UI +- [ ] Tag picker component +- [ ] Unit selector with conversions +- [ ] Inventory list view + +**Deliverable:** Can add/edit items manually + +### Week 3: Barcode Scanning +**Goal:** Scan โ†’ Add workflow + +- [ ] PWA camera permissions +- [ ] Barcode scanner component (html5-qrcode) +- [ ] Open Food Facts API integration +- [ ] Product lookup & cache +- [ ] Scan UI flow +- [ ] Quick-add from scan + +**Deliverable:** Barcode scanning works end-to-end + +### Week 4: Tag System +**Goal:** Flexible organization + +- [ ] Tag management UI +- [ ] Tag categories (position, type, custom) +- [ ] Multi-tag selection +- [ ] Filter by tags +- [ ] Tag-based search +- [ ] Tag statistics + +**Deliverable:** Full tag system functional + +### Week 5: PWA & Offline +**Goal:** Mobile experience + +- [ ] PWA manifest +- [ ] Service worker (offline cache) +- [ ] Install prompt +- [ ] Mobile UI polish +- [ ] Touch gestures +- [ ] Loading states + +**Deliverable:** Installable PWA with offline support + +### Week 6: Deployment +**Goal:** Production-ready + +- [ ] Docker Compose production config +- [ ] Environment variables +- [ ] Coolify deployment guide +- [ ] Backup strategy +- [ ] Monitoring setup +- [ ] User documentation + +**Deliverable:** Running in production + +--- + +## ๐Ÿ—๏ธ Architecture Decisions + +### Monorepo Structure +**Decision:** Single repo with `/app` and `/supabase` + +**Rationale:** +- Simpler deployment (one repo clone) +- Easier version coordination +- Less overhead for solo/small team + +### Supabase Direct Access +**Decision:** Frontend talks to Supabase directly (no custom backend) + +**Rationale:** +- Supabase handles auth, RLS, realtime +- Edge functions for complex logic only +- Faster development +- Less infrastructure + +### Tag-Based Organization +**Decision:** Unified tags (no separate locations/categories) + +**Rationale:** +- More flexible (dairy + fridge + meal-prep) +- Easier to expand (just add tags) +- Simpler data model +- Better search/filter + +### Unit Conversions +**Decision:** Base units + conversion factors + +**Rationale:** +- Handles metric conversions (kg โ†” g) +- Supports custom units (can, jar) +- User-friendly (pick any unit, we convert) +- Grocy-style but simpler + +### Multi-User Without Households +**Decision:** Shared inventory, no tenant isolation + +**Rationale:** +- Simpler for single family use +- Trust model (users can edit anything) +- Avoid SaaS complexity +- Can add later if needed + +--- + +## ๐Ÿงช Testing Strategy + +### Manual Testing (v0.1) +- Playwright E2E tests (critical paths) +- Mobile testing (real devices) +- Barcode scanning (various formats) + +### Automated Testing (v0.2+) +- Unit tests (conversion logic, etc.) +- Component tests (Vue Test Utils) +- API tests (Supabase functions) + +### User Testing (v0.3+) +- Family members (non-technical) +- Real inventory data +- Feedback loop + +--- + +## ๐Ÿ“Š Success Metrics + +### MVP Launch (v0.1) +- [ ] 100+ items scanned successfully +- [ ] <10s average scan-to-add time +- [ ] Zero crashes in 1 week of use +- [ ] Positive feedback from 3+ users + +### v1.0 Goals +- 10+ active deployments (GitHub issues as proxy) +- 100+ GitHub stars +- Positive user testimonials +- No major bugs >1 week old + +--- + +## ๐Ÿšง Known Challenges + +### 1. Barcode Quality +**Problem:** Low-light, damaged labels +**Mitigation:** Fallback to manual barcode entry + +### 2. Open Food Facts Coverage +**Problem:** Not all products in database +**Mitigation:** Allow manual product creation + +### 3. Unit Conversion Edge Cases +**Problem:** Custom units (e.g., "1 can") +**Mitigation:** Store base quantity, let user define conversions + +### 4. PWA Install Friction +**Problem:** iOS Safari limitations +**Mitigation:** Clear install instructions, test on iOS + +--- + +## ๐Ÿ”„ Review & Iteration + +**Weekly Review:** +- What shipped? +- What blocked us? +- Adjust next week's scope + +**Post-MVP:** +- User feedback sessions +- GitHub issues prioritization +- Feature roadmap votes + +--- + +## ๐Ÿ“š References + +- [Architecture](./ARCHITECTURE.md) +- [Database Schema](./DATABASE.md) +- [API Reference](./API.md) +- [Development Guide](./DEVELOPMENT.md) + +--- + +**Next Steps:** Review this plan, then build [ARCHITECTURE.md](./ARCHITECTURE.md)