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 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
# Changelog - project.linkedcorp.net
|
# 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
|
## [2.2.4] - 2026-09-22
|
||||||
### Fixed
|
### Fixed
|
||||||
- El botón "Actualizar" se quedaba visible después de aplicar la actualización, hasta que se
|
- El botón "Actualizar" se quedaba visible después de aplicar la actualización, hasta que se
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "project-linkedcorp-backend",
|
"name": "project-linkedcorp-backend",
|
||||||
"version": "2.2.4",
|
"version": "2.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/server.js",
|
"main": "src/server.js",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "project-linkedcorp-frontend",
|
"name": "project-linkedcorp-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.2.4",
|
"version": "2.3.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Outlet, useNavigate } from 'react-router-dom';
|
import { Outlet, useNavigate } from 'react-router-dom';
|
||||||
import { Menu, Sun, Moon, Bell, LogOut, User, Globe, KeyRound, Save } from 'lucide-react';
|
import { Menu, Sun, Moon, Bell, LogOut, User, Globe, KeyRound, Save } from 'lucide-react';
|
||||||
import { NavigationView } from '../components/NavigationView.jsx';
|
import { NavigationView } from '../components/NavigationView.jsx';
|
||||||
@@ -12,13 +12,14 @@ import { useUi } from '../context/ui-context.jsx';
|
|||||||
import { useAsync } from '../hooks/useAsync.js';
|
import { useAsync } from '../hooks/useAsync.js';
|
||||||
import { listNotifications, markAllNotificationsRead, markNotificationRead } from '../services/notifications.js';
|
import { listNotifications, markAllNotificationsRead, markNotificationRead } from '../services/notifications.js';
|
||||||
import { changePassword } from '../services/auth.js';
|
import { changePassword } from '../services/auth.js';
|
||||||
|
import { getUpdateStatus } from '../services/updater.js';
|
||||||
|
|
||||||
const EMPTY_PASSWORD_FORM = { currentPassword: '', newPassword: '', confirmPassword: '' };
|
const EMPTY_PASSWORD_FORM = { currentPassword: '', newPassword: '', confirmPassword: '' };
|
||||||
|
|
||||||
export default function AppShell() {
|
export default function AppShell() {
|
||||||
const [navMode, setNavMode] = useState('expanded');
|
const [navMode, setNavMode] = useState('expanded');
|
||||||
const { user, logout } = useAuth();
|
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 navigate = useNavigate();
|
||||||
|
|
||||||
const { data: notifData, reload: reloadNotifications } = useAsync(listNotifications, []);
|
const { data: notifData, reload: reloadNotifications } = useAsync(listNotifications, []);
|
||||||
@@ -28,6 +29,31 @@ export default function AppShell() {
|
|||||||
const [passwordForm, setPasswordForm] = useState(EMPTY_PASSWORD_FORM);
|
const [passwordForm, setPasswordForm] = useState(EMPTY_PASSWORD_FORM);
|
||||||
const [passwordError, setPasswordError] = useState('');
|
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() {
|
function toggleNav() {
|
||||||
setNavMode((m) => (m === 'expanded' ? 'compact' : 'expanded'));
|
setNavMode((m) => (m === 'expanded' ? 'compact' : 'expanded'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_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_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_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',
|
config_error_logs: 'Errores del sistema',
|
||||||
error_logs_clear: 'Limpiar registro',
|
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_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_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_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',
|
config_error_logs: 'System errors',
|
||||||
error_logs_clear: 'Clear log',
|
error_logs_clear: 'Clear log',
|
||||||
|
|||||||
@@ -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 { Palette, ListChecks, Flag, FolderKanban, Layers, Mail, Building2, RefreshCw, Bug } from 'lucide-react';
|
||||||
import { useUi } from '../../context/ui-context.jsx';
|
import { useUi } from '../../context/ui-context.jsx';
|
||||||
import { useAuth } from '../../context/auth-context.jsx';
|
import { useAuth } from '../../context/auth-context.jsx';
|
||||||
@@ -14,6 +15,20 @@ import ErrorLogsPanel from './ErrorLogsPanel.jsx';
|
|||||||
export default function ConfiguracionPage() {
|
export default function ConfiguracionPage() {
|
||||||
const { t, theme, setTheme, lang, setLang, buttonMode, setButtonMode } = useUi();
|
const { t, theme, setTheme, lang, setLang, buttonMode, setButtonMode } = useUi();
|
||||||
const { user } = useAuth();
|
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
|
// 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.
|
// para los demas roles; esta guarda de ruta evita entrar tecleando /configuracion directo.
|
||||||
@@ -104,9 +119,11 @@ export default function ConfiguracionPage() {
|
|||||||
<SmtpSettingsPanel />
|
<SmtpSettingsPanel />
|
||||||
</Expander>
|
</Expander>
|
||||||
|
|
||||||
<Expander icon={RefreshCw} title={t('config_updater')}>
|
<div id="updates-panel">
|
||||||
|
<Expander icon={RefreshCw} title={t('config_updater')} defaultOpen={openUpdates}>
|
||||||
<UpdaterPanel />
|
<UpdaterPanel />
|
||||||
</Expander>
|
</Expander>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Expander icon={Bug} title={t('config_error_logs')}>
|
<Expander icon={Bug} title={t('config_error_logs')}>
|
||||||
<ErrorLogsPanel />
|
<ErrorLogsPanel />
|
||||||
|
|||||||
Reference in New Issue
Block a user