feat(#13): Show form elements depending on other form element values
This commit is contained in:
92
CLAUDE.md
92
CLAUDE.md
@@ -97,12 +97,19 @@ Application Form
|
|||||||
└── SubSection (FormElementSubSection)
|
└── SubSection (FormElementSubSection)
|
||||||
├── title, subtitle
|
├── title, subtitle
|
||||||
└── Form Elements (FormElement)
|
└── Form Elements (FormElement)
|
||||||
|
├── id (UUID - generated by backend)
|
||||||
|
├── reference (string - custom key like "art_der_massnahme")
|
||||||
├── type (SELECT, CHECKBOX, RADIOBUTTON, TEXTFIELD, SWITCH, TITLE_BODY_TEXTFIELDS)
|
├── type (SELECT, CHECKBOX, RADIOBUTTON, TEXTFIELD, SWITCH, TITLE_BODY_TEXTFIELDS)
|
||||||
├── title, description
|
├── title, description
|
||||||
└── options (FormOption[])
|
├── options (FormOption[])
|
||||||
├── value, label
|
│ ├── value, label
|
||||||
├── processingPurpose
|
│ ├── processingPurpose
|
||||||
└── employeeDataCategory
|
│ └── employeeDataCategory
|
||||||
|
└── visibilityCondition (FormElementVisibilityCondition)
|
||||||
|
├── conditionType (SHOW, HIDE)
|
||||||
|
├── sourceFormElementReference (string - reference key of source element)
|
||||||
|
├── expectedValue (string - value to compare against)
|
||||||
|
└── operator (EQUALS, NOT_EQUALS, IS_EMPTY, IS_NOT_EMPTY)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Dynamic Addition**: Users can add new form elements to any subsection at runtime via the API endpoint:
|
**Dynamic Addition**: Users can add new form elements to any subsection at runtime via the API endpoint:
|
||||||
@@ -185,6 +192,83 @@ User roles in the system:
|
|||||||
|
|
||||||
Roles are managed via Keycloak and enforced using Spring Security's `@PreAuthorize` annotations.
|
Roles are managed via Keycloak and enforced using Spring Security's `@PreAuthorize` annotations.
|
||||||
|
|
||||||
|
### 10. Conditional Form Element Visibility
|
||||||
|
|
||||||
|
Form elements can be conditionally shown or hidden based on the values of other form elements. This enables dynamic forms that adapt to user input.
|
||||||
|
|
||||||
|
**Key Concepts**:
|
||||||
|
|
||||||
|
- **Visibility Conditions**: Rules attached to form elements that determine when they should be visible
|
||||||
|
- **Source Element**: The form element whose value is being evaluated
|
||||||
|
- **Target Element**: The form element with visibility conditions (the one being shown/hidden)
|
||||||
|
- **Condition Types**:
|
||||||
|
- `SHOW`: Element is visible only if the condition is met
|
||||||
|
- `HIDE`: Element is hidden if the condition is met
|
||||||
|
- **Operators**:
|
||||||
|
- `EQUALS`: Source value exactly matches expected value (case-insensitive)
|
||||||
|
- `NOT_EQUALS`: Source value does not match expected value
|
||||||
|
- `IS_EMPTY`: Source value is empty string
|
||||||
|
- `IS_NOT_EMPTY`: Source value is not empty
|
||||||
|
|
||||||
|
**Implementation Details**:
|
||||||
|
|
||||||
|
- Visibility conditions reference other form elements by their **reference key** (custom string identifier)
|
||||||
|
- Reference keys are stable and meaningful (e.g., "art_der_massnahme", "testphase_findet_statt")
|
||||||
|
- Each form element can have one visibility condition (single object, not array)
|
||||||
|
- Hidden fields are automatically excluded from validation
|
||||||
|
- PDF/HTML exports only include currently visible fields based on form state
|
||||||
|
- Versioning system captures visibility conditions in snapshots
|
||||||
|
- Conditions check the **value** property of form options, not labels
|
||||||
|
|
||||||
|
**Example Configuration**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"reference": "testphase_zeitraum",
|
||||||
|
"title": "Testphase Zeitraum",
|
||||||
|
"description": "Zeitraum der Testphase",
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Testphase Zeitraum",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "testphase_findet_statt",
|
||||||
|
"expectedValue": "Ja",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, the "Testphase Zeitraum" field is only visible when the form element with reference `testphase_findet_statt` has the value "Ja" selected.
|
||||||
|
|
||||||
|
**Frontend Behavior**:
|
||||||
|
|
||||||
|
- Visibility is evaluated client-side using the `useFormElementVisibility` composable
|
||||||
|
- Form elements smoothly appear/disappear as conditions change
|
||||||
|
- Hidden elements are excluded from the form submission
|
||||||
|
|
||||||
|
**Backend Behavior**:
|
||||||
|
|
||||||
|
- Visibility is evaluated server-side when generating PDF/HTML exports
|
||||||
|
- Backend filters form elements before rendering to Thymeleaf templates
|
||||||
|
- Hidden elements are not included in exported documents
|
||||||
|
|
||||||
|
**Best Practices**:
|
||||||
|
|
||||||
|
- Avoid circular dependencies (A depends on B, B depends on A)
|
||||||
|
- Use meaningful reference keys (snake_case recommended, e.g., "art_der_massnahme", "testphase_findet_statt")
|
||||||
|
- Reference keys should be unique within a form
|
||||||
|
- Keep visibility logic simple for better user experience
|
||||||
|
- For radio buttons and selects, use the actual label text as the value
|
||||||
|
- For checkboxes, checked = "true", unchecked = "false"
|
||||||
|
- Test visibility conditions thoroughly before deployment
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|||||||
@@ -1372,6 +1372,9 @@ components:
|
|||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
reference:
|
||||||
|
type: string
|
||||||
|
description: Unique reference key for this form element (e.g., "art_der_massnahme")
|
||||||
title:
|
title:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
@@ -1385,6 +1388,8 @@ components:
|
|||||||
formElementSubSectionId:
|
formElementSubSectionId:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
visibilityCondition:
|
||||||
|
$ref: "#/components/schemas/FormElementVisibilityCondition"
|
||||||
|
|
||||||
FormElementSnapshotDto:
|
FormElementSnapshotDto:
|
||||||
type: object
|
type: object
|
||||||
@@ -1392,6 +1397,8 @@ components:
|
|||||||
- type
|
- type
|
||||||
- options
|
- options
|
||||||
properties:
|
properties:
|
||||||
|
reference:
|
||||||
|
type: string
|
||||||
title:
|
title:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
@@ -1402,6 +1409,8 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/FormOptionDto"
|
$ref: "#/components/schemas/FormOptionDto"
|
||||||
|
visibilityCondition:
|
||||||
|
$ref: "#/components/schemas/FormElementVisibilityCondition"
|
||||||
|
|
||||||
CreateFormElementDto:
|
CreateFormElementDto:
|
||||||
type: object
|
type: object
|
||||||
@@ -1409,6 +1418,8 @@ components:
|
|||||||
- options
|
- options
|
||||||
- type
|
- type
|
||||||
properties:
|
properties:
|
||||||
|
reference:
|
||||||
|
type: string
|
||||||
title:
|
title:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
@@ -1419,6 +1430,8 @@ components:
|
|||||||
$ref: "#/components/schemas/FormOptionDto"
|
$ref: "#/components/schemas/FormOptionDto"
|
||||||
type:
|
type:
|
||||||
$ref: "#/components/schemas/FormElementType"
|
$ref: "#/components/schemas/FormElementType"
|
||||||
|
visibilityCondition:
|
||||||
|
$ref: "#/components/schemas/FormElementVisibilityCondition"
|
||||||
|
|
||||||
FormOptionDto:
|
FormOptionDto:
|
||||||
type: object
|
type: object
|
||||||
@@ -1447,6 +1460,39 @@ components:
|
|||||||
- SWITCH
|
- SWITCH
|
||||||
- TITLE_BODY_TEXTFIELDS
|
- TITLE_BODY_TEXTFIELDS
|
||||||
|
|
||||||
|
FormElementVisibilityCondition:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- conditionType
|
||||||
|
- sourceFormElementReference
|
||||||
|
- expectedValue
|
||||||
|
properties:
|
||||||
|
conditionType:
|
||||||
|
$ref: "#/components/schemas/VisibilityConditionType"
|
||||||
|
sourceFormElementReference:
|
||||||
|
type: string
|
||||||
|
description: Reference key of the source form element to check
|
||||||
|
expectedValue:
|
||||||
|
type: string
|
||||||
|
description: Expected value to compare against the source element's value property
|
||||||
|
operator:
|
||||||
|
$ref: "#/components/schemas/VisibilityConditionOperator"
|
||||||
|
default: EQUALS
|
||||||
|
|
||||||
|
VisibilityConditionType:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- SHOW
|
||||||
|
- HIDE
|
||||||
|
|
||||||
|
VisibilityConditionOperator:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- EQUALS
|
||||||
|
- NOT_EQUALS
|
||||||
|
- IS_EMPTY
|
||||||
|
- IS_NOT_EMPTY
|
||||||
|
|
||||||
####### UserDto #######
|
####### UserDto #######
|
||||||
UserDto:
|
UserDto:
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
package com.betriebsratkanzlei.legalconsenthub.application_form
|
package com.betriebsratkanzlei.legalconsenthub.application_form
|
||||||
|
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElement
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSection
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElementSubSection
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.form_element.VisibilityConditionOperator
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.form_element.VisibilityConditionType
|
||||||
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder
|
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.thymeleaf.TemplateEngine
|
import org.thymeleaf.TemplateEngine
|
||||||
import org.thymeleaf.context.Context
|
import org.thymeleaf.context.Context
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class ApplicationFormFormatService(
|
class ApplicationFormFormatService(
|
||||||
@@ -24,10 +30,118 @@ class ApplicationFormFormatService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun generateHtml(applicationForm: ApplicationForm): String {
|
fun generateHtml(applicationForm: ApplicationForm): String {
|
||||||
|
val filteredForm = filterVisibleElements(applicationForm)
|
||||||
val context =
|
val context =
|
||||||
Context().apply {
|
Context().apply {
|
||||||
setVariable("applicationForm", applicationForm)
|
setVariable("applicationForm", filteredForm)
|
||||||
}
|
}
|
||||||
return templateEngine.process("application_form_template", context)
|
return templateEngine.process("application_form_template", context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun filterVisibleElements(applicationForm: ApplicationForm): ApplicationForm {
|
||||||
|
val formElementsByRef = buildFormElementsMap(applicationForm)
|
||||||
|
val visibilityMap = evaluateVisibility(formElementsByRef)
|
||||||
|
|
||||||
|
val filteredSections =
|
||||||
|
applicationForm.formElementSections.mapNotNull { section ->
|
||||||
|
val filteredSubSections =
|
||||||
|
section.formElementSubSections.mapNotNull { subsection ->
|
||||||
|
val filteredElements =
|
||||||
|
subsection.formElements.filter { element ->
|
||||||
|
visibilityMap[element.id] == true
|
||||||
|
}
|
||||||
|
if (filteredElements.isEmpty()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
FormElementSubSection(
|
||||||
|
id = subsection.id,
|
||||||
|
title = subsection.title,
|
||||||
|
subtitle = subsection.subtitle,
|
||||||
|
formElements = filteredElements.toMutableList(),
|
||||||
|
formElementSection = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filteredSubSections.isEmpty()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
FormElementSection(
|
||||||
|
id = section.id,
|
||||||
|
title = section.title,
|
||||||
|
shortTitle = section.shortTitle,
|
||||||
|
description = section.description,
|
||||||
|
formElementSubSections = filteredSubSections.toMutableList(),
|
||||||
|
applicationForm = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApplicationForm(
|
||||||
|
id = applicationForm.id,
|
||||||
|
name = applicationForm.name,
|
||||||
|
status = applicationForm.status,
|
||||||
|
createdBy = applicationForm.createdBy,
|
||||||
|
lastModifiedBy = applicationForm.lastModifiedBy,
|
||||||
|
createdAt = applicationForm.createdAt,
|
||||||
|
modifiedAt = applicationForm.modifiedAt,
|
||||||
|
isTemplate = applicationForm.isTemplate,
|
||||||
|
organizationId = applicationForm.organizationId,
|
||||||
|
formElementSections = filteredSections.toMutableList(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildFormElementsMap(applicationForm: ApplicationForm): Map<String, FormElement> {
|
||||||
|
val map = mutableMapOf<String, FormElement>()
|
||||||
|
applicationForm.formElementSections.forEach { section ->
|
||||||
|
section.formElementSubSections.forEach { subsection ->
|
||||||
|
subsection.formElements.forEach { element ->
|
||||||
|
element.reference?.let { map[it] = element }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateVisibility(formElementsByRef: Map<String, FormElement>): Map<UUID?, Boolean> {
|
||||||
|
val visibilityMap = mutableMapOf<UUID?, Boolean>()
|
||||||
|
|
||||||
|
formElementsByRef.values.forEach { element ->
|
||||||
|
visibilityMap[element.id] = isElementVisible(element, formElementsByRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
return visibilityMap
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isElementVisible(
|
||||||
|
element: FormElement,
|
||||||
|
formElementsByRef: Map<String, FormElement>,
|
||||||
|
): Boolean {
|
||||||
|
val condition = element.visibilityCondition ?: return true
|
||||||
|
|
||||||
|
// Don't show if source element is missing
|
||||||
|
val sourceElement = formElementsByRef[condition.sourceFormElementReference] ?: return false
|
||||||
|
|
||||||
|
val sourceValue = getFormElementValue(sourceElement)
|
||||||
|
val conditionMet = evaluateCondition(sourceValue, condition.expectedValue, condition.operator)
|
||||||
|
|
||||||
|
return when (condition.conditionType) {
|
||||||
|
VisibilityConditionType.SHOW -> conditionMet
|
||||||
|
VisibilityConditionType.HIDE -> !conditionMet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFormElementValue(element: FormElement): String =
|
||||||
|
element.options.firstOrNull { it.value == "true" }?.label ?: ""
|
||||||
|
|
||||||
|
private fun evaluateCondition(
|
||||||
|
actualValue: String,
|
||||||
|
expectedValue: String,
|
||||||
|
operator: VisibilityConditionOperator,
|
||||||
|
): Boolean =
|
||||||
|
when (operator) {
|
||||||
|
VisibilityConditionOperator.EQUALS -> actualValue.equals(expectedValue, ignoreCase = true)
|
||||||
|
VisibilityConditionOperator.NOT_EQUALS -> !actualValue.equals(expectedValue, ignoreCase = true)
|
||||||
|
VisibilityConditionOperator.IS_EMPTY -> actualValue.isEmpty()
|
||||||
|
VisibilityConditionOperator.IS_NOT_EMPTY -> actualValue.isNotEmpty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class FormElement(
|
|||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
var id: UUID? = null,
|
var id: UUID? = null,
|
||||||
|
var reference: String? = null,
|
||||||
var title: String? = null,
|
var title: String? = null,
|
||||||
var description: String? = null,
|
var description: String? = null,
|
||||||
@ElementCollection
|
@ElementCollection
|
||||||
@@ -26,4 +27,5 @@ class FormElement(
|
|||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "form_element_sub_section_id", nullable = false)
|
@JoinColumn(name = "form_element_sub_section_id", nullable = false)
|
||||||
var formElementSubSection: FormElementSubSection? = null,
|
var formElementSubSection: FormElementSubSection? = null,
|
||||||
|
var visibilityCondition: FormElementVisibilityCondition? = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ import org.springframework.stereotype.Component
|
|||||||
@Component
|
@Component
|
||||||
class FormElementMapper(
|
class FormElementMapper(
|
||||||
private val formOptionMapper: FormOptionMapper,
|
private val formOptionMapper: FormOptionMapper,
|
||||||
|
private val visibilityConditionMapper: FormElementVisibilityConditionMapper,
|
||||||
) {
|
) {
|
||||||
fun toFormElementDto(formElement: FormElement): FormElementDto =
|
fun toFormElementDto(formElement: FormElement): FormElementDto =
|
||||||
FormElementDto(
|
FormElementDto(
|
||||||
id = formElement.id ?: throw IllegalStateException("FormElement ID must not be null!"),
|
id = formElement.id ?: throw IllegalStateException("FormElement ID must not be null!"),
|
||||||
|
reference = formElement.reference,
|
||||||
title = formElement.title,
|
title = formElement.title,
|
||||||
description = formElement.description,
|
description = formElement.description,
|
||||||
options = formElement.options.map { formOptionMapper.toFormOptionDto(it) },
|
options = formElement.options.map { formOptionMapper.toFormOptionDto(it) },
|
||||||
@@ -18,6 +20,10 @@ class FormElementMapper(
|
|||||||
formElementSubSectionId =
|
formElementSubSectionId =
|
||||||
formElement.formElementSubSection?.id
|
formElement.formElementSubSection?.id
|
||||||
?: throw IllegalStateException("FormElementSubSection ID must not be null!"),
|
?: throw IllegalStateException("FormElementSubSection ID must not be null!"),
|
||||||
|
visibilityCondition =
|
||||||
|
formElement.visibilityCondition?.let {
|
||||||
|
visibilityConditionMapper.toFormElementVisibilityConditionDto(it)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toFormElement(
|
fun toFormElement(
|
||||||
@@ -26,11 +32,16 @@ class FormElementMapper(
|
|||||||
): FormElement =
|
): FormElement =
|
||||||
FormElement(
|
FormElement(
|
||||||
id = formElement.id,
|
id = formElement.id,
|
||||||
|
reference = formElement.reference,
|
||||||
title = formElement.title,
|
title = formElement.title,
|
||||||
description = formElement.description,
|
description = formElement.description,
|
||||||
options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(),
|
options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(),
|
||||||
type = formElement.type,
|
type = formElement.type,
|
||||||
formElementSubSection = formElementSubSection,
|
formElementSubSection = formElementSubSection,
|
||||||
|
visibilityCondition =
|
||||||
|
formElement.visibilityCondition?.let {
|
||||||
|
visibilityConditionMapper.toFormElementVisibilityCondition(it)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toFormElement(
|
fun toFormElement(
|
||||||
@@ -39,10 +50,15 @@ class FormElementMapper(
|
|||||||
): FormElement =
|
): FormElement =
|
||||||
FormElement(
|
FormElement(
|
||||||
id = null,
|
id = null,
|
||||||
|
reference = formElement.reference,
|
||||||
title = formElement.title,
|
title = formElement.title,
|
||||||
description = formElement.description,
|
description = formElement.description,
|
||||||
options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(),
|
options = formElement.options.map { formOptionMapper.toFormOption(it) }.toMutableList(),
|
||||||
type = formElement.type,
|
type = formElement.type,
|
||||||
formElementSubSection = formElementSubSection,
|
formElementSubSection = formElementSubSection,
|
||||||
|
visibilityCondition =
|
||||||
|
formElement.visibilityCondition?.let {
|
||||||
|
visibilityConditionMapper.toFormElementVisibilityCondition(it)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.form_element
|
||||||
|
|
||||||
|
import jakarta.persistence.Embeddable
|
||||||
|
import jakarta.persistence.EnumType
|
||||||
|
import jakarta.persistence.Enumerated
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
data class FormElementVisibilityCondition(
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
val conditionType: VisibilityConditionType,
|
||||||
|
val sourceFormElementReference: String,
|
||||||
|
val expectedValue: String,
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
val operator: VisibilityConditionOperator = VisibilityConditionOperator.EQUALS,
|
||||||
|
)
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.form_element
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementVisibilityCondition as FormElementVisibilityConditionDto
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionOperator as VisibilityConditionOperatorDto
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionType as VisibilityConditionTypeDto
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class FormElementVisibilityConditionMapper {
|
||||||
|
fun toFormElementVisibilityConditionDto(
|
||||||
|
condition: FormElementVisibilityCondition,
|
||||||
|
): FormElementVisibilityConditionDto =
|
||||||
|
FormElementVisibilityConditionDto(
|
||||||
|
conditionType = toVisibilityConditionTypeDto(condition.conditionType),
|
||||||
|
sourceFormElementReference = condition.sourceFormElementReference,
|
||||||
|
expectedValue = condition.expectedValue,
|
||||||
|
operator = toVisibilityConditionOperatorDto(condition.operator),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun toFormElementVisibilityCondition(
|
||||||
|
conditionDto: FormElementVisibilityConditionDto,
|
||||||
|
): FormElementVisibilityCondition =
|
||||||
|
FormElementVisibilityCondition(
|
||||||
|
conditionType = toVisibilityConditionType(conditionDto.conditionType),
|
||||||
|
sourceFormElementReference = conditionDto.sourceFormElementReference,
|
||||||
|
expectedValue = conditionDto.expectedValue,
|
||||||
|
operator =
|
||||||
|
conditionDto.operator?.let { toVisibilityConditionOperator(it) }
|
||||||
|
?: VisibilityConditionOperator.EQUALS,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun toVisibilityConditionTypeDto(type: VisibilityConditionType): VisibilityConditionTypeDto =
|
||||||
|
when (type) {
|
||||||
|
VisibilityConditionType.SHOW -> VisibilityConditionTypeDto.SHOW
|
||||||
|
VisibilityConditionType.HIDE -> VisibilityConditionTypeDto.HIDE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toVisibilityConditionType(typeDto: VisibilityConditionTypeDto): VisibilityConditionType =
|
||||||
|
when (typeDto) {
|
||||||
|
VisibilityConditionTypeDto.SHOW -> VisibilityConditionType.SHOW
|
||||||
|
VisibilityConditionTypeDto.HIDE -> VisibilityConditionType.HIDE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toVisibilityConditionOperatorDto(
|
||||||
|
operator: VisibilityConditionOperator,
|
||||||
|
): VisibilityConditionOperatorDto =
|
||||||
|
when (operator) {
|
||||||
|
VisibilityConditionOperator.EQUALS ->
|
||||||
|
VisibilityConditionOperatorDto.EQUALS
|
||||||
|
VisibilityConditionOperator.NOT_EQUALS ->
|
||||||
|
VisibilityConditionOperatorDto.NOT_EQUALS
|
||||||
|
VisibilityConditionOperator.IS_EMPTY ->
|
||||||
|
VisibilityConditionOperatorDto.IS_EMPTY
|
||||||
|
VisibilityConditionOperator.IS_NOT_EMPTY ->
|
||||||
|
VisibilityConditionOperatorDto.IS_NOT_EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toVisibilityConditionOperator(
|
||||||
|
operatorDto: VisibilityConditionOperatorDto,
|
||||||
|
): VisibilityConditionOperator =
|
||||||
|
when (operatorDto) {
|
||||||
|
VisibilityConditionOperatorDto.EQUALS ->
|
||||||
|
VisibilityConditionOperator.EQUALS
|
||||||
|
VisibilityConditionOperatorDto.NOT_EQUALS ->
|
||||||
|
VisibilityConditionOperator.NOT_EQUALS
|
||||||
|
VisibilityConditionOperatorDto.IS_EMPTY ->
|
||||||
|
VisibilityConditionOperator.IS_EMPTY
|
||||||
|
VisibilityConditionOperatorDto.IS_NOT_EMPTY ->
|
||||||
|
VisibilityConditionOperator.IS_NOT_EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.form_element
|
||||||
|
|
||||||
|
enum class VisibilityConditionOperator {
|
||||||
|
EQUALS,
|
||||||
|
NOT_EQUALS,
|
||||||
|
IS_EMPTY,
|
||||||
|
IS_NOT_EMPTY,
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.form_element
|
||||||
|
|
||||||
|
enum class VisibilityConditionType {
|
||||||
|
SHOW,
|
||||||
|
HIDE,
|
||||||
|
}
|
||||||
@@ -62,12 +62,17 @@ create table form_element_options
|
|||||||
|
|
||||||
create table form_element
|
create table form_element
|
||||||
(
|
(
|
||||||
form_element_order integer,
|
form_element_order integer,
|
||||||
type smallint not null check (type between 0 and 5),
|
type smallint not null check (type between 0 and 5),
|
||||||
form_element_sub_section_id uuid not null,
|
form_element_sub_section_id uuid not null,
|
||||||
id uuid not null,
|
id uuid not null,
|
||||||
description varchar(255),
|
condition_type varchar(255) check (condition_type in ('SHOW', 'HIDE')),
|
||||||
title varchar(255),
|
description varchar(255),
|
||||||
|
expected_value varchar(255),
|
||||||
|
operator varchar(255) check (operator in ('EQUALS', 'NOT_EQUALS', 'IS_EMPTY', 'IS_NOT_EMPTY')),
|
||||||
|
reference varchar(255),
|
||||||
|
source_form_element_reference varchar(255),
|
||||||
|
title varchar(255),
|
||||||
primary key (id)
|
primary key (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -165,4 +170,4 @@ alter table if exists form_element_sub_section
|
|||||||
alter table if exists notification
|
alter table if exists notification
|
||||||
add constraint FKeg1j4hnp0y4lbm0y35hgr4e8r
|
add constraint FKeg1j4hnp0y4lbm0y35hgr4e8r
|
||||||
foreign key (recipient_id)
|
foreign key (recipient_id)
|
||||||
references app_user
|
references app_user;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-for="(formElement, index) in props.modelValue" :key="formElement.id">
|
<template v-for="(formElement, index) in visibleFormElements" :key="formElement.id">
|
||||||
<div class="group flex py-3 lg:py-4">
|
<div class="group flex py-3 lg:py-4">
|
||||||
<div class="flex-auto">
|
<div class="flex-auto">
|
||||||
<p v-if="formElement.title" class="font-semibold">{{ formElement.title }}</p>
|
<p v-if="formElement.title" class="font-semibold">{{ formElement.title }}</p>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
:is="getResolvedComponent(formElement)"
|
:is="getResolvedComponent(formElement)"
|
||||||
:form-options="formElement.options"
|
:form-options="formElement.options"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
@update:form-options="updateFormOptions($event, index)"
|
@update:form-options="updateFormOptions($event, formElement.id)"
|
||||||
/>
|
/>
|
||||||
<TheComment
|
<TheComment
|
||||||
v-if="applicationFormId && activeFormElement === formElement.id"
|
v-if="applicationFormId && activeFormElement === formElement.id"
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
</UDropdownMenu>
|
</UDropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<USeparator v-if="index < props.modelValue.length - 1" />
|
<USeparator v-if="index < visibleFormElements.length - 1" />
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -45,6 +45,7 @@ import { useCommentStore } from '~~/stores/useCommentStore'
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: FormElementDto[]
|
modelValue: FormElementDto[]
|
||||||
|
visibilityMap: Map<string, boolean>
|
||||||
applicationFormId?: string
|
applicationFormId?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
@@ -68,6 +69,10 @@ const route = useRoute()
|
|||||||
const activeFormElement = ref('')
|
const activeFormElement = ref('')
|
||||||
const openDropdownId = ref<string | null>(null)
|
const openDropdownId = ref<string | null>(null)
|
||||||
|
|
||||||
|
const visibleFormElements = computed(() => {
|
||||||
|
return props.modelValue.filter((element) => props.visibilityMap.get(element.id) !== false)
|
||||||
|
})
|
||||||
|
|
||||||
function handleDropdownToggle(formElementId: string, isOpen: boolean) {
|
function handleDropdownToggle(formElementId: string, isOpen: boolean) {
|
||||||
openDropdownId.value = isOpen ? formElementId : null
|
openDropdownId.value = isOpen ? formElementId : null
|
||||||
}
|
}
|
||||||
@@ -112,14 +117,15 @@ function getDropdownItems(formElementId: string, formElementPosition: number): D
|
|||||||
return [items]
|
return [items]
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFormOptions(formOptions: FormOptionDto[], formElementIndex: number) {
|
function updateFormOptions(formOptions: FormOptionDto[], formElementId: string) {
|
||||||
console.log('Updating form options for element index:', formElementIndex, formOptions)
|
console.log('Updating form options for element ID:', formElementId, formOptions)
|
||||||
const updatedModelValue = [...props.modelValue]
|
const updatedModelValue = props.modelValue.map((element) => {
|
||||||
const currentElement = updatedModelValue[formElementIndex]
|
if (element.id === formElementId) {
|
||||||
if (currentElement) {
|
return { ...element, options: formOptions }
|
||||||
updatedModelValue[formElementIndex] = { ...currentElement, options: formOptions }
|
}
|
||||||
emit('update:modelValue', updatedModelValue)
|
return element
|
||||||
}
|
})
|
||||||
|
emit('update:modelValue', updatedModelValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleComments(formElementId: string) {
|
function toggleComments(formElementId: string) {
|
||||||
|
|||||||
@@ -8,25 +8,31 @@
|
|||||||
{{ currentFormElementSection.title }}
|
{{ currentFormElementSection.title }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<template v-if="currentFormElementSection?.formElementSubSections">
|
<UCard
|
||||||
<UCard
|
v-for="subsection in visibleSubsections"
|
||||||
v-for="subsection in currentFormElementSection.formElementSubSections"
|
:key="subsection.id"
|
||||||
:key="subsection.id"
|
variant="subtle"
|
||||||
variant="subtle"
|
class="mb-6"
|
||||||
class="mb-6"
|
>
|
||||||
>
|
<div class="mb-4">
|
||||||
<div class="mb-4">
|
<h2 class="text-lg font-semibold text-highlighted">{{ subsection.title }}</h2>
|
||||||
<h2 class="text-lg font-semibold text-highlighted">{{ subsection.title }}</h2>
|
<p v-if="subsection.subtitle" class="text-sm text-dimmed">{{ subsection.subtitle }}</p>
|
||||||
<p v-if="subsection.subtitle" class="text-sm text-dimmed">{{ subsection.subtitle }}</p>
|
</div>
|
||||||
</div>
|
|
||||||
<FormEngine
|
<FormEngine
|
||||||
v-model="subsection.formElements"
|
v-model="subsection.formElements"
|
||||||
|
:visibility-map="visibilityMap"
|
||||||
:application-form-id="applicationFormId"
|
:application-form-id="applicationFormId"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@add:input-form="(position) => handleAddInputForm(position, subsection.id)"
|
@add:input-form="(position) => handleAddInputForm(position, subsection.id)"
|
||||||
/>
|
/>
|
||||||
</UCard>
|
</UCard>
|
||||||
</template>
|
|
||||||
|
<UCard v-if="visibleSubsections.length === 0" variant="subtle" class="mb-6">
|
||||||
|
<div class="text-center py-8 text-dimmed">
|
||||||
|
<UIcon name="i-lucide-eye-off" class="w-12 h-12 mx-auto mb-3 opacity-50" />
|
||||||
|
<p>{{ $t('applicationForms.noVisibleElements') }}</p>
|
||||||
|
</div>
|
||||||
|
</UCard>
|
||||||
|
|
||||||
<div class="flex gap-2 justify-between">
|
<div class="flex gap-2 justify-between">
|
||||||
<UButton leading-icon="i-lucide-arrow-left" :disabled="!stepper?.hasPrev" @click="handleNavigate('backward')">
|
<UButton leading-icon="i-lucide-arrow-left" :disabled="!stepper?.hasPrev" @click="handleNavigate('backward')">
|
||||||
@@ -76,6 +82,30 @@ const { stepper, activeStepperItemIndex, stepperItems, currentFormElementSection
|
|||||||
computed(() => props.formElementSections)
|
computed(() => props.formElementSections)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const allFormElements = computed(() => {
|
||||||
|
return props.formElementSections.flatMap(section =>
|
||||||
|
section.formElementSubSections?.flatMap(subsection =>
|
||||||
|
subsection.formElements
|
||||||
|
) ?? []
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const { evaluateVisibility } = useFormElementVisibility()
|
||||||
|
|
||||||
|
const visibilityMap = computed(() => {
|
||||||
|
return evaluateVisibility(allFormElements.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const visibleSubsections = computed(() => {
|
||||||
|
if (!currentFormElementSection.value?.formElementSubSections) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentFormElementSection.value.formElementSubSections.filter(subsection => {
|
||||||
|
return subsection.formElements.some(element => visibilityMap.value.get(element.id) !== false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.initialSectionIndex !== undefined) {
|
if (props.initialSectionIndex !== undefined) {
|
||||||
activeStepperItemIndex.value = props.initialSectionIndex
|
activeStepperItemIndex.value = props.initialSectionIndex
|
||||||
|
|||||||
@@ -13,10 +13,17 @@ export function useApplicationFormValidator() {
|
|||||||
return Object.values(ComplianceStatus)[highestComplianceNumber] ?? ComplianceStatus.NonCritical
|
return Object.values(ComplianceStatus)[highestComplianceNumber] ?? ComplianceStatus.NonCritical
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateFormElements(formElements: FormElementDto[]): Map<FormElementId, ComplianceStatus> {
|
function validateFormElements(
|
||||||
|
formElements: FormElementDto[],
|
||||||
|
visibilityMap?: Map<string, boolean>
|
||||||
|
): Map<FormElementId, ComplianceStatus> {
|
||||||
formElementComplianceMap.value.clear()
|
formElementComplianceMap.value.clear()
|
||||||
|
|
||||||
formElements.forEach((formElement) => {
|
formElements.forEach((formElement) => {
|
||||||
|
if (visibilityMap && visibilityMap.get(formElement.id) === false) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!complianceCheckableElementTypes.includes(formElement.type)) return
|
if (!complianceCheckableElementTypes.includes(formElement.type)) return
|
||||||
|
|
||||||
// Reset any previously set compliance status when all options are false
|
// Reset any previously set compliance status when all options are false
|
||||||
|
|||||||
83
legalconsenthub/app/composables/useFormElementVisibility.ts
Normal file
83
legalconsenthub/app/composables/useFormElementVisibility.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import type { FormElementDto, VisibilityConditionOperator } from '~~/.api-client'
|
||||||
|
import { VisibilityConditionOperator as VCOperator, VisibilityConditionType as VCType } from '~~/.api-client'
|
||||||
|
|
||||||
|
export function useFormElementVisibility() {
|
||||||
|
function evaluateVisibility(allFormElements: FormElementDto[]): Map<string, boolean> {
|
||||||
|
const formElementsByRef = buildFormElementsMap(allFormElements)
|
||||||
|
const visibilityMap = new Map<string, boolean>()
|
||||||
|
|
||||||
|
allFormElements.forEach((element) => {
|
||||||
|
const isVisible = isElementVisible(element, formElementsByRef, visibilityMap)
|
||||||
|
visibilityMap.set(element.id, isVisible)
|
||||||
|
})
|
||||||
|
|
||||||
|
return visibilityMap
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFormElementsMap(formElements: FormElementDto[]): Map<string, FormElementDto> {
|
||||||
|
const map = new Map<string, FormElementDto>()
|
||||||
|
formElements.forEach((element) => {
|
||||||
|
if (element.reference) {
|
||||||
|
map.set(element.reference, element)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
function isElementVisible(
|
||||||
|
element: FormElementDto,
|
||||||
|
formElementsByRef: Map<string, FormElementDto>,
|
||||||
|
_visibilityMap: Map<string, boolean>
|
||||||
|
): boolean {
|
||||||
|
if (!element.visibilityCondition) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const condition = element.visibilityCondition
|
||||||
|
|
||||||
|
const sourceElement = formElementsByRef.get(condition.sourceFormElementReference)
|
||||||
|
if (!sourceElement) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceValue = getFormElementValue(sourceElement)
|
||||||
|
|
||||||
|
const operator = condition.operator || VCOperator.Equals
|
||||||
|
const conditionMet = evaluateCondition(sourceValue, condition.expectedValue, operator)
|
||||||
|
|
||||||
|
return condition.conditionType === VCType.Show ? conditionMet : !conditionMet
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFormElementValue(element: FormElementDto): string {
|
||||||
|
const selectedOption = element.options.find((option) => option.value === 'true')
|
||||||
|
return selectedOption?.label || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function evaluateCondition(
|
||||||
|
actualValue: string,
|
||||||
|
expectedValue: string,
|
||||||
|
operator: VisibilityConditionOperator
|
||||||
|
): boolean {
|
||||||
|
let result: boolean
|
||||||
|
switch (operator) {
|
||||||
|
case VCOperator.Equals:
|
||||||
|
result = actualValue.toLowerCase() === expectedValue.toLowerCase()
|
||||||
|
return result
|
||||||
|
case VCOperator.NotEquals:
|
||||||
|
result = actualValue.toLowerCase() !== expectedValue.toLowerCase()
|
||||||
|
return result
|
||||||
|
case VCOperator.IsEmpty:
|
||||||
|
result = actualValue === ''
|
||||||
|
return result
|
||||||
|
case VCOperator.IsNotEmpty:
|
||||||
|
result = actualValue !== ''
|
||||||
|
return result
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
evaluateVisibility
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,6 +75,7 @@ const {
|
|||||||
|
|
||||||
const { updateApplicationForm: updateForm, submitApplicationForm } = useApplicationForm()
|
const { updateApplicationForm: updateForm, submitApplicationForm } = useApplicationForm()
|
||||||
const { validateFormElements, getHighestComplianceStatus } = useApplicationFormValidator()
|
const { validateFormElements, getHighestComplianceStatus } = useApplicationFormValidator()
|
||||||
|
const { evaluateVisibility } = useFormElementVisibility()
|
||||||
const { canWriteApplicationForms } = usePermissions()
|
const { canWriteApplicationForms } = usePermissions()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { user } = storeToRefs(userStore)
|
const { user } = storeToRefs(userStore)
|
||||||
@@ -100,10 +101,14 @@ const allFormElements = computed(() => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const visibilityMap = computed(() => {
|
||||||
|
return evaluateVisibility(allFormElements.value)
|
||||||
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => allFormElements.value,
|
() => allFormElements.value,
|
||||||
(updatedFormElements: FormElementDto[]) => {
|
(updatedFormElements: FormElementDto[]) => {
|
||||||
validationMap.value = validateFormElements(updatedFormElements)
|
validationMap.value = validateFormElements(updatedFormElements, visibilityMap.value)
|
||||||
validationStatus.value = getHighestComplianceStatus()
|
validationStatus.value = getHighestComplianceStatus()
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import { useUserStore } from '~~/stores/useUserStore'
|
|||||||
const { getAllApplicationFormTemplates } = await useApplicationFormTemplate()
|
const { getAllApplicationFormTemplates } = await useApplicationFormTemplate()
|
||||||
const { createApplicationForm, submitApplicationForm } = useApplicationForm()
|
const { createApplicationForm, submitApplicationForm } = useApplicationForm()
|
||||||
const { validateFormElements, getHighestComplianceStatus } = useApplicationFormValidator()
|
const { validateFormElements, getHighestComplianceStatus } = useApplicationFormValidator()
|
||||||
|
const { evaluateVisibility } = useFormElementVisibility()
|
||||||
const { canWriteApplicationForms } = usePermissions()
|
const { canWriteApplicationForms } = usePermissions()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { selectedOrganization } = storeToRefs(userStore)
|
const { selectedOrganization } = storeToRefs(userStore)
|
||||||
@@ -87,10 +88,14 @@ const allFormElements = computed(() => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const visibilityMap = computed(() => {
|
||||||
|
return evaluateVisibility(allFormElements.value)
|
||||||
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => allFormElements.value,
|
() => allFormElements.value,
|
||||||
(updatedFormElements: FormElementDto[]) => {
|
(updatedFormElements: FormElementDto[]) => {
|
||||||
validationMap.value = validateFormElements(updatedFormElements)
|
validationMap.value = validateFormElements(updatedFormElements, visibilityMap.value)
|
||||||
validationStatus.value = getHighestComplianceStatus()
|
validationStatus.value = getHighestComplianceStatus()
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"noPermission": "Keine Berechtigung",
|
"noPermission": "Keine Berechtigung",
|
||||||
"noPermissionDescription": "Sie haben keine Berechtigung zum Erstellen von Anträgen.",
|
"noPermissionDescription": "Sie haben keine Berechtigung zum Erstellen von Anträgen.",
|
||||||
"backToOverview": "Zurück zur Übersicht",
|
"backToOverview": "Zurück zur Übersicht",
|
||||||
|
"noVisibleElements": "Alle Formularelemente in diesem Abschnitt sind ausgeblendet",
|
||||||
"deleteConfirm": "Möchten Sie wirklich den Mitbestimmungsantrag \"{name}\" löschen?",
|
"deleteConfirm": "Möchten Sie wirklich den Mitbestimmungsantrag \"{name}\" löschen?",
|
||||||
"deleteTitle": "Mitbestimmungsantrag löschen",
|
"deleteTitle": "Mitbestimmungsantrag löschen",
|
||||||
"lastEditedBy": "Zuletzt bearbeitet von",
|
"lastEditedBy": "Zuletzt bearbeitet von",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"noPermission": "No Permission",
|
"noPermission": "No Permission",
|
||||||
"noPermissionDescription": "You do not have permission to create applications.",
|
"noPermissionDescription": "You do not have permission to create applications.",
|
||||||
"backToOverview": "Back to Overview",
|
"backToOverview": "Back to Overview",
|
||||||
|
"noVisibleElements": "All form elements in this section are hidden",
|
||||||
"deleteConfirm": "Do you really want to delete the co-determination application \"{name}\"?",
|
"deleteConfirm": "Do you really want to delete the co-determination application \"{name}\"?",
|
||||||
"deleteTitle": "Delete Co-determination Application",
|
"deleteTitle": "Delete Co-determination Application",
|
||||||
"lastEditedBy": "Last edited by",
|
"lastEditedBy": "Last edited by",
|
||||||
|
|||||||
720
testdata.json
720
testdata.json
@@ -1,95 +1,661 @@
|
|||||||
{
|
{
|
||||||
"isTemplate": true,
|
"isTemplate": true,
|
||||||
"name": "",
|
"name": "Name des IT-Systems",
|
||||||
"formElementSections": [
|
"formElementSections": [
|
||||||
{
|
{
|
||||||
"title": "Section 1",
|
"title": "Angaben zum IT-System",
|
||||||
"shortTitle": "S1",
|
"shortTitle": "IT-System",
|
||||||
"description": "First section of the form",
|
"description": "Alle Angaben zum IT-System",
|
||||||
"formElements": [
|
"formElementSubSections": [
|
||||||
{
|
{
|
||||||
"title": "Zustimmung erforderlich",
|
"title": "Art der Maßnahme",
|
||||||
"description": "Bitte wählen Sie eine Option aus, um fortzufahren.",
|
"subtitle": "",
|
||||||
"options": [
|
"formElements": [
|
||||||
{
|
{
|
||||||
"value": "false",
|
"reference": "art_der_massnahme",
|
||||||
"label": "Zustimmen (schwerwiegend)",
|
"title": "Art der IT-System Maßnahme",
|
||||||
"processingPurpose": "BUSINESS_PROCESS",
|
"description": "Handelt es sich um eine Einführung, Änderung, Erweiterung oder Ablösung/Einstellung eines IT-Systems?",
|
||||||
"employeeDataCategory": "SENSITIVE"
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "Einführung",
|
||||||
|
"label": "Einführung",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Änderung IT-System",
|
||||||
|
"label": "Änderung IT-System",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Erweiterung",
|
||||||
|
"label": "Erweiterung",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Ablösung/Einstellung IT-System",
|
||||||
|
"label": "Ablösung/Einstellung IT-System",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "RADIOBUTTON"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"type": "SWITCH"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Zustimmung erforderlich",
|
"title": "Allgemeine Information",
|
||||||
"description": "Bitte wählen Sie eine Option aus, um fortzufahren.",
|
"subtitle": "Grundlegende Informationen zum IT-System",
|
||||||
"options": [
|
"formElements": [
|
||||||
{
|
{
|
||||||
"value": "false",
|
"reference": "einfuehrungszeitpunkt",
|
||||||
"label": "Zustimmen (keine Auswirkungen)",
|
"title": "Einführungszeitpunkt",
|
||||||
"processingPurpose": "NONE",
|
"description": "Geplanter Termin Going Live",
|
||||||
"employeeDataCategory": "NONE"
|
"options": [
|
||||||
}
|
{
|
||||||
],
|
"value": "",
|
||||||
"type": "SWITCH"
|
"label": "Einführungszeitpunkt",
|
||||||
},
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
{
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
"title": "Zustimmung erforderlich",
|
}
|
||||||
"description": "Bitte wählen Sie eine Option aus, um fortzufahren.",
|
],
|
||||||
"options": [
|
"type": "TEXTFIELD",
|
||||||
{
|
"visibilityCondition": {
|
||||||
"value": "false",
|
"conditionType": "HIDE",
|
||||||
"label": "Zustimmen (Mittel)",
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
"processingPurpose": "DATA_ANALYSIS",
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
"employeeDataCategory": "REVIEW_REQUIRED"
|
"operator": "EQUALS"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"type": "CHECKBOX"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Section 2",
|
|
||||||
"shortTitle": "S2",
|
|
||||||
"description": "Second section of the form",
|
|
||||||
"formElements": [
|
|
||||||
{
|
|
||||||
"title": "Eine weitere Zustimmung erforderlich",
|
|
||||||
"description": "Bitte wählen Sie eine Option aus, um fortzufahren.",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"value": "false",
|
|
||||||
"label": "Zustimmen",
|
|
||||||
"processingPurpose": "BUSINESS_PROCESS",
|
|
||||||
"employeeDataCategory": "SENSITIVE"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": "false",
|
"reference": "testphase_findet_statt",
|
||||||
"label": "Ablehnen",
|
"title": "Testphase findet statt",
|
||||||
"processingPurpose": "DATA_ANALYSIS",
|
"description": "Findet eine Testphase statt?",
|
||||||
"employeeDataCategory": "REVIEW_REQUIRED"
|
"options": [
|
||||||
}
|
{
|
||||||
],
|
"value": "Ja",
|
||||||
"type": "SELECT"
|
"label": "Ja",
|
||||||
},
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
{
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
"title": "Eine weitere Zustimmung erforderlich",
|
},
|
||||||
"description": "Bitte wählen Sie eine Option aus, um fortzufahren.",
|
{
|
||||||
"options": [
|
"value": "Nein",
|
||||||
{
|
"label": "Nein",
|
||||||
"value": "false",
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
"label": "Zustimmen",
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
"processingPurpose": "BUSINESS_PROCESS",
|
}
|
||||||
"employeeDataCategory": "SENSITIVE"
|
],
|
||||||
|
"type": "RADIOBUTTON",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": "false",
|
"reference": "testphase_zeitraum",
|
||||||
"label": "Ablehnen",
|
"title": "Testphase Zeitraum",
|
||||||
"processingPurpose": "DATA_ANALYSIS",
|
"description": "Zeitraum der Testphase (von ... bis)",
|
||||||
"employeeDataCategory": "REVIEW_REQUIRED"
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Testphase Zeitraum",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "testphase_findet_statt",
|
||||||
|
"expectedValue": "Ja",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "verwendung_anonymisierter_daten",
|
||||||
|
"title": "Verwendung anonymisierter/fiktiver Daten",
|
||||||
|
"description": "Während der Testphase/n wird mit anonymisierten bzw. fiktiven Daten gearbeitet",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "Ja",
|
||||||
|
"label": "Ja",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Nein",
|
||||||
|
"label": "Nein",
|
||||||
|
"processingPurpose": "DATA_ANALYSIS",
|
||||||
|
"employeeDataCategory": "REVIEW_REQUIRED"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "RADIOBUTTON",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "testphase_findet_statt",
|
||||||
|
"expectedValue": "Ja",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "art_der_mitarbeiterdaten",
|
||||||
|
"title": "Art der Mitarbeiterdaten (falls Echtdaten)",
|
||||||
|
"description": "Welche Art von Mitarbeiterdaten werden verwendet?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Art der Mitarbeiterdaten",
|
||||||
|
"processingPurpose": "DATA_ANALYSIS",
|
||||||
|
"employeeDataCategory": "SENSITIVE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "verwendung_anonymisierter_daten",
|
||||||
|
"expectedValue": "Nein",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "anzahl_betroffener_mitarbeiter",
|
||||||
|
"title": "Anzahl betroffener Mitarbeiter (Testphase)",
|
||||||
|
"description": "Wie viele Mitarbeiter sind von der Testphase betroffen?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Anzahl betroffener Mitarbeiter",
|
||||||
|
"processingPurpose": "DATA_ANALYSIS",
|
||||||
|
"employeeDataCategory": "REVIEW_REQUIRED"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "verwendung_anonymisierter_daten",
|
||||||
|
"expectedValue": "Nein",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "umfang_der_mitarbeiterdatenverarbeitung",
|
||||||
|
"title": "Umfang der Mitarbeiterdatenverarbeitung",
|
||||||
|
"description": "In welchem Umfang werden Mitarbeiterdaten verarbeitet?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Umfang der Verarbeitung",
|
||||||
|
"processingPurpose": "DATA_ANALYSIS",
|
||||||
|
"employeeDataCategory": "SENSITIVE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "verwendung_anonymisierter_daten",
|
||||||
|
"expectedValue": "Nein",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "abgeloestes_system",
|
||||||
|
"title": "Abgelöstes System",
|
||||||
|
"description": "Folgendes System wird mit Einführung o.g. IT-Systems abgelöst",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Nicht zutreffend",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "CHECKBOX",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "name_des_abgeloesten_systems",
|
||||||
|
"title": "Name des abgelösten Systems",
|
||||||
|
"description": "Bezeichnung des abgelösten Systems",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Name des abgelösten Systems",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "abgeloestes_system",
|
||||||
|
"expectedValue": "true",
|
||||||
|
"operator": "NOT_EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "geaendertes_system",
|
||||||
|
"title": "Geändertes System",
|
||||||
|
"description": "Folgendes System wird mit Einführung o.g. IT-Systems geändert",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Nicht zutreffend",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "CHECKBOX",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "name_des_geaenderten_systems",
|
||||||
|
"title": "Name des geänderten Systems",
|
||||||
|
"description": "Bezeichnung des geänderten Systems",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Name des geänderten Systems",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "geaendertes_system",
|
||||||
|
"expectedValue": "true",
|
||||||
|
"operator": "NOT_EQUALS"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"type": "RADIOBUTTON"
|
},
|
||||||
|
{
|
||||||
|
"title": "Verantwortlicher und betroffene Betriebe / Betriebsteile",
|
||||||
|
"subtitle": "Informationen zu Verantwortlichen und betroffenen Bereichen",
|
||||||
|
"formElements": [
|
||||||
|
{
|
||||||
|
"reference": "verantwortlicher_fachbereich",
|
||||||
|
"title": "Verantwortlicher Fachbereich und Ansprechpartner",
|
||||||
|
"description": "Bitte geben Sie den verantwortlichen Fachbereich und Ansprechpartner an",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Fachbereich und Ansprechpartner",
|
||||||
|
"processingPurpose": "BUSINESS_PROCESS",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "betroffene_betriebe",
|
||||||
|
"title": "Betroffene Betriebe/Betriebsteile",
|
||||||
|
"description": "Für welche Betriebe/Betriebsteile wird das IT-System eingeführt?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Betroffene Betriebe/Betriebsteile",
|
||||||
|
"processingPurpose": "BUSINESS_PROCESS",
|
||||||
|
"employeeDataCategory": "REVIEW_REQUIRED"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "betroffene_bereiche",
|
||||||
|
"title": "Betroffene Bereiche/Abteilungen",
|
||||||
|
"description": "Für welche Bereiche bzw. Abteilungen soll das IT-System zum Einsatz kommen?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Bereiche/Abteilungen",
|
||||||
|
"processingPurpose": "BUSINESS_PROCESS",
|
||||||
|
"employeeDataCategory": "REVIEW_REQUIRED"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Angaben zum IT-System",
|
||||||
|
"subtitle": "Detaillierte Informationen zum IT-System",
|
||||||
|
"formElements": [
|
||||||
|
{
|
||||||
|
"reference": "systembeschreibung",
|
||||||
|
"title": "Systembeschreibung",
|
||||||
|
"description": "Beschreibung des IT-Systems",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Systembeschreibung",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "zielsetzung",
|
||||||
|
"title": "Zielsetzung des Systemeinsatzes",
|
||||||
|
"description": "Zielsetzung und Zweck des IT-Systems",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Zielsetzung",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "hersteller",
|
||||||
|
"title": "Hersteller/Anbieter",
|
||||||
|
"description": "Hersteller oder Anbieter des IT-Systems",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Hersteller/Anbieter",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "speicherort",
|
||||||
|
"title": "Speicherort",
|
||||||
|
"description": "Wo werden die Daten gespeichert?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "Server im Rechenzentrum",
|
||||||
|
"label": "Server im Rechenzentrum",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "REVIEW_REQUIRED"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Cloud",
|
||||||
|
"label": "Cloud",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "SENSITIVE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "RADIOBUTTON",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "geraeteart",
|
||||||
|
"title": "Zugriff auf die Daten erfolgt über (Geräteart)",
|
||||||
|
"description": "Über welche Art von Gerät erfolgt der Zugriff?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "stationäres Endgerät",
|
||||||
|
"label": "stationäres Endgerät",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "mobile App",
|
||||||
|
"label": "mobile App",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "REVIEW_REQUIRED"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Browser",
|
||||||
|
"label": "Browser",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "SELECT",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "geraetebesitz",
|
||||||
|
"title": "Zugriff auf die Daten erfolgt über (Gerätebesitz)",
|
||||||
|
"description": "Mit welchen Geräten wird auf die Daten zugegriffen?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "dienstliche Endgeräte",
|
||||||
|
"label": "dienstliche Endgeräte",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "private Endgeräte",
|
||||||
|
"label": "private Endgeräte",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "SENSITIVE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "RADIOBUTTON",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "modulbasiertes_system",
|
||||||
|
"title": "Modulbasiertes System",
|
||||||
|
"description": "Ist das IT-System modulbasiert?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "Ja",
|
||||||
|
"label": "Ja",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Nein",
|
||||||
|
"label": "Nein",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "RADIOBUTTON",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "modul_1",
|
||||||
|
"title": "Modul 1",
|
||||||
|
"description": "Beschreibung des ersten Moduls",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Modul 1",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "modulbasiertes_system",
|
||||||
|
"expectedValue": "Ja",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "modul_2",
|
||||||
|
"title": "Modul 2",
|
||||||
|
"description": "Beschreibung des zweiten Moduls",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Modul 2",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "modulbasiertes_system",
|
||||||
|
"expectedValue": "Ja",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "modul_3",
|
||||||
|
"title": "Modul 3",
|
||||||
|
"description": "Beschreibung des dritten Moduls",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Modul 3",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "modulbasiertes_system",
|
||||||
|
"expectedValue": "Ja",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "ki_einsatz",
|
||||||
|
"title": "Einsatz Künstlicher Intelligenz",
|
||||||
|
"description": "Kommt im IT-System Künstliche Intelligenz zum Einsatz?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "Nein",
|
||||||
|
"label": "Nein",
|
||||||
|
"processingPurpose": "SYSTEM_OPERATION",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Ja",
|
||||||
|
"label": "Ja",
|
||||||
|
"processingPurpose": "DATA_ANALYSIS",
|
||||||
|
"employeeDataCategory": "SENSITIVE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "RADIOBUTTON",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "wirtschaftliche_auswirkungen",
|
||||||
|
"title": "Wirtschaftliche Auswirkungen",
|
||||||
|
"description": "Zu erwartende wirtschaftliche Auswirkungen auf das Unternehmen",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "Keine",
|
||||||
|
"label": "Keine",
|
||||||
|
"processingPurpose": "BUSINESS_PROCESS",
|
||||||
|
"employeeDataCategory": "NON_CRITICAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Ja",
|
||||||
|
"label": "Ja",
|
||||||
|
"processingPurpose": "BUSINESS_PROCESS",
|
||||||
|
"employeeDataCategory": "REVIEW_REQUIRED"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "RADIOBUTTON",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "HIDE",
|
||||||
|
"sourceFormElementReference": "art_der_massnahme",
|
||||||
|
"expectedValue": "Ablösung/Einstellung IT-System",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"reference": "beschreibung_wirtschaftliche_auswirkungen",
|
||||||
|
"title": "Beschreibung wirtschaftliche Auswirkungen",
|
||||||
|
"description": "Bitte beschreiben Sie die wirtschaftlichen Auswirkungen",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value": "",
|
||||||
|
"label": "Beschreibung der Auswirkungen",
|
||||||
|
"processingPurpose": "BUSINESS_PROCESS",
|
||||||
|
"employeeDataCategory": "REVIEW_REQUIRED"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "TEXTFIELD",
|
||||||
|
"visibilityCondition": {
|
||||||
|
"conditionType": "SHOW",
|
||||||
|
"sourceFormElementReference": "wirtschaftliche_auswirkungen",
|
||||||
|
"expectedValue": "Ja",
|
||||||
|
"operator": "EQUALS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user