forked from UKSOURCE/cms.lams
fix
This commit is contained in:
@@ -4,6 +4,45 @@ const Programme = require("../models/programme");
|
||||
const writeAuditLog = require("../audit/writeAuditLog");
|
||||
const diffObject = require("../audit/diffObject");
|
||||
const AUDIT_ACTIONS = require("../constants/auditAction");
|
||||
const { readJsonFile, writeJsonFile } = require("../utils/jsonHelper");
|
||||
const DEFAULT_PROGRAMME_FIELDS = [
|
||||
"Computer Science",
|
||||
"Data Analytics",
|
||||
"Business & Admin",
|
||||
"Healthcare",
|
||||
"Finance & Accounting",
|
||||
"Marketing",
|
||||
"Education",
|
||||
"Engineering",
|
||||
"Psychology",
|
||||
"Cybersecurity",
|
||||
];
|
||||
const DEFAULT_DURATION_RANGES = [
|
||||
"< 6 months",
|
||||
"6-12 months",
|
||||
"12-24 months",
|
||||
"24-48 months",
|
||||
"48+ months",
|
||||
];
|
||||
const DEFAULT_DEGREE_LEVELS = ["Bachelor's", "Master's"];
|
||||
|
||||
function readProgrammeFiltersConfig() {
|
||||
const saved = readJsonFile("programme-filters");
|
||||
return {
|
||||
levels:
|
||||
saved && Array.isArray(saved.levels) && saved.levels.length > 0
|
||||
? saved.levels
|
||||
: DEFAULT_DEGREE_LEVELS,
|
||||
fields:
|
||||
saved && Array.isArray(saved.fields) && saved.fields.length > 0
|
||||
? saved.fields
|
||||
: DEFAULT_PROGRAMME_FIELDS,
|
||||
durations:
|
||||
saved && Array.isArray(saved.durations) && saved.durations.length > 0
|
||||
? saved.durations
|
||||
: DEFAULT_DURATION_RANGES,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize all icon fields in the payload against the ICON_OPTIONS whitelist.
|
||||
@@ -69,15 +108,24 @@ exports.edit = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
let programme;
|
||||
const filtersConfig = readProgrammeFiltersConfig();
|
||||
const levelOptions = Array.isArray(filtersConfig.levels)
|
||||
? [...filtersConfig.levels]
|
||||
: [];
|
||||
const durationOptions = Array.isArray(filtersConfig.durations)
|
||||
? [...filtersConfig.durations]
|
||||
: [];
|
||||
|
||||
if (id === "new") {
|
||||
programme = {
|
||||
_id: null,
|
||||
id: "",
|
||||
level: "",
|
||||
fieldOfStudy: "",
|
||||
title: "",
|
||||
description: "",
|
||||
duration: "",
|
||||
durationInMonths: 0,
|
||||
cost: "",
|
||||
selected: false,
|
||||
link: "",
|
||||
@@ -103,11 +151,19 @@ exports.edit = async (req, res) => {
|
||||
req.flash("error_msg", "Programme not found");
|
||||
return res.redirect("/admin/programme");
|
||||
}
|
||||
if (programme.level && !levelOptions.includes(programme.level)) {
|
||||
levelOptions.push(programme.level);
|
||||
}
|
||||
if (programme.duration && !durationOptions.includes(programme.duration)) {
|
||||
durationOptions.push(programme.duration);
|
||||
}
|
||||
}
|
||||
|
||||
res.render("admin/programme/edit", {
|
||||
title: id === "new" ? "Add New Programme" : `Edit: ${programme.title}`,
|
||||
programme,
|
||||
levelOptions,
|
||||
durationOptions,
|
||||
iconOptions: ICON_OPTIONS,
|
||||
frontendUrl: process.env.FRONTEND_URL || "",
|
||||
currentPath: req.path,
|
||||
@@ -145,7 +201,9 @@ exports.update = async (req, res) => {
|
||||
title: (body.title || "").trim(),
|
||||
description: (body.description || "").trim(),
|
||||
duration: (body.duration || "").trim(),
|
||||
durationInMonths: parseInt(body.durationInMonths, 10) || 0,
|
||||
cost: (body.cost || "").trim(),
|
||||
fieldOfStudy: (body.fieldOfStudy || "").trim(),
|
||||
selected: body.selected === "true" || body.selected === true,
|
||||
link: (body.link || "").trim(),
|
||||
shortName: (body.shortName || "").trim(),
|
||||
@@ -272,6 +330,98 @@ exports.api = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* GET /api/programmes/filters
|
||||
* Return filter options for FE (field of study + duration ranges + levels).
|
||||
*/
|
||||
exports.apiFilters = async (req, res) => {
|
||||
try {
|
||||
const data = await Programme.find().lean();
|
||||
const cfg = readProgrammeFiltersConfig();
|
||||
|
||||
const normalize = (value) =>
|
||||
String(value || "")
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]/g, "");
|
||||
const levels = cfg.levels.map((label) => {
|
||||
const normalizedLabel = normalize(label);
|
||||
const count = data.filter((p) =>
|
||||
normalize(p.level).includes(normalizedLabel)
|
||||
).length;
|
||||
return { label, count };
|
||||
});
|
||||
|
||||
const fieldsMap = new Map(cfg.fields.map((f) => [f, 0]));
|
||||
data.forEach((p) => {
|
||||
if (p.fieldOfStudy && fieldsMap.has(p.fieldOfStudy)) {
|
||||
fieldsMap.set(p.fieldOfStudy, (fieldsMap.get(p.fieldOfStudy) || 0) + 1);
|
||||
}
|
||||
});
|
||||
const fields = [...fieldsMap.entries()].map(([label, count]) => ({ label, count }));
|
||||
|
||||
res.json({
|
||||
levels,
|
||||
fields,
|
||||
durations: cfg.durations.map((label) => ({ label })),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("programme.apiFilters:", err);
|
||||
res.status(500).json({ error: "Error loading programme filters" });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* GET /admin/programme/filters
|
||||
* CMS page to manage programme filter options centrally.
|
||||
*/
|
||||
exports.filtersPage = async (req, res) => {
|
||||
try {
|
||||
const cfg = readProgrammeFiltersConfig();
|
||||
res.render("admin/programme/filters", {
|
||||
title: "Programme Filters",
|
||||
filters: cfg,
|
||||
frontendUrl: process.env.FRONTEND_URL || "",
|
||||
currentPath: req.path,
|
||||
user: req.session.user,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("programme.filtersPage:", err);
|
||||
req.flash("error_msg", "Error loading programme filters");
|
||||
res.redirect("/admin/programme");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* POST /admin/programme/filters
|
||||
* Save centralized programme filters.
|
||||
*/
|
||||
exports.updateFilters = async (req, res) => {
|
||||
try {
|
||||
const normalizeLines = (text) =>
|
||||
String(text || "")
|
||||
.split(/\r?\n/)
|
||||
.map((x) => x.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const levels = normalizeLines(req.body.levels);
|
||||
const fields = normalizeLines(req.body.fields);
|
||||
const durations = normalizeLines(req.body.durations);
|
||||
|
||||
if (!levels.length || !fields.length || !durations.length) {
|
||||
req.flash("error_msg", "Degree levels, fields and duration ranges cannot be empty.");
|
||||
return res.redirect("/admin/programme/filters");
|
||||
}
|
||||
|
||||
writeJsonFile("programme-filters", { levels, fields, durations });
|
||||
req.flash("success_msg", "Programme filters updated successfully.");
|
||||
res.redirect("/admin/programme/filters");
|
||||
} catch (err) {
|
||||
console.error("programme.updateFilters:", err);
|
||||
req.flash("error_msg", "Error updating programme filters.");
|
||||
res.redirect("/admin/programme/filters");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* GET /api/programmes/:id
|
||||
* Return a single programme matched by the slug `id` field.
|
||||
|
||||
Reference in New Issue
Block a user