@gov-cy/govcy-express-services 1.0.0-alpha.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1024 -74
- package/package.json +9 -3
- package/src/auth/cyLoginAuth.mjs +2 -1
- package/src/index.mjs +13 -2
- package/src/middleware/cyLoginAuth.mjs +3 -1
- package/src/middleware/govcyFileDeleteHandler.mjs +320 -0
- package/src/middleware/{govcyUpload.mjs → govcyFileUpload.mjs} +1 -1
- package/src/middleware/govcyFileViewHandler.mjs +161 -0
- package/src/middleware/govcyPDFRender.mjs +3 -1
- package/src/middleware/govcyPageHandler.mjs +3 -6
- package/src/middleware/govcyPageRender.mjs +10 -0
- package/src/middleware/govcyReviewPageHandler.mjs +4 -1
- package/src/middleware/govcyReviewPostHandler.mjs +1 -1
- package/src/middleware/govcySuccessPageHandler.mjs +2 -3
- package/src/public/js/govcyFiles.js +201 -77
- package/src/public/js/govcyForms.js +19 -8
- package/src/resources/govcyResources.mjs +57 -7
- package/src/utils/govcyConstants.mjs +1 -1
- package/src/utils/govcyDataLayer.mjs +192 -14
- package/src/utils/govcyFormHandling.mjs +8 -4
- package/src/utils/govcyHandleFiles.mjs +23 -13
- package/src/utils/govcySubmitData.mjs +162 -109
|
@@ -17,7 +17,7 @@ import { logger } from "./govcyLogger.mjs";
|
|
|
17
17
|
export function prepareSubmissionData(req, siteId, service) {
|
|
18
18
|
// Get the raw data from the session store
|
|
19
19
|
// const rawData = dataLayer.getSiteInputData(req.session, siteId);
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
// ----- Conditional logic comes here
|
|
22
22
|
// Filter site input data based on active pages only
|
|
23
23
|
// const rawData = {};
|
|
@@ -32,7 +32,7 @@ export function prepareSubmissionData(req, siteId, service) {
|
|
|
32
32
|
// }
|
|
33
33
|
// }
|
|
34
34
|
|
|
35
|
-
// ----- consistent data model for
|
|
35
|
+
// ----- consistent data model for submissionData (CONFIG-BASED)
|
|
36
36
|
const submissionData = {};
|
|
37
37
|
|
|
38
38
|
// Loop through every page in the service definition
|
|
@@ -48,7 +48,8 @@ export function prepareSubmissionData(req, siteId, service) {
|
|
|
48
48
|
|
|
49
49
|
if (!formElement) continue; // ⛔ Skip pages without a <form> element
|
|
50
50
|
|
|
51
|
-
submissionData[pageUrl] = { formData: {} }; // ✅ Now initialize only if a form is present
|
|
51
|
+
// submissionData[pageUrl] = { formData: {} }; // ✅ Now initialize only if a form is present
|
|
52
|
+
submissionData[pageUrl] = {}; // ✅ Now initialize only if a form is present
|
|
52
53
|
|
|
53
54
|
// Traverse the form elements inside the form
|
|
54
55
|
for (const element of formElement.params.elements || []) {
|
|
@@ -64,13 +65,16 @@ export function prepareSubmissionData(req, siteId, service) {
|
|
|
64
65
|
const value = getValue(element, pageUrl, req, siteId) ?? "";
|
|
65
66
|
|
|
66
67
|
// Store in submissionData
|
|
67
|
-
submissionData[pageUrl].formData[elId] = value;
|
|
68
|
+
// submissionData[pageUrl].formData[elId] = value;
|
|
69
|
+
submissionData[pageUrl][elId] = value;
|
|
68
70
|
|
|
69
71
|
// handle fileInput
|
|
70
72
|
if (elType === "fileInput") {
|
|
71
73
|
// change the name of the key to include "Attachment" at the end but not have the original key
|
|
72
|
-
submissionData[pageUrl].formData[elId + "Attachment"] = value;
|
|
73
|
-
|
|
74
|
+
// submissionData[pageUrl].formData[elId + "Attachment"] = value;
|
|
75
|
+
submissionData[pageUrl][elId + "Attachment"] = value;
|
|
76
|
+
// delete submissionData[pageUrl].formData[elId];
|
|
77
|
+
delete submissionData[pageUrl][elId];
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
// 🔄 If radios with conditionalElements, walk ALL options
|
|
@@ -90,12 +94,15 @@ export function prepareSubmissionData(req, siteId, service) {
|
|
|
90
94
|
const condValue = getValue(condElement, pageUrl, req, siteId) ?? "";
|
|
91
95
|
|
|
92
96
|
// Store even if the field was not visible to user
|
|
93
|
-
submissionData[pageUrl].formData[condId] = condValue;
|
|
97
|
+
// submissionData[pageUrl].formData[condId] = condValue;
|
|
98
|
+
submissionData[pageUrl][condId] = condValue;
|
|
94
99
|
// handle fileInput
|
|
95
100
|
if (condType === "fileInput") {
|
|
96
101
|
// change the name of the key to include "Attachment" at the end but not have the original key
|
|
97
|
-
submissionData[pageUrl].formData[condId + "Attachment"] = condValue;
|
|
98
|
-
|
|
102
|
+
// submissionData[pageUrl].formData[condId + "Attachment"] = condValue;
|
|
103
|
+
submissionData[pageUrl][condId + "Attachment"] = condValue;
|
|
104
|
+
// delete submissionData[pageUrl].formData[condId];
|
|
105
|
+
delete submissionData[pageUrl][condId];
|
|
99
106
|
}
|
|
100
107
|
}
|
|
101
108
|
}
|
|
@@ -103,7 +110,7 @@ export function prepareSubmissionData(req, siteId, service) {
|
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
112
|
logger.debug("Submission Data prepared:", submissionData);
|
|
106
|
-
// ----- END config-based stable
|
|
113
|
+
// ----- END config-based stable submissionData block
|
|
107
114
|
|
|
108
115
|
// Get the print-friendly data from the session store
|
|
109
116
|
const printFriendlyData = preparePrintFriendlyData(req, siteId, service);
|
|
@@ -112,14 +119,14 @@ export function prepareSubmissionData(req, siteId, service) {
|
|
|
112
119
|
const reviewSummaryList = generateReviewSummary(printFriendlyData, req, siteId, false);
|
|
113
120
|
// Prepare the submission data object
|
|
114
121
|
return {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
submissionUsername: dataLayer.getUser(req.session).name,
|
|
123
|
+
submissionEmail: dataLayer.getUser(req.session).email,
|
|
124
|
+
submissionData: submissionData, // Raw data as submitted by the user in each page
|
|
125
|
+
submissionDataVersion: service.site?.submission_data_version || "", // The submission data version
|
|
126
|
+
printFriendlyData: printFriendlyData, // Print-friendly data
|
|
127
|
+
rendererData: reviewSummaryList, // Renderer data of the summary list
|
|
128
|
+
rendererVersion: service.site?.renderer_version || "", // The renderer version
|
|
129
|
+
designSystemsVersion: service.site?.design_systems_version || "", // The design systems version
|
|
123
130
|
service: { // Service info
|
|
124
131
|
id: service.site.id, // Service ID
|
|
125
132
|
title: service.site.title // Service title multilingual object
|
|
@@ -136,16 +143,16 @@ export function prepareSubmissionData(req, siteId, service) {
|
|
|
136
143
|
* @returns {object} The API-ready submission data object with all fields as strings
|
|
137
144
|
*/
|
|
138
145
|
export function prepareSubmissionDataAPI(data) {
|
|
139
|
-
|
|
146
|
+
|
|
140
147
|
return {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
submissionUsername: String(data.submissionUsername ?? ""),
|
|
149
|
+
submissionEmail: String(data.submissionEmail ?? ""),
|
|
150
|
+
submissionData: JSON.stringify(data.submissionData ?? {}),
|
|
151
|
+
submissionDataVersion: String(data.submissionDataVersion ?? ""),
|
|
152
|
+
printFriendlyData: JSON.stringify(data.printFriendlyData ?? []),
|
|
153
|
+
rendererData: JSON.stringify(data.rendererData ?? {}),
|
|
154
|
+
rendererVersion: String(data.rendererVersion ?? ""),
|
|
155
|
+
designSystemsVersion: String(data.designSystemsVersion ?? ""),
|
|
149
156
|
service: JSON.stringify(data.service ?? {})
|
|
150
157
|
};
|
|
151
158
|
}
|
|
@@ -225,7 +232,7 @@ export function preparePrintFriendlyData(req, siteId, service) {
|
|
|
225
232
|
}
|
|
226
233
|
}
|
|
227
234
|
|
|
228
|
-
return submissionData
|
|
235
|
+
return submissionData;
|
|
229
236
|
}
|
|
230
237
|
|
|
231
238
|
//------------------------------- Helper Functions -------------------------------//
|
|
@@ -310,6 +317,19 @@ function getValue(formElement, pageUrl, req, siteId) {
|
|
|
310
317
|
} else {
|
|
311
318
|
value = dataLayer.getFormDataValue(req.session, siteId, pageUrl, formElement.params.name);
|
|
312
319
|
}
|
|
320
|
+
|
|
321
|
+
// 🔁 Normalize checkboxes: always return an array
|
|
322
|
+
if (formElement.element === "checkboxes") {
|
|
323
|
+
// If no value, return empty array
|
|
324
|
+
if (value == null || value === "") return [];
|
|
325
|
+
// If already an array, return as-is (but strip empties just in case)
|
|
326
|
+
if (Array.isArray(value)) {
|
|
327
|
+
// Strip empties just in case
|
|
328
|
+
return value.filter(v => v != null && v !== "");
|
|
329
|
+
}
|
|
330
|
+
// Else single value, convert to array
|
|
331
|
+
return [String(value)];
|
|
332
|
+
}
|
|
313
333
|
return value;
|
|
314
334
|
}
|
|
315
335
|
|
|
@@ -437,7 +457,34 @@ export function generateReviewSummary(submissionData, req, siteId, showChangeLin
|
|
|
437
457
|
};
|
|
438
458
|
}
|
|
439
459
|
|
|
440
|
-
|
|
460
|
+
/**
|
|
461
|
+
* Helper function to create a summary list item for file links.
|
|
462
|
+
* @param {object} key the key of multilingual object
|
|
463
|
+
* @param {string} value the value
|
|
464
|
+
* @param {string} siteId the site id
|
|
465
|
+
* @param {string} pageUrl the page url
|
|
466
|
+
* @param {string} elementName the element name
|
|
467
|
+
* @returns {object} the summary list item with file link
|
|
468
|
+
*/
|
|
469
|
+
function createSummaryListItemFileLink(key, value, siteId, pageUrl, elementName) {
|
|
470
|
+
return {
|
|
471
|
+
"key": key,
|
|
472
|
+
"value": [
|
|
473
|
+
{
|
|
474
|
+
"element": "htmlElement",
|
|
475
|
+
"params": {
|
|
476
|
+
"text": {
|
|
477
|
+
"en": `<a href="/${siteId}/${pageUrl}/view-file/${elementName}" target="_blank">${govcyResources.staticResources.text.viewFile.en}<span class="govcy-visually-hidden"> ${key?.en || ""}</span></a>`,
|
|
478
|
+
"el": `<a href="/${siteId}/${pageUrl}/view-file/${elementName}" target="_blank">${govcyResources.staticResources.text.viewFile.el}<span class="govcy-visually-hidden"> ${key?.el || ""}</span></a>`,
|
|
479
|
+
"tr": `<a href="/${siteId}/${pageUrl}/view-file/${elementName}" target="_blank">${govcyResources.staticResources.text.viewFile.tr}<span class="govcy-visually-hidden"> ${key?.tr || ""}</span></a>`
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
]
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
|
|
441
488
|
|
|
442
489
|
|
|
443
490
|
// Loop through each page in the submission data
|
|
@@ -452,8 +499,14 @@ export function generateReviewSummary(submissionData, req, siteId, showChangeLin
|
|
|
452
499
|
for (const field of fields) {
|
|
453
500
|
const label = field.label;
|
|
454
501
|
const valueLabel = getSubmissionValueLabelString(field.valueLabel, req.globalLang);
|
|
455
|
-
//
|
|
456
|
-
|
|
502
|
+
// --- HACK --- to see if this is a file element
|
|
503
|
+
// check if field.value is an object with `sha256` and `fileId` properties
|
|
504
|
+
if (typeof field.value === "object" && field.value.hasOwnProperty("sha256") && field.value.hasOwnProperty("fileId") && showChangeLinks) {
|
|
505
|
+
summaryListInner.params.items.push(createSummaryListItemFileLink(label, valueLabel, siteId, pageUrl, field.name));
|
|
506
|
+
} else {
|
|
507
|
+
// add the field to the summary entry
|
|
508
|
+
summaryListInner.params.items.push(createSummaryListItem(label, valueLabel));
|
|
509
|
+
}
|
|
457
510
|
}
|
|
458
511
|
|
|
459
512
|
// Add inner summary list to the main summary list
|
|
@@ -510,7 +563,7 @@ export function generateSubmitEmail(service, submissionData, submissionId, req)
|
|
|
510
563
|
}
|
|
511
564
|
);
|
|
512
565
|
}
|
|
513
|
-
|
|
566
|
+
|
|
514
567
|
// Add data title to the body
|
|
515
568
|
body.push(
|
|
516
569
|
{
|
|
@@ -528,7 +581,7 @@ export function generateSubmitEmail(service, submissionData, submissionId, req)
|
|
|
528
581
|
body.push(
|
|
529
582
|
{
|
|
530
583
|
component: "bodyHeading",
|
|
531
|
-
params: {"headingLevel":2},
|
|
584
|
+
params: { "headingLevel": 2 },
|
|
532
585
|
body: govcyResources.getLocalizeContent(pageTitle, req.globalLang)
|
|
533
586
|
}
|
|
534
587
|
);
|
|
@@ -538,14 +591,14 @@ export function generateSubmitEmail(service, submissionData, submissionId, req)
|
|
|
538
591
|
for (const field of fields) {
|
|
539
592
|
const label = govcyResources.getLocalizeContent(field.label, req.globalLang);
|
|
540
593
|
const valueLabel = getSubmissionValueLabelString(field.valueLabel, req.globalLang);
|
|
541
|
-
dataUl.push({key: label, value: valueLabel});
|
|
594
|
+
dataUl.push({ key: label, value: valueLabel });
|
|
542
595
|
}
|
|
543
596
|
// add data to the body
|
|
544
597
|
body.push(
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
598
|
+
{
|
|
599
|
+
component: "bodyKeyValue",
|
|
600
|
+
params: { type: "ul", items: dataUl },
|
|
601
|
+
});
|
|
549
602
|
|
|
550
603
|
}
|
|
551
604
|
|
|
@@ -566,84 +619,84 @@ export function generateSubmitEmail(service, submissionData, submissionId, req)
|
|
|
566
619
|
|
|
567
620
|
|
|
568
621
|
|
|
569
|
-
|
|
622
|
+
/*
|
|
570
623
|
{
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
624
|
+
"bank-details": {
|
|
625
|
+
"formData": {
|
|
626
|
+
"AccountName": "asd",
|
|
627
|
+
"Iban": "CY12 0020 0123 0000 0001 2345 6789",
|
|
628
|
+
"Swift": "BANKCY2NXXX",
|
|
629
|
+
"_csrf": "sjknv79rxjgv0uggo0d5312vzgz37jsh"
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
"answer-bank-boc": {
|
|
633
|
+
"formData": {
|
|
634
|
+
"Objection": "Object",
|
|
635
|
+
"country": "Azerbaijan",
|
|
636
|
+
"ObjectionReason": "ObjectionReasonCode1",
|
|
637
|
+
"ObjectionExplanation": "asdsa",
|
|
638
|
+
"DepositsBOCAttachment": "",
|
|
639
|
+
"_csrf": "sjknv79rxjgv0uggo0d5312vzgz37jsh"
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
"bank-settlement": {
|
|
643
|
+
"formData": {
|
|
644
|
+
"ReceiveSettlementExplanation": "",
|
|
645
|
+
"ReceiveSettlementDate_day": "",
|
|
646
|
+
"ReceiveSettlementDate_month": "",
|
|
647
|
+
"ReceiveSettlementDate_year": "",
|
|
648
|
+
"ReceiveSettlement": "no",
|
|
649
|
+
"_csrf": "sjknv79rxjgv0uggo0d5312vzgz37jsh"
|
|
650
|
+
}
|
|
651
|
+
}
|
|
599
652
|
}
|
|
600
653
|
|
|
601
654
|
|
|
602
655
|
|
|
603
656
|
[
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
657
|
+
{
|
|
658
|
+
pageUrl: "personal-details",
|
|
659
|
+
pageTitle: { en: "Personal data", el: "Προσωπικά στοιχεία" }, // from pageData.title in correct language
|
|
660
|
+
fields: [
|
|
661
|
+
[
|
|
662
|
+
{
|
|
663
|
+
id: "firstName",
|
|
664
|
+
label: { en: "First Name", el: "Όνομα" },
|
|
665
|
+
value: "John", // The actual user input value
|
|
666
|
+
valueLabel: { en: "John", el: "John" } // Same label as the value for text inputs
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
id: "lastName",
|
|
670
|
+
label: { en: "Last Name", el: "Επίθετο" },
|
|
671
|
+
value: "Doe", // The actual user input value
|
|
672
|
+
valueLabel: { en: "Doe", el: "Doe" } // Same label as the value for text inputs
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
id: "gender",
|
|
676
|
+
label: { en: "Gender", el: "Φύλο" },
|
|
677
|
+
value: "m", // The actual value ("male")
|
|
678
|
+
valueLabel: { en: "Male", el: "Άντρας" } // The corresponding label for "male"
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
id: "languages",
|
|
682
|
+
label: { en: "Languages", el: "Γλώσσες" },
|
|
683
|
+
value: ["en", "el"], // The selected values ["en", "el"]
|
|
684
|
+
valueLabel: [
|
|
685
|
+
{ en: "English", el: "Αγγλικά" }, // Labels corresponding to "en" and "el"
|
|
686
|
+
{ en: "Greek", el: "Ελληνικά" }
|
|
687
|
+
]
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
id: "birthDate",
|
|
691
|
+
label: { en: "Birth Date", el: "Ημερομηνία Γέννησης" },
|
|
692
|
+
value: "1990-01-13", // The actual value based on user input
|
|
693
|
+
valueLabel: "13/1/1990" // Date inputs label will be conveted to D/M/YYYY
|
|
694
|
+
}
|
|
695
|
+
]
|
|
696
|
+
},
|
|
697
|
+
...
|
|
645
698
|
]
|
|
646
699
|
|
|
647
700
|
|
|
648
701
|
|
|
649
|
-
|
|
702
|
+
*/
|