Drug Development Pipeline Dashboard

Drug Development Pipeline Dashboard

Total Drugs in Pipeline

0

In Clinical Trials

0

In Pre-Clinical

0

Average Success Probability

0.00%

Drugs by Current Stage

Pipeline Overview

Drug Name Current Stage Stage Start Date Est. End Date Success Prob. (%) Therapeutic Area Lead Scientist

No data to visualize.

`; return; } const stageCounts = {}; const orderedStages = ['Discovery', 'Pre-Clinical', 'Phase I', 'Phase II', 'Phase III', 'Regulatory Review', 'Approved', 'Discontinued']; orderedStages.forEach(stage => stageCounts[stage] = 0); // Initialize all stages drugPipelineData.forEach(drug => { if (stageCounts.hasOwnProperty(drug.currentStage)) { stageCounts[drug.currentStage]++; } }); const maxCount = Math.max(...Object.values(stageCounts)); orderedStages.forEach(stage => { const count = stageCounts[stage]; const percentage = maxCount > 0 ? (count / maxCount) * 100 : 0; const barItem = document.createElement('div'); barItem.classList.add('bar-item'); barItem.innerHTML = ` ${stage}:
${count}
`; stageBarChartEl.appendChild(barItem); }); } /** * Renders the drug pipeline data into the data configuration table with edit/delete actions. */ function renderManagePipelineTable() { if (!managePipelineTableBody) { console.error('Manage pipeline table body not found.'); return; } managePipelineTableBody.innerHTML = ''; // Clear existing rows if (drugPipelineData.length === 0) { managePipelineTableBody.innerHTML = `No drug pipeline entries to manage.`; return; } drugPipelineData.forEach(drug => { const row = managePipelineTableBody.insertRow(); row.setAttribute('data-drug-id', drug.id); // Set data attribute for easy lookup row.classList.add('hover:bg-gray-50'); row.innerHTML = ` `; }); } /** * Adds a new drug pipeline entry from the input fields. */ function addDrug() { if (!newDrugNameInput || !newCurrentStageSelect || !newStageStartDateInput || !newEstEndDateInput || !newSuccessProbabilityInput || !newTherapeuticAreaInput || !newLeadScientistInput) { console.error('New drug input elements not found.'); return; } const name = newDrugNameInput.value.trim(); const currentStage = newCurrentStageSelect.value; const stageStartDate = newStageStartDateInput.value; const estEndDate = newEstEndDateInput.value; const successProbability = parseFloat(newSuccessProbabilityInput.value); const therapeuticArea = newTherapeuticAreaInput.value.trim(); const leadScientist = newLeadScientistInput.value.trim(); if (!name || !currentStage || !stageStartDate || !estEndDate || isNaN(successProbability) || successProbability < 0 || successProbability > 100 || !therapeuticArea || !leadScientist) { alert('Please fill in all fields correctly: Drug Name, Current Stage, Stage Start Date, Estimated End Date, Success Probability (0-100), Therapeutic Area, and Lead Scientist/Team.'); return; } if (new Date(stageStartDate) > new Date(estEndDate)) { alert('Stage Start Date cannot be after Estimated End Date.'); return; } drugPipelineData.push({ id: generateUniqueId(), name: name, currentStage: currentStage, stageStartDate: stageStartDate, estEndDate: estEndDate, successProbability: successProbability, therapeuticArea: therapeuticArea, leadScientist: leadScientist }); // Clear input fields newDrugNameInput.value = ''; newCurrentStageSelect.value = 'Discovery'; // Reset to default newStageStartDateInput.value = ''; newEstEndDateInput.value = ''; newSuccessProbabilityInput.value = ''; newTherapeuticAreaInput.value = ''; newLeadScientistInput.value = ''; // Re-render tables and update metrics updateAllViews(); } /** * Updates an existing drug pipeline entry based on changes in the manage table. * This function is exposed globally for `onclick` attributes. * @param {string} drugId - The ID of the drug to update. * @param {HTMLElement} buttonElement - The button element that triggered the update. */ window.updateDrug = function(drugId, buttonElement) { const row = buttonElement.closest('tr'); if (!row) { console.error('Could not find row for drugId:', drugId); return; } const nameInput = row.querySelector('[data-field="name"]'); const currentStageSelect = row.querySelector('[data-field="currentStage"]'); const stageStartDateInput = row.querySelector('[data-field="stageStartDate"]'); const estEndDateInput = row.querySelector('[data-field="estEndDate"]'); const successProbabilityInput = row.querySelector('[data-field="successProbability"]'); const therapeuticAreaInput = row.querySelector('[data-field="therapeuticArea"]'); const leadScientistInput = row.querySelector('[data-field="leadScientist"]'); if (!nameInput || !currentStageSelect || !stageStartDateInput || !estEndDateInput || !successProbabilityInput || !therapeuticAreaInput || !leadScientistInput) { console.error('Input fields not found in row for drugId:', drugId); return; } const updatedName = nameInput.value.trim(); const updatedCurrentStage = currentStageSelect.value; const updatedStageStartDate = stageStartDateInput.value; const updatedEstEndDate = estEndDateInput.value; const updatedSuccessProbability = parseFloat(successProbabilityInput.value); const updatedTherapeuticArea = therapeuticAreaInput.value.trim(); const updatedLeadScientist = leadScientistInput.value.trim(); if (!updatedName || !updatedCurrentStage || !updatedStageStartDate || !updatedEstEndDate || isNaN(updatedSuccessProbability) || updatedSuccessProbability < 0 || updatedSuccessProbability > 100 || !updatedTherapeuticArea || !updatedLeadScientist) { alert('Please ensure all fields are valid before saving.'); return; } if (new Date(updatedStageStartDate) > new Date(updatedEstEndDate)) { alert('Stage Start Date cannot be after Estimated End Date.'); return; } const drugIndex = drugPipelineData.findIndex(d => d.id === drugId); if (drugIndex !== -1) { drugPipelineData[drugIndex] = { ...drugPipelineData[drugIndex], name: updatedName, currentStage: updatedCurrentStage, stageStartDate: updatedStageStartDate, estEndDate: updatedEstEndDate, successProbability: updatedSuccessProbability, therapeuticArea: updatedTherapeuticArea, leadScientist: updatedLeadScientist }; updateAllViews(); } else { console.warn('Drug pipeline entry not found for update:', drugId); } }; /** * Deletes a drug pipeline entry from the list. * This function is exposed globally for `onclick` attributes. * @param {string} drugId - The ID of the drug to delete. */ window.deleteDrug = function(drugId) { const initialLength = drugPipelineData.length; drugPipelineData = drugPipelineData.filter(drug => drug.id !== drugId); if (drugPipelineData.length < initialLength) { updateAllViews(); } else { console.warn('Drug pipeline entry not found for deletion:', drugId); } }; /** * Updates all relevant views (metrics, dashboard table, manage table, bar chart). */ function updateAllViews() { updateDashboardMetrics(); renderPipelineTable(); renderStageBarChart(); renderManagePipelineTable(); } /** * Generates and downloads a PDF of the dashboard content. */ function downloadDashboardPdf() { // Ensure jsPDF is available if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { console.error("jsPDF library not loaded. Cannot generate PDF."); alert("PDF generation library not loaded. Please try again later."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('landscape'); // Use landscape for wider tables // Set font and color for consistency doc.setFont('helvetica'); doc.setTextColor('#1f2937'); // Dark gray // Title doc.setFontSize(24); doc.text('Drug Development Pipeline Dashboard', doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' }); // Add a small separator doc.setDrawColor('#d1d5db'); doc.line(20, 25, doc.internal.pageSize.getWidth() - 20, 25); // Dashboard Metrics doc.setFontSize(14); let yPos = 40; const metrics = [ { label: 'Total Drugs in Pipeline:', value: totalDrugsInPipelineEl ? totalDrugsInPipelineEl.textContent : 'N/A' }, { label: 'In Clinical Trials:', value: drugsInClinicalTrialsEl ? drugsInClinicalTrialsEl.textContent : 'N/A' }, { label: 'In Pre-Clinical:', value: drugsInPreClinicalEl ? drugsInPreClinicalEl.textContent : 'N/A' }, { label: 'Average Success Probability:', value: avgSuccessProbabilityEl ? avgSuccessProbabilityEl.textContent : 'N/A' } ]; metrics.forEach(metric => { doc.text(`${metric.label} ${metric.value}`, 20, yPos); yPos += 10; }); yPos += 15; // Extra space before table // Drugs by Current Stage (as a table in PDF) doc.setFontSize(16); doc.text('Drugs by Current Stage', 20, yPos); yPos += 10; const stageCountsForPdf = {}; const orderedStagesForPdf = ['Discovery', 'Pre-Clinical', 'Phase I', 'Phase II', 'Phase III', 'Regulatory Review', 'Approved', 'Discontinued']; orderedStagesForPdf.forEach(stage => stageCountsForPdf[stage] = 0); drugPipelineData.forEach(drug => { if (stageCountsForPdf.hasOwnProperty(drug.currentStage)) { stageCountsForPdf[drug.currentStage]++; } }); const stageTableColumn = ["Stage", "Count"]; const stageTableRows = []; orderedStagesForPdf.forEach(stage => { stageTableRows.push([stage, stageCountsForPdf[stage]]); }); doc.autoTable({ head: [stageTableColumn], body: stageTableRows, startY: yPos, theme: 'grid', styles: { fontSize: 10, cellPadding: 3, textColor: '#1f2937', lineColor: '#e5e7eb', lineWidth: 0.1 }, headStyles: { fillColor: '#f3f4f6', textColor: '#4b5563', fontStyle: 'bold' }, alternateRowStyles: { fillColor: '#ffffff' }, margin: { left: 20, right: 20 }, didDrawPage: function(data) { let str = "Page " + doc.internal.getNumberOfPages(); doc.setFontSize(10); doc.setTextColor('#6b7280'); doc.text(str, doc.internal.pageSize.getWidth() - 20, doc.internal.pageSize.getHeight() - 10, { align: 'right' }); } }); yPos = doc.autoTable.previous.finalY + 15; // Continue from where the last table ended // Pipeline Overview Table doc.setFontSize(16); doc.text('Pipeline Overview', 20, yPos); yPos += 10; const overviewTableColumn = ["Drug Name", "Current Stage", "Stage Start Date", "Est. End Date", "Success Prob. (%)", "Therapeutic Area", "Lead Scientist"]; const overviewTableRows = []; drugPipelineData.forEach(drug => { overviewTableRows.push([ drug.name, drug.currentStage, drug.stageStartDate, drug.estEndDate, formatPercentage(drug.successProbability), drug.therapeuticArea, drug.leadScientist ]); }); doc.autoTable({ head: [overviewTableColumn], body: overviewTableRows, startY: yPos, theme: 'grid', styles: { fontSize: 6, // Further reduced font size for more columns in landscape cellPadding: 1, textColor: '#1f2937', lineColor: '#e5e7eb', lineWidth: 0.1 }, headStyles: { fillColor: '#f3f4f6', textColor: '#4b5563', fontStyle: 'bold' }, alternateRowStyles: { fillColor: '#ffffff' }, margin: { left: 10, right: 10 }, // Adjusted margins to fit more content didDrawPage: function(data) { let str = "Page " + doc.internal.getNumberOfPages(); doc.setFontSize(10); doc.setTextColor('#6b7280'); doc.text(str, doc.internal.pageSize.getWidth() - 20, doc.internal.pageSize.getHeight() - 10, { align: 'right' }); } }); // Save the PDF doc.save('drug_development_pipeline_dashboard.pdf'); } // --- Event Listeners --- // Tab button clicks if (dashboardTabButton) { dashboardTabButton.addEventListener('click', function() { switchTab(0); }); } if (dataConfigTabButton) { dataConfigTabButton.addEventListener('click', function() { switchTab(1); }); } // Navigation button clicks if (prevTabButton) { prevTabButton.addEventListener('click', function() { if (currentTabIndex > 0) { switchTab(currentTabIndex - 1); } }); } if (nextTabButton) { nextTabButton.addEventListener('click', function() { if (currentTabIndex < tabs.length - 1) { switchTab(currentTabIndex + 1); } }); } // Add Drug button click if (addDrugButton) { addDrugButton.addEventListener('click', addDrug); } // PDF Download button click if (downloadPdfButton) { downloadPdfButton.addEventListener('click', downloadDashboardPdf); } // --- Initial Render --- // Set initial tab to Dashboard switchTab(0); // Populate data and update views updateAllViews(); });
Scroll to Top