feat: add reservation import script for Excel data and install xlsx dependency
This commit is contained in:
@@ -13,6 +13,9 @@ interface UserProfileRow {
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
// Toggle para reactivar el borrado de usuarios desde la UI.
|
||||
const SHOW_DELETE_USER = false as boolean;
|
||||
|
||||
const ROLE_OPTIONS: { value: UserRole; label: string; description: string; icon: typeof Shield }[] = [
|
||||
{ value: 'admin', label: 'Admin', description: 'Acceso total y gestión de usuarios', icon: Shield },
|
||||
{ value: 'internal_staff', label: 'Staff', description: 'Gestión completa de reservas', icon: UsersIcon },
|
||||
@@ -34,7 +37,8 @@ export function UserManagement() {
|
||||
const [users, setUsers] = useState<UserProfileRow[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [inviteOpen, setInviteOpen] = useState(false);
|
||||
// Invitación deshabilitada por ahora (sin SMTP). El modal y endpoint se conservan.
|
||||
const [inviteOpen, _setInviteOpen] = useState(false);
|
||||
const [savingId, setSavingId] = useState<string | null>(null);
|
||||
|
||||
const loadUsers = async () => {
|
||||
@@ -84,6 +88,7 @@ export function UserManagement() {
|
||||
}
|
||||
};
|
||||
|
||||
// Borrado deshabilitado en UI; se conserva para reactivar.
|
||||
const handleDelete = async (user: UserProfileRow) => {
|
||||
if (!confirm(`¿Eliminar a ${user.email}? Esta acción no se puede deshacer.`)) return;
|
||||
setSavingId(user.id);
|
||||
@@ -104,19 +109,9 @@ export function UserManagement() {
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-4 md:p-6">
|
||||
<header className="mb-6 flex flex-col md:flex-row md:items-end justify-between gap-3">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black text-white tracking-tight mb-1">Usuarios</h1>
|
||||
<p className="text-slate-400 text-sm">Gestiona accesos y roles del equipo.</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setInviteOpen(true)}
|
||||
className="inline-flex items-center gap-2 px-5 py-3 bg-emerald-600 hover:bg-emerald-500 text-white font-bold rounded-2xl shadow-lg shadow-emerald-900/30 transition-all"
|
||||
>
|
||||
<UserPlus className="w-4 h-4" />
|
||||
Invitar usuario
|
||||
</button>
|
||||
<header className="mb-6">
|
||||
<h1 className="text-3xl md:text-4xl font-black text-white tracking-tight mb-1">Usuarios</h1>
|
||||
<p className="text-slate-400 text-sm">Gestiona accesos y roles del equipo. Las altas se hacen desde Supabase.</p>
|
||||
</header>
|
||||
|
||||
{error && (
|
||||
@@ -138,13 +133,13 @@ export function UserManagement() {
|
||||
<th className="px-4 py-3 text-left font-bold">Email / Nombre</th>
|
||||
<th className="px-4 py-3 text-left font-bold">Rol</th>
|
||||
<th className="px-4 py-3 text-left font-bold hidden md:table-cell">Creado</th>
|
||||
<th className="px-4 py-3 text-right font-bold">Acciones</th>
|
||||
{SHOW_DELETE_USER && <th className="px-4 py-3 text-right font-bold">Acciones</th>}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={4} className="px-4 py-12 text-center text-slate-500">
|
||||
<td colSpan={SHOW_DELETE_USER ? 4 : 3} className="px-4 py-12 text-center text-slate-500">
|
||||
No hay usuarios registrados.
|
||||
</td>
|
||||
</tr>
|
||||
@@ -171,17 +166,19 @@ export function UserManagement() {
|
||||
<td className="px-4 py-3 text-slate-400 text-xs hidden md:table-cell">
|
||||
{new Date(user.created_at).toLocaleDateString('es-ES')}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
<button
|
||||
type="button"
|
||||
disabled={savingId === user.id}
|
||||
onClick={() => handleDelete(user)}
|
||||
className="inline-flex p-2 text-slate-500 hover:text-red-400 transition-colors"
|
||||
title="Eliminar"
|
||||
>
|
||||
{savingId === user.id ? <Loader2 className="w-4 h-4 animate-spin" /> : <Trash2 className="w-4 h-4" />}
|
||||
</button>
|
||||
</td>
|
||||
{SHOW_DELETE_USER && (
|
||||
<td className="px-4 py-3 text-right">
|
||||
<button
|
||||
type="button"
|
||||
disabled={savingId === user.id}
|
||||
onClick={() => handleDelete(user)}
|
||||
className="inline-flex p-2 text-slate-500 hover:text-red-400 transition-colors"
|
||||
title="Eliminar"
|
||||
>
|
||||
{savingId === user.id ? <Loader2 className="w-4 h-4 animate-spin" /> : <Trash2 className="w-4 h-4" />}
|
||||
</button>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@@ -192,10 +189,10 @@ export function UserManagement() {
|
||||
<AnimatePresence>
|
||||
{inviteOpen && (
|
||||
<InviteModal
|
||||
onClose={() => setInviteOpen(false)}
|
||||
onClose={() => _setInviteOpen(false)}
|
||||
onInvited={(newUser) => {
|
||||
setUsers(prev => [newUser, ...prev]);
|
||||
setInviteOpen(false);
|
||||
_setInviteOpen(false);
|
||||
toast.success('Invitación enviada');
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user