This commit is contained in:
2025-12-17 23:12:53 +01:00
commit 5807d949ef
323 changed files with 34158 additions and 0 deletions

135
src/app/page.tsx Normal file
View File

@@ -0,0 +1,135 @@
'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";
export default function Home() {
const supabase = createClientComponentClient();
const [bottles, setBottles] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [user, setUser] = useState<any>(null); // Added user state
useEffect(() => {
// Check session
const checkUser = async () => {
const { data: { session } } = await supabase.auth.getSession();
setUser(session?.user ?? null);
if (session?.user) {
fetchCollection();
} else {
setIsLoading(false);
}
};
checkUser();
// Listen for auth changes
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
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) throw error;
// 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) {
console.error('Error fetching collection:', 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-12 p-6 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>
<CameraCapture onSaveComplete={fetchCollection} />
<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>
) : (
<BottleGrid bottles={bottles} />
)}
</div>
</div>
</main>
);
}