@gov-cy/govcy-express-services 1.5.0 → 1.6.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
CHANGED
|
@@ -31,7 +31,7 @@ The APIs used for submission, temporary save and file uploads are not part of th
|
|
|
31
31
|
- [📦 Full installation guide](#-full-installation-guide)
|
|
32
32
|
- [🛠️ Usage](#%EF%B8%8F-usage)
|
|
33
33
|
- [🔑 Authentication Middleware](#-authentication-middleware)
|
|
34
|
-
-[
|
|
34
|
+
- [cyLogin Access Policies](#cylogin-access-policies)
|
|
35
35
|
- [🧩 Dynamic services](#-dynamic-services)
|
|
36
36
|
- [Pages](#pages)
|
|
37
37
|
- [Form vs static pages](#form-vs-static-pages)
|
|
@@ -773,6 +773,8 @@ Here is an example JSON config:
|
|
|
773
773
|
Here are some details explaining the JSON structure:
|
|
774
774
|
|
|
775
775
|
- `site` object: Contains information about the site, including the site ID, language, and footer links. See [govcy-frontend-renderer](https://github.com/gov-cy/govcy-frontend-renderer/tree/main#site-and-page-meta-data-explained) for more details. Some fields that are only specific to the govcy-express-forms project are the following:
|
|
776
|
+
- `usesDSFSubmissionPlatform`: A boolean that indicates whether the service uses the DSF submission platform (transforms submission data as needed)
|
|
777
|
+
- `cyLoginPolicies`: which types of authenticated CY Login profiles are allowed to access the service
|
|
776
778
|
- `submissionDataVersion` : The submission data version,
|
|
777
779
|
- `rendererVersion` : The govcy-frontend-renderer version,
|
|
778
780
|
- `designSystemsVersion` : The govcy-design-system version,
|
|
@@ -2661,227 +2663,8 @@ To help back-end systems recognize the field as a file, the field's element name
|
|
|
2661
2663
|
If these endpoints are not defined in the service JSON, the file upload logic is skipped entirely.
|
|
2662
2664
|
Existing services will continue to work without modification.
|
|
2663
2665
|
|
|
2664
|
-
|
|
2665
|
-
The **Custom pages** feature allows developers to
|
|
2666
|
-
|
|
2667
|
-
These pages can collect data, display conditional content, and inject custom sections into the **review** and **email** stages of the service flow.
|
|
2668
|
-
|
|
2669
|
-
#### What do custom pages get out of the framework
|
|
2670
|
-
With the custom pages feature, developers can define **pages** and **routes** on an express **service**. On these pages the following apply out of the box:
|
|
2671
|
-
- Login requirement
|
|
2672
|
-
- User policy requirement
|
|
2673
|
-
- Any service eligibility checks
|
|
2674
|
-
- Csrf protection on POST
|
|
2675
|
-
- Generic error page on errors
|
|
2676
|
-
|
|
2677
|
-
The developers can also choose to:
|
|
2678
|
-
- store values in the session to be submitted via the submission API
|
|
2679
|
-
- define where and what the users sees in the `review` page
|
|
2680
|
-
- define errors regarding the custom page in the `review` page
|
|
2681
|
-
- define what the user receives the `email`
|
|
2682
|
-
|
|
2683
|
-
#### What the framework expects from custom pages
|
|
2684
|
-
|
|
2685
|
-
- Every custom page **must be defined** at startup using `defineCustomPages(app, siteId, pageUrl, ...)`.
|
|
2686
|
-
- Custom pages are **not** automatically discovered, they must be registered explicitly.
|
|
2687
|
-
- Each page must specify on **start up**:
|
|
2688
|
-
- `pageTitle`: multilingual object for display in review and email
|
|
2689
|
-
- `insertAfterPageUrl`: the page **after which** this custom page will appear
|
|
2690
|
-
- `summaryElements`, `errors`, and `email` are managed dynamically in session
|
|
2691
|
-
- **NOTE**: Usually a default `required` error must be defined on start up. This will ensure that the when the users try to submit in the “Check your answers” review page, they get an error message to complete an action.
|
|
2692
|
-
- `extraProps` extra property stored in the session to be used by the developers as they wish
|
|
2693
|
-
- During **runtime**, developers are responsible for setting:
|
|
2694
|
-
- `data`: data to include in submission, using the `setCustomPageData` function
|
|
2695
|
-
- `summaryElements`: what appears in the “Check your answers” review page, using the `setCustomPageSummaryElements` function
|
|
2696
|
-
- `email`: array of [`dsf-email-templates`](https://github.com/gov-cy/dsf-email-templates) components for the submission confirmation email, using the `setCustomPageEmail`
|
|
2697
|
-
- `errors`: errors that appear in the “Check your answers” review page. Developers can use the `addCustomPageError` function to add an error, or the `clearCustomPageErrors` to clear the errors
|
|
2698
|
-
#### Available methods
|
|
2699
|
-
|
|
2700
|
-
| Method | Purpose |
|
|
2701
|
-
| ------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
|
|
2702
|
-
| `defineCustomPages(store, siteId, pageUrl, pageTitle, insertAfterSummary, insertAfterPageUrl, errors, summaryElements, summaryHtml, extraProps)` | Registers a custom page definition in `app`. Must be called once at startup. |
|
|
2703
|
-
| `getCustomPageDefinition(store, siteId, pageUrl)` | Retrieves the custom page definition for a given siteId and pageUrl |
|
|
2704
|
-
| `resetCustomPages(configStore, store, siteId)` | Resets per-session data from the global definitions. |
|
|
2705
|
-
| `setCustomPageData(store, siteId, pageUrl, dataObject)` | Sets or replaces the data object used during submission. |
|
|
2706
|
-
| `setCustomPageSummaryElements(store, siteId, pageUrl, summaryElements)` | Sets what appears in the review page summary. |
|
|
2707
|
-
| `clearCustomPageErrors(store, siteId, pageUrl)` | Clears validation errors. |
|
|
2708
|
-
| `addCustomPageError(store, siteId, pageUrl, errorId, errorTextObject)` | Adds a validation error. |
|
|
2709
|
-
| `setCustomPageEmail(store, siteId, pageUrl, arrayOfEmailObjects)` | Defines email sections for the confirmation email using `dsf-email-templates` components. |
|
|
2710
|
-
| `setCustomPageProperty(store, siteId, pageUrl, property, value, isDefinition = false)` | Sets a custom property on a given custom page definition or instance |
|
|
2711
|
-
| `getCustomPageProperty(store, siteId, pageUrl, property, isDefinition = false)` | Gets a custom property from a given custom page definition or instance. |
|
|
2712
|
-
|
|
2713
|
-
---
|
|
2714
|
-
##### Example
|
|
2715
|
-
|
|
2716
|
-
Below is a practical example of a custom **Declarations** page added to an existing service.
|
|
2717
|
-
|
|
2718
|
-
```js
|
|
2719
|
-
import initializeGovCyExpressService from '@gov-cy/govcy-express-services';
|
|
2720
|
-
import {
|
|
2721
|
-
defineCustomPages, resetCustomPages,
|
|
2722
|
-
clearCustomPageErrors, addCustomPageError,
|
|
2723
|
-
setCustomPageData, setCustomPageSummaryElements,
|
|
2724
|
-
setCustomPageEmail, getCustomPageProperty, setCustomPageProperty
|
|
2725
|
-
} from '@gov-cy/govcy-express-services/customPages';
|
|
2726
|
-
|
|
2727
|
-
// Initialize the service
|
|
2728
|
-
const service = initializeGovCyExpressService({
|
|
2729
|
-
beforeMount({ siteRoute, app }) {
|
|
2730
|
-
|
|
2731
|
-
// ==========================================================
|
|
2732
|
-
// 1️⃣ DEFINE GLOBAL CUSTOM PAGE CONFIGS (once per app)
|
|
2733
|
-
// ==========================================================
|
|
2734
|
-
defineCustomPages(
|
|
2735
|
-
app, // <-- using app as store (global config)
|
|
2736
|
-
"cso", // siteId
|
|
2737
|
-
"/cso/custom", // pageUrl
|
|
2738
|
-
{ en: "My custom section", el: "Προσαρμοσμένη ενότητα" }, // pageTitle
|
|
2739
|
-
"qualifications", // insertAfterPageUrl
|
|
2740
|
-
[ // errors
|
|
2741
|
-
{
|
|
2742
|
-
id: "custom-error",
|
|
2743
|
-
text: {
|
|
2744
|
-
en: "This is a custom error custom",
|
|
2745
|
-
el: "Αυτή ειναι ενα προσαρμοσμένη σφαλμα custom",
|
|
2746
|
-
}
|
|
2747
|
-
}
|
|
2748
|
-
],
|
|
2749
|
-
[ // summaryElements
|
|
2750
|
-
{
|
|
2751
|
-
key:
|
|
2752
|
-
{
|
|
2753
|
-
en: "Extra value",
|
|
2754
|
-
el: "Πρόσθετη τιμή"
|
|
2755
|
-
},
|
|
2756
|
-
value: []
|
|
2757
|
-
}
|
|
2758
|
-
],
|
|
2759
|
-
false,
|
|
2760
|
-
{ // other custom properties if needed like nextPage or initial data
|
|
2761
|
-
nextPage: "/cso/memberships", // custom property nextPage. Not needed by Express but useful for the custom logic
|
|
2762
|
-
data : // custom initial data. Useful when you need the data model to be standard or pre-populated
|
|
2763
|
-
{
|
|
2764
|
-
something: "",
|
|
2765
|
-
}
|
|
2766
|
-
}
|
|
2767
|
-
);
|
|
2768
|
-
|
|
2769
|
-
// ==========================================================
|
|
2770
|
-
// 2️⃣ MIDDLEWARE: SET UP SESSION DATA FROM GLOBAL DEFINITIONS
|
|
2771
|
-
// ==========================================================
|
|
2772
|
-
app.use((req, res, next) => {
|
|
2773
|
-
// Initialize session data for custom pages
|
|
2774
|
-
req.session.siteData ??= {};
|
|
2775
|
-
req.session.siteData["cso"] ??= {};
|
|
2776
|
-
|
|
2777
|
-
// Reset session copies if missing (first visit)
|
|
2778
|
-
if (!req.session.siteData["cso"].customPages) {
|
|
2779
|
-
resetCustomPages(app, req.session, "cso"); // 🔁 deep copy from app to session
|
|
2780
|
-
req.session.save((err) => {
|
|
2781
|
-
if (err) console.error("⚠️ Error initializing customPages:", err);
|
|
2782
|
-
});
|
|
2783
|
-
}
|
|
2784
|
-
|
|
2785
|
-
next();
|
|
2786
|
-
});
|
|
2787
|
-
|
|
2788
|
-
// ==========================================================
|
|
2789
|
-
// 3️⃣ CUSTOM ROUTES (still per-user)
|
|
2790
|
-
// ==========================================================
|
|
2791
|
-
|
|
2792
|
-
// GET `/cso/custom`
|
|
2793
|
-
siteRoute("cso", "get", "/cso/custom", (req, res) => {
|
|
2794
|
-
|
|
2795
|
-
// Render the custom page using the session data
|
|
2796
|
-
// It is important for POST to add the csrfToken and to pass on the route query parameter
|
|
2797
|
-
res.send(`<form action="/cso/custom${(req.query.route === "review")?"?route=review":""}" method="post">
|
|
2798
|
-
custom for ${req.params.siteId}
|
|
2799
|
-
<input type="hidden" name="_csrf" value="${req.csrfToken()}">
|
|
2800
|
-
<button type="submit">Submit custom page</button>
|
|
2801
|
-
</form>`);
|
|
2802
|
-
|
|
2803
|
-
});
|
|
2804
|
-
|
|
2805
|
-
// POST `/cso/custom`
|
|
2806
|
-
siteRoute("cso", "post", "/cso/custom", (req, res) => {
|
|
2807
|
-
|
|
2808
|
-
// Update custom page `data` dynamically`
|
|
2809
|
-
setCustomPageData(req.session, "cso", "/cso/custom", {
|
|
2810
|
-
something: "123",
|
|
2811
|
-
});
|
|
2812
|
-
|
|
2813
|
-
// Update `summary elements` dynamically (example)
|
|
2814
|
-
setCustomPageSummaryElements(req.session, "cso", "/cso/custom",
|
|
2815
|
-
[
|
|
2816
|
-
{
|
|
2817
|
-
key: {
|
|
2818
|
-
en: "Extra value",
|
|
2819
|
-
el: "Πρόσθετη τιμή"
|
|
2820
|
-
},
|
|
2821
|
-
value: [
|
|
2822
|
-
{
|
|
2823
|
-
element: "textElement",
|
|
2824
|
-
params: {
|
|
2825
|
-
text: {
|
|
2826
|
-
en: "123 Changed",
|
|
2827
|
-
el: "123 Αλλάχθηκε"
|
|
2828
|
-
},
|
|
2829
|
-
type: "span",
|
|
2830
|
-
showNewLine: true,
|
|
2831
|
-
}
|
|
2832
|
-
}
|
|
2833
|
-
]
|
|
2834
|
-
}
|
|
2835
|
-
]);
|
|
2836
|
-
|
|
2837
|
-
// Update the custom page `email`
|
|
2838
|
-
setCustomPageEmail(req.session, "cso", "/cso/custom", [
|
|
2839
|
-
{
|
|
2840
|
-
component: "bodyKeyValue",
|
|
2841
|
-
params: {
|
|
2842
|
-
type: "ul",
|
|
2843
|
-
items: [
|
|
2844
|
-
{ key: "Extra value", value: "123" },
|
|
2845
|
-
{ key: "Priority level", value: "High" },
|
|
2846
|
-
],
|
|
2847
|
-
},
|
|
2848
|
-
}
|
|
2849
|
-
]);
|
|
2850
|
-
|
|
2851
|
-
// clear any previous errors
|
|
2852
|
-
clearCustomPageErrors(req.session, "cso", "/cso/custom");
|
|
2853
|
-
|
|
2854
|
-
// Add a custom error
|
|
2855
|
-
// addCustomPageError(req.session, "cso", "/cso/custom", {
|
|
2856
|
-
// id: "custom-error",
|
|
2857
|
-
// text: {
|
|
2858
|
-
// en: "This is a custom error custom",
|
|
2859
|
-
// el: "Αυτή ειναι ενα προσαρμοσμένη σφαλμα custom",
|
|
2860
|
-
// }
|
|
2861
|
-
// });
|
|
2862
|
-
|
|
2863
|
-
//if route is review, redirect to review page else go to nextPage property
|
|
2864
|
-
if (req.query.route === "review") {
|
|
2865
|
-
res.redirect("/cso/review");
|
|
2866
|
-
return;
|
|
2867
|
-
} else {
|
|
2868
|
-
//example of using custom property nextPage
|
|
2869
|
-
res.redirect(getCustomPageProperty(req.session, "cso", "/cso/custom", "nextPage", false));
|
|
2870
|
-
return;
|
|
2871
|
-
}
|
|
2872
|
-
});
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
// ==========================================================
|
|
2876
|
-
},
|
|
2877
|
-
});
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
// Start the server
|
|
2881
|
-
service.startServer();
|
|
2882
|
-
|
|
2883
|
-
```
|
|
2884
|
-
|
|
2666
|
+
### ✨ Custom pages feature
|
|
2667
|
+
The **Custom pages** feature allows developers to code service-specific custom pages that exist outside the standard Express Services JSON configuration. More details at [Custom-pages.md](docs/Custom-pages.md)
|
|
2885
2668
|
|
|
2886
2669
|
### 🛣️ Routes
|
|
2887
2670
|
The project uses express.js to serve the following routes:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gov-cy/govcy-express-services",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
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",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"coverage:badge": "coverage-badges --output ./coverage-badges.svg && npm run coverage:copy"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@gov-cy/dsf-email-templates": "^2.1.
|
|
59
|
+
"@gov-cy/dsf-email-templates": "^2.1.11",
|
|
60
60
|
"@gov-cy/govcy-frontend-renderer": "^1.26.0",
|
|
61
61
|
"axios": "^1.9.0",
|
|
62
62
|
"cookie-parser": "^1.4.7",
|
|
@@ -184,7 +184,7 @@ export function govcyReviewPostHandler() {
|
|
|
184
184
|
const submissionData = prepareSubmissionData(req, siteId, service);
|
|
185
185
|
|
|
186
186
|
// Prepare submission data for API
|
|
187
|
-
const submissionDataAPI = prepareSubmissionDataAPI(submissionData);
|
|
187
|
+
const submissionDataAPI = prepareSubmissionDataAPI(submissionData, service);
|
|
188
188
|
|
|
189
189
|
logger.debug("Prepared submission data for API:", submissionDataAPI);
|
|
190
190
|
|
|
@@ -133,7 +133,7 @@ export function populateFormData(
|
|
|
133
133
|
// Delete link (preserve ?route=review if present)
|
|
134
134
|
element.params.deleteHref = `${basePath}/delete-file/${fieldName}${(routeParam) ? `?route=${routeParam}` : ''}`
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
} else {
|
|
138
138
|
// TODO: Ask Andreas how to handle empty file inputs
|
|
139
139
|
element.params.value = "";
|
|
@@ -295,3 +295,74 @@ export function getFormData(elements, formData, store = {}, siteId = "", pageUrl
|
|
|
295
295
|
|
|
296
296
|
return filteredData;
|
|
297
297
|
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get empty form data for multipleThings elements.
|
|
301
|
+
* Used to fill in empty multipleThings pages with the correct structure
|
|
302
|
+
*
|
|
303
|
+
* @param {object|Array} pageOrElements The page or elements of a conditional radio
|
|
304
|
+
* @param {object} emptyObject The object to populate with empty values
|
|
305
|
+
* @returns {object} An object with empty values for each form element
|
|
306
|
+
*/
|
|
307
|
+
export function getMultipleThingsEmptyFormData(pageOrElements, emptyObject = {}) {
|
|
308
|
+
// Determine if we're given a full page or just an array of elements
|
|
309
|
+
let elements = [];
|
|
310
|
+
if (Array.isArray(pageOrElements)) {
|
|
311
|
+
elements = pageOrElements; // recursion case
|
|
312
|
+
} else if (pageOrElements?.pageTemplate?.sections) {
|
|
313
|
+
// Deep copy to avoid modifying the original
|
|
314
|
+
const pageTemplateCopy = JSON.parse(JSON.stringify(pageOrElements.pageTemplate));
|
|
315
|
+
// Find the first form element in sections
|
|
316
|
+
for (const section of pageTemplateCopy.sections) {
|
|
317
|
+
const form = section.elements?.find(el => el.element === "form");
|
|
318
|
+
if (form) {
|
|
319
|
+
elements = form?.params?.elements || [];
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
// No valid elements
|
|
325
|
+
return emptyObject;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Iterate through elements in order (like getFormData)
|
|
329
|
+
elements.forEach(element => {
|
|
330
|
+
const { name } = element.params || {};
|
|
331
|
+
|
|
332
|
+
if (ALLOWED_FORM_ELEMENTS.includes(element.element) && name) {
|
|
333
|
+
// Handle different element types
|
|
334
|
+
if (["checkboxes", "radios", "select"].includes(element.element)) {
|
|
335
|
+
emptyObject[name] = "";
|
|
336
|
+
|
|
337
|
+
// Handle conditional radios (same recursion pattern as getFormData)
|
|
338
|
+
if (element.element === "radios" && Array.isArray(element.params?.items)) {
|
|
339
|
+
element.params.items.forEach(item => {
|
|
340
|
+
if (item.conditionalElements) {
|
|
341
|
+
Object.assign(
|
|
342
|
+
emptyObject,
|
|
343
|
+
getMultipleThingsEmptyFormData(item.conditionalElements, {})
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Handle dateInput (single name, per your clarification)
|
|
351
|
+
else if (element.element === "dateInput") {
|
|
352
|
+
emptyObject[name] = "";
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Handle fileInput (suffix Attachment, per submission schema)
|
|
356
|
+
else if (element.element === "fileInput") {
|
|
357
|
+
emptyObject[`${name}Attachment`] = "";
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Handle all other elements (textInput, textArea, datePicker, etc.)
|
|
361
|
+
else {
|
|
362
|
+
emptyObject[name] = "";
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
return emptyObject;
|
|
368
|
+
}
|
|
@@ -4,7 +4,8 @@ import * as dataLayer from "./govcyDataLayer.mjs";
|
|
|
4
4
|
import { DSFEmailRenderer } from '@gov-cy/dsf-email-templates';
|
|
5
5
|
import { ALLOWED_FORM_ELEMENTS } from "./govcyConstants.mjs";
|
|
6
6
|
import { evaluatePageConditions } from "./govcyExpressions.mjs";
|
|
7
|
-
import { getPageConfigData } from "./govcyLoadConfigData.mjs";
|
|
7
|
+
import { getServiceConfigData, getPageConfigData } from "./govcyLoadConfigData.mjs";
|
|
8
|
+
import { getMultipleThingsEmptyFormData } from "./govcyFormHandling.mjs";
|
|
8
9
|
import { logger } from "./govcyLogger.mjs";
|
|
9
10
|
import { createUmdManualPageTemplate } from "../middleware/govcyUpdateMyDetails.mjs"
|
|
10
11
|
import nunjucks from "nunjucks";
|
|
@@ -252,21 +253,48 @@ export function prepareSubmissionData(req, siteId, service) {
|
|
|
252
253
|
/**
|
|
253
254
|
* Prepares the submission data for the API, stringifying all relevant fields.
|
|
254
255
|
*
|
|
255
|
-
* @param {object} data data prepared by `prepareSubmissionData
|
|
256
|
+
* @param {object} data data prepared by `prepareSubmissionData`\
|
|
257
|
+
* @param {object} service service config data
|
|
256
258
|
* @returns {object} The API-ready submission data object with all fields as strings
|
|
257
259
|
*/
|
|
258
|
-
export function prepareSubmissionDataAPI(data) {
|
|
260
|
+
export function prepareSubmissionDataAPI(data, service) {
|
|
261
|
+
|
|
262
|
+
//deep copy data to avoid mutating the original
|
|
263
|
+
let dataObj = JSON.parse(JSON.stringify(data));
|
|
264
|
+
// get site?.submissionAPIEndpoint.isDSFSubmissionPlatform
|
|
265
|
+
const isDSFSubmissionPlatform = service?.site?.usesDSFSubmissionPlatform || false;
|
|
266
|
+
|
|
267
|
+
// If DSF Submission Platform, ensure submissionData is an array
|
|
268
|
+
// this is intended for multipleThings pages only
|
|
269
|
+
if (isDSFSubmissionPlatform) {
|
|
270
|
+
// loop through submissionData
|
|
271
|
+
for (const [key, value] of Object.entries(dataObj.submissionData || {})) {
|
|
272
|
+
// check if the value is an empty array
|
|
273
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
274
|
+
// get the pageConfigData for the page
|
|
275
|
+
try {
|
|
276
|
+
const page = getPageConfigData(service, key);
|
|
277
|
+
let pageEmptyData = getMultipleThingsEmptyFormData(page);
|
|
278
|
+
//replace the dataObj.submissionData[key] with the empty data
|
|
279
|
+
dataObj.submissionData[key] = [pageEmptyData];
|
|
280
|
+
|
|
281
|
+
} catch (error) {
|
|
282
|
+
logger.error('Error getting pageConfigData:', key);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
259
287
|
|
|
260
288
|
return {
|
|
261
|
-
submissionUsername: String(
|
|
262
|
-
submissionEmail: String(
|
|
263
|
-
submissionData: JSON.stringify(
|
|
264
|
-
submissionDataVersion: String(
|
|
265
|
-
printFriendlyData: JSON.stringify(
|
|
266
|
-
rendererData: JSON.stringify(
|
|
267
|
-
rendererVersion: String(
|
|
268
|
-
designSystemsVersion: String(
|
|
269
|
-
service: JSON.stringify(
|
|
289
|
+
submissionUsername: String(dataObj.submissionUsername ?? ""),
|
|
290
|
+
submissionEmail: String(dataObj.submissionEmail ?? ""),
|
|
291
|
+
submissionData: JSON.stringify(dataObj.submissionData ?? {}),
|
|
292
|
+
submissionDataVersion: String(dataObj.submissionDataVersion ?? ""),
|
|
293
|
+
printFriendlyData: JSON.stringify(dataObj.printFriendlyData ?? []),
|
|
294
|
+
rendererData: JSON.stringify(dataObj.rendererData ?? {}),
|
|
295
|
+
rendererVersion: String(dataObj.rendererVersion ?? ""),
|
|
296
|
+
designSystemsVersion: String(dataObj.designSystemsVersion ?? ""),
|
|
297
|
+
service: JSON.stringify(dataObj.service ?? {})
|
|
270
298
|
};
|
|
271
299
|
}
|
|
272
300
|
|
|
@@ -28,6 +28,7 @@ function validateValue(value, rules) {
|
|
|
28
28
|
alpha: (val) => /^[A-Za-zΑ-Ωα-ω\u0370-\u03ff\u1f00-\u1fff\s]+$/.test(val),
|
|
29
29
|
alphaNum: (val) => /^[A-Za-zΑ-Ωα-ω\u0370-\u03ff\u1f00-\u1fff0-9\s]+$/.test(val),
|
|
30
30
|
noSpecialChars: (val) => /^([0-9]|[A-Z]|[a-z]|[α-ω]|[Α-Ω]|[,]|[.]|[-]|[(]|[)]|[?]|[!]|[;]|[:]|[\n]|[\r]|[ _]|[\u0370-\u03ff\u1f00-\u1fff])+$/.test(val),
|
|
31
|
+
noSpecialCharsEl: (val) => /^([0-9]|[α-ω]|[Α-Ω]|[,]|[.]|[-]|[(]|[)]|[?]|[!]|[;]|[:]|[\n]|[\r]|[ _]|[\u0370-\u03ff\u1f00-\u1fff])+$/.test(val),
|
|
31
32
|
name: (val) => /^[A-Za-zΑ-Ωα-ω\u0370-\u03ff\u1f00-\u1fff\s'-]+$/.test(val),
|
|
32
33
|
tel: (val) => /^(?:\+|00)?[\d\s\-()]{8,20}$/.test(val.replace(/[\s\-()]/g, '')),
|
|
33
34
|
mobile: (val) => /^(?:\+|00)?[\d\s\-()]{8,20}$/.test(val.replace(/[\s\-()]/g, '')),
|