@gov-cy/govcy-express-services 1.0.0-alpha.7 → 1.0.0-alpha.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gov-cy/govcy-express-services",
3
- "version": "1.0.0-alpha.7",
3
+ "version": "1.0.0-alpha.8",
4
4
  "description": "An Express-based system that dynamically renders services using @gov-cy/govcy-frontend-renderer and posts data to a submission API.",
5
5
  "author": "DMRID - DSF Team",
6
6
  "license": "MIT",
@@ -28,7 +28,7 @@ export const govcyUploadMiddleware = [
28
28
 
29
29
  if (result.status !== 200) {
30
30
  logger.error("Upload failed", result);
31
- return res.status(result.status).json(errorResponse(result.status, result.errorMessage || 'File upload failed'));
31
+ return res.status(result.status).json(errorResponse(result.dataStatus, result.errorMessage || 'File upload failed'));
32
32
  }
33
33
 
34
34
  return res.json(successResponse(result.data));
@@ -1,10 +1,20 @@
1
1
  // 🔍 Select all file inputs that have the .govcy-file-upload class
2
- const fileInputs = document.querySelectorAll('input[type="file"].govcy-file-upload');
2
+ var fileInputs = document.querySelectorAll('input[type="file"].govcy-file-upload');
3
3
 
4
4
  // 🔁 Loop over each file input and attach a change event listener
5
- fileInputs.forEach(input => {
6
- input.addEventListener('change', async (event) => {
7
- const messages = {
5
+ fileInputs.forEach(function(input) {
6
+ input.addEventListener('change', _uploadFileEventHandler);
7
+ });
8
+
9
+
10
+ /**
11
+ * Handles the upload of a file event
12
+ *
13
+ * @param {object} event The event
14
+ */
15
+ async function _uploadFileEventHandler(event) {
16
+ var input = event.target;
17
+ var messages = {
8
18
  "uploadSuccesful": {
9
19
  "el": "Το αρχείο ανεβαστηκε",
10
20
  "en": "File uploaded successfully",
@@ -14,139 +24,152 @@ fileInputs.forEach(input => {
14
24
  "el": "Αποτυχια ανεβασης",
15
25
  "en": "File upload failed",
16
26
  "tr": "File upload failed"
27
+ },
28
+ "uploadFailed406": {
29
+ "el": "Το επιλεγμένο αρχείο είναι κενό",
30
+ "en": "The selected file is empty",
31
+ "tr": "The selected file is empty"
32
+ },
33
+ "uploadFailed407": {
34
+ "el": "Το επιλεγμένο αρχείο πρέπει να είναι JPG, JPEG, PNG ή PDF",
35
+ "en": "The selected file must be a JPG, JPEG, PNG or PDF",
36
+ "tr": "The selected file must be a JPG, JPEG, PNG or PDF"
37
+ },
38
+ "uploadFailed408": {
39
+ "el": "Το επιλεγμένο αρχείο πρέπει να είναι JPG, JPEG, PNG ή PDF",
40
+ "en": "The selected file must be a JPG, JPEG, PNG or PDF",
41
+ "tr": "The selected file must be a JPG, JPEG, PNG or PDF"
42
+ },
43
+ "uploadFailed409": {
44
+ "el": "Το επιλεγμένο αρχείο πρέπει να είναι μικρότερο από 5MB",
45
+ "en": "The selected file must be smaller than 5MB",
46
+ "tr": "The selected file must be smaller than 5MB"
17
47
  }
18
48
  };
19
49
  // 🔐 Get the CSRF token from a hidden input field (generated by your backend)
20
- const csrfToken = document.querySelector('input[type="hidden"][name="_csrf"]')?.value;
50
+ var csrfEl = document.querySelector('input[type="hidden"][name="_csrf"]');
51
+ var csrfToken = csrfEl ? csrfEl.value : '';
52
+
21
53
  // 🔧 Define siteId and pageUrl (you can dynamically extract these later)
22
- const siteId = window._govcySiteId || "";
23
- const pageUrl = window._govcyPageUrl || "";
24
- const lang = window._govcyLang || "el";
54
+ var siteId = window._govcySiteId || "";
55
+ var pageUrl = window._govcyPageUrl || "";
56
+ var lang = window._govcyLang || "el";
25
57
  // 📦 Grab the selected file
26
- const file = event.target.files[0];
27
- const elementName = input.name; // Form field's `name` attribute
58
+ var file = event.target.files[0];
59
+ var elementName = input.name; // Form field's `name` attribute
60
+ var elementId = input.id; // Form field's `id` attribute
28
61
 
29
62
  if (!file) return; // Exit if no file was selected
30
63
 
31
64
  // 🧵 Prepare form-data payload for the API
32
- const formData = new FormData();
65
+ var formData = new FormData();
33
66
  formData.append('file', file); // Attach the actual file
34
67
  formData.append('elementName', elementName); // Attach the field name for backend lookup
35
68
 
36
69
  try {
37
70
  // 🚀 Send file to the backend upload API
38
- const response = await axios.post(`/apis/${siteId}/${pageUrl}/upload`, formData, {
71
+ var response = await axios.post(`/apis/${siteId}/${pageUrl}/upload`, formData, {
39
72
  headers: {
40
73
  'X-CSRF-Token': csrfToken // 🔐 Pass CSRF token in custom header
41
74
  }
42
75
  });
43
76
 
44
- const { sha256, fileId } = response.data.Data;
77
+ var sha256 = response.data.Data.sha256;
78
+ var fileId = response.data.Data.fileId;
45
79
 
46
80
  // 📝 Store returned metadata in hidden fields for submission with the form
47
- document.querySelector(`[name="${elementName}Attachment[fileId]"`).value = fileId;
48
- document.querySelector(`[name="${elementName}Attachment[sha256]"`).value = sha256;
49
-
50
- // Success
51
- // Create an instance of GovcyFrontendRendererBrowser
52
- const renderer = new GovcyFrontendRendererBrowser();
53
- // Define the input data
54
- const inputData =
55
- {
56
- "site": {
57
- "lang": lang
58
- }
59
- };
60
-
61
- const fileInputMap = window._govcyFileInputs || {};
62
- let fileElement = fileInputMap[elementName];
63
- fileElement.element = "fileView";
64
- fileElement.params.fileId = fileId;
65
- fileElement.params.sha256 = sha256;
66
- fileElement.params.visuallyHiddenText = fileElement.params.label;
67
- fileElement.params.error = null;
68
- // TODO: Also need to set the `view` and `download` URLs
69
- fileElement.params.viewHref = "#viewHref";
70
- fileElement.params.deleteHref = "#deleteHref";
71
- // Construct the JSONTemplate
72
- const JSONTemplate = {
73
- "elements": [fileElement]
74
- };
75
-
76
- //render HTML into string
77
- let renderedHtml = renderer.renderFromJSON(JSONTemplate,inputData);
78
- // look for element with id `${elementName}-outer-control`
79
- // if not found look for element with id `${elementName}-input-control`
80
- // if not found look for element with id `${elementName}-view-control`
81
- var outerElement = document.getElementById(`${elementName}-outer-control`)
82
- || document.getElementById(`${elementName}-input-control`)
83
- || document.getElementById(`${elementName}-view-control`);
84
-
85
- if (outerElement) {
86
- //remove all classes from outerElement
87
- outerElement.className = "";
88
- //set the id of the outerElement to `${elementName}-outer-control`
89
- outerElement.id = `${elementName}-outer-control`;
90
- //update DOM and initialize the JS components
91
- renderer.updateDOMAndInitialize(`${elementName}-outer-control`, renderedHtml);
92
- }
93
- // ✅ Update ARIA live region with success message
94
- const statusRegion = document.getElementById('_govcy-upload-status');
95
- if (statusRegion) {
81
+ // document.querySelector('[name="' + elementName + 'Attachment[fileId]"]').value = fileId;
82
+ // document.querySelector('[name="' + elementName + 'Attachment[sha256]"]').value = sha256;
83
+
84
+ // Render the file view
85
+ _renderFileElement("fileView", elementId, elementName, fileId, sha256, null);
86
+
87
+ // Accessibility: Update ARIA live region with success message
88
+ var statusRegion = document.getElementById('_govcy-upload-status');
89
+ if (statusRegion) {
90
+ setTimeout(function() {
96
91
  statusRegion.textContent = messages.uploadSuccesful[lang];
97
- setTimeout(() => {
98
- statusRegion.textContent = '';
99
- }, 10000);
100
- }
92
+ }, 200)
93
+ setTimeout(function() {
94
+ statusRegion.textContent = '';
95
+ }, 5000);
96
+ }
101
97
  // alert('✅ File uploaded successfully');
102
98
 
103
99
  } catch (err) {
104
- // Create an instance of GovcyFrontendRendererBrowser
105
- const renderer = new GovcyFrontendRendererBrowser();
106
- const lang = window._govcyLang || "el";
107
- // Define the input data
108
- const inputData =
109
- {
110
- "site": {
111
- "lang": lang
112
- }
113
- };
114
- const fileInputMap = window._govcyFileInputs || {};
115
- let fileElement = fileInputMap[elementName];
116
- fileElement.element = "fileInput";
117
- fileElement.params.fileId = "";
118
- fileElement.params.sha256 = ""
119
- fileElement.params.error = messages.uploadFailed;
120
-
121
- // Construct the JSONTemplate
122
- const JSONTemplate = {
123
- "elements": [fileElement]
124
- };
125
- //render HTML into string
126
- let renderedHtml = renderer.renderFromJSON(JSONTemplate,inputData);
127
- var outerElement = document.getElementById(`${elementName}-outer-control`)
128
- || document.getElementById(`${elementName}-input-control`)
129
- || document.getElementById(`${elementName}-view-control`);
130
-
131
- if (outerElement) {
132
- //remove all classes from outerElement
133
- outerElement.className = "";
134
- //set the id of the outerElement to `${elementName}-outer-control`
135
- outerElement.id = `${elementName}-outer-control`;
136
- //update DOM and initialize the JS components
137
- renderer.updateDOMAndInitialize(`${elementName}-outer-control`, renderedHtml);
138
- //TODO: Kamran need to figure a way to re register the DOM event on change
100
+ // ⚠️ Show an error message if upload fails
101
+ var errorMessage = messages.uploadFailed;
102
+ var errorCode = err && err.response && err.response.data && err.response.data.ErrorCode;
103
+
104
+ if (errorCode === 406 || errorCode === 407 || errorCode === 408 || errorCode === 409) {
105
+ errorMessage = messages["uploadFailed" + errorCode];
139
106
  }
140
- // ✅ Update ARIA live region with success message
141
- const statusRegion = document.getElementById('_govcy-upload-error');
142
- if (statusRegion) {
143
- statusRegion.textContent = messages.uploadFailed[lang];
144
- setTimeout(() => {
145
- statusRegion.textContent = '';
146
- }, 10000);
107
+
108
+ // Render the file input with error
109
+ _renderFileElement("fileInput", elementId, elementName, "","", errorMessage);
110
+
111
+ // Re-bind the file input's change handler
112
+ var newInput = document.getElementById(elementId);
113
+ if (newInput) {
114
+ newInput.addEventListener('change', _uploadFileEventHandler);
147
115
  }
148
- // // ⚠️ Show an error message if upload fails
149
- // alert('❌ Upload failed: ' + (err.response?.data?.error || err.message));
116
+
117
+ // Accessibility: Focus on the form field
118
+ document.getElementById(elementId)?.focus();
119
+
150
120
  }
151
- });
152
- });
121
+ }
122
+
123
+ /**
124
+ * Renders a file element in the DOM
125
+ *
126
+ * @param {string} elementState The element state. Can be "fileInput" or "fileView"
127
+ * @param {string} elementId The element id
128
+ * @param {string} elementName The element name
129
+ * @param {string} fileId The file id
130
+ * @param {string} sha256 The sha256
131
+ * @param {object} errorMessage The error message in all supported languages
132
+ */
133
+ function _renderFileElement(elementState, elementId, elementName, fileId, sha256, errorMessage) {
134
+ // Create an instance of GovcyFrontendRendererBrowser
135
+ var renderer = new GovcyFrontendRendererBrowser();
136
+ var lang = window._govcyLang || "el";
137
+ // Define the input data
138
+ var inputData =
139
+ {
140
+ "site": {
141
+ "lang": lang
142
+ }
143
+ };
144
+ var fileInputMap = window._govcyFileInputs || {};
145
+ var fileElement = fileInputMap[elementName];
146
+ fileElement.element = elementState;
147
+ if (errorMessage != null) fileElement.params.error = errorMessage;
148
+ if (fileId != null) fileElement.params.fileId = fileId;
149
+ if (sha256 != null) fileElement.params.sha256 = sha256;
150
+ if (elementState == "fileView") {
151
+ fileElement.params.visuallyHiddenText = fileElement.params.label;
152
+ // TODO: Also need to set the `view` and `download` URLs
153
+ fileElement.params.viewHref = "#viewHref";
154
+ fileElement.params.deleteHref = "#deleteHref";
155
+ }
156
+ // Construct the JSONTemplate
157
+ var JSONTemplate = {
158
+ "elements": [fileElement]
159
+ };
160
+
161
+ //render HTML into string
162
+ var renderedHtml = renderer.renderFromJSON(JSONTemplate,inputData);
163
+ var outerElement = document.getElementById(`${elementId}-outer-control`)
164
+ || document.getElementById(`${elementId}-input-control`)
165
+ || document.getElementById(`${elementId}-view-control`);
166
+
167
+ if (outerElement) {
168
+ //remove all classes from outerElement
169
+ outerElement.className = "";
170
+ //set the id of the outerElement to `${elementId}-outer-control`
171
+ outerElement.id = `${elementId}-outer-control`;
172
+ //update DOM and initialize the JS components
173
+ renderer.updateDOMAndInitialize(`${elementId}-outer-control`, renderedHtml);
174
+ }
175
+ }
@@ -100,6 +100,16 @@ export const staticResources = {
100
100
  en: "We have received your request. ",
101
101
  el: "Έχουμε λάβει την αίτησή σας. ",
102
102
  tr: "We have received your request. "
103
+ },
104
+ fileUploaded : {
105
+ en: "File has been uploaded. ",
106
+ el: "Το αρχείο ανεβάστηκε. ",
107
+ tr: "File has been uploaded. "
108
+ },
109
+ fileNotUploaded : {
110
+ en: "File has not been uploaded. ",
111
+ el: "Το αρχείο δεν ανεβάστηκε. ",
112
+ tr: "File has not been uploaded. "
103
113
  }
104
114
  },
105
115
  //remderer sections
@@ -18,9 +18,12 @@ import * as dataLayer from "./govcyDataLayer.mjs";
18
18
  * @param {string} pageUrl The page URL
19
19
  * @param {string} lang The language
20
20
  */
21
- export function populateFormData(formElements, theData, validationErrors, store = {}, siteId = "", pageUrl = "", lang = "el") {
21
+ export function populateFormData(formElements, theData, validationErrors, store = {}, siteId = "", pageUrl = "", lang = "el", fileInputElements = null) {
22
22
  const inputElements = ALLOWED_FORM_ELEMENTS;
23
- let fileInputElements = {};
23
+ const isRootCall = !fileInputElements;
24
+ if (isRootCall) {
25
+ fileInputElements = {};
26
+ }
24
27
  // Recursively populate form data with session data
25
28
  formElements.forEach(element => {
26
29
  if (inputElements.includes(element.element)) {
@@ -62,7 +65,9 @@ export function populateFormData(formElements, theData, validationErrors, store
62
65
  } else if (element.element === "fileInput") {
63
66
  // For fileInput, we change the element.element to "fileView" and set the
64
67
  // fileId and sha256 from the session store
65
- const fileData = dataLayer.getFormDataValue(store, siteId, pageUrl, fieldName + "Attachment");
68
+ // unneeded handle of `Attachment` at the end
69
+ // const fileData = dataLayer.getFormDataValue(store, siteId, pageUrl, fieldName + "Attachment");
70
+ const fileData = dataLayer.getFormDataValue(store, siteId, pageUrl, fieldName);
66
71
  // TODO: Ask Andreas how to handle empty file inputs
67
72
  if (fileData) {
68
73
  element.element = "fileView";
@@ -102,7 +107,7 @@ export function populateFormData(formElements, theData, validationErrors, store
102
107
  if (element.element === "radios" && element.params.items) {
103
108
  element.params.items.forEach(item => {
104
109
  if (item.conditionalElements) {
105
- populateFormData(item.conditionalElements, theData, validationErrors);
110
+ populateFormData(item.conditionalElements, theData, validationErrors,store, siteId , pageUrl, lang, fileInputElements);
106
111
 
107
112
  // Check if any conditional element has an error and add to the parent "conditionalHasErrors": true
108
113
  if (item.conditionalElements.some(condEl => condEl.params?.error)) {
@@ -113,7 +118,7 @@ export function populateFormData(formElements, theData, validationErrors, store
113
118
  }
114
119
  });
115
120
  // add file input elements's definition in js object
116
- if (fileInputElements != {}) {
121
+ if (isRootCall && Object.keys(fileInputElements).length > 0) {
117
122
  const scriptTag = `
118
123
  <script type="text/javascript">
119
124
  window._govcyFileInputs = ${JSON.stringify(fileInputElements)};
@@ -121,7 +126,7 @@ export function populateFormData(formElements, theData, validationErrors, store
121
126
  window._govcyPageUrl = "${pageUrl}";
122
127
  window._govcyLang = "${lang}";
123
128
  </script>
124
- <div id="_govcy-upload-status" class="govcy-visually-hidden" role="status" aria-live="polite"></div>
129
+ <div id="_govcy-upload-status" class="govcy-visually-hidden" role="status" aria-live="assertive"></div>
125
130
  <div id="_govcy-upload-error" class="govcy-visually-hidden" role="alert" aria-live="assertive"></div>
126
131
  `.trim();
127
132
  formElements.push({
@@ -165,7 +170,7 @@ export function getFormData(elements, formData, store = {}, siteId = "", pageUrl
165
170
  if (item.conditionalElements) {
166
171
  Object.assign(
167
172
  filteredData,
168
- getFormData(item.conditionalElements, formData)
173
+ getFormData(item.conditionalElements, formData, store, siteId, pageUrl)
169
174
  );
170
175
  }
171
176
  });
@@ -184,12 +189,18 @@ export function getFormData(elements, formData, store = {}, siteId = "", pageUrl
184
189
  } else if (element.element === "fileInput") {
185
190
  // fileInput elements are already stored in the store when it was uploaded
186
191
  // so we just need to check if the file exists in the dataLayer in the store and add it the filteredData
187
- const fileData = dataLayer.getFormDataValue(store, siteId, pageUrl, name + "Attachment");
192
+ // unneeded handle of `Attachment` at the end
193
+ // const fileData = dataLayer.getFormDataValue(store, siteId, pageUrl, name + "Attachment");
194
+ const fileData = dataLayer.getFormDataValue(store, siteId, pageUrl, name);
188
195
  if (fileData) {
189
- filteredData[name + "Attachment"] = fileData;
196
+ // unneeded handle of `Attachment` at the end
197
+ // filteredData[name + "Attachment"] = fileData;
198
+ filteredData[name] = fileData;
190
199
  } else {
191
200
  //TODO: Ask Andreas how to handle empty file inputs
192
- filteredData[name + "Attachment"] = ""; // or handle as needed
201
+ // unneeded handle of `Attachment` at the end
202
+ // filteredData[name + "Attachment"] = ""; // or handle as needed
203
+ filteredData[name] = ""; // or handle as needed
193
204
  }
194
205
  // Handle other elements (e.g., textInput, textArea, datePicker)
195
206
  } else {
@@ -27,6 +27,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
27
27
  if (!file || !elementName) {
28
28
  return {
29
29
  status: 400,
30
+ dataStatus: 400,
30
31
  errorMessage: 'Missing file or element name'
31
32
  };
32
33
  }
@@ -37,6 +38,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
37
38
  if (!uploadCfg?.url || !uploadCfg?.clientKey || !uploadCfg?.serviceId) {
38
39
  return {
39
40
  status: 400,
41
+ dataStatus: 401,
40
42
  errorMessage: 'Missing upload configuration'
41
43
  };
42
44
  }
@@ -53,6 +55,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
53
55
  if (!url || !clientKey) {
54
56
  return {
55
57
  status: 400,
58
+ dataStatus: 402,
56
59
  errorMessage: 'Missing environment variables for upload'
57
60
  };
58
61
  }
@@ -67,6 +70,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
67
70
  if (!page?.pageTemplate) {
68
71
  return {
69
72
  status: 400,
73
+ dataStatus: 403,
70
74
  errorMessage: 'Invalid page configuration'
71
75
  };
72
76
  }
@@ -77,6 +81,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
77
81
  if (conditionResult.result === false) {
78
82
  return {
79
83
  status: 403,
84
+ dataStatus: 404,
80
85
  errorMessage: 'This page is skipped by conditional logic'
81
86
  };
82
87
  }
@@ -88,6 +93,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
88
93
  if (!isAllowed) {
89
94
  return {
90
95
  status: 403,
96
+ dataStatus: 405,
91
97
  errorMessage: `File input [${elementName}] not allowed on this page`
92
98
  };
93
99
  }
@@ -96,6 +102,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
96
102
  if (file.size === 0) {
97
103
  return {
98
104
  status: 400,
105
+ dataStatus: 406,
99
106
  errorMessage: 'Uploaded file is empty'
100
107
  };
101
108
  }
@@ -105,6 +112,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
105
112
  if (!ALLOWED_FILE_MIME_TYPES.includes(file.mimetype)) {
106
113
  return {
107
114
  status: 400,
115
+ dataStatus: 407,
108
116
  errorMessage: 'Invalid file type (MIME not allowed)'
109
117
  };
110
118
  }
@@ -113,6 +121,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
113
121
  if (!isMagicByteValid(file.buffer, file.mimetype)) {
114
122
  return {
115
123
  status: 400,
124
+ dataStatus: 408,
116
125
  errorMessage: 'Invalid file type (magic byte mismatch)'
117
126
  };
118
127
  }
@@ -121,6 +130,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
121
130
  if (file.size > ALLOWED_FILE_SIZE_MB * 1024 * 1024) {
122
131
  return {
123
132
  status: 400,
133
+ dataStatus: 409,
124
134
  errorMessage: 'File exceeds allowed size'
125
135
  };
126
136
  }
@@ -161,6 +171,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
161
171
  if (!response?.Succeeded) {
162
172
  return {
163
173
  status: 500,
174
+ dataStatus: 410,
164
175
  errorMessage: `${response?.ErrorCode} - ${response?.ErrorMessage} - fileUploadAPIEndpoint returned succeeded false`
165
176
  };
166
177
  }
@@ -169,13 +180,16 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
169
180
  if (!response?.Data?.fileId || !response?.Data?.sha256) {
170
181
  return {
171
182
  status: 500,
183
+ dataStatus: 411,
172
184
  errorMessage: 'Missing fileId or sha256 in response'
173
185
  };
174
186
  }
175
187
 
176
188
  // ✅ Success
177
189
  // Store the file metadata in the session store
178
- dataLayer.storePageDataElement(store, siteId, pageUrl, elementName+"Attachment", {
190
+ // unneeded handle of `Attachment` at the end
191
+ // dataLayer.storePageDataElement(store, siteId, pageUrl, elementName+"Attachment", {
192
+ dataLayer.storePageDataElement(store, siteId, pageUrl, elementName, {
179
193
  sha256: response.Data.sha256,
180
194
  fileId: response.Data.fileId,
181
195
  });
@@ -195,6 +209,7 @@ export async function handleFileUpload({ service, store, siteId, pageUrl, elemen
195
209
  } catch (err) {
196
210
  return {
197
211
  status: 500,
212
+ dataStatus: 500,
198
213
  errorMessage: 'Upload failed' + (err.message ? `: ${err.message}` : ''),
199
214
  };
200
215
  }
@@ -66,6 +66,12 @@ export function prepareSubmissionData(req, siteId, service) {
66
66
  // Store in submissionData
67
67
  submissionData[pageUrl].formData[elId] = value;
68
68
 
69
+ // handle fileInput
70
+ if (elType === "fileInput") {
71
+ // 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
+ delete submissionData[pageUrl].formData[elId];
74
+ }
69
75
 
70
76
  // 🔄 If radios with conditionalElements, walk ALL options
71
77
  if (elType === "radios" && Array.isArray(element.params?.items)) {
@@ -85,6 +91,12 @@ export function prepareSubmissionData(req, siteId, service) {
85
91
 
86
92
  // Store even if the field was not visible to user
87
93
  submissionData[pageUrl].formData[condId] = condValue;
94
+ // handle fileInput
95
+ if (condType === "fileInput") {
96
+ // 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
+ delete submissionData[pageUrl].formData[condId];
99
+ }
88
100
  }
89
101
  }
90
102
  }
@@ -291,6 +303,10 @@ function getValue(formElement, pageUrl, req, siteId) {
291
303
  let value = ""
292
304
  if (formElement.element === "dateInput") {
293
305
  value = getDateInputISO(pageUrl, formElement.params.name, req, siteId);
306
+ } else if (formElement.element === "fileInput") {
307
+ // unneeded handle of `Attachment` at the end
308
+ // value = dataLayer.getFormDataValue(req.session, siteId, pageUrl, formElement.params.name + "Attachment");
309
+ value = dataLayer.getFormDataValue(req.session, siteId, pageUrl, formElement.params.name);
294
310
  } else {
295
311
  value = dataLayer.getFormDataValue(req.session, siteId, pageUrl, formElement.params.name);
296
312
  }
@@ -340,6 +356,17 @@ function getValueLabel(formElement, value, pageUrl, req, siteId, service) {
340
356
  return govcyResources.getSameMultilingualObject(service.site.languages, formattedDate);
341
357
  }
342
358
 
359
+ // handle fileInput
360
+ if (formElement.element === "fileInput") {
361
+ // TODO: Ask Andreas how to handle empty file inputs
362
+ if (value) {
363
+ return govcyResources.staticResources.text.fileUploaded;
364
+ } else {
365
+ return govcyResources.getSameMultilingualObject(service.site.languages, "");
366
+ // return govcyResources.staticResources.text.fileNotUploaded;
367
+ }
368
+ }
369
+
343
370
  // textInput, textArea, etc.
344
371
  return govcyResources.getSameMultilingualObject(service.site.languages, value);
345
372
  }
@@ -263,8 +263,9 @@ export function validateFormElements(elements, formData, pageUrl) {
263
263
  formData[`${field.params.name}_day`]]
264
264
  .filter(Boolean) // Remove empty values
265
265
  .join("-") // Join remaining parts
266
- : (field.element === "fileInput") // Handle fileInput
267
- ? formData[`${field.params.name}Attachment`] || ""
266
+ // unneeded handle of `Attachment` at the end
267
+ // : (field.element === "fileInput") // Handle fileInput
268
+ // ? formData[`${field.params.name}Attachment`] || ""
268
269
  : formData[field.params.name] || ""; // Get submitted value
269
270
 
270
271
  //Autocheck: check for "checkboxes", "radios", "select" if `fieldValue` is one of the `field.params.items
@@ -313,7 +314,9 @@ export function validateFormElements(elements, formData, pageUrl) {
313
314
  .filter(Boolean) // Remove empty values
314
315
  .join("-") // Join remaining parts
315
316
  : (conditionalElement.element === "fileInput") // Handle fileInput
316
- ? formData[`${conditionalElement.params.name}Attachment`] || ""
317
+ // unneeded handle of `Attachment` at the end
318
+ // ? formData[`${conditionalElement.params.name}Attachment`] || ""
319
+ ? formData[`${conditionalElement.params.name}`] || ""
317
320
  : formData[conditionalElement.params.name] || ""; // Get submitted value
318
321
 
319
322
  //Autocheck: check for "checkboxes", "radios", "select" if `fieldValue` is one of the `field.params.items`