feat: Initial commit for OnEver Drive centralized backup system with Windows PyQt6 agent and Proxmox LXC deployment
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user