1+ import React from 'react' ;
2+ import { Typography , Box } from '@mui/material' ;
3+ import { VBox } from '@/ui/shared-components/LayoutBoxes' ;
4+ import theme from '@/theme' ;
5+ import {
6+ InConstraint ,
7+ MaxExclusiveConstraint ,
8+ MinInclusiveConstraint
9+ } from './RuleTypes' ;
10+
11+ const typeLabelMap = {
12+ NodeROOT : 'Requirements' ,
13+ NodeAND : 'AND' ,
14+ NodeOR : 'OR' ,
15+ } ;
16+
17+ function renderNode ( node , skipAndWrapper = false , parentField = '' ) {
18+ if ( ! node ) return null ;
19+ const t = node . constructor ?. name ;
20+
21+ if ( t === 'NodeROOT' ) {
22+ return renderNode ( node . children ?. [ 0 ] , true , '' ) ;
23+ }
24+
25+ if ( t === 'NodeDATAFIELD' ) {
26+ const rules = ( node . children || [ ] ) . filter (
27+ c => c . constructor ?. name === 'NodeRULE' &&
28+ c . type !== 'sh:MinCountConstraintComponent'
29+ ) ;
30+ if ( rules . length === 0 ) return null ;
31+
32+ return (
33+ < VBox
34+ gap = { 1 }
35+ sx = { {
36+ border : '1px solid' ,
37+ borderColor : 'black.light' ,
38+ padding : 2 ,
39+ borderRadius : theme . shape . borderRadius ,
40+ } }
41+ >
42+ < Typography variant = "body1" sx = { { fontWeight : 'bold' } } >
43+ { node . path }
44+ </ Typography >
45+ { rules . map ( ( rule , i ) => (
46+ < VBox key = { i } sx = { { pl : 2 } } >
47+ { renderNode ( rule , false , node . path ) }
48+ </ VBox >
49+ ) ) }
50+ </ VBox >
51+ ) ;
52+ }
53+
54+ if ( t === 'NodeNOT' ) {
55+ return null ;
56+ }
57+
58+ if ( t === 'NodeRULE' ) {
59+ if ( node . type === 'sh:MinCountConstraintComponent' ) return null ;
60+
61+ switch ( node . type ) {
62+ case 'sh:MinInclusiveConstraintComponent' :
63+ return < MinInclusiveConstraint field = { parentField } value = { node . value } /> ;
64+ case 'sh:MaxExclusiveConstraintComponent' :
65+ return < MaxExclusiveConstraint field = { parentField } value = { node . value } /> ;
66+ case 'sh:InConstraintComponent' :
67+ return < InConstraint field = { parentField } value = { node . value } /> ;
68+ default : {
69+ const valStr =
70+ typeof node . value === 'boolean'
71+ ? node . value . toString ( )
72+ : Array . isArray ( node . value )
73+ ? node . value . join ( ', ' )
74+ : node . value == null
75+ ? '<no value>'
76+ : node . value ;
77+ return (
78+ < Typography >
79+ { parentField } needs to satisfy { node . type . replace ( 'sh:' , '' ) } — { valStr }
80+ </ Typography >
81+ ) ;
82+ }
83+ }
84+ }
85+
86+ if ( t === 'NodeAND' || t === 'NodeOR' ) {
87+ const children = node . children || [ ] ;
88+
89+ const dataFields = children . filter ( c =>
90+ c . constructor ?. name === 'NodeDATAFIELD' &&
91+ ( c . children || [ ] ) . some (
92+ r => r . constructor ?. name === 'NodeRULE' &&
93+ r . type !== 'sh:MinCountConstraintComponent'
94+ )
95+ ) ;
96+ const andGroups = children . filter ( c => c . constructor ?. name === 'NodeAND' ) ;
97+ const orGroups = children . filter ( c => c . constructor ?. name === 'NodeOR' ) ;
98+
99+ if ( dataFields . length + andGroups . length + orGroups . length === 0 ) {
100+ return null ;
101+ }
102+
103+ const label = skipAndWrapper && t === 'NodeAND'
104+ ? null
105+ : typeLabelMap [ t ] ;
106+
107+ return (
108+ < VBox gap = { 2 } >
109+ { label && < Typography variant = "body1" > { label } </ Typography > }
110+
111+ { dataFields . map ( ( df , i ) => (
112+ < Box key = { `df-${ i } ` } sx = { { pl : 2 } } >
113+ { renderNode ( df , false , parentField ) }
114+ </ Box >
115+ ) ) }
116+
117+ { andGroups . map ( ( grp , i ) => (
118+ < Box key = { `and-${ i } ` } sx = { { pl : 2 } } >
119+ { renderNode ( grp , false , parentField ) }
120+ </ Box >
121+ ) ) }
122+
123+ { orGroups . map ( ( grp , i ) => (
124+ < Box key = { `or-${ i } ` } sx = { { pl : 2 } } >
125+ { renderNode ( grp , false , parentField ) }
126+ </ Box >
127+ ) ) }
128+ </ VBox >
129+ ) ;
130+ }
131+
132+ return null ;
133+ }
134+
135+ export default function RecursiveRulesTable ( { graphRoot } ) {
136+ if ( ! graphRoot ) return null ;
137+ return (
138+ < VBox
139+ sx = { {
140+ backgroundColor : 'white.main' ,
141+ padding : '32px' ,
142+ borderRadius : theme . shape . borderRadius ,
143+ } }
144+ >
145+ { renderNode ( graphRoot ) }
146+ </ VBox >
147+ ) ;
148+ }
0 commit comments