@@ -7,6 +7,7 @@ public final class HaystackServer: API, Sendable {
77    let  recordStore :  RecordStore 
88    let  historyStore :  HistoryStore 
99    let  watchStore :  WatchStore 
10+     let  navPath :  [ String ] 
1011
1112    let  onInvokeAction :  @Sendable   ( Haystack . Ref ,  String ,  [ String :  any  Haystack . Val ] )  async  throws  ->  Haystack . Grid 
1213    let  onEval :  @Sendable  ( String)   async throws  ->  Haystack . Grid 
@@ -15,6 +16,7 @@ public final class HaystackServer: API, Sendable {
1516        recordStore:  RecordStore , 
1617        historyStore:  HistoryStore , 
1718        watchStore:  WatchStore , 
19+         navPath:  [ String ]  =  [ " site " ,  " equip " ,  " point " ] , 
1820        onInvokeAction:  @escaping  @Sendable   ( Haystack . Ref ,  String ,  [ String :  any  Haystack . Val ] )  async  throws  ->  Haystack . Grid  =  {  _,  _,  _ in 
1921            GridBuilder ( ) . toGrid ( ) 
2022        } , 
@@ -25,6 +27,7 @@ public final class HaystackServer: API, Sendable {
2527        self . recordStore =  recordStore
2628        self . historyStore =  historyStore
2729        self . watchStore =  watchStore
30+         self . navPath =  navPath
2831        self . onInvokeAction =  onInvokeAction
2932        self . onEval =  onEval
3033    } 
@@ -106,9 +109,52 @@ public final class HaystackServer: API, Sendable {
106109        return  gb. toGrid ( ) 
107110    } 
108111
109-     public  func  nav( navId _:  Haystack . Ref ? )  async  throws  ->  Haystack . Grid  { 
110-         // TODO: Implement
111-         return  GridBuilder ( ) . toGrid ( ) 
112+     public  func  nav( navId parentId:  Haystack . Ref ? )  async  throws  ->  Haystack . Grid  { 
113+         let  gb  =  Haystack . GridBuilder ( ) 
114+         try . addCol ( name:  " navId " ) 
115+         guard  navPath. count >  0  else  { 
116+             return  gb. toGrid ( ) 
117+         } 
118+         guard let  parentId =  parentId else  { 
119+             // If no input, just return the first level of navigation
120+             for result in try await  recordStore. read ( filter:  " \( navPath [ 0 ] ) " ,  limit:  nil )  { 
121+                 var  navResult  =  result
122+                 navResult [ " navId " ]  =  result [ " id " ] 
123+                 try . addRow ( navResult) 
124+             } 
125+             return gb. toGrid ( ) 
126+         } 
127+         guard let  parentDict =  try await  recordStore. read ( ids:  [ parentId] ) . first  else  { 
128+             throw  ServerError . idNotFound ( parentId) 
129+         } 
130+         // Find the first component of the navPath that matches a tag on the input dict
131+         var parentNavPathIndex:  Int? =  nil 
132+         for index in 0  ..<  navPath. count { 
133+             let  component  =  navPath [ index] 
134+             if  parentDict. has ( component)  { 
135+                 parentNavPathIndex =  index
136+             } 
137+         } 
138+         guard let parentNavPathIndex =  parentNavPathIndex  else  { 
139+             throw  ServerError . navPathComponentNotFound ( navPath) 
140+         } 
141+         guard parentNavPathIndex <  navPath. count -  1  else  { 
142+             // Parent is a navPath leaf. No further navigation is possible, so return nothing.
143+             return  gb. toGrid ( ) 
144+         } 
145+         let parentNavComponent =  navPath [ parentNavPathIndex] 
146+         let  childNavComponent =  navPath [ parentNavPathIndex +  1 ] 
147+         // Read children using child component and inferring parent ref tag
148+         let  children =  try await  recordStore. read ( 
149+             filter:  " \( childNavComponent)  and  \( parentNavComponent) Ref ==  \( parentId. toZinc ( ) ) " , 
150+             limit:  nil 
151+         ) 
152+         for child in children { 
153+             var  navChild  =  child
154+             navChild [ " navId " ]  =  child [ " id " ] 
155+             try . addRow ( navChild) 
156+         } 
157+         return gb. toGrid ( ) 
112158    } 
113159
114160    public func  hisRead ( id:  Haystack . Ref,  range:  Haystack . HisReadRange)  async  throws ->  Haystack. Grid  { 
@@ -199,5 +245,6 @@ public final class HaystackServer: API, Sendable {
199245
200246public enum  ServerError:  Error { 
201247    case  idNotFound( Haystack . Ref) 
248+     case  navPathComponentNotFound( [ String] ) 
202249    case  watchNotFound( String) 
203250} 
0 commit comments