diff --git a/frontend/src/app/(app)/tickets/page.tsx b/frontend/src/app/(app)/tickets/page.tsx index a6beddd..6da59c6 100644 --- a/frontend/src/app/(app)/tickets/page.tsx +++ b/frontend/src/app/(app)/tickets/page.tsx @@ -62,6 +62,7 @@ function TicketsContent() { const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(true); + const [loadError, setLoadError] = useState(""); const [view, setView] = useState("all"); // Vacío = Todos los estados. Con 1 o más = filtra por esos estados. const [statusFilters, setStatusFilters] = useState([]); @@ -119,7 +120,10 @@ function TicketsContent() { // fetch en curso que lo apaga en .finally(). Poner setLoading(true) // en un onClick sin cambio real de estado dejaba spinner eterno. // Diferido como el efecto de URL para no disparar cascading renders. - queueMicrotask(() => setLoading(true)); + queueMicrotask(() => { + setLoading(true); + setLoadError(""); + }); getTickets({ status: statusFilters.length > 0 ? statusFilters : undefined, priority: priorityFilter || undefined, @@ -138,9 +142,10 @@ function TicketsContent() { setTickets(data.items || []); setTotal(data.total || 0); }) - .catch(() => { + .catch((err) => { setTickets([]); setTotal(0); + setLoadError(err instanceof Error ? err.message : "Error al cargar tickets"); }) .finally(() => setLoading(false)); }, [statusFilters, priorityFilter, search, page, view, numericId, hasNumericId, isTeam]); @@ -206,6 +211,13 @@ function TicketsContent() { )} + {loadError && ( +
+ + {loadError} +
+ )} + {hasActiveFilters && (