domma-cms 0.7.1 → 0.7.6

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.
Files changed (43) hide show
  1. package/admin/js/app.js +4 -4
  2. package/admin/js/config/sidebar-config.js +1 -1
  3. package/admin/js/templates/documentation.html +121 -0
  4. package/admin/js/views/documentation.js +1 -1
  5. package/config/navigation.json +5 -0
  6. package/config/plugins.json +8 -9
  7. package/package.json +1 -1
  8. package/plugins/analytics/stats.json +5 -5
  9. package/plugins/data-transfer/admin/templates/data-transfer.html +172 -0
  10. package/plugins/data-transfer/admin/views/data-transfer.js +558 -0
  11. package/plugins/data-transfer/config.js +9 -0
  12. package/plugins/data-transfer/plugin.js +424 -0
  13. package/plugins/data-transfer/plugin.json +33 -0
  14. package/plugins/garage/admin/templates/garage.html +81 -0
  15. package/plugins/garage/admin/views/garage.js +561 -0
  16. package/plugins/garage/config.js +18 -0
  17. package/plugins/garage/data/vehicles.json +70 -0
  18. package/plugins/garage/plugin.js +438 -0
  19. package/plugins/garage/plugin.json +33 -0
  20. package/server/routes/api/auth.js +17 -3
  21. package/server/routes/api/users.js +3 -0
  22. package/server/server.js +11 -3
  23. package/server/services/content.js +6 -2
  24. package/server/services/markdown.js +7 -7
  25. package/server/services/plugins.js +9 -3
  26. package/plugins/domma-effects/admin/templates/domma-effects.html +0 -577
  27. package/plugins/domma-effects/admin/views/domma-effects.js +0 -56
  28. package/plugins/domma-effects/config.js +0 -9
  29. package/plugins/domma-effects/plugin.js +0 -147
  30. package/plugins/domma-effects/plugin.json +0 -36
  31. package/plugins/domma-effects/public/celebrations/core/canvas.js +0 -9
  32. package/plugins/domma-effects/public/celebrations/core/particles.js +0 -1
  33. package/plugins/domma-effects/public/celebrations/core/physics.js +0 -1
  34. package/plugins/domma-effects/public/celebrations/index.js +0 -1
  35. package/plugins/domma-effects/public/celebrations/themes/christmas.js +0 -1
  36. package/plugins/domma-effects/public/celebrations/themes/guy-fawkes.js +0 -1
  37. package/plugins/domma-effects/public/celebrations/themes/halloween.js +0 -1
  38. package/plugins/domma-effects/public/celebrations/themes/st-andrews.js +0 -1
  39. package/plugins/domma-effects/public/celebrations/themes/st-davids.js +0 -1
  40. package/plugins/domma-effects/public/celebrations/themes/st-georges.js +0 -1
  41. package/plugins/domma-effects/public/celebrations/themes/st-patricks.js +0 -1
  42. package/plugins/domma-effects/public/celebrations/themes/valentines.js +0 -1
  43. package/plugins/domma-effects/public/inject-body.html +0 -287
