'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, AlertTriangle, Sparkles } from 'lucide-react'; import { User } from '@/types'; export default function AdminUsersPage() { const [users, setUsers] = useState([]); 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(null); const [toastMessage, setToastMessage] = useState(null); // Modal para configurar Garantía de Envío / Anticipo const [depositModalUser, setDepositModalUser] = useState(null); const [depositRequires, setDepositRequires] = useState(false); const [depositReasonInput, setDepositReasonInput] = useState(''); // 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 handleOpenDepositModal = (user: User) => { setDepositModalUser(user); setDepositRequires(user.requiresShippingDeposit === true); setDepositReasonInput(user.depositReason || ''); }; const handleSaveDepositSettings = async () => { if (!depositModalUser) return; const updates = { requiresShippingDeposit: depositRequires, depositReason: depositReasonInput.trim() || (depositRequires ? 'Garantía requerida por política administrativa' : 'Exonerado por administrador'), }; setUsers((prev) => prev.map((u) => (u.id === depositModalUser.id ? { ...u, ...updates } : u)) ); try { await fetch('/api/users', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: depositModalUser.id, updates }), }); setToastMessage( depositRequires ? `🔴 Garantía de flete ACTIVADA para "${depositModalUser.name}".` : `🟢 Garantía de flete DESACTIVADA para "${depositModalUser.name}" (Pago 100% Contraentrega).` ); setDepositModalUser(null); } 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(''); setPassword(''); setToastMessage(`🎉 Usuario "${name}" registrado exitosamente.`); } else { alert(data.error || 'Error al crear usuario'); } } 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(''); }; const handleSaveEdit = async (e: React.FormEvent) => { e.preventDefault(); if (!editingUser) return; const updates: Partial = { name: editName, email: editEmail, phone: editPhone, city: editCity, address: editAddress, role: editRole, }; 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 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; const depositsRequiredCount = users.filter((u) => u.requiresShippingDeposit === true).length; return (
{/* Header */}

Control de Usuarios, Roles & Garantías ({users.length})

Administra accesos, roles de equipo y control inteligente de Garantías de Envío Anticipado para clientes.

{toastMessage && (
{toastMessage}
)} {/* KPI Stats Cards */}

Super Admins

{adminsCount}

Operadores

{operatorsCount}

Clientes Totales

{customersCount}

Garantía Exigida

{depositsRequiredCount}

{/* Filters Toolbar */}
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" />
{/* Users Table */}
{filtered.length === 0 ? ( ) : ( filtered.map((user) => { const isDepositRequired = user.requiresShippingDeposit === true; return ( {/* Active Status Toggle */} {/* User Avatar & Name */} {/* Contact */} {/* Shipping Guarantee Status (Dynamic Risk Policy) */} {/* City */} {/* Role Dropdown */} {/* Actions (CRUD: Edit & Delete) */} ); }) )}
Estado Usuario Contacto Directo Garantía de Flete Ciudad / Dirección Rol & Permisos Acciones
No se encontraron usuarios con el filtro seleccionado.
{user.name.charAt(0).toUpperCase()}

{user.name}

{user.email}
{user.depositReason && (

{user.depositReason}

)}
{user.city || 'Cali'} {user.address &&

{user.address}

}
{/* Modal: Configuración de Garantía de Envío */} {depositModalUser && (

Garantía de Envío / Anticipo de Flete

{depositModalUser.name}

{depositModalUser.phone} • {depositModalUser.email}

{depositModalUser.rejectedOrdersCount && depositModalUser.rejectedOrdersCount > 0 ? (

⚠️ Historial: {depositModalUser.rejectedOrdersCount} pedido(s) rechazado(s) en entrega.

) : null}

Exigir Anticipo por Flete

Se le cobrará el flete de su ciudad como anticipo descontable al recibir.

setDepositRequires(e.target.checked)} className="w-5 h-5 accent-purple-600 rounded cursor-pointer" />