Spaces:
Running
Running
| "use client"; | |
| import React, { useState, useEffect } from "react"; | |
| import { db, auth } from "@/lib/firebase"; | |
| import { collection, onSnapshot, query, orderBy, limit } from "firebase/firestore"; | |
| import Link from "next/link"; | |
| export default function Dashboard() { | |
| const [stats, setStats] = useState({ | |
| products: 0, | |
| clients: 0, | |
| salesCount: 0, | |
| revenue: 0, | |
| }); | |
| useEffect(() => { | |
| // Real-time stats from Firestore | |
| const unsubProducts = onSnapshot(collection(db, "products"), (snap) => { | |
| setStats((prev) => ({ ...prev, products: snap.size })); | |
| }); | |
| const unsubClients = onSnapshot(collection(db, "clients"), (snap) => { | |
| setStats((prev) => ({ ...prev, clients: snap.size })); | |
| }); | |
| const unsubSales = onSnapshot(collection(db, "sales"), (snap) => { | |
| let rev = 0; | |
| snap.forEach((doc) => { | |
| rev += doc.data().total || 0; | |
| }); | |
| setStats((prev) => ({ ...prev, salesCount: snap.size, revenue: rev })); | |
| }); | |
| return () => { | |
| unsubProducts(); | |
| unsubClients(); | |
| unsubSales(); | |
| }; | |
| }, []); | |
| return ( | |
| <div className="min-h-screen bg-[#0f172a] text-white p-6 font-sans"> | |
| <header className="flex justify-between items-center mb-10"> | |
| <div> | |
| <h1 className="text-4xl font-extrabold bg-gradient-to-r from-blue-400 to-indigo-500 bg-clip-text text-transparent"> | |
| ERP Premium Dashboard | |
| </h1> | |
| <p className="text-gray-400 mt-1">Bienvenido al centro de control de tu negocio</p> | |
| </div> | |
| <div className="flex gap-4"> | |
| <button | |
| onClick={() => auth.signOut()} | |
| className="px-6 py-2 bg-white/10 hover:bg-white/20 rounded-full border border-white/10 transition-all text-sm font-medium backdrop-blur-md" | |
| > | |
| Cerrar Sesión | |
| </button> | |
| </div> | |
| </header> | |
| {/* Stats Grid */} | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-10"> | |
| <StatCard title="Productos" value={stats.products} icon="📦" color="from-blue-500 to-cyan-400" /> | |
| <StatCard title="Clientes" value={stats.clients} icon="👥" color="from-purple-500 to-pink-500" /> | |
| <StatCard title="Ventas Totales" value={stats.salesCount} icon="🛒" color="from-orange-500 to-yellow-400" /> | |
| <StatCard title="Ingresos" value={`$${stats.revenue.toLocaleString()}`} icon="💰" color="from-green-500 to-emerald-400" /> | |
| </div> | |
| <div className="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
| {/* Quick Actions */} | |
| <div className="lg:col-span-1 bg-white/5 border border-white/10 rounded-3xl p-8 backdrop-blur-xl"> | |
| <h2 className="text-xl font-bold mb-6">Acciones Rápidas</h2> | |
| <div className="space-y-4"> | |
| <QuickAction href="/inventory" label="Nueva Existencia" icon="➕" /> | |
| <QuickAction href="/sales" label="Registrar Venta" icon="🏷️" /> | |
| <QuickAction href="/clients" label="Añadir Cliente" icon="👤" /> | |
| <QuickAction href="/hr" label="Gestionar Personal" icon="👔" /> | |
| </div> | |
| </div> | |
| {/* Charts Mockup / Future Widget */} | |
| <div className="lg:col-span-2 bg-white/5 border border-white/10 rounded-3xl p-8 backdrop-blur-xl relative overflow-hidden group"> | |
| <div className="absolute inset-0 bg-gradient-to-br from-blue-600/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700"></div> | |
| <div className="relative z-10"> | |
| <h2 className="text-xl font-bold mb-6">Rendimiento Semanal</h2> | |
| <div className="h-64 flex items-end justify-between gap-2"> | |
| {[60, 45, 80, 55, 95, 70, 85].map((h, i) => ( | |
| <div key={i} className="flex-1 flex flex-col items-center"> | |
| <div | |
| className="w-full bg-gradient-to-t from-blue-600 to-cyan-400 rounded-t-lg transition-all duration-1000 delay-150" | |
| style={{ height: `${h}%` }} | |
| ></div> | |
| <span className="text-[10px] text-gray-500 mt-2">{['L', 'M', 'X', 'J', 'V', 'S', 'D'][i]}</span> | |
| </div> | |
| ))} | |
| </div> | |
| <p className="mt-8 text-sm text-gray-400 items-center flex gap-2"> | |
| <span className="w-2 h-2 rounded-full bg-green-400 animate-pulse"></span> | |
| Actualizado en tiempo real con Firestore | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| function StatCard({ title, value, icon, color }: { title: string, value: string | number, icon: string, color: string }) { | |
| return ( | |
| <div className="bg-white/5 border border-white/10 rounded-3xl p-6 backdrop-blur-md relative overflow-hidden group hover:scale-[1.02] transition-all"> | |
| <div className={`absolute -right-4 -top-4 w-24 h-24 bg-gradient-to-br ${color} opacity-20 blur-2xl rounded-full group-hover:opacity-40 transition-opacity`}></div> | |
| <div className="flex items-center gap-4 relative z-10"> | |
| <div className="text-3xl">{icon}</div> | |
| <div> | |
| <p className="text-sm text-gray-400 uppercase tracking-wider font-semibold">{title}</p> | |
| <p className="text-2xl font-bold">{value}</p> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| function QuickAction({ href, label, icon }: { href: string, label: string, icon: string }) { | |
| return ( | |
| <Link href={href} className="flex items-center gap-4 p-4 rounded-2xl bg-white/5 border border-white/5 hover:bg-white/10 hover:border-white/20 transition-all group"> | |
| <span className="text-xl group-hover:scale-125 transition-transform">{icon}</span> | |
| <span className="font-medium text-gray-200">{label}</span> | |
| <span className="ml-auto opacity-0 group-hover:opacity-100 transition-opacity">→</span> | |
| </Link> | |
| ); | |
| } | |