Metaverse Dashboard

Metaverse Dashboard

Metaverse Overview

Virtual Asset Transactions

Transaction ID Virtual World Asset Type Sale Price ($) Buyer ID Seller ID Transaction Date Status

Configure Your Metaverse Data

Manually input or edit your virtual asset transaction data below. All changes will automatically update the dashboard.

Transaction ID Virtual World Asset Type Sale Price ($) Buyer ID Seller ID Transaction Date Status Actions

${overallMetrics.uniqueAssetsSold.toLocaleString()}

`; } /** * Renders the virtual asset transactions table in the Dashboard tab. */ function renderAssetTransactionsTable() { // Null check for the asset transactions table body if (!assetTransactionsTableBody) { console.error("Asset transactions table body not found."); return; } assetTransactionsTableBody.innerHTML = ''; // Clear existing rows metaverseData.forEach(transaction => { const row = document.createElement('tr'); row.innerHTML = ` ${transaction.id} ${transaction.virtualWorld} ${transaction.assetType} ${formatCurrency(transaction.salePrice)} ${transaction.buyerId} ${transaction.sellerId} ${transaction.transactionDate || 'N/A'} ${transaction.status} `; assetTransactionsTableBody.appendChild(row); }); } /** * Renders the data input table in the Data Configuration tab. */ function renderDataInputTable() { // Null check for the data input table body if (!dataInputTableBody) { console.error("Data input table body not found."); return; } dataInputTableBody.innerHTML = ''; // Clear existing rows metaverseData.forEach(transaction => { const row = document.createElement('tr'); row.innerHTML = ` ${transaction.id} `; dataInputTableBody.appendChild(row); }); // Attach event listeners to newly created input fields and delete buttons document.querySelectorAll('.transaction-input').forEach(input => { input.addEventListener('change', handleInputChange); }); document.querySelectorAll('.delete-row-btn').forEach(button => { button.addEventListener('click', handleDeleteRow); }); } /** * Updates all dashboard and data configuration views. */ function updateViews() { renderMetaverseSummary(); renderAssetTransactionsTable(); renderDataInputTable(); } // --- Event Handlers --- /** * Handles tab button clicks to switch between tabs. * @param {Event} event - The click event. */ function handleTabClick(event) { // Null check for event target if (!event.target) { console.error("Event target is null."); return; } const targetTab = event.target.dataset.tab; // Remove 'active' class from all tab buttons and content tabButtons.forEach(button => button.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); // Add 'active' class to the clicked tab button and corresponding content event.target.classList.add('active'); const activeTabContent = document.getElementById(`${targetTab}-tab`); if (activeTabContent) { // Null check for activeTabContent activeTabContent.classList.add('active'); } else { console.error(`Tab content for ${targetTab} not found.`); } updateViews(); // Update views whenever tab changes } /** * Handles changes in data input fields. * @param {Event} event - The change event. */ function handleInputChange(event) { // Null check for event target if (!event.target) { console.error("Event target is null."); return; } const input = event.target; const id = parseInt(input.dataset.id); const field = input.dataset.field; let value = input.value; // Convert numeric fields to numbers if (['salePrice'].includes(field)) { value = parseFloat(value) || 0; // Default to 0 if parsing fails if (value < 0) value = 0; // Ensure non-negative values input.value = value; // Update input field with sanitized value } const transactionIndex = metaverseData.findIndex(d => d.id === id); if (transactionIndex !== -1) { metaverseData[transactionIndex][field] = value; updateViews(); // Re-render dashboard and data input tables } else { console.error(`Transaction with ID ${id} not found.`); } } /** * Adds a new empty row to the data input table. */ function handleAddTransaction() { const newTransaction = { id: nextId++, virtualWorld: 'Decentraland', assetType: 'Land Plot', salePrice: 0.00, buyerId: 'new_user', sellerId: 'new_seller', transactionDate: new Date().toISOString().slice(0, 10), // Default to today's date status: 'Pending' }; metaverseData.push(newTransaction); updateViews(); // Re-render data input table } /** * Deletes a row from the data input table. * @param {Event} event - The click event. */ function handleDeleteRow(event) { // Null check for event target if (!event.target) { console.error("Event target is null."); return; } const idToDelete = parseInt(event.target.dataset.id); metaverseData = metaverseData.filter(d => d.id !== idToDelete); updateViews(); // Re-render data input table } /** * Handles navigation to the previous tab. */ function handlePrevTab() { const currentActiveTab = document.querySelector('.tab-button.active'); if (!currentActiveTab) { console.error("No active tab found."); return; } const currentIndex = Array.from(tabButtons).indexOf(currentActiveTab); if (currentIndex > 0) { tabButtons[currentIndex - 1].click(); // Simulate click on previous tab } } /** * Handles navigation to the next tab. */ function handleNextTab() { const currentActiveTab = document.querySelector('.tab-button.active'); if (!currentActiveTab) { console.error("No active tab found."); return; } const currentIndex = Array.from(tabButtons).indexOf(currentActiveTab); if (currentIndex < tabButtons.length - 1) { tabButtons[currentIndex + 1].click(); // Simulate click on next tab } } /** * Handles the PDF download functionality. * It uses html2canvas to capture the dashboard content and jsPDF to generate the PDF. */ async function handleDownloadPdf() { const dashboardContent = document.querySelector('.dashboard-content-for-pdf'); if (!dashboardContent) { console.error("Dashboard content for PDF not found."); return; } // Add a temporary title for the PDF const tempTitle = document.createElement('h1'); tempTitle.textContent = "Metaverse Dashboard"; tempTitle.style.textAlign = 'center'; tempTitle.style.marginBottom = '20px'; tempTitle.style.fontSize = '2em'; tempTitle.style.fontWeight = 'bold'; tempTitle.style.color = '#333'; dashboardContent.prepend(tempTitle); // Prepend to capture // Temporarily hide tab navigation and buttons for PDF capture const elementsToHide = document.querySelectorAll('.tab-nav, .tab-navigation-buttons, .add-row-btn, .delete-row-btn, .pdf-download-section .btn-primary'); elementsToHide.forEach(el => el.style.display = 'none'); // Ensure the dashboard tab is active before capturing for PDF const dashboardTabButton = document.querySelector('.tab-button[data-tab="dashboard"]'); if (dashboardTabButton && !dashboardTabButton.classList.contains('active')) { dashboardTabButton.click(); // Activate dashboard tab if not already active // A small delay might be needed here if content takes time to render after tab switch await new Promise(resolve => setTimeout(resolve, 100)); } try { const canvas = await html2canvas(dashboardContent, { scale: 2, // Increase scale for better resolution useCORS: true, // Required if images are from different origin (though we don't use external images) windowWidth: dashboardContent.scrollWidth, windowHeight: dashboardContent.scrollHeight }); const imgData = canvas.toDataURL('image/png'); const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'landscape', // Landscape for wider tables unit: 'px', format: 'a4' }); const imgWidth = pdf.internal.pageSize.getWidth(); const pageHeight = pdf.internal.pageSize.getHeight(); const imgHeight = canvas.height * imgWidth / canvas.width; let heightLeft = imgHeight; let position = 0; pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight); heightLeft -= pageHeight; while (heightLeft >= 0) { position = heightLeft - imgHeight; pdf.addPage(); pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight); heightLeft -= pageHeight; } pdf.save('metaverse_dashboard.pdf'); } catch (error) { console.error("Error generating PDF:", error); // Using alert as a fallback for critical error feedback, as per standard specification alert("Failed to generate PDF. Please try again."); } finally { // Restore hidden elements and remove temporary title elementsToHide.forEach(el => el.style.display = ''); if (tempTitle.parentNode) { tempTitle.parentNode.removeChild(tempTitle); } } } // --- Event Listener Attachments --- tabButtons.forEach(button => { // Null check for button if (button) { button.addEventListener('click', handleTabClick); } }); // Null checks before attaching listeners if (addTransactionBtn) { addTransactionBtn.addEventListener('click', handleAddTransaction); } else { console.error("Add Transaction button not found."); } if (downloadPdfBtn) { downloadPdfBtn.addEventListener('click', handleDownloadPdf); } else { console.error("Download PDF button not found."); } if (prevTabBtn) { prevTabBtn.addEventListener('click', handlePrevTab); } else { console.error("Previous Tab button not found."); } if (nextTabBtn) { nextTabBtn.addEventListener('click', handleNextTab); } else { console.error("Next Tab button not found."); } // Initial render of the dashboard and data input table updateViews(); });
Scroll to Top