package/admin/js/app.js CHANGED
@@ -1,6 +1,6 @@
1
- import{getSidebarConfig as J}from"./config/sidebar-config.js";import{views as K}from"./views/index.js";import{api as o,getUser as r,isAuthenticated as l,logout as H}from"./api.js";$(()=>{(async()=>{try{const t=l()?await o.settings.get():null;Domma.theme.init({theme:t?.adminTheme||"charcoal-dark",persist:!0})}catch{Domma.theme.init({theme:"charcoal-dark",persist:!0})}})();const k=["jb-company","jb-agent","jb-candidate"],P=["/job-board","/my-profile"];function d(t){return t&&k.includes(t.role)}function m(t){return P.some(e=>t===e||t.startsWith(e+"/"))}R.use(async(t,e,i)=>{if(t.path==="/login"||t.path==="/reset-password")return i();if(!l()){R.navigate("/login");return}if(d(r())&&!m(t.path)){R.navigate("/job-board");return}i()});let c=null;async function x(){if(!l())return{};try{const[t,e,i,a,s,n,W,B,T,V,I,U,_]=await Promise.all([o.pages.list().catch(()=>[]),o.media.list().catch(()=>[]),o.users.list().catch(()=>[]),o.plugins.list().catch(()=>[]),o.collections.list().catch(()=>[]),o.forms.list().catch(()=>[]),o.themes.list().catch(()=>[]),o.views.list().catch(()=>[]),o.actions.list().catch(()=>[]),o.blocks.list().catch(()=>[]),o.navigation.get().catch(()=>({})),o.layouts.get().catch(()=>({})),o.get("/collections/roles/entries?limit=100").catch(()=>({entries:[]}))]),C=I.items||[],O=C.filter(h=>!h.hidden).length,D=C.length,F=a.filter(h=>h.enabled).length,y=a.length;return{pages:t.length,media:e.length,users:i.length,plugins:y>0?`${F}/${y}`:null,collections:s.length,forms:n.length,themes:W.length,views:B.length,actions:T.length,blocks:V.length,navigation:D>0?`${O}/${D}`:null,layouts:Object.keys(U).length,roles:(_.entries||[]).length}}catch{return{}}}async function u(){try{return(await o.get("/auth/permissions")).permissions||[]}catch{return[]}}async function j(){try{return await o.get("/settings/db-status")}catch{return{configured:!1}}}async function g(t){c&&$("#admin-sidebar").empty();const[e,i]=await Promise.all([x(),j()]),a=w.map(s=>{if(!s.countKey)return s;const n=e[s.countKey];return{...s,badge:n!=null&&n>0?String(n):null}});if(c=Domma.elements.sidebar("#admin-sidebar",{header:{title:"CMS Admin",icon:"layout"},items:J(t,e,a),collapsible:!0,collapseAt:992,push:!0,contentSelector:".dashboard-main",top:"60px"}),A(),L(),i?.configured){const s=$("#admin-sidebar").find(".sidebar-header-title").first();s.length&&!s.find(".db-badge").length&&s.append('<span class="db-badge" title="MongoDB connected" style="display:inline-flex;align-items:center;background:#16a34a;color:#fff;font-size:10px;font-weight:700;padding:2px 6px;border-radius:9999px;letter-spacing:.5px;margin-left:6px;vertical-align:middle;line-height:1;">DB</span>')}}function A(){$("#admin-sidebar .sidebar-link").each(function(){const t=$(this).find(".sidebar-text").text().trim();t&&$(this).attr("data-tooltip",t)}),E.tooltip("#admin-sidebar [data-tooltip]",{placement:"right"})}function L(){$("#admin-sidebar .sidebar-heading").each(function(){const t=$(this);t.addClass("sidebar-heading--collapsible"),t.append('<span class="sidebar-heading-toggle"><span data-icon="chevron-down"></span></span>'),t.on("click",function(){const e=!t.hasClass("is-collapsed");t.toggleClass("is-collapsed",e);let i=t.next();for(;i.length&&!i.hasClass("sidebar-heading")&&!i.hasClass("sidebar-divider");)i.toggle(!e),i=i.next()})}),Domma.icons.scan("#admin-sidebar")}M.subscribe("router:afterChange",({to:t})=>{c&&t.path!=="/login"&&t.path!=="/reset-password"&&c.setActive("#"+t.path)});const N=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/navigation",view:"navigation",title:"Navigation - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:e})=>{if(!(t.path==="/login"||t.path==="/reset-password")){if(d(r())&&!m(t.path)){R.navigate("/job-board");return}if($("#admin-sidebar").show(),$("#admin-topbar").show(),e?.path==="/login"||e?.path==="/reset-password"){$("#topbar-user-name").remove(),await f();const i=await u();g(i)}v()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){$(t.target).closest("button, a").length||$(this).closest(".card").toggleClass("card-collapsed")}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login"||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const b={...K},p=[...N];let w=[];async function f(){if(l())try{const t=await o.plugins.adminConfig();w=t.sidebar||[],t.routes?.length&&p.push(...t.routes);for(const[e,i]of Object.entries(t.views||{}))try{const a=await import(`/plugins/${i.entry}`);b[e]=a[i.exportName]}catch{}}catch{}}function v(){const t=r();if(!t||$("#topbar-user-name").length)return;const i={admin:"Admin",manager:"Manager",editor:"Editor",subscriber:"Subscriber","jb-company":"Company","jb-agent":"Agent","jb-candidate":"Candidate"}[t.role]||t.role;$("#topbar-user").html(`
2
- <span id="topbar-user-name" class="topbar-user-name">${S(t.name)}</span>
3
- <span class="topbar-role-badge topbar-role-badge--${S(t.role)}">${i}</span>
1
+ import{getSidebarConfig as J}from"./config/sidebar-config.js";import{views as H}from"./views/index.js";import{api as o,getUser as l,isAuthenticated as r,logout as q}from"./api.js";$(()=>{(async()=>{try{const t=r()?await o.settings.get():null;Domma.theme.init({theme:t?.adminTheme||"charcoal-dark",persist:!0})}catch{Domma.theme.init({theme:"charcoal-dark",persist:!0})}})();const P=["jb-company","jb-agent","jb-candidate"],x=["/job-board","/my-profile"];function d(t){return t&&P.includes(t.role)}function m(t){return x.some(i=>t===i||t.startsWith(i+"/"))}R.use(async(t,i,a)=>{if(t.path==="/login"||t.path==="/reset-password")return a();if(!r()){R.navigate("/login");return}if(d(l())&&!m(t.path)){R.navigate("/job-board");return}a()});let c=null;async function j(){if(!r())return{};try{const[t,i,a,s,e,n,_,B,T,V,I,O,U]=await Promise.all([o.pages.list().catch(()=>[]),o.media.list().catch(()=>[]),o.users.list().catch(()=>[]),o.plugins.list().catch(()=>[]),o.collections.list().catch(()=>[]),o.forms.list().catch(()=>[]),o.themes.list().catch(()=>[]),o.views.list().catch(()=>[]),o.actions.list().catch(()=>[]),o.blocks.list().catch(()=>[]),o.navigation.get().catch(()=>({})),o.layouts.get().catch(()=>({})),o.get("/collections/roles/entries?limit=100").catch(()=>({entries:[]}))]),D=I.items||[],K=D.filter(h=>!h.hidden).length,y=D.length,F=s.filter(h=>h.enabled).length,k=s.length;return{pages:t.length,media:i.length,users:a.length,plugins:k>0?`${F}/${k}`:null,collections:e.length,forms:n.length,themes:_.length,views:B.length,actions:T.length,blocks:V.length,navigation:y>0?`${K}/${y}`:null,layouts:Object.keys(O).length,roles:(U.entries||[]).length}}catch{return{}}}async function g(){try{return(await o.get("/auth/permissions")).permissions||[]}catch{return[]}}async function A(){try{return await o.get("/settings/db-status")}catch{return{configured:!1}}}async function u(t){c&&$("#admin-sidebar").empty();const[i,a]=await Promise.all([j(),A()]),s=w.map(e=>{if(!e.countKey)return e;const n=i[e.countKey];return{...e,badge:n!=null&&n>0?String(n):null}});if(c=Domma.elements.sidebar("#admin-sidebar",{header:{title:"CMS Admin",icon:"layout"},items:J(t,i,s),collapsible:!0,collapseAt:992,push:!0,contentSelector:".dashboard-main",top:"60px"}),L(),N(),a?.configured){const e=$("#admin-sidebar").find(".sidebar-header-title").first();e.length&&!e.find(".db-badge").length&&e.append('<span class="db-badge" title="MongoDB connected" style="display:inline-flex;align-items:center;background:#16a34a;color:#fff;font-size:10px;font-weight:700;padding:2px 6px;border-radius:9999px;letter-spacing:.5px;margin-left:6px;vertical-align:middle;line-height:1;">DB</span>')}}function L(){$("#admin-sidebar .sidebar-link").each(function(){const t=$(this).find(".sidebar-text").text().trim();t&&$(this).attr("data-tooltip",t)}),E.tooltip("#admin-sidebar [data-tooltip]",{placement:"right"})}function N(){const t="sidebar_collapsed_sections",i=new Set(S.get(t)||[]);$("#admin-sidebar .sidebar-heading").each(function(){const a=$(this);a.addClass("sidebar-heading--collapsible"),a.append('<span class="sidebar-heading-toggle"><span data-icon="chevron-down"></span></span>');const s=a.text().trim();if(i.has(s)){a.addClass("is-collapsed");let e=a.next();for(;e.length&&!e.hasClass("sidebar-heading")&&!e.hasClass("sidebar-divider");)e.hide(),e=e.next()}a.on("click",function(){const e=!a.hasClass("is-collapsed");a.toggleClass("is-collapsed",e);let n=a.next();for(;n.length&&!n.hasClass("sidebar-heading")&&!n.hasClass("sidebar-divider");)n.toggle(!e),n=n.next();e?i.add(s):i.delete(s),S.set(t,[...i])})}),Domma.icons.scan("#admin-sidebar")}M.subscribe("router:afterChange",({to:t})=>{c&&t.path!=="/login"&&t.path!=="/reset-password"&&c.setActive("#"+t.path)});const W=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/navigation",view:"navigation",title:"Navigation - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:i})=>{if(!(t.path==="/login"||t.path==="/reset-password")){if(d(l())&&!m(t.path)){R.navigate("/job-board");return}if($("#admin-sidebar").show(),$("#admin-topbar").show(),i?.path==="/login"||i?.path==="/reset-password"){$("#topbar-user-name").remove(),await f();const a=await g();u(a)}v()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){$(t.target).closest("button, a").length||$(this).closest(".card").toggleClass("card-collapsed")}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login"||window.location.hash.startsWith("#/reset-password"))return;const i=document.querySelector("#view-container .view-header button.btn-primary");i&&(t.preventDefault(),i.click())});const b={...H},p=[...W];let w=[];async function f(){if(r())try{const t=await o.plugins.adminConfig();w=t.sidebar||[],t.routes?.length&&p.push(...t.routes);for(const[i,a]of Object.entries(t.views||{}))try{const s=await import(`/plugins/${a.entry}`);b[i]=s[a.exportName]}catch{}}catch{}}function v(){const t=l();if(!t||$("#topbar-user-name").length)return;const a={admin:"Admin",manager:"Manager",editor:"Editor",subscriber:"Subscriber","jb-company":"Company","jb-agent":"Agent","jb-candidate":"Candidate"}[t.role]||t.role;$("#topbar-user").html(`
2
+ <span id="topbar-user-name" class="topbar-user-name">${C(t.name)}</span>
3
+ <span class="topbar-role-badge topbar-role-badge--${C(t.role)}">${a}</span>
4
4
  `),$("#topbar-actions").html(`
5
5
  <a href="#/my-profile" class="topbar-action-link" data-tooltip="My Profile" data-tooltip-placement="bottom">
6
6
  <span data-icon="user"></span>
@@ -14,4 +14,4 @@ import{getSidebarConfig as J}from"./config/sidebar-config.js";import{views as K}
14
14
  <span data-icon="log-out"></span>
15
15
  <span>Sign out</span>
16
16
  </a>
17
- `),$("#topbar-logout-btn").on("click",a=>{a.preventDefault(),H()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"})}function S(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}(async()=>{if(!l()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash="#/login";else{const a=(window.location.hash||"#/").slice(1)||"/";if(d(r())&&!m(a)&&(window.location.hash="#/job-board"),await f(),r()){const n=await u();await g(n),v()}}R.init({container:"#view-container",routes:p,views:b,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}});const t=R._extractParams.bind(R);R._extractParams=function(a,s){if(a.endsWith("/*")){const n=a.slice(0,-2);return s.startsWith(n+"/")?{}:null}return t(a,s)};const e=(window.location.hash||"#/").slice(1)||"/";p.filter(a=>a.path.endsWith("/*")).some(a=>e.startsWith(a.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
17
+ `),$("#topbar-logout-btn").on("click",s=>{s.preventDefault(),q()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"})}function C(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}(async()=>{if(!r()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash="#/login";else{const s=(window.location.hash||"#/").slice(1)||"/";if(d(l())&&!m(s)&&(window.location.hash="#/job-board"),await f(),l()){const n=await g();await u(n),v()}}R.init({container:"#view-container",routes:p,views:b,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}});const t=R._extractParams.bind(R);R._extractParams=function(s,e){if(s.endsWith("/*")){const n=s.slice(0,-2);return e.startsWith(n+"/")?{}:null}return t(s,e)};const i=(window.location.hash||"#/").slice(1)||"/";p.filter(s=>s.path.endsWith("/*")).some(s=>i.startsWith(s.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
@@ -1 +1 @@
1
- export function getSidebarConfig(o=[],t={},l=[]){const e=n=>o.includes(n)||["read","create","update","delete"].some(r=>o.includes(`${n}.${r}`)),s=n=>n==null?null:typeof n=="string"?n.length>0?n:null:n>0?String(n):null,i=[],u=o.length>0&&o.every(n=>n.startsWith("jb-"));u||i.push({heading:"Overview"},{id:"dashboard",text:"Dashboard",icon:"home",url:"#/",section:"#/"},{divider:!0}),(e("navigation")||e("layouts"))&&i.push({heading:"Structure"},...e("navigation")?[{id:"navigation",text:"Navigation",icon:"menu",url:"#/navigation",section:"#/navigation",badge:s(t.navigation)}]:[],...e("layouts")?[{id:"layouts",text:"Layouts",icon:"layout",url:"#/layouts",section:"#/layouts",badge:s(t.layouts)}]:[],{divider:!0}),(e("pages")||e("media"))&&(i.push({heading:"Content"}),e("pages")&&i.push({id:"pages",text:"Pages",icon:"file-text",url:"#/pages",section:"#/pages",badge:s(t.pages)}),e("media")&&i.push({id:"media",text:"Media",icon:"image",url:"#/media",section:"#/media",badge:s(t.media)})),(e("collections")||e("views")||e("actions"))&&(i.push({divider:!0},{heading:"Data"}),e("collections")&&(i.push({id:"collections",text:"Collections",icon:"database",url:"#/collections",section:"#/collections",badge:s(t.collections)}),i.push({id:"forms",text:"Forms",icon:"layout",url:"#/forms",section:"#/forms",badge:s(t.forms)})),e("views")&&i.push({id:"views",text:"Views",icon:"eye",url:"#/views",section:"#/views",badge:s(t.views)}),e("actions")&&i.push({id:"actions",text:"Actions",icon:"zap",url:"#/actions",section:"#/actions",badge:s(t.actions)}),e("pages")&&i.push({id:"blocks",text:"Blocks",icon:"box",url:"#/blocks",section:"#/blocks",badge:s(t.blocks)})),(e("plugins")||e("users")||e("settings"))&&(i.push({divider:!0},{heading:"Configuration"}),e("plugins")&&i.push({id:"roles",text:"Roles",icon:"shield",url:"#/roles",section:"#/roles",badge:s(t.roles)}),e("users")&&i.push({id:"users",text:"Users",icon:"users",url:"#/users",section:"#/users",badge:s(t.users)}),e("settings")&&i.push({id:"settings",text:"Site Settings",icon:"settings",url:"#/settings",section:"#/settings"}));const a=l.filter(n=>n.permissions?.length?n.permissions.some(r=>o.includes(r)):!0);return e("plugins")?i.push({divider:!0},{heading:"Plugins"},{id:"plugins",text:"Plugins",icon:"package",url:"#/plugins",section:"#/plugins",badge:s(t.plugins)},...a):a.length&&i.push({divider:!0},...a),(e("navigation")||e("layouts")||e("settings"))&&i.push({divider:!0},{heading:"Pro"},{id:"pro-docs",text:"Documentation",icon:"zap",url:"#/pro/docs",section:"#/pro/docs"}),i.push({divider:!0},{heading:"Account"},{id:"my-profile",text:"My Profile",icon:"user",url:"#/my-profile",section:"#/my-profile"}),u||i.push({divider:!0},{heading:"View Site"},{id:"view-site",text:"View Site",icon:"external-link",url:"/",section:"/"}),(e("pages")||e("collections")||e("plugins")||e("settings"))&&i.push({divider:!0},{heading:"Documentation"},{id:"documentation",text:"Usage",icon:"book",url:"#/documentation",section:"#/documentation",badge:s(t.documents)},{id:"tutorials",text:"Tutorials",icon:"document",url:"#/tutorials",section:"#/documentation",badge:s(t.tutorials)},{id:"api-reference",text:"API Reference",icon:"code",url:"#/api-reference",section:"#/documentation"}),i}
1
+ export function getSidebarConfig(o=[],t={},r=[]){const e=n=>o.includes(n)||["read","create","update","delete"].some(l=>o.includes(`${n}.${l}`)),s=n=>n==null?null:typeof n=="string"?n.length>0?n:null:n>0?String(n):null,i=[],u=o.length>0&&o.every(n=>n.startsWith("jb-"));u||i.push({heading:"Overview"},{id:"dashboard",text:"Dashboard",icon:"home",url:"#/",section:"#/"},{divider:!0}),(e("navigation")||e("layouts"))&&i.push({heading:"Structure"},...e("navigation")?[{id:"navigation",text:"Navigation",icon:"menu",url:"#/navigation",section:"#/navigation",badge:s(t.navigation)}]:[],...e("layouts")?[{id:"layouts",text:"Layouts",icon:"layout",url:"#/layouts",section:"#/layouts",badge:s(t.layouts)}]:[],{divider:!0}),(e("pages")||e("media"))&&(i.push({heading:"Content"}),e("pages")&&i.push({id:"pages",text:"Pages",icon:"file-text",url:"#/pages",section:"#/pages",badge:s(t.pages)}),e("media")&&i.push({id:"media",text:"Media",icon:"image",url:"#/media",section:"#/media",badge:s(t.media)})),(e("collections")||e("views")||e("actions"))&&(i.push({divider:!0},{heading:"Data"}),e("collections")&&(i.push({id:"collections",text:"Collections",icon:"database",url:"#/collections",section:"#/collections",badge:s(t.collections)}),i.push({id:"forms",text:"Forms",icon:"layout",url:"#/forms",section:"#/forms",badge:s(t.forms)})),e("views")&&i.push({id:"views",text:"Views",icon:"eye",url:"#/views",section:"#/views",badge:s(t.views)}),e("actions")&&i.push({id:"actions",text:"Actions",icon:"zap",url:"#/actions",section:"#/actions",badge:s(t.actions)}),e("pages")&&i.push({id:"blocks",text:"Blocks",icon:"box",url:"#/blocks",section:"#/blocks",badge:s(t.blocks)})),(e("plugins")||e("users")||e("settings"))&&(i.push({divider:!0},{heading:"Configuration"}),e("plugins")&&i.push({id:"roles",text:"Roles",icon:"shield",url:"#/roles",section:"#/roles",badge:s(t.roles)}),e("users")&&i.push({id:"users",text:"Users",icon:"users",url:"#/users",section:"#/users",badge:s(t.users)}),e("settings")&&i.push({id:"settings",text:"Site Settings",icon:"settings",url:"#/settings",section:"#/settings"}));const a=r.filter(n=>n.permissions?.length?n.permissions.some(l=>o.includes(l)):!0);return e("plugins")?i.push({divider:!0},{heading:"Plugins"},{id:"plugins",text:"Plugins",icon:"package",url:"#/plugins",section:"#/plugins",badge:s(t.plugins)},...a):a.length&&i.push({divider:!0},...a),i.push({divider:!0},{heading:"Account"},{id:"my-profile",text:"My Profile",icon:"user",url:"#/my-profile",section:"#/my-profile"}),u||i.push({divider:!0},{heading:"View Site"},{id:"view-site",text:"View Site",icon:"external-link",url:"/",section:"/"}),(e("pages")||e("collections")||e("plugins")||e("settings"))&&i.push({divider:!0},{heading:"Documentation"},{id:"documentation",text:"Usage",icon:"book",url:"#/documentation",section:"#/documentation",badge:s(t.documents)},{id:"tutorials",text:"Tutorials",icon:"document",url:"#/tutorials",section:"#/documentation",badge:s(t.tutorials)},{id:"api-reference",text:"API Reference",icon:"code",url:"#/api-reference",section:"#/documentation"}),i}
@@ -627,5 +627,126 @@ Step 3: email to={{entry.data.email}}
627
627
  </div>
628
628
  </div>
629
629
 
630
+ <!-- CTA Shortcode -->
631
+ <div class="card card-collapsible mb-4">
632
+ <div class="card-header" role="button" tabindex="0">
633
+ <div class="card-header-content"><h2><span data-icon="mouse-pointer"></span> CTA Shortcode <span
634
+ class="badge badge-warning" style="font-size:.7rem;">Pro</span></h2></div>
635
+ <span class="card-collapse-icon" data-icon="chevron-down"></span>
636
+ </div>
637
+ <div class="card-body docs-body">
638
+ <p>The <code>[cta]</code> shortcode places an action-trigger button in any public page. Clicking it calls
639
+ <code>POST /api/actions/:slug/public</code> with the entry ID, using the logged-in user's JWT.
640
+ If the user is not logged in, a warning toast is shown instead.</p>
641
+
642
+ <h3>Syntax &amp; Attributes</h3>
643
+ <p><strong>Wrapping form:</strong></p>
644
+ <pre class="code-block"><code>[cta action="slug" entry="entry-id" icon="check" confirm="Are you sure?"]Button label[/cta]</code></pre>
645
+ <p><strong>Self-closing form:</strong></p>
646
+ <pre class="code-block"><code>[cta action="slug" entry="entry-id" label="Button label" /]</code></pre>
647
+ <table class="table table-sm" style="margin-top:1rem;">
648
+ <thead>
649
+ <tr>
650
+ <th>Attribute</th>
651
+ <th>Required</th>
652
+ <th>Default</th>
653
+ <th>Description</th>
654
+ </tr>
655
+ </thead>
656
+ <tbody>
657
+ <tr>
658
+ <td><code>action</code></td>
659
+ <td>Yes</td>
660
+ <td>—</td>
661
+ <td>Action slug</td>
662
+ </tr>
663
+ <tr>
664
+ <td><code>entry</code></td>
665
+ <td>Yes</td>
666
+ <td>—</td>
667
+ <td>Entry UUID to act on</td>
668
+ </tr>
669
+ <tr>
670
+ <td><code>label</code></td>
671
+ <td>No</td>
672
+ <td><code>"Run"</code></td>
673
+ <td>Button text (self-closing only)</td>
674
+ </tr>
675
+ <tr>
676
+ <td><code>style</code></td>
677
+ <td>No</td>
678
+ <td><code>"primary"</code></td>
679
+ <td>primary · secondary · ghost · danger</td>
680
+ </tr>
681
+ <tr>
682
+ <td><code>icon</code></td>
683
+ <td>No</td>
684
+ <td>—</td>
685
+ <td>Domma icon name</td>
686
+ </tr>
687
+ <tr>
688
+ <td><code>size</code></td>
689
+ <td>No</td>
690
+ <td>—</td>
691
+ <td>sm · md · lg</td>
692
+ </tr>
693
+ <tr>
694
+ <td><code>confirm</code></td>
695
+ <td>No</td>
696
+ <td>—</td>
697
+ <td>Confirmation prompt before executing</td>
698
+ </tr>
699
+ </tbody>
700
+ </table>
701
+
702
+ <h3>Collection Integration</h3>
703
+ <p>Add per-entry buttons to any <code>[collection]</code> shortcode:</p>
704
+ <pre class="code-block"><code>[collection slug="applications" display="cards" title-field="name"
705
+ cta="approve-application"
706
+ cta-label="Approve"
707
+ cta-icon="check"
708
+ cta-style="primary"
709
+ cta-confirm="Approve this application?" /]</code></pre>
710
+ <table class="table table-sm" style="margin-top:1rem;">
711
+ <thead>
712
+ <tr>
713
+ <th>Attribute</th>
714
+ <th>Default</th>
715
+ <th>Description</th>
716
+ </tr>
717
+ </thead>
718
+ <tbody>
719
+ <tr>
720
+ <td><code>cta</code></td>
721
+ <td>—</td>
722
+ <td>Action slug — enables per-entry buttons</td>
723
+ </tr>
724
+ <tr>
725
+ <td><code>cta-label</code></td>
726
+ <td><code>"Run"</code></td>
727
+ <td>Button label</td>
728
+ </tr>
729
+ <tr>
730
+ <td><code>cta-icon</code></td>
731
+ <td>—</td>
732
+ <td>Domma icon name</td>
733
+ </tr>
734
+ <tr>
735
+ <td><code>cta-style</code></td>
736
+ <td><code>"primary"</code></td>
737
+ <td>Button variant</td>
738
+ </tr>
739
+ <tr>
740
+ <td><code>cta-confirm</code></td>
741
+ <td>—</td>
742
+ <td>Confirmation prompt</td>
743
+ </tr>
744
+ </tbody>
745
+ </table>
746
+ <p>All three display modes support CTA buttons: <strong>cards</strong> (card footer),
747
+ <strong>list</strong> (inline), <strong>table</strong> (dedicated column).</p>
748
+ </div>
749
+ </div>
750
+
630
751
  </div>
631
752
  </div>
@@ -1 +1 @@
1
- export const documentationView={templateUrl:"/admin/js/templates/documentation.html",async onMount(n){Domma.icons.scan(),Domma.syntax.scan()}};
1
+ export const documentationView={templateUrl:"/admin/js/templates/documentation.html",async onMount(t){Domma.elements.collapsible(t.get(0)),Domma.icons.scan(),Domma.syntax.scan()}};
@@ -72,6 +72,11 @@
72
72
  "icon": "mouse-pointer"
73
73
  }
74
74
  ]
75
+ },
76
+ {
77
+ "text": "Scratch",
78
+ "url": "/scratch",
79
+ "icon": "home"
75
80
  }
