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

@@ -2,9 +2,10 @@
import React, { useState, useEffect } from 'react';
import { createClient } from '@/lib/supabase/client';
import { Users, UserPlus, Trash2, User, Loader2, ChevronDown, ChevronUp } from 'lucide-react';
import { Users, UserPlus, Trash2, Loader2, ChevronDown, ChevronUp, Link2 } from 'lucide-react';
import { useI18n } from '@/i18n/I18nContext';
import { addBuddy, deleteBuddy } from '@/services/buddy';
import BuddyHandshake from './BuddyHandshake';
interface Buddy {
id: string;
@@ -25,6 +26,7 @@ export default function BuddyList() {
}
return false;
});
const [isHandshakeOpen, setIsHandshakeOpen] = useState(false);
useEffect(() => {
fetchBuddies();
@@ -117,6 +119,17 @@ export default function BuddyList() {
</button>
</form>
{/* Link Account Button */}
<button
onClick={() => setIsHandshakeOpen(true)}
className="w-full mb-6 py-3 bg-zinc-950 hover:bg-zinc-800 border border-zinc-800 hover:border-orange-600/50 rounded-2xl transition-all flex items-center justify-center gap-2 group"
>
<Link2 size={16} className="text-orange-600" />
<span className="text-xs font-bold uppercase tracking-widest text-zinc-400 group-hover:text-orange-500 transition-colors">
Account verbinden
</span>
</button>
{isLoading ? (
<div className="flex justify-center py-8 text-zinc-500">
<Loader2 size={24} className="animate-spin" />
@@ -173,6 +186,16 @@ export default function BuddyList() {
<span className="text-[10px] text-zinc-500 font-bold uppercase tracking-widest ml-1">{buddies.length} Buddies</span>
</div>
)}
{/* Buddy Handshake Dialog */}
<BuddyHandshake
isOpen={isHandshakeOpen}
onClose={() => setIsHandshakeOpen(false)}
onSuccess={() => {
setIsHandshakeOpen(false);
fetchBuddies();
}}
/>
</div>
);
}