174 lines
7.0 KiB
TypeScript
174 lines
7.0 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
|
|
import CameraCapture from "@/components/CameraCapture";
|
|
import BottleGrid from "@/components/BottleGrid";
|
|
import AuthForm from "@/components/AuthForm";
|
|
import BuddyList from "@/components/BuddyList";
|
|
import SessionList from "@/components/SessionList";
|
|
|
|
export default function Home() {
|
|
const supabase = createClientComponentClient();
|
|
const [bottles, setBottles] = useState<any[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [user, setUser] = useState<any>(null);
|
|
const [fetchError, setFetchError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
// Check session
|
|
const checkUser = async () => {
|
|
try {
|
|
const { data: { session }, error } = await supabase.auth.getSession();
|
|
console.log('Initial session check:', session ? 'User logged in' : 'No session');
|
|
if (error) {
|
|
console.error('Session retrieval error:', error);
|
|
}
|
|
setUser(session?.user ?? null);
|
|
if (session?.user) {
|
|
fetchCollection();
|
|
}
|
|
} catch (err) {
|
|
console.error('Fatal error checking user:', err);
|
|
setUser(null);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
checkUser();
|
|
|
|
// Listen for auth changes
|
|
const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
|
|
console.log('Auth state change:', event, session?.user ? 'User logged in' : 'No user');
|
|
setUser(session?.user ?? null);
|
|
if (session?.user) {
|
|
fetchCollection();
|
|
} else {
|
|
setBottles([]);
|
|
}
|
|
});
|
|
|
|
return () => subscription.unsubscribe();
|
|
}, []);
|
|
|
|
const fetchCollection = async () => {
|
|
setIsLoading(true);
|
|
try {
|
|
// Fetch bottles with their latest tasting date
|
|
const { data, error } = await supabase
|
|
.from('bottles')
|
|
.select(`
|
|
*,
|
|
tastings (
|
|
created_at
|
|
)
|
|
`)
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) {
|
|
console.error('Supabase fetch error:', error);
|
|
throw error;
|
|
}
|
|
|
|
console.log(`Fetched ${data?.length || 0} bottles from Supabase`);
|
|
|
|
// Process data to get the absolute latest tasting date for each bottle
|
|
const processedBottles = (data || []).map(bottle => {
|
|
const lastTasted = bottle.tastings && bottle.tastings.length > 0
|
|
? bottle.tastings.reduce((latest: string, current: any) =>
|
|
new Date(current.created_at) > new Date(latest) ? current.created_at : latest,
|
|
bottle.tastings[0].created_at
|
|
)
|
|
: null;
|
|
|
|
return {
|
|
...bottle,
|
|
last_tasted: lastTasted
|
|
};
|
|
});
|
|
|
|
setBottles(processedBottles);
|
|
} catch (err: any) {
|
|
console.error('Detailed fetch error:', err);
|
|
setFetchError(err.message || JSON.stringify(err));
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleLogout = async () => {
|
|
await supabase.auth.signOut();
|
|
};
|
|
|
|
if (!user) {
|
|
return (
|
|
<main className="flex min-h-screen flex-col items-center justify-center p-6 bg-zinc-50 dark:bg-black">
|
|
<div className="mb-12 text-center">
|
|
<h1 className="text-5xl font-black text-zinc-900 dark:text-white tracking-tighter mb-4">
|
|
WHISKY<span className="text-amber-600">VAULT</span>
|
|
</h1>
|
|
<p className="text-zinc-500 max-w-sm mx-auto">Scanne deine Flaschen, tracke deine Tastings und verwalte deinen Keller.</p>
|
|
</div>
|
|
<AuthForm />
|
|
</main>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<main className="flex min-h-screen flex-col items-center gap-6 md:gap-12 p-4 md:p-24 bg-zinc-50 dark:bg-black">
|
|
<div className="z-10 max-w-5xl w-full flex flex-col items-center gap-8">
|
|
<header className="w-full flex justify-between items-center">
|
|
<h1 className="text-4xl font-black text-zinc-900 dark:text-white tracking-tighter">
|
|
WHISKY<span className="text-amber-600">VAULT</span>
|
|
</h1>
|
|
<button
|
|
onClick={handleLogout}
|
|
className="text-sm font-medium text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-300 transition-colors"
|
|
>
|
|
Abmelden
|
|
</button>
|
|
</header>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 w-full max-w-5xl">
|
|
<div className="flex flex-col gap-8">
|
|
<CameraCapture onSaveComplete={fetchCollection} />
|
|
<SessionList />
|
|
</div>
|
|
<div>
|
|
<BuddyList />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="w-full mt-12">
|
|
<h2 className="text-2xl font-bold mb-6 text-zinc-800 dark:text-zinc-100 flex items-center gap-3">
|
|
Deine Sammlung
|
|
<span className="text-sm font-normal text-zinc-500 bg-zinc-100 dark:bg-zinc-800 px-3 py-1 rounded-full">
|
|
{bottles.length}
|
|
</span>
|
|
</h2>
|
|
|
|
{isLoading ? (
|
|
<div className="flex justify-center py-12">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-amber-600"></div>
|
|
</div>
|
|
) : fetchError ? (
|
|
<div className="p-8 bg-zinc-100 dark:bg-zinc-900/50 border border-zinc-200 dark:border-zinc-800 rounded-3xl text-center">
|
|
<p className="text-zinc-800 dark:text-zinc-200 font-bold mb-2">Sammlung konnte nicht geladen werden</p>
|
|
<p className="text-zinc-500 text-sm italic mb-4">Möglicherweise müssen die Datenbank-Regeln aktualisiert werden.</p>
|
|
<button
|
|
onClick={fetchCollection}
|
|
className="px-6 py-2 bg-amber-600 hover:bg-amber-700 text-white rounded-xl text-xs font-bold uppercase tracking-widest transition-all"
|
|
>
|
|
Erneut versuchen
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<BottleGrid bottles={bottles} />
|
|
)}
|
|
</div>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|