New trigger creates:
- Profile (username from metadata)
- Subscription (starter plan by default)
- Credits (from plan's monthly_credits, not hardcoded)
Includes RLS policies for self-insert fallback
- Simplified trigger to only create profile
- Added exception handling to prevent user creation failures
- Subscription created client-side after successful signup
New component in header shows:
- Subscription plan badge (Starter/Bronze/Silver/Gold with icons)
- AI credits balance with sparkle icon
Also includes SQL migration for user_subscriptions RLS fix
Registration form now includes:
- Username field (required, unique, validated)
- Full name field (optional)
- Auto-validates username format and availability
- Auto-creates 'starter' subscription on signup
Login form unchanged (email + password only)
New: src/lib/distillery-matcher.ts
- normalizeDistillery(): Fuzzy matches AI responses against distilleries.json
- cleanBottleName(): Removes distillery from bottle name to avoid duplication
- normalizeWhiskyData(): Combined helper for both operations
Example transformations:
- 'ARDNAHOE DISTILLERY CO LTD' → 'Ardnahoe'
- 'Laphroaig 10 Year Old' → '10 Year Old' (with distillery in separate field)
Integration:
- gemini-vision.ts now normalizes results after AI response
- Enables consistent distillery names for enrichment cache
Caches AI enrichment results per distillery to save API calls:
- New table: enrichment_cache (distillery, tags, hit_count)
- New service: cache-enrichment.ts (get, save, increment, stats)
- enrich-data.ts checks cache before AI query
- Saves to cache after successful AI response
- Returns cached: true/false flag for transparency
Benefits:
- 0 API cost for repeated distillery scans
- Near-instant response for cached distilleries
- Shared across all users
- Email displayed as user[at]domain with onClick handler
- Phone displayed split with dashes
- All contact info clickable but not easily scraped
- Ready to fill in at src/app/impressum/page.tsx
Onboarding Tutorial:
- 5-step walkthrough for new users
- Welcome, Scan, Taste, Sessions, Ready steps
- Skippable, stores completion in localStorage
- Beautiful full-screen overlay with animations
Empty States:
- SessionList: Visual empty state with icon and description
- BuddyList: Visual empty state with icon and description
- Reusable EmptyState component ready for more usage
Layout: Added OnboardingTutorial and CookieBanner
- Updated profile-actions.ts to use username column
- Updated ProfileForm.tsx to use username
- Updated settings page to pass username
- Matches database schema (profiles.username)
Cookie Banner:
- GDPR-compliant consent banner
- Shows on first visit, stores consent in localStorage
- Two options: Accept All / Only Essential
- Dark theme with smooth animations
Settings Page (/settings):
- Profile form: display name editing
- Password change with validation
- Cookie settings info section
- Privacy links and account info
New files:
- src/hooks/useCookieConsent.ts
- src/components/CookieBanner.tsx
- src/components/ProfileForm.tsx
- src/components/PasswordChangeForm.tsx
- src/services/profile-actions.ts
- src/app/settings/page.tsx
- bulk-scan.ts: Now uses OpenRouter for batch analysis
- scan-label.ts: Now uses OpenRouter with Gemini fallback
- analyze-bottle.ts: Now uses OpenRouter with Gemini fallback
- All AI calls now respect AI_PROVIDER env variable
- Uses Nebius/FP8 provider preferences consistently
- Unified logging: [FunctionName] Using provider: openrouter/gemini
- enrichData now uses same AI provider switch as vision
- Both scan and enrichment use OpenRouter by default
- Added retry logic for rate limits
- Uses Nebius/FP8 provider preferences
- Logs which provider is being used
- Added OPENROUTER_PROVIDER_PREFERENCES config
- Prioritizes Nebius provider for better availability/speed
- Uses FP8 quantization for quality/speed balance
- Falls back to other providers if Nebius unavailable
- Retries up to 3 times with exponential backoff (2s, 4s, 8s)
- Also handles 503 service unavailable errors
- Logs retry attempts for debugging
- Only retries rate limit errors, other errors fail immediately
- Editor opens immediately with placeholder data
- AI analyzes label in background while user can already edit
- Blue 'KI analysiert...' banner shows when AI is still working
- User edits preserved when AI results arrive (dirty field tracking)
- Model/provider shown in admin perf overlay
- Renamed 'CLOUD' to 'AI' in perf display
- Added openrouter.ts with provider configuration
- Default provider: OpenRouter with google/gemma-3-27b-it
- Switch to Gemini via AI_PROVIDER=gemini in .env.local
- Both providers use same credit system
- OpenRouter uses OpenAI-compatible API
To switch providers, set in .env.local:
AI_PROVIDER=openrouter # default
AI_PROVIDER=gemini # Google Gemini
- Removed Tesseract.js files from precache (~45MB)
- Scanner now uses only Gemini AI (more accurate, less data)
- Offline scans queued for later processing when online
- App download from ~50MB to ~5MB
BREAKING: Local offline OCR no longer available
Use Gemini AI instead (requires network for scanning)
- Use createBrowserClient from @supabase/ssr instead of supabase-js
- Stores auth session in cookies (not localStorage) for SSR compatibility
- Server actions can now access authenticated user sessions
- Fixes 'Nicht autorisiert' error in split creation
- Add 'Split starten' button to bottle detail page
- Support flexible sample sizes (1-20cl) instead of fixed 5/10cl
- Preselect bottle when coming from bottle page
- Save sample sizes/shipping defaults to localStorage
- Update schema: sample_sizes JSONB array
- Update server actions and UI for dynamic sizes
- Add Buddy linking via QR code/handshake (buddy_invites table)
- Add Bulk Scanner for rapid-fire bottle scanning in sessions
- Add processing_status to bottles for background AI analysis
- Fix offline OCR with proper tessdata caching in Service Worker
- Fix Supabase GoTrueClient singleton warning
- Add collection refresh after offline sync completes
New components:
- BuddyHandshake.tsx - QR code display and code entry
- BulkScanSheet.tsx - Camera UI with capture queue
- BottleSkeletonCard.tsx - Pending bottle display
- useBulkScanner.ts - Queue management hook
- buddy-link.ts - Server actions for buddy linking
- bulk-scan.ts - Server actions for batch processing
- Renamed app to DramLog and updated branding to Gold (#C89D46)
- Implemented new BottomNavigation with Floating Scan Button
- Fixed 'black screen' race condition in ScanAndTasteFlow
- Refactored TastingEditor and StatsDashboard for a cleaner editorial look
- Standardized colors and typography across the application
- Reverted theme from gold to amber and restored legacy typography.
- Refactored ScanAndTasteFlow and TastingEditor for robust desktop scrolling.
- Hotfixed sw.js to completely bypass Supabase Auth/API requests to fix 'Failed to fetch' in production.
- Integrated full tasting note persistence (tags, buddies, sessions).