cg-data-analytics / index.html
Cg16's picture
use this logo in the header - Follow Up Deployment
794467c verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Visualization AI</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<style>
.dropzone {
border: 2px dashed #3b82f6;
border-radius: 0.5rem;
transition: all 0.3s ease;
}
.dropzone:hover {
background-color: #eff6ff;
}
.dropzone.active {
border-color: #10b981;
background-color: #ecfdf5;
}
.chart-container {
transition: all 0.3s ease;
}
.chart-container:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body class="bg-gray-50 min-h-screen flex flex-col">
<header id="header" class="bg-white shadow-sm">
<div class="container mx-auto px-4 py-4 flex justify-between items-center">
<div id="branding" class="flex items-center space-x-2">
<img id="companyLogo" src="https://huggingface.co/spaces/Cg16/cg-data-analytics/resolve/main/images/syf3.png" alt="Company Logo" class="h-10 w-10 rounded-full">
<h1 id="reportHeader" class="text-xl font-bold text-gray-800">Data Visualization Dashboard</h1>
</div>
<div id="headerControls" class="hidden space-x-4">
<div id="editControls" class="flex space-x-4">
<button id="uploadLogoBtn" class="text-blue-600 hover:text-blue-800 flex items-center">
<i data-feather="image" class="mr-1"></i> Upload Logo
</button>
<button id="editHeaderBtn" class="text-blue-600 hover:text-blue-800 flex items-center">
<i data-feather="edit" class="mr-1"></i> Edit Header
</button>
</div>
<button id="doneEditingBtn" class="bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded-md text-sm hidden">
<i data-feather="check" class="mr-1"></i> Done
</button>
<button id="shareReportBtn" class="text-blue-600 hover:text-blue-800 flex items-center">
<i data-feather="share-2" class="mr-1"></i> Share Report
</button>
</div>
</div>
</header>
<main class="flex-grow container mx-auto px-4 py-8">
<section class="mb-12" data-aos="fade-up">
<div class="text-center mb-8">
<h2 class="text-3xl font-bold text-gray-800 mb-2">AI-Powered Data Visualization</h2>
<p class="text-gray-600 max-w-2xl mx-auto">Upload your Excel, CSV files or paste a file URL to generate dynamic visualizations with AI analysis</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="dropzone p-8 text-center cursor-pointer" id="dropzone">
<div class="flex flex-col items-center justify-center h-full">
<i data-feather="upload" class="w-12 h-12 text-blue-500 mb-4"></i>
<h3 class="text-lg font-medium text-gray-700 mb-2">Drag & Drop Files Here</h3>
<p class="text-gray-500 mb-4">or</p>
<label for="fileInput" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md cursor-pointer transition">
Select Files
</label>
<input type="file" id="fileInput" class="hidden" accept=".csv, .xlsx, .xls">
</div>
</div>
<div class="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
<h3 class="text-lg font-medium text-gray-700 mb-4">Or paste a file URL</h3>
<div class="flex space-x-2">
<input type="url" id="fileUrl" placeholder="https://example.com/data.csv" class="flex-grow px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<button id="fetchUrlBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md transition">
<i data-feather="download" class="w-4 h-4"></i>
</button>
</div>
<div class="mt-4">
<p class="text-sm text-gray-500">Supported formats: CSV, Excel (.xlsx, .xls)</p>
</div>
</div>
</div>
</section>
<section id="visualizationSection" class="hidden" data-aos="fade-up">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-gray-800">AI-Generated Visualizations</h2>
<div id="chartControls" class="hidden space-x-2">
<button id="editChartTitlesBtn" class="text-blue-600 hover:text-blue-800 flex items-center">
<i data-feather="edit-2" class="mr-1"></i> Edit Titles
</button>
</div>
</div>
<div id="chartContainer" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Charts will be dynamically inserted here -->
</div>
<div id="aiInsights" class="mt-12 bg-white p-6 rounded-lg shadow-sm border border-gray-200">
<h3 class="text-xl font-medium text-gray-700 mb-4 flex items-center">
<i data-feather="zap" class="text-yellow-500 mr-2"></i> AI Analysis
</h3>
<div id="insightsContent" class="text-gray-600">
<!-- AI insights will be dynamically inserted here -->
</div>
</div>
</section>
</main>
<footer class="bg-gray-100 py-6">
<div class="container mx-auto px-4 text-center">
<p class="text-gray-600">
<a href="#" id="footerLink" class="hover:text-blue-600 transition">Created by CG. All rights reserved</a><br>
<span class="text-xs text-gray-500">For internal use only. Uses company security policies.</span>
</p>
</div>
</footer>
<!-- Modals -->
<div id="pinModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
<div class="bg-white p-6 rounded-lg shadow-xl max-w-sm w-full">
<h3 class="text-xl font-bold text-gray-800 mb-4">Enter Credentials</h3>
<input type="password" id="pinInput" placeholder="Enter PIN" class="w-full px-4 py-2 border border-gray-300 rounded-md mb-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
<input type="text" id="usernameInput" placeholder="Enter Username (except Super Admin)" class="w-full px-4 py-2 border border-gray-300 rounded-md mb-4 focus:outline-none focus:ring-2 focus:ring-blue-500 hidden">
<div class="flex justify-end space-x-3">
<button id="cancelPinBtn" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-100">Cancel</button>
<button id="submitPinBtn" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">Submit</button>
</div>
</div>
</div>
<div id="logoModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
<div class="bg-white p-6 rounded-lg shadow-xl max-w-sm w-full">
<h3 class="text-xl font-bold text-gray-800 mb-4">Upload Company Logo</h3>
<input type="file" id="logoUpload" accept="image/*" class="w-full mb-4">
<div class="flex justify-end space-x-3">
<button id="cancelLogoBtn" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-100">Cancel</button>
<button id="saveLogoBtn" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">Save</button>
</div>
</div>
</div>
<div id="headerModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
<div class="bg-white p-6 rounded-lg shadow-xl max-w-sm w-full">
<h3 class="text-xl font-bold text-gray-800 mb-4">Edit Report Header</h3>
<input type="text" id="headerInput" placeholder="Enter new header" class="w-full px-4 py-2 border border-gray-300 rounded-md mb-4 focus:outline-none focus:ring-2 focus:ring-blue-500">
<div class="flex justify-end space-x-3">
<button id="cancelHeaderBtn" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-100">Cancel</button>
<button id="saveHeaderBtn" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">Save</button>
</div>
</div>
</div>
<div id="doneBtnContainer" class="text-center mt-8 hidden">
<button id="doneBtn" class="bg-green-500 hover:bg-green-600 text-white px-6 py-3 rounded-md transition flex items-center mx-auto">
<i data-feather="check-circle" class="mr-2"></i> Done
</button>
</div>
<div id="shareModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
<div class="bg-white p-6 rounded-lg shadow-xl max-w-md w-full">
<h3 class="text-xl font-bold text-gray-800 mb-4">Share Report</h3>
<p class="text-gray-600 mb-4">Share this link for view-only access:</p>
<div class="flex items-center mb-4">
<input type="text" id="shareLink" readonly class="flex-grow px-4 py-2 border border-gray-300 rounded-l-md focus:outline-none">
<button id="copyLinkBtn" class="bg-blue-500 text-white px-4 py-2 rounded-r-md hover:bg-blue-600">
<i data-feather="copy"></i>
</button>
</div>
<div class="flex justify-end">
<button id="closeShareBtn" class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600">Close</button>
</div>
</div>
</div>
<div id="chartTitleModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
<div class="bg-white p-6 rounded-lg shadow-xl max-w-sm w-full">
<h3 class="text-xl font-bold text-gray-800 mb-4">Edit Chart Title</h3>
<input type="text" id="chartTitleInput" placeholder="Enter new title" class="w-full px-4 py-2 border border-gray-300 rounded-md mb-4 focus:outline-none focus:ring-2 focus:ring-blue-500">
<div class="flex justify-end space-x-3">
<button id="cancelTitleBtn" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-100">Cancel</button>
<button id="saveTitleBtn" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">Save</button>
</div>
</div>
</div>
<script>
// User management modal
const userManagementModal = `
<div id="userManagementModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
<div class="bg-white p-6 rounded-lg shadow-xl max-w-md w-full">
<h3 class="text-xl font-bold text-gray-800 mb-4">User Management</h3>
<div class="mb-4 max-h-60 overflow-y-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b">
<th class="text-left py-2">Username</th>
<th class="text-left py-2">PIN</th>
<th class="text-left py-2">Actions</th>
</tr>
</thead>
<tbody id="userList">
<!-- Users will be populated here -->
</tbody>
</table>
</div>
<div class="flex space-x-2 mb-4">
<input type="text" id="newUsername" placeholder="New Username" class="flex-1 px-3 py-2 border rounded">
<input type="text" id="newPin" placeholder="New PIN" class="w-24 px-3 py-2 border rounded">
<button id="addUserBtn" class="bg-blue-500 text-white px-3 py-2 rounded">Add</button>
</div>
<div class="flex justify-end space-x-3">
<button id="closeUserModalBtn" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-100">Close</button>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', userManagementModal);
document.addEventListener('DOMContentLoaded', function() {
feather.replace();
AOS.init();
// Sample data for demonstration
const sampleData = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [{
label: 'Sales',
data: [65, 59, 80, 81, 56, 55],
backgroundColor: 'rgba(59, 130, 246, 0.2)',
borderColor: 'rgba(59, 130, 246, 1)',
borderWidth: 1
}]
};
const aiInsights = [
"The data shows a peak in sales during March and April, suggesting a seasonal trend.",
"February had the lowest sales, which might indicate a need for promotional activities during this period.",
"Overall, the first half of the year shows positive growth compared to previous years."
];
// Dropzone functionality
const dropzone = document.getElementById('dropzone');
const fileInput = document.getElementById('fileInput');
const fileUrl = document.getElementById('fileUrl');
const fetchUrlBtn = document.getElementById('fetchUrlBtn');
const visualizationSection = document.getElementById('visualizationSection');
const chartContainer = document.getElementById('chartContainer');
const insightsContent = document.getElementById('insightsContent');
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropzone.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
dropzone.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropzone.addEventListener(eventName, unhighlight, false);
});
function highlight() {
dropzone.classList.add('active');
}
function unhighlight() {
dropzone.classList.remove('active');
}
dropzone.addEventListener('drop', handleDrop, false);
fileInput.addEventListener('change', handleFiles, false);
fetchUrlBtn.addEventListener('click', handleUrlFetch);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles({ target: { files } });
}
function handleFiles(e) {
const files = e.target.files;
if (files.length) {
processFiles(files);
}
}
function handleUrlFetch() {
const url = fileUrl.value.trim();
if (url) {
// In a real app, you would fetch the file from the URL
// For demo purposes, we'll just simulate processing
setTimeout(() => {
processFiles([{ name: url.split('/').pop() }]);
}, 1000);
}
}
function processFiles(files) {
// Show loading state
dropzone.innerHTML = '<div class="flex flex-col items-center justify-center h-full"><i data-feather="loader" class="animate-spin w-12 h-12 text-blue-500 mb-4"></i><p class="text-gray-700">Processing file...</p></div>';
feather.replace();
// Simulate file processing and AI analysis
setTimeout(() => {
generateVisualizations();
showAIInsights();
visualizationSection.classList.remove('hidden');
document.getElementById('doneBtnContainer').classList.remove('hidden');
feather.replace();
}, 1500);
}
// Done button functionality
document.getElementById('doneBtn').addEventListener('click', function() {
alert('Your report has been processed successfully!');
});
// Share functionality
const shareReportBtn = document.getElementById('shareReportBtn');
const shareModal = document.getElementById('shareModal');
const shareLink = document.getElementById('shareLink');
const copyLinkBtn = document.getElementById('copyLinkBtn');
const closeShareBtn = document.getElementById('closeShareBtn');
shareReportBtn.addEventListener('click', function() {
// Check if viewer needs approval
if (window.location.search.includes('view=true')) {
alert('This report requires approval from the creator to share further.');
return;
}
// Generate unique URL
const reportId = Math.random().toString(36).substring(2, 8);
shareLink.value = window.location.href.split('?')[0] + `?view=true&report=${reportId}`;
shareModal.classList.remove('hidden');
// Show download prompt for first-time viewers
if (!localStorage.getItem('downloadedApp')) {
setTimeout(() => {
if (confirm('Download the HTML App shortcut to your desktop?')) {
const blob = new Blob([document.documentElement.outerHTML], {type: 'text/html'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'Data_Visualization_App.html';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
localStorage.setItem('downloadedApp', 'true');
}
}, 1000);
}
});
copyLinkBtn.addEventListener('click', function() {
shareLink.select();
document.execCommand('copy');
feather.replace();
alert('Link copied to clipboard!');
});
closeShareBtn.addEventListener('click', function() {
shareModal.classList.add('hidden');
});
// Check for view-only mode
if (window.location.search.includes('view=true')) {
document.getElementById('headerControls').classList.add('hidden');
document.getElementById('chartControls').classList.add('hidden');
document.getElementById('dropzone').classList.add('hidden');
document.getElementById('fileInput').classList.add('hidden');
document.querySelector('input[type="url"]').parentElement.classList.add('hidden');
document.getElementById('doneBtnContainer').classList.add('hidden');
}
function generateVisualizations() {
// Clear previous charts
chartContainer.innerHTML = '';
// Generate sample charts (in a real app, this would be dynamic based on the data)
const chartTypes = ['bar', 'line', 'pie', 'doughnut', 'radar', 'polarArea'];
chartTypes.forEach((type, index) => {
const chartCard = document.createElement('div');
chartCard.className = 'chart-container bg-white p-4 rounded-lg shadow-sm border border-gray-200';
chartCard.innerHTML = `
<div class="flex justify-between items-center mb-3">
<h3 class="font-medium text-gray-700 chart-title">${type.charAt(0).toUpperCase() + type.slice(1)} Chart</h3>
<i data-feather="chevron-right" class="text-gray-400"></i>
</div>
<div class="h-64">
<canvas id="chart-${index}"></canvas>
</div>
`;
chartContainer.appendChild(chartCard);
// Initialize chart
const ctx = document.getElementById(`chart-${index}`).getContext('2d');
new Chart(ctx, {
type: type,
data: sampleData,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
});
feather.replace();
}
function showAIInsights() {
insightsContent.innerHTML = '';
aiInsights.forEach(insight => {
const insightItem = document.createElement('p');
insightItem.className = 'mb-3 pl-4 border-l-4 border-blue-500';
insightItem.textContent = insight;
insightsContent.appendChild(insightItem);
});
}
// User management functionality
const userManagementBtn = document.createElement('button');
userManagementBtn.id = 'userManagementBtn';
userManagementBtn.className = 'text-blue-600 hover:text-blue-800 flex items-center hidden';
userManagementBtn.innerHTML = '<i data-feather="users" class="mr-1"></i> Manage Users';
document.getElementById('headerControls').prepend(userManagementBtn);
userManagementBtn.addEventListener('click', function() {
document.getElementById('userManagementModal').classList.remove('hidden');
// Populate user list
const userList = document.getElementById('userList');
userList.innerHTML = `
<tr>
<td class="py-2">Celine</td>
<td class="py-2">1111</td>
<td class="py-2"><i data-feather="trash-2" class="text-red-500 cursor-pointer"></i></td>
</tr>
<tr>
<td class="py-2">Jason</td>
<td class="py-2">C-1111</td>
<td class="py-2"><i data-feather="trash-2" class="text-red-500 cursor-pointer"></i></td>
</tr>
<tr>
<td class="py-2">Chris</td>
<td class="py-2">C-2222</td>
<td class="py-2"><i data-feather="trash-2" class="text-red-500 cursor-pointer"></i></td>
</tr>
<tr>
<td class="py-2">MNL1</td>
<td class="py-2">M-1111</td>
<td class="py-2"><i data-feather="trash-2" class="text-red-500 cursor-pointer"></i></td>
</tr>
<tr>
<td class="py-2">MNL2</td>
<td class="py-2">M-2222</td>
<td class="py-2"><i data-feather="trash-2" class="text-red-500 cursor-pointer"></i></td>
</tr>
<tr>
<td class="py-2">CEB3</td>
<td class="py-2">C-3333</td>
<td class="py-2"><i data-feather="trash-2" class="text-red-500 cursor-pointer"></i></td>
</tr>
`;
feather.replace();
});
document.getElementById('closeUserModalBtn').addEventListener('click', function() {
document.getElementById('userManagementModal').classList.add('hidden');
});
// Admin functionality
const footerLink = document.getElementById('footerLink');
const pinModal = document.getElementById('pinModal');
const pinInput = document.getElementById('pinInput');
const submitPinBtn = document.getElementById('submitPinBtn');
const cancelPinBtn = document.getElementById('cancelPinBtn');
const headerControls = document.getElementById('headerControls');
const chartControls = document.getElementById('chartControls');
footerLink.addEventListener('click', function(e) {
e.preventDefault();
pinModal.classList.remove('hidden');
document.getElementById('usernameInput').classList.remove('hidden');
if (pinInput.value === '1123') {
document.getElementById('usernameInput').classList.add('hidden');
}
});
cancelPinBtn.addEventListener('click', function() {
pinModal.classList.add('hidden');
pinInput.value = '';
});
submitPinBtn.addEventListener('click', function() {
const pin = pinInput.value;
const username = document.getElementById('usernameInput').value;
// Super Admin
if (pin === '1123') {
headerControls.classList.remove('hidden');
document.getElementById('editControls').classList.remove('hidden');
document.getElementById('doneEditingBtn').classList.remove('hidden');
chartControls.classList.remove('hidden');
pinModal.classList.add('hidden');
pinInput.value = '';
document.getElementById('usernameInput').classList.add('hidden');
}
// Admin
else if (pin === '1111' && username === 'Celine') {
headerControls.classList.remove('hidden');
document.getElementById('editControls').classList.remove('hidden');
document.getElementById('doneEditingBtn').classList.remove('hidden');
chartControls.classList.remove('hidden');
pinModal.classList.add('hidden');
pinInput.value = '';
document.getElementById('usernameInput').value = '';
}
// Regular Users
else if (
(pin === 'C-1111' && username === 'Jason') ||
(pin === 'C-2222' && username === 'Chris') ||
(pin === 'M-1111' && username === 'MNL1') ||
(pin === 'M-2222' && username === 'MNL2') ||
(pin === 'C-3333' && username === 'CEB3')
) {
headerControls.classList.remove('hidden');
document.getElementById('uploadLogoBtn').classList.add('hidden');
document.getElementById('editHeaderBtn').classList.add('hidden');
document.getElementById('doneEditingBtn').classList.add('hidden');
chartControls.classList.remove('hidden');
pinModal.classList.add('hidden');
pinInput.value = '';
document.getElementById('usernameInput').value = '';
} else {
alert('Incorrect PIN or Username');
}
});
// Show user management button only for super admin
if (pin === '1123') {
document.getElementById('userManagementBtn').classList.remove('hidden');
}
// Logo upload
const uploadLogoBtn = document.getElementById('uploadLogoBtn');
const logoModal = document.getElementById('logoModal');
const logoUpload = document.getElementById('logoUpload');
const saveLogoBtn = document.getElementById('saveLogoBtn');
const cancelLogoBtn = document.getElementById('cancelLogoBtn');
const companyLogo = document.getElementById('companyLogo');
uploadLogoBtn.addEventListener('click', function() {
logoModal.classList.remove('hidden');
});
cancelLogoBtn.addEventListener('click', function() {
logoModal.classList.add('hidden');
logoUpload.value = '';
});
saveLogoBtn.addEventListener('click', function() {
if (logoUpload.files.length) {
const file = logoUpload.files[0];
const reader = new FileReader();
reader.onload = function(e) {
companyLogo.src = e.target.result;
};
reader.readAsDataURL(file);
logoModal.classList.add('hidden');
}
});
// Header edit
const editHeaderBtn = document.getElementById('editHeaderBtn');
const headerModal = document.getElementById('headerModal');
const headerInput = document.getElementById('headerInput');
const saveHeaderBtn = document.getElementById('saveHeaderBtn');
const cancelHeaderBtn = document.getElementById('cancelHeaderBtn');
const reportHeader = document.getElementById('reportHeader');
editHeaderBtn.addEventListener('click', function() {
headerInput.value = reportHeader.textContent;
headerModal.classList.remove('hidden');
});
cancelHeaderBtn.addEventListener('click', function() {
headerModal.classList.add('hidden');
});
saveHeaderBtn.addEventListener('click', function() {
reportHeader.textContent = headerInput.value;
headerModal.classList.add('hidden');
});
// Chart title edit
const editChartTitlesBtn = document.getElementById('editChartTitlesBtn');
const chartTitleModal = document.getElementById('chartTitleModal');
const chartTitleInput = document.getElementById('chartTitleInput');
const saveTitleBtn = document.getElementById('saveTitleBtn');
const cancelTitleBtn = document.getElementById('cancelTitleBtn');
let currentChartTitle = null;
editChartTitlesBtn.addEventListener('click', function() {
const chartTitles = document.querySelectorAll('.chart-title');
chartTitles.forEach(title => {
title.style.cursor = 'pointer';
title.addEventListener('click', function() {
currentChartTitle = this;
chartTitleInput.value = this.textContent;
chartTitleModal.classList.remove('hidden');
});
});
});
cancelTitleBtn.addEventListener('click', function() {
chartTitleModal.classList.add('hidden');
});
saveTitleBtn.addEventListener('click', function() {
if (currentChartTitle) {
currentChartTitle.textContent = chartTitleInput.value;
chartTitleModal.classList.add('hidden');
}
});
});
</script>
</body>
</html>