@gov-cy/govcy-express-services
Version:
An Express-based system that dynamically renders services using @gov-cy/govcy-frontend-renderer and posts data to a submission API.
233 lines (205 loc) β’ 9.66 kB
JavaScript
import * as govcyResources from "../resources/govcyResources.mjs";
import * as dataLayer from "../utils/govcyDataLayer.mjs";
import { logger } from "../utils/govcyLogger.mjs";
import { handleMiddlewareError } from "../utils/govcyUtils.mjs";
import { getPageConfigData } from "../utils/govcyLoadConfigData.mjs";
import { evaluatePageConditions } from "../utils/govcyExpressions.mjs";
import { whatsIsMyEnvironment } from '../utils/govcyEnvVariables.mjs';
import { tempSaveIfConfigured } from "../utils/govcyTempSave.mjs";
import nunjucks from "nunjucks";
import { URL } from "url";
/**
* Middleware to show a confirmation page before deleting a multipleThings item.
*/
export function govcyMultipleThingsDeletePageHandler() {
return (req, res, next) => {
try {
const { siteId, pageUrl, index } = req.params;
const serviceCopy = req.serviceData;
const page = getPageConfigData(serviceCopy, pageUrl);
// --- Sanity checks ---
const mtConfig = page.multipleThings;
if (!mtConfig) {
return handleMiddlewareError(`π¨ multipleThings config not found for ${siteId}/${pageUrl}`, 404, next);
}
if (!mtConfig.listPage || !mtConfig.listPage.title) {
return handleMiddlewareError(`π¨ multipleThings.listPage.title is required for ${siteId}/${pageUrl}`, 404, next);
}
if (!mtConfig.itemTitleTemplate || !mtConfig.min === undefined || !mtConfig.min === null || !mtConfig.max) {
return handleMiddlewareError(`π¨ multipleThings.itemTitleTemplate, .min and .max are required for ${siteId}/${pageUrl}`, 404, next);
}
// --- Conditions ---
const conditionResult = evaluatePageConditions(page, req.session, siteId, req);
if (conditionResult.result === false) {
logger.debug("βοΈ Page condition evaluated to true on GET β skipping and redirecting:", conditionResult);
return res.redirect(govcyResources.constructPageUrl(siteId, conditionResult.redirect));
}
// --- Validate index ---
let items = dataLayer.getPageData(req.session, siteId, pageUrl);
if (!Array.isArray(items)) items = [];
const idx = parseInt(index, 10);
if (Number.isNaN(idx) || idx < 0 || idx >= items.length) {
return handleMiddlewareError(`π¨ multipleThings delete index not found for ${siteId}/${pageUrl} (index=${index})`, 404, next);
}
const item = items[idx];
// --- Build page title ---
// Weβll use the itemTitleTemplate to render the item title
const env = new nunjucks.Environment(null, { autoescape: false });
const itemTitle = env.renderString(mtConfig.itemTitleTemplate, item);
// Base page template
let pageTemplate = {
sections: [
{
name: "beforeMain",
elements: [govcyResources.staticResources.elements.backLink]
}
]
};
// Deep copy page title (so we donβt mutate template)
let pageTitle = JSON.parse(JSON.stringify(govcyResources.staticResources.text.multipleThingsDeleteTitle));
// Replace label placeholders on page title
for (const lang of Object.keys(pageTitle)) {
pageTitle[lang] = pageTitle[lang].replace("{{item}}", itemTitle);
}
// Radios for confirmation
const pageRadios = {
element: "radios",
params: {
id: "deleteItem",
name: "deleteItem",
legend: pageTitle,
isPageHeading: true,
classes: "govcy-mb-6",
items: [
{ value: "yes", text: govcyResources.staticResources.text.multipleThingsDeleteYesOption },
{ value: "no", text: govcyResources.staticResources.text.multipleThingsDeleteNoOption }
]
}
};
// Form element
const formElement = {
element: "form",
params: {
action: govcyResources.constructPageUrl(siteId, `${pageUrl}/multiple/delete/${index}`, req.query.route === "review" ? "review" : ""),
method: "POST",
elements: [
pageRadios,
{
element: "button",
params: {
type: "submit",
text: govcyResources.staticResources.text.continue
}
},
govcyResources.csrfTokenInput(req.csrfToken())
]
}
};
// --------- Handle Validation Errors ---------
// Add validation errors if present
const validationErrors = [];
let mainElements = [];
if (req?.query?.hasError) {
validationErrors.push({
link: "#deleteItem-option-1",
text: govcyResources.staticResources.text.multipleThingsDeleteValidationError
});
mainElements.push(govcyResources.errorSummary(validationErrors));
formElement.params.elements[0].params.error = govcyResources.staticResources.text.multipleThingsDeleteValidationError;
}
//--------- End Handle Validation Errors ---------
mainElements.push(formElement);
pageTemplate.sections.push({ name: "main", elements: mainElements });
const pageData = JSON.parse(JSON.stringify(govcyResources.staticResources.rendererPageData));
// Handle isTesting
pageData.site.isTesting = (whatsIsMyEnvironment() === "staging");
pageData.site = serviceCopy.site;
pageData.pageData.title = pageTitle;
req.processedPage = { pageData, pageTemplate };
logger.debug("Processed delete item page data:", req.processedPage);
next();
} catch (error) {
return next(error);
}
};
}
/**
* Middleware to handle delete item POST for multipleThings
*/
export function govcyMultipleThingsDeletePostHandler() {
return (req, res, next) => {
try {
const { siteId, pageUrl, index } = req.params;
const service = req.serviceData;
const page = getPageConfigData(service, pageUrl);
// --- Sanity checks ---
const mtConfig = page.multipleThings;
if (!mtConfig) {
return handleMiddlewareError(
`π¨ multipleThings config not found for ${siteId}/${pageUrl}`,
404,
next
);
}
// --- Conditions ---
const conditionResult = evaluatePageConditions(page, req.session, siteId, req);
if (conditionResult.result === false) {
logger.debug(
"βοΈ Page condition evaluated to true on POST β skipping and redirecting:",
conditionResult
);
return res.redirect(
govcyResources.constructPageUrl(siteId, conditionResult.redirect)
);
}
// --- Validate index ---
let items = dataLayer.getPageData(req.session, siteId, pageUrl);
if (!Array.isArray(items)) items = [];
const idx = parseInt(index, 10);
if (Number.isNaN(idx) || idx < 0 || idx >= items.length) {
return handleMiddlewareError(
`π¨ multipleThings delete index not found for ${siteId}/${pageUrl} (index=${index})`,
404,
next
);
}
// --- Validate form value ---
if (
!req?.body?.deleteItem ||
(req.body.deleteItem !== "yes" && req.body.deleteItem !== "no")
) {
logger.debug(
"βοΈ No deleteItem value provided on POST β redirecting with error:",
req.body
);
const pageBaseReturnUrl = `http://localhost:3000/${siteId}/${pageUrl}/multiple/delete/${index}`;
let myUrl = new URL(pageBaseReturnUrl);
if (req.query.route === "review") {
myUrl.searchParams.set("route", "review");
}
myUrl.searchParams.set("hasError", "1");
return res.redirect(
govcyResources.constructErrorSummaryUrl(myUrl.pathname + myUrl.search)
);
}
// --- Handle deletion ---
if (req.body.deleteItem === "yes") {
items.splice(idx, 1); // remove the item
dataLayer.storePageData(req.session, siteId, pageUrl, items);
logger.info(`Item deleted by user`, { siteId, pageUrl, index });
//Temp save
(async () => { try { await tempSaveIfConfigured(req.session, service, siteId); } catch (e) { } })();
}
// --- Redirect back to the hub ---
const hubUrl = govcyResources.constructPageUrl(
siteId,
pageUrl,
req.query.route === "review" ? "review" : ""
);
return res.redirect(hubUrl);
} catch (error) {
logger.error("Error in govcyMultipleThingsDeletePostHandler middleware:", error);
return next(error);
}
};
}