'use strict'; const mdl_NotificationTemplate = require('../../models/notifications/notification_template.mdl'); const R = require('../../utils/response.util'); const logActivity = require('../../utils/logActivity.util'); const slugify = (str) => str.toLowerCase().trim().replace(/[^a-z0-9]+/g, '_').replace(/(^_|_$)/g, ''); // ─── GET /admin/notification-templates ───────────────────────────────────────── exports.getNotificationTemplates = async (req, res) => { try { const templates = await mdl_NotificationTemplate.findAll({ order: [['notify_type', 'ASC'], ['type', 'ASC']], }); return R.success(res, 'Announcement templates retrieved.', templates); } catch (err) { console.error('[ADMIN][GET NOTIFICATION TEMPLATES]', err); return R.error(res, 'Could not retrieve announcement templates.', 500); } }; // ─── GET /admin/notification-templates/:id ───────────────────────────────────── exports.getNotificationTemplate = async (req, res) => { try { const template = await mdl_NotificationTemplate.findByPk(req.params.id); if (!template) return R.error(res, 'Announcement template not found.', 404); return R.success(res, 'Announcement template retrieved.', template); } catch (err) { console.error('[ADMIN][GET NOTIFICATION TEMPLATE]', err); return R.error(res, 'Could not retrieve announcement template.', 500); } }; // ─── POST /admin/notification-templates ──────────────────────────────────────── // Only creates custom (is_system: false) rows. System types still can't be // added here — they need a code call site (services/notificationTemplate // .service.js's renderNotification()) before a type means anything. Custom // rows have no call site at all: they're reusable title/message presets an // admin can load into the Announcements composer (see AddNotificationBroadcast // .jsx), so `type` only exists to satisfy the unique key — nothing looks it up. exports.createNotificationTemplate = async (req, res) => { try { const { label, title, message } = req.body; if (!label?.trim()) return R.error(res, 'label is required.', 400); if (!title?.trim()) return R.error(res, 'title is required.', 400); if (!message?.trim()) return R.error(res, 'message cannot be empty.', 400); const base = slugify(label) || 'template'; let type = `custom_${base}`; let suffix = 1; while (await mdl_NotificationTemplate.findOne({ where: { type } })) { suffix += 1; type = `custom_${base}_${suffix}`; } const template = await mdl_NotificationTemplate.create({ type, notify_type: 'announcement', scope: 'both', label: label.trim(), status: 'sent', title: title.trim(), message: message.trim(), is_system: false, }); logActivity(req.user?.user_id, 'create_notification_template', { entityType: 'notification_template', entityId: template.notification_template_id, details: { label: template.label } }); return R.success(res, 'Announcement template created.', template, 201); } catch (err) { console.error('[ADMIN][CREATE NOTIFICATION TEMPLATE]', err); return R.error(res, 'Could not create announcement template.', 500); } }; // ─── DELETE /admin/notification-templates/:id ────────────────────────────────── // System templates stay protected — deleting one would break the code call // site that references its type. exports.deleteNotificationTemplate = async (req, res) => { try { const template = await mdl_NotificationTemplate.findByPk(req.params.id); if (!template) return R.error(res, 'Announcement template not found.', 404); if (template.is_system) return R.error(res, 'System templates cannot be deleted.', 400); await template.destroy(); logActivity(req.user?.user_id, 'delete_notification_template', { entityType: 'notification_template', entityId: template.notification_template_id, details: { label: template.label } }); return R.success(res, 'Announcement template deleted.'); } catch (err) { console.error('[ADMIN][DELETE NOTIFICATION TEMPLATE]', err); return R.error(res, 'Could not delete announcement template.', 500); } }; // ─── PUT /admin/notification-templates/:id ───────────────────────────────────── exports.updateNotificationTemplate = async (req, res) => { try { const template = await mdl_NotificationTemplate.findByPk(req.params.id); if (!template) return R.error(res, 'Announcement template not found.', 404); const { label, title, message, publish } = req.body; if (title !== undefined && !title.trim()) return R.error(res, 'title cannot be empty.', 400); if (message !== undefined && !message.trim()) return R.error(res, 'message cannot be empty.', 400); // Custom templates are just reusable presets — nothing reads them at a // fixed publish time, so there's no draft/publish workflow: title/message // save straight to the live columns. if (!template.is_system) { await template.update({ label: label ?? template.label, title: title ?? template.title, message: message ?? template.message, }); logActivity(req.user?.user_id, 'update_notification_template', { entityType: 'notification_template', entityId: template.notification_template_id, details: { custom: true } }); return R.success(res, 'Announcement template updated.', template); } // "Publish" writes title/message straight to the live columns // renderNotification() reads and clears any pending draft. A plain save // (no publish flag) writes into draft_title/draft_message instead, so // real notifications keep using the last-published content until an // admin comes back and explicitly publishes again. const isPublishing = publish === true || publish === 'true'; const nextTitle = title ?? template.draft_title ?? template.title; const nextMessage = message ?? template.draft_message ?? template.message; await template.update({ label: label ?? template.label, ...(isPublishing ? { status: 'sent', title: nextTitle, message: nextMessage, draft_title: null, draft_message: null, last_sent_at: new Date(), } : { draft_title: nextTitle, draft_message: nextMessage, }), }); logActivity(req.user?.user_id, 'update_notification_template', { entityType: 'notification_template', entityId: template.notification_template_id, details: { type: template.type, published: isPublishing } }); return R.success(res, 'Announcement template updated.', template); } catch (err) { console.error('[ADMIN][UPDATE NOTIFICATION TEMPLATE]', err); return R.error(res, 'Could not update announcement template.', 500); } };