diff --git a/app-9xzmfic2e4g1/src/components/trip/Timeline.tsx b/app-9xzmfic2e4g1/src/components/trip/Timeline.tsx index 0ce9515..c7f34c8 100644 --- a/app-9xzmfic2e4g1/src/components/trip/Timeline.tsx +++ b/app-9xzmfic2e4g1/src/components/trip/Timeline.tsx @@ -1,38 +1,66 @@ import { ItineraryDay, Place } from '@/db/api'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; -import { Star, Clock, MapPin, GripVertical, Car, Compass, Camera, Zap, Trash2, Edit3, MessageSquare, MoreVertical, Coffee, Sun, Sunset, Moon } from 'lucide-react'; -import api from '@/db/api'; -import { useState } from 'react'; import { - DndContext, - closestCenter, - KeyboardSensor, - PointerSensor, - useSensor, - useSensors, - DragEndEvent, + Star, Clock, MapPin, GripVertical, Car, Trash2, Edit3, + MessageSquare, MoreVertical, Sun, Sunset, Moon, Coffee, + Package, Wand2 +} from 'lucide-react'; +import api from '@/db/api'; +import { useState, useMemo } from 'react'; +import { + DndContext, closestCenter, KeyboardSensor, PointerSensor, + useSensor, useSensors, DragEndEvent, } from '@dnd-kit/core'; import { - arrayMove, - SortableContext, - sortableKeyboardCoordinates, - verticalListSortingStrategy, - useSortable, + arrayMove, SortableContext, sortableKeyboardCoordinates, + verticalListSortingStrategy, useSortable, } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { cn } from '@/lib/utils'; -import { motion } from 'framer-motion'; +import { motion, AnimatePresence } from 'framer-motion'; import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; import { PlaceSearch } from './PlaceSearch'; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, + DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +// ──────────────────────────────────────────────────────────────────────────── +// Helpers +// ──────────────────────────────────────────────────────────────────────────── +function parseMinutes(time: string): number { + const [h = 0, m = 0] = time.split(':').map(Number); + return h * 60 + m; +} + +function formatTime(mins: number): string { + return `${String(Math.floor(mins / 60) % 24).padStart(2, '0')}:${String(mins % 60).padStart(2, '0')}`; +} + +/** Compute end_time from start_time + estimated_duration_minutes */ +function calcEndTime(item: Place): string { + const start = parseMinutes(item.start_time || '09:00'); + return formatTime(start + (item.estimated_duration_minutes || 60)); +} + +/** Time-of-day segment label */ +function getSegment(time: string): 'morning' | 'afternoon' | 'evening' { + const mins = parseMinutes(time); + if (mins < 12 * 60) return 'morning'; + if (mins < 18 * 60) return 'afternoon'; + return 'evening'; +} + +const SEGMENT_META = { + morning: { label: 'Sabah', icon: Sun, color: 'text-amber-500' }, + afternoon: { label: 'Öğleden Sonra', icon: Coffee, color: 'text-orange-500' }, + evening: { label: 'Akşam', icon: Sunset, color: 'text-rose-500' }, +}; + +// ──────────────────────────────────────────────────────────────────────────── +// Props +// ──────────────────────────────────────────────────────────────────────────── interface TimelineProps { itinerary: { days: ItineraryDay[] }; onReorder: (dayIndex: number, newItems: Place[]) => void; @@ -42,328 +70,388 @@ interface TimelineProps { onUpdateDayNote: (dayIndex: number, note: string) => void; onPlaceClick: (id: string) => void; activePlaceId: string | null; + dayStartDate?: string; } -export function Timeline({ - itinerary, - onReorder, - onAddPlace, - onDeletePlace, - onUpdatePlaceNote, - onUpdateDayNote, - onPlaceClick, - activePlaceId -}: TimelineProps) { +// ──────────────────────────────────────────────────────────────────────────── +// Timeline root +// ──────────────────────────────────────────────────────────────────────────── +export function Timeline(props: TimelineProps) { return (
- {itinerary.days.map((day, dayIndex) => ( - + {props.itinerary.days.map((day, dayIndex) => ( + ))}
); } -function DaySection({ - day, - dayIndex, - onReorder, - onAddPlace, - onDeletePlace, - onUpdatePlaceNote, - onUpdateDayNote, - onPlaceClick, - activePlaceId -}: { - day: ItineraryDay; - dayIndex: number; - onReorder: (dayIndex: number, newItems: Place[]) => void; - onAddPlace: (dayIndex: number, place: Place) => void; - onDeletePlace: (dayIndex: number, placeId: string) => void; - onUpdatePlaceNote: (dayIndex: number, placeId: string, note: string) => void; - onUpdateDayNote: (dayIndex: number, note: string) => void; - onPlaceClick: (id: string) => void; - activePlaceId: string | null; -}) { - const [isEditingDayNote, setIsEditingDayNote] = useState(false); - const [dayNote, setDayNote] = useState(day.notes || ''); +// ──────────────────────────────────────────────────────────────────────────── +// DaySection +// ──────────────────────────────────────────────────────────────────────────── +function DaySection({ + day, dayIndex, + onReorder, onAddPlace, onDeletePlace, + onUpdatePlaceNote, onUpdateDayNote, + onPlaceClick, activePlaceId, +}: TimelineProps & { day: ItineraryDay; dayIndex: number }) { + const [isEditingNote, setIsEditingNote] = useState(false); + const [noteText, setNoteText] = useState(day.notes || ''); const sensors = useSensors( useSensor(PointerSensor), - useSensor(KeyboardSensor, { - coordinateGetter: sortableKeyboardCoordinates, - }) + useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ); const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; - if (over && active.id !== over.id) { - const oldIndex = day.items.findIndex((i) => i.place_id === active.id); - const newIndex = day.items.findIndex((i) => i.place_id === over.id); - - const newItems = arrayMove(day.items, oldIndex, newIndex); - onReorder(dayIndex, newItems); + const oldIdx = day.items.findIndex(i => i.place_id === active.id); + const newIdx = day.items.findIndex(i => i.place_id === over.id); + onReorder(dayIndex, arrayMove(day.items, oldIdx, newIdx)); } }; - const handleSaveDayNote = () => { - onUpdateDayNote(dayIndex, dayNote); - setIsEditingDayNote(false); - }; + // Group items by time-of-day + const grouped = useMemo(() => { + const segments: Record = {}; + for (const item of day.items) { + const seg = getSegment(item.start_time || '09:00'); + if (!segments[seg]) segments[seg] = []; + segments[seg].push(item); + } + return segments; + }, [day.items]); + + const segmentOrder: Array<'morning' | 'afternoon' | 'evening'> = ['morning', 'afternoon', 'evening']; return ( - - {/* Day Notes */} -
- {isEditingDayNote ? ( + + + {/* Day note */} +
+ {isEditingNote ? (
-