robobyte-front-builder 1.0.8 → 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.8",
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": [
package/src/lib/index.js CHANGED
@@ -73,6 +73,9 @@ export { default as RoboByteFrontBuilderProvider } from './providers/RoboByteFro
73
73
  export { default as UIBuilderPage } from '../pages/viewBuilder/index'
74
74
  export { default as ReportBuilderPage } from '../pages/reportModule/reportBuilder/index'
75
75
  export { default as NavigatorBuilderPage } from '../pages/navigatorBuilder/index'
76
+ // ViewerPage renders a saved UI Builder view (read-only production mode).
77
+ // Create a host page at e.g. pages/viewer/[id].jsx and re-export this component.
78
+ export { default as ViewerPage } from '../pages/viewer/[id]/index'
76
79
 
77
80
  // ── Core Contexts & Hooks (advanced use) ─────────────────────────────────────
78
81
  // Useful when you need to read or drive builder state from the host app.
@@ -1851,7 +1851,7 @@ const NavigatorBuilder = () => {
1851
1851
 
1852
1852
  const jsonString = JSON.stringify(saveData)
1853
1853
 
1854
- // Use 'navigatorSetting' key as requested in previous sessions for DataTypes.Params
1854
+ // C# endpoint: [FromBody] NavigatorSettingForm form → expects { "value": "..." }
1855
1855
  const result = await PostService(Endpoints.Navigator.Post.NavigatorSetting, true, {
1856
1856
  value: jsonString
1857
1857
  })
@@ -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
  }