Files
seisee/frontend_src/components/H5Coverage.tsx

152 lines
7.3 KiB
TypeScript

import { useState, useEffect } from 'react';
const API_BASE = '/seismic/api';
interface CoverageStats {
total_positions: number;
with_data: number;
with_aux: number;
total_files: number;
coverage_pct: number;
missing: number;
}
interface Gap {
start: number;
end: number;
length: number;
}
function H5Coverage({ onClose }: { onClose: () => void }) {
const [stats, setStats] = useState<CoverageStats | null>(null);
const [gaps, setGaps] = useState<Gap[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
Promise.all([
fetch(`${API_BASE}/h5/coverage`).then(r => r.json()),
fetch(`${API_BASE}/h5/gaps`).then(r => r.json())
]).then(([statsData, gapsData]) => {
setStats(statsData);
setGaps(gapsData.gaps.sort((a: Gap, b: Gap) => b.length - a.length));
setLoading(false);
});
}, []);
if (loading) {
return (
<div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, background: '#0f172a', zIndex: 3000, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff' }}>
<div>Chargement des statistiques H5</div>
</div>
);
}
return (
<div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, background: '#0f172a', zIndex: 3000, display: 'flex', flexDirection: 'column', overflowY: 'auto' }}>
<div style={{ padding: '20px', background: '#1e293b', borderBottom: '1px solid #334155', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<h2 style={{ margin: 0, color: '#f8fafc' }}>📊 H5 Data Coverage</h2>
<button onClick={onClose} style={{ background: '#ef4444', color: '#fff', border: 'none', padding: '8px 20px', borderRadius: '4px', cursor: 'pointer' }}>Fermer</button>
</div>
<div style={{ flex: 1, padding: '20px', maxWidth: '1200px', margin: '0 auto', width: '100%' }}>
{/* Stats globales */}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '15px', marginBottom: '30px' }}>
<div style={{ background: '#1e293b', padding: '20px', borderRadius: '8px', border: '1px solid #334155' }}>
<div style={{ fontSize: '0.8rem', color: '#94a3b8', marginBottom: '5px' }}>Positions totales</div>
<div style={{ fontSize: '2rem', fontWeight: 'bold', color: '#f8fafc' }}>{stats?.total_positions}</div>
</div>
<div style={{ background: '#1e293b', padding: '20px', borderRadius: '8px', border: '1px solid #334155' }}>
<div style={{ fontSize: '0.8rem', color: '#94a3b8', marginBottom: '5px' }}>Avec données</div>
<div style={{ fontSize: '2rem', fontWeight: 'bold', color: '#4ade80' }}>{stats?.with_data}</div>
</div>
<div style={{ background: '#1e293b', padding: '20px', borderRadius: '8px', border: '1px solid #334155' }}>
<div style={{ fontSize: '0.8rem', color: '#94a3b8', marginBottom: '5px' }}>Manquantes</div>
<div style={{ fontSize: '2rem', fontWeight: 'bold', color: '#ef4444' }}>{stats?.missing}</div>
</div>
<div style={{ background: '#1e293b', padding: '20px', borderRadius: '8px', border: '1px solid #334155' }}>
<div style={{ fontSize: '0.8rem', color: '#94a3b8', marginBottom: '5px' }}>Coverage</div>
<div style={{ fontSize: '2rem', fontWeight: 'bold', color: stats && stats.coverage_pct > 50 ? '#4ade80' : '#fbbf24' }}>{stats?.coverage_pct}%</div>
</div>
<div style={{ background: '#1e293b', padding: '20px', borderRadius: '8px', border: '1px solid #334155' }}>
<div style={{ fontSize: '0.8rem', color: '#94a3b8', marginBottom: '5px' }}>Fichiers totaux</div>
<div style={{ fontSize: '2rem', fontWeight: 'bold', color: '#38bdf8' }}>{stats?.total_files}</div>
</div>
</div>
{/* Progress bar */}
<div style={{ background: '#1e293b', padding: '20px', borderRadius: '8px', border: '1px solid #334155', marginBottom: '30px' }}>
<div style={{ fontSize: '0.9rem', color: '#94a3b8', marginBottom: '10px' }}>Progression du déploiement</div>
<div style={{ background: '#0f172a', height: '30px', borderRadius: '15px', overflow: 'hidden', position: 'relative' }}>
<div style={{
background: 'linear-gradient(90deg, #4ade80, #22c55e)',
height: '100%',
width: `${stats?.coverage_pct}%`,
transition: 'width 1s ease'
}}></div>
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', color: '#fff', fontWeight: 'bold', fontSize: '0.9rem' }}>
{stats?.with_data} / {stats?.total_positions} positions
</div>
</div>
</div>
{/* Top 10 gaps */}
<div style={{ background: '#1e293b', padding: '20px', borderRadius: '8px', border: '1px solid #334155' }}>
<h3 style={{ margin: '0 0 15px 0', color: '#f8fafc' }}>🔍 Top 10 Gaps (plages manquantes)</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
{gaps.slice(0, 10).map((gap, i) => (
<div key={i} style={{ background: '#0f172a', padding: '12px', borderRadius: '6px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<span style={{ color: '#f8fafc', fontWeight: 'bold' }}>b{gap.start}</span>
<span style={{ color: '#94a3b8', margin: '0 8px' }}></span>
<span style={{ color: '#f8fafc', fontWeight: 'bold' }}>b{gap.end}</span>
</div>
<div style={{
background: gap.length > 50 ? '#dc2626' : gap.length > 20 ? '#f59e0b' : '#3b82f6',
color: '#fff',
padding: '4px 12px',
borderRadius: '12px',
fontSize: '0.85rem',
fontWeight: 'bold'
}}>
{gap.length} positions
</div>
</div>
))}
</div>
{gaps.length > 10 && (
<div style={{ marginTop: '15px', textAlign: 'center', color: '#94a3b8', fontSize: '0.85rem' }}>
et {gaps.length - 10} autres gaps
</div>
)}
</div>
{/* Verdict */}
<div style={{
background: stats && stats.coverage_pct < 50 ? '#7f1d1d' : '#065f46',
padding: '20px',
borderRadius: '8px',
marginTop: '30px',
border: stats && stats.coverage_pct < 50 ? '1px solid #dc2626' : '1px solid #10b981'
}}>
<div style={{ fontSize: '1.1rem', fontWeight: 'bold', color: '#fff', marginBottom: '10px' }}>
{stats && stats.coverage_pct < 50 ? '⚠️ Déploiement partiel détecté' : '✅ Coverage acceptable'}
</div>
<div style={{ color: '#e5e7eb', fontSize: '0.9rem' }}>
{stats && stats.coverage_pct < 50
? `Seulement ${stats.coverage_pct}% des positions planifiées ont des données. Cela suggère un test de déploiement plutôt qu'une collecte complète.`
: `${stats?.coverage_pct}% des positions déployées avec succès.`
}
</div>
</div>
</div>
</div>
);
}
export default H5Coverage;