Cargando Futbol Arias Fit Pro

Estamos preparando el panel y los reportes del jugador. Si tarda demasiado, recarga la pagina.

`; }function openPrintWindow(report) { if (!report) return; const reportHtml = renderReportDocument(report); const popup = window.open('', '_blank', 'width=1320,height=960'); if (!popup || !popup.document) { alert('Tu navegador bloqueó la ventana del informe. Permite popups para abrir la vista previa PDF.'); return; } const popupDocument = popup.document; popupDocument.open(); popupDocument.write(buildStandaloneReportHtml(report, reportHtml)); popupDocument.close(); popup.focus(); }function AdviceGrid({ insights }) { const safeInsights = Array.isArray(insights) ? insights : []; return (
{safeInsights.map((tip, index) => { const priorityMeta = getPriorityMeta(tip.priority); const tone = tip.priority === 'alta' ? { wrapper: 'tw-border-red-200 tw-bg-red-50', title: '#881337', body: '#9f1239', chip: 'tw-bg-white tw-border-red-200 tw-text-red-700' } : (tip.priority === 'media' ? { wrapper: 'tw-border-amber-200 tw-bg-amber-50', title: '#92400e', body: '#a16207', chip: 'tw-bg-white tw-border-amber-200 tw-text-amber-700' } : { wrapper: 'tw-border-emerald-200 tw-bg-emerald-50', title: '#065f46', body: '#047857', chip: 'tw-bg-white tw-border-emerald-200 tw-text-emerald-700' }); return (

{tip.title}

{priorityMeta.label}

{tip.message}

); })}
); }function ReportPreview({ report, variant = 'inline' }) { if (!report) return null; return (
); }function ReportPreviewModal({ title, report, onClose }) { if (!report) return null; return ( ); }// ========================================== // COMPONENTES UI // ========================================== function Button({ children, onClick, variant = 'primary', className = '', type = 'button' }) { const base = "tw-px-4 tw-py-2 tw-rounded-lg tw-font-medium tw-transition-all tw-flex tw-items-center tw-justify-center tw-gap-2 tw-text-sm tw-shadow-md active:tw-scale-95"; const styles = { primary: "tw-bg-sky-600 hover:tw-bg-sky-500 tw-text-white", success: "tw-bg-emerald-600 hover:tw-bg-emerald-500 tw-text-white", secondary: "tw-bg-slate-700 hover:tw-bg-slate-600 tw-text-slate-200 tw-border tw-border-slate-600", ghost: "tw-bg-transparent hover:tw-bg-slate-800 tw-text-slate-300 tw-border tw-border-slate-700" }; return ; }function Input({ label, name, value, onChange, type = "text", placeholder = "", required = false, className="" }) { return (
); }function Select({ label, name, value, onChange, options, required = false }) { return (
); }function Card({ children, className = "" }) { return
{children}
; }function Modal({ title, children, onClose, size = 'md' }) { const sizeClass = size === 'xl' ? 'afp-modal-xl' : (size === 'lg' ? 'afp-modal-lg' : ''); return (

{title}

{children}
); }function ResultBadge({ label, value, status }) { let colorClass = "tw-bg-slate-700 tw-text-slate-300"; if (status === 'good') colorClass = "tw-bg-emerald-500/20 tw-text-emerald-400 tw-border tw-border-emerald-500/50"; if (status === 'warning') colorClass = "tw-bg-yellow-500/20 tw-text-yellow-400 tw-border tw-border-yellow-500/50"; if (status === 'bad') colorClass = "tw-bg-red-500/20 tw-text-red-400 tw-border tw-border-red-500/50"; return (
{label} {value}
); }function EstaturaDelta({ current, previous }) { if (!previous) return 1ra Medición; const diff = parseFloat(current) - parseFloat(previous); if (isNaN(diff)) return null; if (Math.abs(diff) < 0.1) return Se mantuvo estable; if (diff > 0) return ¡Creció +{diff.toFixed(1)} cm! ↗; return Bajó {Math.abs(diff).toFixed(1)} cm ↘; }function DeltaIndicator({ current, previous, inverse = false, unit = "" }) { if (!previous) return null; const curVal = parseFloat(current); const prevVal = parseFloat(previous); if (isNaN(curVal) || isNaN(prevVal)) return null; const diff = curVal - prevVal; if (Math.abs(diff) < 0.1) return null; const isPositive = diff > 0; let isGood = isPositive; if (inverse) isGood = !isPositive; let colorClass = isGood ? 'tw-text-emerald-400' : 'tw-text-red-400'; const IconToUse = isPositive ? Icons.TrendingUp : Icons.TrendingDown; return ( {isPositive ? '+' : ''}{diff.toFixed(1)}{unit} ); }function EvolutionChart({ history, title, dataKey, color }) { const chartRef = useRef(null); const chartInstanceRef = useRef(null); useEffect(() => { if (!chartRef.current || !Array.isArray(history) || history.length < 2) return; const sortedHistory = [...history].sort((a,b) => new Date(a.date) - new Date(b.date)); const labels = sortedHistory.map(m => new Date(m.date).toLocaleDateString(undefined, {month:'short', day:'numeric'})); const dataPoints = sortedHistory.map(m => { if (dataKey === 'masaMagraKg') return parseFloat(m.results?.masaMagraKg || 0); return parseFloat(m.data?.[dataKey] || 0); }); if (chartInstanceRef.current) chartInstanceRef.current.destroy(); const ctx = chartRef.current.getContext('2d'); chartInstanceRef.current = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: title, data: dataPoints, borderColor: color, backgroundColor: color + '33', borderWidth: 2, pointBackgroundColor: '#0f172a', pointBorderColor: color, fill: true, tension: 0.3 }] }, options: { responsive: true, maintainAspectRatio: false, animation: false, plugins: { legend: { display: false } }, scales: { x: { grid: { color: '#334155' }, ticks: { color: '#94a3b8' } }, y: { grid: { color: '#334155' }, ticks: { color: '#94a3b8' } } } } }); return () => { if (chartInstanceRef.current) chartInstanceRef.current.destroy(); }; }, [history, dataKey, color, title]); if (!Array.isArray(history) || history.length < 2) return null; return (

{title}

); }function BarChart({ labels, data, title, color }) { const chartRef = useRef(null); const chartInstanceRef = useRef(null); useEffect(() => { if (!chartRef.current || !labels || !data || labels.length === 0) return; if (chartInstanceRef.current) chartInstanceRef.current.destroy(); const ctx = chartRef.current.getContext('2d'); chartInstanceRef.current = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: title, data: data, backgroundColor: color + 'CC', borderColor: color, borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, animation: false, plugins: { legend: { display: false } }, scales: { x: { grid: { display: false }, ticks: { color: '#94a3b8' } }, y: { grid: { color: '#334155' }, ticks: { color: '#94a3b8' } } } } }); return () => { if (chartInstanceRef.current) chartInstanceRef.current.destroy(); }; }, [labels, data, color, title]); if (!labels || labels.length === 0) return null; return (

{title}

); }// ========================================== // VISTAS Y MODALES PRINCIPALES // ==========================================function CategoryManager({ categories, onSave, onClose }) { const safeCats = Array.isArray(categories) ? categories : []; const [localCats, setLocalCats] = useState([...safeCats]); const [newCat, setNewCat] = useState(''); return (

Escribe el nombre de la serie y presiona el botón '+' para agregarla.

setNewCat(e.target.value)} placeholder="Ej: Sub-14" className="tw-flex-1 tw-bg-slate-900 tw-border tw-border-slate-700 tw-rounded-lg tw-px-3 tw-text-white focus:tw-outline-none focus:tw-border-sky-500" />
{localCats.map(cat => (
{cat}
))}
); }function PlayerFormModal({ player, categories, onSave, onClose }) { const safeCats = Array.isArray(categories) ? categories : []; const [formData, setFormData] = useState({ id: player?.id || '', name: player?.name || '', dob: player?.dob || '', category: player?.category || (safeCats.length > 0 ? safeCats[0] : 'General'), position: player?.position || 'mediocampista', gender: player?.gender || 'masculino', pin: player?.pin || '' });const handleSubmit = (e) => { e.preventDefault(); onSave({ ...formData, id: formData.id || Date.now().toString() }); };return (
setFormData({...formData, name: e.target.value})} required />
setFormData({...formData, pin: e.target.value})} placeholder="Ej: 1234" required /> setFormData({...formData, dob: e.target.value})} required />
setFormData({...formData, position: e.target.value})} options={['portero','defensa','mediocampista','delantero']} />
setForm({...form, peso:e.target.value})} /> setForm({...form, estatura:e.target.value})} /> setForm({...form, grasaCorporal:e.target.value})} /> setForm({...form, tmb:e.target.value})} />

Bioimpedancia (Opcional)

setForm({...form, masaMuscular:e.target.value})} /> setForm({...form, grasaVisceral:e.target.value})} /> setForm({...form, aguaCorporal:e.target.value})} /> setForm({...form, masaOsea:e.target.value})} />

Antropometría y Somatotipo (Opcional)

setForm({...form, pliegueTriceps:e.target.value})} /> setForm({...form, pliegueSubescapular:e.target.value})} /> setForm({...form, pliegueSupraespinal:e.target.value})} /> setForm({...form, plieguePantorrilla:e.target.value})} /> setForm({...form, diametroHumero:e.target.value})} /> setForm({...form, diametroFemur:e.target.value})} /> setForm({...form, perimetroBrazo:e.target.value})} /> setForm({...form, perimetroPantorrilla:e.target.value})} />
); }function PlayerDetail({ player, measurements, onBack, onNew, onDeleteM, onViewM, onEditM, onEditPlayer }) { const safeMeasurements = Array.isArray(measurements) ? measurements : []; const history = safeMeasurements.filter(m => m && m.playerId === player.id).sort((a,b) => new Date(b.date || 0) - new Date(a.date || 0)); const last = history.length > 0 ? history[0] : null; const prev = history.length > 1 ? history[1] : null; const latestReport = last ? buildPlayerReportModel(player, last, history) : null;return (

{player.name}

{player.category}
{player?.name ? player.name.charAt(0) : '?'}
Edad
{calculateAge(player.dob)}
Posición
{player.position}
Peso Actual
{last?.data?.peso ? last.data.peso + 'kg' : '-'}
% Grasa
{last?.data?.grasaCorporal ? last.data.grasaCorporal + '%' : '-'}
{last && (
Estatura Actual
{last.data.estatura} cm
)}
{last && }
{/* INTELIGENCIA DEPORTIVA DESTACADA EN PERFIL */} {last && (

Inteligencia Deportiva: Recomendaciones

)}{history.length > 1 && (

Gráficas de Progreso

)}

Historial de Evaluaciones

{history.length}
{history.length === 0 ?

No hay mediciones registradas.

: history.map((m, i) => { const prevM = history[i+1]; return (
{new Date(m.date || 0).getFullYear()}
{new Date(m.date || 0).toLocaleDateString(undefined, {month:'short', day:'numeric'})}
Peso
{m.data?.peso} kg
Grasa Corp.
{m.data?.grasaCorporal}%
Masa Magra
{m.results?.masaMagraKg} kg
Estatura {m.data?.estatura} cm
)})}
); }function MeasurementResultView({ player, measurement, history, onBack, mode = 'view' }) { if (!measurement || !measurement.results || !measurement.data) return null; let result = { ...measurement.results }; if (!result.grasaObjetivoMin || !result.grasaObjetivoMax) { const targets = getTargetFat(player); result.grasaObjetivoMin = targets.min; result.grasaObjetivoMax = targets.max; }const form = measurement.data; const safeHistory = Array.isArray(history) ? history : []; const sortedHistory = [...safeHistory].sort((a,b) => new Date(b.date) - new Date(a.date)); const idx = sortedHistory.findIndex(m => m.id === measurement.id); const prevM = (idx >= 0 && idx + 1 < sortedHistory.length) ? sortedHistory[idx + 1] : null; const reportModel = buildPlayerReportModel(player, measurement, safeHistory); const [showPreview, setShowPreview] = useState(false);return (

{mode === 'player' ? 'Mi informe físico' : 'Informe profesional del jugador'}

{player?.name || ''} | {formatDate(measurement.date || Date.now())}

{onBack && }

Lectura rápida

{reportModel?.hero?.value || '-'}

{reportModel?.hero?.note || ''}

{reportModel?.status ? {reportModel.status.label} : null}

Inteligencia deportiva: recomendaciones

{showPreview && setShowPreview(false)} />}
);return (

{mode === 'new' ? 'Resultados Calculados' : 'Informe de Jugador'}

{new Date(measurement.date || Date.now()).toLocaleDateString()} {player?.name || ''}
{mode === 'view' && }
{mode === 'new' && (
)}
); }function TeamReportView({ players, measurements, category, onBack }) { const filteredPlayers = players.filter(p => category === 'Todas' || p.category === category); const getLatestMeasurement = (playerId) => { const history = (measurements || []).filter(m => m && m.playerId === playerId).sort((a,b) => new Date(b.date || 0) - new Date(a.date || 0)); return history.length > 0 ? history[0] : null; }; const playerData = filteredPlayers.map(p => ({ player: p, latest: getLatestMeasurement(p.id) })).filter(item => item.latest !== null); const reportModel = buildTeamReportModel(players, measurements, category); const [showPreview, setShowPreview] = useState(false);return (

Informe profesional de serie

{category} | Resumen ejecutivo para el cuerpo técnico

Alertas e inteligencia de serie

{showPreview && setShowPreview(false)} />}
);let sumPeso = 0, sumGrasa = 0, sumMagra = 0, alertasGrasa = 0, alertasHidratacion = 0; playerData.forEach(item => { sumPeso += parseFloat(item.latest.data?.peso || 0); sumGrasa += parseFloat(item.latest.data?.grasaCorporal || 0); sumMagra += parseFloat(item.latest.results?.masaMagraKg || 0); if (item.latest.results?.analisis?.grasa?.status === 'bad') alertasGrasa++; if (item.latest.results?.analisis?.agua?.status === 'bad') alertasHidratacion++; }); const count = playerData.length; const avgPeso = count > 0 ? (sumPeso / count).toFixed(1) : 0; const avgGrasa = count > 0 ? (sumGrasa / count).toFixed(1) : 0; const avgMagra = count > 0 ? (sumMagra / count).toFixed(1) : 0;return ( ); }function ClubReportView({ players, measurements, categories, onBack }) { const getLatestMeasurement = (playerId) => { const history = (measurements || []).filter(m => m && m.playerId === playerId).sort((a,b) => new Date(b.date || 0) - new Date(a.date || 0)); return history.length > 0 ? history[0] : null; }; const allPlayerData = players.map(p => ({ player: p, latest: getLatestMeasurement(p.id) })).filter(item => item.latest !== null); const reportModel = buildClubReportModel(players, measurements, categories); const [showPreview, setShowPreview] = useState(false);return (

Reporte institucional

Vista ejecutiva del estado físico del club

Lectura institucional

{showPreview && setShowPreview(false)} />}
);let sumPesoGlobal = 0, sumGrasaGlobal = 0, totalJugadores = allPlayerData.length; const categoryStats = categories.map(cat => { const catPlayers = allPlayerData.filter(p => p.player.category === cat); let sp = 0, sg = 0; catPlayers.forEach(cp => { sp += parseFloat(cp.latest.data?.peso || 0); sg += parseFloat(cp.latest.data?.grasaCorporal || 0); }); sumPesoGlobal += sp; sumGrasaGlobal += sg; return { category: cat, count: catPlayers.length, avgPeso: catPlayers.length > 0 ? (sp / catPlayers.length).toFixed(1) : 0, avgGrasa: catPlayers.length > 0 ? (sg / catPlayers.length).toFixed(1) : 0 }; }).filter(cs => cs.count > 0);const avgPesoGlobal = totalJugadores > 0 ? (sumPesoGlobal / totalJugadores).toFixed(1) : 0; const avgGrasaGlobal = totalJugadores > 0 ? (sumGrasaGlobal / totalJugadores).toFixed(1) : 0;return ( ); }function MeasurementResultViewLegacy({ player, measurement, history, onBack, mode = 'view' }) { if (!measurement || !measurement.results || !measurement.data) return null; const reportModel = buildPlayerReportModel(player, measurement, history); const [showPreview, setShowPreview] = useState(false);return (

{mode === 'player' ? 'Mi informe fisico' : 'Informe profesional del jugador'}

{player?.name || ''} | {formatDate(measurement.date || Date.now())}

{onBack && }

Resumen de recomendaciones

{showPreview && setShowPreview(false)} />}
); }function TeamReportViewLegacy({ players, measurements, category, onBack }) { const reportModel = buildTeamReportModel(players, measurements, category); const [showPreview, setShowPreview] = useState(false);return (

Informe profesional de serie

{category} | Resumen ejecutivo para el cuerpo tecnico

Alertas e inteligencia de serie

{showPreview && setShowPreview(false)} />}
); }function ClubReportViewLegacy({ players, measurements, categories, onBack }) { const reportModel = buildClubReportModel(players, measurements, categories); const [showPreview, setShowPreview] = useState(false);return (

Reporte institucional

Vista ejecutiva del estado fisico del club

Lectura institucional

{showPreview && setShowPreview(false)} />}
); }function RosterView({ players, categories, measurements, onSelectPlayer, onSavePlayer, onDeletePlayer, onManageCategories, onShowTeamReport, onShowClubReport, onNewPlayer }) { const [search, setSearch] = useState(''); const [filterCat, setFilterCat] = useState('Todas'); const safePlayers = Array.isArray(players) ? players : []; const safeCategories = Array.isArray(categories) ? categories : ['General']; const filtered = safePlayers.filter(p => p && (filterCat === 'Todas' || p.category === filterCat) && (p.name || '').toLowerCase().includes(search.toLowerCase()));const exportToCSV = () => { let csvContent = "data:text/csv;charset=utf-8,\uFEFF"; csvContent += "Nombre;Categoria;Posicion;Edad;Peso(kg);Grasa Corp.(%);Masa Magra(kg);IMC;Endomorfia;Mesomorfia;Ectomorfia;Ultima Medicion\n"; filtered.forEach(p => { const safeMeasurements = Array.isArray(measurements) ? measurements : []; const history = safeMeasurements.filter(m => m && m.playerId === p.id).sort((a,b) => new Date(b.date || 0) - new Date(a.date || 0)); const latest = history.length > 0 ? history[0] : null; const age = calculateAge(p.dob); let row = [ `"${p.name || ''}"`, `"${p.category || ''}"`, `"${p.position || ''}"`, age, (latest?.data?.peso || '-').toString().replace('.', ','), (latest?.data?.grasaCorporal || '-').toString().replace('.', ','), (latest?.results?.masaMagraKg || '-').toString().replace('.', ','), (latest?.results?.imc || '-').toString().replace('.', ','), (latest?.results?.somatotipo?.endo || '-').toString().replace('.', ','), (latest?.results?.somatotipo?.meso || '-').toString().replace('.', ','), (latest?.results?.somatotipo?.ecto || '-').toString().replace('.', ','), latest?.date ? new Date(latest.date).toLocaleDateString() : '-' ].join(";"); csvContent += row + "\n"; }); const link = document.createElement("a"); link.setAttribute("href", encodeURI(csvContent)); link.setAttribute("download", `reporte_plantilla_${new Date().toISOString().split('T')[0]}.csv`); document.body.appendChild(link); link.click(); document.body.removeChild(link); };return (

Panel Manager

Total: {safePlayers.length} Jugadores

{safeCategories.map(cat => { const count = safePlayers.filter(p => p.category === cat).length; return (

{cat}

{count} Jugadores

) })}
setSearch(e.target.value)} placeholder="Buscar jugador..." className="tw-w-full tw-bg-slate-800 tw-pl-10 tw-py-2 tw-rounded-lg tw-border-slate-700 tw-text-white focus:tw-ring-2 focus:tw-ring-sky-500" />
{['Todas', ...safeCategories].map(c => ())}
{filtered.map(p => (
onSelectPlayer(p)} className="tw-bg-slate-800 tw-border tw-border-slate-700 tw-p-3 tw-rounded-xl tw-cursor-pointer hover:tw-border-sky-500 hover:tw-bg-slate-800/80 tw-transition-all tw-group tw-relative tw-shadow-sm hover:tw-shadow-md">

{p.name}

{p.position}{calculateAge(p.dob)} años
{p.category}
))}
); }function PlayerLoginView({ onLogin }) { const [name, setName] = useState(''); const [pin, setPin] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const handleSubmit = async (e) => { e.preventDefault(); setLoading(true); setError(''); const res = await api.playerLogin(name, pin); setLoading(false); if (res && res.success) onLogin(res.data.player, res.data.measurements, res.data.categoryAverages); else setError(res?.data?.message || 'Error de conexión o datos incorrectos.'); }; return (

Portal del Jugador

Fútbol Arias Fit Pro

setName(e.target.value)} placeholder="Ej: Juan Pérez" required /> setPin(e.target.value)} placeholder="****" required /> {error &&
{error}
}
); }class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false, errorInfo: null }; } static getDerivedStateFromError(error) { return { hasError: true, errorInfo: error.toString() }; } render() { if (this.state.hasError) { return (

⚠️ Error de Interfaz

Ocurrió un problema de carga. Asegúrate de haber vaciado la caché de tu web.

{this.state.errorInfo}
); } return this.props.children; } }function App() { const [view, setView] = useState('loading'); const [players, setPlayers] = useState([]); const [measurements, setMeasurements] = useState([]); const [categories, setCategories] = useState([]); const [selectedPlayer, setSelectedPlayer] = useState(null); const [viewingMeasurement, setViewingMeasurement] = useState(null); const [editingMeasurement, setEditingMeasurement] = useState(null); const [selectedCategoryReport, setSelectedCategoryReport] = useState('Todas'); const [currentPlayer, setCurrentPlayer] = useState(null); const [playerMeasurements, setPlayerMeasurements] = useState([]); const [showCat, setShowCat] = useState(false); const [showPlayerModal, setShowPlayerModal] = useState(false); const [editingPlayer, setEditingPlayer] = useState(null); const [status, setStatus] = useState(null); const latestSnapshotRef = useRef({ players: [], measurements: [], categories: [] }); const runTransition = typeof startTransition === 'function' ? startTransition : (callback) => callback();useEffect(() => { if (config.isManager) { api.get().then(res => { if (res && res.success && res.data) { const nextPlayers = Array.isArray(res.data.players) ? res.data.players : []; const nextMeasurements = Array.isArray(res.data.measurements) ? res.data.measurements : []; const nextCategories = Array.isArray(res.data.categories) ? res.data.categories : ['General']; latestSnapshotRef.current = { players: nextPlayers, measurements: nextMeasurements, categories: nextCategories }; setPlayers(nextPlayers); setMeasurements(nextMeasurements); setCategories(nextCategories); setView('roster'); } else setView('player_login'); }); } else setView('player_login'); }, []);useEffect(() => { latestSnapshotRef.current = { players, measurements, categories }; }, [players, measurements, categories]);const save = async (p, m, c) => { const newP = Array.isArray(p) ? p : players; const newM = Array.isArray(m) ? m : measurements; const newC = Array.isArray(c) ? c : categories; latestSnapshotRef.current = { players: newP, measurements: newM, categories: newC }; setPlayers(newP); setMeasurements(newM); setCategories(newC); const res = await api.save({ players: newP, measurements: newM, categories: newC }); setStatus(res?.success ? {t:'success', m:'Guardado Correctamente'} : {t:'error', m:'Error al Guardar'}); setTimeout(()=>setStatus(null), 3000); };const applyRemoteSnapshotSilently = (nextPlayers, nextMeasurements, nextCategories) => { runTransition(() => { setPlayers(nextPlayers); setMeasurements(nextMeasurements); setCategories(nextCategories);if (selectedPlayer?.id) { setSelectedPlayer(nextPlayers.find(player => player.id === selectedPlayer.id) || null); }if (currentPlayer?.id) { const refreshedCurrentPlayer = nextPlayers.find(player => player.id === currentPlayer.id) || currentPlayer; const refreshedPlayerMeasurements = nextMeasurements .filter(item => item && item.playerId === refreshedCurrentPlayer.id) .sort((a, b) => new Date(b.date || 0) - new Date(a.date || 0)); setCurrentPlayer(refreshedCurrentPlayer); setPlayerMeasurements(refreshedPlayerMeasurements);if (view === 'player_dashboard') { const refreshedDashboardMeasurement = viewingMeasurement?.id ? refreshedPlayerMeasurements.find(item => item.id === viewingMeasurement.id) || refreshedPlayerMeasurements[0] || null : refreshedPlayerMeasurements[0] || null; setViewingMeasurement(refreshedDashboardMeasurement); } } else if (viewingMeasurement?.id) { setViewingMeasurement(nextMeasurements.find(item => item.id === viewingMeasurement.id) || viewingMeasurement); } }); };const handlePlayerLogin = (p, m, averages) => { setCurrentPlayer(p); const mArray = Array.isArray(m) ? m : []; setPlayerMeasurements(mArray); const sorted = [...mArray].sort((a,b) => new Date(b.date || 0) - new Date(a.date || 0)); if (sorted.length > 0) { setViewingMeasurement(sorted[0]); setView('player_dashboard'); } else { alert("Aún no tienes mediciones registradas por tu entrenador."); } };useEffect(() => { if (!config.isManager) return; if (showCat || showPlayerModal || view === 'calc' || view === 'loading' || view === 'player_login') return;const intervalId = window.setInterval(async () => { const res = await api.get(); if (!(res && res.success && res.data)) return;const nextPlayers = Array.isArray(res.data.players) ? res.data.players : []; const nextMeasurements = Array.isArray(res.data.measurements) ? res.data.measurements : []; const nextCategories = Array.isArray(res.data.categories) ? res.data.categories : ['General']; const currentSignature = JSON.stringify(latestSnapshotRef.current); const nextSignature = JSON.stringify({ players: nextPlayers, measurements: nextMeasurements, categories: nextCategories }); if (currentSignature === nextSignature) return;latestSnapshotRef.current = { players: nextPlayers, measurements: nextMeasurements, categories: nextCategories }; applyRemoteSnapshotSilently(nextPlayers, nextMeasurements, nextCategories); }, 15000);return () => window.clearInterval(intervalId); }, [view, showCat, showPlayerModal, selectedPlayer, currentPlayer, viewingMeasurement]);const renderView = () => { if (view === 'loading') return
; if (config.isManager) { if (view === 'roster') return {setSelectedPlayer(p); setView('player');}} onSavePlayer={(p) => { const sp = Array.isArray(players)?players:[]; if(sp.some(pl=>pl.id===p.id)) save(sp.map(pl=>pl.id===p.id?p:pl)); else save([...sp, p]); setShowPlayerModal(false); }} onDeletePlayer={id => save((players||[]).filter(p=>p&&p.id!==id), (measurements||[]).filter(m=>m&&m.playerId!==id))} onManageCategories={() => setShowCat(true)} onShowTeamReport={(cat) => { setSelectedCategoryReport(cat); setView('team_report'); }} onShowClubReport={() => setView('club_report')} onNewPlayer={() => { setEditingPlayer(null); setShowPlayerModal(true); }} />; if (view === 'club_report') return setView('roster')} />; if (view === 'team_report') return setView('roster')} />; if (view === 'player') return {setSelectedPlayer(null); setView('roster');}} onNew={() => { setEditingMeasurement(null); setView('calc'); }} onDeleteM={id => save(undefined, (measurements||[]).filter(m=>m&&m.id!==id))} onViewM={(m) => { setViewingMeasurement(m); setView('view_measurement'); }} onEditM={(m) => { setEditingMeasurement(m); setView('calc'); }} onEditPlayer={(p) => { setEditingPlayer(p); setShowPlayerModal(true); }} />; if (view === 'calc') return { setEditingMeasurement(null); setView('player'); }} onSave={(d, r) => { if(editingMeasurement) { const upM = {...editingMeasurement, data:d, results:r}; save(undefined, measurements.map(m=>m.id===upM.id?upM:m)); setEditingMeasurement(null); } else { save(undefined, [...measurements, {id: Date.now().toString(), playerId: selectedPlayer.id, date: new Date().toISOString(), data: d, results: r}]); } setView('player'); }} />; if (view === 'view_measurement') return m.playerId === selectedPlayer.id)} onBack={() => { setViewingMeasurement(null); setView('player'); }} />; } if (view === 'player_login') return ; if (view === 'player_dashboard') return setView('player_login')} mode="player" />; };return (

Fútbol Arias Fit Pro

{!config.isManager && view === 'player_dashboard' && }
{status &&
{status.m}
} {renderView()}{showCat && save(undefined, undefined, newCats)} onClose={() => setShowCat(false)} />} {showPlayerModal && { const sp = Array.isArray(players)?players:[]; if(sp.some(pl=>pl.id===p.id)) save(sp.map(pl=>pl.id===p.id?p:pl)); else save([...sp, p]); setShowPlayerModal(false); }} onClose={() => setShowPlayerModal(false)} />}
); }try { const root = ReactDOM.createRoot(document.getElementById('arias-fit-root')); root.render(); window.__afpBooted = true; } catch (error) { if (typeof window !== 'undefined' && typeof window.__afpShowBootFallback === 'function') { window.__afpShowBootFallback(error?.message || 'Error al montar la interfaz principal.'); } }