feat(fullstack): Add per-row visibility, add read-only cells, update seeds

This commit is contained in:
2026-03-01 09:00:07 +01:00
parent f72923f945
commit fea2ca8a3b
10 changed files with 283 additions and 106 deletions

View File

@@ -35,6 +35,18 @@ class FormOption(
AttributeOverride(name = "filterCondition.operator", column = Column(name = "col_config_filter_operator")),
AttributeOverride(name = "isReadOnly", column = Column(name = "col_config_is_read_only")),
AttributeOverride(name = "isCheckbox", column = Column(name = "col_config_is_checkbox")),
AttributeOverride(
name = "readOnlyDefaultValue",
column = Column(name = "col_config_read_only_default_value"),
),
AttributeOverride(
name = "readOnlyConditions",
column = Column(name = "col_config_read_only_conditions", columnDefinition = "jsonb"),
),
AttributeOverride(
name = "rowVisibilityCondition",
column = Column(name = "col_config_row_visibility_condition", columnDefinition = "jsonb"),
),
)
var columnConfig: TableColumnConfig? = null,
@JdbcTypeCode(SqlTypes.JSON)

View File

@@ -5,6 +5,8 @@ import jakarta.persistence.AttributeOverrides
import jakarta.persistence.Column
import jakarta.persistence.Embeddable
import jakarta.persistence.Embedded
import org.hibernate.annotations.JdbcTypeCode
import org.hibernate.type.SqlTypes
@Embeddable
data class TableColumnConfig(
@@ -29,4 +31,11 @@ data class TableColumnConfig(
val isReadOnly: Boolean = false,
val isMultipleAllowed: Boolean = false,
val isCheckbox: Boolean = false,
@JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "jsonb")
val readOnlyConditions: GroupCondition? = null,
val readOnlyDefaultValue: String? = null,
@JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "jsonb")
val rowVisibilityCondition: RowVisibilityCondition? = null,
)

View File

