feat(fullstack): Add logical AND and OR operators to seed files, add Sensitiviäts-Check
This commit is contained in:
@@ -1548,10 +1548,9 @@ components:
|
|||||||
format: uuid
|
format: uuid
|
||||||
nullable: true
|
nullable: true
|
||||||
visibilityConditions:
|
visibilityConditions:
|
||||||
type: array
|
$ref: "#/components/schemas/VisibilityConditionGroup"
|
||||||
items:
|
nullable: true
|
||||||
$ref: "#/components/schemas/FormElementVisibilityCondition"
|
description: Recursive visibility condition tree (AND/OR groups with leaf conditions)
|
||||||
description: List of visibility conditions (all must be met for element to be visible - AND logic)
|
|
||||||
sectionSpawnTriggers:
|
sectionSpawnTriggers:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
@@ -1612,9 +1611,8 @@ components:
|
|||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/FormOptionDto"
|
$ref: "#/components/schemas/FormOptionDto"
|
||||||
visibilityConditions:
|
visibilityConditions:
|
||||||
type: array
|
$ref: "#/components/schemas/VisibilityConditionGroup"
|
||||||
items:
|
nullable: true
|
||||||
$ref: "#/components/schemas/FormElementVisibilityCondition"
|
|
||||||
sectionSpawnTriggers:
|
sectionSpawnTriggers:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
@@ -1643,6 +1641,8 @@ components:
|
|||||||
$ref: "#/components/schemas/EmployeeDataCategory"
|
$ref: "#/components/schemas/EmployeeDataCategory"
|
||||||
columnConfig:
|
columnConfig:
|
||||||
$ref: "#/components/schemas/TableColumnConfigDto"
|
$ref: "#/components/schemas/TableColumnConfigDto"
|
||||||
|
visibilityConditions:
|
||||||
|
$ref: "#/components/schemas/VisibilityConditionGroup"
|
||||||
|
|
||||||
TableColumnConfigDto:
|
TableColumnConfigDto:
|
||||||
type: object
|
type: object
|
||||||
@@ -1716,24 +1716,56 @@ components:
|
|||||||
- TABLE
|
- TABLE
|
||||||
- FILE_UPLOAD
|
- FILE_UPLOAD
|
||||||
|
|
||||||
FormElementVisibilityCondition:
|
VisibilityConditionNode:
|
||||||
type: object
|
type: object
|
||||||
required:
|
description: A visibility condition node - either a leaf (single condition) or a group (AND/OR of conditions). Use nodeType to determine which properties are relevant.
|
||||||
- formElementConditionType
|
|
||||||
- sourceFormElementReference
|
|
||||||
properties:
|
properties:
|
||||||
|
nodeType:
|
||||||
|
type: string
|
||||||
|
enum: [LEAF, GROUP]
|
||||||
|
default: LEAF
|
||||||
|
description: Type discriminator - LEAF for single condition, GROUP for AND/OR of conditions
|
||||||
|
# Leaf properties (used when nodeType=LEAF)
|
||||||
formElementConditionType:
|
formElementConditionType:
|
||||||
$ref: "#/components/schemas/VisibilityConditionType"
|
$ref: "#/components/schemas/VisibilityConditionType"
|
||||||
sourceFormElementReference:
|
sourceFormElementReference:
|
||||||
type: string
|
type: string
|
||||||
description: Reference key of the source form element to check
|
nullable: true
|
||||||
|
description: "[LEAF] Reference key of the source form element to check"
|
||||||
formElementExpectedValue:
|
formElementExpectedValue:
|
||||||
type: string
|
type: string
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Expected value to compare against the source element's value property. Not required for IS_EMPTY and IS_NOT_EMPTY operators.
|
description: "[LEAF] Expected value to compare against the source element's value"
|
||||||
formElementOperator:
|
formElementOperator:
|
||||||
$ref: "#/components/schemas/VisibilityConditionOperator"
|
$ref: "#/components/schemas/VisibilityConditionOperator"
|
||||||
default: EQUALS
|
# Group properties (used when nodeType=GROUP)
|
||||||
|
groupOperator:
|
||||||
|
type: string
|
||||||
|
enum: [AND, OR]
|
||||||
|
nullable: true
|
||||||
|
description: "[GROUP] Logical operator to combine conditions"
|
||||||
|
conditions:
|
||||||
|
type: array
|
||||||
|
nullable: true
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/VisibilityConditionNode"
|
||||||
|
description: "[GROUP] List of child conditions"
|
||||||
|
|
||||||
|
VisibilityConditionGroup:
|
||||||
|
type: object
|
||||||
|
description: Root-level visibility condition group containing child conditions
|
||||||
|
properties:
|
||||||
|
operator:
|
||||||
|
type: string
|
||||||
|
enum: [AND, OR]
|
||||||
|
nullable: true
|
||||||
|
description: Logical operator to combine conditions
|
||||||
|
conditions:
|
||||||
|
type: array
|
||||||
|
nullable: true
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/VisibilityConditionNode"
|
||||||
|
description: List of child conditions (can be leaves or nested groups)
|
||||||
|
|
||||||
VisibilityConditionType:
|
VisibilityConditionType:
|
||||||
type: string
|
type: string
|
||||||
@@ -1748,6 +1780,8 @@ components:
|
|||||||
- NOT_EQUALS
|
- NOT_EQUALS
|
||||||
- IS_EMPTY
|
- IS_EMPTY
|
||||||
- IS_NOT_EMPTY
|
- IS_NOT_EMPTY
|
||||||
|
- CONTAINS
|
||||||
|
- NOT_CONTAINS
|
||||||
|
|
||||||
SectionSpawnTriggerDto:
|
SectionSpawnTriggerDto:
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormSnapshotD
|
|||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSectionSnapshotDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSectionSnapshotDto
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSnapshotDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSnapshotDto
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSubSectionSnapshotDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementSubSectionSnapshotDto
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementVisibilityCondition
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference
|
import com.fasterxml.jackson.core.type.TypeReference
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
@@ -21,6 +20,8 @@ import java.time.Instant
|
|||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionGroup as VisibilityConditionGroupDto
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionNode as VisibilityConditionNodeDto
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionOperator as VisibilityConditionOperatorDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionOperator as VisibilityConditionOperatorDto
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionType as VisibilityConditionTypeDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionType as VisibilityConditionTypeDto
|
||||||
|
|
||||||
@@ -168,12 +169,12 @@ class ApplicationFormFormatService(
|
|||||||
if (element.options.isEmpty()) return "Keine Daten" to false
|
if (element.options.isEmpty()) return "Keine Daten" to false
|
||||||
|
|
||||||
val objectMapper = jacksonObjectMapper()
|
val objectMapper = jacksonObjectMapper()
|
||||||
val headers = element.options.map { LatexEscaper.escape(it.label ?: "") }
|
val headers = element.options.map { LatexEscaper.escape(it.label) }
|
||||||
val columnData =
|
val columnData =
|
||||||
element.options.map { option ->
|
element.options.map { option ->
|
||||||
try {
|
try {
|
||||||
val typeRef = object : TypeReference<List<String>>() {}
|
val typeRef = object : TypeReference<List<String>>() {}
|
||||||
objectMapper.readValue(option.value ?: "[]", typeRef)
|
objectMapper.readValue(option.value, typeRef)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
emptyList<String>()
|
emptyList<String>()
|
||||||
}
|
}
|
||||||
@@ -268,26 +269,61 @@ class ApplicationFormFormatService(
|
|||||||
element: FormElementSnapshotDto,
|
element: FormElementSnapshotDto,
|
||||||
formElementsByRef: Map<String, FormElementSnapshotDto>,
|
formElementsByRef: Map<String, FormElementSnapshotDto>,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val conditions = element.visibilityConditions
|
val group = element.visibilityConditions ?: return true
|
||||||
if (conditions.isNullOrEmpty()) return true
|
if (group.conditions?.isEmpty() != false) return true
|
||||||
|
|
||||||
// All conditions must be met (AND logic)
|
return evaluateGroup(group, formElementsByRef)
|
||||||
return conditions.all { condition -> evaluateSingleCondition(condition, formElementsByRef) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun evaluateSingleCondition(
|
private fun evaluateGroup(
|
||||||
condition: FormElementVisibilityCondition,
|
group: VisibilityConditionGroupDto,
|
||||||
formElementsByRef: Map<String, FormElementSnapshotDto>,
|
formElementsByRef: Map<String, FormElementSnapshotDto>,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val sourceElement = formElementsByRef[condition.sourceFormElementReference] ?: return false
|
val conditions = group.conditions ?: return true
|
||||||
|
val results = conditions.map { evaluateNode(it, formElementsByRef) }
|
||||||
|
return when (group.operator) {
|
||||||
|
VisibilityConditionGroupDto.Operator.AND -> results.all { it }
|
||||||
|
VisibilityConditionGroupDto.Operator.OR -> results.any { it }
|
||||||
|
null -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateNode(
|
||||||
|
node: VisibilityConditionNodeDto,
|
||||||
|
formElementsByRef: Map<String, FormElementSnapshotDto>,
|
||||||
|
): Boolean =
|
||||||
|
when (node.nodeType) {
|
||||||
|
VisibilityConditionNodeDto.NodeType.LEAF, null -> evaluateLeafCondition(node, formElementsByRef)
|
||||||
|
VisibilityConditionNodeDto.NodeType.GROUP -> {
|
||||||
|
val nestedGroup =
|
||||||
|
VisibilityConditionGroupDto(
|
||||||
|
operator =
|
||||||
|
when (node.groupOperator) {
|
||||||
|
VisibilityConditionNodeDto.GroupOperator.AND -> VisibilityConditionGroupDto.Operator.AND
|
||||||
|
VisibilityConditionNodeDto.GroupOperator.OR -> VisibilityConditionGroupDto.Operator.OR
|
||||||
|
null -> VisibilityConditionGroupDto.Operator.AND
|
||||||
|
},
|
||||||
|
conditions = node.conditions,
|
||||||
|
)
|
||||||
|
evaluateGroup(nestedGroup, formElementsByRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateLeafCondition(
|
||||||
|
node: VisibilityConditionNodeDto,
|
||||||
|
formElementsByRef: Map<String, FormElementSnapshotDto>,
|
||||||
|
): Boolean {
|
||||||
|
val sourceRef = node.sourceFormElementReference ?: return false
|
||||||
|
val sourceElement = formElementsByRef[sourceRef] ?: return false
|
||||||
val sourceValue = getFormElementValue(sourceElement)
|
val sourceValue = getFormElementValue(sourceElement)
|
||||||
|
|
||||||
val operator = condition.formElementOperator ?: VisibilityConditionOperatorDto.EQUALS
|
val operator = node.formElementOperator ?: VisibilityConditionOperatorDto.EQUALS
|
||||||
val conditionMet = evaluateCondition(sourceValue, condition.formElementExpectedValue, operator)
|
val conditionMet = evaluateCondition(sourceValue, node.formElementExpectedValue ?: "", operator)
|
||||||
|
|
||||||
return when (condition.formElementConditionType) {
|
return when (node.formElementConditionType) {
|
||||||
VisibilityConditionTypeDto.SHOW -> conditionMet
|
VisibilityConditionTypeDto.SHOW -> conditionMet
|
||||||
VisibilityConditionTypeDto.HIDE -> !conditionMet
|
VisibilityConditionTypeDto.HIDE -> !conditionMet
|
||||||
|
null -> conditionMet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,5 +350,9 @@ class ApplicationFormFormatService(
|
|||||||
expectedValue?.let { !actualValue.equals(it, ignoreCase = true) } ?: false
|
expectedValue?.let { !actualValue.equals(it, ignoreCase = true) } ?: false
|
||||||
VisibilityConditionOperatorDto.IS_EMPTY -> actualValue.isEmpty()
|
VisibilityConditionOperatorDto.IS_EMPTY -> actualValue.isEmpty()
|
||||||
VisibilityConditionOperatorDto.IS_NOT_EMPTY -> actualValue.isNotEmpty()
|
VisibilityConditionOperatorDto.IS_NOT_EMPTY -> actualValue.isNotEmpty()
|
||||||
|
VisibilityConditionOperatorDto.CONTAINS ->
|
||||||
|
expectedValue?.let { actualValue.contains(it, ignoreCase = true) } ?: false
|
||||||
|
VisibilityConditionOperatorDto.NOT_CONTAINS ->
|
||||||
|
expectedValue?.let { !actualValue.contains(it, ignoreCase = true) } ?: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,11 +127,9 @@ class ApplicationFormVersionService(
|
|||||||
type = element.type,
|
type = element.type,
|
||||||
options = element.options.map { formOptionMapper.toFormOptionDto(it) },
|
options = element.options.map { formOptionMapper.toFormOptionDto(it) },
|
||||||
visibilityConditions =
|
visibilityConditions =
|
||||||
element.visibilityConditions
|
element.visibilityConditions?.let {
|
||||||
.map {
|
visibilityConditionMapper.toGroupConditionDto(it)
|
||||||
visibilityConditionMapper
|
},
|
||||||
.toFormElementVisibilityConditionDto(it)
|
|
||||||
},
|
|
||||||
sectionSpawnTriggers =
|
sectionSpawnTriggers =
|
||||||
element.sectionSpawnTriggers.map {
|
element.sectionSpawnTriggers.map {
|
||||||
spawnTriggerMapper.toSectionSpawnTriggerDto(it)
|
spawnTriggerMapper.toSectionSpawnTriggerDto(it)
|
||||||
@@ -186,10 +184,9 @@ class ApplicationFormVersionService(
|
|||||||
.map { formOptionMapper.toFormOption(it) }
|
.map { formOptionMapper.toFormOption(it) }
|
||||||
.toMutableList(),
|
.toMutableList(),
|
||||||
visibilityConditions =
|
visibilityConditions =
|
||||||
elementSnapshot.visibilityConditions
|
elementSnapshot.visibilityConditions?.let {
|
||||||
?.map { visibilityConditionMapper.toFormElementVisibilityCondition(it) }
|
visibilityConditionMapper.toGroupCondition(it)
|
||||||
?.toMutableList()
|
},
|
||||||
?: mutableListOf(),
|
|
||||||
sectionSpawnTriggers =
|
sectionSpawnTriggers =
|
||||||
elementSnapshot.sectionSpawnTriggers
|
elementSnapshot.sectionSpawnTriggers
|
||||||
?.map { spawnTriggerMapper.toSectionSpawnTrigger(it) }
|
?.map { spawnTriggerMapper.toSectionSpawnTrigger(it) }
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.config
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.Module
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class JacksonConfig {
|
||||||
|
@Bean
|
||||||
|
fun visibilityConditionJacksonModule(): Module = visibilityConditionModule()
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.config
|
||||||
|
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionNode
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionOperator
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionType
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
|
|
||||||
|
class VisibilityConditionNodeDeserializer : JsonDeserializer<VisibilityConditionNode>() {
|
||||||
|
override fun deserialize(
|
||||||
|
p: JsonParser,
|
||||||
|
ctxt: DeserializationContext,
|
||||||
|
): VisibilityConditionNode {
|
||||||
|
val node = p.codec.readTree<JsonNode>(p)
|
||||||
|
|
||||||
|
val nodeType = node.get("nodeType")?.asText()
|
||||||
|
|
||||||
|
return if (nodeType == "GROUP") {
|
||||||
|
val groupOperator =
|
||||||
|
node.get("groupOperator")?.asText()?.let {
|
||||||
|
VisibilityConditionNode.GroupOperator.forValue(it)
|
||||||
|
}
|
||||||
|
val conditions =
|
||||||
|
node.get("conditions")?.map { childNode ->
|
||||||
|
val childParser = childNode.traverse(p.codec)
|
||||||
|
childParser.nextToken()
|
||||||
|
deserialize(childParser, ctxt)
|
||||||
|
} ?: emptyList()
|
||||||
|
VisibilityConditionNode(
|
||||||
|
nodeType = VisibilityConditionNode.NodeType.GROUP,
|
||||||
|
groupOperator = groupOperator,
|
||||||
|
conditions = conditions,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Default to LEAF for backward compatibility
|
||||||
|
val sourceRef = node.get("sourceFormElementReference")?.asText()
|
||||||
|
val conditionType =
|
||||||
|
node.get("formElementConditionType")?.asText()?.let {
|
||||||
|
VisibilityConditionType.valueOf(it)
|
||||||
|
}
|
||||||
|
val expectedValue = node.get("formElementExpectedValue")?.asText()
|
||||||
|
val formElementOperator =
|
||||||
|
node.get("formElementOperator")?.asText()?.let {
|
||||||
|
VisibilityConditionOperator.valueOf(it)
|
||||||
|
}
|
||||||
|
VisibilityConditionNode(
|
||||||
|
nodeType = VisibilityConditionNode.NodeType.LEAF,
|
||||||
|
sourceFormElementReference = sourceRef,
|
||||||
|
formElementConditionType = conditionType,
|
||||||
|
formElementExpectedValue = expectedValue,
|
||||||
|
formElementOperator = formElementOperator,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VisibilityConditionNodeSerializer : JsonSerializer<VisibilityConditionNode>() {
|
||||||
|
override fun serialize(
|
||||||
|
value: VisibilityConditionNode,
|
||||||
|
gen: JsonGenerator,
|
||||||
|
serializers: SerializerProvider,
|
||||||
|
) {
|
||||||
|
gen.writeStartObject()
|
||||||
|
gen.writeStringField("nodeType", (value.nodeType ?: VisibilityConditionNode.NodeType.LEAF).value)
|
||||||
|
|
||||||
|
if (value.nodeType == VisibilityConditionNode.NodeType.GROUP) {
|
||||||
|
value.groupOperator?.let { gen.writeStringField("groupOperator", it.value) }
|
||||||
|
gen.writeArrayFieldStart("conditions")
|
||||||
|
value.conditions?.forEach { serialize(it, gen, serializers) }
|
||||||
|
gen.writeEndArray()
|
||||||
|
} else {
|
||||||
|
value.formElementConditionType?.let {
|
||||||
|
gen.writeStringField("formElementConditionType", it.value)
|
||||||
|
}
|
||||||
|
value.sourceFormElementReference?.let {
|
||||||
|
gen.writeStringField("sourceFormElementReference", it)
|
||||||
|
}
|
||||||
|
value.formElementExpectedValue?.let {
|
||||||
|
gen.writeStringField("formElementExpectedValue", it)
|
||||||
|
}
|
||||||
|
value.formElementOperator?.let {
|
||||||
|
gen.writeStringField("formElementOperator", it.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gen.writeEndObject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visibilityConditionModule(): SimpleModule =
|
||||||
|
SimpleModule("VisibilityConditionModule").apply {
|
||||||
|
addDeserializer(VisibilityConditionNode::class.java, VisibilityConditionNodeDeserializer())
|
||||||
|
addSerializer(VisibilityConditionNode::class.java, VisibilityConditionNodeSerializer())
|
||||||
|
}
|
||||||
@@ -12,6 +12,8 @@ import jakarta.persistence.GeneratedValue
|
|||||||
import jakarta.persistence.Id
|
import jakarta.persistence.Id
|
||||||
import jakarta.persistence.JoinColumn
|
import jakarta.persistence.JoinColumn
|
||||||
import jakarta.persistence.ManyToOne
|
import jakarta.persistence.ManyToOne
|
||||||
|
import org.hibernate.annotations.JdbcTypeCode
|
||||||
|
import org.hibernate.type.SqlTypes
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@@ -30,9 +32,9 @@ 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,
|
||||||
@ElementCollection
|
@JdbcTypeCode(SqlTypes.JSON)
|
||||||
@CollectionTable(name = "visibility_conditions", joinColumns = [JoinColumn(name = "form_element_id")])
|
@Column(columnDefinition = "jsonb")
|
||||||
var visibilityConditions: MutableList<FormElementVisibilityCondition> = mutableListOf(),
|
var visibilityConditions: GroupCondition? = null,
|
||||||
@ElementCollection
|
@ElementCollection
|
||||||
@CollectionTable(name = "section_spawn_triggers", joinColumns = [JoinColumn(name = "form_element_id")])
|
@CollectionTable(name = "section_spawn_triggers", joinColumns = [JoinColumn(name = "form_element_id")])
|
||||||
var sectionSpawnTriggers: MutableList<SectionSpawnTrigger> = mutableListOf(),
|
var sectionSpawnTriggers: MutableList<SectionSpawnTrigger> = mutableListOf(),
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ class FormElementMapper(
|
|||||||
formElement.formElementSubSection?.id
|
formElement.formElementSubSection?.id
|
||||||
?: throw IllegalStateException("FormElementSubSection ID must not be null!"),
|
?: throw IllegalStateException("FormElementSubSection ID must not be null!"),
|
||||||
visibilityConditions =
|
visibilityConditions =
|
||||||
formElement.visibilityConditions.map {
|
formElement.visibilityConditions?.let {
|
||||||
visibilityConditionMapper.toFormElementVisibilityConditionDto(it)
|
visibilityConditionMapper.toGroupConditionDto(it)
|
||||||
},
|
},
|
||||||
sectionSpawnTriggers =
|
sectionSpawnTriggers =
|
||||||
formElement.sectionSpawnTriggers.map {
|
formElement.sectionSpawnTriggers.map {
|
||||||
@@ -49,10 +49,9 @@ class FormElementMapper(
|
|||||||
type = formElement.type,
|
type = formElement.type,
|
||||||
formElementSubSection = formElementSubSection,
|
formElementSubSection = formElementSubSection,
|
||||||
visibilityConditions =
|
visibilityConditions =
|
||||||
formElement.visibilityConditions
|
formElement.visibilityConditions?.let {
|
||||||
?.map { visibilityConditionMapper.toFormElementVisibilityCondition(it) }
|
visibilityConditionMapper.toGroupCondition(it)
|
||||||
?.toMutableList()
|
},
|
||||||
?: mutableListOf(),
|
|
||||||
sectionSpawnTriggers =
|
sectionSpawnTriggers =
|
||||||
formElement.sectionSpawnTriggers
|
formElement.sectionSpawnTriggers
|
||||||
?.map { spawnTriggerMapper.toSectionSpawnTrigger(it) }
|
?.map { spawnTriggerMapper.toSectionSpawnTrigger(it) }
|
||||||
@@ -78,10 +77,9 @@ class FormElementMapper(
|
|||||||
type = formElement.type,
|
type = formElement.type,
|
||||||
formElementSubSection = formElementSubSection,
|
formElementSubSection = formElementSubSection,
|
||||||
visibilityConditions =
|
visibilityConditions =
|
||||||
formElement.visibilityConditions
|
formElement.visibilityConditions?.let {
|
||||||
?.map { visibilityConditionMapper.toFormElementVisibilityCondition(it) }
|
visibilityConditionMapper.toGroupCondition(it)
|
||||||
?.toMutableList()
|
},
|
||||||
?: mutableListOf(),
|
|
||||||
sectionSpawnTriggers =
|
sectionSpawnTriggers =
|
||||||
formElement.sectionSpawnTriggers
|
formElement.sectionSpawnTriggers
|
||||||
?.map { spawnTriggerMapper.toSectionSpawnTrigger(it) }
|
?.map { spawnTriggerMapper.toSectionSpawnTrigger(it) }
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
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 formElementConditionType: VisibilityConditionType,
|
|
||||||
val sourceFormElementReference: String,
|
|
||||||
val formElementExpectedValue: String?,
|
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
val formElementOperator: VisibilityConditionOperator = VisibilityConditionOperator.EQUALS,
|
|
||||||
)
|
|
||||||
@@ -1,34 +1,87 @@
|
|||||||
package com.betriebsratkanzlei.legalconsenthub.form_element
|
package com.betriebsratkanzlei.legalconsenthub.form_element
|
||||||
|
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.FormElementVisibilityCondition as FormElementVisibilityConditionDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionGroup as VisibilityConditionGroupDto
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionNode as VisibilityConditionNodeDto
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionOperator as VisibilityConditionOperatorDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionOperator as VisibilityConditionOperatorDto
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionType as VisibilityConditionTypeDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionType as VisibilityConditionTypeDto
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class FormElementVisibilityConditionMapper {
|
class FormElementVisibilityConditionMapper {
|
||||||
fun toFormElementVisibilityConditionDto(
|
fun toGroupConditionDto(group: GroupCondition): VisibilityConditionGroupDto =
|
||||||
condition: FormElementVisibilityCondition,
|
VisibilityConditionGroupDto(
|
||||||
): FormElementVisibilityConditionDto =
|
operator = toGroupOperatorDto(group.operator),
|
||||||
FormElementVisibilityConditionDto(
|
conditions = group.conditions.map { toNodeDto(it) },
|
||||||
formElementConditionType = toVisibilityConditionTypeDto(condition.formElementConditionType),
|
|
||||||
sourceFormElementReference = condition.sourceFormElementReference,
|
|
||||||
formElementExpectedValue = condition.formElementExpectedValue,
|
|
||||||
formElementOperator = toVisibilityConditionOperatorDto(condition.formElementOperator),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toFormElementVisibilityCondition(
|
fun toGroupCondition(groupDto: VisibilityConditionGroupDto): GroupCondition =
|
||||||
conditionDto: FormElementVisibilityConditionDto,
|
GroupCondition(
|
||||||
): FormElementVisibilityCondition =
|
operator = toGroupOperator(groupDto.operator ?: VisibilityConditionGroupDto.Operator.AND),
|
||||||
FormElementVisibilityCondition(
|
conditions = groupDto.conditions?.map { toNode(it) } ?: emptyList(),
|
||||||
formElementConditionType = toVisibilityConditionType(conditionDto.formElementConditionType),
|
|
||||||
sourceFormElementReference = conditionDto.sourceFormElementReference,
|
|
||||||
formElementExpectedValue = conditionDto.formElementExpectedValue,
|
|
||||||
formElementOperator =
|
|
||||||
conditionDto.formElementOperator?.let { toVisibilityConditionOperator(it) }
|
|
||||||
?: VisibilityConditionOperator.EQUALS,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun toNodeDto(node: VisibilityConditionNode): VisibilityConditionNodeDto =
|
||||||
|
when (node) {
|
||||||
|
is LeafCondition ->
|
||||||
|
VisibilityConditionNodeDto(
|
||||||
|
nodeType = VisibilityConditionNodeDto.NodeType.LEAF,
|
||||||
|
sourceFormElementReference = node.sourceFormElementReference,
|
||||||
|
formElementConditionType = node.formElementConditionType?.let { toVisibilityConditionTypeDto(it) },
|
||||||
|
formElementExpectedValue = node.formElementExpectedValue,
|
||||||
|
formElementOperator = toVisibilityConditionOperatorDto(node.formElementOperator),
|
||||||
|
)
|
||||||
|
|
||||||
|
is GroupCondition ->
|
||||||
|
VisibilityConditionNodeDto(
|
||||||
|
nodeType = VisibilityConditionNodeDto.NodeType.GROUP,
|
||||||
|
groupOperator = toNodeGroupOperatorDto(node.operator),
|
||||||
|
conditions = node.conditions.map { toNodeDto(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toNode(nodeDto: VisibilityConditionNodeDto): VisibilityConditionNode =
|
||||||
|
when (nodeDto.nodeType) {
|
||||||
|
VisibilityConditionNodeDto.NodeType.GROUP ->
|
||||||
|
GroupCondition(
|
||||||
|
operator = toGroupOperatorFromNode(nodeDto.groupOperator),
|
||||||
|
conditions = nodeDto.conditions?.map { toNode(it) } ?: emptyList(),
|
||||||
|
)
|
||||||
|
VisibilityConditionNodeDto.NodeType.LEAF, null ->
|
||||||
|
LeafCondition(
|
||||||
|
formElementConditionType = nodeDto.formElementConditionType?.let { toVisibilityConditionType(it) },
|
||||||
|
sourceFormElementReference = nodeDto.sourceFormElementReference ?: "",
|
||||||
|
formElementExpectedValue = nodeDto.formElementExpectedValue,
|
||||||
|
formElementOperator =
|
||||||
|
nodeDto.formElementOperator?.let { toVisibilityConditionOperator(it) }
|
||||||
|
?: VisibilityConditionOperator.EQUALS,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toGroupOperatorDto(op: GroupOperator): VisibilityConditionGroupDto.Operator =
|
||||||
|
when (op) {
|
||||||
|
GroupOperator.AND -> VisibilityConditionGroupDto.Operator.AND
|
||||||
|
GroupOperator.OR -> VisibilityConditionGroupDto.Operator.OR
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toGroupOperator(op: VisibilityConditionGroupDto.Operator): GroupOperator =
|
||||||
|
when (op) {
|
||||||
|
VisibilityConditionGroupDto.Operator.AND -> GroupOperator.AND
|
||||||
|
VisibilityConditionGroupDto.Operator.OR -> GroupOperator.OR
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toNodeGroupOperatorDto(op: GroupOperator): VisibilityConditionNodeDto.GroupOperator =
|
||||||
|
when (op) {
|
||||||
|
GroupOperator.AND -> VisibilityConditionNodeDto.GroupOperator.AND
|
||||||
|
GroupOperator.OR -> VisibilityConditionNodeDto.GroupOperator.OR
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toGroupOperatorFromNode(op: VisibilityConditionNodeDto.GroupOperator?): GroupOperator =
|
||||||
|
when (op) {
|
||||||
|
VisibilityConditionNodeDto.GroupOperator.AND -> GroupOperator.AND
|
||||||
|
VisibilityConditionNodeDto.GroupOperator.OR -> GroupOperator.OR
|
||||||
|
null -> GroupOperator.AND
|
||||||
|
}
|
||||||
|
|
||||||
private fun toVisibilityConditionTypeDto(type: VisibilityConditionType): VisibilityConditionTypeDto =
|
private fun toVisibilityConditionTypeDto(type: VisibilityConditionType): VisibilityConditionTypeDto =
|
||||||
when (type) {
|
when (type) {
|
||||||
VisibilityConditionType.SHOW -> VisibilityConditionTypeDto.SHOW
|
VisibilityConditionType.SHOW -> VisibilityConditionTypeDto.SHOW
|
||||||
@@ -45,27 +98,23 @@ class FormElementVisibilityConditionMapper {
|
|||||||
operator: VisibilityConditionOperator,
|
operator: VisibilityConditionOperator,
|
||||||
): VisibilityConditionOperatorDto =
|
): VisibilityConditionOperatorDto =
|
||||||
when (operator) {
|
when (operator) {
|
||||||
VisibilityConditionOperator.EQUALS ->
|
VisibilityConditionOperator.EQUALS -> VisibilityConditionOperatorDto.EQUALS
|
||||||
VisibilityConditionOperatorDto.EQUALS
|
VisibilityConditionOperator.NOT_EQUALS -> VisibilityConditionOperatorDto.NOT_EQUALS
|
||||||
VisibilityConditionOperator.NOT_EQUALS ->
|
VisibilityConditionOperator.IS_EMPTY -> VisibilityConditionOperatorDto.IS_EMPTY
|
||||||
VisibilityConditionOperatorDto.NOT_EQUALS
|
VisibilityConditionOperator.IS_NOT_EMPTY -> VisibilityConditionOperatorDto.IS_NOT_EMPTY
|
||||||
VisibilityConditionOperator.IS_EMPTY ->
|
VisibilityConditionOperator.CONTAINS -> VisibilityConditionOperatorDto.CONTAINS
|
||||||
VisibilityConditionOperatorDto.IS_EMPTY
|
VisibilityConditionOperator.NOT_CONTAINS -> VisibilityConditionOperatorDto.NOT_CONTAINS
|
||||||
VisibilityConditionOperator.IS_NOT_EMPTY ->
|
|
||||||
VisibilityConditionOperatorDto.IS_NOT_EMPTY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toVisibilityConditionOperator(
|
private fun toVisibilityConditionOperator(
|
||||||
operatorDto: VisibilityConditionOperatorDto,
|
operatorDto: VisibilityConditionOperatorDto,
|
||||||
): VisibilityConditionOperator =
|
): VisibilityConditionOperator =
|
||||||
when (operatorDto) {
|
when (operatorDto) {
|
||||||
VisibilityConditionOperatorDto.EQUALS ->
|
VisibilityConditionOperatorDto.EQUALS -> VisibilityConditionOperator.EQUALS
|
||||||
VisibilityConditionOperator.EQUALS
|
VisibilityConditionOperatorDto.NOT_EQUALS -> VisibilityConditionOperator.NOT_EQUALS
|
||||||
VisibilityConditionOperatorDto.NOT_EQUALS ->
|
VisibilityConditionOperatorDto.IS_EMPTY -> VisibilityConditionOperator.IS_EMPTY
|
||||||
VisibilityConditionOperator.NOT_EQUALS
|
VisibilityConditionOperatorDto.IS_NOT_EMPTY -> VisibilityConditionOperator.IS_NOT_EMPTY
|
||||||
VisibilityConditionOperatorDto.IS_EMPTY ->
|
VisibilityConditionOperatorDto.CONTAINS -> VisibilityConditionOperator.CONTAINS
|
||||||
VisibilityConditionOperator.IS_EMPTY
|
VisibilityConditionOperatorDto.NOT_CONTAINS -> VisibilityConditionOperator.NOT_CONTAINS
|
||||||
VisibilityConditionOperatorDto.IS_NOT_EMPTY ->
|
|
||||||
VisibilityConditionOperator.IS_NOT_EMPTY
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import jakarta.persistence.AttributeOverrides
|
|||||||
import jakarta.persistence.Column
|
import jakarta.persistence.Column
|
||||||
import jakarta.persistence.Embeddable
|
import jakarta.persistence.Embeddable
|
||||||
import jakarta.persistence.Embedded
|
import jakarta.persistence.Embedded
|
||||||
|
import org.hibernate.annotations.JdbcTypeCode
|
||||||
|
import org.hibernate.type.SqlTypes
|
||||||
|
|
||||||
@Embeddable
|
@Embeddable
|
||||||
class FormOption(
|
class FormOption(
|
||||||
@@ -35,4 +37,7 @@ class FormOption(
|
|||||||
AttributeOverride(name = "isCheckbox", column = Column(name = "col_config_is_checkbox")),
|
AttributeOverride(name = "isCheckbox", column = Column(name = "col_config_is_checkbox")),
|
||||||
)
|
)
|
||||||
var columnConfig: TableColumnConfig? = null,
|
var columnConfig: TableColumnConfig? = null,
|
||||||
|
@JdbcTypeCode(SqlTypes.JSON)
|
||||||
|
@Column(columnDefinition = "jsonb", name = "visibility_conditions")
|
||||||
|
var visibilityConditions: GroupCondition? = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.springframework.stereotype.Component
|
|||||||
@Component
|
@Component
|
||||||
class FormOptionMapper(
|
class FormOptionMapper(
|
||||||
private val columnConfigMapper: TableColumnConfigMapper,
|
private val columnConfigMapper: TableColumnConfigMapper,
|
||||||
|
private val visibilityConditionMapper: FormElementVisibilityConditionMapper,
|
||||||
) {
|
) {
|
||||||
fun toFormOptionDto(formOption: FormOption): FormOptionDto =
|
fun toFormOptionDto(formOption: FormOption): FormOptionDto =
|
||||||
FormOptionDto(
|
FormOptionDto(
|
||||||
@@ -14,6 +15,10 @@ class FormOptionMapper(
|
|||||||
processingPurpose = formOption.processingPurpose,
|
processingPurpose = formOption.processingPurpose,
|
||||||
employeeDataCategory = formOption.employeeDataCategory,
|
employeeDataCategory = formOption.employeeDataCategory,
|
||||||
columnConfig = formOption.columnConfig?.let { columnConfigMapper.toTableColumnConfigDto(it) },
|
columnConfig = formOption.columnConfig?.let { columnConfigMapper.toTableColumnConfigDto(it) },
|
||||||
|
visibilityConditions =
|
||||||
|
formOption.visibilityConditions?.let {
|
||||||
|
visibilityConditionMapper.toGroupConditionDto(it)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toFormOption(formOptionDto: FormOptionDto): FormOption =
|
fun toFormOption(formOptionDto: FormOptionDto): FormOption =
|
||||||
@@ -23,5 +28,9 @@ class FormOptionMapper(
|
|||||||
processingPurpose = formOptionDto.processingPurpose,
|
processingPurpose = formOptionDto.processingPurpose,
|
||||||
employeeDataCategory = formOptionDto.employeeDataCategory,
|
employeeDataCategory = formOptionDto.employeeDataCategory,
|
||||||
columnConfig = formOptionDto.columnConfig?.let { columnConfigMapper.toTableColumnConfig(it) },
|
columnConfig = formOptionDto.columnConfig?.let { columnConfigMapper.toTableColumnConfig(it) },
|
||||||
|
visibilityConditions =
|
||||||
|
formOptionDto.visibilityConditions?.let {
|
||||||
|
visibilityConditionMapper.toGroupCondition(it)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ class SectionSpawnTriggerMapper {
|
|||||||
VisibilityConditionOperator.NOT_EQUALS -> VisibilityConditionOperatorDto.NOT_EQUALS
|
VisibilityConditionOperator.NOT_EQUALS -> VisibilityConditionOperatorDto.NOT_EQUALS
|
||||||
VisibilityConditionOperator.IS_EMPTY -> VisibilityConditionOperatorDto.IS_EMPTY
|
VisibilityConditionOperator.IS_EMPTY -> VisibilityConditionOperatorDto.IS_EMPTY
|
||||||
VisibilityConditionOperator.IS_NOT_EMPTY -> VisibilityConditionOperatorDto.IS_NOT_EMPTY
|
VisibilityConditionOperator.IS_NOT_EMPTY -> VisibilityConditionOperatorDto.IS_NOT_EMPTY
|
||||||
|
VisibilityConditionOperator.CONTAINS -> VisibilityConditionOperatorDto.CONTAINS
|
||||||
|
VisibilityConditionOperator.NOT_CONTAINS -> VisibilityConditionOperatorDto.NOT_CONTAINS
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toVisibilityConditionOperator(
|
private fun toVisibilityConditionOperator(
|
||||||
@@ -53,5 +55,7 @@ class SectionSpawnTriggerMapper {
|
|||||||
VisibilityConditionOperatorDto.NOT_EQUALS -> VisibilityConditionOperator.NOT_EQUALS
|
VisibilityConditionOperatorDto.NOT_EQUALS -> VisibilityConditionOperator.NOT_EQUALS
|
||||||
VisibilityConditionOperatorDto.IS_EMPTY -> VisibilityConditionOperator.IS_EMPTY
|
VisibilityConditionOperatorDto.IS_EMPTY -> VisibilityConditionOperator.IS_EMPTY
|
||||||
VisibilityConditionOperatorDto.IS_NOT_EMPTY -> VisibilityConditionOperator.IS_NOT_EMPTY
|
VisibilityConditionOperatorDto.IS_NOT_EMPTY -> VisibilityConditionOperator.IS_NOT_EMPTY
|
||||||
|
VisibilityConditionOperatorDto.CONTAINS -> VisibilityConditionOperator.CONTAINS
|
||||||
|
VisibilityConditionOperatorDto.NOT_CONTAINS -> VisibilityConditionOperator.NOT_CONTAINS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ class TableColumnFilterMapper {
|
|||||||
VisibilityConditionOperator.NOT_EQUALS -> VisibilityConditionOperatorDto.NOT_EQUALS
|
VisibilityConditionOperator.NOT_EQUALS -> VisibilityConditionOperatorDto.NOT_EQUALS
|
||||||
VisibilityConditionOperator.IS_EMPTY -> VisibilityConditionOperatorDto.IS_EMPTY
|
VisibilityConditionOperator.IS_EMPTY -> VisibilityConditionOperatorDto.IS_EMPTY
|
||||||
VisibilityConditionOperator.IS_NOT_EMPTY -> VisibilityConditionOperatorDto.IS_NOT_EMPTY
|
VisibilityConditionOperator.IS_NOT_EMPTY -> VisibilityConditionOperatorDto.IS_NOT_EMPTY
|
||||||
|
VisibilityConditionOperator.CONTAINS -> VisibilityConditionOperatorDto.CONTAINS
|
||||||
|
VisibilityConditionOperator.NOT_CONTAINS -> VisibilityConditionOperatorDto.NOT_CONTAINS
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun VisibilityConditionOperatorDto.toEntity(): VisibilityConditionOperator =
|
private fun VisibilityConditionOperatorDto.toEntity(): VisibilityConditionOperator =
|
||||||
@@ -34,5 +36,7 @@ class TableColumnFilterMapper {
|
|||||||
VisibilityConditionOperatorDto.NOT_EQUALS -> VisibilityConditionOperator.NOT_EQUALS
|
VisibilityConditionOperatorDto.NOT_EQUALS -> VisibilityConditionOperator.NOT_EQUALS
|
||||||
VisibilityConditionOperatorDto.IS_EMPTY -> VisibilityConditionOperator.IS_EMPTY
|
VisibilityConditionOperatorDto.IS_EMPTY -> VisibilityConditionOperator.IS_EMPTY
|
||||||
VisibilityConditionOperatorDto.IS_NOT_EMPTY -> VisibilityConditionOperator.IS_NOT_EMPTY
|
VisibilityConditionOperatorDto.IS_NOT_EMPTY -> VisibilityConditionOperator.IS_NOT_EMPTY
|
||||||
|
VisibilityConditionOperatorDto.CONTAINS -> VisibilityConditionOperator.CONTAINS
|
||||||
|
VisibilityConditionOperatorDto.NOT_CONTAINS -> VisibilityConditionOperator.NOT_CONTAINS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.form_element
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo
|
||||||
|
|
||||||
|
@JsonTypeInfo(
|
||||||
|
use = JsonTypeInfo.Id.DEDUCTION,
|
||||||
|
defaultImpl = LeafCondition::class,
|
||||||
|
)
|
||||||
|
@JsonSubTypes(
|
||||||
|
JsonSubTypes.Type(GroupCondition::class),
|
||||||
|
JsonSubTypes.Type(LeafCondition::class),
|
||||||
|
)
|
||||||
|
sealed interface VisibilityConditionNode
|
||||||
|
|
||||||
|
data class GroupCondition(
|
||||||
|
val operator: GroupOperator,
|
||||||
|
val conditions: List<VisibilityConditionNode>,
|
||||||
|
) : VisibilityConditionNode
|
||||||
|
|
||||||
|
data class LeafCondition(
|
||||||
|
val formElementConditionType: VisibilityConditionType? = null,
|
||||||
|
val sourceFormElementReference: String,
|
||||||
|
val formElementExpectedValue: String? = null,
|
||||||
|
val formElementOperator: VisibilityConditionOperator = VisibilityConditionOperator.EQUALS,
|
||||||
|
) : VisibilityConditionNode
|
||||||
|
|
||||||
|
enum class GroupOperator {
|
||||||
|
AND,
|
||||||
|
OR,
|
||||||
|
}
|
||||||
@@ -5,4 +5,6 @@ enum class VisibilityConditionOperator {
|
|||||||
NOT_EQUALS,
|
NOT_EQUALS,
|
||||||
IS_EMPTY,
|
IS_EMPTY,
|
||||||
IS_NOT_EMPTY,
|
IS_NOT_EMPTY,
|
||||||
|
CONTAINS,
|
||||||
|
NOT_CONTAINS,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.betriebsratkanzlei.legalconsenthub.seed
|
|||||||
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormMapper
|
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormMapper
|
||||||
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormRepository
|
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormRepository
|
||||||
import com.betriebsratkanzlei.legalconsenthub.application_form_version.ApplicationFormVersionService
|
import com.betriebsratkanzlei.legalconsenthub.application_form_version.ApplicationFormVersionService
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.config.visibilityConditionModule
|
||||||
import com.betriebsratkanzlei.legalconsenthub.user.User
|
import com.betriebsratkanzlei.legalconsenthub.user.User
|
||||||
import com.betriebsratkanzlei.legalconsenthub.user.UserRepository
|
import com.betriebsratkanzlei.legalconsenthub.user.UserRepository
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
|
||||||
@@ -25,7 +26,10 @@ class InitialApplicationFormSeeder(
|
|||||||
private val userRepository: UserRepository,
|
private val userRepository: UserRepository,
|
||||||
private val versionService: ApplicationFormVersionService,
|
private val versionService: ApplicationFormVersionService,
|
||||||
) : ApplicationRunner {
|
) : ApplicationRunner {
|
||||||
private val yamlMapper = ObjectMapper(YAMLFactory()).findAndRegisterModules()
|
private val yamlMapper =
|
||||||
|
ObjectMapper(
|
||||||
|
YAMLFactory(),
|
||||||
|
).findAndRegisterModules().registerModule(visibilityConditionModule())
|
||||||
|
|
||||||
override fun run(args: ApplicationArguments) {
|
override fun run(args: ApplicationArguments) {
|
||||||
seedInitialFormIfMissing()
|
seedInitialFormIfMissing()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.betriebsratkanzlei.legalconsenthub.seed
|
|||||||
|
|
||||||
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormMapper
|
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormMapper
|
||||||
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormRepository
|
import com.betriebsratkanzlei.legalconsenthub.application_form.ApplicationFormRepository
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.config.visibilityConditionModule
|
||||||
import com.betriebsratkanzlei.legalconsenthub.user.User
|
import com.betriebsratkanzlei.legalconsenthub.user.User
|
||||||
import com.betriebsratkanzlei.legalconsenthub.user.UserRepository
|
import com.betriebsratkanzlei.legalconsenthub.user.UserRepository
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
|
||||||
@@ -22,7 +23,10 @@ class InitialApplicationFormTemplateSeeder(
|
|||||||
private val applicationFormMapper: ApplicationFormMapper,
|
private val applicationFormMapper: ApplicationFormMapper,
|
||||||
private val userRepository: UserRepository,
|
private val userRepository: UserRepository,
|
||||||
) : ApplicationRunner {
|
) : ApplicationRunner {
|
||||||
private val yamlMapper = ObjectMapper(YAMLFactory()).findAndRegisterModules()
|
private val yamlMapper =
|
||||||
|
ObjectMapper(
|
||||||
|
YAMLFactory(),
|
||||||
|
).findAndRegisterModules().registerModule(visibilityConditionModule())
|
||||||
|
|
||||||
override fun run(args: ApplicationArguments) {
|
override fun run(args: ApplicationArguments) {
|
||||||
seedInitialTemplateIfMissing()
|
seedInitialTemplateIfMissing()
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ spring:
|
|||||||
database-platform: org.hibernate.dialect.PostgreSQLDialect
|
database-platform: org.hibernate.dialect.PostgreSQLDialect
|
||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: create
|
ddl-auto: create
|
||||||
show-sql: true
|
show-sql: false
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
format_sql: true
|
format_sql: true
|
||||||
|
|||||||
@@ -68,12 +68,13 @@ create table form_element_options
|
|||||||
form_element_id uuid not null,
|
form_element_id uuid not null,
|
||||||
col_config_filter_expected_val varchar(255),
|
col_config_filter_expected_val varchar(255),
|
||||||
col_config_filter_operator varchar(255) check (col_config_filter_operator in
|
col_config_filter_operator varchar(255) check (col_config_filter_operator in
|
||||||
('EQUALS', 'NOT_EQUALS', 'IS_EMPTY',
|
('EQUALS', 'NOT_EQUALS', 'IS_EMPTY', 'IS_NOT_EMPTY',
|
||||||
'IS_NOT_EMPTY')),
|
'CONTAINS', 'NOT_CONTAINS')),
|
||||||
col_config_source_table_ref varchar(255),
|
col_config_source_table_ref varchar(255),
|
||||||
label varchar(255) not null,
|
label varchar(255) not null,
|
||||||
option_value TEXT not null,
|
option_value TEXT not null,
|
||||||
row_constraint_table_reference varchar(255)
|
row_constraint_table_reference varchar(255),
|
||||||
|
visibility_conditions jsonb
|
||||||
);
|
);
|
||||||
|
|
||||||
create table form_element
|
create table form_element
|
||||||
@@ -89,9 +90,11 @@ create table form_element
|
|||||||
reference varchar(255),
|
reference varchar(255),
|
||||||
row_preset_filter_expected_val varchar(255),
|
row_preset_filter_expected_val varchar(255),
|
||||||
row_preset_filter_operator varchar(255) check (row_preset_filter_operator in
|
row_preset_filter_operator varchar(255) check (row_preset_filter_operator in
|
||||||
('EQUALS', 'NOT_EQUALS', 'IS_EMPTY', 'IS_NOT_EMPTY')),
|
('EQUALS', 'NOT_EQUALS', 'IS_EMPTY', 'IS_NOT_EMPTY', 'CONTAINS',
|
||||||
|
'NOT_CONTAINS')),
|
||||||
row_preset_source_table_ref varchar(255),
|
row_preset_source_table_ref varchar(255),
|
||||||
title varchar(255),
|
title varchar(255),
|
||||||
|
visibility_conditions jsonb,
|
||||||
primary key (id)
|
primary key (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -141,7 +144,8 @@ create table section_spawn_triggers
|
|||||||
section_spawn_condition_type varchar(255) check (section_spawn_condition_type in ('SHOW', 'HIDE')),
|
section_spawn_condition_type varchar(255) check (section_spawn_condition_type in ('SHOW', 'HIDE')),
|
||||||
section_spawn_expected_value varchar(255),
|
section_spawn_expected_value varchar(255),
|
||||||
section_spawn_operator varchar(255) check (section_spawn_operator in
|
section_spawn_operator varchar(255) check (section_spawn_operator in
|
||||||
('EQUALS', 'NOT_EQUALS', 'IS_EMPTY', 'IS_NOT_EMPTY')),
|
('EQUALS', 'NOT_EQUALS', 'IS_EMPTY', 'IS_NOT_EMPTY', 'CONTAINS',
|
||||||
|
'NOT_CONTAINS')),
|
||||||
template_reference varchar(255)
|
template_reference varchar(255)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -169,16 +173,6 @@ create table uploaded_file
|
|||||||
primary key (id)
|
primary key (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create table visibility_conditions
|
|
||||||
(
|
|
||||||
form_element_id uuid not null,
|
|
||||||
form_element_condition_type varchar(255) check (form_element_condition_type in ('SHOW', 'HIDE')),
|
|
||||||
form_element_expected_value varchar(255),
|
|
||||||
form_element_operator varchar(255) check (form_element_operator in
|
|
||||||
('EQUALS', 'NOT_EQUALS', 'IS_EMPTY', 'IS_NOT_EMPTY')),
|
|
||||||
source_form_element_reference varchar(255)
|
|
||||||
);
|
|
||||||
|
|
||||||
alter table if exists application_form
|
alter table if exists application_form
|
||||||
add constraint FKhtad5onoy2jknhtyfmx6cvvey
|
add constraint FKhtad5onoy2jknhtyfmx6cvvey
|
||||||
foreign key (created_by_id)
|
foreign key (created_by_id)
|
||||||
@@ -259,8 +253,3 @@ alter table if exists uploaded_file
|
|||||||
add constraint FKtg323a9339lx0do79gu4eftao
|
add constraint FKtg323a9339lx0do79gu4eftao
|
||||||
foreign key (uploaded_by_id)
|
foreign key (uploaded_by_id)
|
||||||
references app_user;
|
references app_user;
|
||||||
|
|
||||||
alter table if exists visibility_conditions
|
|
||||||
add constraint FK5xuf7bd179ogpq5a1m3g8q7jb
|
|
||||||
foreign key (form_element_id)
|
|
||||||
references form_element;
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -78,6 +78,7 @@
|
|||||||
import type { FormElementDto, FormOptionDto, TableRowPresetDto } from '~~/.api-client'
|
import type { FormElementDto, FormOptionDto, TableRowPresetDto } from '~~/.api-client'
|
||||||
import type { TableColumn } from '@nuxt/ui'
|
import type { TableColumn } from '@nuxt/ui'
|
||||||
import { useTableCrossReferences } from '~/composables/useTableCrossReferences'
|
import { useTableCrossReferences } from '~/composables/useTableCrossReferences'
|
||||||
|
import { useFormElementVisibility } from '~/composables/useFormElementVisibility'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
formOptions: FormOptionDto[]
|
formOptions: FormOptionDto[]
|
||||||
@@ -86,6 +87,8 @@ const props = defineProps<{
|
|||||||
tableRowPreset?: TableRowPresetDto
|
tableRowPreset?: TableRowPresetDto
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const { isFormOptionVisible } = useFormElementVisibility()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:formOptions', value: FormOptionDto[]): void
|
(e: 'update:formOptions', value: FormOptionDto[]): void
|
||||||
}>()
|
}>()
|
||||||
@@ -131,16 +134,33 @@ interface DataColumn {
|
|||||||
colIndex: number
|
colIndex: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter columns based on visibility conditions
|
||||||
|
interface VisibleColumn {
|
||||||
|
option: FormOptionDto
|
||||||
|
originalIndex: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const visibleColumns = computed<VisibleColumn[]>(() => {
|
||||||
|
return props.formOptions
|
||||||
|
.map((option, index) => ({ option, originalIndex: index }))
|
||||||
|
.filter(({ option }) => {
|
||||||
|
if (!option.visibilityConditions || !props.allFormElements) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return isFormOptionVisible(option.visibilityConditions, props.allFormElements)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const dataColumns = computed<DataColumn[]>(() =>
|
const dataColumns = computed<DataColumn[]>(() =>
|
||||||
props.formOptions.map((_, index) => ({
|
visibleColumns.value.map(({ originalIndex }) => ({
|
||||||
key: `col_${index}`,
|
key: `col_${originalIndex}`,
|
||||||
colIndex: index
|
colIndex: originalIndex
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
const tableColumns = computed<TableColumn<TableRowData>[]>(() => {
|
const tableColumns = computed<TableColumn<TableRowData>[]>(() => {
|
||||||
const columns: TableColumn<TableRowData>[] = props.formOptions.map((option, index) => ({
|
const columns: TableColumn<TableRowData>[] = visibleColumns.value.map(({ option, originalIndex }) => ({
|
||||||
accessorKey: `col_${index}`,
|
accessorKey: `col_${originalIndex}`,
|
||||||
header: option.label || ''
|
header: option.label || ''
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import type { FormElementDto, FormElementVisibilityCondition, VisibilityConditionOperator } from '~~/.api-client'
|
import type { FormElementDto, VisibilityConditionGroup, VisibilityConditionNode } from '~~/.api-client'
|
||||||
import {
|
import {
|
||||||
VisibilityConditionOperator as VCOperator,
|
|
||||||
VisibilityConditionType as VCType,
|
VisibilityConditionType as VCType,
|
||||||
|
VisibilityConditionNodeNodeTypeEnum as VCNodeTypeEnum,
|
||||||
|
VisibilityConditionGroupOperatorEnum as VCGroupOperatorEnum,
|
||||||
|
VisibilityConditionNodeGroupOperatorEnum as VCNodeGroupOperatorEnum,
|
||||||
|
VisibilityConditionOperator as VCOperator,
|
||||||
FormElementType
|
FormElementType
|
||||||
} from '~~/.api-client'
|
} from '~~/.api-client'
|
||||||
|
|
||||||
export function useFormElementVisibility() {
|
export function useFormElementVisibility() {
|
||||||
/**
|
|
||||||
* Evaluates visibility for all form elements based on their visibility conditions.
|
|
||||||
* Returns a map of element key (id or reference) to visibility status.
|
|
||||||
*/
|
|
||||||
function evaluateFormElementVisibility(allFormElements: FormElementDto[]): Map<string, boolean> {
|
function evaluateFormElementVisibility(allFormElements: FormElementDto[]): Map<string, boolean> {
|
||||||
const formElementsByRef = buildFormElementsMap(allFormElements)
|
const formElementsByRef = buildFormElementsMap(allFormElements)
|
||||||
const visibilityMap = new Map<string, boolean>()
|
const visibilityMap = new Map<string, boolean>()
|
||||||
@@ -25,6 +24,22 @@ export function useFormElementVisibility() {
|
|||||||
return visibilityMap
|
return visibilityMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates visibility conditions for a FormOption (e.g., table column).
|
||||||
|
* Unlike evaluateFormElementVisibility which works on FormElements,
|
||||||
|
* this evaluates standalone condition groups for options/columns.
|
||||||
|
*/
|
||||||
|
function isFormOptionVisible(
|
||||||
|
conditions: VisibilityConditionGroup | undefined,
|
||||||
|
allFormElements: FormElementDto[]
|
||||||
|
): boolean {
|
||||||
|
if (!conditions || !conditions.conditions || conditions.conditions.length === 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const formElementsByRef = buildFormElementsMap(allFormElements)
|
||||||
|
return evaluateGroup(conditions, formElementsByRef)
|
||||||
|
}
|
||||||
|
|
||||||
function buildFormElementsMap(formElements: FormElementDto[]): Map<string, FormElementDto> {
|
function buildFormElementsMap(formElements: FormElementDto[]): Map<string, FormElementDto> {
|
||||||
const map = new Map<string, FormElementDto>()
|
const map = new Map<string, FormElementDto>()
|
||||||
formElements.forEach((element) => {
|
formElements.forEach((element) => {
|
||||||
@@ -35,96 +50,104 @@ export function useFormElementVisibility() {
|
|||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Evaluates if an element is visible based on its visibility conditions.
|
|
||||||
* Multiple conditions use AND logic - all conditions must be met for the element to be visible.
|
|
||||||
*/
|
|
||||||
function isElementVisible(element: FormElementDto, formElementsByRef: Map<string, FormElementDto>): boolean {
|
function isElementVisible(element: FormElementDto, formElementsByRef: Map<string, FormElementDto>): boolean {
|
||||||
const conditions = element.visibilityConditions
|
const group = element.visibilityConditions
|
||||||
if (!conditions || conditions.length === 0) {
|
if (!group || !group.conditions || group.conditions.length === 0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// All conditions must be met (AND logic)
|
return evaluateGroup(group, formElementsByRef)
|
||||||
return conditions.every((condition) => evaluateSingleCondition(condition, formElementsByRef))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function evaluateGroup(group: VisibilityConditionGroup, formElementsByRef: Map<string, FormElementDto>): boolean {
|
||||||
* Evaluates a single visibility condition against the form state.
|
if (!group.conditions || group.conditions.length === 0) {
|
||||||
*/
|
return true
|
||||||
function evaluateSingleCondition(
|
}
|
||||||
condition: FormElementVisibilityCondition,
|
const results = group.conditions.map((c) => evaluateNode(c, formElementsByRef))
|
||||||
|
return group.operator === VCGroupOperatorEnum.And ? results.every(Boolean) : results.some(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
function evaluateNode(node: VisibilityConditionNode, formElementsByRef: Map<string, FormElementDto>): boolean {
|
||||||
|
if (node.nodeType === VCNodeTypeEnum.Group) {
|
||||||
|
return evaluateNodeGroup(node, formElementsByRef)
|
||||||
|
}
|
||||||
|
return evaluateLeafCondition(node, formElementsByRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
function evaluateNodeGroup(node: VisibilityConditionNode, formElementsByRef: Map<string, FormElementDto>): boolean {
|
||||||
|
if (!node.conditions || node.conditions.length === 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const results = node.conditions.map((c) => evaluateNode(c, formElementsByRef))
|
||||||
|
return node.groupOperator === VCNodeGroupOperatorEnum.And ? results.every(Boolean) : results.some(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
function evaluateLeafCondition(
|
||||||
|
leaf: VisibilityConditionNode,
|
||||||
formElementsByRef: Map<string, FormElementDto>
|
formElementsByRef: Map<string, FormElementDto>
|
||||||
): boolean {
|
): boolean {
|
||||||
const sourceElement = formElementsByRef.get(condition.sourceFormElementReference)
|
if (!leaf.sourceFormElementReference) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const sourceElement = formElementsByRef.get(leaf.sourceFormElementReference)
|
||||||
if (!sourceElement) {
|
if (!sourceElement) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special handling for CHECKBOX with multiple options
|
// Special handling for CHECKBOX with multiple options
|
||||||
if (sourceElement.type === FormElementType.Checkbox && sourceElement.options.length > 1) {
|
if (sourceElement.type === FormElementType.Checkbox && sourceElement.options.length > 1) {
|
||||||
const operator = condition.formElementOperator || VCOperator.Equals
|
const operator = leaf.formElementOperator || VCOperator.Equals
|
||||||
const conditionMet = evaluateCheckboxCondition(sourceElement, condition.formElementExpectedValue || '', operator)
|
const conditionMet = evaluateCheckboxCondition(sourceElement, leaf.formElementExpectedValue || '', operator)
|
||||||
return condition.formElementConditionType === VCType.Show ? conditionMet : !conditionMet
|
return leaf.formElementConditionType === VCType.Hide ? !conditionMet : conditionMet
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceValue = getFormElementValue(sourceElement)
|
const sourceValue = getFormElementValue(sourceElement)
|
||||||
const operator = condition.formElementOperator || VCOperator.Equals
|
const operator = leaf.formElementOperator || VCOperator.Equals
|
||||||
const conditionMet = evaluateCondition(sourceValue, condition.formElementExpectedValue || '', operator)
|
const conditionMet = evaluateCondition(sourceValue, leaf.formElementExpectedValue || '', operator)
|
||||||
|
|
||||||
return condition.formElementConditionType === VCType.Show ? conditionMet : !conditionMet
|
return leaf.formElementConditionType === VCType.Hide ? !conditionMet : conditionMet
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFormElementValue(element: FormElementDto): string {
|
function getFormElementValue(element: FormElementDto): string {
|
||||||
// For CHECKBOX with a single option, return the value directly
|
|
||||||
if (element.type === FormElementType.Checkbox && element.options.length === 1) {
|
if (element.type === FormElementType.Checkbox && element.options.length === 1) {
|
||||||
return element.options[0]?.value || ''
|
return element.options[0]?.value || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// For other element types (RADIOBUTTON, SELECT, etc.), find the selected option and return its label
|
|
||||||
const selectedOption = element.options.find((option) => option.value === 'true')
|
const selectedOption = element.options.find((option) => option.value === 'true')
|
||||||
return selectedOption?.label || ''
|
return selectedOption?.label || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function evaluateCheckboxCondition(element: FormElementDto, expectedValue: string, operator: string): boolean {
|
||||||
* Evaluates visibility condition for CHECKBOX with multiple options.
|
|
||||||
* Checks if ANY of the selected checkboxes matches the expected value.
|
|
||||||
*/
|
|
||||||
function evaluateCheckboxCondition(
|
|
||||||
element: FormElementDto,
|
|
||||||
expectedValue: string,
|
|
||||||
operator: VisibilityConditionOperator
|
|
||||||
): boolean {
|
|
||||||
const selectedLabels = element.options.filter((option) => option.value === 'true').map((option) => option.label)
|
const selectedLabels = element.options.filter((option) => option.value === 'true').map((option) => option.label)
|
||||||
|
|
||||||
switch (operator) {
|
switch (operator) {
|
||||||
case VCOperator.Equals:
|
case VCOperator.Equals:
|
||||||
// Check if any selected checkbox label matches the expected value
|
|
||||||
return selectedLabels.some((label) => label.toLowerCase() === expectedValue.toLowerCase())
|
return selectedLabels.some((label) => label.toLowerCase() === expectedValue.toLowerCase())
|
||||||
case VCOperator.NotEquals:
|
case VCOperator.NotEquals:
|
||||||
// Check if no selected checkbox label matches the expected value
|
return !selectedLabels.some((label) => label.toLowerCase() === expectedValue.toLowerCase())
|
||||||
|
case VCOperator.Contains:
|
||||||
|
return selectedLabels.some((label) => label.toLowerCase() === expectedValue.toLowerCase())
|
||||||
|
case VCOperator.NotContains:
|
||||||
return !selectedLabels.some((label) => label.toLowerCase() === expectedValue.toLowerCase())
|
return !selectedLabels.some((label) => label.toLowerCase() === expectedValue.toLowerCase())
|
||||||
case VCOperator.IsEmpty:
|
case VCOperator.IsEmpty:
|
||||||
// Check if no checkboxes are selected
|
|
||||||
return selectedLabels.length === 0
|
return selectedLabels.length === 0
|
||||||
case VCOperator.IsNotEmpty:
|
case VCOperator.IsNotEmpty:
|
||||||
// Check if at least one checkbox is selected
|
|
||||||
return selectedLabels.length > 0
|
return selectedLabels.length > 0
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function evaluateCondition(
|
function evaluateCondition(actualValue: string, expectedValue: string, operator: string): boolean {
|
||||||
actualValue: string,
|
|
||||||
expectedValue: string,
|
|
||||||
operator: VisibilityConditionOperator
|
|
||||||
): boolean {
|
|
||||||
switch (operator) {
|
switch (operator) {
|
||||||
case VCOperator.Equals:
|
case VCOperator.Equals:
|
||||||
return actualValue.toLowerCase() === expectedValue.toLowerCase()
|
return actualValue.toLowerCase() === expectedValue.toLowerCase()
|
||||||
case VCOperator.NotEquals:
|
case VCOperator.NotEquals:
|
||||||
return actualValue.toLowerCase() !== expectedValue.toLowerCase()
|
return actualValue.toLowerCase() !== expectedValue.toLowerCase()
|
||||||
|
case VCOperator.Contains:
|
||||||
|
return actualValue.toLowerCase().includes(expectedValue.toLowerCase())
|
||||||
|
case VCOperator.NotContains:
|
||||||
|
return !actualValue.toLowerCase().includes(expectedValue.toLowerCase())
|
||||||
case VCOperator.IsEmpty:
|
case VCOperator.IsEmpty:
|
||||||
return actualValue === ''
|
return actualValue === ''
|
||||||
case VCOperator.IsNotEmpty:
|
case VCOperator.IsNotEmpty:
|
||||||
@@ -135,6 +158,7 @@ export function useFormElementVisibility() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
evaluateFormElementVisibility
|
evaluateFormElementVisibility,
|
||||||
|
isFormOptionVisible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user