import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState } from 'react'; import { Link } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Skeleton } from '@/components/ui/skeleton'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog'; import { useToast } from '@/hooks/useToast'; import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useMySpotCollections, useCreateSpotCollection, useDeleteSpotCollection, useUpdateSpotCollection } from '@/hooks/useSpotCollections'; import type { SpotCollection } from '@/lib/types'; import { FolderPlus, FolderOpen, MapPin, Plus, Loader2, Pencil, Trash2, Calendar, ArrowRight, Bookmark } from 'lucide-react'; import { useTranslation } from '@/contexts/language'; import { cn } from '@/lib/utils'; function CollectionCard({ collection, onEdit, onDelete, }: { collection: SpotCollection; onEdit: (collection: SpotCollection) => void; onDelete: (collection: SpotCollection) => void; }) { const { t } = useTranslation(); return ( {/* Image/Header */} {collection.image ? ( ) : ( )} {/* Spot count badge */} {collection.spotRefs.length === 1 ? t('mapUi.spotSingular') : t('mapUi.spotPlural', { count: collection.spotRefs.length })} {/* Action buttons - always visible on mobile, hover on desktop */} { e.preventDefault(); e.stopPropagation(); onEdit(collection); }} > { e.preventDefault(); e.stopPropagation(); onDelete(collection); }} > {collection.title} {collection.description && ( {collection.description} )} {new Date(collection.createdAt * 1000).toLocaleDateString(dateLocaleCode())} {t('mapUi.view')} ); } function CollectionCardSkeleton() { return ( ); } export default function SpotCollectionsPage() { const { t } = useTranslation(); const { user } = useCurrentUser(); const { data: collections, isLoading } = useMySpotCollections(); const { createCollection, isPending: isCreating } = useCreateSpotCollection(); const { deleteCollection, isPending: isDeleting } = useDeleteSpotCollection(); const { updateCollection, isPending: isUpdating } = useUpdateSpotCollection(); const { toast } = useToast(); const [showCreateDialog, setShowCreateDialog] = useState(false); const [showEditDialog, setShowEditDialog] = useState(false); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [selectedCollection, setSelectedCollection] = useState(null); const [newTitle, setNewTitle] = useState(''); const [newDescription, setNewDescription] = useState(''); const [editTitle, setEditTitle] = useState(''); const [editDescription, setEditDescription] = useState(''); useSeoMeta({ title: t('seoUi.spotCollectionsTitle'), description: t('seoUi.spotCollectionsDesc'), }); const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); if (!newTitle.trim()) return; try { await createCollection({ title: newTitle.trim(), description: newDescription.trim() || undefined, }); toast({ title: t('mapUi.collectionCreated'), description: t('mapUi.collectionCreatedDesc', { title: newTitle }), }); setNewTitle(''); setNewDescription(''); setShowCreateDialog(false); } catch (error) { toast({ title: t('mapUi.collectionCreateFailed'), description: error instanceof Error ? error.message : t('mapUi.somethingWentWrong'), variant: 'destructive', }); } }; const handleEdit = (collection: SpotCollection) => { setSelectedCollection(collection); setEditTitle(collection.title); setEditDescription(collection.description || ''); setShowEditDialog(true); }; const handleEditSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedCollection || !editTitle.trim()) return; try { await updateCollection({ collection: selectedCollection, title: editTitle.trim(), description: editDescription.trim() || undefined, }); toast({ title: t('mapUi.collectionUpdated'), description: t('mapUi.changesSaved'), }); setShowEditDialog(false); setSelectedCollection(null); } catch (error) { toast({ title: t('mapUi.collectionUpdateFailed'), description: error instanceof Error ? error.message : t('mapUi.somethingWentWrong'), variant: 'destructive', }); } }; const handleDelete = (collection: SpotCollection) => { setSelectedCollection(collection); setShowDeleteDialog(true); }; const handleDeleteConfirm = async () => { if (!selectedCollection) return; try { await deleteCollection(selectedCollection); toast({ title: t('mapUi.collectionDeleted'), description: t('mapUi.collectionDeletedDesc', { title: selectedCollection.title }), }); setShowDeleteDialog(false); setSelectedCollection(null); } catch (error) { toast({ title: t('mapUi.collectionDeleteFailed'), description: error instanceof Error ? error.message : t('mapUi.somethingWentWrong'), variant: 'destructive', }); } }; return ( <> {/* Header */} {t('mapUi.myCollections')} {t('mapUi.collectionsSubtitle')} {user && ( setShowCreateDialog(true)} className="bg-[#ccff00] text-black hover:bg-[#ccff00]/90 font-bold uppercase gap-2 h-11 sm:h-auto w-full sm:w-auto flex-shrink-0" > {t('mapUi.newCollection')} )} {/* Not logged in state */} {!user && ( {t('mapUi.loginToCreate')} {t('mapUi.loginToCreateDesc')} )} {/* Loading state */} {user && isLoading && ( {Array.from({ length: 6 }).map((_, i) => ( ))} )} {/* Empty state */} {user && !isLoading && collections?.length === 0 && ( {t('mapUi.noCollections')} {t('mapUi.noCollectionsDesc')} setShowCreateDialog(true)} className="bg-[#ccff00] text-black hover:bg-[#ccff00]/90 font-bold uppercase gap-2" > {t('mapUi.createFirstCollection')} )} {/* Collections grid */} {user && !isLoading && collections && collections.length > 0 && ( {collections.map((collection) => ( ))} )} {/* Create Collection Dialog */} {t('mapUi.createNewCollection')} {t('mapUi.createCollectionDesc')} {t('mapUi.collectionName')} setNewTitle(e.target.value)} placeholder={t('mapUi.collectionNamePlaceholder')} className="bg-black border-gray-700 text-white h-11" autoFocus /> {t('mapUi.descriptionOptional')} setNewDescription(e.target.value)} placeholder={t('mapUi.descriptionPlaceholder')} className="bg-black border-gray-700 text-white resize-none" rows={3} /> setShowCreateDialog(false)} className="flex-1 border-gray-700 h-11" > {t('common.cancel')} {isCreating ? ( <> {t('mapUi.creating')} > ) : ( <> {t('mapUi.createCollection')} {t('common.create')} > )} {/* Edit Collection Dialog */} {t('mapUi.editCollection')} {t('mapUi.collectionName')} setEditTitle(e.target.value)} className="bg-black border-gray-700 text-white h-11" /> {t('mapUi.descriptionOptional')} setEditDescription(e.target.value)} className="bg-black border-gray-700 text-white resize-none" rows={3} /> setShowEditDialog(false)} className="flex-1 border-gray-700 h-11" > {t('common.cancel')} {isUpdating ? ( <> {t('mapUi.saving')} > ) : ( t('mapUi.saveChanges') )} {/* Delete Confirmation Dialog */} {t('mapUi.deleteCollectionTitle')} {t('mapUi.deleteCollectionDesc', { title: selectedCollection?.title ?? '' })} {t('common.cancel')} {isDeleting ? ( <> {t('mapUi.deleting')} > ) : ( t('common.delete') )} > ); }
{collection.description}
{t('mapUi.collectionsSubtitle')}
{t('mapUi.loginToCreateDesc')}
{t('mapUi.noCollectionsDesc')}