almeira-web/dripzone-app/src/app/admin/usuarios/page.tsx

677 lines
30 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import React, { useState, useEffect } from 'react';
import { Users, UserPlus, Shield, ShieldCheck, UserCheck, MessageCircle, Mail, Phone, MapPin, Eye, EyeOff, Trash2, Edit, Check, X, Lock, Key } from 'lucide-react';
import { User } from '@/types';
export default function AdminUsersPage() {
const [users, setUsers] = useState<User[]>([]);
const [search, setSearch] = useState('');
const [roleFilter, setRoleFilter] = useState<'all' | 'admin' | 'operator' | 'customer'>('all');
const [statusFilter, setStatusFilter] = useState<'all' | 'active' | 'inactive'>('all');
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingUser, setEditingUser] = useState<User | null>(null);
const [toastMessage, setToastMessage] = useState<string | null>(null);
// New user modal form
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
const [city, setCity] = useState('Cali');
const [address, setAddress] = useState('');
const [role, setRole] = useState<'admin' | 'operator' | 'customer'>('operator');
const [password, setPassword] = useState('');
// Edit user modal form
const [editName, setEditName] = useState('');
const [editEmail, setEditEmail] = useState('');
const [editPhone, setEditPhone] = useState('');
const [editCity, setEditCity] = useState('Cali');
const [editAddress, setEditAddress] = useState('');
const [editRole, setEditRole] = useState<'admin' | 'operator' | 'customer'>('operator');
const [editPassword, setEditPassword] = useState('');
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
try {
const res = await fetch('/api/users');
const data = await res.json();
if (data.success) {
setUsers(data.users);
}
} catch (e) {
console.error(e);
}
};
const handleToggleStatus = async (user: User) => {
const newStatus = !user.isActive;
setUsers((prev) => prev.map((u) => (u.id === user.id ? { ...u, isActive: newStatus } : u)));
try {
await fetch('/api/users', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: user.id, updates: { isActive: newStatus } }),
});
setToastMessage(`Estado de ${user.name} actualizado a ${newStatus ? '🟢 ACTIVO' : '⚪ INACTIVO'}.`);
} catch (e) {
console.error(e);
}
};
const handleChangeRole = async (user: User, newRole: 'admin' | 'operator' | 'customer') => {
setUsers((prev) => prev.map((u) => (u.id === user.id ? { ...u, role: newRole } : u)));
try {
await fetch('/api/users', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: user.id, updates: { role: newRole } }),
});
setToastMessage(`Rol de ${user.name} actualizado a [${newRole.toUpperCase()}].`);
} catch (e) {
console.error(e);
}
};
const handleDeleteUser = async (id: string, name: string) => {
if (!confirm(`¿Estás seguro de eliminar permanentemente al usuario ${name}?`)) return;
setUsers((prev) => prev.filter((u) => u.id !== id));
try {
await fetch(`/api/users?id=${id}`, { method: 'DELETE' });
setToastMessage(`🗑️ Usuario ${name} eliminado del sistema.`);
} catch (e) {
console.error(e);
}
};
const handleCreateUser = async (e: React.FormEvent) => {
e.preventDefault();
try {
const res = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, email, phone, city, address, role, password }),
});
const data = await res.json();
if (data.success) {
setUsers((prev) => [data.user, ...prev]);
setIsModalOpen(false);
setName('');
setEmail('');
setPhone('');
setAddress('');
setPassword('');
setToastMessage(`✨ Usuario "${data.user.name}" registrado con éxito.`);
} else {
alert(data.error);
}
} catch (e) {
console.error(e);
}
};
const handleOpenEdit = (user: User) => {
setEditingUser(user);
setEditName(user.name);
setEditEmail(user.email);
setEditPhone(user.phone);
setEditCity(user.city || 'Cali');
setEditAddress(user.address || '');
setEditRole(user.role);
setEditPassword(user.password || '');
};
const handleSaveEdit = async (e: React.FormEvent) => {
e.preventDefault();
if (!editingUser) return;
const updates = {
name: editName,
email: editEmail,
phone: editPhone,
city: editCity,
address: editAddress,
role: editRole,
password: editPassword,
};
setUsers((prev) => prev.map((u) => (u.id === editingUser.id ? { ...u, ...updates } : u)));
try {
const res = await fetch('/api/users', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: editingUser.id, updates }),
});
const data = await res.json();
if (data.success) {
setEditingUser(null);
setToastMessage(`✅ Datos de "${editName}" actualizados correctamente.`);
}
} catch (e) {
console.error(e);
}
};
const handleWhatsApp = (user: User) => {
const cleanPhone = user.phone.startsWith('57') ? user.phone : `57${user.phone}`;
const text = encodeURIComponent(`Hola ${user.name}! Te escribimos del equipo administrativo de DripZone 🟣`);
window.open(`https://wa.me/${cleanPhone}?text=${text}`, '_blank');
};
const filtered = users.filter((u) => {
const matchSearch =
u.name.toLowerCase().includes(search.toLowerCase()) ||
u.email.toLowerCase().includes(search.toLowerCase()) ||
u.phone.includes(search) ||
(u.city && u.city.toLowerCase().includes(search.toLowerCase()));
if (!matchSearch) return false;
if (roleFilter !== 'all' && u.role !== roleFilter) return false;
if (statusFilter === 'active' && !u.isActive) return false;
if (statusFilter === 'inactive' && u.isActive) return false;
return true;
});
const adminsCount = users.filter((u) => u.role === 'admin').length;
const operatorsCount = users.filter((u) => u.role === 'operator').length;
const customersCount = users.filter((u) => u.role === 'customer').length;
return (
<div className="space-y-6">
{/* Header */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h1 className="text-2xl font-black text-white flex items-center gap-2">
<Users className="w-6 h-6 text-purple-400" />
<span>Control de Usuarios, Roles & Permisos ({users.length})</span>
</h1>
<p className="text-xs text-neutral-400 mt-1">
Crea, edita, activa/inactiva y asigna roles a <strong>Administradores</strong>, <strong>Operadores de Despachos</strong> y <strong>Clientes</strong>.
</p>
</div>
<button
onClick={() => setIsModalOpen(true)}
className="bg-gradient-to-r from-purple-600 to-fuchsia-500 hover:from-purple-500 hover:to-fuchsia-400 text-white font-bold text-xs px-4 py-2.5 rounded-xl flex items-center space-x-1.5 shadow-[0_0_15px_rgba(168,85,247,0.3)] cursor-pointer"
>
<UserPlus className="w-4 h-4" />
<span>+ Registrar Nuevo Usuario Admin</span>
</button>
</div>
{toastMessage && (
<div className="bg-purple-950/80 border border-purple-500 text-purple-300 px-4 py-3 rounded-2xl text-xs flex items-center justify-between shadow-md animate-in fade-in">
<span>{toastMessage}</span>
<button onClick={() => setToastMessage(null)} className="font-bold text-white cursor-pointer ml-3">
✕
</button>
</div>
)}
{/* KPI Stats Cards */}
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div className="bg-neutral-900 border border-neutral-800 rounded-2xl p-4 flex items-center space-x-3 shadow-lg">
<div className="w-10 h-10 rounded-xl bg-purple-500/20 border border-purple-500/30 flex items-center justify-center text-purple-400">
<Shield className="w-5 h-5" />
</div>
<div>
<p className="text-[11px] text-neutral-400 font-semibold">Super Administradores</p>
<p className="text-xl font-black text-white">{adminsCount}</p>
</div>
</div>
<div className="bg-neutral-900 border border-neutral-800 rounded-2xl p-4 flex items-center space-x-3 shadow-lg">
<div className="w-10 h-10 rounded-xl bg-fuchsia-500/20 border border-fuchsia-500/30 flex items-center justify-center text-fuchsia-400">
<ShieldCheck className="w-5 h-5" />
</div>
<div>
<p className="text-[11px] text-neutral-400 font-semibold">Operadores Logísticos</p>
<p className="text-xl font-black text-white">{operatorsCount}</p>
</div>
</div>
<div className="bg-neutral-900 border border-neutral-800 rounded-2xl p-4 flex items-center space-x-3 shadow-lg">
<div className="w-10 h-10 rounded-xl bg-emerald-500/20 border border-emerald-500/30 flex items-center justify-center text-emerald-400">
<UserCheck className="w-5 h-5" />
</div>
<div>
<p className="text-[11px] text-neutral-400 font-semibold">Clientes Registrados</p>
<p className="text-xl font-black text-white">{customersCount}</p>
</div>
</div>
</div>
{/* Filters Toolbar */}
<div className="flex flex-col md:flex-row items-stretch md:items-center justify-between gap-3">
{/* Role Pills */}
<div className="flex items-center space-x-1.5 bg-neutral-900 border border-neutral-800 p-1 rounded-2xl overflow-x-auto">
<button
onClick={() => setRoleFilter('all')}
className={`text-xs font-bold px-3 py-1.5 rounded-xl transition-all cursor-pointer ${
roleFilter === 'all' ? 'bg-white text-neutral-950 shadow' : 'text-neutral-400 hover:text-white'
}`}
>
Todos ({users.length})
</button>
<button
onClick={() => setRoleFilter('admin')}
className={`text-xs font-bold px-3 py-1.5 rounded-xl transition-all cursor-pointer ${
roleFilter === 'admin' ? 'bg-purple-500/30 text-purple-300 border border-purple-500/50 shadow' : 'text-neutral-400 hover:text-purple-300'
}`}
>
Admins ({adminsCount})
</button>
<button
onClick={() => setRoleFilter('operator')}
className={`text-xs font-bold px-3 py-1.5 rounded-xl transition-all cursor-pointer ${
roleFilter === 'operator' ? 'bg-fuchsia-500/30 text-fuchsia-300 border border-fuchsia-500/50 shadow' : 'text-neutral-400 hover:text-fuchsia-300'
}`}
>
Operadores ({operatorsCount})
</button>
<button
onClick={() => setRoleFilter('customer')}
className={`text-xs font-bold px-3 py-1.5 rounded-xl transition-all cursor-pointer ${
roleFilter === 'customer' ? 'bg-emerald-500/30 text-emerald-300 border border-emerald-500/50 shadow' : 'text-neutral-400 hover:text-emerald-300'
}`}
>
Clientes ({customersCount})
</button>
</div>
{/* Search */}
<div className="max-w-xs w-full">
<input
type="text"
placeholder="Buscar por nombre, correo o celular..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-full bg-neutral-900 border border-neutral-800 rounded-xl px-3.5 py-2 text-xs text-white outline-hidden focus:border-purple-400"
/>
</div>
</div>
{/* Users Table */}
<div className="bg-neutral-900 border border-neutral-800 rounded-3xl overflow-hidden shadow-xl">
<div className="overflow-x-auto">
<table className="w-full text-left text-xs text-neutral-300">
<thead className="bg-neutral-950 text-neutral-400 font-bold uppercase text-[10px] border-b border-neutral-800">
<tr>
<th className="py-3.5 px-4">Estado</th>
<th className="py-3.5 px-4">Usuario</th>
<th className="py-3.5 px-4">Contacto Directo</th>
<th className="py-3.5 px-4">Ciudad / Dirección</th>
<th className="py-3.5 px-4">Rol & Permisos</th>
<th className="py-3.5 px-4">Último Acceso</th>
<th className="py-3.5 px-4 text-center">Acciones (CRUD)</th>
</tr>
</thead>
<tbody className="divide-y divide-neutral-800/60">
{filtered.length === 0 ? (
<tr>
<td colSpan={7} className="text-center py-8 text-neutral-500">
No se encontraron usuarios con el filtro seleccionado.
</td>
</tr>
) : (
filtered.map((user) => {
return (
<tr
key={user.id}
className={`hover:bg-neutral-800/50 transition-colors ${
!user.isActive ? 'opacity-65 bg-neutral-950/40' : ''
}`}
>
{/* Active Status Toggle */}
<td className="py-3.5 px-4">
<button
onClick={() => handleToggleStatus(user)}
className={`text-[11px] font-bold px-2.5 py-1 rounded-xl border flex items-center gap-1.5 transition-all cursor-pointer ${
user.isActive
? 'bg-emerald-500/20 border-emerald-500/40 text-emerald-300 hover:bg-emerald-500/30'
: 'bg-neutral-950 border-neutral-700 text-neutral-400 hover:text-white'
}`}
title="Activar o suspender acceso"
>
{user.isActive ? (
<>
<Eye className="w-3.5 h-3.5 text-emerald-400" />
<span>Activo</span>
</>
) : (
<>
<EyeOff className="w-3.5 h-3.5 text-neutral-400" />
<span>Inactivo</span>
</>
)}
</button>
</td>
{/* User Avatar & Name */}
<td className="py-3.5 px-4 flex items-center space-x-3">
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-purple-600 to-fuchsia-500 flex items-center justify-center text-white font-bold text-xs shadow-sm shrink-0">
{user.name.charAt(0).toUpperCase()}
</div>
<div>
<p className="font-bold text-white text-xs">{user.name}</p>
<span className="text-[10px] text-neutral-400">{user.email}</span>
</div>
</td>
{/* Contact */}
<td className="py-3.5 px-4">
<div className="space-y-1">
<button
onClick={() => handleWhatsApp(user)}
className="text-[11px] text-emerald-400 hover:text-emerald-300 font-bold flex items-center gap-1 cursor-pointer"
>
<MessageCircle className="w-3.5 h-3.5" />
<span>{user.phone}</span>
</button>
</div>
</td>
{/* City */}
<td className="py-3.5 px-4">
<div className="text-[11px]">
<span className="text-white font-medium">{user.city || 'Cali'}</span>
{user.address && <p className="text-[10px] text-neutral-400 truncate max-w-xs">{user.address}</p>}
</div>
</td>
{/* Role Dropdown */}
<td className="py-3.5 px-4">
<select
value={user.role}
onChange={(e) => handleChangeRole(user, e.target.value as any)}
className={`text-[11px] font-bold px-2 py-1 rounded-lg border outline-hidden cursor-pointer ${
user.role === 'admin'
? 'bg-purple-500/20 border-purple-500/40 text-purple-300'
: user.role === 'operator'
? 'bg-fuchsia-500/20 border-fuchsia-500/40 text-fuchsia-300'
: 'bg-emerald-500/20 border-emerald-500/40 text-emerald-300'
}`}
>
<option value="admin" className="bg-neutral-900 text-white">👑 Admin Total</option>
<option value="operator" className="bg-neutral-900 text-white">📦 Operador Logística</option>
<option value="customer" className="bg-neutral-900 text-white">🛍️ Cliente Comprador</option>
</select>
</td>
{/* Last Login */}
<td className="py-3.5 px-4 text-neutral-400 text-[11px] font-mono">
{user.lastLogin || user.createdAt}
</td>
{/* Actions (CRUD: Edit & Delete) */}
<td className="py-3.5 px-4 text-center">
<div className="flex items-center justify-center space-x-1.5">
<button
onClick={() => handleOpenEdit(user)}
className="text-neutral-400 hover:text-purple-300 p-1.5 rounded-lg hover:bg-purple-500/10 cursor-pointer transition-colors"
title="Editar datos del usuario"
>
<Edit className="w-4 h-4" />
</button>
<button
onClick={() => handleDeleteUser(user.id, user.name)}
className="text-neutral-400 hover:text-red-400 p-1.5 rounded-lg hover:bg-red-500/10 cursor-pointer transition-colors"
title="Eliminar usuario"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</td>
</tr>
);
})
)}
</tbody>
</table>
</div>
</div>
{/* Modal Create User (POST) */}
{isModalOpen && (
<div className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xs flex items-center justify-center p-4 animate-in fade-in">
<div className="bg-neutral-900 border border-neutral-800 rounded-3xl max-w-md w-full p-6 space-y-4 shadow-2xl">
<div className="flex items-center justify-between border-b border-neutral-800 pb-3">
<h3 className="font-bold text-white text-base flex items-center gap-2">
<UserPlus className="w-5 h-5 text-purple-400" />
<span>Registrar Nuevo Usuario / Admin</span>
</h3>
<button onClick={() => setIsModalOpen(false)} className="text-neutral-400 hover:text-white cursor-pointer">
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handleCreateUser} className="space-y-3 text-xs">
<div>
<label className="block text-neutral-400 font-bold mb-1">Nombre Completo *</label>
<input
type="text"
required
placeholder="ej: Andrés Operador"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-neutral-400 font-bold mb-1">Correo Electrónico *</label>
<input
type="email"
required
placeholder="andres@dripzone.co"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
<div>
<label className="block text-neutral-400 font-bold mb-1">WhatsApp / Celular *</label>
<input
type="tel"
required
placeholder="3167455877"
value={phone}
onChange={(e) => setPhone(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-neutral-400 font-bold mb-1">Ciudad Principal</label>
<input
type="text"
value={city}
onChange={(e) => setCity(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
<div>
<label className="block text-neutral-400 font-bold mb-1">Contraseña (Opcional)</label>
<input
type="password"
placeholder="••••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
</div>
<div>
<label className="block text-neutral-400 font-bold mb-1">Dirección (Opcional)</label>
<input
type="text"
placeholder="ej: Bodega Central / Cali"
value={address}
onChange={(e) => setAddress(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
<div>
<label className="block text-purple-400 font-bold mb-1">Rol y Nivel de Acceso *</label>
<select
value={role}
onChange={(e) => setRole(e.target.value as any)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden font-bold"
>
<option value="admin">👑 Super Administrador (Acceso Total)</option>
<option value="operator">📦 Operador de Despachos & Logística</option>
<option value="customer">🛍️ Cliente Registrado</option>
</select>
</div>
<div className="flex justify-end space-x-2 pt-3 border-t border-neutral-800">
<button
type="button"
onClick={() => setIsModalOpen(false)}
className="bg-neutral-800 hover:bg-neutral-700 text-neutral-300 font-bold py-2 px-4 rounded-xl cursor-pointer"
>
Cancelar
</button>
<button
type="submit"
className="bg-gradient-to-r from-purple-600 to-fuchsia-500 hover:from-purple-500 hover:to-fuchsia-400 text-white font-bold py-2 px-4 rounded-xl shadow-md cursor-pointer"
>
Guardar Usuario
</button>
</div>
</form>
</div>
</div>
)}
{/* Modal Edit User (PUT) */}
{editingUser && (
<div className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xs flex items-center justify-center p-4 animate-in fade-in">
<div className="bg-neutral-900 border border-purple-500/40 rounded-3xl max-w-md w-full p-6 space-y-4 shadow-2xl">
<div className="flex items-center justify-between border-b border-neutral-800 pb-3">
<h3 className="font-bold text-white text-base flex items-center gap-2">
<Edit className="w-5 h-5 text-purple-400" />
<span>Editar Usuario: {editingUser.name}</span>
</h3>
<button onClick={() => setEditingUser(null)} className="text-neutral-400 hover:text-white cursor-pointer">
<X className="w-5 h-5" />
</button>
</div>
<form onSubmit={handleSaveEdit} className="space-y-3 text-xs">
<div>
<label className="block text-neutral-400 font-bold mb-1">Nombre Completo *</label>
<input
type="text"
required
value={editName}
onChange={(e) => setEditName(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-neutral-400 font-bold mb-1">Correo Electrónico *</label>
<input
type="email"
required
value={editEmail}
onChange={(e) => setEditEmail(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
<div>
<label className="block text-neutral-400 font-bold mb-1">WhatsApp / Celular *</label>
<input
type="tel"
required
value={editPhone}
onChange={(e) => setEditPhone(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-neutral-400 font-bold mb-1">Ciudad Principal</label>
<input
type="text"
value={editCity}
onChange={(e) => setEditCity(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
<div>
<label className="block text-neutral-400 font-bold mb-1">Nueva Contraseña</label>
<input
type="password"
placeholder="•••••••• (dejar vacio si no cambia)"
value={editPassword}
onChange={(e) => setEditPassword(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
</div>
<div>
<label className="block text-neutral-400 font-bold mb-1">Dirección</label>
<input
type="text"
value={editAddress}
onChange={(e) => setEditAddress(e.target.value)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden focus:border-purple-400"
/>
</div>
<div>
<label className="block text-purple-400 font-bold mb-1">Rol y Privilegios *</label>
<select
value={editRole}
onChange={(e) => setEditRole(e.target.value as any)}
className="w-full bg-neutral-950 border border-neutral-800 rounded-xl px-3 py-2 text-white outline-hidden font-bold"
>
<option value="admin">👑 Super Administrador (Acceso Total)</option>
<option value="operator">📦 Operador de Despachos & Logística</option>
<option value="customer">🛍️ Cliente Registrado</option>
</select>
</div>
<div className="flex justify-end space-x-2 pt-3 border-t border-neutral-800">
<button
type="button"
onClick={() => setEditingUser(null)}
className="bg-neutral-800 hover:bg-neutral-700 text-neutral-300 font-bold py-2 px-4 rounded-xl cursor-pointer"
>
Cancelar
</button>
<button
type="submit"
className="bg-gradient-to-r from-purple-600 to-fuchsia-500 hover:from-purple-500 hover:to-fuchsia-400 text-white font-bold py-2 px-4 rounded-xl shadow-md cursor-pointer"
>
Guardar Cambios
</button>
</div>
</form>
</div>
</div>
)}
</div>
);
}