'use client' import React from 'react' import type { RuleDefinition, ConditionGroup, ConditionNode, RuleAction, FieldCatalogEntry } from '@/types' interface Props { definition: RuleDefinition fieldCatalog: FieldCatalogEntry[] } const OPERATOR_LABELS: Record = { EQUALS: '=', NOT_EQUALS: '≠', GREATER_THAN: '>', GREATER_THAN_OR_EQUAL: '≥', LESS_THAN: '<', LESS_THAN_OR_EQUAL: '≤', BETWEEN: 'between', IN: 'in', NOT_IN: 'not in', CONTAINS: 'contains', IS_NULL: 'is empty', IS_NOT_NULL: 'is not empty', IS_TRUE: 'is true', IS_FALSE: 'is false', REGEX: 'matches', } const ACTION_COLORS: Record = { SET_DECISION: 'bg-red-100 text-red-800', SET_RISK: 'bg-yellow-100 text-yellow-800', SET_TRAFFIC_LIGHT: 'bg-green-100 text-green-800', ADD_DEVIATION: 'bg-orange-100 text-orange-800', SET_FIELD: 'bg-blue-100 text-blue-800', ADD_TAG: 'bg-purple-100 text-purple-800', SET_SCORE: 'bg-teal-100 text-teal-800', } export function RulePreview({ definition, fieldCatalog }: Props) { const getFieldName = (path: string) => fieldCatalog.find((f) => f.fieldPath === path)?.displayName ?? path const renderConditionGroup = (group: ConditionGroup, depth = 0): React.ReactNode => { const indent = depth * 24 return (
{group.rules.map((node, idx) => (
{idx > 0 && (
{group.operator}
)} {renderNode(node, depth)}
))}
) } const renderNode = (node: ConditionNode, depth: number): React.ReactNode => { if (node.isGroup && node.group) { return (
{node.group.operator} group: {renderConditionGroup(node.group, depth + 1)}
) } return (
{getFieldName(node.field ?? '')} {OPERATOR_LABELS[node.operator ?? ''] ?? node.operator} {node.value !== undefined && node.value !== '' && ( {node.operator === 'BETWEEN' ? `${node.value} and ${node.value2}` : String(node.value)} )}
) } const renderAction = (action: RuleAction): string => { const colorClass = ACTION_COLORS[action.type] ?? 'bg-gray-100 text-gray-800' switch (action.type) { case 'SET_DECISION': return `Decision → ${action.value}` case 'SET_RISK': return `Risk Level → ${action.value}` case 'SET_TRAFFIC_LIGHT': return `Traffic Light → ${action.value}` case 'ADD_DEVIATION': return `Deviation: ${action.value} (${action.parameters?.severity ?? 'MEDIUM'})` case 'SET_FIELD': return `Set ${action.field} = ${action.value}` case 'ADD_TAG': return `Tag: ${action.value}` case 'SET_SCORE': return `Risk Score ${action.value?.startsWith?.('+') || action.value?.startsWith?.('-') ? 'adjust' : '='} ${action.value}` default: return `${action.type}: ${action.value}` } } const hasConditions = definition.conditions.rules.length > 0 const hasActions = definition.actions.length > 0 return (

Rule Preview

Human-readable rule representation

{/* IF block */}
IF all/any of these conditions match:
{hasConditions ? ( renderConditionGroup(definition.conditions) ) : (

No conditions defined

)}
{/* Divider */}
{/* THEN block */}
THEN execute these actions:
{hasActions ? (
{definition.actions.map((action, idx) => (
{idx + 1}. {renderAction(action)}
))}
) : (

No actions defined

)}
{/* Metadata */} {definition.metadata && ( <>
Order: {definition.metadata.executionOrder ?? 0} Error: {definition.metadata.errorHandling ?? 'SKIP'} {definition.metadata.stopOnMatch && ( ⚡ Stop on Match )}
)}
) }