Edit app-9xzmfic2e4g1/src/components/trip/Timeline.tsx via Editor

This commit is contained in:
Flatlogic Bot 2026-03-03 12:28:18 +00:00
parent 044b506f7a
commit 3cb97d4eee

View File

@ -38,13 +38,11 @@ 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';
@ -53,9 +51,9 @@ function getSegment(time: string): 'morning' | 'afternoon' | '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' },
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' },
};
// ────────────────────────────────────────────────────────────────────────────
@ -112,7 +110,6 @@ function DaySection({
}
};
// Group items by time-of-day
const grouped = useMemo(() => {
const segments: Record<string, Place[]> = {};
for (const item of day.items) {
@ -189,7 +186,6 @@ function DaySection({
if (!items?.length) return null;
const meta = SEGMENT_META[seg];
const SegIcon = meta.icon;
const globalOffset = day.items.indexOf(items[0]);
return (
<div key={seg} className="space-y-3">
@ -272,7 +268,7 @@ function DaySection({
}
// ────────────────────────────────────────────────────────────────────────────
// SortableItem — redesigned with larger photo
// SortableItem — Wanderlog stili küçük thumbnail
// ────────────────────────────────────────────────────────────────────────────
function SortableItem({
item, index, isActive, onClick, onDelete, onUpdateNote
@ -319,66 +315,24 @@ function SortableItem({
: "border-gray-100 hover:border-gray-200 hover:shadow-md"
)}
>
{/* Photo (16:9) */}
{photoUrl && !imgError && (
<div className="relative w-full aspect-video overflow-hidden bg-gray-100">
{!imgLoaded && (
<div className="absolute inset-0 bg-gradient-to-r from-gray-200 via-gray-100 to-gray-200 animate-pulse" />
)}
<img
src={photoUrl}
alt={item.name}
onLoad={() => setImgLoaded(true)}
onError={() => setImgError(true)}
className={cn(
"w-full h-full object-cover transition-all duration-500",
imgLoaded ? "opacity-100 scale-100" : "opacity-0 scale-105"
)}
/>
{/* Time badge on photo */}
<div className="absolute top-3 left-3 bg-black/60 backdrop-blur-sm text-white text-[10px] font-black px-2 py-1 rounded-lg">
{item.start_time || '09:00'} {endTime}
</div>
{/* Rating badge */}
{item.rating && (
<div className="absolute top-3 right-3 flex items-center gap-1 bg-white/90 backdrop-blur-sm text-gray-900 text-[10px] font-black px-2 py-1 rounded-lg">
<Star className="h-2.5 w-2.5 fill-amber-400 text-amber-400" />
{item.rating}
</div>
)}
{/* Index bubble */}
<div className="absolute bottom-3 left-3 w-7 h-7 rounded-full bg-white/90 backdrop-blur-sm flex items-center justify-center text-gray-900 font-black text-xs shadow-sm">
<CardContent className="p-3">
{/* Ana satır */}
<div className="flex items-start gap-2">
{/* Index numarası */}
<div className="w-6 h-6 rounded-full bg-orange-100 flex items-center justify-center text-orange-700 font-black text-[10px] shrink-0 mt-0.5">
{index + 1}
</div>
</div>
)}
<CardContent className="p-3">
{/* No photo: compact row layout */}
{(!photoUrl || imgError) && (
<div className="flex items-center gap-3 mb-2">
<div className="w-8 h-8 rounded-full bg-orange-100 flex items-center justify-center text-orange-700 font-black text-xs shrink-0">
{index + 1}
</div>
{item.start_time && (
<span className="text-[10px] font-bold text-gray-500 bg-gray-100 px-2 py-0.5 rounded-md">
{item.start_time} {endTime}
</span>
)}
{item.rating && (
<span className="flex items-center gap-1 text-[10px] font-bold text-gray-500">
<Star className="h-3 w-3 fill-amber-400 text-amber-400" />
{item.rating}
</span>
)}
</div>
)}
{/* Name + actions */}
<div className="flex items-start justify-between gap-2">
{/* Bilgi alanı */}
<div className="flex-1 min-w-0">
<h4 className="text-sm font-black text-gray-900 truncate">{item.name}</h4>
<div className="flex items-center gap-2 mt-1">
<div className="flex items-center gap-1.5 mt-0.5 flex-wrap">
{item.start_time && (
<span className="text-[10px] font-bold text-gray-500 bg-gray-100 px-1.5 py-0.5 rounded-md">
{item.start_time} {endTime}
</span>
)}
<Badge variant="secondary" className="bg-gray-100 text-gray-500 text-[9px] font-bold px-1.5 py-0 h-4 border-0">
{item.category}
</Badge>
@ -388,9 +342,35 @@ function SortableItem({
{item.estimated_duration_minutes}dk
</span>
)}
{item.rating && (
<span className="flex items-center gap-0.5 text-[9px] font-bold text-gray-400">
<Star className="h-2.5 w-2.5 fill-amber-400 text-amber-400" />
{item.rating}
</span>
)}
</div>
</div>
{/* Küçük thumbnail */}
{photoUrl && !imgError && (
<div className="relative w-16 h-16 rounded-xl overflow-hidden bg-gray-100 shrink-0">
{!imgLoaded && (
<div className="absolute inset-0 bg-gradient-to-r from-gray-200 via-gray-100 to-gray-200 animate-pulse" />
)}
<img
src={photoUrl}
alt={item.name}
onLoad={() => setImgLoaded(true)}
onError={() => setImgError(true)}
className={cn(
"w-full h-full object-cover transition-opacity duration-300",
imgLoaded ? "opacity-100" : "opacity-0"
)}
/>
</div>
)}
{/* Aksiyonlar (hover'da görünür) */}
<div className="flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-all shrink-0">
<div
{...attributes} {...listeners}
@ -406,7 +386,10 @@ function SortableItem({
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-36 rounded-xl p-1">
<DropdownMenuItem onClick={e => { e.stopPropagation(); setIsEditingNote(true); }} className="text-xs font-bold gap-2 rounded-lg">
<DropdownMenuItem
onClick={e => { e.stopPropagation(); setIsEditingNote(true); }}
className="text-xs font-bold gap-2 rounded-lg"
>
<Edit3 className="h-3.5 w-3.5 text-orange-500" />Not Düzenle
</DropdownMenuItem>
<DropdownMenuItem
@ -420,7 +403,7 @@ function SortableItem({
</div>
</div>
{/* Note */}
{/* Not düzenleme alanı */}
<AnimatePresence>
{isEditingNote && (
<motion.div
@ -448,6 +431,7 @@ function SortableItem({
)}
</AnimatePresence>
{/* Mevcut not gösterimi */}
{!isEditingNote && item.notes && (
<div className="mt-2.5 p-2 bg-orange-50 rounded-xl border-l-2 border-orange-500">
<p className="text-[11px] font-medium italic text-orange-800">{item.notes}</p>