>();
useEffect(() => {
getPolicyStats().then(setStats).catch(() => setStats(null));
getPolicyFacets().then(setFacets).catch(() => setFacets(null));
}, []);
const runSearch = useCallback(
(p: number) => {
setLoading(true);
setError(null);
listPolicies({
query: query || undefined,
status: status === "all" ? undefined : status,
typeId: typeId || undefined,
providerId: providerId || undefined,
sort,
days: EXPIRY_WINDOW_DAYS,
page: p,
pageSize: 25,
})
.then((res) => {
setData(res);
setLoading(false);
})
.catch((e) => {
setError(e?.message ?? "No se pudieron cargar las pólizas.");
setLoading(false);
});
},
[query, status, typeId, providerId, sort],
);
// Debounced re-query whenever any filter changes; always back to page 1.
useEffect(() => {
if (debounceRef.current) clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => runSearch(1), 280);
return () => {
if (debounceRef.current) clearTimeout(debounceRef.current);
};
}, [runSearch]);
function goToPage(p: number) {
runSearch(p);
if (typeof window !== "undefined")
window.scrollTo({ top: 0, behavior: "smooth" });
}
const filtered =
query !== "" || status !== "all" || typeId !== "" || providerId !== "";
return (
<>
Cartera de seguros
Pólizas
{canIngest && (
+ Captura OCR
)}
{canCreate && (
+ Nueva póliza
)}
setStatus(s)}
/>
{filtered && (
)}
{data && !loading && !error && (
{data.total === 0
? "Sin resultados"
: `${formatNumber(data.total)} ${
data.total === 1 ? "póliza" : "pólizas"
}`}
{query ? ` para “${query}”` : ""}
)}
{error ? (
{error}
) : loading ? (
) : data && data.items.length === 0 ? (
) : (
<>
{data?.items.map((p) => (
))}
{data && data.pageCount > 1 && (
)}
>
)}
>
);
}
/** Counts double as filter shortcuts — clicking a cell applies that bucket. */
function StatStrip({
stats,
status,
onPickStatus,
}: {
stats: PolicyStats | null;
status: StatusFilter;
onPickStatus: (s: StatusFilter) => void;
}) {
if (!stats) {
return (
{Array.from({ length: 6 }).map((_, i) => (
))}
);
}
const cells: {
key: StatusFilter;
value: number;
label: string;
accent?: boolean;
}[] = [
{ key: "all", value: stats.total, label: "Pólizas", accent: true },
{
key: "expiring",
value: stats.expiring,
label: `Vencen en ${stats.days} días`,
accent: true,
},
{ key: "active", value: stats.active, label: "Vigentes" },
{ key: "expired", value: stats.expired, label: "Vencidas" },
{ key: "undated", value: stats.undated, label: "Sin vigencia" },
];
return (
{cells.map((c) => (
))}
{formatNumber(stats.pending)}
Sin liquidar
);
}
/** Premium in force, split by currency — MXN and USD can't be summed. */
function PremiumStrip({ stats }: { stats: PolicyStats | null }) {
if (!stats || stats.premiumInForce.length === 0) return null;
return (
Prima neta vigente
{stats.premiumInForce.map((row) => (
{formatMoney(row.netPremium, row.currency)}
{row.currency} · {formatNumber(row.count)}{" "}
{row.count === 1 ? "póliza" : "pólizas"}
))}
);
}
function PolicyRow({ p }: { p: PolicyListItem }) {
const premium = premiumHeadline(p);
const phrase = expiryPhrase(p.daysToExpiry);
const showPhrase = p.status === "expiring" || p.status === "active";
return (
{p.policyNumber || "—"}
{p.policyType?.name && (
{p.policyType.name}
)}
{policyStatusLabel(p.status)}
{!p.liquidated && (
Sin liquidar
)}
{p.customerName}
{p.insuranceProvider?.name && (
<>
·
{p.insuranceProvider.name}
>
)}
{p.vehicleCount > 0 && (
<>
·
{p.vehicleCount}{" "}
{p.vehicleCount === 1 ? "vehículo" : "vehículos"}
>
)}
{formatMoney(premium.value, p.currency)}
{formatDate(p.policyFrom)} – {formatDate(p.policyTo)}
{showPhrase && phrase && (
{phrase}
)}
);
}
function Pager({
page,
pageCount,
onChange,
}: {
page: number;
pageCount: number;
onChange: (p: number) => void;
}) {
return (
);
}
function ListSkeleton() {
return (
{Array.from({ length: 8 }).map((_, i) => (
))}
);
}
function EmptyState({ query }: { query: string }) {
return (
⌕
Sin resultados
{query
? `No encontramos pólizas para “${query}”.`
: "No hay pólizas que coincidan con los filtros."}
);
}