domma-cms 0.25.14 → 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/public/js/site.js +1 -1
- package/server/server.js +3 -1
- package/server/services/menus.js +18 -0
- package/server/services/renderer.js +2 -0
- 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/public/js/site.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{makeBadgeEl as _,applyColour as A,applyPill as I}from"/public/js/menu-decor.mjs";$(()=>{const C=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let o=function(e){const f=(e||"07:00").split(":");return+f[0]*60+(+f[1]||0)},a=function(){const e=new Date,f=e.getHours()*60+e.getMinutes();return f>=o(t.dayStart)&&f<o(t.nightStart)?t.dayTheme:t.nightTheme};const t=g.autoTheme;Domma.theme.set(a()),setInterval(()=>Domma.theme.set(a()),6e4)}const b=$("#site-navbar");if(b.length&&C.brand){let a=function(c,r=0){return Array.isArray(c)?c.filter(i=>!(i&&i.type==="separator"&&r===0)).map(i=>i&&i.type==="separator"?{divider:!0}:Array.isArray(i?.items)?{...i,items:a(i.items,r+1)}:i):c},e=function(c){return Array.isArray(c)?c.filter(r=>!(r&&r.type==="separator")):[]};const t={...C.brand},o=t.size&&t.size!=="md"?` navbar-brand-${t.size}`:"";if(t.logo||t.icon||t.tagline){let c="";t.logo?c+=`<img src="${t.logo}" class="navbar-brand-logo" alt="${t.text||""}">`:t.icon&&(c+=`<span data-icon="${t.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),t.text&&(c+=`<span class="navbar-brand-text${o}">${t.text}</span>`),t.tagline&&(c+=`<small class="navbar-brand-tagline">${t.tagline}</small>`),t.html=c}else o&&t.text&&(t.html=`<span class="navbar-brand-text${o}">${t.text}</span>`);Domma.elements.navbar("#site-navbar",{brand:t,items:a(C.items||[]),variant:C.variant||"dark",position:C.position||"sticky",collapsible:!0}),Domma.icons.scan("#site-navbar");const f=b.get(0),d=f.querySelector(".navbar-toggle"),l=f.querySelector(".navbar-collapse");if(d&&l){let c=null;const r=()=>{l.classList.contains("show")&&d.click()},i=()=>{c&&(c.remove(),c=null),document.body.classList.remove("dm-drawer-open")},n=()=>{c||(c=document.createElement("div"),c.className="dm-navbar-backdrop",c.addEventListener("click",r),document.body.appendChild(c),document.body.classList.add("dm-drawer-open"))};d.addEventListener("click",()=>{requestAnimationFrame(()=>{l.classList.contains("show")?n():i()})}),l.addEventListener("click",u=>{u.target.closest("a")&&requestAnimationFrame(i)}),document.addEventListener("keydown",u=>{u.key==="Escape"&&l.classList.contains("show")&&r()})}const y=e(C.items||[]);f.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(c=>{const r=y[parseInt(c.dataset.index,10)];if(!r)return;const i=c.dataset.subindex!=null,n=i&&Array.isArray(r.items)?r.items[parseInt(c.dataset.subindex,10)]:r;if(!n)return;n.colour&&A(c,n.colour),n.pill&&!i&&I(c,n.pill);const u=_(n.badge);u&&c.appendChild(u)})}const v=$("#site-footer");if(v.length){let f=function(s){return String(s??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},d=function(s){return f(s).replace(/'/g,"'")},l=function(s){if(s&&s.type==="separator")return'<li class="footer-sep"><hr></li>';const m=d(s.url||"#"),h=f(s.text||""),k=Array.isArray(s.items)&&s.items.length?`<ul>${s.items.map(l).join("")}</ul>`:"";return`<li><a href="${m}">${h}</a>${k}</li>`},y=function(s,m){return(s||[]).forEach(h=>{h&&h.type==="separator"||(m.push(h),Array.isArray(h.items)&&h.items.length&&y(h.items,m))}),m};const t=g.social||{},o={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let a='<div class="footer-inner container">';const e=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(e.copyright&&(a+=`<p>${f(e.copyright)}</p>`),e.primary.length&&(a+=`<nav class="footer-nav footer-nav--primary"><ul>${e.primary.map(l).join("")}</ul></nav>`),e.legal.length&&(a+=`<nav class="footer-nav footer-nav--legal"><ul>${e.legal.map(l).join("")}</ul></nav>`),g.footer){const s=Object.keys(o).filter(m=>t[m]);s.length&&(a+='<div class="footer-social">',s.forEach(m=>{const{label:h,svg:k}=o[m];a+=`<a href="${t[m]}" target="_blank" rel="noopener noreferrer" aria-label="${h}" class="footer-social-link">${k}</a>`}),a+="</div>")}a+="</div>",v.html(a);const c=[...y(e.primary,[]),...y(e.legal,[])];v.get(0).querySelectorAll(".footer-nav a").forEach((s,m)=>{const h=c[m];if(!h)return;h.colour&&A(s,h.colour);const k=_(h.badge);k&&s.appendChild(k)});const i=S.get("reduced_motion"),n=i!==null?!!i:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),u=v.get(0).querySelector(".footer-inner");if(u){const s=document.createElement("input");s.type="checkbox",s.className="form-switch-input",s.id="dm-motion-switch",s.checked=n,s.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const m=document.createElement("span");m.className="form-switch-label",m.textContent="Reduce motion";const h=document.createElement("label");h.className="form-switch footer-motion-switch",h.title="Reduce motion",h.appendChild(s),h.appendChild(m),u.appendChild(h)}}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan();const p=$(".page-body");if(p.length){p.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),p.find(".tabs").each(function(){Domma.elements.tabs(this)}),p.find(".carousel").each(function(){const t={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const o=parseInt(this.dataset.animationDuration,10);Number.isFinite(o)&&(t.animationDuration=o)}this.dataset.animationEasing&&(t.animationEasing=this.dataset.animationEasing),Domma.elements.carousel(this,t)}),p.find(".dm-countdown").each(function(){const t={autoStart:!0};this.dataset.to&&(t.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(t.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(t.format=this.dataset.format),Domma.elements.timer(this,t)}),p.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),p.find(".dm-progression").each(function(){Domma.elements.progression(this,{layout:this.dataset.layout||"vertical",theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(t=>{const o=t.dataset.revealMode||"stagger",a=t.dataset.revealAnimation||"slide-up",e=parseInt(t.dataset.revealDuration,10)||400,f=parseInt(t.dataset.revealStagger,10)||60,d=parseInt(t.dataset.revealDelay,10)||0,l=t.dataset.revealDirection==="rtl",y=Array.from(t.children),c={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};y.forEach((r,i)=>{r.style.opacity="0",r.style.transform=c[a]||"",r.style.transition=`opacity ${e}ms ease, transform ${e}ms ease`;const n=l?y.length-1-i:i;r.style.transitionDelay=o==="stagger"?`${d+n*f}ms`:`${d}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const r=new IntersectionObserver(i=>{i.forEach(n=>{n.isIntersecting&&(n.target.offsetWidth,n.target.style.opacity="1",n.target.style.transform="none",r.unobserve(n.target))})},{threshold:.1});y.forEach(i=>r.observe(i))}))}),p.find(".card[data-collapsible]").each(function(){const t=this.querySelector(".card-header");t&&t.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),p.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const t=this.dataset.soTarget,o=document.getElementById(t);if(!o)return;const a=E.slideover({title:o.dataset.soTitle||"",size:o.dataset.soSize||"md",position:o.dataset.soPosition||"right"});o.style.display="",a.setContent(o),a.open()})})}if(typeof $.setup=="function"){const t=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(o=>{try{const a=atob(o.dataset.config),e=JSON.parse(a);Object.assign(t,e)}catch{}}),Object.keys(t).length>0){const o={};for(const[a,e]of Object.entries(t)){const f=e?.events?.click,{confirm:d,toast:l,alert:y,prompt:c,...r}=f||{};d||l||y||c?($(a).on("click",async function(n){if(n.preventDefault(),d&&!await E.confirm(d))return;let u=null;if(!(c&&(u=await E.prompt(c,{inputPlaceholder:r.promptPlaceholder||"",inputValue:r.promptDefault||""}),u===null))){if(r.target){const s=$(r.target);r.toggleClass&&s.toggleClass(r.toggleClass),r.addClass&&s.addClass(r.addClass),r.removeClass&&s.removeClass(r.removeClass),u!==null&&(r.setText&&s.text(u),r.setVal&&s.val(u),r.setAttr&&s.attr(r.setAttr,u))}r.href&&(window.location.href=r.href),l&&E.toast(l,{type:r.toastType||"success"}),y&&E.alert(y)}}),Object.keys(r).length&&(o[a]={...e,events:{...e.events,click:r}})):o[a]=e}$.setup(o)}}p.length&&x(p.get(0))});function x(C){C.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const b=g.dataset.action,v=g.dataset.entry,w=g.dataset.confirm;let p=S.get("auth_token");if(!p){E.toast("Please log in to perform this action.",{type:"warning"});return}if(w&&!await E.confirm(w))return;const t=Array.from(g.childNodes).map(a=>a.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const o=a=>fetch(`/api/actions/${b}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({entryId:v})});try{let a=await o(p);if(a.status===401){const f=S.get("auth_refresh_token");if(f){const d=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:f})});if(d.ok){const{token:l}=await d.json();S.set("auth_token",l),p=l,a=await o(p)}}}const e=await a.json().catch(()=>({}));if(!a.ok)throw new Error(e.error||e.message||`Error ${a.status}`);E.toast(e.message||"Action completed.",{type:"success"})}catch(a){E.toast(a.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",t.forEach(a=>g.appendChild(a)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const b=S.get("auth_token");function v(p,t){const o=document.createElement("div");o.className="dm-collection-display dm-collection-empty";const a=document.createElement("p");if(t){a.appendChild(document.createTextNode("Please "));const e=document.createElement("a");e.href="/login",e.textContent="sign in",a.appendChild(e),a.appendChild(document.createTextNode(" to view your entries."))}else a.textContent=p;return o.appendChild(a),o}function w(p,t){const o=document.createElement("template");o.innerHTML=t||"",p.replaceWith(o.content)}g.forEach(async p=>{const t=p.dataset.collectionAttrs;if(!t)return;if(!b){p.replaceChildren(v("",!0));return}const o=a=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({attrs:t})});try{let a=b,e=await o(a);if(e.status===401){const d=S.get("auth_refresh_token");if(d){const l=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:d})});if(l.ok){const{token:y}=await l.json();S.set("auth_token",y),a=y,e=await o(a)}}}if(!e.ok){const{error:d}=await e.json().catch(()=>({}));p.replaceChildren(v(d||"Unable to load entries.",!1));return}const{html:f}=await e.json();w(p,f)}catch{p.replaceChildren(v("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),x(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(b=>{let v;try{v=JSON.parse(atob(b.dataset.payload))}catch{return}const{columns:w,rows:p,search:t,sortable:o,exportable:a,pageSize:e,empty:f,ctaConfig:d}=v;if(!w?.length)return;if(d){const c=w.findIndex(r=>r.key==="_cta");c!==-1&&(w[c]={key:"_cta",title:"",render:(r,i)=>{const n=document.createElement("button");if(n.className=`btn btn-${d.style||"primary"} dm-cta-trigger`,n.dataset.action=d.action||"",n.dataset.entry=i._entryId||"",d.confirm&&(n.dataset.confirm=d.confirm),d.icon){const u=document.createElement("span");u.dataset.icon=d.icon,n.appendChild(u),n.appendChild(document.createTextNode(" "))}return n.appendChild(document.createTextNode(d.label||"Run")),n}})}const l="col-table-"+Math.random().toString(36).slice(2,7),y=document.createElement("div");y.id=l,b.replaceChildren(y),T.create("#"+l,{data:p,columns:w,search:t,sortable:o,exportable:a,pageSize:e,emptyMessage:f}),d&&x(y)})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function b(t,o){const a={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const f=e.type==="checkbox"?"boolean":e.type==="date"?"string":e.type,d={...e.formConfig||{}};d.span==="full"&&o&&(d.span=o);const l={type:f,label:e.label,required:e.required,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...d}};e.type==="chooser"&&(e.variant&&(l.variant=e.variant),e.multiple&&(l.multiple=!0),e.density&&(l.density=e.density),e.columns&&(l.columns=e.columns),e.accent&&(l.accent=e.accent),e.accentStyle&&(l.accentStyle=e.accentStyle),e.glow&&(l.glow=!0),e.glowColour&&(l.glowColour=e.glowColour),e.shadow&&(l.shadow=e.shadow),e.shadowColour&&(l.shadowColour=e.shadowColour)),a[e.name]=l}),a}function v(t){const o={};return(t||[]).forEach(a=>{if(!(!a.name||a.type==="page-break"||a.type==="spacer")&&(a.type==="select"||a.type==="multiselect")&&a.required){const e=(a.options||[])[0];e&&(o[a.name]=typeof e=="object"?e.value:e)}}),o}function w(t,o){(o||[]).forEach(a=>{if(a.type!=="date"||!a.name)return;const e=t.querySelector(`[name="${a.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function p(t,o,a){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=o,e.style.cssText=a?"color:var(--danger,#f87171);margin-top:.75rem;":"color:var(--success,#4ade80);margin-top:.75rem;"}g.forEach(t=>{let o;try{o=JSON.parse(atob(t.dataset.formInline))}catch{return}const a=o.fields||[],e=o.settings||{},f=e.columns||1,d=e.layout||"stacked",l=a.some(i=>i.type==="page-break"),y=async i=>{try{const n=t.querySelector('[name="website"]'),u=t.querySelector('[name="_t"]'),s=Object.assign({},i);n!==null&&(s._hp=n.value),u!==null&&(s._t=u.value);const m=await H.post(`/api/forms/submit/${o.slug}`,s);if(m&&m.error){p(t,m.error,!0);const h=new Error(m.error);throw h.formSubmitFailed=!0,h}if(m?.redirect){window.location.href=m.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);p(t,m?.message||e.successMessage||"Thank you for your submission.",!1)}catch(n){throw n.formSubmitFailed||p(t,n.message||"Submission failed. Please try again.",!0),n}};function c(i){const n=i.querySelector("form");if(!n)return;const u=document.createElement("div");u.className="fb-form-honeypot",u.setAttribute("aria-hidden","true");const s=document.createElement("input");s.name="website",s.type="text",s.tabIndex=-1,s.autocomplete="url",s.placeholder="https://",u.appendChild(s);const m=document.createElement("input");m.name="_t",m.type="hidden",m.value=Date.now(),u.appendChild(m),n.appendChild(u)}function r(){if(!window.FormLogicEngine||!a.some(n=>n.logic))return;const i=new window.FormLogicEngine.FormLogicRuntime(o,t);if(i.init(),t._formLogicRuntime=i,t.parentNode&&typeof MutationObserver<"u"){const n=new MutationObserver(function(u){for(const s of u)for(const m of s.removedNodes)if(m===t||m.nodeType===1&&m.contains&&m.contains(t)){i.destroy(),n.disconnect();return}});n.observe(t.parentNode,{childList:!0,subtree:!1})}}if(l&&F.wizard){const i=[];let n=[],u=o.title||"Step 1",s="";a.forEach(h=>{h.type==="page-break"?(i.push({title:u,description:s,fields:b(n,f)}),n=[],u=h.label||`Step ${i.length+1}`,s=h.description||""):h.type!=="spacer"&&n.push(h)}),i.push({title:u,description:s,fields:b(n,f)});const m=F.wizard(t,{schema:{steps:i},onSubmit:y});Promise.resolve(m).then(function(){w(t,a),e.honeypot!==!1&&c(t),r()})}else if(F.render){const i=F.render(t,b(a,f),v(a),{submitText:e.submitText||"Submit",layout:d,columns:f,onSubmit:y});Promise.resolve(i).then(function(){if(d==="grid"&&e.submitSpan==="full"){const n=t.querySelector(".form-buttons");n&&n.classList.add("col-span-full")}w(t,a),e.honeypot!==!1&&c(t),r()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
|
|
1
|
+
import{makeBadgeEl as _,applyColour as A,applyPill as I}from"/public/js/menu-decor.mjs";$(()=>{const C=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let o=function(e){const f=(e||"07:00").split(":");return+f[0]*60+(+f[1]||0)},a=function(){const e=new Date,f=e.getHours()*60+e.getMinutes();return f>=o(t.dayStart)&&f<o(t.nightStart)?t.dayTheme:t.nightTheme};const t=g.autoTheme;Domma.theme.set(a()),setInterval(()=>Domma.theme.set(a()),6e4)}const b=$("#site-navbar");if(b.length&&C.brand){let a=function(i,n=0){return Array.isArray(i)?i.filter(c=>!(c&&c.type==="separator"&&n===0)).map(c=>c&&c.type==="separator"?{divider:!0}:Array.isArray(c?.items)?{...c,items:a(c.items,n+1)}:c):i},e=function(i){return Array.isArray(i)?i.filter(n=>!(n&&n.type==="separator")):[]};const t={...C.brand},o=t.size&&t.size!=="md"?` navbar-brand-${t.size}`:"";if(t.logo||t.icon||t.tagline){let i="";t.logo?i+=`<img src="${t.logo}" class="navbar-brand-logo" alt="${t.text||""}">`:t.icon&&(i+=`<span data-icon="${t.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),t.text&&(i+=`<span class="navbar-brand-text${o}">${t.text}</span>`),t.tagline&&(i+=`<small class="navbar-brand-tagline">${t.tagline}</small>`),t.html=i}else o&&t.text&&(t.html=`<span class="navbar-brand-text${o}">${t.text}</span>`);Domma.elements.navbar("#site-navbar",{brand:t,items:a(C.items||[]),variant:C.variant||"dark",position:C.position||"sticky",collapsible:!0}),Domma.icons.scan("#site-navbar");const f=b.get(0);if(C.appearOnHover){const i=window.matchMedia("(min-width: 993px)");f.querySelectorAll(".navbar-dropdown").forEach(n=>{n.addEventListener("mouseenter",()=>{i.matches&&n.classList.add("open")}),n.addEventListener("mouseleave",()=>{i.matches&&n.classList.remove("open")})})}const d=f.querySelector(".navbar-toggle"),l=f.querySelector(".navbar-collapse");if(d&&l){let i=null;const n=()=>{l.classList.contains("show")&&d.click()},c=()=>{i&&(i.remove(),i=null),document.body.classList.remove("dm-drawer-open")},s=()=>{i||(i=document.createElement("div"),i.className="dm-navbar-backdrop",i.addEventListener("click",n),document.body.appendChild(i),document.body.classList.add("dm-drawer-open"))};d.addEventListener("click",()=>{requestAnimationFrame(()=>{l.classList.contains("show")?s():c()})}),l.addEventListener("click",u=>{u.target.closest("a")&&requestAnimationFrame(c)}),document.addEventListener("keydown",u=>{u.key==="Escape"&&l.classList.contains("show")&&n()})}const y=e(C.items||[]);f.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(i=>{const n=y[parseInt(i.dataset.index,10)];if(!n)return;const c=i.dataset.subindex!=null,s=c&&Array.isArray(n.items)?n.items[parseInt(i.dataset.subindex,10)]:n;if(!s)return;s.colour&&A(i,s.colour),s.pill&&!c&&I(i,s.pill);const u=_(s.badge);u&&i.appendChild(u)})}const v=$("#site-footer");if(v.length){let f=function(r){return String(r??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},d=function(r){return f(r).replace(/'/g,"'")},l=function(r){if(r&&r.type==="separator")return'<li class="footer-sep"><hr></li>';const m=d(r.url||"#"),h=f(r.text||""),k=Array.isArray(r.items)&&r.items.length?`<ul>${r.items.map(l).join("")}</ul>`:"";return`<li><a href="${m}">${h}</a>${k}</li>`},y=function(r,m){return(r||[]).forEach(h=>{h&&h.type==="separator"||(m.push(h),Array.isArray(h.items)&&h.items.length&&y(h.items,m))}),m};const t=g.social||{},o={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let a='<div class="footer-inner container">';const e=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(e.copyright&&(a+=`<p>${f(e.copyright)}</p>`),e.primary.length&&(a+=`<nav class="footer-nav footer-nav--primary"><ul>${e.primary.map(l).join("")}</ul></nav>`),e.legal.length&&(a+=`<nav class="footer-nav footer-nav--legal"><ul>${e.legal.map(l).join("")}</ul></nav>`),g.footer){const r=Object.keys(o).filter(m=>t[m]);r.length&&(a+='<div class="footer-social">',r.forEach(m=>{const{label:h,svg:k}=o[m];a+=`<a href="${t[m]}" target="_blank" rel="noopener noreferrer" aria-label="${h}" class="footer-social-link">${k}</a>`}),a+="</div>")}a+="</div>",v.html(a);const i=[...y(e.primary,[]),...y(e.legal,[])];v.get(0).querySelectorAll(".footer-nav a").forEach((r,m)=>{const h=i[m];if(!h)return;h.colour&&A(r,h.colour);const k=_(h.badge);k&&r.appendChild(k)});const c=S.get("reduced_motion"),s=c!==null?!!c:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),u=v.get(0).querySelector(".footer-inner");if(u){const r=document.createElement("input");r.type="checkbox",r.className="form-switch-input",r.id="dm-motion-switch",r.checked=s,r.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const m=document.createElement("span");m.className="form-switch-label",m.textContent="Reduce motion";const h=document.createElement("label");h.className="form-switch footer-motion-switch",h.title="Reduce motion",h.appendChild(r),h.appendChild(m),u.appendChild(h)}}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan();const p=$(".page-body");if(p.length){p.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),p.find(".tabs").each(function(){Domma.elements.tabs(this)}),p.find(".carousel").each(function(){const t={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const o=parseInt(this.dataset.animationDuration,10);Number.isFinite(o)&&(t.animationDuration=o)}this.dataset.animationEasing&&(t.animationEasing=this.dataset.animationEasing),Domma.elements.carousel(this,t)}),p.find(".dm-countdown").each(function(){const t={autoStart:!0};this.dataset.to&&(t.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(t.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(t.format=this.dataset.format),Domma.elements.timer(this,t)}),p.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),p.find(".dm-progression").each(function(){Domma.elements.progression(this,{layout:this.dataset.layout||"vertical",theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(t=>{const o=t.dataset.revealMode||"stagger",a=t.dataset.revealAnimation||"slide-up",e=parseInt(t.dataset.revealDuration,10)||400,f=parseInt(t.dataset.revealStagger,10)||60,d=parseInt(t.dataset.revealDelay,10)||0,l=t.dataset.revealDirection==="rtl",y=Array.from(t.children),i={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};y.forEach((n,c)=>{n.style.opacity="0",n.style.transform=i[a]||"",n.style.transition=`opacity ${e}ms ease, transform ${e}ms ease`;const s=l?y.length-1-c:c;n.style.transitionDelay=o==="stagger"?`${d+s*f}ms`:`${d}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const n=new IntersectionObserver(c=>{c.forEach(s=>{s.isIntersecting&&(s.target.offsetWidth,s.target.style.opacity="1",s.target.style.transform="none",n.unobserve(s.target))})},{threshold:.1});y.forEach(c=>n.observe(c))}))}),p.find(".card[data-collapsible]").each(function(){const t=this.querySelector(".card-header");t&&t.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),p.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const t=this.dataset.soTarget,o=document.getElementById(t);if(!o)return;const a=E.slideover({title:o.dataset.soTitle||"",size:o.dataset.soSize||"md",position:o.dataset.soPosition||"right"});o.style.display="",a.setContent(o),a.open()})})}if(typeof $.setup=="function"){const t=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(o=>{try{const a=atob(o.dataset.config),e=JSON.parse(a);Object.assign(t,e)}catch{}}),Object.keys(t).length>0){const o={};for(const[a,e]of Object.entries(t)){const f=e?.events?.click,{confirm:d,toast:l,alert:y,prompt:i,...n}=f||{};d||l||y||i?($(a).on("click",async function(s){if(s.preventDefault(),d&&!await E.confirm(d))return;let u=null;if(!(i&&(u=await E.prompt(i,{inputPlaceholder:n.promptPlaceholder||"",inputValue:n.promptDefault||""}),u===null))){if(n.target){const r=$(n.target);n.toggleClass&&r.toggleClass(n.toggleClass),n.addClass&&r.addClass(n.addClass),n.removeClass&&r.removeClass(n.removeClass),u!==null&&(n.setText&&r.text(u),n.setVal&&r.val(u),n.setAttr&&r.attr(n.setAttr,u))}n.href&&(window.location.href=n.href),l&&E.toast(l,{type:n.toastType||"success"}),y&&E.alert(y)}}),Object.keys(n).length&&(o[a]={...e,events:{...e.events,click:n}})):o[a]=e}$.setup(o)}}p.length&&x(p.get(0))});function x(C){C.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const b=g.dataset.action,v=g.dataset.entry,w=g.dataset.confirm;let p=S.get("auth_token");if(!p){E.toast("Please log in to perform this action.",{type:"warning"});return}if(w&&!await E.confirm(w))return;const t=Array.from(g.childNodes).map(a=>a.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const o=a=>fetch(`/api/actions/${b}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({entryId:v})});try{let a=await o(p);if(a.status===401){const f=S.get("auth_refresh_token");if(f){const d=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:f})});if(d.ok){const{token:l}=await d.json();S.set("auth_token",l),p=l,a=await o(p)}}}const e=await a.json().catch(()=>({}));if(!a.ok)throw new Error(e.error||e.message||`Error ${a.status}`);E.toast(e.message||"Action completed.",{type:"success"})}catch(a){E.toast(a.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",t.forEach(a=>g.appendChild(a)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const b=S.get("auth_token");function v(p,t){const o=document.createElement("div");o.className="dm-collection-display dm-collection-empty";const a=document.createElement("p");if(t){a.appendChild(document.createTextNode("Please "));const e=document.createElement("a");e.href="/login",e.textContent="sign in",a.appendChild(e),a.appendChild(document.createTextNode(" to view your entries."))}else a.textContent=p;return o.appendChild(a),o}function w(p,t){const o=document.createElement("template");o.innerHTML=t||"",p.replaceWith(o.content)}g.forEach(async p=>{const t=p.dataset.collectionAttrs;if(!t)return;if(!b){p.replaceChildren(v("",!0));return}const o=a=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({attrs:t})});try{let a=b,e=await o(a);if(e.status===401){const d=S.get("auth_refresh_token");if(d){const l=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:d})});if(l.ok){const{token:y}=await l.json();S.set("auth_token",y),a=y,e=await o(a)}}}if(!e.ok){const{error:d}=await e.json().catch(()=>({}));p.replaceChildren(v(d||"Unable to load entries.",!1));return}const{html:f}=await e.json();w(p,f)}catch{p.replaceChildren(v("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),x(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(b=>{let v;try{v=JSON.parse(atob(b.dataset.payload))}catch{return}const{columns:w,rows:p,search:t,sortable:o,exportable:a,pageSize:e,empty:f,ctaConfig:d}=v;if(!w?.length)return;if(d){const i=w.findIndex(n=>n.key==="_cta");i!==-1&&(w[i]={key:"_cta",title:"",render:(n,c)=>{const s=document.createElement("button");if(s.className=`btn btn-${d.style||"primary"} dm-cta-trigger`,s.dataset.action=d.action||"",s.dataset.entry=c._entryId||"",d.confirm&&(s.dataset.confirm=d.confirm),d.icon){const u=document.createElement("span");u.dataset.icon=d.icon,s.appendChild(u),s.appendChild(document.createTextNode(" "))}return s.appendChild(document.createTextNode(d.label||"Run")),s}})}const l="col-table-"+Math.random().toString(36).slice(2,7),y=document.createElement("div");y.id=l,b.replaceChildren(y),T.create("#"+l,{data:p,columns:w,search:t,sortable:o,exportable:a,pageSize:e,emptyMessage:f}),d&&x(y)})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function b(t,o){const a={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const f=e.type==="checkbox"?"boolean":e.type==="date"?"string":e.type,d={...e.formConfig||{}};d.span==="full"&&o&&(d.span=o);const l={type:f,label:e.label,required:e.required,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...d}};e.type==="chooser"&&(e.variant&&(l.variant=e.variant),e.multiple&&(l.multiple=!0),e.density&&(l.density=e.density),e.columns&&(l.columns=e.columns),e.accent&&(l.accent=e.accent),e.accentStyle&&(l.accentStyle=e.accentStyle),e.glow&&(l.glow=!0),e.glowColour&&(l.glowColour=e.glowColour),e.shadow&&(l.shadow=e.shadow),e.shadowColour&&(l.shadowColour=e.shadowColour)),a[e.name]=l}),a}function v(t){const o={};return(t||[]).forEach(a=>{if(!(!a.name||a.type==="page-break"||a.type==="spacer")&&(a.type==="select"||a.type==="multiselect")&&a.required){const e=(a.options||[])[0];e&&(o[a.name]=typeof e=="object"?e.value:e)}}),o}function w(t,o){(o||[]).forEach(a=>{if(a.type!=="date"||!a.name)return;const e=t.querySelector(`[name="${a.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function p(t,o,a){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=o,e.style.cssText=a?"color:var(--danger,#f87171);margin-top:.75rem;":"color:var(--success,#4ade80);margin-top:.75rem;"}g.forEach(t=>{let o;try{o=JSON.parse(atob(t.dataset.formInline))}catch{return}const a=o.fields||[],e=o.settings||{},f=e.columns||1,d=e.layout||"stacked",l=a.some(c=>c.type==="page-break"),y=async c=>{try{const s=t.querySelector('[name="website"]'),u=t.querySelector('[name="_t"]'),r=Object.assign({},c);s!==null&&(r._hp=s.value),u!==null&&(r._t=u.value);const m=await H.post(`/api/forms/submit/${o.slug}`,r);if(m&&m.error){p(t,m.error,!0);const h=new Error(m.error);throw h.formSubmitFailed=!0,h}if(m?.redirect){window.location.href=m.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);p(t,m?.message||e.successMessage||"Thank you for your submission.",!1)}catch(s){throw s.formSubmitFailed||p(t,s.message||"Submission failed. Please try again.",!0),s}};function i(c){const s=c.querySelector("form");if(!s)return;const u=document.createElement("div");u.className="fb-form-honeypot",u.setAttribute("aria-hidden","true");const r=document.createElement("input");r.name="website",r.type="text",r.tabIndex=-1,r.autocomplete="url",r.placeholder="https://",u.appendChild(r);const m=document.createElement("input");m.name="_t",m.type="hidden",m.value=Date.now(),u.appendChild(m),s.appendChild(u)}function n(){if(!window.FormLogicEngine||!a.some(s=>s.logic))return;const c=new window.FormLogicEngine.FormLogicRuntime(o,t);if(c.init(),t._formLogicRuntime=c,t.parentNode&&typeof MutationObserver<"u"){const s=new MutationObserver(function(u){for(const r of u)for(const m of r.removedNodes)if(m===t||m.nodeType===1&&m.contains&&m.contains(t)){c.destroy(),s.disconnect();return}});s.observe(t.parentNode,{childList:!0,subtree:!1})}}if(l&&F.wizard){const c=[];let s=[],u=o.title||"Step 1",r="";a.forEach(h=>{h.type==="page-break"?(c.push({title:u,description:r,fields:b(s,f)}),s=[],u=h.label||`Step ${c.length+1}`,r=h.description||""):h.type!=="spacer"&&s.push(h)}),c.push({title:u,description:r,fields:b(s,f)});const m=F.wizard(t,{schema:{steps:c},onSubmit:y});Promise.resolve(m).then(function(){w(t,a),e.honeypot!==!1&&i(t),n()})}else if(F.render){const c=F.render(t,b(a,f),v(a),{submitText:e.submitText||"Submit",layout:d,columns:f,onSubmit:y});Promise.resolve(c).then(function(){if(d==="grid"&&e.submitSpan==="full"){const s=t.querySelector(".form-buttons");s&&s.classList.add("col-span-full")}w(t,a),e.honeypot!==!1&&i(t),n()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
|
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
|
}
|
package/server/services/menus.js
CHANGED
|
@@ -13,6 +13,19 @@ import path from 'path';
|
|
|
13
13
|
import {fileURLToPath} from 'url';
|
|
14
14
|
import {checkVisibility} from '../middleware/auth.js';
|
|
15
15
|
import {listEntries} from './collections.js';
|
|
16
|
+
import * as cache from './cache/index.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Bust the response-cache tags that menu changes affect. Public pages are
|
|
20
|
+
* cached with the `nav` and `site` tags (see routes/public.js), so any menu
|
|
21
|
+
* mutation — content, behaviour/style overrides, or slot mapping — must clear
|
|
22
|
+
* them or the navbar/footer/[menu] output stays stale until TTL or restart.
|
|
23
|
+
* No-op when the cache is disabled (e.g. dev / boot-time seeding).
|
|
24
|
+
*/
|
|
25
|
+
async function invalidateNavCache() {
|
|
26
|
+
try { await cache.invalidateTags(['nav', 'site']); }
|
|
27
|
+
catch { /* cache not ready (boot) — safe to ignore */ }
|
|
28
|
+
}
|
|
16
29
|
|
|
17
30
|
const __filename = fileURLToPath(import.meta.url);
|
|
18
31
|
const __dirname = path.dirname(__filename);
|
|
@@ -124,6 +137,8 @@ async function readMenuFile(slug) {
|
|
|
124
137
|
async function writeMenuFile(menu) {
|
|
125
138
|
await ensureMenusDir();
|
|
126
139
|
await fs.writeFile(menuPath(menu.slug), JSON.stringify(menu, null, 2) + '\n', 'utf8');
|
|
140
|
+
// Menu content / behaviour / style changed — drop the cached public pages.
|
|
141
|
+
await invalidateNavCache();
|
|
127
142
|
}
|
|
128
143
|
|
|
129
144
|
/**
|
|
@@ -263,6 +278,7 @@ export async function deleteMenu(slug) {
|
|
|
263
278
|
if (!validateSlug(slug)) return false;
|
|
264
279
|
try {
|
|
265
280
|
await fs.unlink(menuPath(slug));
|
|
281
|
+
await invalidateNavCache();
|
|
266
282
|
return true;
|
|
267
283
|
} catch (err) {
|
|
268
284
|
if (err.code === 'ENOENT') return false;
|
|
@@ -477,6 +493,8 @@ export async function setLocations(map) {
|
|
|
477
493
|
}
|
|
478
494
|
}
|
|
479
495
|
await fs.writeFile(LOCATIONS_FILE, JSON.stringify(map, null, 2) + '\n', 'utf8');
|
|
496
|
+
// Slot → menu mapping changed (e.g. a different menu now drives the navbar).
|
|
497
|
+
await invalidateNavCache();
|
|
480
498
|
}
|
|
481
499
|
|
|
482
500
|
// ---------------------------------------------------------------------------
|
|
@@ -97,6 +97,7 @@ export async function renderPage(page, opts = {}) {
|
|
|
97
97
|
items: navItemsDecorated,
|
|
98
98
|
variant: navMenu.variant || 'default',
|
|
99
99
|
position: navMenu.position || 'sticky',
|
|
100
|
+
...(navMenu.appearOnHover && {appearOnHover: true}),
|
|
100
101
|
...(navMenu.style && {style: navMenu.style})
|
|
101
102
|
}
|
|
102
103
|
: (await readLegacyNavBackup()) || {brand: site.brand || {}, items: []};
|
|
@@ -481,6 +482,7 @@ export async function renderBlogPage(templatePath, data = {}, seoMeta = {}) {
|
|
|
481
482
|
items: navItemsDecorated,
|
|
482
483
|
variant: navMenu.variant || 'default',
|
|
483
484
|
position: navMenu.position || 'sticky',
|
|
485
|
+
...(navMenu.appearOnHover && {appearOnHover: true}),
|
|
484
486
|
...(navMenu.style && {style: navMenu.style})
|
|
485
487
|
}
|
|
486
488
|
: (await readLegacyNavBackup()) || {brand: site.brand || {}, items: []};
|
|
@@ -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
|
+
}
|