import type { FormElementDto, FormOptionDto, TableColumnConfigDto, TableColumnFilterDto, TableRowPresetDto } from '~~/.api-client' import { VisibilityConditionOperator as VCOperator } from '~~/.api-client' export function useTableCrossReferences() { // Get available values for a column that references another table's column function getReferencedColumnValues( columnConfig: TableColumnConfigDto | undefined, allFormElements: FormElementDto[] ): string[] { if (!columnConfig?.sourceTableReference || columnConfig.sourceColumnIndex === undefined) { return [] } const sourceTable = findTableElement(columnConfig.sourceTableReference, allFormElements) if (!sourceTable) { return [] } const sourceColumn = sourceTable.options[columnConfig.sourceColumnIndex] if (!sourceColumn) { return [] } const columnValues = parseColumnValues(sourceColumn.value) // Apply filter if present if (columnConfig.filterCondition) { return filterColumnValues(columnValues, columnConfig.filterCondition, sourceTable) } return columnValues.filter((v) => v.trim() !== '') } // Get filtered values based on constraints from another table // Used for cases like "Permission-ID can only use permissions allowed for the selected role" function getConstrainedColumnValues( columnConfig: TableColumnConfigDto | undefined, currentRowData: Record, constraintTableReference: string, constraintKeyColumnIndex: number, constraintValueColumnIndex: number, allFormElements: FormElementDto[], currentRowKeyColumnIndex?: number ): string[] { if (!columnConfig?.sourceTableReference) { return [] } const constraintTable = findTableElement(constraintTableReference, allFormElements) if (!constraintTable) { // No constraint found, return all values from source table column return getReferencedColumnValues(columnConfig, allFormElements) } const lookupColumnIndex = currentRowKeyColumnIndex ?? constraintKeyColumnIndex const keyValue = currentRowData[`col_${lookupColumnIndex}`] if (!keyValue) { // No key value to look up, return all values from source table column return getReferencedColumnValues(columnConfig, allFormElements) } const allowedValuesRaw = getAllowedValuesFromConstraintTable( constraintTable, keyValue, constraintKeyColumnIndex, constraintValueColumnIndex ) const allowedValues = allowedValuesRaw.flatMap((v) => (typeof v === 'boolean' ? String(v) : v)) // If no allowed values found, fall back to all values from source table if (allowedValues.length === 0) { return getReferencedColumnValues(columnConfig, allFormElements) } return allowedValues } // Apply row presets from a source table based on filter conditions function applyRowPresets( tableRowPreset: TableRowPresetDto | undefined, targetOptions: FormOptionDto[], allFormElements: FormElementDto[] ): FormOptionDto[] { if (!tableRowPreset?.sourceTableReference) { return targetOptions } const sourceTable = findTableElement(tableRowPreset.sourceTableReference, allFormElements) if (!sourceTable) { return targetOptions } // Get source table data const sourceData = parseTableData(sourceTable.options) // Filter rows based on filter condition const filteredRows = tableRowPreset.filterCondition ? filterTableRows(sourceData, tableRowPreset.filterCondition, sourceTable.options) : sourceData // Apply column mappings to create preset rows in target const columnMappings = tableRowPreset.columnMappings || [] const presetRowCount = filteredRows.length return targetOptions.map((option, targetColIndex) => { const mapping = columnMappings.find((m) => m.targetColumnIndex === targetColIndex) // For mapped columns, use values from source if (mapping && mapping.sourceColumnIndex !== undefined) { const sourceColIndex = mapping.sourceColumnIndex const presetValues = filteredRows.map((row) => String(row[sourceColIndex] ?? '')) return { ...option, value: JSON.stringify(presetValues) } } // For non-mapped columns, ensure we have the right number of rows const existingValues = parseColumnValues(option.value) const isCheckboxColumn = option.columnConfig?.isCheckbox === true // Pad or trim to match preset row count const adjustedValues: (string | boolean)[] = [] for (let i = 0; i < presetRowCount; i++) { if (i < existingValues.length && existingValues[i] !== undefined) { adjustedValues.push(existingValues[i]!) } else { // Initialize new rows with appropriate default adjustedValues.push(isCheckboxColumn ? false : '') } } return { ...option, value: JSON.stringify(adjustedValues) } }) } function findTableElement(reference: string, allFormElements: FormElementDto[]): FormElementDto | undefined { return allFormElements.find((el) => el.reference === reference && el.type === 'TABLE') } function parseColumnValues(jsonValue: string | undefined): string[] { if (!jsonValue) return [] try { const parsed = JSON.parse(jsonValue) return Array.isArray(parsed) ? parsed : [] } catch { return [] } } function parseTableData(options: FormOptionDto[]): (string | boolean)[][] { const columnData = options.map((opt) => parseColumnValuesWithTypes(opt.value)) const rowCount = Math.max(...columnData.map((col) => col.length), 0) const rows: (string | boolean)[][] = [] for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) { const row = columnData.map((col) => col[rowIndex] ?? '') rows.push(row) } return rows } function parseColumnValuesWithTypes(jsonValue: string | undefined): (string | boolean)[] { if (!jsonValue) return [] try { const parsed = JSON.parse(jsonValue) return Array.isArray(parsed) ? parsed : [] } catch { return [] } } function filterColumnValues( values: string[], filterCondition: TableColumnFilterDto, sourceTable: FormElementDto ): string[] { if (filterCondition.sourceColumnIndex === undefined) { return values } const filterColumn = sourceTable.options[filterCondition.sourceColumnIndex] if (!filterColumn) { return values } const filterColumnValues = parseColumnValues(filterColumn.value) return values.filter((_, index) => { const filterValue = filterColumnValues[index] || '' return evaluateFilterCondition(filterValue, filterCondition) }) } function filterTableRows( rows: (string | boolean)[][], filterCondition: TableColumnFilterDto, _options: FormOptionDto[] ): (string | boolean)[][] { if (filterCondition.sourceColumnIndex === undefined) { return rows } return rows.filter((row) => { const filterValue = row[filterCondition.sourceColumnIndex!] ?? '' return evaluateFilterCondition(filterValue, filterCondition) }) } function evaluateFilterCondition(actualValue: string | boolean, filterCondition: TableColumnFilterDto): boolean { const expectedValue = filterCondition.expectedValue || '' const operator = filterCondition.operator || VCOperator.Equals // Handle boolean values (from checkbox columns) const normalizedActual = typeof actualValue === 'boolean' ? String(actualValue) : actualValue switch (operator) { case VCOperator.Equals: return normalizedActual.toLowerCase() === expectedValue.toLowerCase() case VCOperator.NotEquals: return normalizedActual.toLowerCase() !== expectedValue.toLowerCase() case VCOperator.IsEmpty: return normalizedActual.trim() === '' case VCOperator.IsNotEmpty: return normalizedActual.trim() !== '' default: return true } } function getAllowedValuesFromConstraintTable( constraintTable: FormElementDto, keyValue: string, keyColumnIndex: number, valueColumnIndex: number ): (string | boolean | string[])[] { const tableData = parseTableData(constraintTable.options) const allowedValues: (string | boolean | string[])[] = [] tableData.forEach((row) => { const keyCell = row[keyColumnIndex] const keyCellStr = Array.isArray(keyCell) ? keyCell[0] : typeof keyCell === 'boolean' ? String(keyCell) : keyCell if (keyCellStr?.toLowerCase() === keyValue.toLowerCase()) { const value = row[valueColumnIndex] if (value !== undefined && !allowedValues.includes(value)) { allowedValues.push(value) } } }) return allowedValues } return { getReferencedColumnValues, getConstrainedColumnValues, applyRowPresets } }