domma-cms 0.25.15 → 0.25.16
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/admin/js/app.js +2 -2
- package/admin/js/templates/api-reference.html +68 -1233
- package/admin/js/templates/docs/api-actions.html +146 -0
- package/admin/js/templates/docs/api-authentication.html +202 -0
- package/admin/js/templates/docs/api-collections.html +347 -0
- package/admin/js/templates/docs/api-layouts.html +123 -0
- package/admin/js/templates/docs/api-media.html +158 -0
- package/admin/js/templates/docs/api-navigation.html +99 -0
- package/admin/js/templates/docs/api-pages.html +214 -0
- package/admin/js/templates/docs/api-plugins.html +136 -0
- package/admin/js/templates/docs/api-settings.html +148 -0
- package/admin/js/templates/docs/api-users.html +189 -0
- package/admin/js/templates/docs/api-views.html +142 -0
- package/admin/js/templates/docs/components-howto.html +175 -0
- package/admin/js/templates/docs/components-reference.html +259 -0
- package/admin/js/templates/docs/components-rules.html +167 -0
- package/admin/js/templates/docs/components-walkthrough.html +167 -0
- package/admin/js/templates/docs/tutorial-crud.html +344 -0
- package/admin/js/templates/docs/tutorial-forms.html +93 -0
- package/admin/js/templates/docs/tutorial-plugin.html +234 -0
- package/admin/js/templates/docs/usage-actions.html +122 -0
- package/admin/js/templates/docs/usage-cta-shortcode.html +124 -0
- package/admin/js/templates/docs/usage-dconfig.html +148 -0
- package/admin/js/templates/docs/usage-media.html +25 -0
- package/admin/js/templates/docs/usage-navigation.html +31 -0
- package/admin/js/templates/docs/usage-pages.html +68 -0
- package/admin/js/templates/docs/usage-plugins.html +36 -0
- package/admin/js/templates/docs/usage-shortcodes.html +810 -0
- package/admin/js/templates/docs/usage-site-settings.html +52 -0
- package/admin/js/templates/docs/usage-users-roles.html +84 -0
- package/admin/js/templates/docs/usage-views.html +105 -0
- package/admin/js/templates/documentation.html +60 -1522
- package/admin/js/templates/tutorials.html +46 -659
- package/admin/js/views/api-reference.js +1 -1
- package/admin/js/views/doc-pages.js +1 -0
- package/admin/js/views/documentation.js +1 -1
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/tutorials.js +1 -1
- package/config/menus/admin-sidebar.json +76 -6
- package/package.json +1 -1
- package/server/server.js +3 -1
- package/server/services/sidebar-migration.js +145 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
export const apiReferenceView={templateUrl:"/admin/js/templates/api-reference.html",async onMount(
|
|
1
|
+
export const apiReferenceView={templateUrl:"/admin/js/templates/api-reference.html",async onMount(){Domma.icons.scan(),Domma.syntax.scan()}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{mountScaffolder as i}from"../lib/crud-tutorial.js";function t(o,n){return{templateUrl:`/admin/js/templates/docs/${o}.html`,async onMount(e){typeof n=="function"&&n(e),Domma.icons.scan(),Domma.syntax.scan()}}}export const docViews={tutorialCrud:t("tutorial-crud",o=>{const n=o.find("#tutorial-scaffolder-mount").get(0);n&&i(n,null,null)}),tutorialPlugin:t("tutorial-plugin"),tutorialForms:t("tutorial-forms"),componentsReference:t("components-reference"),componentsHowto:t("components-howto"),componentsWalkthrough:t("components-walkthrough"),componentsRules:t("components-rules")};const a=["pages","media","navigation","dconfig","shortcodes","site-settings","plugins","users-roles","views","actions","cta-shortcode"],c=["authentication","pages","settings","layouts","navigation","media","users","plugins","collections","views","actions"],s=o=>o.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("");for(const o of a)docViews["usage"+s(o)]=t(`usage-${o}`);for(const o of c)docViews["apiRef"+s(o)]=t(`api-${o}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const documentationView={templateUrl:"/admin/js/templates/documentation.html",async onMount(
|
|
1
|
+
export const documentationView={templateUrl:"/admin/js/templates/documentation.html",async onMount(){Domma.icons.scan(),Domma.syntax.scan()}};
|
package/admin/js/views/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as t}from"./settings.js";import{navigationView as e}from"./navigation.js";import{notificationsView as m}from"./notifications.js";import{layoutsView as p}from"./layouts.js";import{mediaView as s}from"./media.js";import{loginView as n}from"./login.js";import{usersView as f}from"./users.js";import{userEditorView as w}from"./user-editor.js";import{pluginsView as c}from"./plugins.js";import{documentationView as V}from"./documentation.js";import{tutorialsView as a}from"./tutorials.js";import{
|
|
1
|
+
import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as t}from"./settings.js";import{navigationView as e}from"./navigation.js";import{notificationsView as m}from"./notifications.js";import{layoutsView as p}from"./layouts.js";import{mediaView as s}from"./media.js";import{loginView as n}from"./login.js";import{usersView as f}from"./users.js";import{userEditorView as w}from"./user-editor.js";import{pluginsView as c}from"./plugins.js";import{documentationView as V}from"./documentation.js";import{tutorialsView as a}from"./tutorials.js";import{docViews as d}from"./doc-pages.js";import{apiReferenceView as l}from"./api-reference.js";import{collectionsView as E}from"./collections.js";import{collectionEditorView as u}from"./collection-editor.js";import{collectionEntriesView as g}from"./collection-entries.js";import{formsView as v}from"./forms.js";import{formEditorView as b}from"./form-editor.js";import{formSubmissionsView as j}from"./form-submissions.js";import{viewsListView as k}from"./views-list.js";import{viewEditorView as L}from"./view-editor.js";import{viewPreviewView as y}from"./view-preview.js";import{actionsListView as h}from"./actions-list.js";import{actionEditorView as D}from"./action-editor.js";import{proDocsView as P}from"./pro-docs.js";import{blocksView as R}from"./blocks.js";import{blockEditorView as S}from"./block-editor.js";import"./block-editor-enhance.js";import{componentsView as T}from"./components.js";import{componentEditorView as x}from"./component-editor.js";import{myProfileView as M}from"./my-profile.js";import{rolesView as U}from"./roles.js";import{roleEditorView as q}from"./role-editor.js";import{effectsView as z}from"./effects.js";import{menusView as A}from"./menus.js";import{menuEditorView as B}from"./menu-editor.js";import{menuLocationsView as C}from"./menu-locations.js";import{projectsView as F}from"./projects.js";import{projectEditorView as G}from"./project-editor.js";import{projectDetailView as H}from"./project-detail.js";import{projectSettingsView as I}from"./project-settings.js";import{apiTokensView as J}from"./api-tokens.js";import{apiEndpointsView as K}from"./api-endpoints.js";import{apiEndpointEditorView as N}from"./api-endpoint-editor.js";const O={templateUrl:"",async onMount(){location.hash="#/menus"}};export const views={projects:F,projectEditor:G,projectDetail:H,projectSettings:I,apiTokens:J,apiEndpoints:K,apiEndpointEditor:N,dashboard:o,pages:i,pageEditor:r,settings:t,navigation:e,layouts:p,media:s,login:n,users:f,userEditor:w,plugins:c,documentation:V,tutorials:a,apiReference:l,collections:E,collectionEditor:u,collectionEntries:g,forms:v,formEditor:b,formSubmissions:j,viewsList:k,viewEditor:L,viewPreview:y,actionsList:h,actionEditor:D,proDocs:P,blocks:R,blockEditor:S,components:T,componentEditor:x,myProfile:M,roles:U,roleEditor:q,effects:z,notifications:m,menus:A,menuEditor:B,menuLocations:C,menusRedirect:O,...d};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export const tutorialsView={templateUrl:"/admin/js/templates/tutorials.html",async onMount(){Domma.icons.scan(),Domma.syntax.scan()}};
|
|
@@ -165,18 +165,88 @@
|
|
|
165
165
|
"items": [
|
|
166
166
|
{
|
|
167
167
|
"text": "Usage",
|
|
168
|
-
"url": "
|
|
169
|
-
"icon": "book"
|
|
168
|
+
"url": "",
|
|
169
|
+
"icon": "book",
|
|
170
|
+
"items": [
|
|
171
|
+
{"text": "Pages", "url": "#/docs/usage/pages", "icon": "file-text"},
|
|
172
|
+
{"text": "Media", "url": "#/docs/usage/media", "icon": "image"},
|
|
173
|
+
{"text": "Navigation", "url": "#/docs/usage/navigation", "icon": "menu"},
|
|
174
|
+
{"text": "DConfig", "url": "#/docs/usage/dconfig", "icon": "settings"},
|
|
175
|
+
{"text": "Shortcodes", "url": "#/docs/usage/shortcodes", "icon": "code"},
|
|
176
|
+
{"text": "Site Settings", "url": "#/docs/usage/site-settings", "icon": "settings"},
|
|
177
|
+
{"text": "Plugins", "url": "#/docs/usage/plugins", "icon": "package"},
|
|
178
|
+
{"text": "Users & Roles", "url": "#/docs/usage/users-roles", "icon": "users"},
|
|
179
|
+
{"text": "Views", "url": "#/docs/usage/views", "icon": "eye"},
|
|
180
|
+
{"text": "Actions", "url": "#/docs/usage/actions", "icon": "zap"},
|
|
181
|
+
{"text": "CTA Shortcode", "url": "#/docs/usage/cta-shortcode", "icon": "zap"}
|
|
182
|
+
]
|
|
170
183
|
},
|
|
171
184
|
{
|
|
172
185
|
"text": "Tutorials",
|
|
173
|
-
"url": "
|
|
174
|
-
"icon": "document"
|
|
186
|
+
"url": "",
|
|
187
|
+
"icon": "document",
|
|
188
|
+
"items": [
|
|
189
|
+
{
|
|
190
|
+
"text": "Building a CRUD App",
|
|
191
|
+
"url": "#/tutorials/crud",
|
|
192
|
+
"icon": "database"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"text": "Writing a Plugin",
|
|
196
|
+
"url": "#/tutorials/plugin",
|
|
197
|
+
"icon": "package"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"text": "Form Follow-Up",
|
|
201
|
+
"url": "#/tutorials/forms",
|
|
202
|
+
"icon": "layout"
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"text": "Components",
|
|
208
|
+
"url": "",
|
|
209
|
+
"icon": "component",
|
|
210
|
+
"items": [
|
|
211
|
+
{
|
|
212
|
+
"text": "Reference",
|
|
213
|
+
"url": "#/docs/components",
|
|
214
|
+
"icon": "book"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"text": "How-To",
|
|
218
|
+
"url": "#/docs/components-howto",
|
|
219
|
+
"icon": "zap"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"text": "Walkthrough",
|
|
223
|
+
"url": "#/docs/components-walkthrough",
|
|
224
|
+
"icon": "document"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"text": "Rules",
|
|
228
|
+
"url": "#/docs/components-rules",
|
|
229
|
+
"icon": "shield"
|
|
230
|
+
}
|
|
231
|
+
]
|
|
175
232
|
},
|
|
176
233
|
{
|
|
177
234
|
"text": "API Reference",
|
|
178
|
-
"url": "
|
|
179
|
-
"icon": "code"
|
|
235
|
+
"url": "",
|
|
236
|
+
"icon": "code",
|
|
237
|
+
"items": [
|
|
238
|
+
{"text": "Authentication", "url": "#/docs/api/authentication", "icon": "shield"},
|
|
239
|
+
{"text": "Pages", "url": "#/docs/api/pages", "icon": "file-text"},
|
|
240
|
+
{"text": "Settings", "url": "#/docs/api/settings", "icon": "settings"},
|
|
241
|
+
{"text": "Layouts", "url": "#/docs/api/layouts", "icon": "layout"},
|
|
242
|
+
{"text": "Navigation", "url": "#/docs/api/navigation", "icon": "menu"},
|
|
243
|
+
{"text": "Media", "url": "#/docs/api/media", "icon": "image"},
|
|
244
|
+
{"text": "Users", "url": "#/docs/api/users", "icon": "users"},
|
|
245
|
+
{"text": "Plugins", "url": "#/docs/api/plugins", "icon": "package"},
|
|
246
|
+
{"text": "Collections", "url": "#/docs/api/collections", "icon": "database"},
|
|
247
|
+
{"text": "Views API", "url": "#/docs/api/views", "icon": "eye"},
|
|
248
|
+
{"text": "Actions API", "url": "#/docs/api/actions", "icon": "zap"}
|
|
249
|
+
]
|
|
180
250
|
}
|
|
181
251
|
]
|
|
182
252
|
}
|
package/package.json
CHANGED
package/server/server.js
CHANGED
|
@@ -202,7 +202,7 @@ try {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
try {
|
|
205
|
-
const {runMigration: runSidebarMigration, ensureSidebarItem} = await import('./services/sidebar-migration.js');
|
|
205
|
+
const {runMigration: runSidebarMigration, ensureSidebarItem, ensureDocumentationSubmenus} = await import('./services/sidebar-migration.js');
|
|
206
206
|
await runSidebarMigration();
|
|
207
207
|
// Surface admin pages added after first boot on existing installs.
|
|
208
208
|
await ensureSidebarItem({
|
|
@@ -213,6 +213,8 @@ try {
|
|
|
213
213
|
groupText: 'Data',
|
|
214
214
|
item: {text: 'API Builder', url: '#/api-endpoints', icon: 'code', permission: 'api-endpoints'}
|
|
215
215
|
});
|
|
216
|
+
// Restructure Documentation: Tutorials → submenu, add Components reference set.
|
|
217
|
+
await ensureDocumentationSubmenus();
|
|
216
218
|
} catch (err) {
|
|
217
219
|
app.log.warn(`[admin-sidebar] Migration skipped: ${err.message}`);
|
|
218
220
|
}
|
|
@@ -63,13 +63,80 @@ const SEED_ITEMS = [
|
|
|
63
63
|
{
|
|
64
64
|
text: 'Documentation', icon: 'book',
|
|
65
65
|
items: [
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
DOC_USAGE_SUBMENU(),
|
|
67
|
+
DOC_TUTORIALS_SUBMENU(),
|
|
68
|
+
DOC_COMPONENTS_SUBMENU(),
|
|
69
|
+
DOC_API_SUBMENU()
|
|
69
70
|
]
|
|
70
71
|
}
|
|
71
72
|
];
|
|
72
73
|
|
|
74
|
+
// ─── Documentation sub-trees ──────────────────────────────────────────────
|
|
75
|
+
// Defined as factory functions so the seed and the migration each get their
|
|
76
|
+
// own fresh object (never a shared reference). Used by both SEED_ITEMS (fresh
|
|
77
|
+
// installs) and ensureDocumentationSubmenus() (existing installs).
|
|
78
|
+
|
|
79
|
+
function DOC_USAGE_SUBMENU() {
|
|
80
|
+
return {
|
|
81
|
+
text: 'Usage', icon: 'book',
|
|
82
|
+
items: [
|
|
83
|
+
{text: 'Pages', url: '#/docs/usage/pages', icon: 'file-text'},
|
|
84
|
+
{text: 'Media', url: '#/docs/usage/media', icon: 'image'},
|
|
85
|
+
{text: 'Navigation', url: '#/docs/usage/navigation', icon: 'menu'},
|
|
86
|
+
{text: 'DConfig', url: '#/docs/usage/dconfig', icon: 'settings'},
|
|
87
|
+
{text: 'Shortcodes', url: '#/docs/usage/shortcodes', icon: 'code'},
|
|
88
|
+
{text: 'Site Settings', url: '#/docs/usage/site-settings', icon: 'settings'},
|
|
89
|
+
{text: 'Plugins', url: '#/docs/usage/plugins', icon: 'package'},
|
|
90
|
+
{text: 'Users & Roles', url: '#/docs/usage/users-roles', icon: 'users'},
|
|
91
|
+
{text: 'Views', url: '#/docs/usage/views', icon: 'eye'},
|
|
92
|
+
{text: 'Actions', url: '#/docs/usage/actions', icon: 'zap'},
|
|
93
|
+
{text: 'CTA Shortcode', url: '#/docs/usage/cta-shortcode', icon: 'zap'}
|
|
94
|
+
]
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function DOC_TUTORIALS_SUBMENU() {
|
|
99
|
+
return {
|
|
100
|
+
text: 'Tutorials', icon: 'document',
|
|
101
|
+
items: [
|
|
102
|
+
{text: 'Building a CRUD App', url: '#/tutorials/crud', icon: 'database'},
|
|
103
|
+
{text: 'Writing a Plugin', url: '#/tutorials/plugin', icon: 'package'},
|
|
104
|
+
{text: 'Form Follow-Up', url: '#/tutorials/forms', icon: 'layout'}
|
|
105
|
+
]
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function DOC_COMPONENTS_SUBMENU() {
|
|
110
|
+
return {
|
|
111
|
+
text: 'Components', icon: 'component',
|
|
112
|
+
items: [
|
|
113
|
+
{text: 'Reference', url: '#/docs/components', icon: 'book'},
|
|
114
|
+
{text: 'How-To', url: '#/docs/components-howto', icon: 'zap'},
|
|
115
|
+
{text: 'Walkthrough', url: '#/docs/components-walkthrough', icon: 'document'},
|
|
116
|
+
{text: 'Rules', url: '#/docs/components-rules', icon: 'shield'}
|
|
117
|
+
]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function DOC_API_SUBMENU() {
|
|
122
|
+
return {
|
|
123
|
+
text: 'API Reference', icon: 'code',
|
|
124
|
+
items: [
|
|
125
|
+
{text: 'Authentication', url: '#/docs/api/authentication', icon: 'shield'},
|
|
126
|
+
{text: 'Pages', url: '#/docs/api/pages', icon: 'file-text'},
|
|
127
|
+
{text: 'Settings', url: '#/docs/api/settings', icon: 'settings'},
|
|
128
|
+
{text: 'Layouts', url: '#/docs/api/layouts', icon: 'layout'},
|
|
129
|
+
{text: 'Navigation', url: '#/docs/api/navigation', icon: 'menu'},
|
|
130
|
+
{text: 'Media', url: '#/docs/api/media', icon: 'image'},
|
|
131
|
+
{text: 'Users', url: '#/docs/api/users', icon: 'users'},
|
|
132
|
+
{text: 'Plugins', url: '#/docs/api/plugins', icon: 'package'},
|
|
133
|
+
{text: 'Collections', url: '#/docs/api/collections', icon: 'database'},
|
|
134
|
+
{text: 'Views API', url: '#/docs/api/views', icon: 'eye'},
|
|
135
|
+
{text: 'Actions API', url: '#/docs/api/actions', icon: 'zap'}
|
|
136
|
+
]
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
73
140
|
async function exists(p) {
|
|
74
141
|
try { await fs.access(p); return true; } catch { return false; }
|
|
75
142
|
}
|
|
@@ -160,3 +227,78 @@ export async function ensureSidebarItem({groupText, item}, opts = {}) {
|
|
|
160
227
|
console.log(`[admin-sidebar] Added "${item.text}" to the ${groupText} group`);
|
|
161
228
|
return {ensured: true};
|
|
162
229
|
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Upgrade EXISTING installs so the Documentation group's one-page docs become
|
|
233
|
+
* per-topic submenus. Idempotent and non-clobbering:
|
|
234
|
+
*
|
|
235
|
+
* - If the persisted menu has no "Documentation" group, do nothing (the admin
|
|
236
|
+
* may have removed it deliberately — don't resurrect it).
|
|
237
|
+
* - Convert each flat leaf — "Usage" (`#/documentation`), "Tutorials"
|
|
238
|
+
* (`#/tutorials`) and "API Reference" (`#/api-reference`) — into its
|
|
239
|
+
* submenu, replacing the node in place so its position is preserved. A leaf
|
|
240
|
+
* that the admin already turned into a parent, or whose new pages already
|
|
241
|
+
* appear anywhere in the group, is left untouched.
|
|
242
|
+
* - Add the Components submenu only if `#/docs/components` isn't present yet —
|
|
243
|
+
* inserted before "API Reference" when that node exists.
|
|
244
|
+
*
|
|
245
|
+
* Returns `{updated: boolean, reason?: string}`.
|
|
246
|
+
*
|
|
247
|
+
* @param {{configDir?: string}} [opts]
|
|
248
|
+
*/
|
|
249
|
+
export async function ensureDocumentationSubmenus(opts = {}) {
|
|
250
|
+
const configDir = opts.configDir || DEFAULT_CONFIG_DIR;
|
|
251
|
+
const menuPath = path.join(configDir, 'menus', 'admin-sidebar.json');
|
|
252
|
+
|
|
253
|
+
if (!await exists(menuPath)) {
|
|
254
|
+
return {updated: false, reason: 'admin-sidebar.json not present'};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const menu = await readJson(menuPath);
|
|
258
|
+
const items = Array.isArray(menu.items) ? menu.items : [];
|
|
259
|
+
const docGroup = items.find(n => typeof n?.text === 'string' && n.text.toLowerCase() === 'documentation');
|
|
260
|
+
if (!docGroup || !Array.isArray(docGroup.items)) {
|
|
261
|
+
return {updated: false, reason: 'no Documentation group'};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const hasUrl = (nodes, url) => Array.isArray(nodes) &&
|
|
265
|
+
nodes.some(n => n?.url === url || hasUrl(n?.items, url));
|
|
266
|
+
const findByText = (text) => docGroup.items.findIndex(n =>
|
|
267
|
+
typeof n?.text === 'string' && n.text.toLowerCase() === text.toLowerCase());
|
|
268
|
+
|
|
269
|
+
let changed = false;
|
|
270
|
+
|
|
271
|
+
// Leaf → submenu conversions. `guardUrl` is a page from the submenu: if it's
|
|
272
|
+
// already present anywhere we've migrated before and skip.
|
|
273
|
+
const CONVERSIONS = [
|
|
274
|
+
{text: 'Usage', guardUrl: '#/docs/usage/pages', build: DOC_USAGE_SUBMENU},
|
|
275
|
+
{text: 'Tutorials', guardUrl: '#/tutorials/crud', build: DOC_TUTORIALS_SUBMENU},
|
|
276
|
+
{text: 'API Reference', guardUrl: '#/docs/api/authentication', build: DOC_API_SUBMENU}
|
|
277
|
+
];
|
|
278
|
+
for (const {text, guardUrl, build} of CONVERSIONS) {
|
|
279
|
+
if (hasUrl(docGroup.items, guardUrl)) continue;
|
|
280
|
+
const idx = findByText(text);
|
|
281
|
+
if (idx === -1) continue;
|
|
282
|
+
const node = docGroup.items[idx];
|
|
283
|
+
const alreadyParent = Array.isArray(node.items) && node.items.length > 0;
|
|
284
|
+
if (alreadyParent) continue;
|
|
285
|
+
docGroup.items[idx] = build();
|
|
286
|
+
changed = true;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Components submenu — add if absent, before API Reference.
|
|
290
|
+
if (!hasUrl(docGroup.items, '#/docs/components')) {
|
|
291
|
+
const apiIdx = findByText('API Reference');
|
|
292
|
+
const node = DOC_COMPONENTS_SUBMENU();
|
|
293
|
+
if (apiIdx !== -1) docGroup.items.splice(apiIdx, 0, node);
|
|
294
|
+
else docGroup.items.push(node);
|
|
295
|
+
changed = true;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!changed) return {updated: false, reason: 'already up to date'};
|
|
299
|
+
|
|
300
|
+
menu.meta = {...(menu.meta || {}), updatedAt: new Date().toISOString()};
|
|
301
|
+
await writeJson(menuPath, menu);
|
|
302
|
+
console.log('[admin-sidebar] Upgraded Documentation group with per-topic submenus');
|
|
303
|
+
return {updated: true};
|
|
304
|
+
}
|