${currencyFormatter.format(deal.discounted)}
${currencyFormatter.format(deal.original)}
(${discountPercent.toFixed(0)}% off)
${countdownText}
`;
scheduleContainer.appendChild(card);
});
};
const updateCountdowns = () => {
document.querySelectorAll('.countdown').forEach(el => {
const targetTime = new Date(el.dataset.time).getTime();
const now = new Date().getTime();
const distance = targetTime - now;
if (distance < 0) {
el.parentElement.textContent = "Offer status changed.";
// A full re-render might be needed here to update status tag
return;
}
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
el.textContent = `${days}d ${hours}h ${minutes}m ${seconds}s`;
});
};
const handleAddOrUpdate = () => {
const name = dealNameEl.value.trim();
const original = parseFloat(originalPriceEl.value);
const discounted = parseFloat(discountedPriceEl.value);
const start = startTimeEl.value;
const end = endTimeEl.value;
if (!name || isNaN(original) || isNaN(discounted) || !start || !end) {
alert('Please fill out all fields with valid data.');
return;
}
if (new Date(start) >= new Date(end)) {
alert('End time must be after the start time.');
return;
}
if (editingId) {
// Update existing
deals = deals.map(d => d.id === editingId ? { ...d, name, original, discounted, start, end } : d);
} else {
// Add new
const newDeal = { id: Date.now(), name, original, discounted, start, end };
deals.push(newDeal);
}
resetForm();
renderDeals();
};
const handleScheduleClick = (e) => {
if (e.target.classList.contains('edit-btn')) {
const id = Number(e.target.dataset.id);
const dealToEdit = deals.find(d => d.id === id);
if (dealToEdit) {
formTitleEl.textContent = 'Edit Deal';
addUpdateBtn.textContent = 'Update Deal';
addUpdateBtn.classList.remove('bg-blue-600', 'hover:bg-blue-700');
addUpdateBtn.classList.add('bg-amber-500', 'hover:bg-amber-600');
cancelEditBtn.classList.remove('hidden');
dealIdEl.value = dealToEdit.id;
dealNameEl.value = dealToEdit.name;
originalPriceEl.value = dealToEdit.original;
discountedPriceEl.value = dealToEdit.discounted;
startTimeEl.value = dealToEdit.start;
endTimeEl.value = dealToEdit.end;
editingId = id;
window.scrollTo({ top: 0, behavior: 'smooth' });
}
} else if (e.target.classList.contains('delete-btn')) {
const id = Number(e.target.dataset.id);
deals = deals.filter(d => d.id !== id);
renderDeals();
}
};
const downloadPDF = () => {
const { jsPDF } = window.jspdf;
const pdfContent = document.getElementById('pdf-content');
// Temporarily remove buttons for PDF
pdfContent.querySelectorAll('button').forEach(btn => btn.style.display = 'none');
html2canvas(pdfContent, { scale: 2, backgroundColor: '#f9fafb' }).then(canvas => {
// Restore buttons after capture
pdfContent.querySelectorAll('button').forEach(btn => btn.style.display = 'block');
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = (canvas.height * pdfWidth) / canvas.width;
pdf.setFontSize(16);
pdf.text("Deal Schedule", pdfWidth / 2, 15, { align: 'center' });
pdf.addImage(imgData, 'PNG', 0, 25, pdfWidth, pdfHeight);
pdf.save('deal-schedule.pdf');
});
};
// --- EVENT LISTENERS ---
addUpdateBtn.addEventListener('click', handleAddOrUpdate);
cancelEditBtn.addEventListener('click', resetForm);
scheduleContainer.addEventListener('click', handleScheduleClick);
downloadPdfBtn.addEventListener('click', downloadPDF);
// --- INITIALIZATION ---
renderDeals();
setInterval(() => {
updateCountdowns();
// Periodically check if a deal's status has changed
renderDeals();
}, 1000);
});