feat: session deletion, improved tasting deletion visibility, and PWA login loop fix

This commit is contained in:
2025-12-18 21:02:44 +01:00
parent a4a9d79c4c
commit a64e8f17a1
8 changed files with 174 additions and 32 deletions

View File

@@ -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<Session[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isCreating, setIsCreating] = useState(false);
const [isDeleting, setIsDeleting] = useState<string | null>(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 (
<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">
@@ -172,6 +194,21 @@ export default function SessionList() {
</div>
)}
<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>
))

View File

@@ -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<string | null>(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"
>
<div className="flex justify-between items-center">
<div className="flex items-center gap-3">
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<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">
<Star size={14} fill="currentColor" className="text-amber-500" />
{note.rating}/100
@@ -113,38 +115,41 @@ export default function TastingList({ initialTastings }: TastingListProps) {
{note.is_sample ? 'Sample' : 'Bottle'}
</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">
<Clock size={10} />
{new Date(note.created_at).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}
</div>
{note.tasting_sessions && (
<Link
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" />
{note.tasting_sessions.name}
</Link>
)}
</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">
<Calendar size={12} />
{new Date(note.created_at).toLocaleDateString('de-DE')}
</div>
<button
onClick={() => handleDelete(note.id, note.bottle_id)}
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"
title="Tasting löschen"
>
{isDeleting === note.id ? (
<Loader2 size={14} className="animate-spin" />
) : (
<>
<Trash2 size={14} />
<span className="opacity-0 group-hover:opacity-100 transition-opacity">Löschen</span>
</>
)}
</button>
{(!currentUserId || note.user_id === currentUserId) && (
<button
onClick={() => handleDelete(note.id, note.bottle_id)}
disabled={!!isDeleting}
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"
>
{isDeleting === note.id ? (
<Loader2 size={14} className="animate-spin" />
) : (
<>
<Trash2 size={14} fill="currentColor" />
<span>Löschen</span>
</>
)}
</button>
)}
</div>
</div>