IDKHowToCodeFr/tinyml-backend
1
1"use client";2 3import { motion } from "framer-motion";4import { Database, LineChart, Table2, Layers } from "lucide-react";5import Link from "next/link";6import { useEffect, useState } from "react";7 8export default function Analytics() {9 const [dataset, setDataset] = useState<any[]>([]);10 const [loading, setLoading] = useState(true);11 12 useEffect(() => {13 fetch((process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000") + "/dataset")14 .then((res) => res.json())15 .then((data) => {16 setDataset(data);17 setLoading(false);18 })19 .catch((err) => {20 console.error(err);21 setLoading(false);22 });23 }, []);24 25 const spring = {26 type: "spring" as const,27 stiffness: 100,28 damping: 20,29 };30 31 const columns = dataset.length > 0 ? Object.keys(dataset[0]) : [];32 33 return (34 <main className="min-h-[100dvh] relative overflow-hidden bg-canvas">35 36 <div className="max-w-[1200px] mx-auto px-8 py-24 relative">37 <div className="absolute top-0 right-1/4 w-[30vw] h-[30vw] rounded-full bg-gradient-sky opacity-20 blur-[80px] -z-10" />38 39 <motion.div40 initial={{ opacity: 0, y: 20 }}41 animate={{ opacity: 1, y: 0 }}42 transition={spring}43 className="mb-12 flex justify-between items-end"44 >45 <div>46 <h1 className="font-display text-[48px] font-light tracking-[-0.02em] text-ink leading-[1.08] mb-4">47 Dataset Explorer48 </h1>49 <p className="font-sans text-[16px] text-body max-w-2xl leading-relaxed tracking-[0.01em]">50 Preview the current ground-truth telemetry used for training the TinyML models.51 </p>52 </div>53 <div className="flex gap-4">54 <div className="px-4 py-2 bg-canvas-soft border border-hairline-soft rounded-full flex items-center text-sm font-medium text-ink">55 <Layers className="w-4 h-4 mr-2 text-muted" />56 {columns.length} Features57 </div>58 <div className="px-4 py-2 bg-surface-card border border-hairline rounded-full flex items-center text-sm font-medium text-ink shadow-[0_4px_16px_rgba(0,0,0,0.02)]">59 <Database className="w-4 h-4 mr-2 text-muted" />60 {dataset.length} Rows61 </div>62 </div>63 </motion.div>64 65 <motion.div 66 initial={{ opacity: 0, y: 20 }}67 animate={{ opacity: 1, y: 0 }}68 transition={{ ...spring, delay: 0.1 }}69 className="bg-surface-card rounded-xl border border-hairline shadow-[0_4px_16px_rgba(0,0,0,0.04)] overflow-hidden flex flex-col h-[600px]"70 >71 <div className="p-4 border-b border-hairline-soft flex items-center justify-between bg-canvas-soft">72 <h2 className="font-sans text-[14px] font-medium text-ink flex items-center gap-2">73 <Table2 size={16} className="text-muted" /> Raw Tabular View74 </h2>75 </div>76 <div className="flex-grow overflow-auto relative bg-surface-card">77 <table className="w-full text-left text-[14px]">78 <thead className="bg-canvas-soft sticky top-0 z-10 border-b border-hairline text-muted font-medium text-[12px] uppercase tracking-[0.06em]">79 <tr>80 {columns.map(col => (81 <th key={col} className="px-6 py-4 font-medium whitespace-nowrap">82 {col}83 </th>84 ))}85 </tr>86 </thead>87 <tbody className="divide-y divide-hairline-soft text-ink">88 {loading ? (89 <tr>90 <td colSpan={columns.length || 1} className="px-6 py-12 text-center text-muted">91 Loading dataset...92 </td>93 </tr>94 ) : dataset.length === 0 ? (95 <tr>96 <td colSpan={columns.length || 1} className="px-6 py-12 text-center text-muted">97 No dataset available. Upload one in MLOps.98 </td>99 </tr>100 ) : (101 dataset.slice(0, 100).map((row, i) => (102 <tr key={i} className="hover:bg-canvas-soft/50 transition-colors">103 {columns.map(col => (104 <td key={col} className="px-6 py-3 whitespace-nowrap">105 {typeof row[col] === 'number' && !Number.isInteger(row[col]) 106 ? row[col].toFixed(2) 107 : String(row[col])}108 </td>109 ))}110 </tr>111 ))112 )}113 </tbody>114 </table>115 {!loading && dataset.length > 100 && (116 <div className="p-4 text-center text-[13px] text-muted-soft border-t border-hairline-soft">117 Showing first 100 rows.118 </div>119 )}120 </div>121 </motion.div>122 </div>123 </main>124 );125}126 