feat: session deletion, improved tasting deletion visibility, and PWA login loop fix
This commit is contained in:
28
public/sw.js
28
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 = [
|
const ASSETS_TO_CACHE = [
|
||||||
'/',
|
|
||||||
'/manifest.json',
|
'/manifest.json',
|
||||||
'/icon-192.png',
|
'/icon-192.png',
|
||||||
'/icon-512.png',
|
'/icon-512.png',
|
||||||
@@ -27,13 +26,36 @@ self.addEventListener('activate', (event) => {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
self.clients.claim();
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('fetch', (event) => {
|
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(
|
event.respondWith(
|
||||||
fetch(event.request)
|
fetch(event.request)
|
||||||
.then((response) => {
|
.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;
|
return response;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export default async function BottlePage({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const supabase = createServerComponentClient({ cookies });
|
const supabase = createServerComponentClient({ cookies });
|
||||||
|
const { data: { user } } = await supabase.auth.getUser();
|
||||||
|
|
||||||
const { data: bottle } = await supabase
|
const { data: bottle } = await supabase
|
||||||
.from('bottles')
|
.from('bottles')
|
||||||
@@ -187,7 +188,7 @@ export default async function BottlePage({
|
|||||||
|
|
||||||
{/* List */}
|
{/* List */}
|
||||||
<div className="lg:col-span-2">
|
<div className="lg:col-span-2">
|
||||||
<TastingList initialTastings={tastings || []} />
|
<TastingList initialTastings={tastings || []} currentUserId={user?.id} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -25,16 +25,22 @@ export default function Home() {
|
|||||||
const checkUser = async () => {
|
const checkUser = async () => {
|
||||||
try {
|
try {
|
||||||
const { data: { session }, error } = await supabase.auth.getSession();
|
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) {
|
if (error) {
|
||||||
console.error('Session retrieval error:', error);
|
console.error('[Auth] Session retrieval error:', error);
|
||||||
}
|
}
|
||||||
setUser(session?.user ?? null);
|
setUser(session?.user ?? null);
|
||||||
if (session?.user) {
|
if (session?.user) {
|
||||||
fetchCollection();
|
fetchCollection();
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Fatal error checking user:', err);
|
console.error('[Auth] Fatal error checking user:', err);
|
||||||
setUser(null);
|
setUser(null);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@@ -45,10 +51,17 @@ export default function Home() {
|
|||||||
|
|
||||||
// Listen for auth changes
|
// Listen for auth changes
|
||||||
const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
|
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);
|
setUser(session?.user ?? null);
|
||||||
if (session?.user) {
|
if (session?.user) {
|
||||||
|
if (event === 'SIGNED_IN' || event === 'INITIAL_SESSION' || event === 'TOKEN_REFRESHED') {
|
||||||
fetchCollection();
|
fetchCollection();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setBottles([]);
|
setBottles([]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
|
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
|
||||||
import { ChevronLeft, Users, Calendar, GlassWater, Plus, Trash2, Loader2, Sparkles, ChevronRight, Play, Square } from 'lucide-react';
|
import { ChevronLeft, Users, Calendar, GlassWater, Plus, Trash2, Loader2, Sparkles, ChevronRight, Play, Square } from 'lucide-react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { deleteSession } from '@/services/delete-session';
|
||||||
import { useSession } from '@/context/SessionContext';
|
import { useSession } from '@/context/SessionContext';
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
import { useI18n } from '@/i18n/I18nContext';
|
import { useI18n } from '@/i18n/I18nContext';
|
||||||
@@ -48,6 +49,7 @@ export default function SessionDetailPage() {
|
|||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const { activeSession, setActiveSession } = useSession();
|
const { activeSession, setActiveSession } = useSession();
|
||||||
const [isAddingParticipant, setIsAddingParticipant] = useState(false);
|
const [isAddingParticipant, setIsAddingParticipant] = useState(false);
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSessionData();
|
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) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center bg-zinc-50 dark:bg-black">
|
<div className="min-h-screen flex items-center justify-center bg-zinc-50 dark:bg-black">
|
||||||
@@ -206,6 +225,15 @@ export default function SessionDetailPage() {
|
|||||||
Session Stoppen
|
Session Stoppen
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={handleDeleteSession}
|
||||||
|
disabled={isDeleting}
|
||||||
|
title="Session löschen"
|
||||||
|
className="p-3 bg-red-50 dark:bg-red-900/10 text-red-600 dark:text-red-400 rounded-2xl hover:bg-red-600 hover:text-white dark:hover:bg-red-600 dark:hover:text-white transition-all border border-red-100 dark:border-red-900/20 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{isDeleting ? <Loader2 size={20} className="animate-spin" /> : <Trash2 size={20} />}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
|
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 Link from 'next/link';
|
||||||
|
import { deleteSession } from '@/services/delete-session';
|
||||||
import { useI18n } from '@/i18n/I18nContext';
|
import { useI18n } from '@/i18n/I18nContext';
|
||||||
import { useSession } from '@/context/SessionContext';
|
import { useSession } from '@/context/SessionContext';
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ export default function SessionList() {
|
|||||||
const [sessions, setSessions] = useState<Session[]>([]);
|
const [sessions, setSessions] = useState<Session[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isCreating, setIsCreating] = useState(false);
|
const [isCreating, setIsCreating] = useState(false);
|
||||||
|
const [isDeleting, setIsDeleting] = useState<string | null>(null);
|
||||||
const [newName, setNewName] = useState('');
|
const [newName, setNewName] = useState('');
|
||||||
const { activeSession, setActiveSession } = useSession();
|
const { activeSession, setActiveSession } = useSession();
|
||||||
|
|
||||||
@@ -92,6 +94,26 @@ export default function SessionList() {
|
|||||||
setIsCreating(false);
|
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 (
|
return (
|
||||||
<div className="bg-white dark:bg-zinc-900 rounded-3xl p-6 border border-zinc-200 dark:border-zinc-800 shadow-xl">
|
<div className="bg-white dark:bg-zinc-900 rounded-3xl p-6 border border-zinc-200 dark:border-zinc-800 shadow-xl">
|
||||||
<h3 className="text-xl font-bold mb-6 flex items-center gap-2 text-zinc-800 dark:text-zinc-100 italic">
|
<h3 className="text-xl font-bold mb-6 flex items-center gap-2 text-zinc-800 dark:text-zinc-100 italic">
|
||||||
@@ -172,6 +194,21 @@ export default function SessionList() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ChevronRight size={20} className={activeSession?.id === session.id ? 'text-white/50' : 'text-zinc-300'} />
|
<ChevronRight size={20} className={activeSession?.id === session.id ? 'text-white/50' : 'text-zinc-300'} />
|
||||||
|
<button
|
||||||
|
onClick={(e) => handleDeleteSession(e, session.id)}
|
||||||
|
disabled={!!isDeleting}
|
||||||
|
className={`p-2 rounded-xl transition-all ${activeSession?.id === session.id
|
||||||
|
? 'text-white/40 hover:text-white hover:bg-white/10'
|
||||||
|
: 'text-zinc-300 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/10'
|
||||||
|
}`}
|
||||||
|
title="Session löschen"
|
||||||
|
>
|
||||||
|
{isDeleting === session.id ? (
|
||||||
|
<Loader2 size={18} className="animate-spin" />
|
||||||
|
) : (
|
||||||
|
<Trash2 size={18} />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -24,13 +24,15 @@ interface Tasting {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
user_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TastingListProps {
|
interface TastingListProps {
|
||||||
initialTastings: Tasting[];
|
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 [sortBy, setSortBy] = useState<'date-desc' | 'date-asc' | 'rating-desc' | 'rating-asc'>('date-desc');
|
||||||
const [isDeleting, setIsDeleting] = useState<string | null>(null);
|
const [isDeleting, setIsDeleting] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -100,8 +102,8 @@ export default function TastingList({ initialTastings }: TastingListProps) {
|
|||||||
key={note.id}
|
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"
|
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"
|
||||||
>
|
>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex flex-wrap items-center gap-2 sm:gap-3">
|
||||||
<div className="bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400 px-3 py-1.5 rounded-2xl text-sm font-black ring-1 ring-amber-500/20 flex items-center gap-1.5">
|
<div className="bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400 px-3 py-1.5 rounded-2xl text-sm font-black ring-1 ring-amber-500/20 flex items-center gap-1.5">
|
||||||
<Star size={14} fill="currentColor" className="text-amber-500" />
|
<Star size={14} fill="currentColor" className="text-amber-500" />
|
||||||
{note.rating}/100
|
{note.rating}/100
|
||||||
@@ -113,38 +115,41 @@ export default function TastingList({ initialTastings }: TastingListProps) {
|
|||||||
{note.is_sample ? 'Sample' : 'Bottle'}
|
{note.is_sample ? 'Sample' : 'Bottle'}
|
||||||
</span>
|
</span>
|
||||||
<div className="text-[10px] text-zinc-500 font-bold bg-zinc-100 dark:bg-zinc-800 px-2 py-1 rounded-lg flex items-center gap-1">
|
<div className="text-[10px] text-zinc-500 font-bold bg-zinc-100 dark:bg-zinc-800 px-2 py-1 rounded-lg flex items-center gap-1">
|
||||||
|
<Clock size={10} />
|
||||||
{new Date(note.created_at).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}
|
{new Date(note.created_at).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}
|
||||||
</div>
|
</div>
|
||||||
{note.tasting_sessions && (
|
{note.tasting_sessions && (
|
||||||
<Link
|
<Link
|
||||||
href={`/sessions/${note.tasting_sessions.id}`}
|
href={`/sessions/${note.tasting_sessions.id}`}
|
||||||
className="text-[10px] text-zinc-500 font-bold bg-amber-50 dark:bg-amber-900/20 px-2 py-1 rounded-lg flex items-center gap-1 border border-amber-200/50 dark:border-amber-800/50 transition-all hover:bg-amber-100 dark:hover:bg-amber-900/40"
|
className="text-[10px] text-zinc-500 font-bold bg-amber-50 dark:bg-amber-900/20 px-2 py-1 rounded-lg flex items-center gap-1 border border-amber-200/50 dark:border-amber-800/50 transition-all hover:bg-amber-100 dark:hover:bg-amber-900/40 truncate max-w-[120px] sm:max-w-none"
|
||||||
>
|
>
|
||||||
<GlassWater size={10} className="text-amber-600" />
|
<GlassWater size={10} className="text-amber-600" />
|
||||||
{note.tasting_sessions.name}
|
{note.tasting_sessions.name}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center justify-between w-full sm:w-auto gap-4">
|
||||||
<div className="text-[10px] text-zinc-400 font-black tracking-widest uppercase flex items-center gap-1">
|
<div className="text-[10px] text-zinc-400 font-black tracking-widest uppercase flex items-center gap-1">
|
||||||
<Calendar size={12} />
|
<Calendar size={12} />
|
||||||
{new Date(note.created_at).toLocaleDateString('de-DE')}
|
{new Date(note.created_at).toLocaleDateString('de-DE')}
|
||||||
</div>
|
</div>
|
||||||
|
{(!currentUserId || note.user_id === currentUserId) && (
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete(note.id, note.bottle_id)}
|
onClick={() => handleDelete(note.id, note.bottle_id)}
|
||||||
disabled={!!isDeleting}
|
disabled={!!isDeleting}
|
||||||
className="px-3 py-1.5 text-zinc-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-xl transition-all disabled:opacity-50 flex items-center gap-2 border border-transparent hover:border-red-200 dark:hover:border-red-900/30 font-black text-[9px] uppercase tracking-widest"
|
className="px-3 py-1.5 text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20 rounded-xl transition-all disabled:opacity-50 flex items-center gap-2 border border-red-100 dark:border-red-900/30 font-black text-[10px] uppercase tracking-widest shadow-sm hover:bg-red-600 hover:text-white dark:hover:bg-red-600 dark:hover:text-white"
|
||||||
title="Tasting löschen"
|
title="Tasting löschen"
|
||||||
>
|
>
|
||||||
{isDeleting === note.id ? (
|
{isDeleting === note.id ? (
|
||||||
<Loader2 size={14} className="animate-spin" />
|
<Loader2 size={14} className="animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Trash2 size={14} />
|
<Trash2 size={14} fill="currentColor" />
|
||||||
<span className="opacity-0 group-hover:opacity-100 transition-opacity">Löschen</span>
|
<span>Löschen</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,11 @@ export async function middleware(req: NextRequest) {
|
|||||||
const { data: { session } } = await supabase.auth.getSession();
|
const { data: { session } } = await supabase.auth.getSession();
|
||||||
|
|
||||||
if (!isStatic) {
|
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) {
|
} catch (e) {
|
||||||
console.error('Middleware session refresh failed:', e);
|
console.error('[MW] Auth Error:', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
35
src/services/delete-session.ts
Normal file
35
src/services/delete-session.ts
Normal file
@@ -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.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user