Spaces:
Running
Running
| "use client"; | |
| import React, { useState, useEffect } from "react"; | |
| import { db } from "@/lib/firebase"; | |
| import { collection, onSnapshot, addDoc, serverTimestamp } from "firebase/firestore"; | |
| import Link from "next/link"; | |
| interface Sale { | |
| id: string; | |
| client: string; | |
| items: string; | |
| total: number; | |
| date: any; | |
| } | |
| export default function SalesPage() { | |
| const [sales, setSales] = useState<Sale[]>([]); | |
| const [products, setProducts] = useState<any[]>([]); | |
| const [isModalOpen, setIsModalOpen] = useState(false); | |
| const [newSale, setNewSale] = useState({ client: "", items: "", total: 0 }); | |
| useEffect(() => { | |
| const unsubSales = onSnapshot(collection(db, "sales"), (snap) => { | |
| const data = snap.docs.map(doc => ({ id: doc.id, ...doc.data() } as Sale)); | |
| setSales(data); | |
| }); | |
| const unsubProducts = onSnapshot(collection(db, "products"), (snap) => { | |
| setProducts(snap.docs.map(doc => ({ id: doc.id, ...doc.data() }))); | |
| }); | |
| return () => { unsubSales(); unsubProducts(); }; | |
| }, []); | |
| const handleAdd = async (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| await addDoc(collection(db, "sales"), { | |
| ...newSale, | |
| date: serverTimestamp() | |
| }); | |
| setIsModalOpen(false); | |
| setNewSale({ client: "", items: "", total: 0 }); | |
| }; | |
| return ( | |
| <div className="min-h-screen bg-[#0f172a] text-white p-6"> | |
| <header className="flex justify-between items-center mb-10"> | |
| <div> | |
| <Link href="/" className="text-orange-400 hover:text-orange-300 transition-colors flex items-center gap-2 mb-2 font-medium"> | |
| ← Home | |
| </Link> | |
| <h1 className="text-4xl font-black bg-gradient-to-r from-orange-400 to-yellow-500 bg-clip-text text-transparent">Historial de Ventas</h1> | |
| </div> | |
| <button | |
| onClick={() => setIsModalOpen(true)} | |
| className="px-10 py-4 bg-orange-600 hover:bg-orange-500 rounded-full font-black text-xs uppercase tracking-widest transition-all shadow-xl shadow-orange-900/40 transform hover:scale-105 active:scale-95" | |
| > | |
| ➕ Registrar Transacción | |
| </button> | |
| </header> | |
| <div className="bg-white/5 border border-white/10 rounded-[3rem] p-10 backdrop-blur-2xl"> | |
| <div className="space-y-6"> | |
| {sales.map((s) => ( | |
| <div key={s.id} className="flex items-center justify-between p-6 bg-white/5 border border-white/5 rounded-3xl hover:bg-white/10 transition-all group"> | |
| <div className="flex items-center gap-6"> | |
| <div className="w-14 h-14 bg-gradient-to-br from-orange-500 to-yellow-500 rounded-2xl flex items-center justify-center text-2xl shadow-lg">🧾</div> | |
| <div> | |
| <h3 className="font-bold text-lg group-hover:text-orange-400 transition-colors uppercase tracking-tight">{s.client}</h3> | |
| <p className="text-gray-500 text-xs font-medium mt-1">PRODUCTOS: <span className="text-gray-400">{s.items}</span></p> | |
| </div> | |
| </div> | |
| <div className="text-right"> | |
| <p className="text-2xl font-black text-white">${Number(s.total).toLocaleString()}</p> | |
| <p className="text-[10px] text-gray-600 font-black uppercase mt-1">Completado</p> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| {isModalOpen && ( | |
| <div className="fixed inset-0 bg-[#0f172a]/95 backdrop-blur-3xl flex items-center justify-center p-6 z-50"> | |
| <div className="bg-white/5 border border-white/10 p-12 rounded-[50px] w-full max-w-lg shadow-2xl relative overflow-hidden"> | |
| <div className="absolute top-0 right-0 w-32 h-32 bg-orange-500/10 blur-[80px] rounded-full"></div> | |
| <h2 className="text-3xl font-black mb-10 tracking-tighter italic">NUEVA VENTA /</h2> | |
| <form onSubmit={handleAdd} className="space-y-6"> | |
| <input | |
| placeholder="Nombre del Cliente" required | |
| value={newSale.client} onChange={e => setNewSale({...newSale, client: e.target.value})} | |
| className="w-full bg-white/5 border-b border-white/10 py-4 text-xl outline-none focus:border-orange-500 transition-all font-bold" | |
| /> | |
| <input | |
| placeholder="Descripción de Ítems" required | |
| value={newSale.items} onChange={e => setNewSale({...newSale, items: e.target.value})} | |
| className="w-full bg-white/5 border-b border-white/10 py-4 text-lg outline-none focus:border-orange-500 transition-all" | |
| /> | |
| <div className="relative"> | |
| <span className="absolute left-0 top-1/2 -translate-y-1/2 text-3xl font-black text-gray-700">$</span> | |
| <input | |
| type="number" placeholder="0.00" required | |
| value={newSale.total} onChange={e => setNewSale({...newSale, total: Number(e.target.value)})} | |
| className="w-full bg-transparent border-b border-white/10 pl-10 py-6 text-6xl outline-none focus:border-orange-500 transition-all font-black text-orange-400 placeholder:text-gray-800" | |
| /> | |
| </div> | |
| <div className="flex gap-4 pt-12"> | |
| <button type="button" onClick={() => setIsModalOpen(false)} className="px-8 py-5 text-gray-500 font-black text-xs uppercase hover:text-white transition-colors tracking-widest">Descartar</button> | |
| <button type="submit" className="flex-1 bg-orange-600 hover:bg-orange-500 py-5 rounded-3xl font-black text-xs uppercase tracking-[0.2em] transition-all shadow-xl shadow-orange-900/20">Finalizar Venta</button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |