91 lines
3.8 KiB
TypeScript
91 lines
3.8 KiB
TypeScript
import React from 'react';
|
|
import { UploadCloud, CheckCircle2, AlertTriangle } from 'lucide-react';
|
|
|
|
export interface ActiveUpload {
|
|
session_code: string;
|
|
filename: string;
|
|
client_id: number;
|
|
chunk_index: number;
|
|
received_chunks: number;
|
|
total_chunks: number;
|
|
progress_percent: number;
|
|
status?: string;
|
|
speed_mb?: number;
|
|
}
|
|
|
|
interface LiveTransferMeterProps {
|
|
uploads: ActiveUpload[];
|
|
}
|
|
|
|
export const LiveTransferMeter: React.FC<LiveTransferMeterProps> = ({ uploads }) => {
|
|
if (uploads.length === 0) {
|
|
return (
|
|
<div className="glass-card" style={{ marginBottom: '28px', textAlign: 'center', padding: '32px 20px' }}>
|
|
<UploadCloud size={32} color="var(--text-dim)" style={{ margin: '0 auto 10px auto' }} />
|
|
<h4 style={{ fontSize: '0.95rem', fontWeight: 600, color: 'var(--text-muted)' }}>Sin transferencias activas</h4>
|
|
<p style={{ fontSize: '0.8rem', color: 'var(--text-dim)', marginTop: '4px' }}>
|
|
Los agentes Windows transmitirán bloques de archivos automáticamente según sus trabajos programados.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="glass-card" style={{ marginBottom: '28px', border: '1px solid rgba(6, 182, 212, 0.3)' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '16px' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
|
|
<div className="pulse-dot" />
|
|
<h3 style={{ fontSize: '1rem', fontWeight: 700 }}>Transferencias en Vivo ({uploads.length})</h3>
|
|
</div>
|
|
<span className="badge badge-syncing">Motor de Chunks Activo</span>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
|
{uploads.map((upload) => {
|
|
const isComplete = upload.progress_percent >= 100;
|
|
return (
|
|
<div
|
|
key={upload.session_code}
|
|
style={{
|
|
background: 'rgba(0,0,0,0.3)',
|
|
padding: '16px',
|
|
borderRadius: 'var(--radius-md)',
|
|
border: '1px solid var(--border-color)',
|
|
}}
|
|
>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
|
|
<div>
|
|
<span style={{ fontWeight: 600, fontSize: '0.92rem', color: '#fff' }}>
|
|
{upload.filename}
|
|
</span>
|
|
<span style={{ fontSize: '0.78rem', color: 'var(--text-muted)', marginLeft: '12px' }}>
|
|
Cliente #{upload.client_id}
|
|
</span>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
<span style={{ fontFamily: 'var(--font-mono)', fontSize: '0.85rem', color: 'var(--accent-cyan)', fontWeight: 600 }}>
|
|
{upload.received_chunks} / {upload.total_chunks} Chunks ({upload.progress_percent}%)
|
|
</span>
|
|
{isComplete && <CheckCircle2 size={16} color="var(--accent-emerald)" />}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="progress-track" style={{ height: '10px' }}>
|
|
<div
|
|
className={`progress-fill ${isComplete ? '' : 'animated'}`}
|
|
style={{ width: `${Math.min(100, upload.progress_percent)}%` }}
|
|
/>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '0.74rem', color: 'var(--text-dim)', marginTop: '6px' }}>
|
|
<span>Sesión: {upload.session_code.substring(0, 18)}...</span>
|
|
<span>{isComplete ? 'Ensamblando & Verificando SHA-256...' : 'Transmitiendo bloques de 4 MB'}</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|