From 561d8621c7d78fd03b82bca4d81901f136e4a0b1 Mon Sep 17 00:00:00 2001 From: Kevin Rosales Date: Thu, 7 Aug 2025 10:05:33 -0600 Subject: [PATCH] Se modifico panel de notificaciones --- .env | 8 +- package-lock.json | 128 ---------- package.json | 1 - src/pages/LoginBroken.jsx | 391 ------------------------------ src/pages/LoginFixed.jsx | 286 ---------------------- src/pages/Notificaciones.jsx | 450 +++++++++++++++++++++++++++-------- 6 files changed, 362 insertions(+), 902 deletions(-) delete mode 100644 src/pages/LoginBroken.jsx delete mode 100644 src/pages/LoginFixed.jsx diff --git a/.env b/.env index 6160c3f..2696b88 100644 --- a/.env +++ b/.env @@ -1,4 +1,6 @@ -DEBUG_MODE=true +VITE_DEBUG_MODE=false -VITE_EFC_API_URL=https://api.efc-aduanasoft.com/api/v1 -VITE_EFC_MICROSERVICE_URL=https://api.efc-aduanasoft.com/microservice/api/v1 +VITE_EFC_API_URL=http://192.168.1.195:8000/api/v1 +VITE_EFC_MICROSERVICE_URL=http://192.168.1.195:8001/api/v1 + +VITE_WEBSOCKET_URL=ws://192.168.1.195:8000/ws/notifications/ diff --git a/package-lock.json b/package-lock.json index c9d343a..b2446ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,6 @@ "react-dom": "^19.1.0", "react-router-dom": "^7.6.2", "react-window": "^1.8.11", - "socket.io-client": "^4.8.1", "styled-components": "^6.1.19" }, "devDependencies": { @@ -1462,11 +1461,6 @@ "win32" ] }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" - }, "node_modules/@tanstack/query-core": { "version": "5.81.5", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.81.5.tgz", @@ -2100,42 +2094,6 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "node_modules/engine.io-client": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", - "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1", - "xmlhttprequest-ssl": "~2.1.1" - } - }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -3707,64 +3665,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/socket.io-client": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", - "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.6.1", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -4356,34 +4256,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", - "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index 1f5ff63..f5e2851 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "react-dom": "^19.1.0", "react-router-dom": "^7.6.2", "react-window": "^1.8.11", - "socket.io-client": "^4.8.1", "styled-components": "^6.1.19" }, "devDependencies": { diff --git a/src/pages/LoginBroken.jsx b/src/pages/LoginBroken.jsx deleted file mode 100644 index dfaacc3..0000000 --- a/src/pages/LoginBroken.jsx +++ /dev/null @@ -1,391 +0,0 @@ -import React, { useState } from 'react'; -import { login } from '../api/auth'; -import { Link } from 'react-router-dom'; -import { colors } from '../theme'; - -export default function Login() { - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const [loading, setLoading] = useState(false); - const [showPassword, setShowPassword] = useState(false); - - const handleSubmit = async (e) => { - e.preventDefault(); - setError(''); - setLoading(true); - try { - const data = await login(username, password); - localStorage.setItem('access', data.access); - localStorage.setItem('refresh', data.refresh); - - // Disparar evento personalizado para que el navbar se actualice - window.dispatchEvent(new CustomEvent('authStateChanged')); - - // Redirigir al dashboard - window.location.href = '/admin'; - } catch (err) { - setError('Usuario o contraseña incorrectos'); - } finally { - setLoading(false); - } - }; - - return ( - <> -
- {/* Background pattern */} -
-
-
- -
- {/* Main Card */} -
- {/* Header with navy background */} -
-
- -

- EFC -

- -
-

- Bienvenido de vuelta -

-

- Inicia sesión para acceder a tu plataforma aduanal -

-
- - {/* Form */} -
-
- {/* Username Field */} -
- -
-
- - - -
- setUsername(e.target.value)} - /> -
-
- - {/* Password Field */} -
- -
-
- - - -
- setPassword(e.target.value)} - /> - -
-
- - {/* Error Message */} - {error && ( -
-
-
- - - -
-
-

- {error} -

-
-
-
- )} - - {/* Login Button */} -
- -
- - {/* Additional Links */} -
- -
- - - - - Volver al inicio - -
-
-
-
- - {/* Footer */} -
-
-

- Desarrollado por @AduanaSoft -

-

- Solución especializada para Agentes Aduanales -

-
-
-
- - {/* Floating elements with new colors */} -
-
-
-
- - ); -} - -
- {/* Main Card */} -
- {/* Header with gradient */} -
-
- -

- EFC -

- -
-

- Bienvenido de vuelta -

-

- Inicia sesión para acceder a tu plataforma aduanal -

