Spaces:
Running
Running
| "use client"; | |
| import React, { useState, useEffect } from "react"; | |
| import { db } from "@/lib/firebase"; | |
| import { collection, onSnapshot, addDoc, deleteDoc, doc } from "firebase/firestore"; | |
| import Link from "next/link"; | |
| interface Employee { | |
| id: string; | |
| name: string; | |
| position: string; | |
| salary: number; | |
| } | |
| export default function HRPage() { | |
| const [employees, setEmployees] = useState<Employee[]>([]); | |
| const [isModalOpen, setIsModalOpen] = useState(false); | |
| const [newEmp, setNewEmp] = useState({ name: "", position: "", salary: 0 }); | |
| useEffect(() => { | |
| const unsub = onSnapshot(collection(db, "employees"), (snap) => { | |
| const data = snap.docs.map(doc => ({ id: doc.id, ...doc.data() } as Employee)); | |
| setEmployees(data); | |
| }); | |
| return () => unsub(); | |
| }, []); | |
| const handleAdd = async (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| await addDoc(collection(db, "employees"), newEmp); | |
| setIsModalOpen(false); | |
| setNewEmp({ name: "", position: "", salary: 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-cyan-400 hover:text-cyan-300 transition-colors flex items-center gap-2 mb-2 font-medium"> | |
| ← Panel Principal | |
| </Link> | |
| <h1 className="text-4xl font-extrabold tracking-tight">Gestión Humana</h1> | |
| </div> | |
| <button | |
| onClick={() => setIsModalOpen(true)} | |
| className="px-8 py-3 bg-white/10 hover:bg-white/20 border border-white/10 rounded-full font-bold transition-all backdrop-blur-md" | |
| > | |
| ➕ Registrar Personal | |
| </button> | |
| </header> | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> | |
| {employees.map((e) => ( | |
| <div key={e.id} className="bg-gradient-to-br from-[#1e293b] to-[#0f172a] border border-white/5 rounded-[2rem] p-8 hover:border-cyan-500/30 transition-all group shadow-2xl"> | |
| <div className="flex items-center gap-4 mb-6"> | |
| <div className="w-12 h-12 bg-cyan-500/20 text-cyan-400 rounded-2xl flex items-center justify-center text-xl font-bold"> | |
| {e.name.charAt(0)} | |
| </div> | |
| <div> | |
| <h3 className="font-bold group-hover:text-cyan-400 transition-colors">{e.name}</h3> | |
| <span className="text-[10px] text-gray-500 font-black uppercase tracking-widest">{e.position}</span> | |
| </div> | |
| </div> | |
| <div className="flex justify-between items-center py-4 border-y border-white/5 mb-6"> | |
| <span className="text-xs text-gray-500 uppercase font-bold">Salario</span> | |
| <span className="font-mono text-cyan-400 font-bold">${Number(e.salary).toLocaleString()}</span> | |
| </div> | |
| <button | |
| onClick={() => deleteDoc(doc(db, "employees", e.id))} | |
| className="w-full py-3 bg-red-500/5 hover:bg-red-500/20 text-red-500/60 hover:text-red-500 text-[10px] font-black uppercase tracking-widest rounded-xl transition-all border border-red-500/10" | |
| > | |
| Dar de Baja | |
| </button> | |
| </div> | |
| ))} | |
| </div> | |
| {isModalOpen && ( | |
| <div className="fixed inset-0 bg-[#0f172a]/95 backdrop-blur-xl flex items-center justify-center p-6 z-50"> | |
| <div className="w-full max-w-sm"> | |
| <h2 className="text-4xl font-black mb-8 text-white italic tracking-tighter">NEW STAFF /</h2> | |
| <form onSubmit={handleAdd} className="space-y-6"> | |
| <div className="space-y-1"> | |
| <label className="text-[10px] font-black text-cyan-500 uppercase ml-2">Nombre completo</label> | |
| <input required value={newEmp.name} onChange={v => setNewEmp({...newEmp, name: v.target.value})} className="w-full bg-white/5 border-b border-white/20 px-4 py-4 outline-none focus:border-cyan-500 transition-all font-medium text-lg"/> | |
| </div> | |
| <div className="space-y-1"> | |
| <label className="text-[10px] font-black text-cyan-500 uppercase ml-2">Cargo / Posición</label> | |
| <input required value={newEmp.position} onChange={v => setNewEmp({...newEmp, position: v.target.value})} className="w-full bg-white/5 border-b border-white/20 px-4 py-4 outline-none focus:border-cyan-500 transition-all font-medium text-lg"/> | |
| </div> | |
| <div className="space-y-1"> | |
| <label className="text-[10px] font-black text-cyan-500 uppercase ml-2">Salario Mensual</label> | |
| <input type="number" required value={newEmp.salary} onChange={v => setNewEmp({...newEmp, salary: Number(v.target.value)})} className="w-full bg-white/5 border-b border-white/20 px-4 py-4 outline-none focus:border-cyan-500 transition-all font-medium text-lg"/> | |
| </div> | |
| <div className="flex gap-4 pt-10"> | |
| <button type="button" onClick={() => setIsModalOpen(false)} className="px-6 py-4 text-gray-500 font-black text-xs uppercase hover:text-white transition-colors">Cancelar</button> | |
| <button type="submit" className="flex-1 bg-cyan-600 hover:bg-cyan-500 py-4 rounded-full font-black text-xs uppercase tracking-widest transition-all">Contratar</button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |