Edit app-9xzmfic2e4g1/src/components/trip/Timeline.tsx via Editor
This commit is contained in:
parent
044b506f7a
commit
3cb97d4eee
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user