App configuration
Edit on GitHubInstead of writing different components for different App configurations, we use JSON to determine the form for the configuration of the apps. There is a playground for NgxSchemaForm. There are predefined form elements and also custom form elements.
For the app configuration translation, see App configuration translation. For information about the translation, see Translation appendix.
Apps configuration form
Configuration widget JSON format:
{
"properties": {
<fieldId>: {
"widget": <widgetId>,
...otherFieldProperties
},
},
};
Example of the configuration widget:
{
"properties": {
"client_name": {
"type": "string",
"title": "Client Name",
"placeholder": "Enter the client name",
"isRequired": true,
"description": "Name of the client",
"widget": "string"
}
}
}
Display on the frontend:
Widget catalog
Common properties of a widget are:
type
: widget typewidget
: widgetId—see the available widgetIDs in the following sectionstitle
: display title of the fieldplaceholder
: string as a placeholder for the input fielddescription
: additional description for the fieldisRequired
: determines if the field is required with thetrue
andfalse
valuesdefault
: default value of the field
Form input widget
widgetId | display | Description |
---|---|---|
string | ![]() |
Default string input widget that allows the user to input a single line string. |
number | ![]() ![]() |
Allows the user to enter a number. |
date | ![]() |
Allows the user to enter a date. |
time | ![]() |
Allows the user to enter time. |
password | ![]() |
Allows the user to enter a masked string that is used for passwords or API keys. |
textarea | ![]() |
Allows the user to enter a multi-line string. |
file | ![]() |
Allows the user to upload a file. |
link | As a tag:![]() As a button: ![]() |
Displays a link as a tag or a button. Required properties: isButtonLink : false—display as a tag, true—display as a button. url : the url. target : set to _blank to open the url in a new tab. variant : (for button only)—primary or secondary button.See the link example under this table. |
notification | ![]() |
The notification widget is used to inform the user of some additional information. There cannot be any user input for this type of widget. Properties:
Seenotification exampleunder this table. |
link example
{
"properties": {
"credentials_clientName": {
"type": "string",
"widget": {
"id": "link"
},
"target": "_blank",
"url": "http://google.com",
"isButtonLink": true,
"variant": "primary",
"title": "click to google"
}
}
}
notification example
{
"properties": {
"notification": {
"type": "string",
"widget": {
"id": "notification"
},
"notificationType": "info",
"content": "Don’t have credentials yet? Visit <a href=\"https://google.com/\" target=\"_blank\">admin.usercentrics.eu/#/login</a> to create your account."
}
}
}
Selection widget
widgetId | display | Description |
---|---|---|
checkbox/ boolean | ![]() |
This widget is of type boolean , the input value can be either true or false . |
radio | ![]() |
The radio widgets rely on the oneOf property. The description is the displayed label for an option, while the enum is the value that is saved in the form. For example, if we select OSX in the form, the actual saved value is osx .Properties: oneOf : list of objects used as options.See radio example under this table. |
select | Single selection: ![]() ![]() |
Similar to the radio widget, but instead of radio buttons, you can use select for selection in a dropdown. Besides, this widget also allows multiple options selection by setting multiple: true .Properties:
multipleOptions or enum instead of oneOf .See example for select without oneOf under this table. |
app-status | ![]() |
This custom widget shows only Active (true ) or Inactive (false ) value to fit the design.See app-status example under this table. |
radio example
{
"properties": {
"info": {
"type": "string",
"title": "Operation system",
"description": "Operation system for the demo store",
"widget": "radio",
"oneOf": [
{
"enum": ["linux"],
"description": "GNU/Linux"
},
{
"enum": ["osx"],
"description": "OSX"
},
{
"enum": ["windows"],
"description": "Windows"
},
{
"enum": ["other"],
"description": "Other"
}
],
"default": "other"
}
}
}
example for select with oneOf
{
"properties": {
"info": {
"type": "string",
"title": "Operation system",
"description": "Operation system for the demo store",
"widget": "select",
"multiple": true,
"oneOf": [
{
"enum": ["linux"],
"description": "GNU/Linux"
},
{
"enum": ["osx"],
"description": "OSX"
},
{
"enum": ["windows"],
"description": "Windows"
},
{
"enum": ["other"],
"description": "Other"
}
],
"default": "other"
}
}
}
example for select without oneOf
{
"properties": {
"info": {
"type": "string",
"title": "Store",
"description": "Store location",
"widget": "select",
"multipleOptions": [
"DE",
"AT",
"US"
],
"multiple": true
}
}
}
app-status example
{
"properties": {
"application_status_isActive": {
"type": "boolean",
"title": "Application status",
"hint": "Setting to Active will make the app visible on your Storefront",
"widget": {
"id": "app-status"
},
"default": false
}
}
}
Form layout
For the form layout, in particular, you should take into consideration the array
type and the order of fields.
Array type
Sometimes, you might ask a user to input one or more values of the same field sets. For example, when you ask for all the pets within a household. These values can differ from each other, as there could be 0, or 10, or 100 pets. In these cases, you can use the array
type of the form:
{
"properties": {
"pet": {
"type": "array",
"addButtonText": "Add pet",
"items": {
"type": "object",
"properties": {
"petType": {
"type": "string",
"title": "Pet type",
"placeholder": "Dog/ Cat, etc."
},
"petName": {
"type": "string",
"title": "Pet name"
}
}
}
}
}
}
Where:
addButtonText
: text of the Add button.items
: definition of fields within an item. The form returns an array of objects defined initems
:The returned object looks like this:
{
"pet": [
{
"petType": "Dog",
"petName": "Lulu"
},
{
"petType": "Cat",
"petName": "Meow meow"
}
]
}
Order of fields
The properties
within the form configuration are to define the form’s fields and their corresponding widgets. By default, the fields are displayed in that order also. In some cases, you can use the order
or fieldsets
property to change the order of the fields.
The order
property changes the order of the fields within the form:
{
"properties": {
"firstName": {
"type": "string",
"description": "First name"
},
"lastName": {
"type": "string",
"description": "Last name"
},
"email": {
"type": "string",
"description": "Email"
}
},
"order": ["lastName", "firstName", "email"]
}
Here is how it looks on the frontend:
To group different fields into different sections, you can define the fieldsets
property:
{
"properties": {
"firstName": {
"type": "string",
"description": "First name"
},
"lastName": {
"type": "string",
"description": "Last name"
},
"email": {
"type": "string",
"description": "Email"
}
},
"fieldsets": [
{
"id": "client_name",
"title": "Client name",
"fields": ["firstName", "lastName"]
},
{
"id": "client_email",
"title": "Client email",
"fields": ["email"]
}
]
}
This is how it looks on the frontend:
If you don’t want any layout for a section, you can set noLayout
to the layout
property:
{
"properties": {
"firstName": {
"type": "string",
"description": "First name"
},
"lastName": {
"type": "string",
"description": "Last name"
},
"email": {
"type": "string",
"description": "Email"
}
},
"fieldsets": [
{
"id": "client_name",
"title": "Client name",
"layout": "noLayout",
"fields": ["firstName", "lastName"]
},
{
"id": "client_email",
"title": "Client email",
"fields": ["email"]
}
]
}
This is how it looks on the frontend:
Full configuration example
Here is an example of the full configuration:
Full configuration example
{
"properties": {
"notification": {
"type": "string",
"widget": {
"id": "notification"
},
"notificationType": "info",
"content": "Don’t have credentials yet? Visit <a href=\"https://google.com/\" target=\"_blank\">admin.usercentrics.eu/#/login</a> to create your account."
},
"userCentricIntegrationType": {
"type": "string",
"widget": {
"id": "radio"
},
"oneOf": [
{
"description": "Enable Smart Data Protector (Default)",
"enum": ["SMART_DATA_PROTECTOR"]
},
{
"description": "Enable Direct Integration (works only with Google Tag Manager)",
"enum": ["DIRECT"]
}
]
},
"storeSettings": {
"type": "array",
"addButtonText": "Add Store Settings",
"items": {
"type": "object",
"properties": {
"storeName": {
"type": "string",
"title": "Store",
"widget": {
"id": "select"
},
"multiple": true,
"multipleOptions": [
"AT",
"DE",
"US"
]
},
"userCentricSettingIdentifier": {
"type": "string",
"title": "Setting ID"
},
"isActive": {
"type": "boolean",
"title": "Store setting is active",
"widget": {
"id": "checkbox"
},
"default": false
}
},
"fieldsets": [
{
"id": "storeSettings",
"fields": [
"storeName",
"userCentricSettingIdentifier",
"isActive"
]
}
],
"widget": {
"id": "object"
}
},
"widget": {
"id": "array"
}
}
},
"fieldsets": [
{
"id": "notifications",
"layout": "noLayout",
"fields": ["notification"]
},
{
"id": "usercentric_integration",
"title": "Usercentric Integration",
"name": "usercentric_integration",
"fields": ["userCentricIntegrationType"]
},
{
"id": "usercentric_stories",
"title": "User consent managment per store",
"name": "usercentric_integration",
"fields": ["storeSettings"]
}
]
}
This is how the configuration looks in the Back Office:
Translation appendix
Translation for an app configuration is provided in the app-store-suite/app/config/<app-name>/translation.json
file. Each field defined in the JSON’s properties
needs to match its corresponding translation entity in the translation.json
file. For example, to translate the title
of the widget isLiveMode
, we provide "title": "isLiveMode"
in the app configuration JSON.
translation.json file example:
{
"isLiveMode": {
"de_DE": "Auswahl des Modus für Payone Umgebung",
"en_US": "Select Payone Environment Mode"
},
"isLiveMode_test": {
"de_DE": "Test",
"en_US": "Test"
},
"isLiveMode_live": {
"de_DE": "Live",
"en_US": "Live"
},
"payoneEnvironmentMode": {
"de_DE": "Payone Umgebung",
"en_US": "Payone Environment Mode"
}
}
configuration.json file example:
{
"properties": {
"isLiveMode": {
"type": "string",
"title": "isLiveMode",
"widget": {
"id": "radio"
},
"oneOf": [
{
"description": "isLiveMode_test",
"enum": ["0"]
},
{
"description": "isLiveMode_live",
"enum": ["1"]
}
]
},
}
}
This is how the result of the configuration looks on the frontend:
DE shop:
EN shop:
Form validation (server side)
The app will receive the request from the configuration form to the URL defined in your api.json
file, for example, like this:
{
"configuration": "/private/configure",
"disconnection": "/private/disconnect"
}
In the cURL request, it will look like:
curl -x POST 'https://your-app.domain.name/private/configure' \
-H 'Accept-Language: en' \
-H 'X-Tenant-Identifier: tenant-uuid' \
--data-raw '{
"data": {
"attributes": {
"configuration": "{\"fieldName1\":\"value1\", \"fieldName2\":\"value2\"}"
}
}
}'
The app response format for a valid request is just the HTTP status 200
.
The app response format for an invalid request could be an error for the whole form:
{
"errors": [
{
"code": 443,
"message": "human readable message for a user, localized",
"status": 400
}
]
}
or field-specific messages in the following format:
{
"errors": [
{
"code": 444,
"message": "{"\"fieldName1\": \"errorMessage\", \"fieldName2\": \"errorMessage\"}",
"status": 400
}
]
}
In both cases HTTP status has to be 400
.
The current limitation for the number of displayed errors from the app response is 1.
Thank you!
For submitting the form