Migrate Analytics, Salarios, Pensions, ServiciosMunicipales, Proyecciones to queries

Every page now goes through TanStack Query with cancellation,
placeholder data on filter changes, and a visible ErrorState with retry
instead of console.error/silent-empty rendering (FE-02, UX-02).
Analytics keeps the trend query on its own key so cycle changes don't
refetch it. Pensions/Servicios upload-triggered refreshes are bounded
by the api-level 30s timeout (FE-05); pension upload results cap at 10
rows with an overflow note (FE-19). Note: the Pensions ROI fallback
already guarded short chart data — FE-04 was already handled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Carlos Escalante
2026-06-10 17:23:52 -06:00
parent 65dffdac06
commit 7a1733bf7e
5 changed files with 139 additions and 88 deletions

View File

@@ -1,9 +1,11 @@
import { useEffect, useMemo, useState } from 'react';
import { useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import { type ColumnDef } from '@tanstack/react-table';
import { Landmark, RefreshCw, Hash, CalendarDays, Banknote } from 'lucide-react';
import { type Transaction, type SalariosSummary, getSalarios, getSalariosSummary } from '@/lib/api';
import { formatAmount, formatDate } from '@/lib/format';
import ErrorState from '@/components/ErrorState';
import { DataTable } from '@/components/ui/data-table';
import { DataTableColumnHeader } from '@/components/ui/data-table-column-header';
import { Card, CardContent } from '@/components/ui/card';
@@ -11,27 +13,20 @@ import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
export default function Salarios() {
const [deposits, setDeposits] = useState<Transaction[]>([]);
const [summary, setSummary] = useState<SalariosSummary | null>(null);
const [loading, setLoading] = useState(true);
const fetchData = async () => {
setLoading(true);
try {
const query = useQuery({
queryKey: ['salarios'],
queryFn: async () => {
const [depRes, sumRes] = await Promise.all([
getSalarios({ limit: 500 }),
getSalariosSummary(),
]);
setDeposits(depRes.data);
setSummary(sumRes.data);
} catch (e) {
console.error(e);
} finally {
setLoading(false);
}
};
useEffect(() => { fetchData(); }, []);
return { deposits: depRes.data, summary: sumRes.data };
},
});
const deposits = query.data?.deposits ?? [];
const summary = query.data?.summary ?? null;
const loading = query.isFetching;
const fetchData = () => query.refetch();
const columns = useMemo<ColumnDef<Transaction, unknown>[]>(
() => [
@@ -146,18 +141,25 @@ export default function Salarios() {
)}
{/* Data table */}
<Card>
<CardContent className="p-0">
<DataTable
columns={columns}
data={deposits}
pagination
pageSize={25}
initialSorting={[{ id: 'date', desc: true }]}
emptyMessage="No hay depósitos registrados aún."
/>
</CardContent>
</Card>
{query.isError ? (
<ErrorState
message="No se pudieron cargar los salarios"
onRetry={fetchData}
/>
) : (
<Card>
<CardContent className="p-0">
<DataTable
columns={columns}
data={deposits}
pagination
pageSize={25}
initialSorting={[{ id: 'date', desc: true }]}
emptyMessage="No hay depósitos registrados aún."
/>
</CardContent>
</Card>
)}
</div>
);
}