@gov-cy/govcy-express-services 1.0.0-alpha.1 → 1.0.0-alpha.11
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 +156 -11
- package/package.json +8 -3
- package/src/index.mjs +20 -1
- package/src/middleware/cyLoginAuth.mjs +8 -0
- package/src/middleware/govcyCsrf.mjs +15 -1
- package/src/middleware/govcyFileDeleteHandler.mjs +239 -0
- package/src/middleware/govcyFileUpload.mjs +36 -0
- package/src/middleware/govcyFileViewHandler.mjs +161 -0
- package/src/middleware/govcyFormsPostHandler.mjs +1 -1
- package/src/middleware/govcyHttpErrorHandler.mjs +4 -3
- package/src/middleware/govcyPageHandler.mjs +3 -3
- package/src/middleware/govcyPageRender.mjs +10 -0
- package/src/middleware/govcyReviewPageHandler.mjs +4 -1
- package/src/middleware/govcySuccessPageHandler.mjs +1 -2
- package/src/public/js/govcyFiles.js +299 -0
- package/src/public/js/govcyForms.js +19 -8
- package/src/resources/govcyResources.mjs +79 -3
- package/src/utils/govcyApiDetection.mjs +17 -0
- package/src/utils/govcyApiRequest.mjs +30 -5
- package/src/utils/govcyApiResponse.mjs +31 -0
- package/src/utils/govcyConstants.mjs +5 -1
- package/src/utils/govcyDataLayer.mjs +22 -0
- package/src/utils/govcyExpressions.mjs +1 -1
- package/src/utils/govcyFormHandling.mjs +81 -5
- package/src/utils/govcyHandleFiles.mjs +307 -0
- package/src/utils/govcyLoadSubmissionDataAPIs.mjs +10 -3
- package/src/utils/govcySubmitData.mjs +62 -2
- package/src/utils/govcyTempSave.mjs +2 -1
- package/src/utils/govcyValidator.mjs +7 -0
|
@@ -6,6 +6,11 @@ export const staticResources = {
|
|
|
6
6
|
el: "Υποβολή",
|
|
7
7
|
tr: "Gönder"
|
|
8
8
|
},
|
|
9
|
+
continue: {
|
|
10
|
+
en: "Continue",
|
|
11
|
+
el: "Συνέχεια",
|
|
12
|
+
tr: "Continue"
|
|
13
|
+
},
|
|
9
14
|
cancel: {
|
|
10
15
|
en: "Cancel",
|
|
11
16
|
el: "Ακύρωση",
|
|
@@ -100,6 +105,46 @@ export const staticResources = {
|
|
|
100
105
|
en: "We have received your request. ",
|
|
101
106
|
el: "Έχουμε λάβει την αίτησή σας. ",
|
|
102
107
|
tr: "We have received your request. "
|
|
108
|
+
},
|
|
109
|
+
fileUploaded : {
|
|
110
|
+
en: "File uploaded",
|
|
111
|
+
el: "Το αρχείο ανεβάστηκε",
|
|
112
|
+
tr: "File uploaded"
|
|
113
|
+
},
|
|
114
|
+
fileNotUploaded : {
|
|
115
|
+
en: "File has not been uploaded. ",
|
|
116
|
+
el: "Το αρχείο δεν ανεβάστηκε. ",
|
|
117
|
+
tr: "File has not been uploaded. "
|
|
118
|
+
},
|
|
119
|
+
fileYouHaveUploaded : {
|
|
120
|
+
en: "You have uploaded the file for \"{{file}}\"",
|
|
121
|
+
el: "Έχετε ανεβάσει το αρχείο \"{{file}}\"",
|
|
122
|
+
tr: "You have uploaded the file for \"{{file}}\""
|
|
123
|
+
},
|
|
124
|
+
deleteFileTitle : {
|
|
125
|
+
en: "Are you sure you want to delete the file \"{{file}}\"? ",
|
|
126
|
+
el: "Είστε σίγουροι ότι θέλετε να διαγράψετε το αρχείο \"{{file}}\";",
|
|
127
|
+
tr: "Are you sure you want to delete the file \"{{file}}\"? "
|
|
128
|
+
},
|
|
129
|
+
deleteYesOption: {
|
|
130
|
+
el:"Ναι, θέλω να διαγράψω το αρχείο",
|
|
131
|
+
en:"Yes, I want to delete this file",
|
|
132
|
+
tr:"Yes, I want to delete this file"
|
|
133
|
+
},
|
|
134
|
+
deleteNoOption: {
|
|
135
|
+
el:"Όχι, δεν θέλω να διαγράψω το αρχείο",
|
|
136
|
+
en:"No, I don't want to delete this file",
|
|
137
|
+
tr:"No, I don't want to delete this file"
|
|
138
|
+
},
|
|
139
|
+
deleteFileValidationError: {
|
|
140
|
+
en: "Select if you want to delete the file",
|
|
141
|
+
el: "Επιλέξτε αν θέλετε να διαγράψετε το αρχείο",
|
|
142
|
+
tr: "Select if you want to delete the file"
|
|
143
|
+
},
|
|
144
|
+
viewFile: {
|
|
145
|
+
en: "View file",
|
|
146
|
+
el: "Προβολή αρχείου",
|
|
147
|
+
tr: "View file"
|
|
103
148
|
}
|
|
104
149
|
},
|
|
105
150
|
//remderer sections
|
|
@@ -113,9 +158,19 @@ export const staticResources = {
|
|
|
113
158
|
element: "htmlElement",
|
|
114
159
|
params: {
|
|
115
160
|
text: {
|
|
116
|
-
en: `<script src="/js/govcyForms.js"></script>`,
|
|
117
|
-
el: `<script src="/js/govcyForms.js"></script>`,
|
|
118
|
-
tr: `<script src="/js/govcyForms.js"></script>`
|
|
161
|
+
en: `<script src="https://cdn.jsdelivr.net/gh/gov-cy/govcy-frontend-renderer@v1.22.0/dist/govcyCompiledTemplates.browser.js"></script><script src="https://cdn.jsdelivr.net/gh/gov-cy/govcy-frontend-renderer@v1.22.0/dist/govcyFrontendRenderer.browser.js"></script><script type="module" src="/js/govcyForms.js"></script><script type="module" src="/js/govcyFiles.js"></script>`,
|
|
162
|
+
el: `<script src="https://cdn.jsdelivr.net/gh/gov-cy/govcy-frontend-renderer@v1.22.0/dist/govcyCompiledTemplates.browser.js"></script><script src="https://cdn.jsdelivr.net/gh/gov-cy/govcy-frontend-renderer@v1.22.0/dist/govcyFrontendRenderer.browser.js"></script><script type="module" src="/js/govcyForms.js"></script><script type="module" src="/js/govcyFiles.js"></script>`,
|
|
163
|
+
tr: `<script src="https://cdn.jsdelivr.net/gh/gov-cy/govcy-frontend-renderer@v1.22.0/dist/govcyCompiledTemplates.browser.js"></script><script src="https://cdn.jsdelivr.net/gh/gov-cy/govcy-frontend-renderer@v1.22.0/dist/govcyFrontendRenderer.browser.js"></script><script type="module" src="/js/govcyForms.js"></script><script type="module" src="/js/govcyFiles.js"></script>`
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
govcyLoadingOverlay: {
|
|
168
|
+
element: "htmlElement",
|
|
169
|
+
params: {
|
|
170
|
+
text: {
|
|
171
|
+
en: `<style>.govcy-loadingOverlay{position:fixed;top:0;right:0;bottom:0;left:0;display:none;justify-content:center;align-items:center;background:rgba(255,255,255,.7);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);z-index:1050}.govcy-loadingOverlay[aria-hidden="false"]{display:flex}</style><div id="govcy--loadingOverlay" class="govcy-loadingOverlay" aria-hidden="true" role="dialog" aria-modal="true" tabindex="-1"><div class="govcy-loadingOverlay__content" role="status" aria-live="polite"><div class="spinner-border govcy-text-primary" role="status"><span class="govcy-visually-hidden">Loading...</span></div></div></div>`,
|
|
172
|
+
el: `<style>.govcy-loadingOverlay{position:fixed;top:0;right:0;bottom:0;left:0;display:none;justify-content:center;align-items:center;background:rgba(255,255,255,.7);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);z-index:1050}.govcy-loadingOverlay[aria-hidden="false"]{display:flex}</style><div id="govcy--loadingOverlay" class="govcy-loadingOverlay" aria-hidden="true" role="dialog" aria-modal="true" tabindex="-1"><div class="govcy-loadingOverlay__content" role="status" aria-live="polite"><div class="spinner-border govcy-text-primary" role="status"><span class="govcy-visually-hidden">Φόρτωση...</span></div></div></div>`,
|
|
173
|
+
tr: `<style>.govcy-loadingOverlay{position:fixed;top:0;right:0;bottom:0;left:0;display:none;justify-content:center;align-items:center;background:rgba(255,255,255,.7);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);z-index:1050}.govcy-loadingOverlay[aria-hidden="false"]{display:flex}</style><div id="govcy--loadingOverlay" class="govcy-loadingOverlay" aria-hidden="true" role="dialog" aria-modal="true" tabindex="-1"><div class="govcy-loadingOverlay__content" role="status" aria-live="polite"><div class="spinner-border govcy-text-primary" role="status"><span class="govcy-visually-hidden">Loading...</span></div></div></div>`
|
|
119
174
|
}
|
|
120
175
|
}
|
|
121
176
|
},
|
|
@@ -192,6 +247,27 @@ export function csrfTokenInput(csrfToken) {
|
|
|
192
247
|
};
|
|
193
248
|
}
|
|
194
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Get the site and page input elements
|
|
252
|
+
* @param {string} siteId The site id
|
|
253
|
+
* @param {string} pageUrl The page url
|
|
254
|
+
* @param {string} lang The page language
|
|
255
|
+
* @returns {object} htmlElement with the site and page inputs
|
|
256
|
+
*/
|
|
257
|
+
export function siteAndPageInput(siteId, pageUrl, lang = "el") {
|
|
258
|
+
const siteAndPageInputs = `<input type="hidden" name="_siteId" value="${siteId}"><input type="hidden" name="_pageUrl" value="${pageUrl}"><input type="hidden" name="_lang" value="${lang}">`;
|
|
259
|
+
return {
|
|
260
|
+
element: "htmlElement",
|
|
261
|
+
params: {
|
|
262
|
+
text: {
|
|
263
|
+
en: siteAndPageInputs,
|
|
264
|
+
el: siteAndPageInputs,
|
|
265
|
+
tr: siteAndPageInputs
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
195
271
|
/**
|
|
196
272
|
* Error page template
|
|
197
273
|
* @param {object} title the title text element
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determines if a request is targeting an API endpoint.
|
|
3
|
+
* Currently matches:
|
|
4
|
+
* - Accept header with application/json
|
|
5
|
+
* - URLs ending with /upload or /download under a site/page structure
|
|
6
|
+
*
|
|
7
|
+
* @param {object} req - Express request object
|
|
8
|
+
* @returns {boolean}
|
|
9
|
+
*/
|
|
10
|
+
export function isApiRequest(req) {
|
|
11
|
+
const acceptJson = (req.headers?.accept || "").toLowerCase().includes("application/json");
|
|
12
|
+
|
|
13
|
+
const apiUrlPattern = /^\/apis\/[^/]+\/[^/]+\/(upload|download)$/;
|
|
14
|
+
const isStructuredApiUrl = apiUrlPattern.test(req.originalUrl || req.url);
|
|
15
|
+
|
|
16
|
+
return acceptJson || isStructuredApiUrl;
|
|
17
|
+
}
|
|
@@ -5,12 +5,13 @@ import { logger } from "./govcyLogger.mjs";
|
|
|
5
5
|
* Utility to handle API communication with retry logic
|
|
6
6
|
* @param {string} method - HTTP method (e.g., 'post', 'get', etc.)
|
|
7
7
|
* @param {string} url - API endpoint URL
|
|
8
|
-
* @param {object} inputData - Payload for the request (optional)
|
|
8
|
+
* @param {object|FormData} inputData - Payload for the request (optional)
|
|
9
9
|
* @param {boolean} useAccessTokenAuth - Whether to use Authorization header with Bearer token
|
|
10
10
|
* @param {object} user - User object containing access_token (optional)
|
|
11
11
|
* @param {object} headers - Custom headers (optional)
|
|
12
12
|
* @param {number} retries - Number of retry attempts (default: 3)
|
|
13
13
|
* @param {boolean} allowSelfSignedCerts - Whether to allow self-signed certificates (default: false)
|
|
14
|
+
* @param {array} allowedHTTPStatusCodes - Array of allowed HTTP status codes (default: [200])
|
|
14
15
|
* @returns {Promise<object>} - API response
|
|
15
16
|
*/
|
|
16
17
|
export async function govcyApiRequest(
|
|
@@ -21,7 +22,8 @@ export async function govcyApiRequest(
|
|
|
21
22
|
user = null,
|
|
22
23
|
headers = {},
|
|
23
24
|
retries = 3,
|
|
24
|
-
allowSelfSignedCerts = false
|
|
25
|
+
allowSelfSignedCerts = false,
|
|
26
|
+
allowedHTTPStatusCodes = [200]
|
|
25
27
|
) {
|
|
26
28
|
let attempt = 0;
|
|
27
29
|
|
|
@@ -37,6 +39,14 @@ export async function govcyApiRequest(
|
|
|
37
39
|
requestHeaders['Authorization'] = `Bearer ${user.access_token}`;
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
// If inputData is FormData, for attachments
|
|
43
|
+
if (inputData instanceof (await import('form-data')).default) {
|
|
44
|
+
requestHeaders = {
|
|
45
|
+
...requestHeaders,
|
|
46
|
+
...inputData.getHeaders(), // includes boundary in content-type
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
40
50
|
while (attempt < retries) {
|
|
41
51
|
try {
|
|
42
52
|
logger.debug(`📤 Sending API request (Attempt ${attempt + 1})`, { method, url, inputData, requestHeaders });
|
|
@@ -45,11 +55,22 @@ export async function govcyApiRequest(
|
|
|
45
55
|
const axiosConfig = {
|
|
46
56
|
method,
|
|
47
57
|
url,
|
|
48
|
-
|
|
58
|
+
...(inputData instanceof (await import('form-data')).default // If inputData is FormData, for attachments
|
|
59
|
+
? { data: inputData }
|
|
60
|
+
: { [method?.toLowerCase() === 'get' ? 'params' : 'data']: inputData }),
|
|
49
61
|
headers: requestHeaders,
|
|
50
62
|
timeout: 10000, // 10 seconds timeout
|
|
63
|
+
// ✅ Treat only these statuses as "resolved" (no throw)
|
|
64
|
+
validateStatus: (status) => allowedHTTPStatusCodes.includes(status),
|
|
51
65
|
};
|
|
52
66
|
|
|
67
|
+
// If inputData is FormData, for attachments
|
|
68
|
+
if (inputData instanceof (await import('form-data')).default) {
|
|
69
|
+
axiosConfig.maxContentLength = Infinity;
|
|
70
|
+
axiosConfig.maxBodyLength = Infinity;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
53
74
|
// Add httpsAgent if NOT production to allow self-signed certificates
|
|
54
75
|
// Use per-call config for self-signed certs
|
|
55
76
|
if (allowSelfSignedCerts) {
|
|
@@ -60,9 +81,13 @@ export async function govcyApiRequest(
|
|
|
60
81
|
|
|
61
82
|
logger.debug(`📥 Received API response`, { status: response.status, data: response.data });
|
|
62
83
|
|
|
63
|
-
|
|
84
|
+
// Validate HTTP status
|
|
85
|
+
if (!allowedHTTPStatusCodes.includes(response.status)) {
|
|
64
86
|
throw new Error(`Unexpected HTTP status: ${response.status}`);
|
|
65
87
|
}
|
|
88
|
+
// if (response.status !== 200) {
|
|
89
|
+
// throw new Error(`Unexpected HTTP status: ${response.status}`);
|
|
90
|
+
// }
|
|
66
91
|
|
|
67
92
|
// const { Succeeded, ErrorCode, ErrorMessage } = response.data;
|
|
68
93
|
// Normalize to PascalCase regardless of input case
|
|
@@ -77,7 +102,7 @@ export async function govcyApiRequest(
|
|
|
77
102
|
ErrorMessage,
|
|
78
103
|
Data,
|
|
79
104
|
InformationMessage
|
|
80
|
-
} = response.data;
|
|
105
|
+
} = response.data ?? {};
|
|
81
106
|
|
|
82
107
|
const normalized = {
|
|
83
108
|
Succeeded: Succeeded !== undefined ? Succeeded : succeeded,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The successResponse function creates a standardized success response object.
|
|
3
|
+
*
|
|
4
|
+
* @param {*} data - The data to be included in the response.
|
|
5
|
+
* @returns {Object} - The success response object.
|
|
6
|
+
*/
|
|
7
|
+
export function successResponse(data = null) {
|
|
8
|
+
return {
|
|
9
|
+
Succeeded: true,
|
|
10
|
+
ErrorCode: 0,
|
|
11
|
+
ErrorMessage: '',
|
|
12
|
+
Data: data
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The errorResponse function creates a standardized error response object.
|
|
18
|
+
*
|
|
19
|
+
* @param {int} code - The error code to be included in the response.
|
|
20
|
+
* @param {string} message - The error message to be included in the response.
|
|
21
|
+
* @param {Object} data - Additional data to be included in the response.
|
|
22
|
+
* @returns {Object} - The error response object.
|
|
23
|
+
*/
|
|
24
|
+
export function errorResponse(code = 1, message = 'Unknown error', data = null) {
|
|
25
|
+
return {
|
|
26
|
+
Succeeded: false,
|
|
27
|
+
ErrorCode: code,
|
|
28
|
+
ErrorMessage: message,
|
|
29
|
+
Data: data
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared constants for allowed form elements.
|
|
3
3
|
*/
|
|
4
|
-
export const ALLOWED_FORM_ELEMENTS = ["textInput", "textArea", "select", "radios", "checkboxes", "datePicker", "dateInput"];
|
|
4
|
+
export const ALLOWED_FORM_ELEMENTS = ["textInput", "textArea", "select", "radios", "checkboxes", "datePicker", "dateInput","fileInput","fileView"];
|
|
5
|
+
export const ALLOWED_FILE_MIME_TYPES = ['application/pdf', 'image/jpeg', 'image/png'];
|
|
6
|
+
export const ALLOWED_FILE_EXTENSIONS = ['pdf', 'jpg', 'jpeg', 'png'];
|
|
7
|
+
export const ALLOWED_FILE_SIZE_MB = 5; // Maximum file size in MB
|
|
8
|
+
export const ALLOWED_MULTER_FILE_SIZE_MB = 10; // Maximum file size in MB
|
|
@@ -98,6 +98,14 @@ export function storePageData(store, siteId, pageUrl, formData) {
|
|
|
98
98
|
|
|
99
99
|
store.siteData[siteId].inputData[pageUrl]["formData"] = formData;
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
export function storePageDataElement(store, siteId, pageUrl, elementName, value) {
|
|
103
|
+
// Ensure session structure is initialized
|
|
104
|
+
initializeSiteData(store, siteId, pageUrl);
|
|
105
|
+
|
|
106
|
+
// Store the element value
|
|
107
|
+
store.siteData[siteId].inputData[pageUrl].formData[elementName] = value;
|
|
108
|
+
}
|
|
101
109
|
/**
|
|
102
110
|
* Stores the page's input data in the data layer
|
|
103
111
|
* *
|
|
@@ -328,6 +336,20 @@ export function getSiteLoadData(store, siteId) {
|
|
|
328
336
|
return null;
|
|
329
337
|
}
|
|
330
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Get the site's reference number from load data from the store
|
|
341
|
+
*
|
|
342
|
+
* @param {object} store The session store
|
|
343
|
+
* @param {string} siteId The site ID
|
|
344
|
+
* @returns {string|null} The reference number or null if not available
|
|
345
|
+
*/
|
|
346
|
+
export function getSiteLoadDataReferenceNumber(store, siteId) {
|
|
347
|
+
const ref = store?.siteData?.[siteId]?.loadData?.referenceValue;
|
|
348
|
+
|
|
349
|
+
return typeof ref === 'string' && ref.trim() !== '' ? ref : null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
331
353
|
/**
|
|
332
354
|
* Get the site's input data from the store
|
|
333
355
|
*
|
|
@@ -124,7 +124,7 @@ export function evaluateExpressionWithFlattening(expression, object, prefix = ''
|
|
|
124
124
|
* @returns {{ result: true } | { result: false, redirect: string }}
|
|
125
125
|
* An object indicating whether to render the page or redirect
|
|
126
126
|
*/
|
|
127
|
-
export function evaluatePageConditions(page, store, siteKey, req) {
|
|
127
|
+
export function evaluatePageConditions(page, store, siteKey, req = {}) {
|
|
128
128
|
// Get conditions array from nested page structure
|
|
129
129
|
const conditions = page?.pageData?.conditions;
|
|
130
130
|
|
|
@@ -5,16 +5,28 @@
|
|
|
5
5
|
* and show error summary when there are validation errors.
|
|
6
6
|
*/
|
|
7
7
|
import { ALLOWED_FORM_ELEMENTS } from "./govcyConstants.mjs";
|
|
8
|
+
import * as dataLayer from "./govcyDataLayer.mjs";
|
|
9
|
+
import * as govcyResources from "../resources/govcyResources.mjs";
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Helper function to populate form data with session data
|
|
12
14
|
* @param {Array} formElements The form elements
|
|
13
15
|
* @param {*} theData The data either from session or request
|
|
16
|
+
* @param {Object} validationErrors The validation errors
|
|
17
|
+
* @param {Object} store The session store
|
|
18
|
+
* @param {string} siteId The site ID
|
|
19
|
+
* @param {string} pageUrl The page URL
|
|
20
|
+
* @param {string} lang The language
|
|
21
|
+
* @param {Object} fileInputElements The file input elements
|
|
22
|
+
* @param {string} routeParam The route parameter
|
|
14
23
|
*/
|
|
15
|
-
export function populateFormData(formElements, theData, validationErrors) {
|
|
24
|
+
export function populateFormData(formElements, theData, validationErrors, store = {}, siteId = "", pageUrl = "", lang = "el", fileInputElements = null, routeParam = "") {
|
|
16
25
|
const inputElements = ALLOWED_FORM_ELEMENTS;
|
|
17
|
-
|
|
26
|
+
const isRootCall = !fileInputElements;
|
|
27
|
+
if (isRootCall) {
|
|
28
|
+
fileInputElements = {};
|
|
29
|
+
}
|
|
18
30
|
// Recursively populate form data with session data
|
|
19
31
|
formElements.forEach(element => {
|
|
20
32
|
if (inputElements.includes(element.element)) {
|
|
@@ -53,6 +65,27 @@ export function populateFormData(formElements, theData, validationErrors) {
|
|
|
53
65
|
// Invalid format (not matching D/M/YYYY or DD/MM/YYYY)
|
|
54
66
|
element.params.value = "";
|
|
55
67
|
}
|
|
68
|
+
} else if (element.element === "fileInput") {
|
|
69
|
+
// For fileInput, we change the element.element to "fileView" and set the
|
|
70
|
+
// fileId and sha256 from the session store
|
|
71
|
+
// unneeded handle of `Attachment` at the end
|
|
72
|
+
// const fileData = dataLayer.getFormDataValue(store, siteId, pageUrl, fieldName + "Attachment");
|
|
73
|
+
const fileData = dataLayer.getFormDataValue(store, siteId, pageUrl, fieldName);
|
|
74
|
+
// TODO: Ask Andreas how to handle empty file inputs
|
|
75
|
+
if (fileData) {
|
|
76
|
+
element.element = "fileView";
|
|
77
|
+
element.params.fileId = fileData.fileId;
|
|
78
|
+
element.params.sha256 = fileData.sha256;
|
|
79
|
+
element.params.visuallyHiddenText = element.params.label;
|
|
80
|
+
// TODO: Also need to set the `view` and `download` URLs
|
|
81
|
+
element.params.viewHref = `/${siteId}/${pageUrl}/view-file/${fieldName}`;
|
|
82
|
+
element.params.viewTarget = "_blank";
|
|
83
|
+
element.params.deleteHref = `/${siteId}/${pageUrl}/delete-file/${fieldName}${(routeParam) ? `?route=${routeParam}` : ''}`;
|
|
84
|
+
} else {
|
|
85
|
+
// TODO: Ask Andreas how to handle empty file inputs
|
|
86
|
+
element.params.value = "";
|
|
87
|
+
}
|
|
88
|
+
fileInputElements[fieldName] = element;
|
|
56
89
|
// Handle all other input elements (textInput, checkboxes, radios, etc.)
|
|
57
90
|
} else {
|
|
58
91
|
element.params.value = theData[fieldName] || "";
|
|
@@ -78,7 +111,7 @@ export function populateFormData(formElements, theData, validationErrors) {
|
|
|
78
111
|
if (element.element === "radios" && element.params.items) {
|
|
79
112
|
element.params.items.forEach(item => {
|
|
80
113
|
if (item.conditionalElements) {
|
|
81
|
-
populateFormData(item.conditionalElements, theData, validationErrors);
|
|
114
|
+
populateFormData(item.conditionalElements, theData, validationErrors,store, siteId , pageUrl, lang, fileInputElements, routeParam);
|
|
82
115
|
|
|
83
116
|
// Check if any conditional element has an error and add to the parent "conditionalHasErrors": true
|
|
84
117
|
if (item.conditionalElements.some(condEl => condEl.params?.error)) {
|
|
@@ -88,6 +121,29 @@ export function populateFormData(formElements, theData, validationErrors) {
|
|
|
88
121
|
});
|
|
89
122
|
}
|
|
90
123
|
});
|
|
124
|
+
// add file input elements's definition in js object
|
|
125
|
+
if (isRootCall && Object.keys(fileInputElements).length > 0) {
|
|
126
|
+
const scriptTag = `
|
|
127
|
+
<script type="text/javascript">
|
|
128
|
+
window._govcyFileInputs = ${JSON.stringify(fileInputElements)};
|
|
129
|
+
window._govcySiteId = "${siteId}";
|
|
130
|
+
window._govcyPageUrl = "${pageUrl}";
|
|
131
|
+
window._govcyLang = "${lang}";
|
|
132
|
+
</script>
|
|
133
|
+
<div id="_govcy-upload-status" class="govcy-visually-hidden" role="status" aria-live="assertive"></div>
|
|
134
|
+
<div id="_govcy-upload-error" class="govcy-visually-hidden" role="alert" aria-live="assertive"></div>
|
|
135
|
+
`.trim();
|
|
136
|
+
formElements.push({
|
|
137
|
+
element: 'htmlElement',
|
|
138
|
+
params: {
|
|
139
|
+
text: {
|
|
140
|
+
en: scriptTag,
|
|
141
|
+
el: scriptTag,
|
|
142
|
+
tr: scriptTag
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
91
147
|
}
|
|
92
148
|
|
|
93
149
|
|
|
@@ -96,9 +152,12 @@ export function populateFormData(formElements, theData, validationErrors) {
|
|
|
96
152
|
*
|
|
97
153
|
* @param {Array} elements - The form elements (including conditional ones).
|
|
98
154
|
* @param {Object} formData - The submitted form data.
|
|
155
|
+
* @param {Object} store - The session store .
|
|
156
|
+
* @param {string} siteId - The site ID .
|
|
157
|
+
* @param {string} pageUrl - The page URL .
|
|
99
158
|
* @returns {Object} filteredData - The filtered form data.
|
|
100
159
|
*/
|
|
101
|
-
export function getFormData(elements, formData) {
|
|
160
|
+
export function getFormData(elements, formData, store = {}, siteId = "", pageUrl = "") {
|
|
102
161
|
const filteredData = {};
|
|
103
162
|
elements.forEach(element => {
|
|
104
163
|
const { name } = element.params || {};
|
|
@@ -115,7 +174,7 @@ export function getFormData(elements, formData) {
|
|
|
115
174
|
if (item.conditionalElements) {
|
|
116
175
|
Object.assign(
|
|
117
176
|
filteredData,
|
|
118
|
-
getFormData(item.conditionalElements, formData)
|
|
177
|
+
getFormData(item.conditionalElements, formData, store, siteId, pageUrl)
|
|
119
178
|
);
|
|
120
179
|
}
|
|
121
180
|
});
|
|
@@ -130,6 +189,23 @@ export function getFormData(elements, formData) {
|
|
|
130
189
|
filteredData[`${name}_day`] = day !== undefined && day !== null ? day : "";
|
|
131
190
|
filteredData[`${name}_month`] = month !== undefined && month !== null ? month : "";
|
|
132
191
|
filteredData[`${name}_year`] = year !== undefined && year !== null ? year : "";
|
|
192
|
+
// handle fileInput
|
|
193
|
+
} else if (element.element === "fileInput") {
|
|
194
|
+
// fileInput elements are already stored in the store when it was uploaded
|
|
195
|
+
// so we just need to check if the file exists in the dataLayer in the store and add it the filteredData
|
|
196
|
+
// unneeded handle of `Attachment` at the end
|
|
197
|
+
// const fileData = dataLayer.getFormDataValue(store, siteId, pageUrl, name + "Attachment");
|
|
198
|
+
const fileData = dataLayer.getFormDataValue(store, siteId, pageUrl, name);
|
|
199
|
+
if (fileData) {
|
|
200
|
+
// unneeded handle of `Attachment` at the end
|
|
201
|
+
// filteredData[name + "Attachment"] = fileData;
|
|
202
|
+
filteredData[name] = fileData;
|
|
203
|
+
} else {
|
|
204
|
+
//TODO: Ask Andreas how to handle empty file inputs
|
|
205
|
+
// unneeded handle of `Attachment` at the end
|
|
206
|
+
// filteredData[name + "Attachment"] = ""; // or handle as needed
|
|
207
|
+
filteredData[name] = ""; // or handle as needed
|
|
208
|
+
}
|
|
133
209
|
// Handle other elements (e.g., textInput, textArea, datePicker)
|
|
134
210
|
} else {
|
|
135
211
|
filteredData[name] = formData[name] !== undefined && formData[name] !== null ? formData[name] : "";
|