From b172a8f087a705e960ad2854bb4331482ff333af Mon Sep 17 00:00:00 2001 From: Milton Gonzalez Date: Thu, 3 Sep 2026 11:30:20 -0500 Subject: [PATCH] fix(ui): no desmontar la pantalla en cada recarga en segundo plano MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Las 11 pantallas con listado/detalle bloqueaban TODO su render con "if (loading || !data) return ". Como useAsync pone loading=true en cada reload (no solo en la carga inicial), cualquier accion dentro de un DetailPopup abierto que dispara un reload del padre (agregar subtarea, agregar dependencia, guardar comentario, etc.) desmontaba momentaneamente TODA la pantalla -y con ella el popup y su estado interno (pestaña activa)-, que volvia a montarse desde cero con sus valores iniciales. Sintoma reportado: al agregar una subtarea (Enter o boton "Nuevo"), el popup de la actividad volvia a la pestaña Info en vez de quedarse en Subtareas. Fix: la guarda ahora es solo "if (!data)" -bloquea unicamente la carga inicial, cuando todavia no hay nada que mostrar-. Las recargas subsiguientes actualizan el contenido en el sitio sin desmontar nada. Co-Authored-By: Claude Sonnet 5 --- frontend/src/components/AttachmentsPanel.jsx | 4 ++-- frontend/src/components/CommentsPanel.jsx | 4 ++-- frontend/src/pages/Configuracion/CatalogEditor.jsx | 4 ++-- frontend/src/pages/Dashboard/DashboardPage.jsx | 4 ++-- frontend/src/pages/Organizacion/DepartmentsPanel.jsx | 4 ++-- frontend/src/pages/Organizacion/UsersPanel.jsx | 4 ++-- frontend/src/pages/Proyectos/ProjectActivitiesTab.jsx | 4 ++-- frontend/src/pages/Proyectos/ProjectCalendarTab.jsx | 4 ++-- frontend/src/pages/Proyectos/ProjectGanttTab.jsx | 4 ++-- frontend/src/pages/Proyectos/ProyectoDetallePage.jsx | 4 ++-- frontend/src/pages/Proyectos/ProyectosPage.jsx | 4 ++-- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/frontend/src/components/AttachmentsPanel.jsx b/frontend/src/components/AttachmentsPanel.jsx index 18f91cc..cd1b581 100644 --- a/frontend/src/components/AttachmentsPanel.jsx +++ b/frontend/src/components/AttachmentsPanel.jsx @@ -8,7 +8,7 @@ import { Button } from './Button.jsx'; export function AttachmentsPanel({ entityType, entityId }) { const { t, openDialog } = useUi(); const runAction = useAction(); - const { data: files, loading, reload } = useAsync(() => listAttachments(entityType, entityId), [entityType, entityId]); + const { data: files, reload } = useAsync(() => listAttachments(entityType, entityId), [entityType, entityId]); const inputRef = useRef(null); async function handleFile(e) { @@ -33,7 +33,7 @@ export function AttachmentsPanel({ entityType, entityId }) { }); } - if (loading || !files) return
{t('common_loading')}
; + if (!files) return
{t('common_loading')}
; return (
diff --git a/frontend/src/components/CommentsPanel.jsx b/frontend/src/components/CommentsPanel.jsx index 1256913..0a4e9e6 100644 --- a/frontend/src/components/CommentsPanel.jsx +++ b/frontend/src/components/CommentsPanel.jsx @@ -8,7 +8,7 @@ import { Button } from './Button.jsx'; export function CommentsPanel({ entityType, entityId }) { const { t } = useUi(); const runAction = useAction(); - const { data: comments, loading, reload } = useAsync(() => listComments(entityType, entityId), [entityType, entityId]); + const { data: comments, reload } = useAsync(() => listComments(entityType, entityId), [entityType, entityId]); const [message, setMessage] = useState(''); async function send() { @@ -20,7 +20,7 @@ export function CommentsPanel({ entityType, entityId }) { }); } - if (loading || !comments) return
{t('common_loading')}
; + if (!comments) return
{t('common_loading')}
; return (
diff --git a/frontend/src/pages/Configuracion/CatalogEditor.jsx b/frontend/src/pages/Configuracion/CatalogEditor.jsx index 839c44e..e9ab6a1 100644 --- a/frontend/src/pages/Configuracion/CatalogEditor.jsx +++ b/frontend/src/pages/Configuracion/CatalogEditor.jsx @@ -22,7 +22,7 @@ function emptyForm(fields) { export function CatalogEditor({ catalogKey, fields, title }) { const { t, openDialog, pushToast } = useUi(); const runAction = useAction(); - const { data: items, loading, reload } = useAsync(() => listCatalog(catalogKey), [catalogKey]); + const { data: items, reload } = useAsync(() => listCatalog(catalogKey), [catalogKey]); const [editing, setEditing] = useState(false); const [selected, setSelected] = useState(null); const [form, setForm] = useState(emptyForm(fields)); @@ -75,7 +75,7 @@ export function CatalogEditor({ catalogKey, fields, title }) { : undefined, })); - if (loading || !items) return
{t('common_loading')}
; + if (!items) return
{t('common_loading')}
; return (
diff --git a/frontend/src/pages/Dashboard/DashboardPage.jsx b/frontend/src/pages/Dashboard/DashboardPage.jsx index 06263c6..3804e6e 100644 --- a/frontend/src/pages/Dashboard/DashboardPage.jsx +++ b/frontend/src/pages/Dashboard/DashboardPage.jsx @@ -8,9 +8,9 @@ import { ProgressBar, DueBadge } from '../../components/StatusBadge.jsx'; export default function DashboardPage() { const { t } = useUi(); const navigate = useNavigate(); - const { data, loading } = useAsync(getSummary, []); + const { data } = useAsync(getSummary, []); - if (loading || !data) return
{t('common_loading')}
; + if (!data) return
{t('common_loading')}
; const { kpis, myActivities, projectsByStatus } = data; return ( diff --git a/frontend/src/pages/Organizacion/DepartmentsPanel.jsx b/frontend/src/pages/Organizacion/DepartmentsPanel.jsx index ce14d70..10b7ca0 100644 --- a/frontend/src/pages/Organizacion/DepartmentsPanel.jsx +++ b/frontend/src/pages/Organizacion/DepartmentsPanel.jsx @@ -20,7 +20,7 @@ function flatten(nodes, acc = []) { export default function DepartmentsPanel() { const { t, openDialog, pushToast } = useUi(); const runAction = useAction(); - const { data: tree, loading, reload } = useAsync(listDepartments, []); + const { data: tree, reload } = useAsync(listDepartments, []); const [selected, setSelected] = useState(null); const [editing, setEditing] = useState(false); const [form, setForm] = useState({ name: '', parentId: '' }); @@ -71,7 +71,7 @@ export default function DepartmentsPanel() { }); } - if (loading || !tree) return
{t('common_loading')}
; + if (!tree) return
{t('common_loading')}
; return (
diff --git a/frontend/src/pages/Organizacion/UsersPanel.jsx b/frontend/src/pages/Organizacion/UsersPanel.jsx index fe2ea3a..e656771 100644 --- a/frontend/src/pages/Organizacion/UsersPanel.jsx +++ b/frontend/src/pages/Organizacion/UsersPanel.jsx @@ -22,7 +22,7 @@ const EMPTY_FORM = { name: '', email: '', roleId: '', departmentId: '', position export default function UsersPanel() { const { t, pushToast } = useUi(); const runAction = useAction(); - const { data: users, loading, reload } = useAsync(listUsers, []); + const { data: users, reload } = useAsync(listUsers, []); const { data: roles } = useAsync(listRoles, []); const { data: deptTree } = useAsync(listDepartments, []); @@ -78,7 +78,7 @@ export default function UsersPanel() { ) }, ]; - if (loading || !users) return
{t('common_loading')}
; + if (!users) return
{t('common_loading')}
; return (
diff --git a/frontend/src/pages/Proyectos/ProjectActivitiesTab.jsx b/frontend/src/pages/Proyectos/ProjectActivitiesTab.jsx index c7b9aae..a0fd4d1 100644 --- a/frontend/src/pages/Proyectos/ProjectActivitiesTab.jsx +++ b/frontend/src/pages/Proyectos/ProjectActivitiesTab.jsx @@ -20,7 +20,7 @@ const EMPTY_FORM = { name: '', description: '', responsibleUserId: '', priorityI export default function ProjectActivitiesTab({ projectId }) { const { t, pushToast } = useUi(); const runAction = useAction(); - const { data: activities, loading, reload } = useAsync(() => listActivitiesByProject(projectId), [projectId]); + const { data: activities, reload } = useAsync(() => listActivitiesByProject(projectId), [projectId]); const { data: priorities } = useAsync(() => listCatalog('priorities'), []); const { data: statuses } = useAsync(() => listCatalog('activity-statuses'), []); const { data: users } = useAsync(listUsers, []); @@ -70,7 +70,7 @@ export default function ProjectActivitiesTab({ projectId }) { { key: 'progress_pct', label: t('activity_progress'), render: (r) =>
}, ]; - if (loading || !activities) return
{t('common_loading')}
; + if (!activities) return
{t('common_loading')}
; return (
diff --git a/frontend/src/pages/Proyectos/ProjectCalendarTab.jsx b/frontend/src/pages/Proyectos/ProjectCalendarTab.jsx index 40255e4..28bff0e 100644 --- a/frontend/src/pages/Proyectos/ProjectCalendarTab.jsx +++ b/frontend/src/pages/Proyectos/ProjectCalendarTab.jsx @@ -20,7 +20,7 @@ function buildGrid(year, month) { export default function ProjectCalendarTab({ projectId }) { const { t } = useUi(); - const { data: activities, loading } = useAsync(() => listActivitiesByProject(projectId), [projectId]); + const { data: activities } = useAsync(() => listActivitiesByProject(projectId), [projectId]); const [cursor, setCursor] = useState(() => { const n = new Date(); return new Date(n.getFullYear(), n.getMonth(), 1); }); const cells = useMemo(() => buildGrid(cursor.getFullYear(), cursor.getMonth()), [cursor]); @@ -35,7 +35,7 @@ export default function ProjectCalendarTab({ projectId }) { return map; }, [activities]); - if (loading || !activities) return
{t('common_loading')}
; + if (!activities) return
{t('common_loading')}
; return (
diff --git a/frontend/src/pages/Proyectos/ProjectGanttTab.jsx b/frontend/src/pages/Proyectos/ProjectGanttTab.jsx index dde8f4c..62aecf5 100644 --- a/frontend/src/pages/Proyectos/ProjectGanttTab.jsx +++ b/frontend/src/pages/Proyectos/ProjectGanttTab.jsx @@ -4,9 +4,9 @@ import { getGantt } from '../../services/projects.js'; export default function ProjectGanttTab({ projectId }) { const { t } = useUi(); - const { data: items, loading } = useAsync(() => getGantt(projectId), [projectId]); + const { data: items } = useAsync(() => getGantt(projectId), [projectId]); - if (loading || !items) return
{t('common_loading')}
; + if (!items) return
{t('common_loading')}
; const withDates = items.filter((i) => i.startDate && i.dueDate); if (withDates.length === 0) return
{t('common_empty')}
; diff --git a/frontend/src/pages/Proyectos/ProyectoDetallePage.jsx b/frontend/src/pages/Proyectos/ProyectoDetallePage.jsx index 953fa64..12c781f 100644 --- a/frontend/src/pages/Proyectos/ProyectoDetallePage.jsx +++ b/frontend/src/pages/Proyectos/ProyectoDetallePage.jsx @@ -21,9 +21,9 @@ export default function ProyectoDetallePage() { const navigate = useNavigate(); const [tab, setTab] = useState('info'); - const { data: project, loading, reload } = useAsync(() => getProject(projectId), [projectId]); + const { data: project, reload } = useAsync(() => getProject(projectId), [projectId]); - if (loading || !project) return
{t('common_loading')}
; + if (!project) return
{t('common_loading')}
; return (
diff --git a/frontend/src/pages/Proyectos/ProyectosPage.jsx b/frontend/src/pages/Proyectos/ProyectosPage.jsx index f2e7e1a..9db61eb 100644 --- a/frontend/src/pages/Proyectos/ProyectosPage.jsx +++ b/frontend/src/pages/Proyectos/ProyectosPage.jsx @@ -32,7 +32,7 @@ export default function ProyectosPage() { const navigate = useNavigate(); const runAction = useAction(); - const { data: projects, loading, reload } = useAsync(listProjects, []); + const { data: projects, reload } = useAsync(listProjects, []); const { data: priorities } = useAsync(() => listCatalog('priorities'), []); const { data: statuses } = useAsync(() => listCatalog('project-statuses'), []); const { data: types } = useAsync(() => listCatalog('project-types'), []); @@ -75,7 +75,7 @@ export default function ProyectosPage() { { key: 'risk_level', label: t('project_risk'), render: (r) => {t(`risk_${r.risk_level}`)} }, ]; - if (loading || !projects) return
{t('common_loading')}
; + if (!projects) return
{t('common_loading')}
; return (