Files
gremiumhub/legalconsenthub/app/composables/useFormElementVisibility.ts

165 lines
6.3 KiB
TypeScript

import type { FormElementDto, VisibilityConditionGroup, VisibilityConditionNode } from '~~/.api-client'
import {
VisibilityConditionType as VCType,
VisibilityConditionNodeNodeTypeEnum as VCNodeTypeEnum,
VisibilityConditionGroupOperatorEnum as VCGroupOperatorEnum,
VisibilityConditionNodeGroupOperatorEnum as VCNodeGroupOperatorEnum,
VisibilityConditionOperator as VCOperator,
FormElementType
} from '~~/.api-client'
export function useFormElementVisibility() {
function evaluateFormElementVisibility(allFormElements: FormElementDto[]): Map<string, boolean> {
const formElementsByRef = buildFormElementsMap(allFormElements)
const visibilityMap = new Map<string, boolean>()
allFormElements.forEach((element) => {
const isVisible = isElementVisible(element, formElementsByRef)
const key = element.id || element.reference
if (key) {
visibilityMap.set(key, isVisible)
}
})
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> {
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>): boolean {
const group = element.visibilityConditions
if (!group || !group.conditions || group.conditions.length === 0) {
return true
}
return evaluateGroup(group, formElementsByRef)
}
function evaluateGroup(group: VisibilityConditionGroup, formElementsByRef: Map<string, FormElementDto>): boolean {
if (!group.conditions || group.conditions.length === 0) {
return true
}
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>
): boolean {
if (!leaf.sourceFormElementReference) {
return false
}
const sourceElement = formElementsByRef.get(leaf.sourceFormElementReference)
if (!sourceElement) {
return false
}
// Special handling for CHECKBOX with multiple options
if (sourceElement.type === FormElementType.Checkbox && sourceElement.options.length > 1) {
const operator = leaf.formElementOperator || VCOperator.Equals
const conditionMet = evaluateCheckboxCondition(sourceElement, leaf.formElementExpectedValue || '', operator)
return leaf.formElementConditionType === VCType.Hide ? !conditionMet : conditionMet
}
const sourceValue = getFormElementValue(sourceElement)
const operator = leaf.formElementOperator || VCOperator.Equals
const conditionMet = evaluateCondition(sourceValue, leaf.formElementExpectedValue || '', operator)
return leaf.formElementConditionType === VCType.Hide ? !conditionMet : conditionMet
}
function getFormElementValue(element: FormElementDto): string {
if (element.type === FormElementType.Checkbox && element.options.length === 1) {
return element.options[0]?.value || ''
}
const selectedOption = element.options.find((option) => option.value === 'true')
return selectedOption?.label || ''
}
function evaluateCheckboxCondition(element: FormElementDto, expectedValue: string, operator: string): boolean {
const selectedLabels = element.options.filter((option) => option.value === 'true').map((option) => option.label)
switch (operator) {
case VCOperator.Equals:
return selectedLabels.some((label) => label.toLowerCase() === expectedValue.toLowerCase())
case VCOperator.NotEquals:
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())
case VCOperator.IsEmpty:
return selectedLabels.length === 0
case VCOperator.IsNotEmpty:
return selectedLabels.length > 0
default:
return false
}
}
function evaluateCondition(actualValue: string, expectedValue: string, operator: string): boolean {
switch (operator) {
case VCOperator.Equals:
return actualValue.toLowerCase() === expectedValue.toLowerCase()
case VCOperator.NotEquals:
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:
return actualValue === ''
case VCOperator.IsNotEmpty:
return actualValue !== ''
default:
return false
}
}
return {
evaluateFormElementVisibility,
isFormOptionVisible
}
}