@@ -8,10 +8,12 @@ import { AttributeType, EntityType, GroupType } from "@/lib/Types";
88import { updateURL } from "@/lib/url-utils" ;
99import { copyToClipboard , generateGroupLink } from "@/lib/clipboard-utils" ;
1010import { useSnackbar } from "@/contexts/SnackbarContext" ;
11+ import { useEntityFilters } from "@/contexts/EntityFiltersContext" ;
1112import { Box , CircularProgress , debounce , Tooltip } from '@mui/material' ;
1213
1314interface IListProps {
1415 setCurrentIndex : ( index : number ) => void ;
16+ entityActiveTabs : Map < string , number > ;
1517}
1618
1719// Helper to highlight search matches
@@ -22,15 +24,26 @@ export function highlightMatch(text: string, search: string) {
2224 return < > { text . slice ( 0 , idx ) } < mark className = "bg-yellow-200 text-black px-0.5 rounded" > { text . slice ( idx , idx + search . length ) } </ mark > { text . slice ( idx + search . length ) } </ > ;
2325}
2426
25- export const List = ( { setCurrentIndex } : IListProps ) => {
27+ export const List = ( { setCurrentIndex, entityActiveTabs } : IListProps ) => {
2628 const dispatch = useDatamodelViewDispatch ( ) ;
2729 const { currentSection, loadingSection } = useDatamodelView ( ) ;
2830 const { groups, filtered, search } = useDatamodelData ( ) ;
31+ const { selectedSecurityRoles } = useEntityFilters ( ) ;
2932 const { showSnackbar } = useSnackbar ( ) ;
3033 const parentRef = useRef < HTMLDivElement | null > ( null ) ;
3134 // used to relocate section after search/filter
3235 const [ sectionVirtualItem , setSectionVirtualItem ] = useState < string | null > ( null ) ;
3336
37+ // Helper function to check if entity has access from selected security roles
38+ const hasSecurityRoleAccess = useCallback ( ( entity : EntityType ) : boolean => {
39+ if ( selectedSecurityRoles . length === 0 ) return false ;
40+
41+ return entity . SecurityRoles . some ( role =>
42+ selectedSecurityRoles . includes ( role . Name ) &&
43+ ( role . Read !== null && role . Read >= 0 ) // Has any read access
44+ ) ;
45+ } , [ selectedSecurityRoles ] ) ;
46+
3447 const handleCopyGroupLink = useCallback ( async ( groupName : string ) => {
3548 const link = generateGroupLink ( groupName ) ;
3649 const success = await copyToClipboard ( link ) ;
@@ -43,7 +56,7 @@ export const List = ({ setCurrentIndex }: IListProps) => {
4356
4457 // Only recalculate items when filtered or search changes
4558 const flatItems = useMemo ( ( ) => {
46- if ( filtered && filtered . length > 0 ) return filtered . filter ( item => item . type !== 'attribute' ) ;
59+ if ( filtered && filtered . length > 0 ) return filtered . filter ( item => item . type !== 'attribute' && item . type !== 'relationship' ) ;
4760
4861 const lowerSearch = search . trim ( ) . toLowerCase ( ) ;
4962 const items : Array <
@@ -54,6 +67,13 @@ export const List = ({ setCurrentIndex }: IListProps) => {
5467 // Filter entities in this group
5568 const filteredEntities = group . Entities . filter ( ( entity : EntityType ) => {
5669 const typedEntity = entity ;
70+
71+ // If security roles are selected, only show entities with access
72+ if ( selectedSecurityRoles . length > 0 ) {
73+ const hasAccess = hasSecurityRoleAccess ( typedEntity ) ;
74+ if ( ! hasAccess ) return false ;
75+ }
76+
5777 if ( ! lowerSearch ) return true ;
5878 // Match entity schema or display name
5979 const entityMatch = typedEntity . SchemaName . toLowerCase ( ) . includes ( lowerSearch ) ||
@@ -73,7 +93,7 @@ export const List = ({ setCurrentIndex }: IListProps) => {
7393 }
7494 }
7595 return items ;
76- } , [ filtered , search , groups ] ) ;
96+ } , [ filtered , search , groups , selectedSecurityRoles , hasSecurityRoleAccess ] ) ;
7797
7898 const debouncedOnChange = debounce ( ( instance , sync ) => {
7999 if ( ! sync ) {
@@ -192,6 +212,29 @@ export const List = ({ setCurrentIndex }: IListProps) => {
192212 }
193213 } , [ scrollToSection ] ) ;
194214
215+ const scrollToRelationship = useCallback ( ( sectionId : string , relSchema : string ) => {
216+ const relId = `rel-${ sectionId } -${ relSchema } ` ;
217+
218+ // Helper function to attempt scrolling to relationship with retries
219+ const attemptScroll = ( attemptsLeft : number ) => {
220+ const relationshipLocation = document . getElementById ( relId ) ;
221+
222+ if ( relationshipLocation ) {
223+ // Relationship found, scroll to it
224+ relationshipLocation . scrollIntoView ( { behavior : 'smooth' , block : 'center' } ) ;
225+ } else if ( attemptsLeft > 0 ) {
226+ // Relationship not rendered yet, retry after delay
227+ setTimeout ( ( ) => attemptScroll ( attemptsLeft - 1 ) , 100 ) ;
228+ } else {
229+ // Give up after all retries, just scroll to section
230+ scrollToSection ( sectionId ) ;
231+ }
232+ } ;
233+
234+ // Start attempting to scroll with 5 retries (total 500ms wait time)
235+ attemptScroll ( 5 ) ;
236+ } , [ scrollToSection ] ) ;
237+
195238 const scrollToGroup = useCallback ( ( groupName : string ) => {
196239 const groupIndex = flatItems . findIndex ( item =>
197240 item . type === 'group' && item . group . Name === groupName
@@ -214,9 +257,10 @@ export const List = ({ setCurrentIndex }: IListProps) => {
214257 useEffect ( ( ) => {
215258 dispatch ( { type : 'SET_SCROLL_TO_SECTION' , payload : scrollToSection } ) ;
216259 dispatch ( { type : 'SET_SCROLL_TO_ATTRIBUTE' , payload : scrollToAttribute } ) ;
260+ dispatch ( { type : 'SET_SCROLL_TO_RELATIONSHIP' , payload : scrollToRelationship } ) ;
217261 dispatch ( { type : 'SET_SCROLL_TO_GROUP' , payload : scrollToGroup } ) ;
218262 dispatch ( { type : 'SET_RESTORE_SECTION' , payload : restoreSection } ) ;
219- } , [ dispatch , scrollToSection , scrollToAttribute , scrollToGroup ] ) ;
263+ } , [ dispatch , scrollToSection , scrollToAttribute , scrollToRelationship , scrollToGroup , restoreSection ] ) ;
220264
221265 const smartScrollToIndex = useCallback ( ( index : number ) => {
222266 rowVirtualizer . scrollToIndex ( index , { align : 'start' } ) ;
@@ -301,6 +345,8 @@ export const List = ({ setCurrentIndex }: IListProps) => {
301345 entity = { item . entity }
302346 group = { item . group }
303347 search = { search }
348+ activeTab = { entityActiveTabs . get ( item . entity . SchemaName ) }
349+ highlightSecurityRole = { selectedSecurityRoles . length > 0 && hasSecurityRoleAccess ( item . entity ) }
304350 />
305351 </ div >
306352 ) }
0 commit comments