diff --git a/app-9xzmfic2e4g1/src/pages/GuideDetailPage.tsx b/app-9xzmfic2e4g1/src/pages/GuideDetailPage.tsx new file mode 100644 index 0000000..859e1b3 --- /dev/null +++ b/app-9xzmfic2e4g1/src/pages/GuideDetailPage.tsx @@ -0,0 +1,377 @@ +import { useEffect, useState } from 'react'; +import { useParams, useNavigate, Link } from 'react-router-dom'; +import api from '@/db/api'; +import { TripMap } from '@/components/trip/Map'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { motion, AnimatePresence } from 'framer-motion'; +import { + Heart, Eye, MapPin, Calendar, Copy, Loader2, + ChevronLeft, Clock, Lightbulb, User, ChevronDown, ChevronUp +} from 'lucide-react'; +import { format, addDays, differenceInDays } from 'date-fns'; +import { tr } from 'date-fns/locale'; +import { useAuth } from '@/contexts/AuthContext'; +import { toast } from 'sonner'; +import { cn } from '@/lib/utils'; + +const DAY_COLORS = ['#EA580C', '#9333EA', '#2563EB', '#059669', '#D97706']; + +export default function GuideDetailPage() { + const { id } = useParams<{ id: string }>(); + const navigate = useNavigate(); + const { user } = useAuth(); + + const [guide, setGuide] = useState(null); + const [loading, setLoading] = useState(true); + const [cloning, setCloning] = useState(false); + const [liked, setLiked] = useState(false); + const [selectedDay, setSelectedDay] = useState(0); + const [activePlaceId, setActivePlaceId] = useState(null); + const [tipsExpanded, setTipsExpanded] = useState(false); + + useEffect(() => { + if (!id) return; + load(); + }, [id]); + + const load = async () => { + try { + const [data, likes] = await Promise.all([ + api.getPublicGuide(id!), + user ? api.getMyLikes() : Promise.resolve([]), + ]); + setGuide(data); + setLiked(likes.includes(id!)); + } catch { + toast.error('Rehber bulunamadı'); + navigate('/rehberler'); + } finally { + setLoading(false); + } + }; + + const handleLike = async () => { + if (!user) { navigate('/login'); return; } + const wasLiked = liked; + setLiked(!wasLiked); + setGuide((g: any) => ({ ...g, likes_count: g.likes_count + (wasLiked ? -1 : 1) })); + try { await api.toggleLike(id!); } + catch { setLiked(wasLiked); } + }; + + const handleClone = async () => { + if (!user) { navigate('/login'); return; } + setCloning(true); + try { + const newTrip = await api.cloneGuide(id!); + toast.success('Rota kopyalandı! Düzenlemeye başlayabilirsiniz.'); + navigate(`/trip/${newTrip.id}`); + } catch { + toast.error('Kopyalama başarısız'); + } finally { + setCloning(false); + } + }; + + const getCoverPhoto = () => { + const first = guide?.itinerary?.days?.[0]?.items?.[0]; + if (first?.photo_reference) { + return first.photo_reference.startsWith('http') + ? first.photo_reference + : api.getPhotoUrl(first.photo_reference); + } + return 'https://images.unsplash.com/photo-1570168007204-dfb528c6958f?auto=format&fit=crop&q=80&w=1600'; + }; + + const getDayDate = (idx: number) => { + if (!guide?.start_date) return `Gün ${idx + 1}`; + try { + return format(addDays(new Date(guide.start_date), idx), 'd MMM', { locale: tr }); + } catch { return `Gün ${idx + 1}`; } + }; + + const getTripDuration = () => { + if (!guide?.start_date || !guide?.end_date) return null; + try { + return differenceInDays(new Date(guide.end_date), new Date(guide.start_date)) + 1; + } catch { return null; } + }; + + if (loading) { + return ( +
+ +
+ ); + } + + if (!guide) return null; + + const days = guide.itinerary?.days || []; + const currentDay = days[selectedDay]; + const tips: string[] = guide.guide_tips || []; + const duration = getTripDuration(); + + return ( +
+ + {/* Hero */} +
+ {guide.title} { + (e.target as HTMLImageElement).src = 'https://images.unsplash.com/photo-1541167760496-1628856ab772?w=1600&q=80'; + }} + /> +
+ + {/* Back */} + + + {/* Title */} +
+
+
+ {duration && ( + + {duration} GÜN + + )} + + {days.reduce((s: number, d: any) => s + (d.items?.length || 0), 0)} DURAK + + {guide.destination && ( + + 📍 {guide.destination} + + )} +
+

+ {guide.title} +

+
+ {guide.profiles?.username && ( + + + @{guide.profiles.username} + + )} + {guide.published_at && ( + + + {format(new Date(guide.published_at), 'd MMMM yyyy', { locale: tr })} + + )} + + + {guide.views_count || 0} görüntülenme + +
+
+
+
+ + {/* Sticky CTA bar */} +
+
+
+ +
+ +
+
+ +
+ + {/* Author intro */} + {guide.guide_intro && ( + +

Gezgin Notu

+

"{guide.guide_intro}"

+
+ )} + + {/* General tips */} + {tips.length > 0 && ( +
+ + + {tipsExpanded && ( + + {tips.map((tip, i) => ( +
  • + + {i + 1} + + {tip} +
  • + ))} +
    + )} +
    +
    + )} + + {/* Day tabs + itinerary */} +
    +
    + {days.map((day: any, idx: number) => ( + + ))} +
    + + {currentDay && ( +
    + {/* Place list */} +
    + {(currentDay.items || []).map((item: any, idx: number) => ( + setActivePlaceId(item.place_id)} + className={cn( + 'flex gap-3 p-3 rounded-2xl border-2 cursor-pointer transition-all', + activePlaceId === item.place_id + ? 'border-orange-400 bg-orange-50' + : 'border-gray-100 bg-white hover:border-orange-200' + )} + > + {/* Number */} +
    + {idx + 1} +
    + + {/* Info */} +
    +
    +

    {item.name}

    + {item.rating && ( + + ★ {item.rating} + + )} +
    + {item.start_time && ( +

    + + {item.start_time} – {item.end_time} +

    + )} + {item.why_visit && ( +

    + + {item.why_visit} +

    + )} + {item.personal_tip && ( +

    + + {item.personal_tip} +

    + )} +
    + + {/* Thumbnail */} + {item.photo_reference && ( +
    + {item.name} { (e.target as HTMLImageElement).src = 'https://images.unsplash.com/photo-1541167760496-1628856ab772?w=200&q=80'; }} + /> +
    + )} +
    + ))} +
    + + {/* Map */} +
    + +
    +
    + )} +
    + + {/* Bottom CTA */} +
    +

    Bu rotayı beğendiniz mi?

    +

    Bir tıkla kendi planınıza kopyalayın ve özelleştirin.

    + + {!user && ( +

    + Kopyalamak için{' '} + giriş yapın +

    + )} +
    +
    +
    + ); +} \ No newline at end of file