feat: Buddy System & Bulk Scanner

- 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
This commit is contained in:
2025-12-25 22:11:50 +01:00
parent afe9197776
commit 75461d7c30
22 changed files with 2050 additions and 146 deletions

View File

@@ -38,6 +38,7 @@ CREATE TABLE IF NOT EXISTS bottles (
abv DECIMAL,
age INTEGER,
status TEXT DEFAULT 'sealed' CHECK (status IN ('sealed', 'open', 'sampled', 'empty')),
processing_status TEXT DEFAULT 'complete' CHECK (processing_status IN ('pending', 'analyzing', 'complete', 'error')),
whiskybase_id TEXT,
image_url TEXT,
purchase_price DECIMAL(10, 2),
@@ -415,3 +416,32 @@ SELECT
(SELECT id FROM subscription_plans WHERE name = 'starter' LIMIT 1)
FROM auth.users u
ON CONFLICT (user_id) DO NOTHING;
-- ============================================
-- Buddy Invites (Handshake Codes)
-- ============================================
CREATE TABLE IF NOT EXISTS buddy_invites (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
creator_id UUID REFERENCES profiles(id) ON DELETE CASCADE NOT NULL,
code TEXT NOT NULL UNIQUE, -- 6 char uppercase alphanumeric
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('Europe/Berlin'::text, now())
);
CREATE INDEX IF NOT EXISTS idx_buddy_invites_code ON buddy_invites(code);
CREATE INDEX IF NOT EXISTS idx_buddy_invites_creator_id ON buddy_invites(creator_id);
CREATE INDEX IF NOT EXISTS idx_buddy_invites_expires_at ON buddy_invites(expires_at);
ALTER TABLE buddy_invites ENABLE ROW LEVEL SECURITY;
-- Only creator can see their own invites
DROP POLICY IF EXISTS "buddy_invites_creator_policy" ON buddy_invites;
CREATE POLICY "buddy_invites_creator_policy" ON buddy_invites
FOR ALL USING ((SELECT auth.uid()) = creator_id);
-- Allow anyone to SELECT by code (needed for redemption) but only if not expired
DROP POLICY IF EXISTS "buddy_invites_redeem_policy" ON buddy_invites;
CREATE POLICY "buddy_invites_redeem_policy" ON buddy_invites
FOR SELECT USING (expires_at > now());