@@ -1,12 +1,15 @@
package com.betriebsratkanzlei.legalconsenthub.form_element
import com.betriebsratkanzlei.legalconsenthub_api.model.TableColumnConfigDto
import com.betriebsratkanzlei.legalconsenthub_api.model.TableRowVisibilityConditionDto
import org.springframework.stereotype.Component
import com.betriebsratkanzlei.legalconsenthub_api.model.VisibilityConditionOperator as VisibilityConditionOperatorDto
@Component
class TableColumnConfigMapper(
private val filterMapper: TableColumnFilterMapper,
private val rowConstraintMapper: TableRowConstraintMapper,
private val visibilityConditionMapper: FormElementVisibilityConditionMapper,
) {
fun toTableColumnConfigDto(config: TableColumnConfig): TableColumnConfigDto =
TableColumnConfigDto(
@@ -17,6 +20,9 @@ class TableColumnConfigMapper(
isReadOnly = config.isReadOnly,
isMultipleAllowed = config.isMultipleAllowed,
isCheckbox = config.isCheckbox,
readOnlyConditions = config.readOnlyConditions?.let { visibilityConditionMapper.toGroupConditionDto(it) },
readOnlyDefaultValue = config.readOnlyDefaultValue,
rowVisibilityCondition = config.rowVisibilityCondition?.let { toRowVisibilityConditionDto(it) },
)
fun toTableColumnConfig(dto: TableColumnConfigDto): TableColumnConfig =
@@ -28,5 +34,42 @@ class TableColumnConfigMapper(
isReadOnly = dto.isReadOnly ?: false,
isMultipleAllowed = dto.isMultipleAllowed ?: false,
isCheckbox = dto.isCheckbox ?: false,
readOnlyConditions = dto.readOnlyConditions?.let { visibilityConditionMapper.toGroupCondition(it) },
readOnlyDefaultValue = dto.readOnlyDefaultValue,
rowVisibilityCondition = dto.rowVisibilityCondition?.let { toRowVisibilityCondition(it) },
)
private fun toRowVisibilityConditionDto(entity: RowVisibilityCondition): TableRowVisibilityConditionDto =
TableRowVisibilityConditionDto(
sourceColumnIndex = entity.sourceColumnIndex,
expectedValues = entity.expectedValues,
operator = entity.operator.toDto(),
)
private fun toRowVisibilityCondition(dto: TableRowVisibilityConditionDto): RowVisibilityCondition =
RowVisibilityCondition(
sourceColumnIndex = dto.sourceColumnIndex ?: 0,
expectedValues = dto.expectedValues ?: emptyList(),
operator = dto.operator?.toEntity() ?: VisibilityConditionOperator.CONTAINS,
)
private fun VisibilityConditionOperator.toDto(): VisibilityConditionOperatorDto =
when (this) {
VisibilityConditionOperator.EQUALS -> VisibilityConditionOperatorDto.EQUALS
VisibilityConditionOperator.NOT_EQUALS -> VisibilityConditionOperatorDto.NOT_EQUALS
VisibilityConditionOperator.IS_EMPTY -> VisibilityConditionOperatorDto.IS_EMPTY
VisibilityConditionOperator.IS_NOT_EMPTY -> VisibilityConditionOperatorDto.IS_NOT_EMPTY
VisibilityConditionOperator.CONTAINS -> VisibilityConditionOperatorDto.CONTAINS
VisibilityConditionOperator.NOT_CONTAINS -> VisibilityConditionOperatorDto.NOT_CONTAINS
}
private fun VisibilityConditionOperatorDto.toEntity(): VisibilityConditionOperator =
when (this) {
VisibilityConditionOperatorDto.EQUALS -> VisibilityConditionOperator.EQUALS
VisibilityConditionOperatorDto.NOT_EQUALS -> VisibilityConditionOperator.NOT_EQUALS
VisibilityConditionOperatorDto.IS_EMPTY -> VisibilityConditionOperator.IS_EMPTY
VisibilityConditionOperatorDto.IS_NOT_EMPTY -> VisibilityConditionOperator.IS_NOT_EMPTY
VisibilityConditionOperatorDto.CONTAINS -> VisibilityConditionOperator.CONTAINS
VisibilityConditionOperatorDto.NOT_CONTAINS -> VisibilityConditionOperator.NOT_CONTAINS
}
}

View File

@@ -25,6 +25,12 @@ data class LeafCondition(
val formElementOperator: VisibilityConditionOperator = VisibilityConditionOperator.EQUALS,
) : VisibilityConditionNode
data class RowVisibilityCondition(
val sourceColumnIndex: Int,
val expectedValues: List<String>,
val operator: VisibilityConditionOperator = VisibilityConditionOperator.CONTAINS,
)
enum class GroupOperator {
AND,
OR,

View File

@@ -70,10 +70,13 @@ create table form_element_options
col_config_filter_operator varchar(255) check (col_config_filter_operator in
('EQUALS', 'NOT_EQUALS', 'IS_EMPTY', 'IS_NOT_EMPTY',
'CONTAINS', 'NOT_CONTAINS')),
col_config_read_only_default_value varchar(255),
col_config_source_table_ref varchar(255),
label varchar(255) not null,
option_value TEXT not null,
row_constraint_table_reference varchar(255),
col_config_read_only_conditions jsonb,
col_config_row_visibility_condition jsonb,
visibility_conditions jsonb
);

View File

@@ -251,6 +251,12 @@ formElementSubSections:
employeeDataCategory: SENSITIVE
columnConfig:
isCheckbox: true
rowVisibilityCondition:
sourceColumnIndex: 5
expectedValues:
- "Aggregiert (Team)"
- "Aggregiert (Abteilung)"
operator: CONTAINS
- value: '["N/A", "Min. 5 Personen im Vergleich", "Min. 10 Personen pro Auswertung"]'
label: Mindestgruppe/Schwelle
processingPurpose: DATA_ANALYSIS

View File

@@ -76,6 +76,29 @@ formElementSubSections:
label: Leistungs-/Verhaltenskontrolle
processingPurpose: DATA_ANALYSIS
employeeDataCategory: SENSITIVE
columnConfig:
readOnlyDefaultValue: "Nein"
readOnlyConditions:
operator: OR
conditions:
- nodeType: LEAF
formElementConditionType: SHOW
sourceFormElementReference: sens_sichtbarkeit
formElementExpectedValue: Für Administratoren
formElementOperator: EQUALS
- nodeType: GROUP
groupOperator: AND
conditions:
- nodeType: LEAF
formElementConditionType: SHOW
sourceFormElementReference: sens_sichtbarkeit
formElementExpectedValue: Für mehrere Rollen
formElementOperator: EQUALS
- nodeType: LEAF
formElementConditionType: SHOW
sourceFormElementReference: sens_auswertung
formElementExpectedValue: Keine
formElementOperator: EQUALS
- title: Rollen-Sichtbarkeit (Einfache Darstellung)
formElements:
- reference: rollen_sichtbarkeit_einfach_tabelle
@@ -405,6 +428,12 @@ formElementSubSections:
employeeDataCategory: SENSITIVE
columnConfig:
isCheckbox: true
rowVisibilityCondition:
sourceColumnIndex: 5
expectedValues:
- "Aggregiert (Team)"
- "Aggregiert (Abteilung)"
operator: CONTAINS
- value: '[]'
label: Mindestgruppe/Schwelle
processingPurpose: DATA_ANALYSIS