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 { const formElementsByRef = buildFormElementsMap(allFormElements) const visibilityMap = new Map() 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 { const map = new Map() formElements.forEach((element) => { if (element.reference) { map.set(element.reference, element) } }) return map } function isElementVisible(element: FormElementDto, formElementsByRef: Map): 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): 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): boolean { if (node.nodeType === VCNodeTypeEnum.Group) { return evaluateNodeGroup(node, formElementsByRef) } return evaluateLeafCondition(node, formElementsByRef) } function evaluateNodeGroup(node: VisibilityConditionNode, formElementsByRef: Map): 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 ): 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 } }