Competitor Price Tracker
Analyze your pricing strategy against key competitors.
Pricing Analysis
Add New Product
Product List
No products added yet.
`; return; } elements.config.productListContainer.innerHTML = products.map(p => `
${p.name}
`).join('');
};
const handleFormSubmit = (e) => {
e.preventDefault();
const productData = {
name: elements.config.productName.value,
ourPrice: parseFloat(elements.config.ourPrice.value),
compA_name: elements.config.compAName.value || 'Competitor A',
compA_price: elements.config.compAPrice.value ? parseFloat(elements.config.compAPrice.value) : null,
compB_name: elements.config.compBName.value || 'Competitor B',
compB_price: elements.config.compBPrice.value ? parseFloat(elements.config.compBPrice.value) : null,
};
if (editingProductId) {
// Update
const index = products.findIndex(p => p.id === editingProductId);
products[index] = { ...products[index], ...productData };
} else {
// Add new
productData.id = Date.now();
products.push(productData);
}
clearForm();
renderAll();
};
const clearForm = () => {
editingProductId = null;
elements.config.form.reset();
elements.config.productId.value = '';
elements.config.formTitle.textContent = 'Add New Product';
};
const handleEdit = (id) => {
const product = products.find(p => p.id === id);
if (!product) return;
editingProductId = id;
elements.config.productId.value = product.id;
elements.config.productName.value = product.name;
elements.config.ourPrice.value = product.ourPrice;
elements.config.compAName.value = product.compA_name;
elements.config.compAPrice.value = product.compA_price;
elements.config.compBName.value = product.compB_name;
elements.config.compBPrice.value = product.compB_price;
elements.config.formTitle.textContent = 'Edit Product';
switchTab('config');
};
const handleDelete = (id) => {
if (confirm('Are you sure you want to delete this product?')) {
products = products.filter(p => p.id !== id);
if (editingProductId === id) {
clearForm();
}
renderAll();
}
};
const downloadPDF = async () => {
const { jsPDF } = window.jspdf;
const pdfContent = elements.pdf.contentArea;
// Clone the content to modify for PDF without affecting the screen
const pdfClone = pdfContent.cloneNode(true);
pdfClone.style.padding = '20px';
pdfClone.style.backgroundColor = '#ffffff';
// Remove action buttons from the clone
pdfClone.querySelectorAll('.print-hide').forEach(el => el.remove());
document.body.appendChild(pdfClone);
const canvas = await html2canvas(pdfClone, {
scale: 2,
backgroundColor: '#ffffff'
});
document.body.removeChild(pdfClone);
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({
orientation: 'landscape',
unit: 'px',
format: [canvas.width, canvas.height]
});
pdf.addImage(imgData, 'PNG', 0, 0, canvas.width, canvas.height);
// Add footer to PDF
const pageCount = pdf.internal.getNumberOfPages();
for (let i = 1; i <= pageCount; i++) {
pdf.setPage(i);
pdf.setFontSize(10);
pdf.setTextColor(150);
pdf.text(
`Report generated on: ${new Date().toLocaleDateString()}`,
pdf.internal.pageSize.getWidth() / 2,
pdf.internal.pageSize.getHeight() - 10,
{ align: 'center' }
);
}
pdf.save('competitor-price-report.pdf');
};
const renderAll = () => {
renderDashboard();
renderProductList();
};
// --- INITIALIZATION ---
const initialize = () => {
products = [...sampleData];
// Event Listeners
elements.tabs.dashboard.addEventListener('click', () => switchTab('dashboard'));
elements.tabs.config.addEventListener('click', () => switchTab('config'));
elements.navButtons.next.addEventListener('click', () => {
const currentIndex = tabs.indexOf(currentTab);
if (currentIndex < tabs.length - 1) switchTab(tabs[currentIndex + 1]);
});
elements.navButtons.prev.addEventListener('click', () => {
const currentIndex = tabs.indexOf(currentTab);
if (currentIndex > 0) switchTab(tabs[currentIndex - 1]);
});
elements.config.form.addEventListener('submit', handleFormSubmit);
elements.config.clearFormBtn.addEventListener('click', clearForm);
elements.pdf.downloadBtn.addEventListener('click', downloadPDF);
// Make functions globally accessible for inline onclick handlers
window.app = { handleEdit, handleDelete };
renderAll();
updateNavButtons();
};
initialize();
});
