Solucionado submodulos y subiendo codigo real
This commit is contained in:
130
frontend/src/pages/Usuarios.jsx
Normal file
130
frontend/src/pages/Usuarios.jsx
Normal file
@@ -0,0 +1,130 @@
|
||||
// src/pages/Usuarios.jsx
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { api } from "../lib/api";
|
||||
import { Loader, ErrorBox, Filters, Select } from "../components/UI";
|
||||
|
||||
const ROLES = ["ADMINISTRADOR", "VENTAS", "COBRANZA"];
|
||||
|
||||
export default function Usuarios() {
|
||||
const [usuarios, setUsuarios] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
// form nuevo usuario
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [nombre, setNombre] = useState("");
|
||||
const [rol, setRol] = useState("COBRANZA");
|
||||
const [creando, setCreando] = useState(false);
|
||||
const [msg, setMsg] = useState(null);
|
||||
|
||||
const cargar = useCallback(() => {
|
||||
setLoading(true); setError(null);
|
||||
api.usuariosListar()
|
||||
.then((res) => { setUsuarios(res.usuarios || []); setLoading(false); })
|
||||
.catch((e) => { setError(e.message); setLoading(false); });
|
||||
}, []);
|
||||
|
||||
useEffect(() => { cargar(); }, [cargar]);
|
||||
|
||||
async function crear() {
|
||||
setMsg(null);
|
||||
if (!email.trim() || !password.trim()) { setMsg({ ok:false, txt:"Correo y contraseña son obligatorios." }); return; }
|
||||
setCreando(true);
|
||||
try {
|
||||
await api.usuariosCrear(email.trim(), password, nombre.trim(), rol);
|
||||
setMsg({ ok:true, txt:`Usuario ${email} creado como ${rol}.` });
|
||||
setEmail(""); setPassword(""); setNombre(""); setRol("COBRANZA");
|
||||
cargar();
|
||||
} catch (e) {
|
||||
setMsg({ ok:false, txt:"Error: " + e.message });
|
||||
}
|
||||
setCreando(false);
|
||||
}
|
||||
|
||||
async function cambiarRol(id, nuevoRol) {
|
||||
try { await api.usuariosActualizarRol(id, nuevoRol); cargar(); }
|
||||
catch (e) { alert("No se pudo cambiar el rol: " + e.message); }
|
||||
}
|
||||
|
||||
async function eliminar(id, correo) {
|
||||
if (!confirm(`¿Eliminar al usuario ${correo}? Esta acción no se puede deshacer.`)) return;
|
||||
try { await api.usuariosEliminar(id); cargar(); }
|
||||
catch (e) { alert("No se pudo eliminar: " + e.message); }
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="page-title">🔐 Usuarios</h1>
|
||||
|
||||
{/* Crear nuevo usuario */}
|
||||
<div style={{ background:"#fff", border:"1px solid #e2e8f0", borderRadius:12, padding:18, marginBottom:20 }}>
|
||||
<h3 style={{ fontSize:15, fontWeight:700, color:"#0f172a", marginBottom:14 }}>➕ Crear nuevo usuario</h3>
|
||||
<div style={{ display:"flex", gap:12, flexWrap:"wrap", alignItems:"flex-end" }}>
|
||||
<Campo label="Correo">
|
||||
<input value={email} onChange={(e)=>setEmail(e.target.value)} placeholder="usuario@escuela.com" style={inp} />
|
||||
</Campo>
|
||||
<Campo label="Contraseña">
|
||||
<input type="text" value={password} onChange={(e)=>setPassword(e.target.value)} placeholder="mín. 6 caracteres" style={inp} />
|
||||
</Campo>
|
||||
<Campo label="Nombre">
|
||||
<input value={nombre} onChange={(e)=>setNombre(e.target.value)} placeholder="Nombre completo" style={inp} />
|
||||
</Campo>
|
||||
<Campo label="Rol">
|
||||
<Select value={rol} options={ROLES} onChange={setRol} />
|
||||
</Campo>
|
||||
<button className="btn btn-primary" style={{height:38}} disabled={creando} onClick={crear}>
|
||||
{creando ? "Creando..." : "Crear usuario"}
|
||||
</button>
|
||||
</div>
|
||||
{msg && (
|
||||
<div style={{ marginTop:12, padding:10, borderRadius:8, fontSize:13,
|
||||
background: msg.ok ? "#d1fae5" : "#fee2e2", color: msg.ok ? "#065f46" : "#991b1b" }}>
|
||||
{msg.ok ? "✅" : "⚠️"} {msg.txt}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Lista de usuarios */}
|
||||
{loading ? <Loader text="Cargando usuarios..." /> :
|
||||
error ? <ErrorBox msg={error} /> :
|
||||
<div className="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th>CORREO</th><th>NOMBRE</th><th>ROL</th><th>ACCIONES</th></tr></thead>
|
||||
<tbody>
|
||||
{usuarios.map((u) => (
|
||||
<tr key={u.id}>
|
||||
<td className="col-name">{u.email}</td>
|
||||
<td>{u.nombre || "—"}</td>
|
||||
<td>
|
||||
<select value={u.rol} onChange={(e)=>cambiarRol(u.id, e.target.value)}
|
||||
style={{ padding:"5px 8px", border:"1px solid #cbd5e1", borderRadius:6, fontSize:12 }}>
|
||||
{ROLES.map((r)=><option key={r} value={r}>{r}</option>)}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button className="btn" style={{ padding:"4px 10px", fontSize:11, background:"#fee2e2", color:"#991b1b" }}
|
||||
onClick={()=>eliminar(u.id, u.email)}>🗑️ Eliminar</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{usuarios.length === 0 && (
|
||||
<tr><td colSpan={4} style={{ padding:20, textAlign:"center", color:"#94a3b8" }}>Sin usuarios.</td></tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Campo({ label, children }) {
|
||||
return (
|
||||
<div className="filter-group">
|
||||
<label>{label}</label>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const inp = { padding:"8px 12px", border:"1px solid #cbd5e1", borderRadius:8, fontSize:13, minWidth:180 };
|
||||
Reference in New Issue
Block a user