first commit
This commit is contained in:
191
frontend/src/pages/AdminPage.jsx
Normal file
191
frontend/src/pages/AdminPage.jsx
Normal file
@@ -0,0 +1,191 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { usersApi } from '../api/client';
|
||||
|
||||
const SHEET_LABELS = {
|
||||
ventas: 'Ventas',
|
||||
servicios: 'Servicios',
|
||||
alquileres: 'Alquileres',
|
||||
gastos_diarios: 'Gastos diarios',
|
||||
gastos_fijos: 'Gastos fijos',
|
||||
personal: 'Personal',
|
||||
reportes: 'Reportes',
|
||||
};
|
||||
|
||||
const SHEET_KEYS = Object.keys(SHEET_LABELS);
|
||||
|
||||
function emptyPerms() {
|
||||
return Object.fromEntries(SHEET_KEYS.map((k) => [k, { view: false, edit: false }]));
|
||||
}
|
||||
|
||||
export default function AdminPage() {
|
||||
const [users, setUsers] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const [newUser, setNewUser] = useState({ username: '', password: '', permissions: emptyPerms() });
|
||||
|
||||
const load = async () => {
|
||||
try {
|
||||
setUsers(await usersApi.list());
|
||||
} catch (e) {
|
||||
setError(e.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
load();
|
||||
}, []);
|
||||
|
||||
const handleCreate = async (e) => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
try {
|
||||
await usersApi.create(newUser);
|
||||
setNewUser({ username: '', password: '', permissions: emptyPerms() });
|
||||
await load();
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const updateUserPerms = async (user, sheet, field, value) => {
|
||||
const permissions = { ...user.permissions, [sheet]: { ...user.permissions[sheet], [field]: value } };
|
||||
try {
|
||||
await usersApi.update(user.id, { permissions });
|
||||
await load();
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleActive = async (user) => {
|
||||
if (user.isAdmin) return;
|
||||
try {
|
||||
await usersApi.update(user.id, { active: !user.active });
|
||||
await load();
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <p className="loading">Cargando usuarios…</p>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="page-title">Administración de usuarios</h1>
|
||||
<p style={{ color: 'var(--muted)' }}>
|
||||
El superusuario <strong>JEFE</strong> puede crear usuarios, asignar contraseñas y habilitar acceso por planilla.
|
||||
</p>
|
||||
{error && <div className="alert alert-error">{error}</div>}
|
||||
|
||||
<div className="card admin-grid" style={{ marginBottom: '1.5rem' }}>
|
||||
<h2 style={{ marginTop: 0 }}>Nuevo usuario</h2>
|
||||
<form onSubmit={handleCreate}>
|
||||
<div className="form-group">
|
||||
<label>Usuario</label>
|
||||
<input
|
||||
value={newUser.username}
|
||||
onChange={(e) => setNewUser((p) => ({ ...p, username: e.target.value }))}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>Contraseña</label>
|
||||
<input
|
||||
type="password"
|
||||
value={newUser.password}
|
||||
onChange={(e) => setNewUser((p) => ({ ...p, password: e.target.value }))}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<p style={{ fontSize: '0.85rem' }}>Permisos por planilla:</p>
|
||||
<div className="perm-grid">
|
||||
{SHEET_KEYS.filter((k) => k !== 'reportes').map((key) => (
|
||||
<div key={key} className="perm-item">
|
||||
<strong>{SHEET_LABELS[key]}</strong>
|
||||
<label className="checkbox-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={newUser.permissions[key]?.view}
|
||||
onChange={(e) =>
|
||||
setNewUser((p) => ({
|
||||
...p,
|
||||
permissions: {
|
||||
...p.permissions,
|
||||
[key]: { ...p.permissions[key], view: e.target.checked },
|
||||
},
|
||||
}))
|
||||
}
|
||||
/>
|
||||
Ver
|
||||
</label>
|
||||
<label className="checkbox-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={newUser.permissions[key]?.edit}
|
||||
onChange={(e) =>
|
||||
setNewUser((p) => ({
|
||||
...p,
|
||||
permissions: {
|
||||
...p.permissions,
|
||||
[key]: { ...p.permissions[key], edit: e.target.checked },
|
||||
},
|
||||
}))
|
||||
}
|
||||
/>
|
||||
Editar
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<button type="submit" className="btn btn-primary" style={{ marginTop: '1rem' }}>
|
||||
Crear usuario
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="card">
|
||||
<h2 style={{ marginTop: 0 }}>Usuarios del sistema</h2>
|
||||
{users.map((u) => (
|
||||
<div key={u.id} style={{ borderBottom: '1px solid var(--border)', padding: '1rem 0' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem', flexWrap: 'wrap' }}>
|
||||
<strong>{u.username}</strong>
|
||||
{u.isAdmin && <span style={{ color: 'var(--accent)' }}>Administrador</span>}
|
||||
{!u.isAdmin && (
|
||||
<button type="button" className="btn btn-secondary" onClick={() => toggleActive(u)}>
|
||||
{u.active ? 'Deshabilitar' : 'Habilitar'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{!u.isAdmin && (
|
||||
<div className="perm-grid" style={{ marginTop: '0.75rem' }}>
|
||||
{SHEET_KEYS.map((key) => (
|
||||
<div key={key} className="perm-item">
|
||||
<span>{SHEET_LABELS[key]}</span>
|
||||
<label className="checkbox-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={u.permissions?.[key]?.view}
|
||||
onChange={(e) => updateUserPerms(u, key, 'view', e.target.checked)}
|
||||
/>
|
||||
Ver
|
||||
</label>
|
||||
<label className="checkbox-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={u.permissions?.[key]?.edit}
|
||||
onChange={(e) => updateUserPerms(u, key, 'edit', e.target.checked)}
|
||||
/>
|
||||
Editar
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user