${months[month]} ${year}
Sun
Mon
Tue
Wed
Thu
Fri
Sat
`;
// Add blank days for the first week
for (let i = 0; i < firstDay.getDay(); i++) {
monthHTML += ``;
}
// Add days of the month
for (let day = 1; day <= daysInMonth; day++) {
const date = new Date(year, month, day);
const campaignsOnThisDay = scheduledCampaigns.filter(s => {
const start = new Date(s.start);
const end = new Date(s.end);
start.setHours(0,0,0,0);
end.setHours(23,59,59,999);
return date >= start && date <= end;
});
let campaignBarsHTML = '';
if (campaignsOnThisDay.length > 0) {
const campaignType = campaignTypes.find(ct => ct.id === campaignsOnThisDay[0].typeId);
if (campaignType) {
campaignBarsHTML = `
`;
}
}
monthHTML += `
`; // Close calendar-grid
monthContainer.innerHTML = monthHTML;
calendarContainer.appendChild(monthContainer);
}
}
// --- PDF GENERATION ---
function downloadPDF() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF('p', 'mm', 'a4');
const date = new Date().toLocaleString();
const currentYear = yearSelector.value;
doc.setFillColor(154, 52, 18); // Orange 800
doc.rect(0, 0, doc.internal.pageSize.getWidth(), 30, 'F');
doc.setFont('helvetica', 'bold');
doc.setFontSize(22);
doc.setTextColor(255, 255, 255);
doc.text(`Discount Schedule for ${currentYear}`, 15, 18);
if (scheduledCampaigns.length === 0) {
doc.setFontSize(12);
doc.setTextColor(100);
doc.text("No discounts have been scheduled for this year.", 15, 45);
} else {
const sorted = [...scheduledCampaigns].sort((a,b) => new Date(a.start) - new Date(b.start));
const tableBody = sorted.map(s => {
const type = campaignTypes.find(c => c.id === s.typeId);
return [
type ? type.name : 'Unknown',
type ? `${type.discount}%` : 'N/A',
new Date(s.start).toLocaleDateString(),
new Date(s.end).toLocaleDateString()
];
});
doc.autoTable({
startY: 40,
head: [['Campaign Name', 'Discount', 'Start Date', 'End Date']],
body: tableBody,
theme: 'grid',
headStyles: { fillColor: [249, 115, 22] } // Orange 500
});
}
doc.setFontSize(10);
doc.setTextColor(150);
doc.text(`Report generated on: ${date}`, 15, doc.internal.pageSize.getHeight() - 10);
doc.save(`discount_schedule_${currentYear}.pdf`);
}
// --- EVENT LISTENERS ---
yearSelector.addEventListener('change', () => {
year = parseInt(yearSelector.value) || new Date().getFullYear();
generateCalendar(year);
});
addCampaignTypeBtn.addEventListener('click', () => openCampaignModal());
modalSaveBtn.addEventListener('click', saveCampaignType);
modalCancelBtn.addEventListener('click', closeCampaignModal);
campaignTypeList.addEventListener('click', (e) => {
const editBtn = e.target.closest('.edit-type-btn');
const deleteBtn = e.target.closest('.delete-type-btn');
if (editBtn) {
const campaign = campaignTypes.find(c => c.id == editBtn.dataset.id);
if (campaign) openCampaignModal(campaign);
}
if (deleteBtn) {
deleteCampaignType(deleteBtn.dataset.id);
}
});
// This is a bit of a trick for dynamic content. We can have a button on the calendar to trigger scheduling.
// For now, let's add a button in the scheduled list header
const scheduleBtnHtml = ``;
document.querySelector('#scheduled-list').parentElement.querySelector('h3').style.position = 'relative';
document.querySelector('#scheduled-list').parentElement.querySelector('h3').insertAdjacentHTML('beforeend', scheduleBtnHtml);
document.getElementById('open-schedule-modal-btn').addEventListener('click', openScheduleModal);
scheduleAddBtn.addEventListener('click', addScheduledCampaign);
scheduleCancelBtn.addEventListener('click', closeScheduleModal);
scheduledList.addEventListener('click', (e) => {
const deleteBtn = e.target.closest('.delete-scheduled-btn');
if(deleteBtn) {
deleteScheduledCampaign(deleteBtn.dataset.id);
}
});
pdfBtn.addEventListener('click', downloadPDF);
// --- INITIALIZATION ---
function initialize() {
// Pre-populate with sample USA data
campaignTypes = [
{ id: 1, name: 'Presidents\' Day Sale', discount: 15, color: '#3b82f6' },
{ id: 2, name: 'Summer Kickoff', discount: 20, color: '#f97316' },
{ id: 3, name: 'Black Friday', discount: 50, color: '#1f2937' },
{ id: 4, name: 'Holiday Special', discount: 25, color: '#dc2626' }
];
scheduledCampaigns = [
{ id: 101, typeId: 1, start: '2025-02-14', end: '2025-02-17' },
{ id: 102, typeId: 2, start: '2025-05-23', end: '2025-05-31' },
{ id: 103, typeId: 3, start: '2025-11-28', end: '2025-11-30' },
{ id: 104, typeId: 4, start: '2025-12-15', end: '2025-12-24' },
];
yearSelector.value = year;
renderAll();
}
initialize();
});
${campaignType.name} (${campaignType.discount}%)
${day}
${campaignBarsHTML}
`;
}
monthHTML += `