${item.value}
`;
dashboardSummary.appendChild(card);
});
// Populate customer profiles table (sorted by customer since date descending)
const sortedProfilesData = [...customerProfilesData].sort((a, b) => new Date(b.customerSince) - new Date(a.customerSince));
sortedProfilesData.forEach(item => {
const row = customerProfilesTableBody.insertRow();
row.innerHTML = `
${item.customerId}
${item.name}
${item.email}
${item.age}
${item.gender}
${item.customerSince}
${item.lastPurchaseDate}
$${item.totalSpend.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
${item.preferredCommunication}
`;
});
}
// --- Data Configuration Table Rendering and Editing ---
/**
* Renders the data in the configuration table.
*/
function renderConfigTable() {
// Null check for config data table body
if (!configDataTableBody) {
console.error("Config data table body not found.");
return;
}
configDataTableBody.innerHTML = ''; // Clear existing rows
customerProfilesData.forEach((item, index) => {
const row = configDataTableBody.insertRow();
row.innerHTML = `
${item.customerId}
${item.name}
${item.email}
${item.age}
${item.gender}
${item.customerSince}
${item.lastPurchaseDate}
$${item.totalSpend.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
${item.preferredCommunication}
Edit
Delete
`;
});
}
/**
* Adds a new row or updates an existing row in the customer profile data.
*/
window.addRow = function() {
// Null checks for input fields
if (!customerIdInput || !customerNameInput || !customerEmailInput || !customerPhoneInput || !customerAddressInput || !customerAgeInput || !customerGenderSelect || !customerSinceInput || !lastPurchaseDateInput || !totalSpendInput || !preferredCommunicationSelect || !notesInput) {
console.error("One or more input fields not found.");
return;
}
const newCustomer = {
customerId: customerIdInput.value.trim(),
name: customerNameInput.value.trim(),
email: customerEmailInput.value.trim(),
phone: customerPhoneInput.value.trim(),
address: customerAddressInput.value.trim(),
age: parseInt(customerAgeInput.value) || 0,
gender: customerGenderSelect.value.trim(),
customerSince: customerSinceInput.value.trim(),
lastPurchaseDate: lastPurchaseDateInput.value.trim(),
totalSpend: parseFloat(totalSpendInput.value) || 0,
preferredCommunication: preferredCommunicationSelect.value.trim(),
notes: notesInput.value.trim()
};
// Simple validation
if (!newCustomer.customerId || !newCustomer.name || !newCustomer.email || newCustomer.age <= 0 || !newCustomer.gender || !newCustomer.customerSince || newCustomer.totalSpend < 0) {
showModal("Please fill in all required fields (Customer ID, Name, Email, Age, Gender, Customer Since, Total Spend) with valid values.");
return;
}
if (!newCustomer.email.includes('@') || !newCustomer.email.includes('.')) {
showModal("Please enter a valid email address.");
return;
}
// Check for duplicate Customer ID if adding a new row
if (editingIndex === -1) {
const isDuplicate = customerProfilesData.some(customer => customer.customerId === newCustomer.customerId);
if (isDuplicate) {
showModal(`Customer ID "${newCustomer.customerId}" already exists. Please use a unique ID.`);
return;
}
customerProfilesData.push(newCustomer);
} else {
// Update existing row
customerProfilesData[editingIndex] = newCustomer;
editingIndex = -1; // Reset editing state
updateRowButton.classList.add('hidden');
cancelEditButton.classList.add('hidden');
addRowButton.classList.remove('hidden');
customerIdInput.disabled = false; // Re-enable customer ID for new entries
}
clearForm();
renderConfigTable();
updateDashboard(); // Keep dashboard in sync
};
/**
* Populates the form with data from the row to be edited.
* @param {HTMLElement} button - The edit button clicked.
*/
window.editRow = function(button) {
// Null checks for input fields and edit buttons
if (!customerIdInput || !customerNameInput || !customerEmailInput || !customerPhoneInput || !customerAddressInput || !customerAgeInput || !customerGenderSelect || !customerSinceInput || !lastPurchaseDateInput || !totalSpendInput || !preferredCommunicationSelect || !notesInput || !addRowButton || !updateRowButton || !cancelEditButton) {
console.error("One or more form/button elements not found for editing.");
return;
}
const index = parseInt(button.dataset.index);
editingIndex = index;
const item = customerProfilesData[index];
customerIdInput.value = item.customerId;
customerNameInput.value = item.name;
customerEmailInput.value = item.email;
customerPhoneInput.value = item.phone;
customerAddressInput.value = item.address;
customerAgeInput.value = item.age;
customerGenderSelect.value = item.gender;
customerSinceInput.value = item.customerSince;
lastPurchaseDateInput.value = item.lastPurchaseDate;
totalSpendInput.value = item.totalSpend;
preferredCommunicationSelect.value = item.preferredCommunication;
notesInput.value = item.notes;
addRowButton.classList.add('hidden');
updateRowButton.classList.remove('hidden');
cancelEditButton.classList.remove('hidden');
customerIdInput.disabled = true; // Disable editing Customer ID during update
};
/**
* Deletes a row from the customer profile data.
* @param {HTMLElement} button - The delete button clicked.
*/
window.deleteRow = function(button) {
const index = parseInt(button.dataset.index);
showModal("Are you sure you want to delete this customer profile record?", true, (confirmed) => {
if (confirmed) {
customerProfilesData.splice(index, 1);
renderConfigTable();
updateDashboard(); // Keep dashboard in sync
clearForm(); // Clear form if the deleted item was being edited
editingIndex = -1;
updateRowButton.classList.add('hidden');
cancelEditButton.classList.add('hidden');
addRowButton.classList.remove('hidden');
customerIdInput.disabled = false; // Re-enable customer ID
}
});
};
/**
* Clears the input form fields.
*/
function clearForm() {
// Null checks for input fields
if (!customerIdInput || !customerNameInput || !customerEmailInput || !customerPhoneInput || !customerAddressInput || !customerAgeInput || !customerGenderSelect || !customerSinceInput || !lastPurchaseDateInput || !totalSpendInput || !preferredCommunicationSelect || !notesInput) {
console.error("One or more input fields not found for clearing.");
return;
}
customerIdInput.value = '';
customerNameInput.value = '';
customerEmailInput.value = '';
customerPhoneInput.value = '';
customerAddressInput.value = '';
customerAgeInput.value = '';
customerGenderSelect.value = ''; // Reset to default "Select Gender"
customerSinceInput.value = '';
lastPurchaseDateInput.value = '';
totalSpendInput.value = '';
preferredCommunicationSelect.value = 'Email'; // Reset to default preferred communication
notesInput.value = '';
}
/**
* Cancels the current edit operation and clears the form.
*/
window.cancelEdit = function() {
editingIndex = -1;
clearForm();
updateRowButton.classList.add('hidden');
cancelEditButton.classList.add('hidden');
addRowButton.classList.remove('hidden');
customerIdInput.disabled = false; // Re-enable customer ID
};
// --- PDF Download Functionality ---
/**
* Downloads the dashboard content as a PDF.
*/
window.downloadPdf = async function() {
// Null check for dashboard content
if (!dashboardTabContent) {
console.error("Dashboard content for PDF not found.");
return;
}
// Temporarily show the dashboard content if it's hidden, for accurate PDF generation
const wasHidden = dashboardTabContent.classList.contains('hidden');
if (wasHidden) {
dashboardTabContent.classList.remove('hidden');
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF('p', 'pt', 'letter'); // 'p' for portrait, 'pt' for points, 'letter' size
const element = dashboardTabContent; // The element to convert to PDF
// Use html2canvas to render the HTML element to a canvas
await html2canvas(element, {
scale: 2, // Increase scale for better quality
useCORS: true, // Important for images/external resources if any (though we don't use them here)
logging: false // Disable logging for cleaner console
}).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const imgWidth = 550; // A fixed width for the image in PDF
const pageHeight = doc.internal.pageSize.height;
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
let position = 30; // Initial Y position for content
doc.setFontSize(22);
doc.setTextColor(31, 41, 55); // Gray 900 equivalent
doc.text("Customer Profile Dashboard", doc.internal.pageSize.getWidth() / 2, position, { align: "center" });
position += 30; // Move down for image
doc.addImage(imgData, 'PNG', 30, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
// If content overflows, add new pages
while (heightLeft >= 0) {
position = heightLeft - imgHeight + 30; // Adjust position for new page
doc.addPage();
doc.addImage(imgData, 'PNG', 30, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
doc.save('Customer_Profile_Dashboard.pdf');
});
// Restore hidden state if it was hidden initially
if (wasHidden) {
dashboardTabContent.classList.add('hidden');
}
};
// --- Event Listeners ---
if (addRowButton) {
addRowButton.addEventListener('click', addRow);
} else {
console.error("Add Row Button not found.");
}
if (updateRowButton) {
updateRowButton.addEventListener('click', addRow); // addRow function handles both add and update based on editingIndex
} else {
console.error("Update Row Button not found.");
}
if (cancelEditButton) {
cancelEditButton.addEventListener('click', cancelEdit);
} else {
console.error("Cancel Edit Button not found.");
}
// Initial rendering and setup
openTab('dashboard'); // Open dashboard by default
updateDashboard(); // Populate dashboard with initial data
renderConfigTable(); // Populate config table with initial data
});