Files
IPSExtractor/services/desktop_app/src/renderer/components/ParallelBotProgress.tsx
T

66 lines
2.3 KiB
TypeScript

type ParallelShard = {
id?: string;
worker_id?: string | null;
status?: string;
shard_index?: number | null;
result?: Record<string, unknown>;
error?: string | null;
};
interface Props {
shards: ParallelShard[];
unit: string;
}
function asCount(value: unknown): number {
const parsed = Number(value ?? 0);
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
}
function shardProgress(shard: ParallelShard): { processed: number; total: number; percent: number } {
const result = shard.result ?? {};
const total = asCount(result.total);
const outcomes =
asCount(result.exitosos ?? result.ok) +
asCount(result.fallidos ?? result.error) +
asCount(result.existentes);
const processed = Math.min(total || Number.MAX_SAFE_INTEGER, asCount(result.processed) || outcomes);
const percent = total > 0 ? Math.min(100, Math.round((processed / total) * 100)) : 0;
return { processed, total, percent };
}
export function ParallelBotProgress({ shards, unit }: Props) {
if (shards.length === 0) return null;
return (
<div className="wiz-section" style={{ marginTop: 16, width: "100%", maxWidth: 640 }}>
<h3 className="wiz-section-title">Avance por bot</h3>
<div className="wiz-review-grid" style={{ marginTop: 8 }}>
{shards.map((shard, index) => {
const progress = shardProgress(shard);
return (
<div key={shard.id || String(index)} className="wiz-review-item">
<div style={{ width: "100%" }}>
<span className="wiz-review-label">
Bot {shard.worker_id || `#${(shard.shard_index ?? index) + 1}`}
</span>
<span className="wiz-review-value">
{progress.processed}/{progress.total || "?"} {unit} · {progress.percent}% · {shard.status || "—"}
</span>
<div className="wiz-progress-track" style={{ marginTop: 8 }}>
<div className="wiz-progress-fill" style={{ width: `${progress.percent}%` }} />
</div>
{shard.error ? (
<span className="wiz-hint" style={{ color: "var(--danger, #dc2626)", marginTop: 6 }}>
{shard.error}
</span>
) : null}
</div>
</div>
);
})}
</div>
</div>
);
}