76
81
  ],
77
82
  "variant": "dark",
@@ -17,15 +17,6 @@
17
17
  "debounceMs": 300
18
18
  }
19
19
  },
20
- "domma-effects": {
21
- "enabled": true,
22
- "settings": {
23
- "respectMotion": false,
24
- "defaultDuration": 600,
25
- "defaultAnimation": "fade",
26
- "defaultThreshold": 0.1
27
- }
28
- },
29
20
  "job-board": {
30
21
  "enabled": false,
31
22
  "settings": {}
@@ -45,5 +36,13 @@
45
36
  "docs": {
46
37
  "enabled": false,
47
38
  "settings": {}
39
+ },
40
+ "garage": {
41
+ "enabled": true,
42
+ "settings": {}
43
+ },
44
+ "data-transfer": {
45
+ "enabled": true,
46
+ "settings": {}
48
47
  }
49
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.7.1",
3
+ "version": "0.7.6",
4
4
  "description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
@@ -1,8 +1,8 @@
1
1
  {
2
- "/": 161,
2
+ "/": 162,
3
3
  "/about": 88,
4
- "/blog": 36,
5
- "/contact": 30,
4
+ "/blog": 42,
5
+ "/contact": 31,
6
6
  "/resources/typography": 4,
7
7
  "/resources": 13,
8
8
  "/resources/shortcodes": 14,
@@ -11,12 +11,12 @@
11
11
  "/resources/grid": 6,
12
12
  "/forms": 14,
13
13
  "/resources/effects": 6,
14
- "/blog/hello-world": 23,
14
+ "/blog/hello-world": 28,
15
15
  "/feedback": 42,
16
16
  "/resources/dependencies": 2,
17
17
  "/resources/components": 6,
18
18
  "/gdpr": 3,
19
- "/scratch": 70,
19
+ "/scratch": 84,
20
20
  "/getting-started": 3,
21
21
  "/resources/pro": 1,
22
22
  "/todo": 23,
@@ -0,0 +1,172 @@
1
+ <div class="data-transfer-plugin">
2
+
3
+ <div class="mb-4" style="display:flex;align-items:center;gap:0.75rem;">
4
+ <span data-icon="hard-drive" style="width:28px;height:28px;opacity:0.8;"></span>
5
+ <div>
6
+ <h1 style="font-size:1.25rem;font-weight:600;margin:0;">Data Transfer</h1>
7
+ <p style="font-size:0.875rem;color:var(--dm-text-muted);margin:0;">Bulk transfer, backup, restore, and clone
8
+ collections.</p>
9
+ </div>
10
+ </div>
11
+
12
+ <div id="dt-tabs" class="tabs">
13
+ <div class="tab-list" role="tablist">
14
+ <button class="tab-item active" data-tab="transfer">Transfer</button>
15
+ <button class="tab-item" data-tab="backups">Backups</button>
16
+ <button class="tab-item" data-tab="clone">Clone</button>
17
+ </div>
18
+
19
+ <div class="tab-content">
20
+
21
+ <!-- ===== Transfer ===== -->
22
+ <div class="tab-panel active" data-panel="transfer">
23
+ <div class="card mt-4">
24
+ <div class="card-header">
25
+ <strong>Move Collections Between Storage Adapters</strong>
26
+ </div>
27
+ <div class="card-body">
28
+
29
+ <div id="dt-mongo-warning" class="card mb-3"
30
+ style="display:none;border-left:3px solid var(--dm-warning);">
31
+ <div class="card-body"
32
+ style="display:flex;align-items:center;gap:0.5rem;padding:0.65rem 0.875rem;">
33
+ <span data-icon="alert-triangle" style="width:16px;height:16px;flex-shrink:0;"></span>
34
+ <span style="font-size:0.875rem;">MongoDB is not connected. File &rarr; MongoDB transfers are unavailable.</span>
35
+ </div>
36
+ </div>
37
+
38
+ <div class="form-group mb-4">
39
+ <label class="form-label">Direction</label>
40
+ <div style="display:flex;gap:1.5rem;margin-top:0.25rem;">
41
+ <label style="display:flex;align-items:center;gap:0.5rem;cursor:pointer;">
42
+ <input type="radio" name="dt-direction" value="file-to-mongo" id="dt-dir-ftm"
43
+ checked class="form-check-input">
44
+ File &rarr; MongoDB
45
+ </label>
46
+ <label style="display:flex;align-items:center;gap:0.5rem;cursor:pointer;">
47
+ <input type="radio" name="dt-direction" value="mongo-to-file" id="dt-dir-mtf"
48
+ class="form-check-input">
49
+ MongoDB &rarr; File
50
+ </label>
51
+ </div>
52
+ </div>
53
+
54
+ <div id="dt-collections-loading" style="font-size:0.875rem;color:var(--dm-text-muted);">Loading
55
+ collections…
56
+ </div>
57
+
58
+ <div id="dt-collections-wrap" style="display:none;">
59
+ <table class="table mb-3" id="dt-collections-table">
60
+ <thead>
61
+ <tr>
62
+ <th style="width:36px;">
63
+ <input type="checkbox" id="dt-select-all" class="form-check-input"
64
+ title="Select all">
65
+ </th>
66
+ <th>Collection</th>
67
+ <th>Slug</th>
68
+ <th>Adapter</th>
69
+ <th>Entries</th>
70
+ </tr>
71
+ </thead>
72
+ <tbody id="dt-collections-body"></tbody>
73
+ </table>
74
+
75
+ <button class="btn btn-primary" id="dt-transfer-btn">
76
+ <span data-icon="arrow-left-right" style="width:14px;height:14px;"></span>
77
+ Transfer Selected
78
+ </button>
79
+ </div>
80
+
81
+ <div id="dt-transfer-results" style="display:none;margin-top:1.5rem;">
82
+ <strong style="display:block;margin-bottom:0.5rem;">Results</strong>
83
+ <div id="dt-transfer-results-body"></div>
84
+ </div>
85
+
86
+ </div>
87
+ </div>
88
+ </div>
89
+
90
+ <!-- ===== Backups ===== -->
91
+ <div class="tab-panel" data-panel="backups">
92
+ <div class="card mt-4">
93
+ <div class="card-header" style="display:flex;align-items:center;justify-content:space-between;">
94
+ <strong>Backup Snapshots</strong>
95
+ <button class="btn btn-primary btn-sm" id="dt-create-backup-btn">
96
+ <span data-icon="download" style="width:14px;height:14px;"></span>
97
+ Create Backup
98
+ </button>
99
+ </div>
100
+ <div class="card-body">
101
+ <div id="dt-backups-loading" style="font-size:0.875rem;color:var(--dm-text-muted);">Loading
102
+ backups…
103
+ </div>
104
+ <div id="dt-backups-wrap" style="display:none;">
105
+ <table class="table" id="dt-backups-table">
106
+ <thead>
107
+ <tr>
108
+ <th>Timestamp</th>
109
+ <th>Collections</th>
110
+ <th>Total Entries</th>
111
+ <th style="width:180px;">Actions</th>
112
+ </tr>
113
+ </thead>
114
+ <tbody id="dt-backups-body"></tbody>
115
+ </table>
116
+ <p id="dt-backups-empty"
117
+ style="display:none;font-size:0.875rem;color:var(--dm-text-muted);">No backups yet.</p>
118
+ </div>
119
+ </div>
120
+ </div>
121
+ </div>
122
+
123
+ <!-- ===== Clone ===== -->
124
+ <div class="tab-panel" data-panel="clone">
125
+ <div class="card mt-4" style="max-width:520px;">
126
+ <div class="card-header">
127
+ <strong>Clone a Collection</strong>
128
+ </div>
129
+ <div class="card-body">
130
+
131
+ <div class="form-group mb-3">
132
+ <label class="form-label" for="dt-clone-source">Source Collection</label>
133
+ <select class="form-input" id="dt-clone-source">
134
+ <option value="">— select —</option>
135
+ </select>
136
+ </div>
137
+
138
+ <div class="form-group mb-3">
139
+ <label class="form-label" for="dt-clone-title">New Title</label>
140
+ <input type="text" class="form-input" id="dt-clone-title"
141
+ placeholder="e.g. My Collection Copy">
142
+ </div>
143
+
144
+ <div class="form-group mb-3">
145
+ <label class="form-label" for="dt-clone-slug">
146
+ New Slug
147
+ <span style="font-size:0.8rem;color:var(--dm-text-muted);">(auto-generated)</span>
148
+ </label>
149
+ <input type="text" class="form-input" id="dt-clone-slug"
150
+ placeholder="e.g. my-collection-copy">
151
+ </div>
152
+
153
+ <div class="form-group mb-4">
154
+ <label style="display:flex;align-items:center;gap:0.5rem;cursor:pointer;">
155
+ <input type="checkbox" class="form-check-input" id="dt-clone-entries">
156
+ Copy entries (with new IDs)
157
+ </label>
158
+ </div>
159
+
160
+ <button class="btn btn-primary" id="dt-clone-btn">
161
+ <span data-icon="copy" style="width:14px;height:14px;"></span>
162
+ Clone Collection
163
+ </button>
164
+
165
+ </div>
166
+ </div>
167
+ </div>
168
+
169
+ </div>
170
+ </div>
171
+
172
+ </div>