742 lines
28 KiB
JavaScript
742 lines
28 KiB
JavaScript
|
|
// API Base URL
|
|
const API_BASE_URL = '/api';
|
|
|
|
// Global variables to store related microbial data
|
|
let relatedMicrobes = [];
|
|
let allRelatedMicrobes = []; // Store all related microbial data
|
|
let currentRelatedPage = 1;
|
|
let relatedRowsPerPage = 20;
|
|
let totalRelatedRows = 0;
|
|
let totalRelatedPages = 0;
|
|
let currentSort = { column: null, direction: 'asc' }; // Current sort state
|
|
|
|
// --- Search Functions ---
|
|
|
|
// Search function (with auto scroll)
|
|
async function performSearch() {
|
|
const microbialInput = document.getElementById('microbial-name-input').value;
|
|
|
|
if (microbialInput.trim()) {
|
|
try {
|
|
showLoadingState();
|
|
console.log('Searching for:', microbialInput);
|
|
|
|
// Call Search API
|
|
const response = await fetch(`${API_BASE_URL}/microbial-detail?name=${encodeURIComponent(microbialInput)}`);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const result = await response.json();
|
|
const data = result.data;
|
|
console.log('Search results:', data);
|
|
|
|
hideLoadingState();
|
|
|
|
if (data) {
|
|
// Show results
|
|
displaySearchResult(data);
|
|
// Scroll to results
|
|
document.getElementById('results-section').scrollIntoView({ behavior: 'smooth' });
|
|
} else {
|
|
showNoResults();
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Search error:', error);
|
|
hideLoadingState();
|
|
showErrorState(error.message);
|
|
}
|
|
} else {
|
|
alert('Please enter a microbial name.');
|
|
}
|
|
}
|
|
|
|
// Search function (without scroll - for initial load)
|
|
async function performSearchWithoutScroll() {
|
|
const microbialInput = document.getElementById('microbial-name-input').value;
|
|
if (microbialInput.trim()) {
|
|
try {
|
|
showLoadingState();
|
|
console.log('Searching for:', microbialInput);
|
|
|
|
const response = await fetch(`${API_BASE_URL}/microbial-detail?name=${encodeURIComponent(microbialInput)}`);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const result = await response.json();
|
|
const data = result.data;
|
|
console.log('Search results:', data);
|
|
|
|
hideLoadingState();
|
|
|
|
if (data) {
|
|
displaySearchResult(data);
|
|
} else {
|
|
showNoResults();
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Search error:', error);
|
|
hideLoadingState();
|
|
showErrorState(error.message);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Display search results
|
|
function displaySearchResult(microbial) {
|
|
console.log('Displaying result for:', microbial);
|
|
|
|
if (!microbial) {
|
|
console.error('No microbial data provided');
|
|
showNoResults();
|
|
return;
|
|
}
|
|
|
|
// Update basic info
|
|
updateMicrobialInfo(microbial);
|
|
|
|
// Update culture medium info
|
|
updateCultureMediumInfo(microbial);
|
|
|
|
// Update related species statistics and table
|
|
if (microbial.related_microbes && microbial.related_microbes.length > 0) {
|
|
updateRelatedMicrobes(microbial.related_microbes);
|
|
updateStatistics(microbial.related_microbes);
|
|
} else {
|
|
// Clear related data if none
|
|
updateRelatedMicrobes([]);
|
|
updateStatistics([]);
|
|
}
|
|
|
|
console.log('Search result displayed successfully');
|
|
}
|
|
|
|
// Format probability
|
|
function formatProbability(probability) {
|
|
if (!probability || probability === 'N/A') {
|
|
return 'N/A';
|
|
}
|
|
const probValue = parseFloat(probability);
|
|
if (isNaN(probValue)) {
|
|
return 'N/A';
|
|
}
|
|
return probValue.toFixed(2);
|
|
}
|
|
|
|
// Get confidence color
|
|
function getConfidenceColor(probability) {
|
|
if (!probability || probability === 'N/A') {
|
|
return '#f0f0f0'; // Default gray
|
|
}
|
|
|
|
let probValue = parseFloat(probability);
|
|
if (isNaN(probValue)) {
|
|
return '#f0f0f0';
|
|
}
|
|
|
|
if (probValue <= 1) {
|
|
probValue = probValue * 100;
|
|
}
|
|
|
|
if (probValue >= 0 && probValue < 50) {
|
|
return '#D0E7ED';
|
|
} else if (probValue >= 50 && probValue < 70) {
|
|
return '#9392BE';
|
|
} else if (probValue >= 70 && probValue <= 100) {
|
|
return '#D5E4A8';
|
|
} else {
|
|
return '#f0f0f0';
|
|
}
|
|
}
|
|
|
|
// --- DOM Update Functions ---
|
|
|
|
function updateMicrobialInfo(microbial) {
|
|
console.log('Updating microbial info with:', microbial);
|
|
|
|
// Specie name
|
|
const specie_name = document.getElementById('specie-name');
|
|
if (specie_name) {
|
|
const specieNameElement = specie_name.querySelector('p');
|
|
if (specieNameElement) {
|
|
specieNameElement.textContent = microbial.microbial_name || 'N/A';
|
|
}
|
|
}
|
|
|
|
// Taxonomy
|
|
const Taxonomy = document.getElementById('Taxonomy');
|
|
if (Taxonomy) {
|
|
const taxonomyElement = Taxonomy.querySelector('p');
|
|
if (taxonomyElement) {
|
|
taxonomyElement.textContent = microbial.taxonomy || 'N/A';
|
|
}
|
|
}
|
|
|
|
// pH
|
|
const pH = document.getElementById('pH');
|
|
if (pH) {
|
|
const pHValue = pH.querySelector('p');
|
|
if (pHValue) {
|
|
pHValue.textContent = microbial.ppH ? parseFloat(microbial.ppH).toFixed(0) : 'N/A';
|
|
}
|
|
}
|
|
|
|
// Temperature
|
|
const Temperature = document.getElementById('Temperature');
|
|
if (Temperature) {
|
|
const tempValue = Temperature.querySelector('p');
|
|
const tempConfidenceColor = getConfidenceColor(microbial.ptemperature_probability);
|
|
const formattedTempProbability = formatProbability(microbial.ptemperature_probability);
|
|
|
|
if (tempValue) {
|
|
tempValue.textContent = microbial.ptemperature || 'N/A';
|
|
|
|
const existingConfidence = tempValue.parentElement.querySelector('.confidence-span');
|
|
if (existingConfidence) {
|
|
existingConfidence.remove();
|
|
}
|
|
|
|
if (microbial.ptemperature_probability) {
|
|
const confidenceSpan = document.createElement('span');
|
|
confidenceSpan.style.cssText = 'margin-left: 10px; padding: 5px; font-weight: 400; border-radius: 4px; background: ' + tempConfidenceColor + ';';
|
|
confidenceSpan.textContent = 'Confidence: ' + formattedTempProbability;
|
|
confidenceSpan.className = 'confidence-span';
|
|
tempValue.parentElement.insertBefore(confidenceSpan, tempValue.nextSibling);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Oxygen
|
|
const Oxygen = document.getElementById('Oxygen-resistance');
|
|
if (Oxygen) {
|
|
const o2Value = Oxygen.querySelector('p');
|
|
const o2ConfidenceColor = getConfidenceColor(microbial.po2_probability);
|
|
const formattedO2Probability = formatProbability(microbial.po2_probability);
|
|
|
|
if (o2Value) {
|
|
o2Value.textContent = microbial.po2 || 'N/A';
|
|
|
|
const existingConfidence = o2Value.parentElement.querySelector('.confidence-span');
|
|
if (existingConfidence) {
|
|
existingConfidence.remove();
|
|
}
|
|
|
|
if (microbial.po2_probability) {
|
|
const confidenceSpan = document.createElement('span');
|
|
confidenceSpan.style.cssText = 'margin-left: 10px; padding: 5px; font-weight: 400; border-radius: 4px; background: ' + o2ConfidenceColor + ';';
|
|
confidenceSpan.textContent = 'Confidence: ' + formattedO2Probability;
|
|
confidenceSpan.className = 'confidence-span';
|
|
o2Value.parentElement.insertBefore(confidenceSpan, o2Value.nextSibling);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateCultureMediumInfo(microbial) {
|
|
const cultureMediumCard = document.getElementById('culture-medium');
|
|
if (!cultureMediumCard || !microbial.nutrition) return;
|
|
|
|
const nutritionArrays = microbial.nutrition.split(';').map(arrStr => {
|
|
const trimmed = arrStr.trim().replace(/^\[|\]$/g, '');
|
|
return trimmed
|
|
? trimmed
|
|
.split(',')
|
|
.map(item => item.trim().replace(/'/g, ''))
|
|
: [];
|
|
});
|
|
|
|
if (nutritionArrays && nutritionArrays.length > 0) {
|
|
let mediaHtml = '';
|
|
mediaHtml += `<span>Media</span>`;
|
|
|
|
nutritionArrays.forEach((media, index) => {
|
|
if (media && media.length > 0) {
|
|
mediaHtml += `
|
|
<div class="medium-item">
|
|
<h4 style="display: inline; font-weight:400;">${index + 1}:</h4>
|
|
<span style="font-weight: 400; color: #666;">${Array.isArray(media) ? media.join(', ') : media}</span>
|
|
</div>
|
|
`;
|
|
}
|
|
});
|
|
|
|
cultureMediumCard.innerHTML = mediaHtml;
|
|
}
|
|
}
|
|
|
|
// --- Related Microbes Table Logic ---
|
|
|
|
function updateRelatedMicrobes(relatedMicrobes) {
|
|
allRelatedMicrobes = relatedMicrobes || [];
|
|
totalRelatedRows = allRelatedMicrobes.length;
|
|
totalRelatedPages = Math.ceil(totalRelatedRows / relatedRowsPerPage);
|
|
|
|
if(totalRelatedPages === 0) totalRelatedPages = 1;
|
|
|
|
updateRelatedTable();
|
|
updateRelatedPaginationInfo();
|
|
}
|
|
|
|
function updateRelatedTable() {
|
|
const tableBody = document.querySelector('.table-container tbody');
|
|
|
|
if (!tableBody) {
|
|
console.error('Table body not found');
|
|
return;
|
|
}
|
|
|
|
if (!allRelatedMicrobes || allRelatedMicrobes.length === 0) {
|
|
tableBody.innerHTML = '<tr><td colspan="8" style="text-align:center; padding: 20px;">No relevant microbial data available</td></tr>';
|
|
return;
|
|
}
|
|
|
|
const startIndex = (currentRelatedPage - 1) * relatedRowsPerPage;
|
|
const endIndex = Math.min(startIndex + relatedRowsPerPage, totalRelatedRows);
|
|
const currentPageData = allRelatedMicrobes.slice(startIndex, endIndex);
|
|
|
|
let tableHtml = '';
|
|
|
|
currentPageData.forEach((microbe, index) => {
|
|
const micppH = microbe.ppH ? parseFloat(microbe.ppH).toFixed(0) : 'Unknown';
|
|
const rowNumber = startIndex + index + 1;
|
|
|
|
tableHtml += `
|
|
<tr onclick="window.navigateToDetail('${(microbe.microbial_name || '').replace(/'/g, "\\'")}')" style="cursor: pointer;">
|
|
<td>${rowNumber}</td>
|
|
<td><a href="#" class="microbial-link" onclick="event.stopPropagation(); window.navigateToDetail('${(microbe.microbial_name || '').replace(/'/g, "\\'")}')">${microbe.microbial_name || 'N/A'}</a></td>
|
|
<td><a href="#" class="nutrition-link" onclick="event.stopPropagation()">${microbe.nutrition || 'N/A'}</a></td>
|
|
<td>${microbe.domain || 'N/A'}</td>
|
|
<td>${micppH}</td>
|
|
<td>${microbe.ptemperature || 'Unknown'}</td>
|
|
<td>${microbe.po2 || 'Unknown'}</td>
|
|
<td>${microbe.cultured_type || 'Unknown'}</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
|
|
tableBody.innerHTML = tableHtml;
|
|
}
|
|
|
|
window.navigateToDetail = function(microbialName) {
|
|
// If you want to redirect to a detail page, uncomment below
|
|
// window.location.href = `Search_result.html?microbial=${encodeURIComponent(microbialName)}`;
|
|
|
|
// If you want to update the current page (SPA style) since we are on Search_merged
|
|
document.getElementById('microbial-name-input').value = microbialName;
|
|
performSearch();
|
|
// Scroll to top to see new input
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
}
|
|
|
|
function updateRelatedPaginationInfo() {
|
|
const showingText = document.getElementById('showing-text');
|
|
const prevBtn = document.getElementById('prev-btn');
|
|
const nextBtn = document.getElementById('next-btn');
|
|
|
|
if (showingText) {
|
|
const startIndex = totalRelatedRows > 0 ? (currentRelatedPage - 1) * relatedRowsPerPage + 1 : 0;
|
|
const endIndex = Math.min(currentRelatedPage * relatedRowsPerPage, totalRelatedRows);
|
|
showingText.textContent = `Showing ${startIndex} to ${endIndex} of ${totalRelatedRows} entries.`;
|
|
}
|
|
|
|
if (prevBtn) {
|
|
prevBtn.disabled = currentRelatedPage <= 1;
|
|
}
|
|
|
|
if (nextBtn) {
|
|
nextBtn.disabled = currentRelatedPage >= totalRelatedPages;
|
|
}
|
|
|
|
updateRelatedPageNumbers();
|
|
}
|
|
|
|
function updateRelatedPageNumbers() {
|
|
const pageNumbers = document.getElementById('page-numbers');
|
|
if (!pageNumbers) return;
|
|
|
|
pageNumbers.innerHTML = '';
|
|
|
|
if (totalRelatedPages <= 7) {
|
|
for (let i = 1; i <= totalRelatedPages; i++) {
|
|
const pageBtn = document.createElement('button');
|
|
pageBtn.textContent = i;
|
|
pageBtn.className = `page-num ${i === currentRelatedPage ? 'active' : ''}`;
|
|
pageBtn.addEventListener('click', () => {
|
|
currentRelatedPage = i;
|
|
updateRelatedTable();
|
|
updateRelatedPaginationInfo();
|
|
});
|
|
pageNumbers.appendChild(pageBtn);
|
|
}
|
|
} else {
|
|
const startPage = Math.max(1, currentRelatedPage - 2);
|
|
const endPage = Math.min(totalRelatedPages, startPage + 4);
|
|
|
|
if (startPage > 1) {
|
|
const firstButton = document.createElement('button');
|
|
firstButton.className = `page-num ${1 === currentRelatedPage ? 'active' : ''}`;
|
|
firstButton.textContent = '1';
|
|
firstButton.addEventListener('click', () => {
|
|
currentRelatedPage = 1;
|
|
updateRelatedTable();
|
|
updateRelatedPaginationInfo();
|
|
});
|
|
pageNumbers.appendChild(firstButton);
|
|
|
|
if (startPage > 2) {
|
|
const ellipsis = document.createElement('span');
|
|
ellipsis.className = 'page-ellipsis';
|
|
ellipsis.textContent = '...';
|
|
ellipsis.style.cssText = `padding: 8px 4px; color: #666; font-weight: bold; display: flex; align-items: center; justify-content: center; min-width: 20px;`;
|
|
pageNumbers.appendChild(ellipsis);
|
|
}
|
|
}
|
|
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
if (i === 1 && startPage > 1) continue;
|
|
|
|
const pageBtn = document.createElement('button');
|
|
pageBtn.textContent = i;
|
|
pageBtn.className = `page-num ${i === currentRelatedPage ? 'active' : ''}`;
|
|
pageBtn.addEventListener('click', () => {
|
|
currentRelatedPage = i;
|
|
updateRelatedTable();
|
|
updateRelatedPaginationInfo();
|
|
});
|
|
pageNumbers.appendChild(pageBtn);
|
|
}
|
|
|
|
if (endPage < totalRelatedPages) {
|
|
if (endPage < totalRelatedPages - 1) {
|
|
const ellipsis = document.createElement('span');
|
|
ellipsis.className = 'page-ellipsis';
|
|
ellipsis.textContent = '...';
|
|
ellipsis.style.cssText = `padding: 8px 4px; color: #666; font-weight: bold; display: flex; align-items: center; justify-content: center; min-width: 20px;`;
|
|
pageNumbers.appendChild(ellipsis);
|
|
}
|
|
|
|
const lastButton = document.createElement('button');
|
|
lastButton.className = `page-num ${totalRelatedPages === currentRelatedPage ? 'active' : ''}`;
|
|
lastButton.textContent = totalRelatedPages;
|
|
lastButton.addEventListener('click', () => {
|
|
currentRelatedPage = totalRelatedPages;
|
|
updateRelatedTable();
|
|
updateRelatedPaginationInfo();
|
|
});
|
|
pageNumbers.appendChild(lastButton);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Statistics and Charts ---
|
|
|
|
function updateStatistics(relatedMicrobes) {
|
|
if (!relatedMicrobes || relatedMicrobes.length === 0) {
|
|
// Clear charts if no data
|
|
updatePHChart([]);
|
|
updateO2Chart([]);
|
|
updateTemperatureChart([]);
|
|
updateNutritionChart([]);
|
|
return;
|
|
}
|
|
|
|
const pHData = calculatePHDistribution(relatedMicrobes);
|
|
updatePHChart(pHData);
|
|
|
|
const o2Data = calculateO2Distribution(relatedMicrobes);
|
|
updateO2Chart(o2Data);
|
|
|
|
const tempData = calculateTemperatureDistribution(relatedMicrobes);
|
|
updateTemperatureChart(tempData);
|
|
|
|
const nutritionData = calculateNutritionDistribution(relatedMicrobes);
|
|
updateNutritionChart(nutritionData);
|
|
}
|
|
|
|
function calculatePHDistribution(relatedMicrobes) {
|
|
const pHCounts = {};
|
|
relatedMicrobes.forEach(microbe => {
|
|
if (microbe.pH || microbe.ppH) {
|
|
const pH = parseFloat(microbe.pH || microbe.ppH);
|
|
let category;
|
|
if (pH < 6) category = 'acidic (pH < 6)';
|
|
else if (pH > 8) category = 'alkaline (pH > 8)';
|
|
else category = 'neutral (pH 6-8)';
|
|
pHCounts[category] = (pHCounts[category] || 0) + 1;
|
|
}
|
|
});
|
|
return Object.entries(pHCounts).map(([name, value]) => ({ name: name.replace(' ', '\n'), value }));
|
|
}
|
|
|
|
function calculateO2Distribution(relatedMicrobes) {
|
|
const o2Counts = {};
|
|
relatedMicrobes.forEach(microbe => {
|
|
const o2 = microbe.o2 || microbe.po2;
|
|
if (o2) {
|
|
const o2Lower = o2.toLowerCase();
|
|
o2Counts[o2Lower] = (o2Counts[o2Lower] || 0) + 1;
|
|
}
|
|
});
|
|
return Object.entries(o2Counts).map(([name, value]) => ({ name, value }));
|
|
}
|
|
|
|
function calculateTemperatureDistribution(relatedMicrobes) {
|
|
const tempCounts = {};
|
|
relatedMicrobes.forEach(microbe => {
|
|
const tVal = microbe.temperature || microbe.ptemperature;
|
|
if (tVal) {
|
|
const temp = parseFloat(tVal);
|
|
let category;
|
|
if (temp < 20) category = 'low (< 20°C)';
|
|
else if (temp > 40) category = 'high (> 40°C)';
|
|
else category = 'medium (20-40°C)';
|
|
tempCounts[category] = (tempCounts[category] || 0) + 1;
|
|
}
|
|
});
|
|
return Object.entries(tempCounts).map(([name, value]) => ({ name: name.replace(' ', '\n'), value }));
|
|
}
|
|
|
|
function calculateNutritionDistribution(relatedMicrobes) {
|
|
const nutritionCounts = {};
|
|
relatedMicrobes.forEach(microbe => {
|
|
if (microbe.nutrition) {
|
|
let nutrients = [];
|
|
if (typeof microbe.nutrition === 'string') {
|
|
const nutritionStr = microbe.nutrition.replace(/[\[\]']/g, '');
|
|
nutrients = nutritionStr.split(',').map(n => n.trim()).filter(n => n !== '');
|
|
}
|
|
nutrients.forEach(nutrient => {
|
|
if (nutrient.trim() !== '') {
|
|
nutritionCounts[nutrient] = (nutritionCounts[nutrient] || 0) + 1;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
return Object.entries(nutritionCounts)
|
|
.sort((a, b) => b[1] - a[1])
|
|
.map(([name, value]) => ({
|
|
name,
|
|
value,
|
|
frequency: (relatedMicrobes.length > 0) ? (value / relatedMicrobes.length).toFixed(2) : 0
|
|
}));
|
|
}
|
|
|
|
// Chart Updating Functions (using ECharts)
|
|
|
|
function updatePHChart(data) {
|
|
const chartElement = document.querySelector('.chart-pH');
|
|
if (!chartElement) return;
|
|
|
|
let chart = echarts.getInstanceByDom(chartElement);
|
|
if (!chart) chart = echarts.init(chartElement);
|
|
|
|
const option = {
|
|
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
grid: { left: '1%', right: '4%', bottom: '3%', top: '6%', containLabel: true },
|
|
xAxis: { type: 'category', data: data.map(item => item.name), axisLabel: { fontSize: 10 } },
|
|
yAxis: { type: 'value' },
|
|
series: [{ type: 'bar', data: data.map(item => item.value), itemStyle: { color: '#9fbaae' } }]
|
|
};
|
|
chart.setOption(option);
|
|
window.addEventListener('resize', () => chart.resize());
|
|
}
|
|
|
|
function updateO2Chart(data) {
|
|
const chartElement = document.querySelector('.chart-O2');
|
|
if (!chartElement) return;
|
|
|
|
let chart = echarts.getInstanceByDom(chartElement);
|
|
if (!chart) chart = echarts.init(chartElement);
|
|
|
|
const option = {
|
|
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
grid: { left: '1%', right: '4%', bottom: '3%', top: '6%', containLabel: true },
|
|
xAxis: { type: 'value' },
|
|
yAxis: {
|
|
type: 'category',
|
|
data: data.map(item => item.name),
|
|
axisLabel: {
|
|
fontSize: 10,
|
|
formatter: function(value) { return value.length > 10 ? value.substring(0, 10) + '...' : value; }
|
|
}
|
|
},
|
|
series: [{ type: 'bar', data: data.map(item => item.value), itemStyle: { color: '#9fb3ba' } }]
|
|
};
|
|
chart.setOption(option);
|
|
window.addEventListener('resize', () => chart.resize());
|
|
}
|
|
|
|
function updateTemperatureChart(data) {
|
|
const chartElement = document.querySelector('.chart-temp');
|
|
if (!chartElement) return;
|
|
|
|
let chart = echarts.getInstanceByDom(chartElement);
|
|
if (!chart) chart = echarts.init(chartElement);
|
|
|
|
const option = {
|
|
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
grid: { left: '1%', right: '4%', bottom: '3%', top: '6%', containLabel: true },
|
|
xAxis: { type: 'category', data: data.map(item => item.name), axisLabel: { fontSize: 10 } },
|
|
yAxis: { type: 'value' },
|
|
series: [{ type: 'bar', data: data.map(item => item.value), itemStyle: { color: '#9ea0b9' } }]
|
|
};
|
|
chart.setOption(option);
|
|
window.addEventListener('resize', () => chart.resize());
|
|
}
|
|
|
|
function updateNutritionChart(data) {
|
|
const chartElement = document.querySelector('.chart-nutrition');
|
|
if (!chartElement) return;
|
|
|
|
const sortedData = data.sort((a, b) => parseFloat(b.frequency) - parseFloat(a.frequency)).slice(0, 15); // Top 15
|
|
|
|
chartElement.innerHTML = '';
|
|
const scrollContainer = document.createElement('div');
|
|
scrollContainer.style.cssText = `width: 100%; height: 100%; overflow-y: auto; overflow-x: hidden;`;
|
|
const chartContainer = document.createElement('div');
|
|
const itemHeight = 30;
|
|
const calculatedHeight = Math.max(200, sortedData.length * itemHeight);
|
|
chartContainer.style.cssText = `width: 100%; height: ${calculatedHeight}px;`;
|
|
|
|
chartElement.appendChild(scrollContainer);
|
|
scrollContainer.appendChild(chartContainer);
|
|
|
|
const chart = echarts.init(chartContainer);
|
|
|
|
const option = {
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
axisPointer: { type: 'shadow' },
|
|
formatter: function(params) {
|
|
const dataIndex = params[0].dataIndex;
|
|
const item = sortedData[dataIndex];
|
|
return item.name + '<br/>Count: ' + item.value + '<br/>Freq: ' + item.frequency;
|
|
}
|
|
},
|
|
grid: { left: '0%', right: '12%', bottom: '3%', top: '1%', containLabel: true },
|
|
xAxis: { type: 'value', name: 'Freq' },
|
|
yAxis: {
|
|
type: 'category',
|
|
data: sortedData.map(item => item.name),
|
|
axisLabel: {
|
|
formatter: function(value) { return value.length > 10 ? value.substring(0, 10) + '...' : value; }
|
|
}
|
|
},
|
|
series: [{
|
|
type: 'bar',
|
|
data: sortedData.map(item => parseFloat(item.frequency)),
|
|
itemStyle: { color: '#af9eb9' },
|
|
label: { show: true, position: 'right', formatter: '{@score}' }
|
|
}]
|
|
};
|
|
chart.setOption(option);
|
|
window.addEventListener('resize', () => chart.resize());
|
|
}
|
|
|
|
// --- Utils ---
|
|
|
|
function showLoadingState() { console.log('Loading...'); }
|
|
function hideLoadingState() { console.log('Loading complete'); }
|
|
function showErrorState(message) { alert('Error: ' + message); }
|
|
function showNoResults() { alert('No results found'); }
|
|
|
|
async function downloadCSV() {
|
|
if (!allRelatedMicrobes || allRelatedMicrobes.length === 0) {
|
|
alert('No data available for download');
|
|
return;
|
|
}
|
|
|
|
const headers = ['Number', 'Microbial', 'Nutritional combination', 'Taxo', 'pH', 'Temperature', 'O2', 'Type'];
|
|
const csvContent = [
|
|
headers.join(','),
|
|
...allRelatedMicrobes.map((row, index) => {
|
|
return [
|
|
index + 1,
|
|
`"${row.microbial_name || ''}"`,
|
|
`"${(row.nutrition || '').replace(/"/g, '""')}"`,
|
|
`"${row.domain || ''}"`,
|
|
row.ppH || row.pH || '',
|
|
row.ptemperature || '',
|
|
row.po2 || '',
|
|
row.cultured_type || ''
|
|
].join(',');
|
|
})
|
|
].join('\n');
|
|
|
|
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
const link = document.createElement('a');
|
|
link.href = URL.createObjectURL(blob);
|
|
link.setAttribute('download', 'related_microbes_data.csv');
|
|
link.style.visibility = 'hidden';
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
}
|
|
|
|
// --- Initialization ---
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Tabs
|
|
const tabButtons = document.querySelectorAll('.tab-btn');
|
|
const tabPanels = document.querySelectorAll('.tab-panel');
|
|
|
|
tabButtons.forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const targetTab = this.getAttribute('data-tab');
|
|
tabButtons.forEach(btn => btn.classList.remove('active'));
|
|
tabPanels.forEach(panel => panel.classList.remove('active'));
|
|
this.classList.add('active');
|
|
document.getElementById(targetTab).classList.add('active');
|
|
});
|
|
});
|
|
|
|
// Pagination Controls
|
|
document.getElementById('per_page').addEventListener('change', function() {
|
|
relatedRowsPerPage = parseInt(this.value);
|
|
currentRelatedPage = 1;
|
|
updateRelatedTable();
|
|
updateRelatedPaginationInfo();
|
|
});
|
|
|
|
document.getElementById('prev-btn').addEventListener('click', function() {
|
|
if (currentRelatedPage > 1) {
|
|
currentRelatedPage--;
|
|
updateRelatedTable();
|
|
updateRelatedPaginationInfo();
|
|
}
|
|
});
|
|
|
|
document.getElementById('next-btn').addEventListener('click', function() {
|
|
if (currentRelatedPage < totalRelatedPages) {
|
|
currentRelatedPage++;
|
|
updateRelatedTable();
|
|
updateRelatedPaginationInfo();
|
|
}
|
|
});
|
|
|
|
// Buttons
|
|
document.getElementById('reset-btn').addEventListener('click', function() {
|
|
document.getElementById('microbial-name-input').value = '';
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
});
|
|
|
|
document.getElementById('search-btn').addEventListener('click', performSearch);
|
|
|
|
document.getElementById('download-btn').addEventListener('click', downloadCSV);
|
|
|
|
// Auto-search if sample data present (Simulation)
|
|
const microbialInput = document.getElementById('microbial-name-input');
|
|
setTimeout(() => {
|
|
if (microbialInput && !microbialInput.value) {
|
|
microbialInput.value = 'Escherichia coli B3';
|
|
performSearchWithoutScroll();
|
|
}
|
|
}, 500);
|
|
});
|