-
- - {/* Form */} -
-
- {/* Username Field */} -
- -
-
- - - -
- setUsername(e.target.value)} - /> -
-
- - {/* Password Field */} -
- -
-
- - - -
- setPassword(e.target.value)} - /> - -
-
- - {/* Error Message */} - {error && ( -
-
-
- - - -
-
-

- {error} -

-
-
-
- )} - - {/* Login Button */} -
- -
- - {/* Additional Links */} -
- -
- - - - - Volver al inicio - -
-
-
-
- - {/* Footer */} -
-
-

- Desarrollado por @AduanaSoft -

-

- Solución especializada para Agentes Aduanales -

-
-
- - {/* Floating elements */} -
-
-
-
- - ); -} \ No newline at end of file diff --git a/src/pages/LoginFixed.jsx b/src/pages/LoginFixed.jsx deleted file mode 100644 index 59918f0..0000000 --- a/src/pages/LoginFixed.jsx +++ /dev/null @@ -1,286 +0,0 @@ -import React, { useState } from 'react'; -import { login } from '../api/auth'; -import { Link } from 'react-router-dom'; -import { colors } from '../theme'; - -export default function Login() { - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const [loading, setLoading] = useState(false); - const [showPassword, setShowPassword] = useState(false); - - const handleSubmit = async (e) => { - e.preventDefault(); - setError(''); - setLoading(true); - try { - const data = await login(username, password); - localStorage.setItem('access', data.access); - localStorage.setItem('refresh', data.refresh); - - // Disparar evento personalizado para que el navbar se actualice - window.dispatchEvent(new CustomEvent('authStateChanged')); - - // Redirigir al dashboard - window.location.href = '/admin'; - } catch (err) { - setError('Usuario o contraseña incorrectos'); - } finally { - setLoading(false); - } - }; - - return ( -
- {/* Background pattern */} -
-
-
- -
- {/* Main Card */} -
- {/* Header with navy background */} -
-
- -

- EFC -

- -
-

- Bienvenido de vuelta -

-

- Inicia sesión para acceder a tu plataforma aduanal -

-
- - {/* Form */} -
-
- {/* Username Field */} -
- -
-
- - - -
- setUsername(e.target.value)} - onFocus={(e) => { - e.target.style.borderColor = 'transparent'; - e.target.style.boxShadow = '0 0 0 2px #4DA6FF'; - }} - onBlur={(e) => { - e.target.style.borderColor = '#d1d5db'; - e.target.style.boxShadow = 'none'; - }} - onMouseEnter={(e) => { - if (document.activeElement !== e.target) { - e.target.style.borderColor = '#4DA6FF'; - } - }} - onMouseLeave={(e) => { - if (document.activeElement !== e.target) { - e.target.style.borderColor = '#d1d5db'; - } - }} - /> -
-
- - {/* Password Field */} -
- -
-
- - - -
- setPassword(e.target.value)} - onFocus={(e) => { - e.target.style.borderColor = 'transparent'; - e.target.style.boxShadow = '0 0 0 2px #4DA6FF'; - }} - onBlur={(e) => { - e.target.style.borderColor = '#d1d5db'; - e.target.style.boxShadow = 'none'; - }} - onMouseEnter={(e) => { - if (document.activeElement !== e.target) { - e.target.style.borderColor = '#4DA6FF'; - } - }} - onMouseLeave={(e) => { - if (document.activeElement !== e.target) { - e.target.style.borderColor = '#d1d5db'; - } - }} - /> - -
-
- - {/* Error Message */} - {error && ( -
-
-
- - - -
-
-

- {error} -

-
-
-
- )} - - {/* Login Button */} -
- -
- - {/* Additional Links */} -
- -
- e.target.style.color = '#1976D2'} - onMouseLeave={(e) => e.target.style.color = '#4DA6FF'} - > - - - - Volver al inicio - -
-
-
-
- - {/* Footer */} -
-
-

- Desarrollado por @AduanaSoft -

-

- Solución especializada para Agentes Aduanales -

