| document.addEventListener('DOMContentLoaded', () => { |
| const navLinks = document.querySelectorAll('header nav a'); |
| navLinks.forEach(link => { |
| link.addEventListener('click', function(event) { |
| event.stopImmediatePropagation(); |
| window.location.href = this.href; |
| }, true); |
| }); |
| |
| console.log('Landing page loaded.'); |
| |
| |
| const animatedElements = document.querySelectorAll('.animate-on-scroll'); |
| const observer = new IntersectionObserver(entries => { |
| entries.forEach(entry => { |
| if (entry.isIntersecting) { |
| entry.target.classList.add('animate-fadeIn'); |
| observer.unobserve(entry.target); |
| } |
| }); |
| }, { threshold: 0.2 }); |
| |
| animatedElements.forEach(el => observer.observe(el)); |
|
|
| |
| const initialPlotDiv = document.getElementById('initialPlot'); |
| if (initialPlotDiv) { |
| const plotData = JSON.parse(initialPlotDiv.dataset.plot); |
| Plotly.newPlot('plotDiv', plotData.data, plotData.layout); |
| } |
|
|
| |
| if (!document.getElementById('toast-container')) { |
| const toastContainer = document.createElement('div'); |
| toastContainer.id = 'toast-container'; |
| toastContainer.className = 'fixed top-4 right-4 z-50'; |
| document.body.appendChild(toastContainer); |
| } |
| }); |
|
|
| function showToast(message, type = 'success') { |
| const toast = document.createElement('div'); |
| toast.className = `p-4 mb-4 rounded shadow-lg ${type === 'success' ? 'bg-green-500' : 'bg-red-500'} text-white`; |
| toast.textContent = message; |
| |
| const container = document.getElementById('toast-container'); |
| container.appendChild(toast); |
| |
| setTimeout(() => { |
| toast.remove(); |
| }, 3000); |
| } |
|
|
| window.createPlot = function() { |
| const column = document.getElementById('plotColumn').value; |
| const plotType = document.getElementById('plotType').value; |
| |
| fetch('/forecast/sales/plot', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/x-www-form-urlencoded', |
| }, |
| body: `column=${column}&plot_type=${plotType}` |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| const plotData = JSON.parse(data.plot); |
| Plotly.newPlot('plotDiv', plotData.data, plotData.layout); |
| showToast(`Generated ${plotType} for ${column}`); |
| } else { |
| showToast(data.error, 'error'); |
| } |
| }) |
| .catch(error => { |
| console.error('Error:', error); |
| showToast(error.message, 'error'); |
| }); |
| } |
|
|
| window.fixNulls = function(column) { |
| const method = document.getElementById('method-' + column).value; |
| fetch('/forecast/sales/fix_nulls', { |
| method: 'POST', |
| headers: {'Content-Type': 'application/x-www-form-urlencoded'}, |
| body: `column=${column}&method=${method}` |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| const msgDiv = document.getElementById('null-fix-message'); |
| if (data.success) { |
| msgDiv.textContent = data.message; |
| msgDiv.classList.remove('text-red-700'); |
| msgDiv.classList.add('text-green-700'); |
| showToast(data.message); |
|
|
| |
| updateSummaryStats(data.summary_stats); |
|
|
| |
| updatePreviewTable(data.columns, data.preview_data); |
|
|
| |
| updateNullsUI(data.summary_stats.missing_values); |
|
|
| } else { |
| msgDiv.textContent = data.error; |
| msgDiv.classList.remove('text-green-700'); |
| msgDiv.classList.add('text-red-700'); |
| showToast(data.error, 'error'); |
| } |
| }); |
| } |
|
|
| |
| function updateSummaryStats(stats) { |
| document.querySelectorAll('.summary-total-rows').forEach(el => el.textContent = stats.total_rows); |
| document.querySelectorAll('.summary-total-cols').forEach(el => el.textContent = stats.total_columns); |
| document.querySelectorAll('.summary-numeric-cols').forEach(el => el.textContent = stats.numeric_columns.join(', ')); |
| document.querySelectorAll('.summary-categorical-cols').forEach(el => el.textContent = stats.categorical_columns.join(', ')); |
| } |
|
|
| |
| function updatePreviewTable(columns, previewData) { |
| const table = document.getElementById('preview-table'); |
| if (!table) return; |
| |
| let thead = table.querySelector('thead'); |
| let tbody = table.querySelector('tbody'); |
| thead.innerHTML = '<tr>' + columns.map(col => `<th class="px-4 py-2 bg-gray-100">${col}</th>`).join('') + '</tr>'; |
| |
| tbody.innerHTML = previewData.map(row => |
| '<tr>' + columns.map(col => `<td class="border px-4 py-2">${row[col]}</td>`).join('') + '</tr>' |
| ).join(''); |
| } |
|
|
| |
| function updateNullsUI(missingValues) { |
| const nullList = document.getElementById('null-list'); |
| if (!nullList) return; |
| let html = ''; |
| let hasNulls = false; |
| for (const [col, count] of Object.entries(missingValues)) { |
| if (count > 0) { |
| hasNulls = true; |
| html += ` |
| <li> |
| <span>${col}: ${count} missing values</span> |
| <select id="method-${col}" class="border rounded p-1 mx-2"> |
| <option value="drop">Drop Rows</option> |
| <option value="mean">Fill Mean</option> |
| <option value="median">Fill Median</option> |
| <option value="mode">Fill Mode</option> |
| </select> |
| <button class="btn-learn-more" onclick="fixNulls('${col}')">Fix</button> |
| </li> |
| `; |
| } |
| } |
| nullList.innerHTML = html; |
| |
| if (!hasNulls) { |
| nullList.innerHTML = '<li class="text-green-700 font-semibold">No missing values remaining!</li>'; |
| } |
| } |
| window.runForecast = function() { |
| const dateCol = document.getElementById('forecastDateCol').value; |
| const targetCol = document.getElementById('forecastTargetCol').value; |
| const model = document.getElementById('forecastModel').value; |
| const horizon = document.getElementById('forecastHorizon').value; |
| const metricsDiv = document.getElementById('forecast-metrics'); |
| const plotDiv = document.getElementById('forecast-plot'); |
| const errorDiv = document.getElementById('forecast-error'); |
| metricsDiv.innerHTML = ''; |
| plotDiv.innerHTML = ''; |
| errorDiv.textContent = ''; |
|
|
| fetch('/forecast/sales/run_forecast', { |
| method: 'POST', |
| headers: {'Content-Type': 'application/x-www-form-urlencoded'}, |
| body: `date_col=${dateCol}&target_col=${targetCol}&model=${model}&horizon=${horizon}` |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| |
| metricsDiv.innerHTML = ` |
| <div class="mb-2"> |
| <strong>Model:</strong> ${data.model} |
| </div> |
| <div class="mb-2"> |
| <strong>MAPE:</strong> ${data.metrics.MAPE.toFixed(2)}% |
| <strong class="ml-4">RMSE:</strong> ${data.metrics.RMSE.toFixed(2)} |
| <strong class="ml-4">R²:</strong> ${data.metrics.R2.toFixed(3)} |
| </div> |
| `; |
| |
| const trace = { |
| x: data.dates, |
| y: data.forecast, |
| mode: 'lines+markers', |
| name: 'Forecast' |
| }; |
| const lower = data.conf_int.map(ci => ci[0]); |
| const upper = data.conf_int.map(ci => ci[1]); |
| const ciTrace = { |
| x: [...data.dates, ...data.dates.slice().reverse()], |
| y: [...upper, ...lower.reverse()], |
| fill: 'toself', |
| fillcolor: 'rgba(0,100,80,0.2)', |
| line: {color: 'transparent'}, |
| name: 'Confidence Interval', |
| showlegend: true, |
| type: 'scatter' |
| }; |
| const layout = { |
| title: 'Forecasted Sales', |
| xaxis: {title: 'Date'}, |
| yaxis: {title: 'Forecast'}, |
| showlegend: true |
| }; |
| Plotly.newPlot(plotDiv, [trace, ciTrace], layout); |
| showToast('Forecast generated!'); |
| } else { |
| errorDiv.textContent = data.error; |
| showToast(data.error, 'error'); |
| } |
| }) |
| .catch(error => { |
| errorDiv.textContent = error.message; |
| showToast(error.message, 'error'); |
| }); |
| } |
|
|
| |
|
|
| function runPrediction() { |
| |
| const metricsDiv = document.getElementById('predict-metrics'); |
| const errorDiv = document.getElementById('predict-error'); |
| const singlePredictionSection = document.getElementById('single-prediction-section'); |
| |
| metricsDiv.innerHTML = '<p>Running prediction...</p>'; |
| errorDiv.textContent = ''; |
| singlePredictionSection.classList.add('hidden'); |
| |
| |
| const targetCol = document.getElementById('predictTargetCol').value; |
| const model = document.getElementById('predictModel').value; |
| |
| |
| const formData = new FormData(); |
| formData.append('target_col', targetCol); |
| formData.append('model', model); |
|
|
| |
| fetch('/predict/machine_failure/run_prediction', { |
| method: 'POST', |
| body: formData |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| |
| let metricsHtml = |
| '<div class="grid grid-cols-2 md:grid-cols-4 gap-4">'; |
| for (const [key, value] of Object.entries(data.metrics)) { |
| metricsHtml += ` |
| <div class="p-4 bg-blue-50 rounded"> |
| <p class="font-semibold">${key}</p> |
| <p class="text-xl">${value.toFixed(4)}</p> |
| </div> |
| `; |
| } |
| metricsHtml += "</div>"; |
| metricsDiv.innerHTML = metricsHtml; |
|
|
| |
| if (data.top_features && Array.isArray(data.top_features)) { |
| const fiDiv = document.getElementById("feature-importance"); |
| const fiList = document.getElementById("feature-importance-list"); |
| fiDiv.classList.remove("hidden"); |
| fiList.innerHTML = ` |
| <table class="min-w-full table-auto"> |
| <thead> |
| <tr> |
| <th class="px-4 py-2 bg-gray-100">Feature</th> |
| <th class="px-4 py-2 bg-gray-100">Importance</th> |
| </tr> |
| </thead> |
| <tbody> |
| ${data.top_features |
| .map( |
| (f) => |
| `<tr> |
| <td class="border px-4 py-2">${ |
| f.feature |
| }</td> |
| <td class="border px-4 py-2">${f.importance.toFixed( |
| 4 |
| )}</td> |
| </tr>` |
| ) |
| .join("")} |
| </tbody> |
| </table> |
| `; |
| } |
|
|
| showToast("Model trained successfully"); |
|
|
| |
| fetchSinglePredictionForm(); |
| } else { |
| errorDiv.textContent = data.error || 'An error occurred'; |
| showToast(data.error || 'An error occurred', 'error'); |
| } |
| }) |
| .catch(error => { |
| errorDiv.textContent = 'Error: ' + error.message; |
| showToast('Error: ' + error.message, 'error'); |
| }); |
| } |
|
|
| window.runPrediction = runPrediction; |
|
|
| |
| function fetchSinglePredictionForm() { |
| fetch('/predict/machine_failure/get_form_data') |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| generatePredictionForm(data.form_fields); |
| document.getElementById('single-prediction-section').classList.remove('hidden'); |
| } else { |
| showToast(data.error, 'error'); |
| document.getElementById('single-prediction-error').textContent = data.error; |
| } |
| }) |
| .catch(error => { |
| console.error('Error fetching form data:', error); |
| showToast('Error fetching form data: ' + error.message, 'error'); |
| document.getElementById('single-prediction-error').textContent = 'Error fetching form data: ' + error.message; |
| }); |
| } |
|
|
| |
| function generatePredictionForm(formFields) { |
| const formContainer = document.getElementById('single-prediction-form'); |
| formContainer.innerHTML = ''; |
|
|
| formFields.forEach(field => { |
| const div = document.createElement('div'); |
| div.className = 'mb-4'; |
|
|
| const label = document.createElement('label'); |
| label.className = 'block text-gray-700 text-sm font-bold mb-2'; |
| label.textContent = field.name.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); |
|
|
| div.appendChild(label); |
|
|
| if (field.type === 'select') { |
| const select = document.createElement('select'); |
| select.id = `input-${field.name}`; |
| select.name = field.name; |
| select.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
| |
| field.options.forEach(option => { |
| const optionElement = document.createElement('option'); |
| optionElement.value = option; |
| optionElement.textContent = option; |
| |
| if (field.default_value !== undefined && String(field.default_value) === String(option)) { |
| optionElement.selected = true; |
| } |
| select.appendChild(optionElement); |
| }); |
| div.appendChild(select); |
| } else if (field.type === 'number') { |
| const input = document.createElement('input'); |
| input.id = `input-${field.name}`; |
| input.name = field.name; |
| input.type = 'number'; |
| input.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
| input.placeholder = `Enter ${field.name.replace(/_/g, ' ')}`; |
| if (field.default_value !== undefined) { |
| input.value = field.default_value; |
| } |
| div.appendChild(input); |
| } else { |
| const input = document.createElement('input'); |
| input.id = `input-${field.name}`; |
| input.name = field.name; |
| input.type = 'text'; |
| input.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
| input.placeholder = field.placeholder || `Enter ${field.name.replace(/_/g, ' ')}`; |
| if (field.default_value !== undefined) { |
| input.value = field.default_value; |
| } |
| div.appendChild(input); |
| } |
| formContainer.appendChild(div); |
| }); |
| } |
|
|
| |
| window.predictSingleInstance = function() { |
| const form = document.getElementById('single-prediction-form'); |
| const formData = {}; |
| const inputs = form.querySelectorAll('input, select'); |
|
|
| inputs.forEach(input => { |
| formData[input.name] = input.value; |
| }); |
|
|
| const resultDiv = document.getElementById('single-prediction-result'); |
| const predictionOutput = document.getElementById('prediction-output'); |
| const probabilityOutput = document.getElementById('probability-output'); |
| const errorDiv = document.getElementById('single-prediction-error'); |
|
|
| resultDiv.classList.add('hidden'); |
| errorDiv.textContent = ''; |
| predictionOutput.textContent = 'Predicting...'; |
| probabilityOutput.innerHTML = ''; |
|
|
| fetch('/predict/machine_failure/predict_single', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify(formData) |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| let displayPrediction = 'Unknown'; |
| if (data.prediction === 0 || data.prediction === '0') { |
| displayPrediction = 'No Failure'; |
| } else if (data.prediction === 1 || data.prediction === '1') { |
| displayPrediction = 'Failure'; |
| } else { |
| displayPrediction = data.prediction; |
| } |
| predictionOutput.textContent = displayPrediction; |
|
|
| if (data.probability && Array.isArray(data.probability)) { |
| if (data.probability.length === 2) { |
| const probNoFailure = data.probability[0]; |
| const probFailure = data.probability[1]; |
| probabilityOutput.innerHTML = `Probability of No Failure: ${(probNoFailure * 100).toFixed(2)}%<br> |
| Probability of Failure: ${(probFailure * 100).toFixed(2)}%`; |
| } else { |
| probabilityOutput.innerHTML = 'Probabilities: ' + data.probability.map((p, i) => `Class ${i}: ${(p * 100).toFixed(2)}%`).join(', '); |
| } |
| } else { |
| probabilityOutput.innerHTML = 'Probability: N/A (not a classification model)'; |
| } |
| resultDiv.classList.remove('hidden'); |
| showToast('Single prediction successful!'); |
| } else { |
| errorDiv.textContent = data.error || 'An error occurred during single prediction.'; |
| showToast(data.error || 'An error occurred', 'error'); |
| } |
| }) |
| .catch(error => { |
| console.error('Error during single prediction:', error); |
| errorDiv.textContent = 'Error: ' + error.message; |
| showToast('Error: ' + error.message, 'error'); |
| }); |
| }; |
|
|
|
|
| |
|
|
| window.runSupplyPrediction = function() { |
| const metricsDiv = document.getElementById('predict-metrics-supply'); |
| const errorDiv = document.getElementById('predict-error-supply'); |
| const singlePredictionSection = document.getElementById('single-prediction-section-supply'); |
| |
| metricsDiv.innerHTML = '<p>Running prediction...</p>'; |
| errorDiv.textContent = ''; |
| singlePredictionSection.classList.add('hidden'); |
| |
| |
| const targetCol = 'failure_flag'; |
| const model = document.getElementById('predictModelSupply').value; |
| |
| const formData = new FormData(); |
| formData.append('target_col', targetCol); |
| formData.append('model', model); |
|
|
| fetch('/predict/supply_failure/run_prediction', { |
| method: 'POST', |
| body: formData |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| let metricsHtml = |
| '<div class="grid grid-cols-2 md:grid-cols-4 gap-4">'; |
| for (const [key, value] of Object.entries(data.metrics)) { |
| metricsHtml += ` |
| <div class="p-4 bg-blue-50 rounded"> |
| <p class="font-semibold">${key}</p> |
| <p class="text-xl">${value.toFixed(4)}</p> |
| </div> |
| `; |
| } |
| metricsHtml += "</div>"; |
| metricsDiv.innerHTML = metricsHtml; |
|
|
| |
| if (data.top_features && Array.isArray(data.top_features)) { |
| const fiDiv = document.getElementById("feature-importance"); |
| const fiList = document.getElementById("feature-importance-list"); |
| fiDiv.classList.remove("hidden"); |
| fiList.innerHTML = ` |
| <table class="min-w-full table-auto"> |
| <thead> |
| <tr> |
| <th class="px-4 py-2 bg-gray-100">Feature</th> |
| <th class="px-4 py-2 bg-gray-100">Importance</th> |
| </tr> |
| </thead> |
| <tbody> |
| ${data.top_features |
| .map( |
| (f) => |
| `<tr> |
| <td class="border px-4 py-2">${ |
| f.feature |
| }</td> |
| <td class="border px-4 py-2">${f.importance.toFixed( |
| 4 |
| )}</td> |
| </tr>` |
| ) |
| .join("")} |
| </tbody> |
| </table> |
| `; |
| } |
|
|
| showToast("Supply Model trained successfully"); |
| fetchSupplySinglePredictionForm(); |
| } else { |
| errorDiv.textContent = data.error || 'An error occurred'; |
| showToast(data.error || 'An error occurred', 'error'); |
| } |
| }) |
| .catch(error => { |
| errorDiv.textContent = 'Error: ' + error.message; |
| showToast('Error: ' + error.message, 'error'); |
| }); |
| } |
|
|
| function fetchSupplySinglePredictionForm() { |
| fetch('/predict/supply_failure/get_form_data') |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| generateSupplyPredictionForm(data.form_fields); |
| document.getElementById('single-prediction-section-supply').classList.remove('hidden'); |
| } else { |
| showToast(data.error, 'error'); |
| document.getElementById('single-prediction-error-supply').textContent = data.error; |
| } |
| }) |
| .catch(error => { |
| console.error('Error fetching supply form data:', error); |
| showToast('Error fetching supply form data: ' + error.message, 'error'); |
| document.getElementById('single-prediction-error-supply').textContent = 'Error fetching supply form data: ' + error.message; |
| }); |
| } |
|
|
| function generateSupplyPredictionForm(formFields) { |
| const formContainer = document.getElementById('single-prediction-form-supply'); |
| formContainer.innerHTML = ''; |
|
|
| formFields.forEach(field => { |
| const div = document.createElement('div'); |
| div.className = 'mb-4'; |
|
|
| const label = document.createElement('label'); |
| label.className = 'block text-gray-700 text-sm font-bold mb-2'; |
| label.textContent = field.name.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); |
|
|
| div.appendChild(label); |
|
|
| if (field.type === 'select') { |
| const select = document.createElement('select'); |
| select.id = `input-supply-${field.name}`; |
| select.name = field.name; |
| select.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
| |
| field.options.forEach(option => { |
| const optionElement = document.createElement('option'); |
| optionElement.value = option; |
| optionElement.textContent = option; |
| if (field.default_value !== undefined && String(field.default_value) === String(option)) { |
| optionElement.selected = true; |
| } |
| select.appendChild(optionElement); |
| }); |
| div.appendChild(select); |
| } else if (field.type === 'number') { |
| const input = document.createElement('input'); |
| input.id = `input-supply-${field.name}`; |
| input.name = field.name; |
| input.type = 'number'; |
| input.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
| input.placeholder = `Enter ${field.name.replace(/_/g, ' ')}`; |
| if (field.default_value !== undefined) { |
| input.value = field.default_value; |
| } |
| div.appendChild(input); |
| } else { |
| const input = document.createElement('input'); |
| input.id = `input-supply-${field.name}`; |
| input.name = field.name; |
| input.type = 'text'; |
| input.className = 'shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'; |
| input.placeholder = field.placeholder || `Enter ${field.name.replace(/_/g, ' ')}`; |
| if (field.default_value !== undefined) { |
| input.value = field.default_value; |
| } |
| div.appendChild(input); |
| } |
| formContainer.appendChild(div); |
| }); |
| } |
|
|
| window.predictSupplySingleInstance = function() { |
| const form = document.getElementById('single-prediction-form-supply'); |
| const formData = {}; |
| const inputs = form.querySelectorAll('input, select'); |
|
|
| inputs.forEach(input => { |
| formData[input.name] = input.value; |
| }); |
|
|
| const resultDiv = document.getElementById('single-prediction-result-supply'); |
| const predictionOutput = document.getElementById('prediction-output-supply'); |
| const probabilityOutput = document.getElementById('probability-output-supply'); |
| const errorDiv = document.getElementById('single-prediction-error-supply'); |
|
|
| resultDiv.classList.add('hidden'); |
| errorDiv.textContent = ''; |
| predictionOutput.textContent = 'Predicting...'; |
| probabilityOutput.innerHTML = ''; |
|
|
| fetch('/predict/supply_failure/predict_single', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: JSON.stringify(formData) |
| }) |
| .then(response => response.json()) |
| .then(data => { |
| if (data.success) { |
| |
| let displayPrediction = 'Unknown'; |
| if (data.prediction === "Delivery Successful") { |
| displayPrediction = 'Delivery Successful'; |
| } else if (data.prediction === "Delivery Failed") { |
| displayPrediction = 'Delivery Failed'; |
| } else { |
| displayPrediction = data.prediction; |
| } |
| predictionOutput.textContent = displayPrediction; |
|
|
| if (data.probability && Array.isArray(data.probability)) { |
| if (data.probability.length === 2) { |
| |
| const probSuccessful = data.probability[0]; |
| const probFailed = data.probability[1]; |
| probabilityOutput.innerHTML = `Probability of Delivery Successful: ${(probSuccessful * 100).toFixed(2)}%<br> |
| Probability of Delivery Failed: ${(probFailed * 100).toFixed(2)}%`; |
| } else { |
| probabilityOutput.innerHTML = 'Probabilities: ' + data.probability.map((p, i) => `Class ${i}: ${(p * 100).toFixed(2)}%`).join(', '); |
| } |
| } else { |
| probabilityOutput.innerHTML = 'Probability: N/A (not a classification model)'; |
| } |
| resultDiv.classList.remove('hidden'); |
| showToast('Single prediction successful!'); |
| } else { |
| errorDiv.textContent = data.error || 'An error occurred during single prediction.'; |
| showToast(data.error || 'An error occurred', 'error'); |
| } |
| }) |
| .catch(error => { |
| console.error('Error during single prediction:', error); |
| errorDiv.textContent = 'Error: ' + error.message; |
| showToast('Error: ' + error.message, 'error'); |
| }); |
| }; |