87 lines
4.1 KiB
TypeScript
87 lines
4.1 KiB
TypeScript
import React from 'react';
|
|
import { Clock, X } from 'lucide-react';
|
|
|
|
export interface SharedLocationPhoto {
|
|
id: string;
|
|
url: string;
|
|
uploaderName: string;
|
|
uploaderAvatar?: string;
|
|
isGuest: boolean;
|
|
capturedAt: string;
|
|
description?: string;
|
|
}
|
|
|
|
interface TimelineProps {
|
|
locationName: string;
|
|
photos: SharedLocationPhoto[];
|
|
onSelectPhoto: (photoId: string) => void;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export const LocationTimelineSheet: React.FC<TimelineProps> = ({ locationName, photos, onSelectPhoto, onClose }) => {
|
|
// Sort photos chronologically by capture timestamp
|
|
const chronologicalPhotos = [...photos].sort(
|
|
(a, b) => new Date(b.capturedAt).getTime() - new Date(a.capturedAt).getTime()
|
|
);
|
|
|
|
return (
|
|
<div className="fixed bottom-0 left-0 right-0 z-[9999] bg-slate-900 border-t border-slate-800 rounded-t-3xl max-h-[85vh] flex flex-col overflow-hidden text-white text-xs shadow-2xl animate-slide-up">
|
|
{/* Dynamic Header Drag/Close Strip */}
|
|
<div className="w-full px-5 py-4 border-b border-slate-800/60 flex justify-between items-center bg-slate-900 sticky top-0 z-10">
|
|
<div>
|
|
<h3 className="font-bold text-sm text-slate-100 truncate max-w-[70vw]">{locationName || "Hành trình tại địa điểm"}</h3>
|
|
<p className="text-[10px] text-slate-400">Tổng hợp {photos.length} khoảnh khắc từ cộng đồng</p>
|
|
</div>
|
|
<button type="button" onClick={onClose} className="p-2 bg-slate-800 hover:bg-slate-700 rounded-xl text-slate-350 hover:text-white transition-colors">
|
|
<X className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* THE CHRONOLOGICAL TIMELINE STREAM CANVAS */}
|
|
<div className="flex-1 overflow-y-auto p-5 space-y-6 relative">
|
|
{/* Vertical Timeline Track Line */}
|
|
<div className="absolute left-[27px] top-6 bottom-6 w-[2px] bg-slate-800" />
|
|
|
|
{chronologicalPhotos.map((photo) => (
|
|
<div key={photo.id} className="flex gap-4 items-start relative group">
|
|
{/* Timeline Node Circle Asset Indicator */}
|
|
<div className="w-6 h-6 rounded-full bg-blue-600 border-4 border-slate-900 flex items-center justify-center z-10 shrink-0 shadow-md" />
|
|
|
|
{/* Core Content Card Box */}
|
|
<div className="flex-1 bg-slate-950/50 border border-slate-800/80 rounded-2xl p-3 space-y-3 hover:border-slate-700/60 transition-colors">
|
|
{/* Meta row identifier */}
|
|
<div className="flex justify-between items-center text-[10px] text-slate-400">
|
|
<div className="flex items-center gap-1.5">
|
|
<div className="w-4 h-4 rounded-full bg-slate-700 flex items-center justify-center font-bold text-[8px] text-white overflow-hidden shrink-0">
|
|
{photo.uploaderAvatar ? (
|
|
<img src={photo.uploaderAvatar} className="object-cover w-full h-full" />
|
|
) : (
|
|
photo.uploaderName.charAt(0).toUpperCase()
|
|
)}
|
|
</div>
|
|
<span className="font-medium text-slate-300 truncate max-w-[120px]">{photo.uploaderName}</span>
|
|
{photo.isGuest && <span className="bg-slate-800 text-[8px] px-1 py-0.5 rounded text-slate-500 font-bold">Khách</span>}
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
<Clock className="w-3 h-3 text-slate-500" />
|
|
<span>{new Date(photo.capturedAt).toLocaleDateString('vi-VN')}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Clickable Card Thumbnail Container */}
|
|
<div
|
|
onClick={() => onSelectPhoto(photo.id)}
|
|
className="w-full aspect-video rounded-xl overflow-hidden bg-slate-900 relative cursor-pointer active:scale-[0.99] transition-transform"
|
|
>
|
|
<img src={photo.url} alt="Timeline view" className="w-full h-full object-cover" />
|
|
</div>
|
|
|
|
{photo.description && <p className="text-slate-300 leading-relaxed text-[11px] px-0.5">{photo.description}</p>}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|