import { BookOpen, MoreHorizontal, Pencil, Plus, Trash2 } from 'lucide-react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { useToast } from '@/components/ui/use-toast'; import { useCreateStory, useDeleteStory, useStories, useStory, useUpdateStory, } from '@/lib/hooks/useStories'; import { cn } from '@/lib/utils/cn'; import { formatDate } from '@/lib/utils/format'; import { useStoryStore } from '@/stores/storyStore'; export function StoryList() { const { t } = useTranslation(); const { data: stories, isLoading } = useStories(); const selectedStoryId = useStoryStore((state) => state.selectedStoryId); const setSelectedStoryId = useStoryStore((state) => state.setSelectedStoryId); const trackEditorHeight = useStoryStore((state) => state.trackEditorHeight); const { data: selectedStory } = useStory(selectedStoryId); const createStory = useCreateStory(); const updateStory = useUpdateStory(); const deleteStory = useDeleteStory(); const [createDialogOpen, setCreateDialogOpen] = useState(false); const [editDialogOpen, setEditDialogOpen] = useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [editingStory, setEditingStory] = useState<{ id: string; name: string; description?: string; } | null>(null); const [deletingStoryId, setDeletingStoryId] = useState(null); const [newStoryName, setNewStoryName] = useState(''); const [newStoryDescription, setNewStoryDescription] = useState(''); const { toast } = useToast(); // Auto-select the first story when the list loads with no selection useEffect(() => { if (!selectedStoryId && stories && stories.length > 0) { setSelectedStoryId(stories[0].id); } }, [selectedStoryId, stories, setSelectedStoryId]); const handleCreateStory = () => { if (!newStoryName.trim()) { toast({ title: t('stories.toast.nameRequired'), description: t('stories.toast.nameRequiredDescription'), variant: 'destructive', }); return; } createStory.mutate( { name: newStoryName.trim(), description: newStoryDescription.trim() || undefined, }, { onSuccess: (story) => { setSelectedStoryId(story.id); setCreateDialogOpen(false); setNewStoryName(''); setNewStoryDescription(''); toast({ title: t('stories.toast.created'), description: t('stories.toast.createdDescription', { name: story.name }), }); }, onError: (error) => { toast({ title: t('stories.toast.createFailed'), description: error.message, variant: 'destructive', }); }, }, ); }; const handleEditClick = (story: { id: string; name: string; description?: string }) => { setEditingStory(story); setNewStoryName(story.name); setNewStoryDescription(story.description || ''); setEditDialogOpen(true); }; const handleUpdateStory = () => { if (!editingStory || !newStoryName.trim()) { toast({ title: t('stories.toast.nameRequired'), description: t('stories.toast.nameRequiredDescription'), variant: 'destructive', }); return; } updateStory.mutate( { storyId: editingStory.id, data: { name: newStoryName.trim(), description: newStoryDescription.trim() || undefined, }, }, { onSuccess: () => { setEditDialogOpen(false); setEditingStory(null); setNewStoryName(''); setNewStoryDescription(''); }, onError: (error) => { toast({ title: t('stories.toast.updateFailed'), description: error.message, variant: 'destructive', }); }, }, ); }; const handleDeleteClick = (storyId: string) => { setDeletingStoryId(storyId); setDeleteDialogOpen(true); }; const handleDeleteConfirm = () => { if (!deletingStoryId) return; deleteStory.mutate(deletingStoryId, { onSuccess: () => { // Clear selection if deleting the currently selected story if (selectedStoryId === deletingStoryId) { setSelectedStoryId(null); } setDeleteDialogOpen(false); setDeletingStoryId(null); }, onError: (error) => { toast({ title: t('stories.toast.deleteFailed'), description: error.message, variant: 'destructive', }); }, }); }; if (isLoading) { return (
{t('stories.loading')}
); } const storyList = stories || []; const hasTrackEditor = selectedStoryId && selectedStory && selectedStory.items.length > 0; return (
{/* Scroll Mask */}
{/* Fixed Header */}

{t('stories.title')}

{/* Scrollable Story List */}
{storyList.length === 0 ? (

{t('stories.empty.title')}

{t('stories.empty.hint')}

) : (
{storyList.map((story) => (
setSelectedStoryId(story.id)} onKeyDown={(e) => { if (e.target !== e.currentTarget) return; if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setSelectedStoryId(story.id); } }} >

{story.name}

{t('stories.row.itemCount', { count: story.item_count })} ยท {formatDate(story.updated_at)}
handleEditClick(story)}> {t('common.edit')} handleDeleteClick(story.id)} className="text-destructive focus:text-destructive" > {t('common.delete')}
))}
)}
{/* Create Story Dialog */} {t('stories.createDialog.title')} {t('stories.createDialog.description')}
setNewStoryName(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { handleCreateStory(); } }} />