106 lines
4.3 KiB
TypeScript
106 lines
4.3 KiB
TypeScript
import { fetchApi } from "@/lib/api";
|
|
import DatasetToolbar from "@/components/DatasetToolbar";
|
|
import { Calendar, User, Crosshair } from "lucide-react";
|
|
import Link from "next/link";
|
|
|
|
interface PlottingData {
|
|
session_id: string;
|
|
wallet_hash: string;
|
|
timestamp: string;
|
|
plotting: {
|
|
impacts: any[];
|
|
};
|
|
}
|
|
|
|
interface PhotoData {
|
|
filename: string;
|
|
imageUrl: string;
|
|
data: PlottingData | null;
|
|
}
|
|
|
|
export default async function DashboardPage() {
|
|
let photos: PhotoData[] = [];
|
|
try {
|
|
const response = await fetchApi('/api/all-data');
|
|
photos = response.data;
|
|
} catch (error) {
|
|
console.error("Error fetching photos:", error);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-8">
|
|
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
|
<div className="flex flex-col gap-2">
|
|
<h2 className="text-3xl font-bold tracking-tight">Galerie des Sessions</h2>
|
|
<p className="text-slate-400">Visualisez et gérez les données d'entraînement collectées.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<DatasetToolbar />
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
|
{photos.map((photo) => (
|
|
<Link
|
|
key={photo.filename}
|
|
href={`/photo/${photo.filename}`}
|
|
className="group relative bg-slate-900 rounded-xl border border-slate-800 overflow-hidden hover:border-indigo-500/50 transition-all hover:shadow-2xl hover:shadow-indigo-500/10"
|
|
>
|
|
{/* Image Preview */}
|
|
<div className="aspect-[3/4] relative overflow-hidden bg-slate-800">
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
<img
|
|
src={photo.imageUrl}
|
|
alt={photo.filename}
|
|
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
|
|
/>
|
|
<div className="absolute inset-0 bg-gradient-to-t from-slate-950 via-transparent to-transparent opacity-80" />
|
|
|
|
{/* Badge impacts */}
|
|
{photo.data?.plotting?.impacts && (
|
|
<div className="absolute top-3 right-3 bg-indigo-600/90 backdrop-blur-md px-2 py-1 rounded-md text-[10px] font-bold flex items-center gap-1">
|
|
<Crosshair size={12} />
|
|
{photo.data.plotting.impacts.length} IMPACTS
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Info */}
|
|
<div className="p-4 space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2 text-xs text-slate-400">
|
|
<Calendar size={14} className="text-indigo-400" />
|
|
{photo.data ? new Date(photo.data.timestamp).toLocaleDateString() : 'Date inconnue'}
|
|
</div>
|
|
{photo.data ? (
|
|
<span className="text-[10px] bg-emerald-500/10 text-emerald-400 px-1.5 py-0.5 rounded border border-emerald-500/20 font-bold">DATA OK</span>
|
|
) : (
|
|
<span className="text-[10px] bg-rose-500/10 text-rose-400 px-1.5 py-0.5 rounded border border-rose-500/20 font-bold">NO DATA</span>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2 text-xs text-slate-300 font-mono truncate">
|
|
<User size={14} className="text-indigo-400" />
|
|
{photo.data?.wallet_hash?.substring(0, 12) || 'Anonyme'}...
|
|
</div>
|
|
|
|
<div className="pt-2 border-t border-slate-800 flex justify-between items-center">
|
|
<span className="text-[10px] text-slate-500 uppercase tracking-widest font-bold">Session ID</span>
|
|
<span className="text-xs text-slate-300 font-mono truncate max-w-[120px]">
|
|
{photo.data?.session_id || 'N/A'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
|
|
{photos.length === 0 && (
|
|
<div className="flex flex-col items-center justify-center py-24 text-slate-500 gap-4 border-2 border-dashed border-slate-800 rounded-3xl">
|
|
<Crosshair size={48} className="opacity-20" />
|
|
<p>Aucune session d'entraînement trouvée.</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|