robobyte-front-builder 1.0.10 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "robobyte-front-builder",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "RoboByte low-code UI builder, Report builder, and navigation extension system",
5
5
  "main": "src/lib/index.js",
6
6
  "files": [
@@ -1,21 +1,43 @@
1
1
  import { useRouter } from 'next/router'
2
- import { useEffect, useState, useContext } from 'react'
3
- import { Box, Typography, CircularProgress, Grid } from '@mui/material'
2
+ import { useEffect, useState, useContext, useMemo } from 'react'
3
+ import { Box, Typography, CircularProgress, Grid, Tab, Tabs } from '@mui/material'
4
4
  import { SystemContext } from 'context/SystemContext'
5
5
  import { Endpoints, Services } from 'services/Endpoints'
6
6
  import ProductionViewer from 'views/builder/viewer/ProductionViewer'
7
7
 
8
+ /**
9
+ * Finds the parent group of a viewId in the navigator config items tree.
10
+ * Returns the array of siblings (all dynamic children of that parent).
11
+ *
12
+ * nav.config.items shape:
13
+ * [ { title, children: [ { title, type, viewId }, ... ] }, ... ]
14
+ */
15
+ function findSiblings(navConfig, viewId) {
16
+ if (!navConfig?.items || viewId == null) return []
17
+ for (const group of navConfig.items) {
18
+ const children = group.children || []
19
+ const dynamicChildren = children.filter(c => c.type === 'dynamic' && c.viewId != null)
20
+ const found = dynamicChildren.find(c => Number(c.viewId) === Number(viewId))
21
+ if (found) return dynamicChildren
22
+ }
23
+ return []
24
+ }
25
+
8
26
  export default function ViewPage() {
9
27
  const router = useRouter()
10
28
  const { id } = router.query
11
29
  const systemContext = useContext(SystemContext)
12
30
  const viewRegistry = systemContext.nav?.registry || {}
31
+ const navConfig = systemContext.nav?.config
13
32
 
14
33
  const [schema, setSchema] = useState({})
15
34
  const [viewMetaData, setViewMetaData] = useState({})
16
35
  const [loading, setLoading] = useState(true)
17
36
  const [error, setError] = useState(null)
18
37
 
38
+ // Sibling pages under the same root section — used for horizontal sub-nav tabs
39
+ const siblings = useMemo(() => findSiblings(navConfig, id), [navConfig, id])
40
+
19
41
  const handleGetView = async (viewId) => {
20
42
  try {
21
43
  setLoading(true)
@@ -51,7 +73,6 @@ export default function ViewPage() {
51
73
  }
52
74
  }, [id])
53
75
 
54
- // Loading state
55
76
  if (!id) return null
56
77
 
57
78
  if (loading) {
@@ -68,21 +89,73 @@ export default function ViewPage() {
68
89
  const view = viewRegistry[viewId]
69
90
 
70
91
  return (
71
- <Box sx={{ p: 6, textAlign: 'center' }}>
72
- <Typography variant="h4">Coming soon 🚧</Typography>
73
- <Typography variant="body2" sx={{ mt: 2 }}>
74
- View #{viewId} {view ? `(${view.name})` : ''} has not been created yet.
75
- </Typography>
76
- </Box>
92
+ <>
93
+ {siblings.length > 1 && (
94
+ <HorizontalSubNav siblings={siblings} currentId={viewId} router={router} />
95
+ )}
96
+ <Box sx={{ p: 6, textAlign: 'center' }}>
97
+ <Typography variant="h4">Coming soon 🚧</Typography>
98
+ <Typography variant="body2" sx={{ mt: 2 }}>
99
+ View #{viewId} {view ? `(${view.name})` : ''} has not been created yet.
100
+ </Typography>
101
+ </Box>
102
+ </>
77
103
  )
78
104
  }
79
105
 
80
- // Success state - render the view
106
+ // Success state
81
107
  return (
82
- <Grid container>
83
- <Grid item size={12}>
84
- <ProductionViewer schema={schema} />
108
+ <Box>
109
+ {siblings.length > 1 && (
110
+ <HorizontalSubNav siblings={siblings} currentId={Number(id)} router={router} />
111
+ )}
112
+ <Grid container>
113
+ <Grid item size={12}>
114
+ <ProductionViewer schema={schema} />
115
+ </Grid>
85
116
  </Grid>
86
- </Grid>
117
+ </Box>
118
+ )
119
+ }
120
+
121
+ /**
122
+ * Horizontal sub-navigation tabs shown at the top of the viewer page
123
+ * when the current view has sibling pages under the same root section.
124
+ */
125
+ function HorizontalSubNav({ siblings, currentId, router }) {
126
+ const currentIndex = siblings.findIndex(s => Number(s.viewId) === Number(currentId))
127
+ const activeTab = currentIndex >= 0 ? currentIndex : 0
128
+
129
+ const handleChange = (_, newIndex) => {
130
+ const target = siblings[newIndex]
131
+ if (target?.viewId != null) {
132
+ router.push(`/viewer/${target.viewId}`)
133
+ }
134
+ }
135
+
136
+ return (
137
+ <Box
138
+ sx={{
139
+ borderBottom: 1,
140
+ borderColor: 'divider',
141
+ mb: 2,
142
+ backgroundColor: 'background.paper',
143
+ px: 2,
144
+ }}
145
+ >
146
+ <Tabs
147
+ value={activeTab}
148
+ onChange={handleChange}
149
+ variant="scrollable"
150
+ scrollButtons="auto"
151
+ >
152
+ {siblings.map((sibling) => (
153
+ <Tab
154
+ key={sibling.viewId}
155
+ label={sibling.title}
156
+ />
157
+ ))}
158
+ </Tabs>
159
+ </Box>
87
160
  )
88
161
  }