# Pantry - Development Guide **Version:** 1.0 **Last Updated:** 2026-02-08 --- ## ๐Ÿš€ Quick Start ### Prerequisites - **Node.js** 20+ (or Bun 1.0+) - **Docker** & Docker Compose - **Git** - **Code editor** (VS Code recommended) ### Clone & Setup ```bash # Clone repository git clone https://gitea.jeanlucmakiola.de/pantry-app/pantry.git cd pantry # Install dependencies (using Bun) bun install # Copy environment template cp .env.example .env # Start Supabase (PostgreSQL + Auth + Realtime) docker-compose -f docker/docker-compose.dev.yml up -d # Run database migrations cd supabase supabase db reset # Creates schema + seeds data # Start Nuxt dev server cd ../app bun run dev # Access app at http://localhost:3000 ``` --- ## ๐Ÿ“ Project Structure ``` pantry/ โ”œโ”€โ”€ app/ # Nuxt 4 frontend โ”‚ โ”œโ”€โ”€ components/ โ”‚ โ”‚ โ”œโ”€โ”€ inventory/ # Inventory UI components โ”‚ โ”‚ โ”œโ”€โ”€ scan/ # Barcode scanner components โ”‚ โ”‚ โ”œโ”€โ”€ tags/ # Tag management โ”‚ โ”‚ โ””โ”€โ”€ common/ # Shared UI components โ”‚ โ”œโ”€โ”€ composables/ # Shared logic (Vue Composition API) โ”‚ โ”‚ โ”œโ”€โ”€ useBarcode.ts โ”‚ โ”‚ โ”œโ”€โ”€ useInventory.ts โ”‚ โ”‚ โ””โ”€โ”€ useSupabase.ts โ”‚ โ”œโ”€โ”€ pages/ # Route pages โ”‚ โ”‚ โ”œโ”€โ”€ index.vue # Inventory list โ”‚ โ”‚ โ”œโ”€โ”€ scan.vue # Barcode scanner โ”‚ โ”‚ โ””โ”€โ”€ settings.vue # Settings โ”‚ โ”œโ”€โ”€ utils/ # Pure functions โ”‚ โ”‚ โ”œโ”€โ”€ conversions.ts # Unit conversion math โ”‚ โ”‚ โ””โ”€โ”€ validation.ts โ”‚ โ”œโ”€โ”€ nuxt.config.ts # Nuxt configuration โ”‚ โ””โ”€โ”€ package.json โ”œโ”€โ”€ supabase/ # Database & backend โ”‚ โ”œโ”€โ”€ migrations/ # SQL migrations โ”‚ โ”‚ โ”œโ”€โ”€ 001_schema.sql โ”‚ โ”‚ โ”œโ”€โ”€ 002_seed_units.sql โ”‚ โ”‚ โ””โ”€โ”€ 003_rls.sql โ”‚ โ”œโ”€โ”€ functions/ # Edge functions โ”‚ โ”‚ โ””โ”€โ”€ product-lookup/ โ”‚ โ”œโ”€โ”€ seed/ # Seed data (JSON) โ”‚ โ”‚ โ”œโ”€โ”€ units.json โ”‚ โ”‚ โ””โ”€โ”€ tags.json โ”‚ โ””โ”€โ”€ config.toml # Supabase config โ”œโ”€โ”€ docker/ # Docker configs โ”‚ โ”œโ”€โ”€ docker-compose.dev.yml # Development โ”‚ โ””โ”€โ”€ docker-compose.prod.yml # Production โ”œโ”€โ”€ docs/ # Documentation โ”œโ”€โ”€ scripts/ # Utility scripts โ”‚ โ”œโ”€โ”€ seed-db.ts โ”‚ โ””โ”€โ”€ export-schema.ts โ”œโ”€โ”€ .env.example โ”œโ”€โ”€ .gitignore โ””โ”€โ”€ README.md ``` --- ## ๐Ÿ› ๏ธ Development Workflow ### 1. Create a Feature Branch ```bash git checkout -b feature/barcode-scanner ``` ### 2. Make Changes **Add a new component:** ```bash # Create component file touch app/components/scan/BarcodeScanner.vue # Use in a page # app/pages/scan.vue ``` **Add a database migration:** ```bash cd supabase supabase migration new add_location_field # Edit supabase/migrations/XXX_add_location_field.sql ALTER TABLE inventory_items ADD COLUMN location TEXT; # Apply locally supabase db reset ``` ### 3. Test Locally ```bash # Run type check bun run typecheck # Run linter bun run lint # Run tests (when implemented) bun run test # E2E tests bun run test:e2e ``` ### 4. Commit & Push ```bash git add . git commit -m "feat: Add barcode scanner component" git push origin feature/barcode-scanner ``` ### 5. Create Pull Request - Go to Gitea: https://gitea.jeanlucmakiola.de/pantry-app/pantry - Create PR from your branch to `main` - Request review - Merge when approved --- ## ๐Ÿ“ Code Conventions ### Naming **Files:** - Components: `PascalCase.vue` (e.g., `BarcodeScanner.vue`) - Composables: `camelCase.ts` (e.g., `useBarcode.ts`) - Utils: `camelCase.ts` (e.g., `conversions.ts`) - Pages: `kebab-case.vue` (e.g., `item-detail.vue`) **Variables:** - `camelCase` for variables, functions - `PascalCase` for types, interfaces - `SCREAMING_SNAKE_CASE` for constants ```typescript // Good const itemCount = 5 const fetchProducts = () => {} interface Product { ... } const MAX_ITEMS = 100 // Bad const ItemCount = 5 const fetch_products = () => {} interface product { ... } const maxItems = 100 // for constants ``` ### Vue Component Structure ```vue ``` ### TypeScript **Prefer interfaces over types:** ```typescript // Good interface InventoryItem { id: string name: string quantity: number } // Only use type for unions, intersections type Status = 'active' | 'expired' ``` **Use strict typing:** ```typescript // Good const fetchItem = async (id: string): Promise => { const { data } = await supabase .from('inventory_items') .select('*') .eq('id', id) .single() return data } // Bad (implicit any) const fetchItem = async (id) => { const { data } = await supabase... return data } ``` ### Composables **Naming:** Always start with `use` **Structure:** ```typescript // composables/useInventory.ts export function useInventory() { const supabase = useSupabaseClient() const items = ref([]) const loading = ref(false) const fetchItems = async () => { loading.value = true const { data, error } = await supabase .from('inventory_items') .select('*') if (data) items.value = data loading.value = false } const addItem = async (item: NewInventoryItem) => { const { data, error } = await supabase .from('inventory_items') .insert(item) .select() .single() if (data) items.value.push(data) return { data, error } } // Return reactive state + methods return { items: readonly(items), loading: readonly(loading), fetchItems, addItem } } ``` ### Database Migrations **Naming:** ``` 001_initial_schema.sql 002_seed_defaults.sql 003_add_location_field.sql ``` **Structure:** ```sql -- Migration: Add location field to inventory items -- Created: 2026-02-08 BEGIN; ALTER TABLE inventory_items ADD COLUMN location TEXT; -- Update existing items (optional) UPDATE inventory_items SET location = 'Pantry' WHERE location IS NULL; COMMIT; ``` **Rollback (optional):** ```sql -- To rollback: -- ALTER TABLE inventory_items DROP COLUMN location; ``` --- ## ๐Ÿงช Testing ### Unit Tests (Vitest) ```typescript // app/utils/conversions.test.ts import { describe, it, expect } from 'vitest' import { convertUnit } from './conversions' describe('convertUnit', () => { it('converts grams to kilograms', () => { const result = convertUnit(500, { conversion_factor: 0.001, base_unit_id: 'kg' }, { conversion_factor: 1, base_unit_id: null }) expect(result).toBe(0.5) }) it('throws error for incompatible units', () => { expect(() => { convertUnit(1, { unit_type: 'weight', conversion_factor: 1 }, { unit_type: 'volume', conversion_factor: 1 } ) }).toThrow('Cannot convert') }) }) ``` **Run tests:** ```bash bun test bun test --watch # Watch mode ``` ### E2E Tests (Playwright) ```typescript // tests/e2e/inventory.spec.ts import { test, expect } from '@playwright/test' test('can add item to inventory', async ({ page }) => { await page.goto('/') // Login await page.fill('[name=email]', 'test@example.com') await page.fill('[name=password]', 'password') await page.click('button[type=submit]') // Add item await page.click('[data-testid=add-item]') await page.fill('[name=name]', 'Test Item') await page.fill('[name=quantity]', '2') await page.click('button:has-text("Add")') // Verify await expect(page.locator('text=Test Item')).toBeVisible() }) ``` **Run E2E:** ```bash bun run test:e2e bun run test:e2e --ui # Interactive mode ``` --- ## ๐Ÿ”ง Supabase Local Development ### Start Supabase ```bash cd supabase supabase start ``` **Outputs:** ``` API URL: http://localhost:54321 Studio URL: http://localhost:54323 Anon key: eyJhb... Service key: eyJhb... ``` **Access Supabase Studio:** - Open http://localhost:54323 - View tables, run queries, manage auth ### Apply Migrations ```bash # Reset DB (drops all data, reapplies migrations) supabase db reset # Create new migration supabase migration new add_feature # Apply migrations (non-destructive) supabase migration up ``` ### Generate Types ```bash # Generate TypeScript types from database schema supabase gen types typescript --local > app/types/supabase.ts ``` **Usage:** ```typescript import type { Database } from '~/types/supabase' const supabase = useSupabaseClient() ``` --- ## ๐ŸŒ Environment Variables ### `.env` (Development) ```bash # Supabase (from `supabase start`) SUPABASE_URL=http://localhost:54321 SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... # App PUBLIC_APP_URL=http://localhost:3000 # Open Food Facts (optional, no key needed) OPENFOODFACTS_API_URL=https://world.openfoodfacts.org ``` ### `.env.production` ```bash # Supabase (production) SUPABASE_URL=https://your-project.supabase.co SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... # App PUBLIC_APP_URL=https://pantry.yourdomain.com ``` --- ## ๐Ÿ› Debugging ### Nuxt DevTools **Enable:** ```typescript // nuxt.config.ts export default defineNuxtConfig({ devtools: { enabled: true } }) ``` **Access:** Press `Shift + Alt + D` or visit http://localhost:3000/__devtools__ ### Vue DevTools Install browser extension: - Chrome: [Vue DevTools](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) - Firefox: [Vue DevTools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/) ### Supabase Logs ```bash # View realtime logs supabase logs --tail # Filter by service supabase logs --service postgres supabase logs --service auth ``` ### Database Queries **Supabase Studio:** - Open http://localhost:54323 - Go to "SQL Editor" - Run queries directly **CLI:** ```bash # psql into local database supabase db shell # Run query SELECT * FROM inventory_items LIMIT 5; ``` --- ## ๐Ÿ“ฆ Build & Preview ### Build for Production ```bash cd app bun run build ``` **Output:** `.output/` directory (ready for deployment) ### Preview Production Build ```bash bun run preview ``` **Access:** http://localhost:3000 --- ## ๐Ÿ”„ Git Workflow ### Branch Naming ``` feature/barcode-scanner fix/tag-duplication-bug chore/update-dependencies docs/api-reference ``` ### Commit Messages (Conventional Commits) ```bash # Format: (): feat(scan): add barcode detection fix(inventory): prevent duplicate items chore(deps): update Nuxt to 4.1 docs(api): add product lookup endpoint refactor(tags): simplify tag picker logic test(units): add conversion edge cases style(ui): apply Tailwind spacing ``` **Types:** - `feat`: New feature - `fix`: Bug fix - `chore`: Maintenance (deps, config) - `docs`: Documentation - `refactor`: Code restructuring - `test`: Adding/updating tests - `style`: Code style (formatting, no logic change) --- ## ๐Ÿšจ Common Issues ### Supabase won't start ```bash # Check Docker docker ps # Restart Supabase supabase stop supabase start ``` ### Types out of sync ```bash # Regenerate types after schema change supabase gen types typescript --local > app/types/supabase.ts ``` ### Port already in use ```bash # Nuxt (3000) lsof -ti:3000 | xargs kill # Supabase (54321) supabase stop ``` --- ## ๐Ÿ“š Resources **Docs:** - [Nuxt 4](https://nuxt.com) - [Supabase](https://supabase.com/docs) - [Vue 3](https://vuejs.org) - [Tailwind CSS](https://tailwindcss.com) **Community:** - [Pantry Discussions](https://gitea.jeanlucmakiola.de/pantry-app/pantry/issues) - [Nuxt Discord](https://discord.com/invite/ps2h6QT) - [Supabase Discord](https://discord.supabase.com) --- **Next:** [Deployment Guide](./DEPLOYMENT.md)