From ea0feb7e3a18ebb583af6b8fa2c41aa15fee9ab0 Mon Sep 17 00:00:00 2001 From: Milton Gonzalez Date: Tue, 22 Sep 2026 12:49:00 -0500 Subject: [PATCH] feat: version 2.3.0 -- aviso de actualizacion pendiente al iniciar sesion Superadmin ve un popup con acceso directo a Configuracion > Actualizaciones si hay una version nueva disponible, en vez de tener que acordarse de revisar el panel manualmente. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 6 ++++ VERSION | 2 +- backend/package.json | 2 +- frontend/package.json | 2 +- frontend/src/app/AppShell.jsx | 30 +++++++++++++++++-- frontend/src/i18n/messages.js | 6 ++++ .../pages/Configuracion/ConfiguracionPage.jsx | 25 +++++++++++++--- 7 files changed, 64 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6da09f2..0f1fae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog - project.linkedcorp.net +## [2.3.0] - 2026-09-22 +### Added +- Al iniciar sesión, si sos superadmin y hay una actualización pendiente, aparece un aviso con + un acceso directo al panel de Actualizaciones (abre Configuración con ese panel ya + desplegado). + ## [2.2.4] - 2026-09-22 ### Fixed - El botón "Actualizar" se quedaba visible después de aplicar la actualización, hasta que se diff --git a/VERSION b/VERSION index 530cdd9..276cbf9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.4 +2.3.0 diff --git a/backend/package.json b/backend/package.json index f9b099b..66cde7e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "project-linkedcorp-backend", - "version": "2.2.4", + "version": "2.3.0", "private": true, "type": "module", "main": "src/server.js", diff --git a/frontend/package.json b/frontend/package.json index 2b31c51..add4c01 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "project-linkedcorp-frontend", "private": true, - "version": "2.2.4", + "version": "2.3.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/app/AppShell.jsx b/frontend/src/app/AppShell.jsx index a209fd9..e215e85 100644 --- a/frontend/src/app/AppShell.jsx +++ b/frontend/src/app/AppShell.jsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { Outlet, useNavigate } from 'react-router-dom'; import { Menu, Sun, Moon, Bell, LogOut, User, Globe, KeyRound, Save } from 'lucide-react'; import { NavigationView } from '../components/NavigationView.jsx'; @@ -12,13 +12,14 @@ import { useUi } from '../context/ui-context.jsx'; import { useAsync } from '../hooks/useAsync.js'; import { listNotifications, markAllNotificationsRead, markNotificationRead } from '../services/notifications.js'; import { changePassword } from '../services/auth.js'; +import { getUpdateStatus } from '../services/updater.js'; const EMPTY_PASSWORD_FORM = { currentPassword: '', newPassword: '', confirmPassword: '' }; export default function AppShell() { const [navMode, setNavMode] = useState('expanded'); const { user, logout } = useAuth(); - const { t, theme, setTheme, lang, setLang, pushToast } = useUi(); + const { t, theme, setTheme, lang, setLang, pushToast, openDialog } = useUi(); const navigate = useNavigate(); const { data: notifData, reload: reloadNotifications } = useAsync(listNotifications, []); @@ -28,6 +29,31 @@ export default function AppShell() { const [passwordForm, setPasswordForm] = useState(EMPTY_PASSWORD_FORM); const [passwordError, setPasswordError] = useState(''); + // Al entrar (una vez por sesion, no en cada navegacion): si es superadmin y hay una + // actualizacion pendiente, avisar con un popup y un acceso directo al panel. Silencioso si + // falla (ej. Gitea sin conexion) -- no debe molestar el login por un chequeo secundario. + useEffect(() => { + if (user?.roleCode !== 'superadmin') return; + getUpdateStatus() + .then((status) => { + if (status.upToDate) return; + openDialog({ + tone: 'warning', + title: t('updater_login_popup_title'), + message: `${t('updater_login_popup_msg')} v${status.currentVersion} → v${status.latestVersion}`, + actions: [ + { label: t('common_close'), variant: 'standard' }, + { + label: t('updater_login_popup_go'), variant: 'accent', + onClick: () => navigate('/configuracion?open=updates'), + }, + ], + }); + }) + .catch(() => {}); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + function toggleNav() { setNavMode((m) => (m === 'expanded' ? 'compact' : 'expanded')); } diff --git a/frontend/src/i18n/messages.js b/frontend/src/i18n/messages.js index 65281e9..8361a67 100644 --- a/frontend/src/i18n/messages.js +++ b/frontend/src/i18n/messages.js @@ -90,6 +90,9 @@ export const messages = { updater_apply_started: 'Actualización iniciada. El sistema se reiniciará en unos minutos — refrescá la página luego.', updater_applying_hint: 'Actualización en curso — no cierres esta pestaña. El sistema se reiniciará solo al terminar.', updater_apply_not_configured: 'La auto-actualización no está habilitada en este servidor. Pedile a tu administrador que aplique esta actualización por SSH.', + updater_login_popup_title: 'Hay una actualización pendiente', + updater_login_popup_msg: 'El sistema tiene una versión nueva disponible:', + updater_login_popup_go: 'Ver actualizaciones', config_error_logs: 'Errores del sistema', error_logs_clear: 'Limpiar registro', @@ -380,6 +383,9 @@ export const messages = { updater_apply_started: 'Update started. The system will restart in a few minutes — refresh the page afterward.', updater_applying_hint: 'Update in progress — do not close this tab. The system will restart on its own when done.', updater_apply_not_configured: 'Auto-update is not enabled on this server. Ask your administrator to apply this update via SSH.', + updater_login_popup_title: 'There is a pending update', + updater_login_popup_msg: 'A new version of the system is available:', + updater_login_popup_go: 'View updates', config_error_logs: 'System errors', error_logs_clear: 'Clear log', diff --git a/frontend/src/pages/Configuracion/ConfiguracionPage.jsx b/frontend/src/pages/Configuracion/ConfiguracionPage.jsx index 87dbe5d..a8d4ba5 100644 --- a/frontend/src/pages/Configuracion/ConfiguracionPage.jsx +++ b/frontend/src/pages/Configuracion/ConfiguracionPage.jsx @@ -1,4 +1,5 @@ -import { Navigate } from 'react-router-dom'; +import { useEffect } from 'react'; +import { Navigate, useSearchParams } from 'react-router-dom'; import { Palette, ListChecks, Flag, FolderKanban, Layers, Mail, Building2, RefreshCw, Bug } from 'lucide-react'; import { useUi } from '../../context/ui-context.jsx'; import { useAuth } from '../../context/auth-context.jsx'; @@ -14,6 +15,20 @@ import ErrorLogsPanel from './ErrorLogsPanel.jsx'; export default function ConfiguracionPage() { const { t, theme, setTheme, lang, setLang, buttonMode, setButtonMode } = useUi(); const { user } = useAuth(); + const [searchParams] = useSearchParams(); + const openUpdates = searchParams.get('open') === 'updates'; + + // Los demas paneles (catalogos, etc.) cargan sus datos de forma asincrona, asi que la pagina + // todavia no tiene su altura final apenas monta -- un solo scrollIntoView aca no tiene nada + // que scrollear. Se reintenta un par de veces mientras el resto termina de cargar. + useEffect(() => { + if (!openUpdates) return; + const timers = [0, 300, 800].map((delay) => setTimeout(() => { + document.getElementById('updates-panel')?.scrollIntoView({ behavior: 'auto', block: 'start' }); + }, delay)); + return () => timers.forEach(clearTimeout); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); // Regla dura: Configuracion es solo del superadmin (catalogos, SMTP...). El nav ya la oculta // para los demas roles; esta guarda de ruta evita entrar tecleando /configuracion directo. @@ -104,9 +119,11 @@ export default function ConfiguracionPage() { - - - +
+ + + +