diff --git a/public/sw.js b/public/sw.js
index 71feb1e..52d3c5f 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -1,6 +1,5 @@
-const CACHE_NAME = 'whisky-vault-v1';
+const CACHE_NAME = 'whisky-vault-v2'; // Increment version to force update
const ASSETS_TO_CACHE = [
- '/',
'/manifest.json',
'/icon-192.png',
'/icon-512.png',
@@ -27,13 +26,36 @@ self.addEventListener('activate', (event) => {
);
})
);
+ self.clients.claim();
});
self.addEventListener('fetch', (event) => {
- // Network first, fallback to cache
+ const url = new URL(event.request.url);
+
+ // CRITICAL: Always bypass cache for auth, api, and supabase requests
+ if (
+ url.pathname.includes('/auth/') ||
+ url.pathname.includes('/api/') ||
+ url.hostname.includes('supabase.co')
+ ) {
+ return; // Let it fall through to the network
+ }
+
+ // Network first for all other requests, especially navigation
event.respondWith(
fetch(event.request)
.then((response) => {
+ // Optionally cache successful GET requests for assets
+ if (
+ event.request.method === 'GET' &&
+ response.status === 200 &&
+ (url.pathname.startsWith('/_next/static/') || ASSETS_TO_CACHE.includes(url.pathname))
+ ) {
+ const responseClone = response.clone();
+ caches.open(CACHE_NAME).then((cache) => {
+ cache.put(event.request, responseClone);
+ });
+ }
return response;
})
.catch(() => {
diff --git a/src/app/bottles/[id]/page.tsx b/src/app/bottles/[id]/page.tsx
index 69bc618..49e6d40 100644
--- a/src/app/bottles/[id]/page.tsx
+++ b/src/app/bottles/[id]/page.tsx
@@ -28,6 +28,7 @@ export default async function BottlePage({
}
}
const supabase = createServerComponentClient({ cookies });
+ const { data: { user } } = await supabase.auth.getUser();
const { data: bottle } = await supabase
.from('bottles')
@@ -187,7 +188,7 @@ export default async function BottlePage({
{/* List */}
-
+
diff --git a/src/app/page.tsx b/src/app/page.tsx
index e379467..77c2b6d 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -25,16 +25,22 @@ export default function Home() {
const checkUser = async () => {
try {
const { data: { session }, error } = await supabase.auth.getSession();
- console.log('Initial session check:', session ? 'User logged in' : 'No session');
+ console.log('[Auth] Initial session check:', {
+ hasSession: !!session,
+ userId: session?.user?.id,
+ email: session?.user?.email,
+ error: error?.message
+ });
+
if (error) {
- console.error('Session retrieval error:', error);
+ console.error('[Auth] Session retrieval error:', error);
}
setUser(session?.user ?? null);
if (session?.user) {
fetchCollection();
}
} catch (err) {
- console.error('Fatal error checking user:', err);
+ console.error('[Auth] Fatal error checking user:', err);
setUser(null);
} finally {
setIsLoading(false);
@@ -45,10 +51,17 @@ export default function Home() {
// 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');
+ console.log('[Auth] State change event:', event, {
+ hasSession: !!session,
+ userId: session?.user?.id,
+ email: session?.user?.email
+ });
+
setUser(session?.user ?? null);
if (session?.user) {
- fetchCollection();
+ if (event === 'SIGNED_IN' || event === 'INITIAL_SESSION' || event === 'TOKEN_REFRESHED') {
+ fetchCollection();
+ }
} else {
setBottles([]);
}
diff --git a/src/app/sessions/[id]/page.tsx b/src/app/sessions/[id]/page.tsx
index beba47d..610aa16 100644
--- a/src/app/sessions/[id]/page.tsx
+++ b/src/app/sessions/[id]/page.tsx
@@ -4,6 +4,7 @@ import React, { useState, useEffect } from 'react';
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
import { ChevronLeft, Users, Calendar, GlassWater, Plus, Trash2, Loader2, Sparkles, ChevronRight, Play, Square } from 'lucide-react';
import Link from 'next/link';
+import { deleteSession } from '@/services/delete-session';
import { useSession } from '@/context/SessionContext';
import { useParams, useRouter } from 'next/navigation';
import { useI18n } from '@/i18n/I18nContext';
@@ -48,6 +49,7 @@ export default function SessionDetailPage() {
const [isLoading, setIsLoading] = useState(true);
const { activeSession, setActiveSession } = useSession();
const [isAddingParticipant, setIsAddingParticipant] = useState(false);
+ const [isDeleting, setIsDeleting] = useState(false);
useEffect(() => {
fetchSessionData();
@@ -131,6 +133,23 @@ export default function SessionDetailPage() {
}
};
+ const handleDeleteSession = async () => {
+ if (!confirm('Möchtest du diese Session wirklich löschen? Alle Verknüpfungen gehen verloren.')) return;
+
+ setIsDeleting(true);
+ const result = await deleteSession(id as string);
+
+ if (result.success) {
+ if (activeSession?.id === id) {
+ setActiveSession(null);
+ }
+ router.push('/');
+ } else {
+ alert(result.error);
+ setIsDeleting(false);
+ }
+ };
+
if (isLoading) {
return (
@@ -206,6 +225,15 @@ export default function SessionDetailPage() {
Session Stoppen
)}
+
+
diff --git a/src/components/SessionList.tsx b/src/components/SessionList.tsx
index 62d77eb..222577f 100644
--- a/src/components/SessionList.tsx
+++ b/src/components/SessionList.tsx
@@ -2,8 +2,9 @@
import React, { useState, useEffect } from 'react';
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
-import { Calendar, Plus, GlassWater, Loader2, ChevronRight, Users, Check } from 'lucide-react';
+import { Calendar, Plus, GlassWater, Loader2, ChevronRight, Users, Check, Trash2 } from 'lucide-react';
import Link from 'next/link';
+import { deleteSession } from '@/services/delete-session';
import { useI18n } from '@/i18n/I18nContext';
import { useSession } from '@/context/SessionContext';
@@ -21,6 +22,7 @@ export default function SessionList() {
const [sessions, setSessions] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [isCreating, setIsCreating] = useState(false);
+ const [isDeleting, setIsDeleting] = useState(null);
const [newName, setNewName] = useState('');
const { activeSession, setActiveSession } = useSession();
@@ -92,6 +94,26 @@ export default function SessionList() {
setIsCreating(false);
};
+ const handleDeleteSession = async (e: React.MouseEvent, sessionId: string) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (!confirm('Möchtest du diese Session wirklich löschen? Alle Verknüpfungen gehen verloren.')) return;
+
+ setIsDeleting(sessionId);
+ const result = await deleteSession(sessionId);
+
+ if (result.success) {
+ setSessions(prev => prev.filter(s => s.id !== sessionId));
+ if (activeSession?.id === sessionId) {
+ setActiveSession(null);
+ }
+ } else {
+ alert(result.error);
+ }
+ setIsDeleting(null);
+ };
+
return (
@@ -172,6 +194,21 @@ export default function SessionList() {
)}
+
))
diff --git a/src/components/TastingList.tsx b/src/components/TastingList.tsx
index 4d4ae48..faaeba3 100644
--- a/src/components/TastingList.tsx
+++ b/src/components/TastingList.tsx
@@ -24,13 +24,15 @@ interface Tasting {
id: string;
name: string;
};
+ user_id: string;
}
interface TastingListProps {
initialTastings: Tasting[];
+ currentUserId?: string;
}
-export default function TastingList({ initialTastings }: TastingListProps) {
+export default function TastingList({ initialTastings, currentUserId }: TastingListProps) {
const [sortBy, setSortBy] = useState<'date-desc' | 'date-asc' | 'rating-desc' | 'rating-asc'>('date-desc');
const [isDeleting, setIsDeleting] = useState(null);
@@ -100,8 +102,8 @@ export default function TastingList({ initialTastings }: TastingListProps) {
key={note.id}
className="bg-white dark:bg-zinc-900 p-6 rounded-3xl border border-zinc-200 dark:border-zinc-800 shadow-sm space-y-4 hover:border-amber-500/30 transition-all hover:shadow-md group"
>
-
-
+
+
{note.rating}/100
@@ -113,38 +115,41 @@ export default function TastingList({ initialTastings }: TastingListProps) {
{note.is_sample ? 'Sample' : 'Bottle'}
+
{new Date(note.created_at).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}
{note.tasting_sessions && (
{note.tasting_sessions.name}
)}
-
+
{new Date(note.created_at).toLocaleDateString('de-DE')}
-
+ {(!currentUserId || note.user_id === currentUserId) && (
+
+ )}
diff --git a/src/middleware.ts b/src/middleware.ts
index d2d84c5..e9e31f0 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -16,10 +16,11 @@ export async function middleware(req: NextRequest) {
const { data: { session } } = await supabase.auth.getSession();
if (!isStatic) {
- console.log(`[Middleware] Path: ${url.pathname}, Session: ${session ? 'Active' : 'Missing'}, User: ${session?.user?.id || 'N/A'}`);
+ const status = session ? `User:${session.user.id.slice(0, 8)}` : 'No Session';
+ console.log(`[MW] ${req.method} ${url.pathname} | ${status}`);
}
} catch (e) {
- console.error('Middleware session refresh failed:', e);
+ console.error('[MW] Auth Error:', e);
}
}
diff --git a/src/services/delete-session.ts b/src/services/delete-session.ts
new file mode 100644
index 0000000..175638a
--- /dev/null
+++ b/src/services/delete-session.ts
@@ -0,0 +1,35 @@
+'use server';
+
+import { createServerActionClient } from '@supabase/auth-helpers-nextjs';
+import { cookies } from 'next/headers';
+import { revalidatePath } from 'next/cache';
+
+export async function deleteSession(sessionId: string) {
+ const supabase = createServerActionClient({ cookies });
+
+ try {
+ const { data: { session } } = await supabase.auth.getSession();
+ if (!session) {
+ throw new Error('Nicht autorisiert.');
+ }
+
+ const { error: deleteError } = await supabase
+ .from('tasting_sessions')
+ .delete()
+ .eq('id', sessionId)
+ .eq('user_id', session.user.id);
+
+ if (deleteError) throw deleteError;
+
+ revalidatePath('/');
+ revalidatePath(`/sessions/${sessionId}`);
+
+ return { success: true };
+ } catch (error) {
+ console.error('Delete Session Error:', error);
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : 'Fehler beim Löschen der Session.',
+ };
+ }
+}