import { useState } from 'react'; import { Link } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; import { Layout } from '@/components/Layout'; import { Card, CardContent, CardHeader, CardTitle } 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 { cn } from '@/lib/utils'; function CollectionCard({ collection, onEdit, onDelete, }: { collection: SpotCollection; onEdit: (collection: SpotCollection) => void; onDelete: (collection: SpotCollection) => void; }) { return ( {/* Image/Header */} {collection.image ? ( ) : ( )} {/* Spot count badge */} {collection.spotRefs.length} spot{collection.spotRefs.length !== 1 ? 's' : ''} {/* 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()} View ); } function CollectionCardSkeleton() { return ( ); } export default function SpotCollectionsPage() { 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: 'My Spot Collections - SkateSpots', description: 'Organize your favorite skate spots into personalized collections', }); const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); if (!newTitle.trim()) return; try { await createCollection({ title: newTitle.trim(), description: newDescription.trim() || undefined, }); toast({ title: 'Collection created!', description: `"${newTitle}" is ready for spots`, }); setNewTitle(''); setNewDescription(''); setShowCreateDialog(false); } catch (error) { toast({ title: 'Failed to create collection', description: error instanceof Error ? error.message : 'Something went wrong', 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: 'Collection updated!', description: 'Your changes have been saved', }); setShowEditDialog(false); setSelectedCollection(null); } catch (error) { toast({ title: 'Failed to update collection', description: error instanceof Error ? error.message : 'Something went wrong', variant: 'destructive', }); } }; const handleDelete = (collection: SpotCollection) => { setSelectedCollection(collection); setShowDeleteDialog(true); }; const handleDeleteConfirm = async () => { if (!selectedCollection) return; try { await deleteCollection(selectedCollection); toast({ title: 'Collection deleted', description: `"${selectedCollection.title}" has been removed`, }); setShowDeleteDialog(false); setSelectedCollection(null); } catch (error) { toast({ title: 'Failed to delete collection', description: error instanceof Error ? error.message : 'Something went wrong', variant: 'destructive', }); } }; return ( {/* Header */} MY COLLECTIONS Organize your favorite spots into personalized lists {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" > New Collection )} {/* Not logged in state */} {!user && ( Log in to create collections Save your favorite spots to personalized lists like "Barcelona Trip 2024" or "Best Rails in LA" )} {/* Loading state */} {user && isLoading && ( {Array.from({ length: 6 }).map((_, i) => ( ))} )} {/* Empty state */} {user && !isLoading && collections?.length === 0 && ( No collections yet Create your first collection to start organizing spots setShowCreateDialog(true)} className="bg-[#ccff00] text-black hover:bg-[#ccff00]/90 font-bold uppercase gap-2" > Create Your First Collection )} {/* Collections grid */} {user && !isLoading && collections && collections.length > 0 && ( {collections.map((collection) => ( ))} )} {/* Create Collection Dialog */} Create New Collection Give your collection a name and optional description Collection Name setNewTitle(e.target.value)} placeholder="Barcelona Trip 2024" className="bg-black border-gray-700 text-white h-11" autoFocus /> Description (optional) setNewDescription(e.target.value)} placeholder="My favorite spots from my Barcelona trip..." className="bg-black border-gray-700 text-white resize-none" rows={3} /> setShowCreateDialog(false)} className="flex-1 border-gray-700 h-11" > Cancel {isCreating ? ( <> Creating... > ) : ( <> Create Collection Create > )} {/* Edit Collection Dialog */} Edit Collection Collection Name setEditTitle(e.target.value)} className="bg-black border-gray-700 text-white h-11" /> Description (optional) 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" > Cancel {isUpdating ? ( <> Saving... > ) : ( 'Save Changes' )} {/* Delete Confirmation Dialog */} Delete Collection? This will permanently delete "{selectedCollection?.title}". The spots themselves won't be affected. Cancel {isDeleting ? ( <> Deleting... > ) : ( 'Delete' )} ); }
{collection.description}
Organize your favorite spots into personalized lists
Save your favorite spots to personalized lists like "Barcelona Trip 2024" or "Best Rails in LA"
Create your first collection to start organizing spots