import { MODES, PK_MODELS, CLINICAL_THRESHOLDS, APP_VERSION, generateSimulationData, processTimelineEvents } from './pkCore.js'; import { adjustChartYScale } from './chartManager.js'; import { initCalculatorUI } from './calculator.js'; import { exportSimulationToCSV } from './csvExport.js'; import { initI18n, getCurrentLang, t } from './i18n.js'; let currentMode = MODES.ESKETAMINE; let pkChart = null; let currentSimulationData = null; function updateVersionDisplay() { const versionEl = document.querySelector('[data-i18n="app.version"]'); if (versionEl) { versionEl.textContent = t('app.version', { version: APP_VERSION }); } } const timelineInputs = Array.from({ length: 61 }, (_, i) => ({ time: i * 5, bolusMg: 0, perfRate: null })); document.addEventListener('DOMContentLoaded', async () => { await initI18n(); updateVersionDisplay(); const selectLang = document.getElementById('selectLang'); if (selectLang) { selectLang.value = getCurrentLang(); selectLang.addEventListener('change', async (e) => { await initI18n(e.target.value); updateVersionDisplay(); renderTimelineGrid(); updateSimulation(); }); } initChart(); renderTimelineGrid(); initCalculatorUI(); setupEventListeners(); updateSimulation(); }); function initChart() { const ctx = document.getElementById('pkChart').getContext('2d'); pkChart = new Chart(ctx, { type: 'line', data: { labels: timelineInputs.map(d => `${d.time}'`), datasets: [] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, max: CLINICAL_THRESHOLDS[currentMode].maxScale, title: { display: true, text: 'Cp (ng/mL)', color: '#94a3b8' }, grid: { color: 'rgba(51, 65, 85, 0.4)' } }, x: { title: { display: true, text: 'Temps (minutes)', color: '#94a3b8' }, grid: { color: 'rgba(51, 65, 85, 0.4)' } } }, plugins: { legend: { labels: { color: '#f8fafc' } }, annotation: { annotations: {} } } } }); } function updateSimulation() { const weightInput = document.getElementById('patientWeight'); const weight = parseFloat(weightInput?.value) || 70; const unit = document.getElementById('infusionUnitSelect')?.value || 'mg_kg_h'; // Avertissement de poids (30-120 kg) const weightWarning = document.getElementById('weightWarning'); if (weightWarning) { if (weight < 30 || weight > 120) { weightWarning.classList.remove('hidden'); } else { weightWarning.classList.add('hidden'); } } const datasets = []; const dominoData = document.getElementById('chkDomino')?.checked ? generateSimulationData({ model: PK_MODELS.DOMINO, weightKg: weight, timelineInputs, infusionUnit: unit }) : null; const clementsData = document.getElementById('chkClements')?.checked ? generateSimulationData({ model: PK_MODELS.CLEMENTS, weightKg: weight, timelineInputs, infusionUnit: unit }) : null; const kampData = document.getElementById('chkKamp')?.checked ? generateSimulationData({ model: PK_MODELS.KAMP, weightKg: weight, timelineInputs, infusionUnit: unit }) : null; if (dominoData) { datasets.push({ label: PK_MODELS.DOMINO.name, data: dominoData.map(d => d.cp), borderColor: PK_MODELS.DOMINO.color, borderWidth: 3, tension: 0.2 }); } if (clementsData) { datasets.push({ label: PK_MODELS.CLEMENTS.name, data: clementsData.map(d => d.cp), borderColor: PK_MODELS.CLEMENTS.color, borderWidth: 3, tension: 0.2 }); } if (kampData) { datasets.push({ label: PK_MODELS.KAMP.name, data: kampData.map(d => d.cp), borderColor: PK_MODELS.KAMP.color, borderWidth: 3, tension: 0.2 }); } pkChart.data.datasets = datasets; adjustChartYScale(pkChart, currentMode, datasets); pkChart.update(); currentSimulationData = { timeline: processTimelineEvents(timelineInputs, weight, unit), domino: dominoData, clements: clementsData, kamp: kampData }; updateTimelineGridStyles(); renderDataTable(currentSimulationData); } function renderDataTable(simulationData) { const tbody = document.getElementById('dataTableBody'); if (!tbody || !simulationData) return; tbody.innerHTML = ''; simulationData.timeline.forEach((item, index) => { const row = document.createElement('tr'); row.className = index % 2 === 0 ? 'bg-slate-900/30' : 'bg-slate-800/30'; const dominoCp = (simulationData.domino && simulationData.domino[index]) ? Math.round(simulationData.domino[index].cp) : '-'; const clementsCp = (simulationData.clements && simulationData.clements[index]) ? Math.round(simulationData.clements[index].cp) : '-'; const kampCp = (simulationData.kamp && simulationData.kamp[index]) ? Math.round(simulationData.kamp[index].cp) : '-'; row.innerHTML = ` ${item.time} min ${item.bolusMg ? item.bolusMg : '-'} ${item.infusionRate !== null && item.infusionRate !== undefined ? item.infusionRate : '0'} ${dominoCp} ${clementsCp} ${kampCp} `; tbody.appendChild(row); }); } function renderTimelineGrid() { const container = document.getElementById('timelineGrid'); if (!container) return; container.innerHTML = ''; timelineInputs.forEach((item, idx) => { const col = document.createElement('div'); col.className = 'flex-1 bg-slate-900 border border-slate-700/60 rounded-xl p-2 text-center text-xs flex flex-col gap-1.5 min-w-[85px]'; col.innerHTML = `
${item.time} min
${t('timeline.bolus')}
${t('timeline.perf')}
`; container.appendChild(col); }); updateTimelineGridStyles(); } function updateTimelineGridStyles() { const container = document.getElementById('timelineGrid'); if (!container) return; const weight = parseFloat(document.getElementById('patientWeight')?.value) || 70; const unit = document.getElementById('infusionUnitSelect')?.value || 'mg_kg_h'; const processed = processTimelineEvents(timelineInputs, weight, unit); const perfInputs = container.querySelectorAll('input[data-field="perfRate"]'); const bolusInputs = container.querySelectorAll('input[data-field="bolusMg"]'); perfInputs.forEach((input) => { const idx = parseInt(input.dataset.idx, 10); const event = processed[idx]; const isFocused = (document.activeElement === input); if (event.isExplicit) { input.className = 'w-full border text-center rounded py-1 outline-none transition-colors bg-cyan-950/90 border-cyan-400 text-cyan-200 font-bold focus:border-cyan-300 shadow-sm shadow-cyan-950'; if (!isFocused) { input.value = timelineInputs[idx].perfRate !== null ? timelineInputs[idx].perfRate : ''; } } else { input.className = 'w-full border text-center rounded py-1 outline-none transition-colors bg-slate-950/80 border-slate-800/50 text-slate-500 font-normal italic focus:border-amber-500'; if (!isFocused) { input.value = event.infusionRate > 0 ? event.infusionRate : ''; } } }); bolusInputs.forEach((input) => { const idx = parseInt(input.dataset.idx, 10); const isFocused = (document.activeElement === input); if (!isFocused) { input.value = timelineInputs[idx].bolusMg || ''; } }); } function setupEventListeners() { document.getElementById('timelineGrid')?.addEventListener('input', (e) => { const idx = e.target.dataset.idx; const field = e.target.dataset.field; if (idx !== undefined && field) { const rawVal = e.target.value.trim().replace(',', '.'); if (field === 'perfRate') { if (rawVal === '') { timelineInputs[idx].perfRate = null; } else { const parsed = parseFloat(rawVal); timelineInputs[idx].perfRate = isNaN(parsed) ? null : parsed; } } else { if (rawVal === '') { timelineInputs[idx].bolusMg = 0; } else { const parsed = parseFloat(rawVal); timelineInputs[idx].bolusMg = isNaN(parsed) ? 0 : parsed; } } updateSimulation(); } }); document.getElementById('btnEsk')?.addEventListener('click', () => setMode(MODES.ESKETAMINE)); document.getElementById('btnRac')?.addEventListener('click', () => setMode(MODES.RACEMIQUE)); ['patientName', 'patientWeight', 'infusionUnitSelect', 'chkDomino', 'chkClements', 'chkKamp'].forEach(id => { document.getElementById(id)?.addEventListener('change', updateSimulation); document.getElementById(id)?.addEventListener('input', updateSimulation); }); document.getElementById('btnRaz')?.addEventListener('click', () => { timelineInputs.forEach(item => { item.bolusMg = 0; item.perfRate = null; }); const container = document.getElementById('timelineGrid'); if (container) { container.querySelectorAll('input').forEach(inp => inp.value = ''); } updateSimulation(); }); const triggerExport = () => { if (!currentSimulationData) { updateSimulation(); } const name = document.getElementById('patientName')?.value || ''; const weight = parseFloat(document.getElementById('patientWeight')?.value) || 70; const unit = document.getElementById('infusionUnitSelect')?.value || 'mg_kg_h'; exportSimulationToCSV(currentSimulationData, { name: name, weight: weight, infusionUnit: unit }); }; document.getElementById('btnExportCSVNav')?.addEventListener('click', triggerExport); document.getElementById('btnExportCSV')?.addEventListener('click', triggerExport); } function setMode(mode) { currentMode = mode; const btnEsk = document.getElementById('btnEsk'); const btnRac = document.getElementById('btnRac'); if (mode === MODES.ESKETAMINE) { btnEsk.className = 'px-4 py-1.5 rounded-lg text-xs font-bold bg-amber-500 text-slate-950 shadow-md shadow-amber-500/30'; btnRac.className = 'px-4 py-1.5 rounded-lg text-xs font-bold text-slate-400 hover:text-slate-200'; } else { btnRac.className = 'px-4 py-1.5 rounded-lg text-xs font-bold bg-fuchsia-500 text-slate-950 shadow-md shadow-fuchsia-500/30'; btnEsk.className = 'px-4 py-1.5 rounded-lg text-xs font-bold text-slate-400 hover:text-slate-200'; } updateSimulation(); }