← back to Marketing Command Center
modules/calendar/index.js
49 lines
// Marketing Calendar module — curated retail/holiday/awareness dates for a luxury
// wallcoverings house + a lightweight campaign-schedule store. Self-contained per
// the MODULE CONTRACT (see README.md). No outside sends here — purely planning.
// Date math + JSON persistence live in ./store.js (shared with the unified
// "All Calendars" module) so there is one source of truth for the marketing layer.
const { pad, datesForMonth, loadSchedule, saveSchedule, upsertCampaign } = require('./store');
module.exports = {
id: 'calendar',
title: 'Marketing Calendar',
icon: '🗓️',
mount(router) {
// GET /dates?year=YYYY&month=MM — curated marketing moments for the window
router.get('/dates', (req, res) => {
const now = new Date();
let year = parseInt(req.query.year, 10);
let month = parseInt(req.query.month, 10);
if (!Number.isInteger(year)) year = now.getFullYear();
if (!Number.isInteger(month) || month < 1 || month > 12) month = now.getMonth() + 1;
res.json({ year, month, dates: datesForMonth(year, month) });
});
// GET /schedule — all planned campaigns (optionally filter by year/month)
router.get('/schedule', (req, res) => {
let list = loadSchedule();
const ym = req.query.month && req.query.year
? `${req.query.year}-${pad(parseInt(req.query.month, 10))}`
: null;
if (ym) list = list.filter(c => (c.date || '').startsWith(ym));
list.sort((a, b) => (a.date || '').localeCompare(b.date || ''));
res.json({ schedule: list });
});
// POST /schedule — add/update one campaign; ?delete=id removes one
router.post('/schedule', (req, res) => {
if (req.query.delete) {
const id = String(req.query.delete);
const next = loadSchedule().filter(c => c.id !== id);
saveSchedule(next);
return res.json({ ok: true, deleted: id, schedule: next });
}
const r = upsertCampaign(req.body || {});
if (!r.ok) return res.status(400).json(r);
res.json(r);
});
},
};