Se modifico el bell de notificaciones
This commit is contained in:
6
.env
6
.env
@@ -1,4 +1,4 @@
|
|||||||
DEBUG_MODE=true
|
VITE_DEBUG_MODE=false
|
||||||
|
|
||||||
VITE_EFC_API_URL=https://api.efc-aduanasoft.com/api/v1
|
VITE_EFC_API_URL=http://192.168.1.195:8000/api/v1
|
||||||
VITE_EFC_MICROSERVICE_URL=https://api.efc-aduanasoft.com/microservice/api/v1
|
VITE_EFC_MICROSERVICE_URL=http://192.168.1.195:8001/api/v1
|
||||||
|
|||||||
@@ -57,25 +57,33 @@ export default function NotificationBell() {
|
|||||||
switch (tipo) {
|
switch (tipo) {
|
||||||
case 'success':
|
case 'success':
|
||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0 w-8 h-8 bg-green-100 rounded-full flex items-center justify-center">
|
<div className="flex-shrink-0 w-10 h-10 bg-gradient-to-br from-green-400 to-emerald-500 rounded-xl flex items-center justify-center shadow-lg">
|
||||||
<svg className="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case 'warning':
|
case 'warning':
|
||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0 w-8 h-8 bg-yellow-100 rounded-full flex items-center justify-center">
|
<div className="flex-shrink-0 w-10 h-10 bg-gradient-to-br from-yellow-400 to-orange-500 rounded-xl flex items-center justify-center shadow-lg">
|
||||||
<svg className="w-4 h-4 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case 'error':
|
||||||
|
return (
|
||||||
|
<div className="flex-shrink-0 w-10 h-10 bg-gradient-to-br from-red-400 to-red-600 rounded-xl flex items-center justify-center shadow-lg">
|
||||||
|
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0 w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
|
<div className="flex-shrink-0 w-10 h-10 bg-gradient-to-br from-blue-400 to-blue-600 rounded-xl flex items-center justify-center shadow-lg">
|
||||||
<svg className="w-4 h-4 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -97,123 +105,196 @@ export default function NotificationBell() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Campanita flotante */}
|
{/* Campanita flotante con diseño mejorado */}
|
||||||
<div className="fixed bottom-6 right-6 z-50">
|
<div className="fixed bottom-6 right-6 z-50">
|
||||||
<button
|
<div className="relative">
|
||||||
onClick={() => setShowPanel(!showPanel)}
|
{/* Efecto de pulso para notificaciones nuevas */}
|
||||||
className="relative bg-indigo-600 hover:bg-indigo-700 text-white p-3 rounded-full shadow-lg transition-all duration-200 hover:scale-105 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
|
||||||
>
|
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 17h5l-5-5v5zM4 4h5l-5 5v-5z" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
{/* Contador de notificaciones no leídas */}
|
|
||||||
{unreadCount > 0 && (
|
{unreadCount > 0 && (
|
||||||
<span className="absolute -top-2 -right-2 bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center font-medium">
|
<div className="absolute inset-0 bg-blue-400 rounded-full animate-ping opacity-75"></div>
|
||||||
{unreadCount > 9 ? '9+' : unreadCount}
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</button>
|
|
||||||
</div>
|
<button
|
||||||
|
onClick={() => setShowPanel(!showPanel)}
|
||||||
{/* Panel de notificaciones */}
|
className="relative bg-gradient-to-br from-blue-500 to-blue-700 hover:from-blue-600 hover:to-blue-800 text-white p-4 rounded-full shadow-2xl transition-all duration-300 hover:scale-110 hover:shadow-blue-500/25 focus:outline-none focus:ring-4 focus:ring-blue-500/50 group"
|
||||||
{showPanel && (
|
>
|
||||||
<div className="fixed bottom-20 right-6 z-50 w-80 bg-white rounded-lg shadow-xl border border-gray-200">
|
{/* Icono de campanita mejorado */}
|
||||||
{/* Header del panel */}
|
<svg
|
||||||
<div className="p-4 border-b border-gray-200 flex items-center justify-between">
|
className={`w-6 h-6 transition-transform duration-300 ${unreadCount > 0 ? 'animate-bounce' : ''} group-hover:scale-110`}
|
||||||
<h3 className="text-lg font-semibold text-gray-900">Notificaciones</h3>
|
fill="none"
|
||||||
<div className="flex items-center space-x-2">
|
stroke="currentColor"
|
||||||
{unreadCount > 0 && (
|
viewBox="0 0 24 24"
|
||||||
<button
|
>
|
||||||
onClick={markAllAsRead}
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 17h5l-5 5v-5zM4 4h5l-5 5v-5z" />
|
||||||
className="text-sm text-indigo-600 hover:text-indigo-800 font-medium"
|
</svg>
|
||||||
>
|
|
||||||
Marcar todas como leídas
|
{/* Contador de notificaciones mejorado */}
|
||||||
</button>
|
{unreadCount > 0 && (
|
||||||
)}
|
<div className="absolute -top-2 -right-2">
|
||||||
<button
|
<span className="relative flex h-6 w-6">
|
||||||
onClick={() => setShowPanel(false)}
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span>
|
||||||
className="text-gray-400 hover:text-gray-600"
|
<span className="relative inline-flex rounded-full h-6 w-6 bg-gradient-to-br from-red-500 to-red-600 text-white text-xs font-bold items-center justify-center shadow-lg">
|
||||||
>
|
{unreadCount > 9 ? '9+' : unreadCount}
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
</span>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
</span>
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Lista de notificaciones */}
|
|
||||||
<div className="max-h-96 overflow-y-auto">
|
|
||||||
{loading ? (
|
|
||||||
<div className="p-6 text-center text-blue-500">Cargando notificaciones...</div>
|
|
||||||
) : error ? (
|
|
||||||
<div className="p-6 text-center text-red-500">{error}</div>
|
|
||||||
) : notifications.length === 0 ? (
|
|
||||||
<div className="p-6 text-center">
|
|
||||||
<svg className="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 17h5l-5 5v-5zM13 3l-4 9h6l-4 9" />
|
|
||||||
</svg>
|
|
||||||
<h3 className="mt-2 text-sm font-medium text-gray-900">No hay notificaciones</h3>
|
|
||||||
<p className="mt-1 text-sm text-gray-500">Cuando tengas nuevas notificaciones aparecerán aquí.</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="divide-y divide-gray-200">
|
|
||||||
{notifications.map((notification) => (
|
|
||||||
<div
|
|
||||||
key={notification.id}
|
|
||||||
className={`p-4 hover:bg-gray-50 cursor-pointer transition-colors ${
|
|
||||||
!notification.visto ? 'bg-blue-50' : ''
|
|
||||||
}`}
|
|
||||||
onClick={() => markAsRead(notification.id)}
|
|
||||||
>
|
|
||||||
<div className="flex space-x-3">
|
|
||||||
{getNotificationIcon(notification.tipo?.tipo)}
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<p className={`text-sm font-medium ${
|
|
||||||
!notification.visto ? 'text-gray-900' : 'text-gray-700'
|
|
||||||
}`}>
|
|
||||||
{notification.tipo?.descripcion || notification.tipo?.tipo || 'Notificación'}
|
|
||||||
</p>
|
|
||||||
<span className="text-xs text-gray-500">
|
|
||||||
{formatTimestamp(notification.fecha_envio || notification.created_at)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-600 mt-1">{notification.mensaje}</p>
|
|
||||||
{!notification.visto && (
|
|
||||||
<div className="mt-2">
|
|
||||||
<span className="inline-block w-2 h-2 bg-blue-600 rounded-full"></span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
{/* Indicador de estado activo */}
|
||||||
|
{showPanel && (
|
||||||
|
<div className="absolute -top-1 -right-1 w-4 h-4 bg-green-400 rounded-full border-2 border-white animate-pulse"></div>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Footer del panel */}
|
{/* Panel de notificaciones con diseño mejorado */}
|
||||||
{notifications.length > 0 && (
|
{showPanel && (
|
||||||
<div className="p-3 border-t border-gray-200 bg-gray-50 rounded-b-lg">
|
<div className="fixed bottom-24 right-6 z-50 w-96 max-w-sm">
|
||||||
<button
|
{/* Animación de entrada */}
|
||||||
className="w-full text-center text-sm text-indigo-600 hover:text-indigo-800 font-medium"
|
<div className="transform transition-all duration-300 ease-out scale-100 opacity-100 translate-y-0">
|
||||||
onClick={() => {
|
<div className="bg-white/95 backdrop-blur-xl rounded-2xl shadow-2xl border border-gray-200/50 overflow-hidden">
|
||||||
setShowPanel(false);
|
{/* Header del panel mejorado */}
|
||||||
navigate('/notificaciones');
|
<div className="bg-gradient-to-r from-blue-500 to-blue-700 p-5 text-white">
|
||||||
}}
|
<div className="flex items-center justify-between">
|
||||||
>
|
<div className="flex items-center space-x-3">
|
||||||
Ver todas las notificaciones
|
<div className="p-2 bg-white/20 rounded-xl">
|
||||||
</button>
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 17h5l-5 5v-5zM4 4h5l-5 5v-5z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-bold">Notificaciones</h3>
|
||||||
|
<p className="text-sm text-blue-100">
|
||||||
|
{unreadCount > 0 ? `${unreadCount} nuevas` : 'Todas leídas'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
{unreadCount > 0 && (
|
||||||
|
<button
|
||||||
|
onClick={markAllAsRead}
|
||||||
|
className="px-3 py-1.5 bg-white/20 hover:bg-white/30 rounded-lg text-sm font-medium transition-colors duration-200 flex items-center space-x-1"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
<span>Marcar todas</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
onClick={() => setShowPanel(false)}
|
||||||
|
className="p-2 hover:bg-white/20 rounded-lg transition-colors duration-200"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Lista de notificaciones con diseño mejorado */}
|
||||||
|
<div className="max-h-96 overflow-y-auto bg-gray-50/30">
|
||||||
|
{loading ? (
|
||||||
|
<div className="p-8 text-center">
|
||||||
|
<div className="inline-flex items-center space-x-2 text-blue-600">
|
||||||
|
<svg className="animate-spin h-5 w-5" fill="none" viewBox="0 0 24 24">
|
||||||
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||||
|
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||||
|
</svg>
|
||||||
|
<span className="font-medium">Cargando notificaciones...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : error ? (
|
||||||
|
<div className="p-8 text-center">
|
||||||
|
<div className="inline-flex items-center space-x-2 text-red-500">
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
||||||
|
</svg>
|
||||||
|
<span className="font-medium">{error}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : notifications.length === 0 ? (
|
||||||
|
<div className="p-8 text-center">
|
||||||
|
<div className="mx-auto w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
|
||||||
|
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 17h5l-5 5v-5zM13 3l-4 9h6l-4 9" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-sm font-semibold text-gray-900 mb-1">No hay notificaciones</h3>
|
||||||
|
<p className="text-sm text-gray-500">Cuando tengas nuevas notificaciones aparecerán aquí.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="divide-y divide-gray-100">
|
||||||
|
{notifications.map((notification, index) => (
|
||||||
|
<div
|
||||||
|
key={notification.id}
|
||||||
|
className={`p-4 hover:bg-white/60 cursor-pointer transition-all duration-200 group ${
|
||||||
|
!notification.visto ? 'bg-blue-50/50 border-l-4 border-blue-500' : 'hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
onClick={() => markAsRead(notification.id)}
|
||||||
|
style={{ animationDelay: `${index * 50}ms` }}
|
||||||
|
>
|
||||||
|
<div className="flex space-x-3">
|
||||||
|
{getNotificationIcon(notification.tipo?.tipo)}
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<p className={`text-sm font-semibold leading-5 ${
|
||||||
|
!notification.visto ? 'text-gray-900' : 'text-gray-700'
|
||||||
|
}`}>
|
||||||
|
{notification.tipo?.descripcion || notification.tipo?.tipo || 'Notificación'}
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<span className="text-xs text-gray-500 font-medium">
|
||||||
|
{formatTimestamp(notification.fecha_envio || notification.created_at)}
|
||||||
|
</span>
|
||||||
|
{!notification.visto && (
|
||||||
|
<div className="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-600 mt-1 leading-5">{notification.mensaje}</p>
|
||||||
|
{!notification.visto && (
|
||||||
|
<div className="mt-2 flex items-center space-x-1 text-blue-600">
|
||||||
|
<div className="w-1.5 h-1.5 bg-blue-500 rounded-full"></div>
|
||||||
|
<span className="text-xs font-medium">Nueva</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Footer del panel mejorado */}
|
||||||
|
{notifications.length > 0 && (
|
||||||
|
<div className="bg-gradient-to-r from-gray-50 to-gray-100 border-t border-gray-200/50">
|
||||||
|
<button
|
||||||
|
className="w-full p-4 text-center text-sm font-semibold text-blue-600 hover:text-blue-800 hover:bg-white/50 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||||
|
onClick={() => {
|
||||||
|
setShowPanel(false);
|
||||||
|
navigate('/notificaciones');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>Ver todas las notificaciones</span>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Overlay para cerrar el panel al hacer clic fuera */}
|
{/* Overlay para cerrar el panel al hacer clic fuera con animación */}
|
||||||
{showPanel && (
|
{showPanel && (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-40"
|
className="fixed inset-0 z-40 bg-black/20 backdrop-blur-sm transition-all duration-300"
|
||||||
onClick={() => setShowPanel(false)}
|
onClick={() => setShowPanel(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user