-
-
-
- - {/* Floating elements */} -
-
-
-
- ); -} diff --git a/src/pages/Notificaciones.jsx b/src/pages/Notificaciones.jsx index c245b96..c0ea0b5 100644 --- a/src/pages/Notificaciones.jsx +++ b/src/pages/Notificaciones.jsx @@ -1,6 +1,110 @@ import React, { useEffect, useState } from 'react'; import { fetchNotificaciones, fetchAllNotifications, marcarNotificacionComoVista } from '../api/notificaciones'; +// Función para obtener el icono apropiado según el tipo de notificación +const getNotificationIcon = (tipo) => { + const iconProps = "w-6 h-6"; + + switch (tipo) { + case 'success': + case 'exito': + return ( +
+ + + +
+ ); + case 'error': + case 'danger': + return ( +
+ + + +
+ ); + case 'warning': + case 'advertencia': + return ( +
+ + + +
+ ); + case 'info': + case 'informacion': + return ( +
+ + + +
+ ); + case 'documento': + case 'document': + return ( +
+ + + +
+ ); + case 'proceso': + case 'process': + return ( +
+ + + + +
+ ); + default: + return ( +
+ + + +
+ ); + } +}; + +// Función para formatear timestamps +const formatTimestamp = (timestamp) => { + if (!timestamp) return 'Fecha no disponible'; + + try { + const date = new Date(timestamp); + const now = new Date(); + const diffInMinutes = Math.floor((now - date) / (1000 * 60)); + const diffInHours = Math.floor(diffInMinutes / 60); + const diffInDays = Math.floor(diffInHours / 24); + + if (diffInMinutes < 1) { + return 'Ahora mismo'; + } else if (diffInMinutes < 60) { + return `Hace ${diffInMinutes} min`; + } else if (diffInHours < 24) { + return `Hace ${diffInHours} h`; + } else if (diffInDays < 7) { + return `Hace ${diffInDays} días`; + } else { + return date.toLocaleDateString('es-ES', { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); + } + } catch (error) { + return 'Fecha inválida'; + } +}; + export default function Notificaciones() { const [notificaciones, setNotificaciones] = useState([]); const [loading, setLoading] = useState(false); @@ -57,101 +161,261 @@ export default function Notificaciones() { }; return ( -
-
-

Notificaciones

-
- - +
+
+ {/* Header moderno */} +
+
+
+
+ + + +
+
+

Notificaciones

+

+ {count > 0 ? `${count} notificaciones encontradas` : 'No hay notificaciones'} +

+
+
+ +
+ {/* Filtro mejorado */} +
+ +
+ + + +
+
+ + {/* Botón de acción mejorado */} + +
+
-
- {loading ? ( -
Cargando...
- ) : error ? ( -
{error}
- ) : ( -
- - - - - - - - - - - - {Array.from({ length: pageSize }).map((_, idx) => { - const n = notificaciones[idx]; - if (n) { - return ( - - - - - - + {/* Contenido principal */} + {loading ? ( +
+
+ + + + + Cargando notificaciones... +
+
+ ) : error ? ( +
+
+ + + + {error} +
+
+ ) : ( + <> + {/* Vista Desktop - Tabla */} +
+
+
IDTipoMensajeFechaVisto
{n.id}{n.tipo?.descripcion || n.tipo?.tipo}{n.mensaje}{new Date(n.fecha_envio || n.created_at).toLocaleString()} - {n.visto ? ( - - ) : ( - No - )} -
+ + + + + + + - ); - } else { - return ( - - - - - - - - ); - } - })} - -
IDTipoMensajeFechaEstado
-----
-
- )} - {/* Paginación */} -
-
- Página {page} de {Math.ceil(count / pageSize) || 1} -
-
- - - 15 por página -
+ + + {notificaciones.map((n, idx) => ( + + + #{n.id} + + +
+ {getNotificationIcon(n.tipo?.tipo)} + + {n.tipo?.descripcion || n.tipo?.tipo || 'Notificación'} + +
+ + + {n.mensaje} + + + {formatTimestamp(n.fecha_envio || n.created_at)} + + + {n.visto ? ( + + + + + Leída + + ) : ( + + + + + Nueva + + )} + + + ))} + {/* Filas vacías para mantener altura consistente */} + {Array.from({ length: Math.max(0, pageSize - notificaciones.length) }).map((_, idx) => ( + + - + + ))} + + +
+
+ + {/* Vista Mobile - Cards */} +
+ {notificaciones.length === 0 ? ( +
+
+ + + +
+

No hay notificaciones

+

No se encontraron notificaciones con los filtros aplicados.

+
+ ) : ( + notificaciones.map((n, idx) => ( +
+
+
+
+ {getNotificationIcon(n.tipo?.tipo)} +
+
+
+

+ {n.tipo?.descripcion || n.tipo?.tipo || 'Notificación'} +

+ {n.visto ? ( + + + + + Leída + + ) : ( + + + + + Nueva + + )} +
+

{n.mensaje}

+
+ + + + + ID: #{n.id} + + + + + + {formatTimestamp(n.fecha_envio || n.created_at)} + +
+
+
+
+
+ )) + )} +
+ + )} + + {/* Paginación mejorada */} + {!loading && !error && count > 0 && ( +
+
+
+ Página {page} de {Math.ceil(count / pageSize) || 1} + • {count} notificaciones totales +
+
+ + + 15 por página +
+
+
+ )}
);