@zenith-open/zenithcms-admin 1.0.0-beta.2 → 1.0.0-beta.32
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/dist/assets/{ApiExplorerPage-UJpoKRI0.js → ApiExplorerPage-85UWt1EW.js} +1 -1
- package/dist/assets/{ApiExplorerPage-UJpoKRI0.js.map → ApiExplorerPage-85UWt1EW.js.map} +1 -1
- package/dist/assets/{AuditLogPage-8xYlRl1I.js → AuditLogPage-CwULx1tr.js} +1 -1
- package/dist/assets/{AuditLogPage-8xYlRl1I.js.map → AuditLogPage-CwULx1tr.js.map} +1 -1
- package/dist/assets/{BlockBuilderPage-DcOo3Vnt.js → BlockBuilderPage-XD1IjOgx.js} +1 -1
- package/dist/assets/{BlockBuilderPage-DcOo3Vnt.js.map → BlockBuilderPage-XD1IjOgx.js.map} +1 -1
- package/dist/assets/{CollectionHooksPage-Dn_ujtlp.js → CollectionHooksPage-DIaSrKDj.js} +1 -1
- package/dist/assets/{CollectionHooksPage-Dn_ujtlp.js.map → CollectionHooksPage-DIaSrKDj.js.map} +1 -1
- package/dist/assets/{CollectionsPage-BSPHf7H2.js → CollectionsPage-CL_t5JRI.js} +1 -1
- package/dist/assets/{CollectionsPage-BSPHf7H2.js.map → CollectionsPage-CL_t5JRI.js.map} +1 -1
- package/dist/assets/{ComponentBuilderPage-CT6S12LA.js → ComponentBuilderPage-C58Rr1cu.js} +1 -1
- package/dist/assets/{ComponentBuilderPage-CT6S12LA.js.map → ComponentBuilderPage-C58Rr1cu.js.map} +1 -1
- package/dist/assets/{DashboardBuilder-Cbi9Ddiu.js → DashboardBuilder-Chjyqyb7.js} +1 -1
- package/dist/assets/{DashboardBuilder-Cbi9Ddiu.js.map → DashboardBuilder-Chjyqyb7.js.map} +1 -1
- package/dist/assets/{PluginsPage-5YRpbP-N.js → PluginsPage-DAR4Fsz-.js} +1 -1
- package/dist/assets/{PluginsPage-5YRpbP-N.js.map → PluginsPage-DAR4Fsz-.js.map} +1 -1
- package/dist/assets/{RedirectsPage-D_4jAdaI.js → RedirectsPage-DfMTJsac.js} +1 -1
- package/dist/assets/{RedirectsPage-D_4jAdaI.js.map → RedirectsPage-DfMTJsac.js.map} +1 -1
- package/dist/assets/{SchemaBuilderPage-EFA5XIAa.js → SchemaBuilderPage-BYprOkEv.js} +1 -1
- package/dist/assets/{SchemaBuilderPage-EFA5XIAa.js.map → SchemaBuilderPage-BYprOkEv.js.map} +1 -1
- package/dist/assets/{SettingsPage-BRpcMw48.js → SettingsPage-B2r_uNVc.js} +1 -1
- package/dist/assets/{SettingsPage-BRpcMw48.js.map → SettingsPage-B2r_uNVc.js.map} +1 -1
- package/dist/assets/SetupWizard-CBA43yJr.js +62 -0
- package/dist/assets/SetupWizard-CBA43yJr.js.map +1 -0
- package/dist/assets/SpatialEditor-BohlovfE.js +3 -0
- package/dist/assets/SpatialEditor-BohlovfE.js.map +1 -0
- package/dist/assets/{TemplatesPage-B-nNYv3o.js → TemplatesPage-BplB2ksb.js} +1 -1
- package/dist/assets/{TemplatesPage-B-nNYv3o.js.map → TemplatesPage-BplB2ksb.js.map} +1 -1
- package/dist/assets/{TrashPage-Ccusal1w.js → TrashPage-BIhKrs5x.js} +1 -1
- package/dist/assets/{TrashPage-Ccusal1w.js.map → TrashPage-BIhKrs5x.js.map} +1 -1
- package/dist/assets/index-Umj4hIVD.css +1 -0
- package/dist/assets/{index-ChcKY5Xe.js → index-yE_3fruG.js} +3 -3
- package/dist/assets/index-yE_3fruG.js.map +1 -0
- package/dist/index.html +48 -6
- package/dist/manifest.webmanifest +1 -0
- package/dist/sw.js +1 -1
- package/dist/sw.js.map +1 -1
- package/dist/{workbox-9c191d2f.js → workbox-2fbc6a65.js} +3 -3
- package/dist/workbox-2fbc6a65.js.map +1 -0
- package/package.json +4 -4
- package/dist/assets/SetupWizard-D57HIkrs.js +0 -62
- package/dist/assets/SetupWizard-D57HIkrs.js.map +0 -1
- package/dist/assets/SpatialEditor-CPgS7Zrd.js +0 -3
- package/dist/assets/SpatialEditor-CPgS7Zrd.js.map +0 -1
- package/dist/assets/index-ChcKY5Xe.js.map +0 -1
- package/dist/assets/index-CxhwdV2K.css +0 -1
- package/dist/workbox-9c191d2f.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{$t as t,Ct as n,Dt as r,En as i,Et as a,Gn as o,Jn as s,Ln as c,Lt as ee,Mt as te,Q as l,Rn as u,Wt as d,X as f,_n as p,cr as m,h,ir as ne,mr as re,rr as g,u as _,vr as v,wn as y,xr as b}from"./vendor-react-DQVTOTFO.js";import{a as x,o as S,t as C}from"./utils-fgvbH6CB.js";import{p as ie}from"./index-ChcKY5Xe.js";var w=e(b(),1),T=v(),E=w.memo(function({title:e,icon:t,action:n,children:r,className:i,noPadding:a}){let{theme:o}=x();return(0,T.jsxs)(`div`,{className:C(`flex flex-col border z-panel backdrop-blur-md shadow-sm`,i),style:{background:`var(--z-bg-panel)`,borderColor:`var(--z-border)`},children:[(e||n)&&(0,T.jsxs)(`div`,{className:`flex items-center justify-between px-5 py-4 border-b shrink-0 border-z-border`,children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[t&&(0,T.jsx)(`span`,{className:`text-z-secondary`,children:t}),e&&(0,T.jsx)(`h2`,{className:`text-sm font-semibold text-z-secondary`,children:e})]}),n&&(0,T.jsx)(`div`,{children:n})]}),(0,T.jsx)(`div`,{className:C(`flex-1 min-h-0`,!a&&`p-5`),children:r})]})}),D=class extends w.Component{constructor(e){super(e),this.state={hasError:!1}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}componentDidCatch(e,t){console.error(`Widget caught error:`,e,t)}render(){return this.state.hasError?(0,T.jsxs)(`div`,{className:`flex flex-col items-center justify-center p-4 border border-red-500/20 bg-red-500/10 rounded-none min-h-[100px] h-full`,children:[(0,T.jsx)(i,{className:`text-red-500 mb-2`,size:24}),(0,T.jsx)(`span`,{className:`text-xs font-bold text-red-500`,children:`Widget Error`}),(0,T.jsx)(`span`,{className:`text-sm text-red-400 mt-1 truncate max-w-full px-2`,children:this.state.error?.message||`Failed to render`})]}):this.props.children}};function ae(e){let t=Math.floor((Date.now()-new Date(e).getTime())/1e3);return t<60?`${t}s ago`:t<3600?`${Math.floor(t/60)}m ago`:t<86400?`${Math.floor(t/3600)}h ago`:`${Math.floor(t/86400)}d ago`}function O(e){if(e==null)return`—`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),i=Math.floor(e%60);return t>0?`${t}d ${n}h ${r}m`:n>0?`${n}h ${r}m ${i}s`:`${r}m ${i}s`}var oe={create:`text-z-active-text bg-z-active-bg`,update:`text-z-active-text bg-z-active-bg`,delete:`text-rose-400 bg-rose-500/10`,login:`text-sky-400 bg-sky-500/10`,logout:`text-z-muted bg-z-panel`},k=[`bg-z-accent`,`bg-z-accent`,`bg-sky-600`,`bg-amber-600`,`bg-rose-600`,`bg-z-accent`],A=w.memo(function({label:e,value:t,sub:n,icon:r,accent:i,loading:a}){let{theme:o}=x(),s=i===`purple`||i===`emerald`?`text-z-active-text`:i===`red`?`text-rose-400`:`text-z-primary`;return(0,T.jsxs)(`div`,{className:C(`flex flex-col justify-between gap-2 p-5 border transition-colors z-panel backdrop-blur-md shadow-sm`),style:{background:`var(--z-bg-panel)`,borderColor:`var(--z-border)`},children:[(0,T.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,T.jsx)(`span`,{className:`text-sm font-semibold text-z-secondary`,children:e}),(0,T.jsx)(r,{size:13,className:`text-z-secondary`})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`span`,{className:C(`text-2xl font-semibold leading-none tabular-nums`,s),children:a?(0,T.jsx)(`span`,{className:`text-z-secondary text-base`,children:`—`}):t}),n&&(0,T.jsx)(`p`,{className:`text-sm text-z-secondary mt-1`,children:n})]})]})});function j({env:e}){if(!e)return null;let t=e===`production`;return(0,T.jsxs)(`span`,{className:C(`inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-semibold border`,t?`bg-rose-500/10 border-rose-500/20 text-rose-400`:`bg-amber-500/10 border-amber-500/20 text-amber-400`),children:[(0,T.jsx)(`span`,{className:C(`w-1.5 h-1.5 rounded-full`,t?`bg-rose-400 animate-pulse`:`bg-amber-400`)}),e]})}function M(){let{theme:e}=x(),v=re(),[b,M]=(0,w.useState)(null),[N,P]=(0,w.useState)(null),[F,se]=(0,w.useState)([]),[I,ce]=(0,w.useState)(null),[L,le]=(0,w.useState)([]),[R,z]=(0,w.useState)(`—`),[B,V]=(0,w.useState)(null),[H,U]=(0,w.useState)([]),[W,G]=(0,w.useState)(null),[K,q]=(0,w.useState)(null),[J,Y]=(0,w.useState)(!0);(0,w.useEffect)(()=>{let e=setInterval(()=>{M(e=>e&&e.uptime!=null?{...e,uptime:e.uptime+1}:e)},1e3);return()=>clearInterval(e)},[]);let X=(0,w.useCallback)(async()=>{Y(!0);try{let e=performance.now(),t=await Promise.allSettled([S.get(`/system/health`),S.get(`/system/audit-logs?limit=8`),S.get(`/system/audit-logs/stats`),S.get(`/system/schemas`),S.get(`/system/counts`),S.get(`/media?pageSize=1&sort=-createdAt`),S.get(`/presence`)]);t[0].status===`fulfilled`&&(P(Math.round(performance.now()-e)),M(t[0].value.data?.data||null)),t[1].status===`fulfilled`&&se(t[1].value.data?.data||[]),t[2].status===`fulfilled`&&ce(t[2].value.data?.data||null);let n=t[4].status===`fulfilled`&&t[4].value.data?.data||{};if(t[3].status===`fulfilled`){let e=t[3].value.data?.data;le((e?.collections||(Array.isArray(e)?e:[])).map(e=>({name:e.slug||e.name,label:e.label||e.labels?.plural||e.slug||e.name,count:n[e.slug||e.name],drafts:!!e.drafts,icon:e.admin?.icon})));let r=Object.entries(n).filter(([e])=>!e.startsWith(`z_`)).reduce((e,[,t])=>e+t,0);z(r>0?r.toLocaleString():`0`),n.users==null?n.z_users==null?n.members!=null&&G(n.members):G(n.z_users):G(n.users)}if(t[5].status===`fulfilled`){let e=t[5].value.data?.meta?.pagination;V(e?.total??t[5].value.data?.data?.length??null)}t[6].status===`fulfilled`&&U(t[6].value.data?.data||[])}catch{}finally{Y(!1)}},[]);(0,w.useEffect)(()=>{X();let e=setInterval(()=>{let e=performance.now();S.get(`/system/health`).then(t=>{P(Math.round(performance.now()-e)),M(e=>{let n=t.data?.data||null;return e&&n&&n.uptime?{...n,uptime:Math.max(e.uptime,n.uptime)}:n})}).catch(()=>{}),S.get(`/presence`).then(e=>{let t=e.data?.data||[];U(t),G(e=>e===null?t.length:Math.max(e,t.length))}).catch(()=>{})},3e3);return()=>clearInterval(e)},[X]),(0,w.useEffect)(()=>{let e=()=>{S.post(`/presence/heartbeat`,{collection:`dashboard`,documentId:`dashboard`}).catch(()=>{})};e();let t=setInterval(e,3e4);return()=>clearInterval(t)},[]);let Z=b?.status===`ok`,Q=b?.database===`ok`;if(J)return(0,T.jsxs)(`div`,{className:`h-full w-full flex flex-col items-center justify-center gap-4`,children:[(0,T.jsx)(c,{size:26,className:`animate-spin text-z-secondary`,strokeWidth:1.5}),(0,T.jsx)(`p`,{className:`text-sm font-semibold text-z-secondary animate-pulse`,children:`Loading…`})]});let $=e===`dark`?`bg-z-hover hover:bg-z-panel/[0.08] text-z-secondary border border-z-border shadow-sm`:`bg-z-panel/65 backdrop-blur-[12px] hover:bg-z-panel/85 text-z-primary border border-z-border shadow-sm`,ue=[{label:`New Content`,icon:l,path:`/collections`,color:$},{label:`Media Library`,icon:a,path:`/media`,color:$},{label:`API Explorer`,icon:_,path:`/settings?tab=api-explorer`,color:$},{label:`Schema Builder`,icon:u,path:`/schema-builder`,color:$},{label:`API Keys`,icon:n,path:`/settings?tab=keys`,color:$},{label:`Audit Log`,icon:r,path:`/audit-log`,color:$}];return(0,T.jsx)(`div`,{className:`min-h-full transition-colors duration-300`,children:(0,T.jsxs)(`div`,{className:`p-6 space-y-5 max-w-screen-2xl mx-auto`,children:[(0,T.jsx)(ie,{title:`Dashboard`,description:`System overview for your Zenith CMS workspace.`,icon:(0,T.jsx)(y,{size:20}),actions:(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,T.jsx)(j,{env:b?.environment}),b?.version&&(0,T.jsxs)(`span`,{className:`text-sm font-semibold text-z-secondary`,children:[`v`,b.version]})]})}),(0,T.jsx)(D,{children:(0,T.jsxs)(`div`,{className:`grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3`,children:[(0,T.jsx)(A,{label:`Content Records`,value:R,icon:ee}),(0,T.jsx)(A,{label:`Collections`,value:String(L.length||`—`),icon:d}),(0,T.jsx)(A,{label:`Media Files`,value:B==null?`—`:B.toLocaleString(),icon:a}),(0,T.jsx)(A,{label:`Team Members`,value:W==null?`—`:String(W),sub:H.length>0?`${H.length} online now`:void 0,icon:h}),(0,T.jsx)(A,{label:`API Latency`,value:N==null?`—`:`${N}ms`,sub:b?Z?`System Operational`:`System Degraded`:void 0,icon:f,accent:b?N!=null&&N<300?`emerald`:`red`:void 0}),(0,T.jsx)(A,{label:`Database`,value:b?.database||(b?`Connected`:`—`),sub:Q?`Status: Operational`:`Status: Degraded`,icon:y,accent:b?Q?`emerald`:`red`:void 0})]})}),I&&(0,T.jsx)(D,{children:(0,T.jsxs)(`div`,{className:`grid grid-cols-2 sm:grid-cols-3 gap-3`,children:[(0,T.jsx)(A,{label:`Total Events`,value:I.total.toLocaleString(),icon:r}),(0,T.jsx)(A,{label:`Successful Ops`,value:I.success.toLocaleString(),icon:s,accent:`emerald`}),(0,T.jsx)(A,{label:`Failed Ops`,value:I.failed.toLocaleString(),icon:o,accent:I.failed>0?`red`:void 0})]})}),(0,T.jsx)(E,{title:`Quick Actions`,icon:(0,T.jsx)(_,{size:13}),children:(0,T.jsx)(`div`,{className:`grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-2`,children:ue.map(e=>(0,T.jsxs)(`button`,{onClick:()=>v(e.path),className:C(`flex items-center gap-2.5 px-3 py-2.5 text-sm font-semibold tracking-wide transition-all`,e.color),children:[(0,T.jsx)(e.icon,{size:14}),e.label]},e.label))})}),(0,T.jsxs)(`div`,{className:`grid grid-cols-1 lg:grid-cols-2 gap-5`,children:[(0,T.jsx)(E,{title:`Collections`,icon:(0,T.jsx)(d,{size:13}),noPadding:!0,action:(0,T.jsxs)(m,{to:`/schema-builder`,className:C(`text-sm font-semibold flex items-center gap-1 transition-colors`,e===`dark`?`text-z-secondary hover:text-z-secondary`:`text-z-muted hover:text-z-primary`),children:[`Manage `,(0,T.jsx)(p,{size:11})]}),children:L.length===0?(0,T.jsxs)(`div`,{className:`flex flex-col items-center justify-center py-14 gap-4 px-6`,children:[(0,T.jsx)(d,{size:32,className:`text-z-primary`,strokeWidth:1}),(0,T.jsxs)(`div`,{className:`text-center`,children:[(0,T.jsx)(`p`,{className:`text-sm font-bold text-z-muted`,children:`No collections yet`}),(0,T.jsx)(`p`,{className:`text-sm text-z-secondary mt-1`,children:`Create your first collection to start managing content.`})]}),(0,T.jsx)(m,{to:`/schema-builder`,className:`px-5 py-2.5 bg-z-accent hover:brightness-110 text-z-logo-text text-sm font-semibold transition-colors`,children:`+ Create Collection`})]}):(0,T.jsxs)(`div`,{children:[L.slice(0,8).map(t=>(0,T.jsxs)(m,{to:`/collections/${t.name}`,className:C(`flex items-center justify-between px-5 py-3 group transition-colors border-b last:border-b-0`,e===`dark`?`border-z-border hover:bg-z-hover`:`border-z-border hover:bg-[var(--z-bg-input)]`),children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,T.jsx)(`div`,{className:C(`w-6 h-6 flex items-center justify-center border text-z-secondary`,e===`dark`?`bg-z-hover border-z-border`:`bg-z-input border-z-border`),children:(0,T.jsx)(u,{size:11})}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`span`,{className:C(`text-sm font-bold capitalize`,`text-z-primary`),children:t.label||t.name}),t.drafts&&(0,T.jsx)(`span`,{className:`ml-2 text-sm font-semibold text-amber-500 bg-amber-500/10 px-1.5 py-0.5`,children:`Drafts`})]})]}),(0,T.jsxs)(`div`,{className:`flex items-center gap-3`,children:[t.count!=null&&(0,T.jsx)(`span`,{className:`text-sm font-semibold tabular-nums text-z-secondary`,children:t.count.toLocaleString()}),(0,T.jsx)(p,{size:12,className:C(`transition-transform group-hover:translate-x-0.5`,e===`dark`?`text-z-primary`:`text-z-secondary`)})]})]},t.name)),L.length>8&&(0,T.jsx)(`div`,{className:C(`px-5 py-3 border-t`,`border-z-border`),children:(0,T.jsxs)(m,{to:`/schema-builder`,className:`text-sm font-semibold text-z-secondary hover:text-z-active-text transition-colors`,children:[`+`,L.length-8,` more collections →`]})}),(0,T.jsx)(`div`,{className:C(`px-5 py-3 border-t`,`border-z-border`),children:(0,T.jsxs)(m,{to:`/schema-builder`,className:`flex items-center gap-1.5 text-sm font-semibold text-z-secondary hover:text-z-active-text transition-colors w-fit`,children:[(0,T.jsx)(l,{size:11}),` New Collection`]})})]})}),(0,T.jsx)(E,{title:`Recent Activity`,icon:(0,T.jsx)(r,{size:13}),noPadding:!0,action:(0,T.jsxs)(m,{to:`/audit-log`,className:C(`text-sm font-semibold flex items-center gap-1 transition-colors`,e===`dark`?`text-z-secondary hover:text-z-secondary`:`text-z-muted hover:text-z-primary`),children:[`Full Log `,(0,T.jsx)(p,{size:11})]}),children:F.length===0?(0,T.jsxs)(`div`,{className:`flex flex-col items-center justify-center py-14 gap-2`,children:[(0,T.jsx)(r,{size:28,className:`text-z-primary`,strokeWidth:1}),(0,T.jsx)(`p`,{className:`text-sm text-z-secondary`,children:`No activity recorded yet.`})]}):(0,T.jsx)(`div`,{children:F.map(t=>{let n=t.userEmail||t.user?.email||`System`,r=n===`System`?`SY`:n.slice(0,2).toUpperCase(),i=n.charCodeAt(0)%k.length,a=(t.collectionName||t.collection||`system`).replace(/-/g,` `),o=t.status===`failed`;return(0,T.jsxs)(`div`,{onClick:()=>v(`/audit-log`),className:C(`flex items-center gap-3 px-5 py-3 cursor-pointer transition-colors border-b last:border-b-0`,e===`dark`?`border-z-border hover:bg-z-hover`:`border-z-border hover:bg-[var(--z-bg-input)]`,o&&(e===`dark`?`bg-rose-500/[0.03]`:`bg-rose-50/50`)),children:[(0,T.jsx)(`div`,{className:C(`w-6 h-6 flex items-center justify-center text-z-primary text-sm font-semibold shrink-0`,k[i]),children:r}),(0,T.jsx)(`span`,{className:C(`inline-flex px-1.5 py-0.5 text-sm font-semibold shrink-0`,o?`text-rose-400 bg-rose-500/10`:oe[t.action?.toLowerCase()]||`text-z-muted bg-z-panel`),children:t.action}),(0,T.jsx)(`span`,{className:C(`text-sm font-medium flex-1 truncate capitalize`,e===`dark`?`text-z-muted`:`text-z-secondary`),children:a}),(0,T.jsx)(`span`,{className:`text-sm text-z-secondary shrink-0 tabular-nums`,children:ae(t.timestamp)})]},t._id)})})})]}),(0,T.jsxs)(`div`,{className:`grid grid-cols-1 sm:grid-cols-2 gap-5`,children:[(0,T.jsx)(E,{title:`Who's Online`,icon:(0,T.jsx)(h,{size:13}),children:H.length===0?(0,T.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[(0,T.jsx)(`div`,{className:C(`w-2 h-2 rounded-full`,e===`dark`?`bg-z-border`:`bg-[var(--z-border)]`)}),(0,T.jsx)(`p`,{className:`text-sm text-z-secondary`,children:`No one else is online right now.`})]}):(0,T.jsxs)(`div`,{className:`flex items-center gap-4`,children:[(0,T.jsx)(`div`,{className:`flex items-center -space-x-2.5`,children:H.map((t,n)=>{let r=(t.email||t.userId||`Unknown`).split(`@`)[0],i=r.slice(0,2).toUpperCase(),a=t.collection?t.collection.replace(/-/g,` `):null,o=t.color||k[n%k.length],s=o.startsWith(`#`),c=a?`Editing ${a}`:`Browsing`;return(0,T.jsxs)(`div`,{className:`relative group cursor-default`,style:{zIndex:K===(t.userId||String(n))?50:H.length-n},onMouseEnter:()=>q(t.userId||String(n)),onMouseLeave:()=>q(null),children:[(0,T.jsx)(`div`,{className:`w-9 h-9 rounded-full flex items-center justify-center text-white text-xs font-bold transition-transform group-hover:-translate-y-1 group-hover:scale-110`,style:{backgroundColor:s?o:void 0,boxShadow:s?`0 0 0 3px ${o}55, 0 0 0 5px ${e===`dark`?`#18181b`:`#fff`}`:`0 0 0 3px var(--z-accent), 0 0 0 5px ${e===`dark`?`#18181b`:`#fff`}`},children:(0,T.jsx)(`span`,{className:C(!s&&o),children:i})}),(0,T.jsx)(`span`,{className:`absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 animate-pulse`,style:{backgroundColor:s?o:`var(--z-accent)`,borderColor:e===`dark`?`#18181b`:`#fff`}}),(0,T.jsx)(ne,{children:K===(t.userId||String(n))&&(0,T.jsxs)(g.div,{initial:{opacity:0,y:5,scale:.95},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:5,scale:.95},transition:{duration:.12},className:`absolute top-full mt-3 left-1/2 -translate-x-1/2 z-[999] whitespace-nowrap pointer-events-none`,children:[(0,T.jsxs)(`div`,{className:C(`px-3 py-2 rounded-xl shadow-2xl text-xs font-medium flex items-center gap-2`,e===`dark`?`bg-[#18181b] text-z-primary border border-z-border shadow-black/60`:`bg-white text-z-primary border shadow-lg`),children:[(0,T.jsx)(`div`,{className:`w-2.5 h-2.5 rounded-full shrink-0 animate-pulse`,style:{backgroundColor:s?o:`var(--z-accent)`}}),(0,T.jsx)(`span`,{className:`font-semibold`,children:r}),(0,T.jsx)(`span`,{className:`text-z-muted font-normal border-l border-z-border pl-2`,children:c})]}),(0,T.jsx)(`div`,{className:C(`absolute -top-1.5 left-1/2 -translate-x-1/2 w-3 h-3 rotate-45`,e===`dark`?`bg-[#18181b] border-l border-t border-z-border`:`bg-white border-l border-t border-gray-200`)})]})})]},t.userId||n)})}),(0,T.jsxs)(`span`,{className:`text-sm text-z-secondary font-medium`,children:[H.length,` `,H.length===1?`person`:`people`,` online`]})]})}),(0,T.jsx)(E,{title:`System Status`,icon:(0,T.jsx)(te,{size:13}),children:(0,T.jsxs)(`div`,{className:`space-y-3`,children:[[{label:`REST API`,ok:Z,detail:N==null?`Checking…`:`${N}ms latency`},{label:`Database`,ok:Q,detail:b?.database||`Unknown`},{label:`Memory`,ok:!0,detail:b?.memory?.heapUsed?`${b.memory.heapUsed} / ${b.memory.heapTotal} heap`:`—`}].map(e=>(0,T.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[e.ok?(0,T.jsx)(s,{size:13,className:`text-z-active-text shrink-0`}):(0,T.jsx)(i,{size:13,className:`text-rose-400 shrink-0`}),(0,T.jsx)(`span`,{className:C(`text-sm font-bold`,`text-z-secondary`),children:e.label})]}),(0,T.jsx)(`span`,{className:`text-sm text-z-secondary`,children:e.detail})]},e.label)),b?.uptime!=null&&(0,T.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,T.jsx)(t,{size:13,className:`text-z-secondary shrink-0`}),(0,T.jsx)(`span`,{className:C(`text-sm font-bold`,`text-z-secondary`),children:`Uptime`})]}),(0,T.jsx)(`span`,{className:`text-sm text-z-secondary`,children:O(b.uptime)})]})]})})]})]})})}export{M as default};
|
|
1
|
+
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{$t as t,Ct as n,Dt as r,En as i,Et as a,Gn as o,Jn as s,Ln as c,Lt as ee,Mt as te,Q as l,Rn as u,Wt as d,X as f,_n as p,cr as m,h,ir as ne,mr as re,rr as g,u as _,vr as v,wn as y,xr as b}from"./vendor-react-DQVTOTFO.js";import{a as x,o as S,t as C}from"./utils-fgvbH6CB.js";import{p as ie}from"./index-yE_3fruG.js";var w=e(b(),1),T=v(),E=w.memo(function({title:e,icon:t,action:n,children:r,className:i,noPadding:a}){let{theme:o}=x();return(0,T.jsxs)(`div`,{className:C(`flex flex-col border z-panel backdrop-blur-md shadow-sm`,i),style:{background:`var(--z-bg-panel)`,borderColor:`var(--z-border)`},children:[(e||n)&&(0,T.jsxs)(`div`,{className:`flex items-center justify-between px-5 py-4 border-b shrink-0 border-z-border`,children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[t&&(0,T.jsx)(`span`,{className:`text-z-secondary`,children:t}),e&&(0,T.jsx)(`h2`,{className:`text-sm font-semibold text-z-secondary`,children:e})]}),n&&(0,T.jsx)(`div`,{children:n})]}),(0,T.jsx)(`div`,{className:C(`flex-1 min-h-0`,!a&&`p-5`),children:r})]})}),D=class extends w.Component{constructor(e){super(e),this.state={hasError:!1}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}componentDidCatch(e,t){console.error(`Widget caught error:`,e,t)}render(){return this.state.hasError?(0,T.jsxs)(`div`,{className:`flex flex-col items-center justify-center p-4 border border-red-500/20 bg-red-500/10 rounded-none min-h-[100px] h-full`,children:[(0,T.jsx)(i,{className:`text-red-500 mb-2`,size:24}),(0,T.jsx)(`span`,{className:`text-xs font-bold text-red-500`,children:`Widget Error`}),(0,T.jsx)(`span`,{className:`text-sm text-red-400 mt-1 truncate max-w-full px-2`,children:this.state.error?.message||`Failed to render`})]}):this.props.children}};function ae(e){let t=Math.floor((Date.now()-new Date(e).getTime())/1e3);return t<60?`${t}s ago`:t<3600?`${Math.floor(t/60)}m ago`:t<86400?`${Math.floor(t/3600)}h ago`:`${Math.floor(t/86400)}d ago`}function O(e){if(e==null)return`—`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),i=Math.floor(e%60);return t>0?`${t}d ${n}h ${r}m`:n>0?`${n}h ${r}m ${i}s`:`${r}m ${i}s`}var oe={create:`text-z-active-text bg-z-active-bg`,update:`text-z-active-text bg-z-active-bg`,delete:`text-rose-400 bg-rose-500/10`,login:`text-sky-400 bg-sky-500/10`,logout:`text-z-muted bg-z-panel`},k=[`bg-z-accent`,`bg-z-accent`,`bg-sky-600`,`bg-amber-600`,`bg-rose-600`,`bg-z-accent`],A=w.memo(function({label:e,value:t,sub:n,icon:r,accent:i,loading:a}){let{theme:o}=x(),s=i===`purple`||i===`emerald`?`text-z-active-text`:i===`red`?`text-rose-400`:`text-z-primary`;return(0,T.jsxs)(`div`,{className:C(`flex flex-col justify-between gap-2 p-5 border transition-colors z-panel backdrop-blur-md shadow-sm`),style:{background:`var(--z-bg-panel)`,borderColor:`var(--z-border)`},children:[(0,T.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,T.jsx)(`span`,{className:`text-sm font-semibold text-z-secondary`,children:e}),(0,T.jsx)(r,{size:13,className:`text-z-secondary`})]}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`span`,{className:C(`text-2xl font-semibold leading-none tabular-nums`,s),children:a?(0,T.jsx)(`span`,{className:`text-z-secondary text-base`,children:`—`}):t}),n&&(0,T.jsx)(`p`,{className:`text-sm text-z-secondary mt-1`,children:n})]})]})});function j({env:e}){if(!e)return null;let t=e===`production`;return(0,T.jsxs)(`span`,{className:C(`inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-semibold border`,t?`bg-rose-500/10 border-rose-500/20 text-rose-400`:`bg-amber-500/10 border-amber-500/20 text-amber-400`),children:[(0,T.jsx)(`span`,{className:C(`w-1.5 h-1.5 rounded-full`,t?`bg-rose-400 animate-pulse`:`bg-amber-400`)}),e]})}function M(){let{theme:e}=x(),v=re(),[b,M]=(0,w.useState)(null),[N,P]=(0,w.useState)(null),[F,se]=(0,w.useState)([]),[I,ce]=(0,w.useState)(null),[L,le]=(0,w.useState)([]),[R,z]=(0,w.useState)(`—`),[B,V]=(0,w.useState)(null),[H,U]=(0,w.useState)([]),[W,G]=(0,w.useState)(null),[K,q]=(0,w.useState)(null),[J,Y]=(0,w.useState)(!0);(0,w.useEffect)(()=>{let e=setInterval(()=>{M(e=>e&&e.uptime!=null?{...e,uptime:e.uptime+1}:e)},1e3);return()=>clearInterval(e)},[]);let X=(0,w.useCallback)(async()=>{Y(!0);try{let e=performance.now(),t=await Promise.allSettled([S.get(`/system/health`),S.get(`/system/audit-logs?limit=8`),S.get(`/system/audit-logs/stats`),S.get(`/system/schemas`),S.get(`/system/counts`),S.get(`/media?pageSize=1&sort=-createdAt`),S.get(`/presence`)]);t[0].status===`fulfilled`&&(P(Math.round(performance.now()-e)),M(t[0].value.data?.data||null)),t[1].status===`fulfilled`&&se(t[1].value.data?.data||[]),t[2].status===`fulfilled`&&ce(t[2].value.data?.data||null);let n=t[4].status===`fulfilled`&&t[4].value.data?.data||{};if(t[3].status===`fulfilled`){let e=t[3].value.data?.data;le((e?.collections||(Array.isArray(e)?e:[])).map(e=>({name:e.slug||e.name,label:e.label||e.labels?.plural||e.slug||e.name,count:n[e.slug||e.name],drafts:!!e.drafts,icon:e.admin?.icon})));let r=Object.entries(n).filter(([e])=>!e.startsWith(`z_`)).reduce((e,[,t])=>e+t,0);z(r>0?r.toLocaleString():`0`),n.users==null?n.z_users==null?n.members!=null&&G(n.members):G(n.z_users):G(n.users)}if(t[5].status===`fulfilled`){let e=t[5].value.data?.meta?.pagination;V(e?.total??t[5].value.data?.data?.length??null)}t[6].status===`fulfilled`&&U(t[6].value.data?.data||[])}catch{}finally{Y(!1)}},[]);(0,w.useEffect)(()=>{X();let e=setInterval(()=>{let e=performance.now();S.get(`/system/health`).then(t=>{P(Math.round(performance.now()-e)),M(e=>{let n=t.data?.data||null;return e&&n&&n.uptime?{...n,uptime:Math.max(e.uptime,n.uptime)}:n})}).catch(()=>{}),S.get(`/presence`).then(e=>{let t=e.data?.data||[];U(t),G(e=>e===null?t.length:Math.max(e,t.length))}).catch(()=>{})},3e3);return()=>clearInterval(e)},[X]),(0,w.useEffect)(()=>{let e=()=>{S.post(`/presence/heartbeat`,{collection:`dashboard`,documentId:`dashboard`}).catch(()=>{})};e();let t=setInterval(e,3e4);return()=>clearInterval(t)},[]);let Z=b?.status===`ok`,Q=b?.database===`ok`;if(J)return(0,T.jsxs)(`div`,{className:`h-full w-full flex flex-col items-center justify-center gap-4`,children:[(0,T.jsx)(c,{size:26,className:`animate-spin text-z-secondary`,strokeWidth:1.5}),(0,T.jsx)(`p`,{className:`text-sm font-semibold text-z-secondary animate-pulse`,children:`Loading…`})]});let $=e===`dark`?`bg-z-hover hover:bg-z-panel/[0.08] text-z-secondary border border-z-border shadow-sm`:`bg-z-panel/65 backdrop-blur-[12px] hover:bg-z-panel/85 text-z-primary border border-z-border shadow-sm`,ue=[{label:`New Content`,icon:l,path:`/collections`,color:$},{label:`Media Library`,icon:a,path:`/media`,color:$},{label:`API Explorer`,icon:_,path:`/settings?tab=api-explorer`,color:$},{label:`Schema Builder`,icon:u,path:`/schema-builder`,color:$},{label:`API Keys`,icon:n,path:`/settings?tab=keys`,color:$},{label:`Audit Log`,icon:r,path:`/audit-log`,color:$}];return(0,T.jsx)(`div`,{className:`min-h-full transition-colors duration-300`,children:(0,T.jsxs)(`div`,{className:`p-6 space-y-5 max-w-screen-2xl mx-auto`,children:[(0,T.jsx)(ie,{title:`Dashboard`,description:`System overview for your Zenith CMS workspace.`,icon:(0,T.jsx)(y,{size:20}),actions:(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,T.jsx)(j,{env:b?.environment}),b?.version&&(0,T.jsxs)(`span`,{className:`text-sm font-semibold text-z-secondary`,children:[`v`,b.version]})]})}),(0,T.jsx)(D,{children:(0,T.jsxs)(`div`,{className:`grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3`,children:[(0,T.jsx)(A,{label:`Content Records`,value:R,icon:ee}),(0,T.jsx)(A,{label:`Collections`,value:String(L.length||`—`),icon:d}),(0,T.jsx)(A,{label:`Media Files`,value:B==null?`—`:B.toLocaleString(),icon:a}),(0,T.jsx)(A,{label:`Team Members`,value:W==null?`—`:String(W),sub:H.length>0?`${H.length} online now`:void 0,icon:h}),(0,T.jsx)(A,{label:`API Latency`,value:N==null?`—`:`${N}ms`,sub:b?Z?`System Operational`:`System Degraded`:void 0,icon:f,accent:b?N!=null&&N<300?`emerald`:`red`:void 0}),(0,T.jsx)(A,{label:`Database`,value:b?.database||(b?`Connected`:`—`),sub:Q?`Status: Operational`:`Status: Degraded`,icon:y,accent:b?Q?`emerald`:`red`:void 0})]})}),I&&(0,T.jsx)(D,{children:(0,T.jsxs)(`div`,{className:`grid grid-cols-2 sm:grid-cols-3 gap-3`,children:[(0,T.jsx)(A,{label:`Total Events`,value:I.total.toLocaleString(),icon:r}),(0,T.jsx)(A,{label:`Successful Ops`,value:I.success.toLocaleString(),icon:s,accent:`emerald`}),(0,T.jsx)(A,{label:`Failed Ops`,value:I.failed.toLocaleString(),icon:o,accent:I.failed>0?`red`:void 0})]})}),(0,T.jsx)(E,{title:`Quick Actions`,icon:(0,T.jsx)(_,{size:13}),children:(0,T.jsx)(`div`,{className:`grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-2`,children:ue.map(e=>(0,T.jsxs)(`button`,{onClick:()=>v(e.path),className:C(`flex items-center gap-2.5 px-3 py-2.5 text-sm font-semibold tracking-wide transition-all`,e.color),children:[(0,T.jsx)(e.icon,{size:14}),e.label]},e.label))})}),(0,T.jsxs)(`div`,{className:`grid grid-cols-1 lg:grid-cols-2 gap-5`,children:[(0,T.jsx)(E,{title:`Collections`,icon:(0,T.jsx)(d,{size:13}),noPadding:!0,action:(0,T.jsxs)(m,{to:`/schema-builder`,className:C(`text-sm font-semibold flex items-center gap-1 transition-colors`,e===`dark`?`text-z-secondary hover:text-z-secondary`:`text-z-muted hover:text-z-primary`),children:[`Manage `,(0,T.jsx)(p,{size:11})]}),children:L.length===0?(0,T.jsxs)(`div`,{className:`flex flex-col items-center justify-center py-14 gap-4 px-6`,children:[(0,T.jsx)(d,{size:32,className:`text-z-primary`,strokeWidth:1}),(0,T.jsxs)(`div`,{className:`text-center`,children:[(0,T.jsx)(`p`,{className:`text-sm font-bold text-z-muted`,children:`No collections yet`}),(0,T.jsx)(`p`,{className:`text-sm text-z-secondary mt-1`,children:`Create your first collection to start managing content.`})]}),(0,T.jsx)(m,{to:`/schema-builder`,className:`px-5 py-2.5 bg-z-accent hover:brightness-110 text-z-logo-text text-sm font-semibold transition-colors`,children:`+ Create Collection`})]}):(0,T.jsxs)(`div`,{children:[L.slice(0,8).map(t=>(0,T.jsxs)(m,{to:`/collections/${t.name}`,className:C(`flex items-center justify-between px-5 py-3 group transition-colors border-b last:border-b-0`,e===`dark`?`border-z-border hover:bg-z-hover`:`border-z-border hover:bg-[var(--z-bg-input)]`),children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,T.jsx)(`div`,{className:C(`w-6 h-6 flex items-center justify-center border text-z-secondary`,e===`dark`?`bg-z-hover border-z-border`:`bg-z-input border-z-border`),children:(0,T.jsx)(u,{size:11})}),(0,T.jsxs)(`div`,{children:[(0,T.jsx)(`span`,{className:C(`text-sm font-bold capitalize`,`text-z-primary`),children:t.label||t.name}),t.drafts&&(0,T.jsx)(`span`,{className:`ml-2 text-sm font-semibold text-amber-500 bg-amber-500/10 px-1.5 py-0.5`,children:`Drafts`})]})]}),(0,T.jsxs)(`div`,{className:`flex items-center gap-3`,children:[t.count!=null&&(0,T.jsx)(`span`,{className:`text-sm font-semibold tabular-nums text-z-secondary`,children:t.count.toLocaleString()}),(0,T.jsx)(p,{size:12,className:C(`transition-transform group-hover:translate-x-0.5`,e===`dark`?`text-z-primary`:`text-z-secondary`)})]})]},t.name)),L.length>8&&(0,T.jsx)(`div`,{className:C(`px-5 py-3 border-t`,`border-z-border`),children:(0,T.jsxs)(m,{to:`/schema-builder`,className:`text-sm font-semibold text-z-secondary hover:text-z-active-text transition-colors`,children:[`+`,L.length-8,` more collections →`]})}),(0,T.jsx)(`div`,{className:C(`px-5 py-3 border-t`,`border-z-border`),children:(0,T.jsxs)(m,{to:`/schema-builder`,className:`flex items-center gap-1.5 text-sm font-semibold text-z-secondary hover:text-z-active-text transition-colors w-fit`,children:[(0,T.jsx)(l,{size:11}),` New Collection`]})})]})}),(0,T.jsx)(E,{title:`Recent Activity`,icon:(0,T.jsx)(r,{size:13}),noPadding:!0,action:(0,T.jsxs)(m,{to:`/audit-log`,className:C(`text-sm font-semibold flex items-center gap-1 transition-colors`,e===`dark`?`text-z-secondary hover:text-z-secondary`:`text-z-muted hover:text-z-primary`),children:[`Full Log `,(0,T.jsx)(p,{size:11})]}),children:F.length===0?(0,T.jsxs)(`div`,{className:`flex flex-col items-center justify-center py-14 gap-2`,children:[(0,T.jsx)(r,{size:28,className:`text-z-primary`,strokeWidth:1}),(0,T.jsx)(`p`,{className:`text-sm text-z-secondary`,children:`No activity recorded yet.`})]}):(0,T.jsx)(`div`,{children:F.map(t=>{let n=t.userEmail||t.user?.email||`System`,r=n===`System`?`SY`:n.slice(0,2).toUpperCase(),i=n.charCodeAt(0)%k.length,a=(t.collectionName||t.collection||`system`).replace(/-/g,` `),o=t.status===`failed`;return(0,T.jsxs)(`div`,{onClick:()=>v(`/audit-log`),className:C(`flex items-center gap-3 px-5 py-3 cursor-pointer transition-colors border-b last:border-b-0`,e===`dark`?`border-z-border hover:bg-z-hover`:`border-z-border hover:bg-[var(--z-bg-input)]`,o&&(e===`dark`?`bg-rose-500/[0.03]`:`bg-rose-50/50`)),children:[(0,T.jsx)(`div`,{className:C(`w-6 h-6 flex items-center justify-center text-z-primary text-sm font-semibold shrink-0`,k[i]),children:r}),(0,T.jsx)(`span`,{className:C(`inline-flex px-1.5 py-0.5 text-sm font-semibold shrink-0`,o?`text-rose-400 bg-rose-500/10`:oe[t.action?.toLowerCase()]||`text-z-muted bg-z-panel`),children:t.action}),(0,T.jsx)(`span`,{className:C(`text-sm font-medium flex-1 truncate capitalize`,e===`dark`?`text-z-muted`:`text-z-secondary`),children:a}),(0,T.jsx)(`span`,{className:`text-sm text-z-secondary shrink-0 tabular-nums`,children:ae(t.timestamp)})]},t._id)})})})]}),(0,T.jsxs)(`div`,{className:`grid grid-cols-1 sm:grid-cols-2 gap-5`,children:[(0,T.jsx)(E,{title:`Who's Online`,icon:(0,T.jsx)(h,{size:13}),children:H.length===0?(0,T.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[(0,T.jsx)(`div`,{className:C(`w-2 h-2 rounded-full`,e===`dark`?`bg-z-border`:`bg-[var(--z-border)]`)}),(0,T.jsx)(`p`,{className:`text-sm text-z-secondary`,children:`No one else is online right now.`})]}):(0,T.jsxs)(`div`,{className:`flex items-center gap-4`,children:[(0,T.jsx)(`div`,{className:`flex items-center -space-x-2.5`,children:H.map((t,n)=>{let r=(t.email||t.userId||`Unknown`).split(`@`)[0],i=r.slice(0,2).toUpperCase(),a=t.collection?t.collection.replace(/-/g,` `):null,o=t.color||k[n%k.length],s=o.startsWith(`#`),c=a?`Editing ${a}`:`Browsing`;return(0,T.jsxs)(`div`,{className:`relative group cursor-default`,style:{zIndex:K===(t.userId||String(n))?50:H.length-n},onMouseEnter:()=>q(t.userId||String(n)),onMouseLeave:()=>q(null),children:[(0,T.jsx)(`div`,{className:`w-9 h-9 rounded-full flex items-center justify-center text-white text-xs font-bold transition-transform group-hover:-translate-y-1 group-hover:scale-110`,style:{backgroundColor:s?o:void 0,boxShadow:s?`0 0 0 3px ${o}55, 0 0 0 5px ${e===`dark`?`#18181b`:`#fff`}`:`0 0 0 3px var(--z-accent), 0 0 0 5px ${e===`dark`?`#18181b`:`#fff`}`},children:(0,T.jsx)(`span`,{className:C(!s&&o),children:i})}),(0,T.jsx)(`span`,{className:`absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 animate-pulse`,style:{backgroundColor:s?o:`var(--z-accent)`,borderColor:e===`dark`?`#18181b`:`#fff`}}),(0,T.jsx)(ne,{children:K===(t.userId||String(n))&&(0,T.jsxs)(g.div,{initial:{opacity:0,y:5,scale:.95},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:5,scale:.95},transition:{duration:.12},className:`absolute top-full mt-3 left-1/2 -translate-x-1/2 z-[999] whitespace-nowrap pointer-events-none`,children:[(0,T.jsxs)(`div`,{className:C(`px-3 py-2 rounded-xl shadow-2xl text-xs font-medium flex items-center gap-2`,e===`dark`?`bg-[#18181b] text-z-primary border border-z-border shadow-black/60`:`bg-white text-z-primary border shadow-lg`),children:[(0,T.jsx)(`div`,{className:`w-2.5 h-2.5 rounded-full shrink-0 animate-pulse`,style:{backgroundColor:s?o:`var(--z-accent)`}}),(0,T.jsx)(`span`,{className:`font-semibold`,children:r}),(0,T.jsx)(`span`,{className:`text-z-muted font-normal border-l border-z-border pl-2`,children:c})]}),(0,T.jsx)(`div`,{className:C(`absolute -top-1.5 left-1/2 -translate-x-1/2 w-3 h-3 rotate-45`,e===`dark`?`bg-[#18181b] border-l border-t border-z-border`:`bg-white border-l border-t border-gray-200`)})]})})]},t.userId||n)})}),(0,T.jsxs)(`span`,{className:`text-sm text-z-secondary font-medium`,children:[H.length,` `,H.length===1?`person`:`people`,` online`]})]})}),(0,T.jsx)(E,{title:`System Status`,icon:(0,T.jsx)(te,{size:13}),children:(0,T.jsxs)(`div`,{className:`space-y-3`,children:[[{label:`REST API`,ok:Z,detail:N==null?`Checking…`:`${N}ms latency`},{label:`Database`,ok:Q,detail:b?.database||`Unknown`},{label:`Memory`,ok:!0,detail:b?.memory?.heapUsed?`${b.memory.heapUsed} / ${b.memory.heapTotal} heap`:`—`}].map(e=>(0,T.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[e.ok?(0,T.jsx)(s,{size:13,className:`text-z-active-text shrink-0`}):(0,T.jsx)(i,{size:13,className:`text-rose-400 shrink-0`}),(0,T.jsx)(`span`,{className:C(`text-sm font-bold`,`text-z-secondary`),children:e.label})]}),(0,T.jsx)(`span`,{className:`text-sm text-z-secondary`,children:e.detail})]},e.label)),b?.uptime!=null&&(0,T.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,T.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,T.jsx)(t,{size:13,className:`text-z-secondary shrink-0`}),(0,T.jsx)(`span`,{className:C(`text-sm font-bold`,`text-z-secondary`),children:`Uptime`})]}),(0,T.jsx)(`span`,{className:`text-sm text-z-secondary`,children:O(b.uptime)})]})]})})]})]})})}export{M as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardBuilder-Cbi9Ddiu.js","names":[],"sources":["../../src/pages/dashboard/DashboardCard.tsx","../../src/pages/dashboard/WidgetErrorBoundary.tsx","../../src/pages/DashboardBuilder.tsx"],"sourcesContent":["import React from 'react'\nimport { cn } from '../../lib/utils'\nimport { useTheme } from '../../context/ThemeContext'\n\ninterface DashboardCardProps {\n title?: string\n icon?: React.ReactNode\n action?: React.ReactNode\n children: React.ReactNode\n className?: string\n noPadding?: boolean\n}\n\nexport const DashboardCard = React.memo(function DashboardCard({ title, icon, action, children, className, noPadding }: DashboardCardProps) {\n const { theme } = useTheme()\n\n return (\n <div\n className={cn(\n 'flex flex-col border z-panel backdrop-blur-md shadow-sm',\n className\n )}\n style={{ background: 'var(--z-bg-panel)', borderColor: 'var(--z-border)' }}\n >\n {(title || action) && (\n <div\n className=\"flex items-center justify-between px-5 py-4 border-b shrink-0 border-z-border\"\n >\n <div className=\"flex items-center gap-2.5\">\n {icon && (\n <span className=\"text-z-secondary\">\n {icon}\n </span>\n )}\n {title && (\n <h2 className=\"text-sm font-semibold text-z-secondary\">\n {title}\n </h2>\n )}\n </div>\n {action && <div>{action}</div>}\n </div>\n )}\n <div className={cn('flex-1 min-h-0', !noPadding && 'p-5')}>\n {children}\n </div>\n </div>\n )\n})\n","import React from 'react';\nimport { AlertTriangle } from 'lucide-react';\n\ninterface Props {\n children: React.ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n error?: Error;\n}\n\nexport class WidgetErrorBoundary extends React.Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error('Widget caught error:', error, errorInfo);\n }\n\n render() {\n if (this.state.hasError) {\n return (\n <div className=\"flex flex-col items-center justify-center p-4 border border-red-500/20 bg-red-500/10 rounded-none min-h-[100px] h-full\">\n <AlertTriangle className=\"text-red-500 mb-2\" size={24} />\n <span className=\"text-xs font-bold text-red-500\">Widget Error</span>\n <span className=\"text-sm text-red-400 mt-1 truncate max-w-full px-2\">\n {this.state.error?.message || 'Failed to render'}\n </span>\n </div>\n );\n }\n return this.props.children;\n }\n}","import React, { useEffect, useState, useCallback } from 'react'\nimport { Link, useNavigate } from 'react-router-dom'\nimport {\n Activity,\n ArrowRight,\n CheckCircle2,\n AlertTriangle,\n Clock,\n Database,\n FileText,\n History,\n ImageIcon,\n Layers,\n Loader2,\n Plus,\n Radio,\n Users,\n XCircle,\n Globe,\n KeyRound,\n Zap,\n} from 'lucide-react'\nimport { cn } from '../lib/utils'\nimport { useTheme } from '../context/ThemeContext'\nimport { PageHeader } from '../components/ui/PageHeader'\nimport { motion, AnimatePresence } from 'framer-motion'\nimport api from '../lib/api'\nimport { DashboardCard } from './dashboard/DashboardCard'\nimport { WidgetErrorBoundary } from './dashboard/WidgetErrorBoundary'\n\n// ── Types ──────────────────────────────────────────────────────────────────────\ninterface HealthData {\n status: string\n database: string\n version: string\n environment: string\n uptime: number\n memory: { heapUsed: string; heapTotal: string; rss: string }\n}\ninterface AuditEntry {\n _id: string\n action: string\n collection?: string\n collectionName?: string\n user?: { email?: string }\n userEmail?: string\n timestamp: string\n status?: string\n}\ninterface AuditStats {\n total: number\n failed: number\n success: number\n byAction: Record<string, number>\n}\ninterface CollectionInfo {\n name: string\n label?: string\n count?: number\n drafts?: boolean\n icon?: string\n}\ninterface PresenceMember {\n userId: string\n email?: string\n collection?: string\n documentId?: string\n color?: string\n}\n\n// ── Helpers ────────────────────────────────────────────────────────────────────\nfunction timeAgo(ts: string) {\n const secs = Math.floor((Date.now() - new Date(ts).getTime()) / 1000)\n if (secs < 60) return `${secs}s ago`\n if (secs < 3600) return `${Math.floor(secs / 60)}m ago`\n if (secs < 86400) return `${Math.floor(secs / 3600)}h ago`\n return `${Math.floor(secs / 86400)}d ago`\n}\n\nfunction uptimeStr(seconds: number) {\n if (seconds == null) return '—'\n const d = Math.floor(seconds / 86400)\n const h = Math.floor((seconds % 86400) / 3600)\n const m = Math.floor((seconds % 3600) / 60)\n const s = Math.floor(seconds % 60)\n if (d > 0) return `${d}d ${h}h ${m}m`\n if (h > 0) return `${h}h ${m}m ${s}s`\n return `${m}m ${s}s`\n}\n\nconst ACTION_PALETTE: Record<string, string> = {\n create: 'text-z-active-text bg-z-active-bg',\n update: 'text-z-active-text bg-z-active-bg',\n delete: 'text-rose-400 bg-rose-500/10',\n login: 'text-sky-400 bg-sky-500/10',\n logout: 'text-z-muted bg-z-panel',\n}\n\nconst INITIALS_COLORS = [\n 'bg-z-accent', 'bg-z-accent', 'bg-sky-600',\n 'bg-amber-600', 'bg-rose-600', 'bg-z-accent',\n]\n\n// ── Stat Pill ─────────────────────────────────────────────────────────────────\nconst StatPill = React.memo(function StatPill({\n label, value, sub, icon: Icon, accent, loading,\n}: {\n label: string; value: string; sub?: string\n icon: React.ElementType; accent?: 'purple' | 'emerald' | 'red'; loading?: boolean\n}) {\n const { theme } = useTheme()\n const accentClass =\n accent === 'purple' ? 'text-z-active-text' :\n accent === 'emerald' ? 'text-z-active-text' :\n accent === 'red' ? 'text-rose-400' :\n 'text-z-primary'\n\n return (\n <div className={cn(\n 'flex flex-col justify-between gap-2 p-5 border transition-colors z-panel backdrop-blur-md shadow-sm'\n )} style={{ background: 'var(--z-bg-panel)', borderColor: 'var(--z-border)' }}>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-z-secondary\">{label}</span>\n <Icon size={13} className=\"text-z-secondary\" />\n </div>\n <div>\n <span className={cn('text-2xl font-semibold leading-none tabular-nums', accentClass)}>\n {loading ? <span className=\"text-z-secondary text-base\">—</span> : value}\n </span>\n {sub && <p className=\"text-sm text-z-secondary mt-1\">{sub}</p>}\n </div>\n </div>\n )\n})\n\n// ── Environment Badge ──────────────────────────────────────────────────────────\nfunction EnvBadge({ env }: { env?: string }) {\n if (!env) return null\n const isProd = env === 'production'\n return (\n <span className={cn(\n 'inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-semibold border',\n isProd\n ? 'bg-rose-500/10 border-rose-500/20 text-rose-400'\n : 'bg-amber-500/10 border-amber-500/20 text-amber-400'\n )}>\n <span className={cn('w-1.5 h-1.5 rounded-full', isProd ? 'bg-rose-400 animate-pulse' : 'bg-amber-400')} />\n {env}\n </span>\n )\n}\n\n// ── Main Dashboard ─────────────────────────────────────────────────────────────\nexport default function Dashboard() {\n const { theme } = useTheme()\n const navigate = useNavigate()\n\n const [health, setHealth] = useState<HealthData | null>(null)\n const [latency, setLatency] = useState<number | null>(null)\n const [auditLogs, setAuditLogs] = useState<AuditEntry[]>([])\n const [auditStats, setAuditStats] = useState<AuditStats | null>(null)\n const [collections, setCollections] = useState<CollectionInfo[]>([])\n const [totalRecords, setTotalRecords] = useState<string>('—')\n const [mediaCount, setMediaCount] = useState<number | null>(null)\n const [membersOnline, setMembersOnline] = useState<PresenceMember[]>([])\n const [memberCount, setMemberCount] = useState<number | null>(null)\n const [hoveredUser, setHoveredUser] = useState<string | null>(null)\n const [loading, setLoading] = useState(true)\n\n // Make uptime active\n useEffect(() => {\n const timer = setInterval(() => {\n setHealth(prev => prev && prev.uptime != null ? { ...prev, uptime: prev.uptime + 1 } : prev)\n }, 1000)\n return () => clearInterval(timer)\n }, [])\n\n const fetchAll = useCallback(async () => {\n setLoading(true)\n try {\n const t0 = performance.now()\n const results = await Promise.allSettled([\n api.get('/system/health'), // 0\n api.get('/system/audit-logs?limit=8'), // 1\n api.get('/system/audit-logs/stats'), // 2\n api.get('/system/schemas'), // 3 — collections list\n api.get('/system/counts'), // 4\n api.get('/media?pageSize=1&sort=-createdAt'), // 5 — just for total count\n api.get('/presence'), // 6\n ])\n\n if (results[0].status === 'fulfilled') {\n setLatency(Math.round(performance.now() - t0))\n setHealth(results[0].value.data?.data || null)\n }\n if (results[1].status === 'fulfilled') {\n setAuditLogs(results[1].value.data?.data || [])\n }\n if (results[2].status === 'fulfilled') {\n setAuditStats(results[2].value.data?.data || null)\n }\n\n // Build collections from schemas + counts\n const counts: Record<string, number> = results[4].status === 'fulfilled'\n ? results[4].value.data?.data || {}\n : {}\n\n if (results[3].status === 'fulfilled') {\n const schemas = results[3].value.data?.data\n const cols: any[] = schemas?.collections || (Array.isArray(schemas) ? schemas : [])\n setCollections(cols.map((c: any) => ({\n name: c.slug || c.name,\n label: c.label || c.labels?.plural || c.slug || c.name,\n count: counts[c.slug || c.name],\n drafts: !!c.drafts,\n icon: c.admin?.icon,\n })))\n // total records = sum of all counts (excluding internal z_ collections)\n const total = Object.entries(counts)\n .filter(([k]) => !k.startsWith('z_'))\n .reduce((a, [, v]) => a + (v as number), 0)\n setTotalRecords(total > 0 ? total.toLocaleString() : '0')\n // member count from counts\n if (counts['users'] != null) setMemberCount(counts['users'])\n else if (counts['z_users'] != null) setMemberCount(counts['z_users'])\n else if (counts['members'] != null) setMemberCount(counts['members'])\n }\n\n if (results[5].status === 'fulfilled') {\n const pagination = results[5].value.data?.meta?.pagination\n setMediaCount(pagination?.total ?? results[5].value.data?.data?.length ?? null)\n }\n if (results[6].status === 'fulfilled') {\n setMembersOnline(results[6].value.data?.data || [])\n }\n } catch {\n // partial failures are fine\n } finally {\n setLoading(false)\n }\n }, [])\n\n useEffect(() => {\n fetchAll()\n // Dynamic ultra-low resource polling (every 3s) for presence and system health\n const dynamicInterval = setInterval(() => {\n const t0 = performance.now()\n api.get('/system/health').then(r => {\n setLatency(Math.round(performance.now() - t0))\n setHealth(prev => {\n const newData = r.data?.data || null\n // Keep our local ticking uptime if it's ahead or similar to prevent jitter\n if (prev && newData && newData.uptime) {\n return { ...newData, uptime: Math.max(prev.uptime, newData.uptime) }\n }\n return newData\n })\n }).catch(() => {})\n\n api.get('/presence').then(r => {\n const data = r.data?.data || []\n setMembersOnline(data)\n setMemberCount(prev => prev !== null ? Math.max(prev, data.length) : data.length)\n }).catch(() => {})\n }, 3000)\n\n return () => clearInterval(dynamicInterval)\n }, [fetchAll])\n\n // Global presence heartbeat so users just looking at the dashboard appear online\n useEffect(() => {\n const sendHeartbeat = () => {\n api.post('/presence/heartbeat', {\n collection: 'dashboard',\n documentId: 'dashboard',\n }).catch(() => {})\n }\n sendHeartbeat() // initial\n const interval = setInterval(sendHeartbeat, 30000)\n return () => clearInterval(interval)\n }, [])\n\n const isHealthOk = health?.status === 'ok'\n const isDbOk = health?.database === 'ok'\n\n if (loading) {\n return (\n <div className=\"h-full w-full flex flex-col items-center justify-center gap-4\">\n <Loader2 size={26} className=\"animate-spin text-z-secondary\" strokeWidth={1.5} />\n <p className=\"text-sm font-semibold text-z-secondary animate-pulse\">Loading…</p>\n </div>\n )\n }\n\n const quickActionGlass = theme === 'dark' \n ? 'bg-z-hover hover:bg-z-panel/[0.08] text-z-secondary border border-z-border shadow-sm' \n : 'bg-z-panel/65 backdrop-blur-[12px] hover:bg-z-panel/85 text-z-primary border border-z-border shadow-sm';\n\n const QUICK_ACTIONS = [\n { label: 'New Content', icon: Plus, path: '/collections', color: quickActionGlass },\n { label: 'Media Library', icon: ImageIcon, path: '/media', color: quickActionGlass },\n { label: 'API Explorer', icon: Zap, path: '/settings?tab=api-explorer', color: quickActionGlass },\n { label: 'Schema Builder', icon: Layers, path: '/schema-builder', color: quickActionGlass },\n { label: 'API Keys', icon: KeyRound, path: '/settings?tab=keys', color: quickActionGlass },\n { label: 'Audit Log', icon: History, path: '/audit-log', color: quickActionGlass },\n ]\n\n return (\n <div className=\"min-h-full transition-colors duration-300\">\n <div className=\"p-6 space-y-5 max-w-screen-2xl mx-auto\">\n\n {/* Page Header */}\n <PageHeader\n title=\"Dashboard\"\n description=\"System overview for your Zenith CMS workspace.\"\n icon={<Activity size={20} />}\n actions={\n <div className=\"flex items-center gap-2\">\n <EnvBadge env={health?.environment} />\n {health?.version && (\n <span className=\"text-sm font-semibold text-z-secondary\">\n v{health.version}\n </span>\n )}\n </div>\n }\n />\n\n {/* ── Row 1: Key stats ──────────────────────────────────────────────── */}\n <WidgetErrorBoundary>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3\">\n <StatPill label=\"Content Records\" value={totalRecords} icon={FileText} />\n <StatPill label=\"Collections\" value={String(collections.length || '—')} icon={Database} />\n <StatPill\n label=\"Media Files\"\n value={mediaCount != null ? mediaCount.toLocaleString() : '—'}\n icon={ImageIcon}\n />\n <StatPill\n label=\"Team Members\"\n value={memberCount != null ? String(memberCount) : '—'}\n sub={membersOnline.length > 0 ? `${membersOnline.length} online now` : undefined}\n icon={Users}\n />\n <StatPill\n label=\"API Latency\"\n value={latency != null ? `${latency}ms` : '—'}\n sub={health ? (isHealthOk ? 'System Operational' : 'System Degraded') : undefined}\n icon={Radio}\n accent={!health ? undefined : latency != null && latency < 300 ? 'emerald' : 'red'}\n />\n <StatPill\n label=\"Database\"\n value={health?.database || (health ? 'Connected' : '—')}\n sub={isDbOk ? 'Status: Operational' : 'Status: Degraded'}\n icon={Activity}\n accent={!health ? undefined : isDbOk ? 'emerald' : 'red'}\n />\n </div>\n </WidgetErrorBoundary>\n\n {/* ── Row 2: Audit stats ───────────────────────────────────────────── */}\n {auditStats && (\n <WidgetErrorBoundary>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3\">\n <StatPill label=\"Total Events\" value={auditStats.total.toLocaleString()} icon={History} />\n <StatPill label=\"Successful Ops\" value={auditStats.success.toLocaleString()} icon={CheckCircle2} accent=\"emerald\" />\n <StatPill label=\"Failed Ops\" value={auditStats.failed.toLocaleString()} icon={XCircle} accent={auditStats.failed > 0 ? 'red' : undefined} />\n </div>\n </WidgetErrorBoundary>\n )}\n\n {/* ── Row 3: Quick Actions ────────────────────────────────────────────── */}\n <DashboardCard title=\"Quick Actions\" icon={<Zap size={13} />}>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-2\">\n {QUICK_ACTIONS.map((a) => (\n <button\n key={a.label}\n onClick={() => navigate(a.path)}\n className={cn(\n 'flex items-center gap-2.5 px-3 py-2.5 text-sm font-semibold tracking-wide transition-all',\n a.color\n )}\n >\n <a.icon size={14} />\n {a.label}\n </button>\n ))}\n </div>\n </DashboardCard>\n\n {/* ── Row 4: Collections + Activity ──────────────────────────────────── */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-5\">\n\n {/* Collections */}\n <DashboardCard\n title=\"Collections\"\n icon={<Database size={13} />}\n noPadding\n action={\n <Link to=\"/schema-builder\" className={cn('text-sm font-semibold flex items-center gap-1 transition-colors', theme === 'dark' ? 'text-z-secondary hover:text-z-secondary' : 'text-z-muted hover:text-z-primary')}>\n Manage <ArrowRight size={11} />\n </Link>\n }\n >\n {collections.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-14 gap-4 px-6\">\n <Database size={32} className=\"text-z-primary\" strokeWidth={1} />\n <div className=\"text-center\">\n <p className=\"text-sm font-bold text-z-muted\">No collections yet</p>\n <p className=\"text-sm text-z-secondary mt-1\">Create your first collection to start managing content.</p>\n </div>\n <Link to=\"/schema-builder\" className=\"px-5 py-2.5 bg-z-accent hover:brightness-110 text-z-logo-text text-sm font-semibold transition-colors\">\n + Create Collection\n </Link>\n </div>\n ) : (\n <div>\n {collections.slice(0, 8).map((col) => (\n <Link\n key={col.name}\n to={`/collections/${col.name}`}\n className={cn(\n 'flex items-center justify-between px-5 py-3 group transition-colors border-b last:border-b-0',\n theme === 'dark' ? 'border-z-border hover:bg-z-hover' : 'border-z-border hover:bg-[var(--z-bg-input)]'\n )}\n >\n <div className=\"flex items-center gap-3\">\n <div className={cn('w-6 h-6 flex items-center justify-center border text-z-secondary', theme === 'dark' ? 'bg-z-hover border-z-border' : 'bg-z-input border-z-border')}>\n <Layers size={11} />\n </div>\n <div>\n <span className={cn('text-sm font-bold capitalize', theme === 'dark' ? 'text-z-primary' : 'text-z-primary')}>\n {col.label || col.name}\n </span>\n {col.drafts && (\n <span className=\"ml-2 text-sm font-semibold text-amber-500 bg-amber-500/10 px-1.5 py-0.5\">\n Drafts\n </span>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n {col.count != null && (\n <span className=\"text-sm font-semibold tabular-nums text-z-secondary\">\n {col.count.toLocaleString()}\n </span>\n )}\n <ArrowRight size={12} className={cn('transition-transform group-hover:translate-x-0.5', theme === 'dark' ? 'text-z-primary' : 'text-z-secondary')} />\n </div>\n </Link>\n ))}\n {collections.length > 8 && (\n <div className={cn('px-5 py-3 border-t', 'border-z-border')}>\n <Link to=\"/schema-builder\" className=\"text-sm font-semibold text-z-secondary hover:text-z-active-text transition-colors\">\n +{collections.length - 8} more collections →\n </Link>\n </div>\n )}\n <div className={cn('px-5 py-3 border-t', 'border-z-border')}>\n <Link to=\"/schema-builder\" className=\"flex items-center gap-1.5 text-sm font-semibold text-z-secondary hover:text-z-active-text transition-colors w-fit\">\n <Plus size={11} /> New Collection\n </Link>\n </div>\n </div>\n )}\n </DashboardCard>\n\n {/* Recent Activity */}\n <DashboardCard\n title=\"Recent Activity\"\n icon={<History size={13} />}\n noPadding\n action={\n <Link to=\"/audit-log\" className={cn('text-sm font-semibold flex items-center gap-1 transition-colors', theme === 'dark' ? 'text-z-secondary hover:text-z-secondary' : 'text-z-muted hover:text-z-primary')}>\n Full Log <ArrowRight size={11} />\n </Link>\n }\n >\n {auditLogs.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-14 gap-2\">\n <History size={28} className=\"text-z-primary\" strokeWidth={1} />\n <p className=\"text-sm text-z-secondary\">No activity recorded yet.</p>\n </div>\n ) : (\n <div>\n {auditLogs.map((log) => {\n const actor = log.userEmail || log.user?.email || 'System'\n const initials = actor === 'System' ? 'SY' : actor.slice(0, 2).toUpperCase()\n const colorIdx = actor.charCodeAt(0) % INITIALS_COLORS.length\n const collection = (log.collectionName || log.collection || 'system').replace(/-/g, ' ')\n const isFailed = log.status === 'failed'\n\n return (\n <div\n key={log._id}\n onClick={() => navigate('/audit-log')}\n className={cn(\n 'flex items-center gap-3 px-5 py-3 cursor-pointer transition-colors border-b last:border-b-0',\n theme === 'dark' ? 'border-z-border hover:bg-z-hover' : 'border-z-border hover:bg-[var(--z-bg-input)]',\n isFailed && (theme === 'dark' ? 'bg-rose-500/[0.03]' : 'bg-rose-50/50')\n )}\n >\n {/* Avatar */}\n <div className={cn('w-6 h-6 flex items-center justify-center text-z-primary text-sm font-semibold shrink-0', INITIALS_COLORS[colorIdx])}>\n {initials}\n </div>\n {/* Action badge */}\n <span className={cn(\n 'inline-flex px-1.5 py-0.5 text-sm font-semibold shrink-0',\n isFailed ? 'text-rose-400 bg-rose-500/10' : (ACTION_PALETTE[log.action?.toLowerCase()] || 'text-z-muted bg-z-panel')\n )}>\n {log.action}\n </span>\n {/* Collection */}\n <span className={cn('text-sm font-medium flex-1 truncate capitalize', theme === 'dark' ? 'text-z-muted' : 'text-z-secondary')}>\n {collection}\n </span>\n {/* Time */}\n <span className=\"text-sm text-z-secondary shrink-0 tabular-nums\">{timeAgo(log.timestamp)}</span>\n </div>\n )\n })}\n </div>\n )}\n </DashboardCard>\n\n </div>\n\n {/* ── Row 5: Who's online + API health strip ─────────────────────────── */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-5\">\n\n {/* Who's Online — Google Docs style */}\n <DashboardCard title=\"Who's Online\" icon={<Users size={13} />}>\n {membersOnline.length === 0 ? (\n <div className=\"flex items-center gap-2.5\">\n <div className={cn('w-2 h-2 rounded-full', theme === 'dark' ? 'bg-z-border' : 'bg-[var(--z-border)]')} />\n <p className=\"text-sm text-z-secondary\">No one else is online right now.</p>\n </div>\n ) : (\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center -space-x-2.5\">\n {membersOnline.map((m, i) => {\n const email = m.email || m.userId || 'Unknown'\n const name = email.split('@')[0]\n const initials = name.slice(0, 2).toUpperCase()\n const collection = m.collection\n ? m.collection.replace(/-/g, ' ')\n : null\n const avatarColor = m.color || INITIALS_COLORS[i % INITIALS_COLORS.length]\n const isHex = avatarColor.startsWith('#')\n const statusText = collection ? `Editing ${collection}` : 'Browsing'\n\n return (\n <div \n key={m.userId || i} \n className=\"relative group cursor-default\"\n style={{ zIndex: hoveredUser === (m.userId || String(i)) ? 50 : membersOnline.length - i }}\n onMouseEnter={() => setHoveredUser(m.userId || String(i))}\n onMouseLeave={() => setHoveredUser(null)}\n >\n {/* Avatar circle with user color border */}\n <div\n className=\"w-9 h-9 rounded-full flex items-center justify-center text-white text-xs font-bold transition-transform group-hover:-translate-y-1 group-hover:scale-110\"\n style={{\n backgroundColor: isHex ? avatarColor : undefined,\n boxShadow: isHex\n ? `0 0 0 3px ${avatarColor}55, 0 0 0 5px ${theme === 'dark' ? '#18181b' : '#fff'}`\n : `0 0 0 3px var(--z-accent), 0 0 0 5px ${theme === 'dark' ? '#18181b' : '#fff'}`,\n }}\n >\n <span className={cn(!isHex && avatarColor)}>{initials}</span>\n </div>\n {/* Pulsing active dot with user color */}\n <span \n className=\"absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 animate-pulse\"\n style={{\n backgroundColor: isHex ? avatarColor : 'var(--z-accent)',\n borderColor: theme === 'dark' ? '#18181b' : '#fff',\n }}\n />\n {/* Framer Motion tooltip */}\n <AnimatePresence>\n {hoveredUser === (m.userId || String(i)) && (\n <motion.div \n initial={{ opacity: 0, y: 5, scale: 0.95 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 5, scale: 0.95 }}\n transition={{ duration: 0.12 }}\n className=\"absolute top-full mt-3 left-1/2 -translate-x-1/2 z-[999] whitespace-nowrap pointer-events-none\"\n >\n <div className={cn(\n \"px-3 py-2 rounded-xl shadow-2xl text-xs font-medium flex items-center gap-2\",\n theme === 'dark' ? 'bg-[#18181b] text-z-primary border border-z-border shadow-black/60' : 'bg-white text-z-primary border shadow-lg'\n )}>\n <div \n className=\"w-2.5 h-2.5 rounded-full shrink-0 animate-pulse\"\n style={{ backgroundColor: isHex ? avatarColor : 'var(--z-accent)' }}\n />\n <span className=\"font-semibold\">{name}</span>\n <span className=\"text-z-muted font-normal border-l border-z-border pl-2\">{statusText}</span>\n </div>\n {/* Arrow */}\n <div className={cn(\n \"absolute -top-1.5 left-1/2 -translate-x-1/2 w-3 h-3 rotate-45\",\n theme === 'dark' ? 'bg-[#18181b] border-l border-t border-z-border' : 'bg-white border-l border-t border-gray-200'\n )} />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n })}\n </div>\n <span className=\"text-sm text-z-secondary font-medium\">\n {membersOnline.length} {membersOnline.length === 1 ? 'person' : 'people'} online\n </span>\n </div>\n )}\n </DashboardCard>\n\n {/* API / DB Health strip */}\n <DashboardCard title=\"System Status\" icon={<Globe size={13} />}>\n <div className=\"space-y-3\">\n {[\n {\n label: 'REST API',\n ok: isHealthOk,\n detail: latency != null ? `${latency}ms latency` : 'Checking…',\n },\n {\n label: 'Database',\n ok: isDbOk,\n detail: health?.database || 'Unknown',\n },\n {\n label: 'Memory',\n ok: true,\n detail: health?.memory?.heapUsed\n ? `${health.memory.heapUsed} / ${health.memory.heapTotal} heap`\n : '—',\n },\n ].map((item) => (\n <div key={item.label} className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {item.ok\n ? <CheckCircle2 size={13} className=\"text-z-active-text shrink-0\" />\n : <AlertTriangle size={13} className=\"text-rose-400 shrink-0\" />}\n <span className={cn('text-sm font-bold', 'text-z-secondary')}>{item.label}</span>\n </div>\n <span className=\"text-sm text-z-secondary\">{item.detail}</span>\n </div>\n ))}\n {health?.uptime != null && (\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Clock size={13} className=\"text-z-secondary shrink-0\" />\n <span className={cn('text-sm font-bold', 'text-z-secondary')}>Uptime</span>\n </div>\n <span className=\"text-sm text-z-secondary\">{uptimeStr(health.uptime)}</span>\n </div>\n )}\n </div>\n </DashboardCard>\n\n </div>\n\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardBuilder-Chjyqyb7.js","names":[],"sources":["../../src/pages/dashboard/DashboardCard.tsx","../../src/pages/dashboard/WidgetErrorBoundary.tsx","../../src/pages/DashboardBuilder.tsx"],"sourcesContent":["import React from 'react'\nimport { cn } from '../../lib/utils'\nimport { useTheme } from '../../context/ThemeContext'\n\ninterface DashboardCardProps {\n title?: string\n icon?: React.ReactNode\n action?: React.ReactNode\n children: React.ReactNode\n className?: string\n noPadding?: boolean\n}\n\nexport const DashboardCard = React.memo(function DashboardCard({ title, icon, action, children, className, noPadding }: DashboardCardProps) {\n const { theme } = useTheme()\n\n return (\n <div\n className={cn(\n 'flex flex-col border z-panel backdrop-blur-md shadow-sm',\n className\n )}\n style={{ background: 'var(--z-bg-panel)', borderColor: 'var(--z-border)' }}\n >\n {(title || action) && (\n <div\n className=\"flex items-center justify-between px-5 py-4 border-b shrink-0 border-z-border\"\n >\n <div className=\"flex items-center gap-2.5\">\n {icon && (\n <span className=\"text-z-secondary\">\n {icon}\n </span>\n )}\n {title && (\n <h2 className=\"text-sm font-semibold text-z-secondary\">\n {title}\n </h2>\n )}\n </div>\n {action && <div>{action}</div>}\n </div>\n )}\n <div className={cn('flex-1 min-h-0', !noPadding && 'p-5')}>\n {children}\n </div>\n </div>\n )\n})\n","import React from 'react';\nimport { AlertTriangle } from 'lucide-react';\n\ninterface Props {\n children: React.ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n error?: Error;\n}\n\nexport class WidgetErrorBoundary extends React.Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error('Widget caught error:', error, errorInfo);\n }\n\n render() {\n if (this.state.hasError) {\n return (\n <div className=\"flex flex-col items-center justify-center p-4 border border-red-500/20 bg-red-500/10 rounded-none min-h-[100px] h-full\">\n <AlertTriangle className=\"text-red-500 mb-2\" size={24} />\n <span className=\"text-xs font-bold text-red-500\">Widget Error</span>\n <span className=\"text-sm text-red-400 mt-1 truncate max-w-full px-2\">\n {this.state.error?.message || 'Failed to render'}\n </span>\n </div>\n );\n }\n return this.props.children;\n }\n}","import React, { useEffect, useState, useCallback } from 'react'\nimport { Link, useNavigate } from 'react-router-dom'\nimport {\n Activity,\n ArrowRight,\n CheckCircle2,\n AlertTriangle,\n Clock,\n Database,\n FileText,\n History,\n ImageIcon,\n Layers,\n Loader2,\n Plus,\n Radio,\n Users,\n XCircle,\n Globe,\n KeyRound,\n Zap,\n} from 'lucide-react'\nimport { cn } from '../lib/utils'\nimport { useTheme } from '../context/ThemeContext'\nimport { PageHeader } from '../components/ui/PageHeader'\nimport { motion, AnimatePresence } from 'framer-motion'\nimport api from '../lib/api'\nimport { DashboardCard } from './dashboard/DashboardCard'\nimport { WidgetErrorBoundary } from './dashboard/WidgetErrorBoundary'\n\n// ── Types ──────────────────────────────────────────────────────────────────────\ninterface HealthData {\n status: string\n database: string\n version: string\n environment: string\n uptime: number\n memory: { heapUsed: string; heapTotal: string; rss: string }\n}\ninterface AuditEntry {\n _id: string\n action: string\n collection?: string\n collectionName?: string\n user?: { email?: string }\n userEmail?: string\n timestamp: string\n status?: string\n}\ninterface AuditStats {\n total: number\n failed: number\n success: number\n byAction: Record<string, number>\n}\ninterface CollectionInfo {\n name: string\n label?: string\n count?: number\n drafts?: boolean\n icon?: string\n}\ninterface PresenceMember {\n userId: string\n email?: string\n collection?: string\n documentId?: string\n color?: string\n}\n\n// ── Helpers ────────────────────────────────────────────────────────────────────\nfunction timeAgo(ts: string) {\n const secs = Math.floor((Date.now() - new Date(ts).getTime()) / 1000)\n if (secs < 60) return `${secs}s ago`\n if (secs < 3600) return `${Math.floor(secs / 60)}m ago`\n if (secs < 86400) return `${Math.floor(secs / 3600)}h ago`\n return `${Math.floor(secs / 86400)}d ago`\n}\n\nfunction uptimeStr(seconds: number) {\n if (seconds == null) return '—'\n const d = Math.floor(seconds / 86400)\n const h = Math.floor((seconds % 86400) / 3600)\n const m = Math.floor((seconds % 3600) / 60)\n const s = Math.floor(seconds % 60)\n if (d > 0) return `${d}d ${h}h ${m}m`\n if (h > 0) return `${h}h ${m}m ${s}s`\n return `${m}m ${s}s`\n}\n\nconst ACTION_PALETTE: Record<string, string> = {\n create: 'text-z-active-text bg-z-active-bg',\n update: 'text-z-active-text bg-z-active-bg',\n delete: 'text-rose-400 bg-rose-500/10',\n login: 'text-sky-400 bg-sky-500/10',\n logout: 'text-z-muted bg-z-panel',\n}\n\nconst INITIALS_COLORS = [\n 'bg-z-accent', 'bg-z-accent', 'bg-sky-600',\n 'bg-amber-600', 'bg-rose-600', 'bg-z-accent',\n]\n\n// ── Stat Pill ─────────────────────────────────────────────────────────────────\nconst StatPill = React.memo(function StatPill({\n label, value, sub, icon: Icon, accent, loading,\n}: {\n label: string; value: string; sub?: string\n icon: React.ElementType; accent?: 'purple' | 'emerald' | 'red'; loading?: boolean\n}) {\n const { theme } = useTheme()\n const accentClass =\n accent === 'purple' ? 'text-z-active-text' :\n accent === 'emerald' ? 'text-z-active-text' :\n accent === 'red' ? 'text-rose-400' :\n 'text-z-primary'\n\n return (\n <div className={cn(\n 'flex flex-col justify-between gap-2 p-5 border transition-colors z-panel backdrop-blur-md shadow-sm'\n )} style={{ background: 'var(--z-bg-panel)', borderColor: 'var(--z-border)' }}>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-semibold text-z-secondary\">{label}</span>\n <Icon size={13} className=\"text-z-secondary\" />\n </div>\n <div>\n <span className={cn('text-2xl font-semibold leading-none tabular-nums', accentClass)}>\n {loading ? <span className=\"text-z-secondary text-base\">—</span> : value}\n </span>\n {sub && <p className=\"text-sm text-z-secondary mt-1\">{sub}</p>}\n </div>\n </div>\n )\n})\n\n// ── Environment Badge ──────────────────────────────────────────────────────────\nfunction EnvBadge({ env }: { env?: string }) {\n if (!env) return null\n const isProd = env === 'production'\n return (\n <span className={cn(\n 'inline-flex items-center gap-1.5 px-2.5 py-1 text-sm font-semibold border',\n isProd\n ? 'bg-rose-500/10 border-rose-500/20 text-rose-400'\n : 'bg-amber-500/10 border-amber-500/20 text-amber-400'\n )}>\n <span className={cn('w-1.5 h-1.5 rounded-full', isProd ? 'bg-rose-400 animate-pulse' : 'bg-amber-400')} />\n {env}\n </span>\n )\n}\n\n// ── Main Dashboard ─────────────────────────────────────────────────────────────\nexport default function Dashboard() {\n const { theme } = useTheme()\n const navigate = useNavigate()\n\n const [health, setHealth] = useState<HealthData | null>(null)\n const [latency, setLatency] = useState<number | null>(null)\n const [auditLogs, setAuditLogs] = useState<AuditEntry[]>([])\n const [auditStats, setAuditStats] = useState<AuditStats | null>(null)\n const [collections, setCollections] = useState<CollectionInfo[]>([])\n const [totalRecords, setTotalRecords] = useState<string>('—')\n const [mediaCount, setMediaCount] = useState<number | null>(null)\n const [membersOnline, setMembersOnline] = useState<PresenceMember[]>([])\n const [memberCount, setMemberCount] = useState<number | null>(null)\n const [hoveredUser, setHoveredUser] = useState<string | null>(null)\n const [loading, setLoading] = useState(true)\n\n // Make uptime active\n useEffect(() => {\n const timer = setInterval(() => {\n setHealth(prev => prev && prev.uptime != null ? { ...prev, uptime: prev.uptime + 1 } : prev)\n }, 1000)\n return () => clearInterval(timer)\n }, [])\n\n const fetchAll = useCallback(async () => {\n setLoading(true)\n try {\n const t0 = performance.now()\n const results = await Promise.allSettled([\n api.get('/system/health'), // 0\n api.get('/system/audit-logs?limit=8'), // 1\n api.get('/system/audit-logs/stats'), // 2\n api.get('/system/schemas'), // 3 — collections list\n api.get('/system/counts'), // 4\n api.get('/media?pageSize=1&sort=-createdAt'), // 5 — just for total count\n api.get('/presence'), // 6\n ])\n\n if (results[0].status === 'fulfilled') {\n setLatency(Math.round(performance.now() - t0))\n setHealth(results[0].value.data?.data || null)\n }\n if (results[1].status === 'fulfilled') {\n setAuditLogs(results[1].value.data?.data || [])\n }\n if (results[2].status === 'fulfilled') {\n setAuditStats(results[2].value.data?.data || null)\n }\n\n // Build collections from schemas + counts\n const counts: Record<string, number> = results[4].status === 'fulfilled'\n ? results[4].value.data?.data || {}\n : {}\n\n if (results[3].status === 'fulfilled') {\n const schemas = results[3].value.data?.data\n const cols: any[] = schemas?.collections || (Array.isArray(schemas) ? schemas : [])\n setCollections(cols.map((c: any) => ({\n name: c.slug || c.name,\n label: c.label || c.labels?.plural || c.slug || c.name,\n count: counts[c.slug || c.name],\n drafts: !!c.drafts,\n icon: c.admin?.icon,\n })))\n // total records = sum of all counts (excluding internal z_ collections)\n const total = Object.entries(counts)\n .filter(([k]) => !k.startsWith('z_'))\n .reduce((a, [, v]) => a + (v as number), 0)\n setTotalRecords(total > 0 ? total.toLocaleString() : '0')\n // member count from counts\n if (counts['users'] != null) setMemberCount(counts['users'])\n else if (counts['z_users'] != null) setMemberCount(counts['z_users'])\n else if (counts['members'] != null) setMemberCount(counts['members'])\n }\n\n if (results[5].status === 'fulfilled') {\n const pagination = results[5].value.data?.meta?.pagination\n setMediaCount(pagination?.total ?? results[5].value.data?.data?.length ?? null)\n }\n if (results[6].status === 'fulfilled') {\n setMembersOnline(results[6].value.data?.data || [])\n }\n } catch {\n // partial failures are fine\n } finally {\n setLoading(false)\n }\n }, [])\n\n useEffect(() => {\n fetchAll()\n // Dynamic ultra-low resource polling (every 3s) for presence and system health\n const dynamicInterval = setInterval(() => {\n const t0 = performance.now()\n api.get('/system/health').then(r => {\n setLatency(Math.round(performance.now() - t0))\n setHealth(prev => {\n const newData = r.data?.data || null\n // Keep our local ticking uptime if it's ahead or similar to prevent jitter\n if (prev && newData && newData.uptime) {\n return { ...newData, uptime: Math.max(prev.uptime, newData.uptime) }\n }\n return newData\n })\n }).catch(() => {})\n\n api.get('/presence').then(r => {\n const data = r.data?.data || []\n setMembersOnline(data)\n setMemberCount(prev => prev !== null ? Math.max(prev, data.length) : data.length)\n }).catch(() => {})\n }, 3000)\n\n return () => clearInterval(dynamicInterval)\n }, [fetchAll])\n\n // Global presence heartbeat so users just looking at the dashboard appear online\n useEffect(() => {\n const sendHeartbeat = () => {\n api.post('/presence/heartbeat', {\n collection: 'dashboard',\n documentId: 'dashboard',\n }).catch(() => {})\n }\n sendHeartbeat() // initial\n const interval = setInterval(sendHeartbeat, 30000)\n return () => clearInterval(interval)\n }, [])\n\n const isHealthOk = health?.status === 'ok'\n const isDbOk = health?.database === 'ok'\n\n if (loading) {\n return (\n <div className=\"h-full w-full flex flex-col items-center justify-center gap-4\">\n <Loader2 size={26} className=\"animate-spin text-z-secondary\" strokeWidth={1.5} />\n <p className=\"text-sm font-semibold text-z-secondary animate-pulse\">Loading…</p>\n </div>\n )\n }\n\n const quickActionGlass = theme === 'dark' \n ? 'bg-z-hover hover:bg-z-panel/[0.08] text-z-secondary border border-z-border shadow-sm' \n : 'bg-z-panel/65 backdrop-blur-[12px] hover:bg-z-panel/85 text-z-primary border border-z-border shadow-sm';\n\n const QUICK_ACTIONS = [\n { label: 'New Content', icon: Plus, path: '/collections', color: quickActionGlass },\n { label: 'Media Library', icon: ImageIcon, path: '/media', color: quickActionGlass },\n { label: 'API Explorer', icon: Zap, path: '/settings?tab=api-explorer', color: quickActionGlass },\n { label: 'Schema Builder', icon: Layers, path: '/schema-builder', color: quickActionGlass },\n { label: 'API Keys', icon: KeyRound, path: '/settings?tab=keys', color: quickActionGlass },\n { label: 'Audit Log', icon: History, path: '/audit-log', color: quickActionGlass },\n ]\n\n return (\n <div className=\"min-h-full transition-colors duration-300\">\n <div className=\"p-6 space-y-5 max-w-screen-2xl mx-auto\">\n\n {/* Page Header */}\n <PageHeader\n title=\"Dashboard\"\n description=\"System overview for your Zenith CMS workspace.\"\n icon={<Activity size={20} />}\n actions={\n <div className=\"flex items-center gap-2\">\n <EnvBadge env={health?.environment} />\n {health?.version && (\n <span className=\"text-sm font-semibold text-z-secondary\">\n v{health.version}\n </span>\n )}\n </div>\n }\n />\n\n {/* ── Row 1: Key stats ──────────────────────────────────────────────── */}\n <WidgetErrorBoundary>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3\">\n <StatPill label=\"Content Records\" value={totalRecords} icon={FileText} />\n <StatPill label=\"Collections\" value={String(collections.length || '—')} icon={Database} />\n <StatPill\n label=\"Media Files\"\n value={mediaCount != null ? mediaCount.toLocaleString() : '—'}\n icon={ImageIcon}\n />\n <StatPill\n label=\"Team Members\"\n value={memberCount != null ? String(memberCount) : '—'}\n sub={membersOnline.length > 0 ? `${membersOnline.length} online now` : undefined}\n icon={Users}\n />\n <StatPill\n label=\"API Latency\"\n value={latency != null ? `${latency}ms` : '—'}\n sub={health ? (isHealthOk ? 'System Operational' : 'System Degraded') : undefined}\n icon={Radio}\n accent={!health ? undefined : latency != null && latency < 300 ? 'emerald' : 'red'}\n />\n <StatPill\n label=\"Database\"\n value={health?.database || (health ? 'Connected' : '—')}\n sub={isDbOk ? 'Status: Operational' : 'Status: Degraded'}\n icon={Activity}\n accent={!health ? undefined : isDbOk ? 'emerald' : 'red'}\n />\n </div>\n </WidgetErrorBoundary>\n\n {/* ── Row 2: Audit stats ───────────────────────────────────────────── */}\n {auditStats && (\n <WidgetErrorBoundary>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3\">\n <StatPill label=\"Total Events\" value={auditStats.total.toLocaleString()} icon={History} />\n <StatPill label=\"Successful Ops\" value={auditStats.success.toLocaleString()} icon={CheckCircle2} accent=\"emerald\" />\n <StatPill label=\"Failed Ops\" value={auditStats.failed.toLocaleString()} icon={XCircle} accent={auditStats.failed > 0 ? 'red' : undefined} />\n </div>\n </WidgetErrorBoundary>\n )}\n\n {/* ── Row 3: Quick Actions ────────────────────────────────────────────── */}\n <DashboardCard title=\"Quick Actions\" icon={<Zap size={13} />}>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-2\">\n {QUICK_ACTIONS.map((a) => (\n <button\n key={a.label}\n onClick={() => navigate(a.path)}\n className={cn(\n 'flex items-center gap-2.5 px-3 py-2.5 text-sm font-semibold tracking-wide transition-all',\n a.color\n )}\n >\n <a.icon size={14} />\n {a.label}\n </button>\n ))}\n </div>\n </DashboardCard>\n\n {/* ── Row 4: Collections + Activity ──────────────────────────────────── */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-5\">\n\n {/* Collections */}\n <DashboardCard\n title=\"Collections\"\n icon={<Database size={13} />}\n noPadding\n action={\n <Link to=\"/schema-builder\" className={cn('text-sm font-semibold flex items-center gap-1 transition-colors', theme === 'dark' ? 'text-z-secondary hover:text-z-secondary' : 'text-z-muted hover:text-z-primary')}>\n Manage <ArrowRight size={11} />\n </Link>\n }\n >\n {collections.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-14 gap-4 px-6\">\n <Database size={32} className=\"text-z-primary\" strokeWidth={1} />\n <div className=\"text-center\">\n <p className=\"text-sm font-bold text-z-muted\">No collections yet</p>\n <p className=\"text-sm text-z-secondary mt-1\">Create your first collection to start managing content.</p>\n </div>\n <Link to=\"/schema-builder\" className=\"px-5 py-2.5 bg-z-accent hover:brightness-110 text-z-logo-text text-sm font-semibold transition-colors\">\n + Create Collection\n </Link>\n </div>\n ) : (\n <div>\n {collections.slice(0, 8).map((col) => (\n <Link\n key={col.name}\n to={`/collections/${col.name}`}\n className={cn(\n 'flex items-center justify-between px-5 py-3 group transition-colors border-b last:border-b-0',\n theme === 'dark' ? 'border-z-border hover:bg-z-hover' : 'border-z-border hover:bg-[var(--z-bg-input)]'\n )}\n >\n <div className=\"flex items-center gap-3\">\n <div className={cn('w-6 h-6 flex items-center justify-center border text-z-secondary', theme === 'dark' ? 'bg-z-hover border-z-border' : 'bg-z-input border-z-border')}>\n <Layers size={11} />\n </div>\n <div>\n <span className={cn('text-sm font-bold capitalize', theme === 'dark' ? 'text-z-primary' : 'text-z-primary')}>\n {col.label || col.name}\n </span>\n {col.drafts && (\n <span className=\"ml-2 text-sm font-semibold text-amber-500 bg-amber-500/10 px-1.5 py-0.5\">\n Drafts\n </span>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n {col.count != null && (\n <span className=\"text-sm font-semibold tabular-nums text-z-secondary\">\n {col.count.toLocaleString()}\n </span>\n )}\n <ArrowRight size={12} className={cn('transition-transform group-hover:translate-x-0.5', theme === 'dark' ? 'text-z-primary' : 'text-z-secondary')} />\n </div>\n </Link>\n ))}\n {collections.length > 8 && (\n <div className={cn('px-5 py-3 border-t', 'border-z-border')}>\n <Link to=\"/schema-builder\" className=\"text-sm font-semibold text-z-secondary hover:text-z-active-text transition-colors\">\n +{collections.length - 8} more collections →\n </Link>\n </div>\n )}\n <div className={cn('px-5 py-3 border-t', 'border-z-border')}>\n <Link to=\"/schema-builder\" className=\"flex items-center gap-1.5 text-sm font-semibold text-z-secondary hover:text-z-active-text transition-colors w-fit\">\n <Plus size={11} /> New Collection\n </Link>\n </div>\n </div>\n )}\n </DashboardCard>\n\n {/* Recent Activity */}\n <DashboardCard\n title=\"Recent Activity\"\n icon={<History size={13} />}\n noPadding\n action={\n <Link to=\"/audit-log\" className={cn('text-sm font-semibold flex items-center gap-1 transition-colors', theme === 'dark' ? 'text-z-secondary hover:text-z-secondary' : 'text-z-muted hover:text-z-primary')}>\n Full Log <ArrowRight size={11} />\n </Link>\n }\n >\n {auditLogs.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-14 gap-2\">\n <History size={28} className=\"text-z-primary\" strokeWidth={1} />\n <p className=\"text-sm text-z-secondary\">No activity recorded yet.</p>\n </div>\n ) : (\n <div>\n {auditLogs.map((log) => {\n const actor = log.userEmail || log.user?.email || 'System'\n const initials = actor === 'System' ? 'SY' : actor.slice(0, 2).toUpperCase()\n const colorIdx = actor.charCodeAt(0) % INITIALS_COLORS.length\n const collection = (log.collectionName || log.collection || 'system').replace(/-/g, ' ')\n const isFailed = log.status === 'failed'\n\n return (\n <div\n key={log._id}\n onClick={() => navigate('/audit-log')}\n className={cn(\n 'flex items-center gap-3 px-5 py-3 cursor-pointer transition-colors border-b last:border-b-0',\n theme === 'dark' ? 'border-z-border hover:bg-z-hover' : 'border-z-border hover:bg-[var(--z-bg-input)]',\n isFailed && (theme === 'dark' ? 'bg-rose-500/[0.03]' : 'bg-rose-50/50')\n )}\n >\n {/* Avatar */}\n <div className={cn('w-6 h-6 flex items-center justify-center text-z-primary text-sm font-semibold shrink-0', INITIALS_COLORS[colorIdx])}>\n {initials}\n </div>\n {/* Action badge */}\n <span className={cn(\n 'inline-flex px-1.5 py-0.5 text-sm font-semibold shrink-0',\n isFailed ? 'text-rose-400 bg-rose-500/10' : (ACTION_PALETTE[log.action?.toLowerCase()] || 'text-z-muted bg-z-panel')\n )}>\n {log.action}\n </span>\n {/* Collection */}\n <span className={cn('text-sm font-medium flex-1 truncate capitalize', theme === 'dark' ? 'text-z-muted' : 'text-z-secondary')}>\n {collection}\n </span>\n {/* Time */}\n <span className=\"text-sm text-z-secondary shrink-0 tabular-nums\">{timeAgo(log.timestamp)}</span>\n </div>\n )\n })}\n </div>\n )}\n </DashboardCard>\n\n </div>\n\n {/* ── Row 5: Who's online + API health strip ─────────────────────────── */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-5\">\n\n {/* Who's Online — Google Docs style */}\n <DashboardCard title=\"Who's Online\" icon={<Users size={13} />}>\n {membersOnline.length === 0 ? (\n <div className=\"flex items-center gap-2.5\">\n <div className={cn('w-2 h-2 rounded-full', theme === 'dark' ? 'bg-z-border' : 'bg-[var(--z-border)]')} />\n <p className=\"text-sm text-z-secondary\">No one else is online right now.</p>\n </div>\n ) : (\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center -space-x-2.5\">\n {membersOnline.map((m, i) => {\n const email = m.email || m.userId || 'Unknown'\n const name = email.split('@')[0]\n const initials = name.slice(0, 2).toUpperCase()\n const collection = m.collection\n ? m.collection.replace(/-/g, ' ')\n : null\n const avatarColor = m.color || INITIALS_COLORS[i % INITIALS_COLORS.length]\n const isHex = avatarColor.startsWith('#')\n const statusText = collection ? `Editing ${collection}` : 'Browsing'\n\n return (\n <div \n key={m.userId || i} \n className=\"relative group cursor-default\"\n style={{ zIndex: hoveredUser === (m.userId || String(i)) ? 50 : membersOnline.length - i }}\n onMouseEnter={() => setHoveredUser(m.userId || String(i))}\n onMouseLeave={() => setHoveredUser(null)}\n >\n {/* Avatar circle with user color border */}\n <div\n className=\"w-9 h-9 rounded-full flex items-center justify-center text-white text-xs font-bold transition-transform group-hover:-translate-y-1 group-hover:scale-110\"\n style={{\n backgroundColor: isHex ? avatarColor : undefined,\n boxShadow: isHex\n ? `0 0 0 3px ${avatarColor}55, 0 0 0 5px ${theme === 'dark' ? '#18181b' : '#fff'}`\n : `0 0 0 3px var(--z-accent), 0 0 0 5px ${theme === 'dark' ? '#18181b' : '#fff'}`,\n }}\n >\n <span className={cn(!isHex && avatarColor)}>{initials}</span>\n </div>\n {/* Pulsing active dot with user color */}\n <span \n className=\"absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 animate-pulse\"\n style={{\n backgroundColor: isHex ? avatarColor : 'var(--z-accent)',\n borderColor: theme === 'dark' ? '#18181b' : '#fff',\n }}\n />\n {/* Framer Motion tooltip */}\n <AnimatePresence>\n {hoveredUser === (m.userId || String(i)) && (\n <motion.div \n initial={{ opacity: 0, y: 5, scale: 0.95 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 5, scale: 0.95 }}\n transition={{ duration: 0.12 }}\n className=\"absolute top-full mt-3 left-1/2 -translate-x-1/2 z-[999] whitespace-nowrap pointer-events-none\"\n >\n <div className={cn(\n \"px-3 py-2 rounded-xl shadow-2xl text-xs font-medium flex items-center gap-2\",\n theme === 'dark' ? 'bg-[#18181b] text-z-primary border border-z-border shadow-black/60' : 'bg-white text-z-primary border shadow-lg'\n )}>\n <div \n className=\"w-2.5 h-2.5 rounded-full shrink-0 animate-pulse\"\n style={{ backgroundColor: isHex ? avatarColor : 'var(--z-accent)' }}\n />\n <span className=\"font-semibold\">{name}</span>\n <span className=\"text-z-muted font-normal border-l border-z-border pl-2\">{statusText}</span>\n </div>\n {/* Arrow */}\n <div className={cn(\n \"absolute -top-1.5 left-1/2 -translate-x-1/2 w-3 h-3 rotate-45\",\n theme === 'dark' ? 'bg-[#18181b] border-l border-t border-z-border' : 'bg-white border-l border-t border-gray-200'\n )} />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n })}\n </div>\n <span className=\"text-sm text-z-secondary font-medium\">\n {membersOnline.length} {membersOnline.length === 1 ? 'person' : 'people'} online\n </span>\n </div>\n )}\n </DashboardCard>\n\n {/* API / DB Health strip */}\n <DashboardCard title=\"System Status\" icon={<Globe size={13} />}>\n <div className=\"space-y-3\">\n {[\n {\n label: 'REST API',\n ok: isHealthOk,\n detail: latency != null ? `${latency}ms latency` : 'Checking…',\n },\n {\n label: 'Database',\n ok: isDbOk,\n detail: health?.database || 'Unknown',\n },\n {\n label: 'Memory',\n ok: true,\n detail: health?.memory?.heapUsed\n ? `${health.memory.heapUsed} / ${health.memory.heapTotal} heap`\n : '—',\n },\n ].map((item) => (\n <div key={item.label} className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {item.ok\n ? <CheckCircle2 size={13} className=\"text-z-active-text shrink-0\" />\n : <AlertTriangle size={13} className=\"text-rose-400 shrink-0\" />}\n <span className={cn('text-sm font-bold', 'text-z-secondary')}>{item.label}</span>\n </div>\n <span className=\"text-sm text-z-secondary\">{item.detail}</span>\n </div>\n ))}\n {health?.uptime != null && (\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Clock size={13} className=\"text-z-secondary shrink-0\" />\n <span className={cn('text-sm font-bold', 'text-z-secondary')}>Uptime</span>\n </div>\n <span className=\"text-sm text-z-secondary\">{uptimeStr(health.uptime)}</span>\n </div>\n )}\n </div>\n </DashboardCard>\n\n </div>\n\n </div>\n </div>\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{C as t,H as n,I as r,J as i,Jt as a,Kt as o,Ln as s,Wn as c,Wt as l,Z as u,cn as d,d as f,dt as p,fn as m,ir as h,l as g,rr as _,vr as v,wn as y,xr as b}from"./vendor-react-DQVTOTFO.js";import{a as x,o as S,t as C}from"./utils-fgvbH6CB.js";import{d as w,f as T,p as E}from"./index-ChcKY5Xe.js";var D=e(b(),1),O=v(),k=()=>{let{theme:e}=x(),v=e===`dark`,[b,k]=(0,D.useState)(``),[A,j]=(0,D.useState)(`installed`),[M,N]=(0,D.useState)(!0),[P,F]=(0,D.useState)([]),[I,L]=(0,D.useState)(null),[R,z]=(0,D.useState)(null),[B,V]=(0,D.useState)(!1),H=[{id:`cloudinary-asset-storage`,name:`Cloudinary Asset Storage`,author:`Cloudinary Inc.`,version:`2.1.0`,description:`Synchronizes Zenith's media manager to your Cloudinary cloud bucket with auto-responsive imaging and AVIF/WebP transformations.`,downloads:12482,verified:!0,icon:(0,O.jsx)(l,{size:16,className:`text-z-active-text`})},{id:`resend-email-engine`,name:`Resend Email Engine`,author:`Resend Team`,version:`1.4.3`,description:`Integrates high-performance transactional email flows via React Email templates and Resend SMTP nodes.`,downloads:8912,verified:!0,icon:(0,O.jsx)(p,{size:16,className:`text-z-secondary`})},{id:`algolia-search-matrix`,name:`Algolia Search Matrix`,author:`Algolia Labs`,version:`3.0.1`,description:`Automatically indexes newly published content collections to Algolia indexes for instantaneous keyword discovery.`,downloads:6104,verified:!0,icon:(0,O.jsx)(n,{size:16,className:`text-z-active-text`})},{id:`vercel-deploy-webhook`,name:`Vercel Deploy Webhook`,author:`Vercel Core`,version:`1.0.8`,description:`Dispatches static regeneration webhooks to Vercel/Netlify environments automatically whenever collection documents change.`,downloads:15221,verified:!0,icon:(0,O.jsx)(y,{size:16,className:`text-amber-500`})},{id:`openai-copilot-extension`,name:`OpenAI Copilot Extension`,author:`OpenAI Inc.`,version:`4.2.0`,description:`Power tip-tap rich text editors with inline content draft generation, grammar correction, and dynamic prompt templates.`,downloads:19823,verified:!0,icon:(0,O.jsx)(o,{size:16,className:`text-z-secondary`})},{id:`aws-s3-storage`,name:`AWS S3 Storage Driver`,author:`Amazon Web Services`,version:`3.1.5`,description:`Directly mount AWS S3 buckets as your primary asset storage with multi-region CDN routing and edge caching.`,downloads:42100,verified:!0,icon:(0,O.jsx)(l,{size:16,className:`text-amber-500`})},{id:`stripe-billing-portal`,name:`Stripe Billing Portal`,author:`Stripe, Inc.`,version:`1.2.9`,description:`Native integration for subscription management, checkout sessions, and automated invoice syncing for Zenith Commerce.`,downloads:27503,verified:!0,icon:(0,O.jsx)(y,{size:16,className:`text-indigo-400`})},{id:`posthog-analytics`,name:`PostHog Analytics`,author:`PostHog Team`,version:`2.0.0`,description:`Capture product telemetry, user session recordings, and feature flags directly into your Zenith dashboard.`,downloads:11200,verified:!0,icon:(0,O.jsx)(y,{size:16,className:`text-rose-500`})}],U=async()=>{try{F(((await S.get(`/system/plugins`)).data.data||[]).map(e=>({...e,id:e.id||e.name.toLowerCase().replace(/\s+/g,`-`),status:e.status||(e.disabled?`inactive`:`active`),type:e.author===`ROOT_KERNEL`?`core`:`third-party`,verified:e.author===`ROOT_KERNEL`,icon:W(e.name)})))}catch(e){console.error(`Failed to fetch plugins`,e),g.error(`Failed to load plugins`)}finally{N(!1)}},W=e=>{let t=e.toLowerCase();return t.includes(`cloudinary`)?(0,O.jsx)(l,{size:16,className:`text-z-active-text`}):t.includes(`resend`)||t.includes(`mail`)?(0,O.jsx)(p,{size:16,className:`text-z-secondary`}):t.includes(`algolia`)||t.includes(`search`)?(0,O.jsx)(n,{size:16,className:`text-z-active-text`}):t.includes(`vercel`)||t.includes(`deploy`)?(0,O.jsx)(y,{size:16,className:`text-amber-500`}):t.includes(`openai`)||t.includes(`copilot`)||t.includes(`ai`)?(0,O.jsx)(o,{size:16,className:`text-z-secondary`}):(0,O.jsx)(u,{size:16,className:`text-z-secondary`})},G=async(e,t)=>{if(!(!e||!t))try{let n=t===`inactive`;await S.post(`/system/plugins/${e}/${n?`enable`:`disable`}`),g.success(`Plugin ${n?`enabled`:`disabled`}`),U()}catch{g.error(`Failed to toggle plugin status`)}},K=async e=>{z(e)},q=e=>{navigator.clipboard.writeText(e),V(!0),setTimeout(()=>V(!1),2e3),g.success(`Copied to clipboard`)};(0,D.useEffect)(()=>{U()},[]);let J=[...H,...P.filter(e=>!H.some(t=>t.name.toLowerCase()===e.name.toLowerCase()))],Y=A===`installed`?P.filter(e=>e.name.toLowerCase().includes(b.toLowerCase())||e.description?.toLowerCase().includes(b.toLowerCase())):J.filter(e=>e.name.toLowerCase().includes(b.toLowerCase())||e.description?.toLowerCase().includes(b.toLowerCase())),X=e=>P.some(t=>t.name.toLowerCase()===e.toLowerCase());return M?(0,O.jsx)(`div`,{className:`flex justify-center items-center h-[calc(100vh-64px)]`,children:(0,O.jsx)(s,{size:32,className:`animate-spin text-z-secondary`})}):(0,O.jsxs)(`div`,{className:`flex flex-col h-[calc(100vh-64px)] overflow-hidden`,children:[(0,O.jsx)(E,{title:`Plugin System`,actions:(0,O.jsxs)(`div`,{className:`flex gap-2`,children:[(0,O.jsxs)(`div`,{className:C(`flex p-1 border`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:[(0,O.jsx)(`button`,{onClick:()=>j(`installed`),className:C(`px-4 py-1.5 text-sm font-semibold transition-colors`,A===`installed`?v?`bg-white/15 text-white`:`bg-z-primary text-z-inverse`:`text-z-secondary`),children:`Installed`}),(0,O.jsx)(`button`,{onClick:()=>j(`marketplace`),className:C(`px-4 py-1.5 text-sm font-semibold transition-colors`,A===`marketplace`?v?`bg-white/15 text-white`:`bg-z-primary text-z-inverse`:`text-z-secondary`),children:`Marketplace`})]}),(0,O.jsx)(`button`,{onClick:U,className:C(`px-3 border text-z-secondary hover:text-z-primary transition-colors`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:(0,O.jsx)(i,{size:14})})]})}),(0,O.jsxs)(`div`,{className:`flex-1 overflow-auto p-6 md:p-8 space-y-6`,children:[(0,O.jsx)(`div`,{className:`max-w-md`,children:(0,O.jsxs)(`div`,{className:C(`flex items-center gap-3 px-4 py-2 border transition-all`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:[(0,O.jsx)(n,{size:14,className:`text-z-secondary`}),(0,O.jsx)(`input`,{type:`text`,placeholder:A===`installed`?`Search installed plugins...`:`Search marketplace...`,value:b,onChange:e=>k(e.target.value),className:`bg-transparent border-none outline-none text-sm font-semibold text-z-secondary w-full placeholder:text-z-secondary`})]})}),Y.length===0?(0,O.jsxs)(`div`,{className:`text-center py-20 text-z-secondary`,children:[(0,O.jsx)(m,{size:32,className:`mx-auto mb-4 opacity-50`}),(0,O.jsx)(`p`,{className:`text-sm font-semibold`,children:`No plugins found`})]}):(0,O.jsx)(`div`,{className:`grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4`,children:(0,O.jsx)(h,{children:Y.map(e=>{let n=X(e.name);return(0,O.jsx)(_.div,{layout:!0,initial:{opacity:0,scale:.95},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.95},children:(0,O.jsx)(w,{className:`h-full flex flex-col`,children:(0,O.jsxs)(T,{className:`p-5 flex flex-col h-full gap-4`,children:[(0,O.jsx)(`div`,{className:`flex justify-between items-start`,children:(0,O.jsxs)(`div`,{className:`flex gap-3 items-center`,children:[(0,O.jsx)(`div`,{className:C(`w-8 h-8 flex items-center justify-center border`,v?`bg-app border-z-border`:`bg-z-input border-z-border`),children:e.icon}),(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`h3`,{className:`text-sm font-semibold`,children:e.name}),(0,O.jsxs)(`p`,{className:`text-sm text-z-secondary font-bold`,children:[e.author===`ROOT_KERNEL`?`Zenith Core`:e.author,` • v`,e.version]})]})]})}),(0,O.jsx)(`div`,{className:`flex-1`,children:(0,O.jsx)(`p`,{className:`text-sm text-z-secondary leading-relaxed line-clamp-2`,children:e.description})}),(0,O.jsxs)(`div`,{className:`flex items-center justify-between pt-4 border-t border-z-border`,children:[(0,O.jsxs)(`div`,{className:`flex items-center gap-3 text-sm text-z-secondary font-bold`,children:[e.verified&&(0,O.jsxs)(`span`,{className:`flex items-center gap-1`,children:[(0,O.jsx)(r,{size:10}),` Verified`]}),(0,O.jsxs)(`span`,{children:[e.downloads?.toLocaleString(),` DLs`]})]}),(0,O.jsx)(`div`,{className:`flex gap-2`,children:A===`installed`?(0,O.jsxs)(O.Fragment,{children:[(0,O.jsx)(`button`,{onClick:()=>G(e.id,e.status),className:C(`px-3 py-1.5 border text-sm font-semibold transition-colors`,e.status===`active`?`text-red-500 border-red-500/20 hover:bg-red-500/10`:`text-z-secondary border-z-border/20 hover:bg-z-panel`),children:e.status===`active`?`Disable`:`Enable`}),e.author!==`ROOT_KERNEL`&&(0,O.jsx)(`button`,{onClick:()=>g.error(`Core Protection: Delete via CLI`),className:`p-1.5 text-red-500/70 hover:bg-red-500/10`,children:(0,O.jsx)(t,{size:12})})]}):(0,O.jsxs)(`button`,{disabled:n||I!==null,onClick:()=>K(e),className:C(`px-4 py-1.5 border text-sm font-semibold transition-colors flex items-center gap-2`,n?`text-z-secondary border-z-border/20 cursor-not-allowed`:`text-z-primary bg-z-accent border-z-accent hover:bg-z-accent shadow-sm`),children:[I===e.id?(0,O.jsx)(s,{size:10,className:`animate-spin`}):(0,O.jsx)(c,{size:10}),n?`Installed`:`Install`]})})]})]})})},e.id||e.name)})})})]}),R&&(0,O.jsx)(`div`,{className:`fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4`,children:(0,O.jsxs)(`div`,{className:C(`max-w-2xl w-full border shadow-2xl flex flex-col`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:[(0,O.jsxs)(`div`,{className:`flex justify-between items-center p-5 border-b border-z-border`,children:[(0,O.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,O.jsx)(`div`,{className:`w-10 h-10 border border-z-border bg-z-input flex items-center justify-center`,children:R.icon}),(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`h3`,{className:`text-lg font-bold`,children:R.name}),(0,O.jsx)(`p`,{className:`text-xs text-z-secondary font-semibold`,children:`Official Installation Guide`})]})]}),(0,O.jsx)(`button`,{onClick:()=>z(null),className:`p-2 text-z-secondary hover:text-z-primary transition-colors`,children:(0,O.jsx)(f,{size:20})})]}),(0,O.jsxs)(`div`,{className:`p-6 space-y-6 overflow-y-auto max-h-[70vh] custom-editor-scrollbar`,children:[(0,O.jsx)(`p`,{className:`text-sm text-z-secondary leading-relaxed`,children:`To keep Zenith extremely lightweight and production-safe, plugins are installed via your terminal instead of the web dashboard. This guarantees your code repository matches your production environment.`}),(0,O.jsxs)(`div`,{className:`space-y-3`,children:[(0,O.jsxs)(`h4`,{className:`text-sm font-bold flex items-center gap-2 text-z-primary`,children:[(0,O.jsx)(`span`,{className:`w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full`,children:`1`}),`Install the NPM Package`]}),(0,O.jsxs)(`div`,{className:`relative group`,children:[(0,O.jsxs)(`pre`,{className:`bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap`,children:[`pnpm add @zenith-open/`,R.id||R.name.toLowerCase().replace(/\s+/g,`-`)]}),(0,O.jsx)(`button`,{onClick:()=>q(`pnpm add @zenith-open/${R.id||R.name.toLowerCase().replace(/\s+/g,`-`)}`),className:`absolute top-2 right-2 p-1.5 bg-[rgba(255,255,255,0.1)] text-white hover:bg-[rgba(255,255,255,0.2)] opacity-0 group-hover:opacity-100 transition-opacity`,children:B?(0,O.jsx)(d,{size:14}):(0,O.jsx)(a,{size:14})})]})]}),(0,O.jsxs)(`div`,{className:`space-y-3`,children:[(0,O.jsxs)(`h4`,{className:`text-sm font-bold flex items-center gap-2 text-z-primary`,children:[(0,O.jsx)(`span`,{className:`w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full`,children:`2`}),`Enable in your config`]}),(0,O.jsxs)(`p`,{className:`text-xs text-z-secondary`,children:[`Open `,(0,O.jsx)(`code`,{className:`bg-z-input px-1.5 py-0.5 text-z-primary font-mono border border-z-border`,children:`cms.config.ts`}),` and add the plugin to the array.`]}),(0,O.jsxs)(`div`,{className:`relative group`,children:[(0,O.jsx)(`pre`,{className:`bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap`,children:`import { zenithPlugin } from '@zenith-open/${R.id||R.name.toLowerCase().replace(/\s+/g,`-`)}'
|
|
1
|
+
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{C as t,H as n,I as r,J as i,Jt as a,Kt as o,Ln as s,Wn as c,Wt as l,Z as u,cn as d,d as f,dt as p,fn as m,ir as h,l as g,rr as _,vr as v,wn as y,xr as b}from"./vendor-react-DQVTOTFO.js";import{a as x,o as S,t as C}from"./utils-fgvbH6CB.js";import{d as w,f as T,p as E}from"./index-yE_3fruG.js";var D=e(b(),1),O=v(),k=()=>{let{theme:e}=x(),v=e===`dark`,[b,k]=(0,D.useState)(``),[A,j]=(0,D.useState)(`installed`),[M,N]=(0,D.useState)(!0),[P,F]=(0,D.useState)([]),[I,L]=(0,D.useState)(null),[R,z]=(0,D.useState)(null),[B,V]=(0,D.useState)(!1),H=[{id:`cloudinary-asset-storage`,name:`Cloudinary Asset Storage`,author:`Cloudinary Inc.`,version:`2.1.0`,description:`Synchronizes Zenith's media manager to your Cloudinary cloud bucket with auto-responsive imaging and AVIF/WebP transformations.`,downloads:12482,verified:!0,icon:(0,O.jsx)(l,{size:16,className:`text-z-active-text`})},{id:`resend-email-engine`,name:`Resend Email Engine`,author:`Resend Team`,version:`1.4.3`,description:`Integrates high-performance transactional email flows via React Email templates and Resend SMTP nodes.`,downloads:8912,verified:!0,icon:(0,O.jsx)(p,{size:16,className:`text-z-secondary`})},{id:`algolia-search-matrix`,name:`Algolia Search Matrix`,author:`Algolia Labs`,version:`3.0.1`,description:`Automatically indexes newly published content collections to Algolia indexes for instantaneous keyword discovery.`,downloads:6104,verified:!0,icon:(0,O.jsx)(n,{size:16,className:`text-z-active-text`})},{id:`vercel-deploy-webhook`,name:`Vercel Deploy Webhook`,author:`Vercel Core`,version:`1.0.8`,description:`Dispatches static regeneration webhooks to Vercel/Netlify environments automatically whenever collection documents change.`,downloads:15221,verified:!0,icon:(0,O.jsx)(y,{size:16,className:`text-amber-500`})},{id:`openai-copilot-extension`,name:`OpenAI Copilot Extension`,author:`OpenAI Inc.`,version:`4.2.0`,description:`Power tip-tap rich text editors with inline content draft generation, grammar correction, and dynamic prompt templates.`,downloads:19823,verified:!0,icon:(0,O.jsx)(o,{size:16,className:`text-z-secondary`})},{id:`aws-s3-storage`,name:`AWS S3 Storage Driver`,author:`Amazon Web Services`,version:`3.1.5`,description:`Directly mount AWS S3 buckets as your primary asset storage with multi-region CDN routing and edge caching.`,downloads:42100,verified:!0,icon:(0,O.jsx)(l,{size:16,className:`text-amber-500`})},{id:`stripe-billing-portal`,name:`Stripe Billing Portal`,author:`Stripe, Inc.`,version:`1.2.9`,description:`Native integration for subscription management, checkout sessions, and automated invoice syncing for Zenith Commerce.`,downloads:27503,verified:!0,icon:(0,O.jsx)(y,{size:16,className:`text-indigo-400`})},{id:`posthog-analytics`,name:`PostHog Analytics`,author:`PostHog Team`,version:`2.0.0`,description:`Capture product telemetry, user session recordings, and feature flags directly into your Zenith dashboard.`,downloads:11200,verified:!0,icon:(0,O.jsx)(y,{size:16,className:`text-rose-500`})}],U=async()=>{try{F(((await S.get(`/system/plugins`)).data.data||[]).map(e=>({...e,id:e.id||e.name.toLowerCase().replace(/\s+/g,`-`),status:e.status||(e.disabled?`inactive`:`active`),type:e.author===`ROOT_KERNEL`?`core`:`third-party`,verified:e.author===`ROOT_KERNEL`,icon:W(e.name)})))}catch(e){console.error(`Failed to fetch plugins`,e),g.error(`Failed to load plugins`)}finally{N(!1)}},W=e=>{let t=e.toLowerCase();return t.includes(`cloudinary`)?(0,O.jsx)(l,{size:16,className:`text-z-active-text`}):t.includes(`resend`)||t.includes(`mail`)?(0,O.jsx)(p,{size:16,className:`text-z-secondary`}):t.includes(`algolia`)||t.includes(`search`)?(0,O.jsx)(n,{size:16,className:`text-z-active-text`}):t.includes(`vercel`)||t.includes(`deploy`)?(0,O.jsx)(y,{size:16,className:`text-amber-500`}):t.includes(`openai`)||t.includes(`copilot`)||t.includes(`ai`)?(0,O.jsx)(o,{size:16,className:`text-z-secondary`}):(0,O.jsx)(u,{size:16,className:`text-z-secondary`})},G=async(e,t)=>{if(!(!e||!t))try{let n=t===`inactive`;await S.post(`/system/plugins/${e}/${n?`enable`:`disable`}`),g.success(`Plugin ${n?`enabled`:`disabled`}`),U()}catch{g.error(`Failed to toggle plugin status`)}},K=async e=>{z(e)},q=e=>{navigator.clipboard.writeText(e),V(!0),setTimeout(()=>V(!1),2e3),g.success(`Copied to clipboard`)};(0,D.useEffect)(()=>{U()},[]);let J=[...H,...P.filter(e=>!H.some(t=>t.name.toLowerCase()===e.name.toLowerCase()))],Y=A===`installed`?P.filter(e=>e.name.toLowerCase().includes(b.toLowerCase())||e.description?.toLowerCase().includes(b.toLowerCase())):J.filter(e=>e.name.toLowerCase().includes(b.toLowerCase())||e.description?.toLowerCase().includes(b.toLowerCase())),X=e=>P.some(t=>t.name.toLowerCase()===e.toLowerCase());return M?(0,O.jsx)(`div`,{className:`flex justify-center items-center h-[calc(100vh-64px)]`,children:(0,O.jsx)(s,{size:32,className:`animate-spin text-z-secondary`})}):(0,O.jsxs)(`div`,{className:`flex flex-col h-[calc(100vh-64px)] overflow-hidden`,children:[(0,O.jsx)(E,{title:`Plugin System`,actions:(0,O.jsxs)(`div`,{className:`flex gap-2`,children:[(0,O.jsxs)(`div`,{className:C(`flex p-1 border`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:[(0,O.jsx)(`button`,{onClick:()=>j(`installed`),className:C(`px-4 py-1.5 text-sm font-semibold transition-colors`,A===`installed`?v?`bg-white/15 text-white`:`bg-z-primary text-z-inverse`:`text-z-secondary`),children:`Installed`}),(0,O.jsx)(`button`,{onClick:()=>j(`marketplace`),className:C(`px-4 py-1.5 text-sm font-semibold transition-colors`,A===`marketplace`?v?`bg-white/15 text-white`:`bg-z-primary text-z-inverse`:`text-z-secondary`),children:`Marketplace`})]}),(0,O.jsx)(`button`,{onClick:U,className:C(`px-3 border text-z-secondary hover:text-z-primary transition-colors`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:(0,O.jsx)(i,{size:14})})]})}),(0,O.jsxs)(`div`,{className:`flex-1 overflow-auto p-6 md:p-8 space-y-6`,children:[(0,O.jsx)(`div`,{className:`max-w-md`,children:(0,O.jsxs)(`div`,{className:C(`flex items-center gap-3 px-4 py-2 border transition-all`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:[(0,O.jsx)(n,{size:14,className:`text-z-secondary`}),(0,O.jsx)(`input`,{type:`text`,placeholder:A===`installed`?`Search installed plugins...`:`Search marketplace...`,value:b,onChange:e=>k(e.target.value),className:`bg-transparent border-none outline-none text-sm font-semibold text-z-secondary w-full placeholder:text-z-secondary`})]})}),Y.length===0?(0,O.jsxs)(`div`,{className:`text-center py-20 text-z-secondary`,children:[(0,O.jsx)(m,{size:32,className:`mx-auto mb-4 opacity-50`}),(0,O.jsx)(`p`,{className:`text-sm font-semibold`,children:`No plugins found`})]}):(0,O.jsx)(`div`,{className:`grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4`,children:(0,O.jsx)(h,{children:Y.map(e=>{let n=X(e.name);return(0,O.jsx)(_.div,{layout:!0,initial:{opacity:0,scale:.95},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.95},children:(0,O.jsx)(w,{className:`h-full flex flex-col`,children:(0,O.jsxs)(T,{className:`p-5 flex flex-col h-full gap-4`,children:[(0,O.jsx)(`div`,{className:`flex justify-between items-start`,children:(0,O.jsxs)(`div`,{className:`flex gap-3 items-center`,children:[(0,O.jsx)(`div`,{className:C(`w-8 h-8 flex items-center justify-center border`,v?`bg-app border-z-border`:`bg-z-input border-z-border`),children:e.icon}),(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`h3`,{className:`text-sm font-semibold`,children:e.name}),(0,O.jsxs)(`p`,{className:`text-sm text-z-secondary font-bold`,children:[e.author===`ROOT_KERNEL`?`Zenith Core`:e.author,` • v`,e.version]})]})]})}),(0,O.jsx)(`div`,{className:`flex-1`,children:(0,O.jsx)(`p`,{className:`text-sm text-z-secondary leading-relaxed line-clamp-2`,children:e.description})}),(0,O.jsxs)(`div`,{className:`flex items-center justify-between pt-4 border-t border-z-border`,children:[(0,O.jsxs)(`div`,{className:`flex items-center gap-3 text-sm text-z-secondary font-bold`,children:[e.verified&&(0,O.jsxs)(`span`,{className:`flex items-center gap-1`,children:[(0,O.jsx)(r,{size:10}),` Verified`]}),(0,O.jsxs)(`span`,{children:[e.downloads?.toLocaleString(),` DLs`]})]}),(0,O.jsx)(`div`,{className:`flex gap-2`,children:A===`installed`?(0,O.jsxs)(O.Fragment,{children:[(0,O.jsx)(`button`,{onClick:()=>G(e.id,e.status),className:C(`px-3 py-1.5 border text-sm font-semibold transition-colors`,e.status===`active`?`text-red-500 border-red-500/20 hover:bg-red-500/10`:`text-z-secondary border-z-border/20 hover:bg-z-panel`),children:e.status===`active`?`Disable`:`Enable`}),e.author!==`ROOT_KERNEL`&&(0,O.jsx)(`button`,{onClick:()=>g.error(`Core Protection: Delete via CLI`),className:`p-1.5 text-red-500/70 hover:bg-red-500/10`,children:(0,O.jsx)(t,{size:12})})]}):(0,O.jsxs)(`button`,{disabled:n||I!==null,onClick:()=>K(e),className:C(`px-4 py-1.5 border text-sm font-semibold transition-colors flex items-center gap-2`,n?`text-z-secondary border-z-border/20 cursor-not-allowed`:`text-z-primary bg-z-accent border-z-accent hover:bg-z-accent shadow-sm`),children:[I===e.id?(0,O.jsx)(s,{size:10,className:`animate-spin`}):(0,O.jsx)(c,{size:10}),n?`Installed`:`Install`]})})]})]})})},e.id||e.name)})})})]}),R&&(0,O.jsx)(`div`,{className:`fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4`,children:(0,O.jsxs)(`div`,{className:C(`max-w-2xl w-full border shadow-2xl flex flex-col`,v?`bg-app border-z-border`:`bg-z-panel border-z-border`),children:[(0,O.jsxs)(`div`,{className:`flex justify-between items-center p-5 border-b border-z-border`,children:[(0,O.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,O.jsx)(`div`,{className:`w-10 h-10 border border-z-border bg-z-input flex items-center justify-center`,children:R.icon}),(0,O.jsxs)(`div`,{children:[(0,O.jsx)(`h3`,{className:`text-lg font-bold`,children:R.name}),(0,O.jsx)(`p`,{className:`text-xs text-z-secondary font-semibold`,children:`Official Installation Guide`})]})]}),(0,O.jsx)(`button`,{onClick:()=>z(null),className:`p-2 text-z-secondary hover:text-z-primary transition-colors`,children:(0,O.jsx)(f,{size:20})})]}),(0,O.jsxs)(`div`,{className:`p-6 space-y-6 overflow-y-auto max-h-[70vh] custom-editor-scrollbar`,children:[(0,O.jsx)(`p`,{className:`text-sm text-z-secondary leading-relaxed`,children:`To keep Zenith extremely lightweight and production-safe, plugins are installed via your terminal instead of the web dashboard. This guarantees your code repository matches your production environment.`}),(0,O.jsxs)(`div`,{className:`space-y-3`,children:[(0,O.jsxs)(`h4`,{className:`text-sm font-bold flex items-center gap-2 text-z-primary`,children:[(0,O.jsx)(`span`,{className:`w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full`,children:`1`}),`Install the NPM Package`]}),(0,O.jsxs)(`div`,{className:`relative group`,children:[(0,O.jsxs)(`pre`,{className:`bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap`,children:[`pnpm add @zenith-open/`,R.id||R.name.toLowerCase().replace(/\s+/g,`-`)]}),(0,O.jsx)(`button`,{onClick:()=>q(`pnpm add @zenith-open/${R.id||R.name.toLowerCase().replace(/\s+/g,`-`)}`),className:`absolute top-2 right-2 p-1.5 bg-[rgba(255,255,255,0.1)] text-white hover:bg-[rgba(255,255,255,0.2)] opacity-0 group-hover:opacity-100 transition-opacity`,children:B?(0,O.jsx)(d,{size:14}):(0,O.jsx)(a,{size:14})})]})]}),(0,O.jsxs)(`div`,{className:`space-y-3`,children:[(0,O.jsxs)(`h4`,{className:`text-sm font-bold flex items-center gap-2 text-z-primary`,children:[(0,O.jsx)(`span`,{className:`w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full`,children:`2`}),`Enable in your config`]}),(0,O.jsxs)(`p`,{className:`text-xs text-z-secondary`,children:[`Open `,(0,O.jsx)(`code`,{className:`bg-z-input px-1.5 py-0.5 text-z-primary font-mono border border-z-border`,children:`cms.config.ts`}),` and add the plugin to the array.`]}),(0,O.jsxs)(`div`,{className:`relative group`,children:[(0,O.jsx)(`pre`,{className:`bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap`,children:`import { zenithPlugin } from '@zenith-open/${R.id||R.name.toLowerCase().replace(/\s+/g,`-`)}'
|
|
2
2
|
|
|
3
3
|
export default buildConfig({
|
|
4
4
|
collections: [...],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginsPage-5YRpbP-N.js","names":[],"sources":["../../src/pages/PluginsPage.tsx"],"sourcesContent":["import { useState, useEffect } from 'react'\nimport {\n Puzzle,\n Settings,\n Trash2,\n ExternalLink,\n Plus,\n Zap,\n Search,\n Box,\n Loader2,\n RefreshCw,\n Cpu,\n Activity,\n ShieldCheck,\n Database,\n Mail,\n DownloadCloud,\n Code,\n Copy,\n Check,\n X\n} from 'lucide-react'\nimport { motion, AnimatePresence } from 'framer-motion'\nimport { cn } from '../lib/utils'\nimport { useTheme } from '../context/ThemeContext'\nimport toast from 'react-hot-toast'\nimport api from '../lib/api'\nimport { PageHeader } from '../components/ui/PageHeader'\nimport { Card, CardContent } from '../components/ui/Card'\n\ninterface PluginData {\n id?: string\n name: string\n author?: string\n enabled?: boolean\n version?: string\n description?: string\n status?: string\n type?: string\n verified?: boolean\n downloads?: number\n icon?: React.ReactNode\n}\n\nconst PluginsPage = () => {\n const { theme } = useTheme()\n const dark = theme === 'dark'\n const [searchQuery, setSearchQuery] = useState('')\n const [activeTab, setActiveTab] = useState<'installed' | 'marketplace'>('installed')\n const [loading, setLoading] = useState(true)\n const [plugins, setPlugins] = useState<PluginData[]>([])\n const [installingId, setInstallingId] = useState<string | null>(null)\n const [installGuidePlugin, setInstallGuidePlugin] = useState<any>(null)\n const [copiedCode, setCopiedCode] = useState(false)\n\n const MARKETPLACE_PLUGINS: PluginData[] = [\n {\n id: 'cloudinary-asset-storage',\n name: 'Cloudinary Asset Storage',\n author: 'Cloudinary Inc.',\n version: '2.1.0',\n description: \"Synchronizes Zenith's media manager to your Cloudinary cloud bucket with auto-responsive imaging and AVIF/WebP transformations.\",\n downloads: 12482,\n verified: true,\n icon: <Database size={16} className=\"text-z-active-text\" />,\n },\n {\n id: 'resend-email-engine',\n name: 'Resend Email Engine',\n author: 'Resend Team',\n version: '1.4.3',\n description: 'Integrates high-performance transactional email flows via React Email templates and Resend SMTP nodes.',\n downloads: 8912,\n verified: true,\n icon: <Mail size={16} className=\"text-z-secondary\" />,\n },\n {\n id: 'algolia-search-matrix',\n name: 'Algolia Search Matrix',\n author: 'Algolia Labs',\n version: '3.0.1',\n description: 'Automatically indexes newly published content collections to Algolia indexes for instantaneous keyword discovery.',\n downloads: 6104,\n verified: true,\n icon: <Search size={16} className=\"text-z-active-text\" />,\n },\n {\n id: 'vercel-deploy-webhook',\n name: 'Vercel Deploy Webhook',\n author: 'Vercel Core',\n version: '1.0.8',\n description: 'Dispatches static regeneration webhooks to Vercel/Netlify environments automatically whenever collection documents change.',\n downloads: 15221,\n verified: true,\n icon: <Activity size={16} className=\"text-amber-500\" />,\n },\n {\n id: 'openai-copilot-extension',\n name: 'OpenAI Copilot Extension',\n author: 'OpenAI Inc.',\n version: '4.2.0',\n description: 'Power tip-tap rich text editors with inline content draft generation, grammar correction, and dynamic prompt templates.',\n downloads: 19823,\n verified: true,\n icon: <Cpu size={16} className=\"text-z-secondary\" />,\n },\n {\n id: 'aws-s3-storage',\n name: 'AWS S3 Storage Driver',\n author: 'Amazon Web Services',\n version: '3.1.5',\n description: 'Directly mount AWS S3 buckets as your primary asset storage with multi-region CDN routing and edge caching.',\n downloads: 42100,\n verified: true,\n icon: <Database size={16} className=\"text-amber-500\" />,\n },\n {\n id: 'stripe-billing-portal',\n name: 'Stripe Billing Portal',\n author: 'Stripe, Inc.',\n version: '1.2.9',\n description: 'Native integration for subscription management, checkout sessions, and automated invoice syncing for Zenith Commerce.',\n downloads: 27503,\n verified: true,\n icon: <Activity size={16} className=\"text-indigo-400\" />,\n },\n {\n id: 'posthog-analytics',\n name: 'PostHog Analytics',\n author: 'PostHog Team',\n version: '2.0.0',\n description: 'Capture product telemetry, user session recordings, and feature flags directly into your Zenith dashboard.',\n downloads: 11200,\n verified: true,\n icon: <Activity size={16} className=\"text-rose-500\" />,\n },\n ]\n\n const fetchPlugins = async () => {\n try {\n const res = await api.get('/system/plugins')\n const realPlugins = res.data.data || []\n setPlugins(\n realPlugins.map((p: any) => ({\n ...p,\n id: p.id || p.name.toLowerCase().replace(/\\s+/g, '-'),\n status: p.status || (p.disabled ? 'inactive' : 'active'),\n type: p.author === 'ROOT_KERNEL' ? 'core' : 'third-party',\n verified: p.author === 'ROOT_KERNEL',\n icon: getPluginIcon(p.name),\n }))\n )\n } catch (err) {\n console.error('Failed to fetch plugins', err)\n toast.error('Failed to load plugins')\n } finally {\n setLoading(false)\n }\n }\n\n const getPluginIcon = (name: string) => {\n const lowercaseName = name.toLowerCase()\n if (lowercaseName.includes('cloudinary')) return <Database size={16} className=\"text-z-active-text\" />\n if (lowercaseName.includes('resend') || lowercaseName.includes('mail')) return <Mail size={16} className=\"text-z-secondary\" />\n if (lowercaseName.includes('algolia') || lowercaseName.includes('search')) return <Search size={16} className=\"text-z-active-text\" />\n if (lowercaseName.includes('vercel') || lowercaseName.includes('deploy')) return <Activity size={16} className=\"text-amber-500\" />\n if (lowercaseName.includes('openai') || lowercaseName.includes('copilot') || lowercaseName.includes('ai')) return <Cpu size={16} className=\"text-z-secondary\" />\n return <Puzzle size={16} className=\"text-z-secondary\" />\n }\n\n const togglePlugin = async (id: string | undefined, currentStatus: string | undefined) => {\n if (!id || !currentStatus) return\n try {\n const newEnabled = currentStatus === 'inactive'\n await api.post(`/system/plugins/${id}/${newEnabled ? 'enable' : 'disable'}`)\n toast.success(`Plugin ${newEnabled ? 'enabled' : 'disabled'}`)\n fetchPlugins()\n } catch {\n toast.error('Failed to toggle plugin status')\n }\n }\n\n const installMarketplacePlugin = async (mpPlugin: (typeof MARKETPLACE_PLUGINS)[0]) => {\n setInstallGuidePlugin(mpPlugin)\n }\n\n const copyToClipboard = (text: string) => {\n navigator.clipboard.writeText(text)\n setCopiedCode(true)\n setTimeout(() => setCopiedCode(false), 2000)\n toast.success('Copied to clipboard')\n }\n\n useEffect(() => {\n fetchPlugins()\n }, [])\n\n const mergedMarketplacePlugins = [\n ...MARKETPLACE_PLUGINS,\n ...plugins.filter(p => !MARKETPLACE_PLUGINS.some(mp => mp.name.toLowerCase() === p.name.toLowerCase()))\n ]\n\n const displayList = (activeTab === 'installed'\n ? plugins.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()) || p.description?.toLowerCase().includes(searchQuery.toLowerCase()))\n : mergedMarketplacePlugins.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()) || p.description?.toLowerCase().includes(searchQuery.toLowerCase()))\n ) as PluginData[]\n\n const isInstalled = (name: string) => plugins.some((p) => p.name.toLowerCase() === name.toLowerCase())\n\n if (loading) {\n return (\n <div className=\"flex justify-center items-center h-[calc(100vh-64px)]\">\n <Loader2 size={32} className=\"animate-spin text-z-secondary\" />\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col h-[calc(100vh-64px)] overflow-hidden\">\n <PageHeader\n title=\"Plugin System\"\n actions={\n <div className=\"flex gap-2\">\n <div className={cn('flex p-1 border', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}>\n <button\n onClick={() => setActiveTab('installed')}\n className={cn('px-4 py-1.5 text-sm font-semibold transition-colors', activeTab === 'installed' ? (dark ? 'bg-white/15 text-white' : 'bg-z-primary text-z-inverse') : 'text-z-secondary')}\n >\n Installed\n </button>\n <button\n onClick={() => setActiveTab('marketplace')}\n className={cn('px-4 py-1.5 text-sm font-semibold transition-colors', activeTab === 'marketplace' ? (dark ? 'bg-white/15 text-white' : 'bg-z-primary text-z-inverse') : 'text-z-secondary')}\n >\n Marketplace\n </button>\n </div>\n <button\n onClick={fetchPlugins}\n className={cn('px-3 border text-z-secondary hover:text-z-primary transition-colors', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}\n >\n <RefreshCw size={14} />\n </button>\n </div>\n }\n />\n\n <div className=\"flex-1 overflow-auto p-6 md:p-8 space-y-6\">\n <div className=\"max-w-md\">\n <div className={cn('flex items-center gap-3 px-4 py-2 border transition-all', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}>\n <Search size={14} className=\"text-z-secondary\" />\n <input\n type=\"text\"\n placeholder={activeTab === 'installed' ? 'Search installed plugins...' : 'Search marketplace...'}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"bg-transparent border-none outline-none text-sm font-semibold text-z-secondary w-full placeholder:text-z-secondary\"\n />\n </div>\n </div>\n\n {displayList.length === 0 ? (\n <div className=\"text-center py-20 text-z-secondary\">\n <Box size={32} className=\"mx-auto mb-4 opacity-50\" />\n <p className=\"text-sm font-semibold\">No plugins found</p>\n </div>\n ) : (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4\">\n <AnimatePresence>\n {displayList.map((plugin) => {\n const installed = isInstalled(plugin.name)\n return (\n <motion.div\n key={plugin.id || plugin.name}\n layout\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.95 }}\n >\n <Card className=\"h-full flex flex-col\">\n <CardContent className=\"p-5 flex flex-col h-full gap-4\">\n <div className=\"flex justify-between items-start\">\n <div className=\"flex gap-3 items-center\">\n <div className={cn('w-8 h-8 flex items-center justify-center border', dark ? 'bg-app border-z-border' : 'bg-z-input border-z-border')}>\n {plugin.icon}\n </div>\n <div>\n <h3 className=\"text-sm font-semibold\">{plugin.name}</h3>\n <p className=\"text-sm text-z-secondary font-bold\">\n {plugin.author === 'ROOT_KERNEL' ? 'Zenith Core' : plugin.author} • v{plugin.version}\n </p>\n </div>\n </div>\n </div>\n\n {/* Minimalist features instead of descriptive text */}\n <div className=\"flex-1\">\n <p className=\"text-sm text-z-secondary leading-relaxed line-clamp-2\">\n {plugin.description}\n </p>\n </div>\n\n <div className=\"flex items-center justify-between pt-4 border-t border-z-border\">\n <div className=\"flex items-center gap-3 text-sm text-z-secondary font-bold\">\n {plugin.verified && <span className=\"flex items-center gap-1\"><ShieldCheck size={10} /> Verified</span>}\n <span>{plugin.downloads?.toLocaleString()} DLs</span>\n </div>\n\n <div className=\"flex gap-2\">\n {activeTab === 'installed' ? (\n <>\n <button\n onClick={() => togglePlugin(plugin.id, plugin.status)}\n className={cn('px-3 py-1.5 border text-sm font-semibold transition-colors', plugin.status === 'active' ? 'text-red-500 border-red-500/20 hover:bg-red-500/10' : 'text-z-secondary border-z-border/20 hover:bg-z-panel')}\n >\n {plugin.status === 'active' ? 'Disable' : 'Enable'}\n </button>\n {plugin.author !== 'ROOT_KERNEL' && (\n <button onClick={() => toast.error('Core Protection: Delete via CLI')} className=\"p-1.5 text-red-500/70 hover:bg-red-500/10\">\n <Trash2 size={12} />\n </button>\n )}\n </>\n ) : (\n <button\n disabled={installed || installingId !== null}\n onClick={() => installMarketplacePlugin(plugin as any)}\n className={cn('px-4 py-1.5 border text-sm font-semibold transition-colors flex items-center gap-2', installed ? 'text-z-secondary border-z-border/20 cursor-not-allowed' : 'text-z-primary bg-z-accent border-z-accent hover:bg-z-accent shadow-sm')}\n >\n {installingId === plugin.id ? <Loader2 size={10} className=\"animate-spin\" /> : <DownloadCloud size={10} />}\n {installed ? 'Installed' : 'Install'}\n </button>\n )}\n </div>\n </div>\n </CardContent>\n </Card>\n </motion.div>\n )\n })}\n </AnimatePresence>\n </div>\n )}\n </div>\n\n {/* Plugin Install Guide Modal */}\n {installGuidePlugin && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4\">\n <div className={cn('max-w-2xl w-full border shadow-2xl flex flex-col', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}>\n <div className=\"flex justify-between items-center p-5 border-b border-z-border\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 border border-z-border bg-z-input flex items-center justify-center\">\n {installGuidePlugin.icon}\n </div>\n <div>\n <h3 className=\"text-lg font-bold\">{installGuidePlugin.name}</h3>\n <p className=\"text-xs text-z-secondary font-semibold\">Official Installation Guide</p>\n </div>\n </div>\n <button onClick={() => setInstallGuidePlugin(null)} className=\"p-2 text-z-secondary hover:text-z-primary transition-colors\">\n <X size={20} />\n </button>\n </div>\n \n <div className=\"p-6 space-y-6 overflow-y-auto max-h-[70vh] custom-editor-scrollbar\">\n <p className=\"text-sm text-z-secondary leading-relaxed\">\n To keep Zenith extremely lightweight and production-safe, plugins are installed via your terminal instead of the web dashboard. This guarantees your code repository matches your production environment.\n </p>\n\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-bold flex items-center gap-2 text-z-primary\">\n <span className=\"w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full\">1</span> \n Install the NPM Package\n </h4>\n <div className=\"relative group\">\n <pre className=\"bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap\">\n pnpm add @zenith-open/{installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}\n </pre>\n <button \n onClick={() => copyToClipboard(`pnpm add @zenith-open/${installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}`)}\n className=\"absolute top-2 right-2 p-1.5 bg-[rgba(255,255,255,0.1)] text-white hover:bg-[rgba(255,255,255,0.2)] opacity-0 group-hover:opacity-100 transition-opacity\"\n >\n {copiedCode ? <Check size={14} /> : <Copy size={14} />}\n </button>\n </div>\n </div>\n\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-bold flex items-center gap-2 text-z-primary\">\n <span className=\"w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full\">2</span> \n Enable in your config\n </h4>\n <p className=\"text-xs text-z-secondary\">Open <code className=\"bg-z-input px-1.5 py-0.5 text-z-primary font-mono border border-z-border\">cms.config.ts</code> and add the plugin to the array.</p>\n <div className=\"relative group\">\n <pre className=\"bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap\">\n{`import { zenithPlugin } from '@zenith-open/${installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}'\n\nexport default buildConfig({\n collections: [...],\n plugins: [\n zenithPlugin({\n // Provide any necessary config options here\n })\n ]\n})`}\n </pre>\n <button \n onClick={() => copyToClipboard(`import { zenithPlugin } from '@zenith-open/${installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}'\\n\\nexport default buildConfig({\\n collections: [...],\\n plugins: [\\n zenithPlugin({\\n // Provide any necessary config options here\\n })\\n ]\\n})`)}\n className=\"absolute top-2 right-2 p-1.5 bg-[rgba(255,255,255,0.1)] text-white hover:bg-[rgba(255,255,255,0.2)] opacity-0 group-hover:opacity-100 transition-opacity\"\n >\n {copiedCode ? <Check size={14} /> : <Copy size={14} />}\n </button>\n </div>\n </div>\n\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-bold flex items-center gap-2 text-z-primary\">\n <span className=\"w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full\">3</span> \n Restart the Server\n </h4>\n <p className=\"text-xs text-z-secondary\">Restart your development server and the plugin will be instantly active!</p>\n </div>\n\n </div>\n <div className=\"p-4 border-t border-z-border bg-z-input flex justify-end\">\n <button \n onClick={() => setInstallGuidePlugin(null)}\n className=\"px-6 py-2 bg-z-panel text-z-primary border border-z-border text-sm font-bold shadow-sm hover:bg-z-input transition-colors\"\n >\n Done\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport default PluginsPage\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PluginsPage-DAR4Fsz-.js","names":[],"sources":["../../src/pages/PluginsPage.tsx"],"sourcesContent":["import { useState, useEffect } from 'react'\nimport {\n Puzzle,\n Settings,\n Trash2,\n ExternalLink,\n Plus,\n Zap,\n Search,\n Box,\n Loader2,\n RefreshCw,\n Cpu,\n Activity,\n ShieldCheck,\n Database,\n Mail,\n DownloadCloud,\n Code,\n Copy,\n Check,\n X\n} from 'lucide-react'\nimport { motion, AnimatePresence } from 'framer-motion'\nimport { cn } from '../lib/utils'\nimport { useTheme } from '../context/ThemeContext'\nimport toast from 'react-hot-toast'\nimport api from '../lib/api'\nimport { PageHeader } from '../components/ui/PageHeader'\nimport { Card, CardContent } from '../components/ui/Card'\n\ninterface PluginData {\n id?: string\n name: string\n author?: string\n enabled?: boolean\n version?: string\n description?: string\n status?: string\n type?: string\n verified?: boolean\n downloads?: number\n icon?: React.ReactNode\n}\n\nconst PluginsPage = () => {\n const { theme } = useTheme()\n const dark = theme === 'dark'\n const [searchQuery, setSearchQuery] = useState('')\n const [activeTab, setActiveTab] = useState<'installed' | 'marketplace'>('installed')\n const [loading, setLoading] = useState(true)\n const [plugins, setPlugins] = useState<PluginData[]>([])\n const [installingId, setInstallingId] = useState<string | null>(null)\n const [installGuidePlugin, setInstallGuidePlugin] = useState<any>(null)\n const [copiedCode, setCopiedCode] = useState(false)\n\n const MARKETPLACE_PLUGINS: PluginData[] = [\n {\n id: 'cloudinary-asset-storage',\n name: 'Cloudinary Asset Storage',\n author: 'Cloudinary Inc.',\n version: '2.1.0',\n description: \"Synchronizes Zenith's media manager to your Cloudinary cloud bucket with auto-responsive imaging and AVIF/WebP transformations.\",\n downloads: 12482,\n verified: true,\n icon: <Database size={16} className=\"text-z-active-text\" />,\n },\n {\n id: 'resend-email-engine',\n name: 'Resend Email Engine',\n author: 'Resend Team',\n version: '1.4.3',\n description: 'Integrates high-performance transactional email flows via React Email templates and Resend SMTP nodes.',\n downloads: 8912,\n verified: true,\n icon: <Mail size={16} className=\"text-z-secondary\" />,\n },\n {\n id: 'algolia-search-matrix',\n name: 'Algolia Search Matrix',\n author: 'Algolia Labs',\n version: '3.0.1',\n description: 'Automatically indexes newly published content collections to Algolia indexes for instantaneous keyword discovery.',\n downloads: 6104,\n verified: true,\n icon: <Search size={16} className=\"text-z-active-text\" />,\n },\n {\n id: 'vercel-deploy-webhook',\n name: 'Vercel Deploy Webhook',\n author: 'Vercel Core',\n version: '1.0.8',\n description: 'Dispatches static regeneration webhooks to Vercel/Netlify environments automatically whenever collection documents change.',\n downloads: 15221,\n verified: true,\n icon: <Activity size={16} className=\"text-amber-500\" />,\n },\n {\n id: 'openai-copilot-extension',\n name: 'OpenAI Copilot Extension',\n author: 'OpenAI Inc.',\n version: '4.2.0',\n description: 'Power tip-tap rich text editors with inline content draft generation, grammar correction, and dynamic prompt templates.',\n downloads: 19823,\n verified: true,\n icon: <Cpu size={16} className=\"text-z-secondary\" />,\n },\n {\n id: 'aws-s3-storage',\n name: 'AWS S3 Storage Driver',\n author: 'Amazon Web Services',\n version: '3.1.5',\n description: 'Directly mount AWS S3 buckets as your primary asset storage with multi-region CDN routing and edge caching.',\n downloads: 42100,\n verified: true,\n icon: <Database size={16} className=\"text-amber-500\" />,\n },\n {\n id: 'stripe-billing-portal',\n name: 'Stripe Billing Portal',\n author: 'Stripe, Inc.',\n version: '1.2.9',\n description: 'Native integration for subscription management, checkout sessions, and automated invoice syncing for Zenith Commerce.',\n downloads: 27503,\n verified: true,\n icon: <Activity size={16} className=\"text-indigo-400\" />,\n },\n {\n id: 'posthog-analytics',\n name: 'PostHog Analytics',\n author: 'PostHog Team',\n version: '2.0.0',\n description: 'Capture product telemetry, user session recordings, and feature flags directly into your Zenith dashboard.',\n downloads: 11200,\n verified: true,\n icon: <Activity size={16} className=\"text-rose-500\" />,\n },\n ]\n\n const fetchPlugins = async () => {\n try {\n const res = await api.get('/system/plugins')\n const realPlugins = res.data.data || []\n setPlugins(\n realPlugins.map((p: any) => ({\n ...p,\n id: p.id || p.name.toLowerCase().replace(/\\s+/g, '-'),\n status: p.status || (p.disabled ? 'inactive' : 'active'),\n type: p.author === 'ROOT_KERNEL' ? 'core' : 'third-party',\n verified: p.author === 'ROOT_KERNEL',\n icon: getPluginIcon(p.name),\n }))\n )\n } catch (err) {\n console.error('Failed to fetch plugins', err)\n toast.error('Failed to load plugins')\n } finally {\n setLoading(false)\n }\n }\n\n const getPluginIcon = (name: string) => {\n const lowercaseName = name.toLowerCase()\n if (lowercaseName.includes('cloudinary')) return <Database size={16} className=\"text-z-active-text\" />\n if (lowercaseName.includes('resend') || lowercaseName.includes('mail')) return <Mail size={16} className=\"text-z-secondary\" />\n if (lowercaseName.includes('algolia') || lowercaseName.includes('search')) return <Search size={16} className=\"text-z-active-text\" />\n if (lowercaseName.includes('vercel') || lowercaseName.includes('deploy')) return <Activity size={16} className=\"text-amber-500\" />\n if (lowercaseName.includes('openai') || lowercaseName.includes('copilot') || lowercaseName.includes('ai')) return <Cpu size={16} className=\"text-z-secondary\" />\n return <Puzzle size={16} className=\"text-z-secondary\" />\n }\n\n const togglePlugin = async (id: string | undefined, currentStatus: string | undefined) => {\n if (!id || !currentStatus) return\n try {\n const newEnabled = currentStatus === 'inactive'\n await api.post(`/system/plugins/${id}/${newEnabled ? 'enable' : 'disable'}`)\n toast.success(`Plugin ${newEnabled ? 'enabled' : 'disabled'}`)\n fetchPlugins()\n } catch {\n toast.error('Failed to toggle plugin status')\n }\n }\n\n const installMarketplacePlugin = async (mpPlugin: (typeof MARKETPLACE_PLUGINS)[0]) => {\n setInstallGuidePlugin(mpPlugin)\n }\n\n const copyToClipboard = (text: string) => {\n navigator.clipboard.writeText(text)\n setCopiedCode(true)\n setTimeout(() => setCopiedCode(false), 2000)\n toast.success('Copied to clipboard')\n }\n\n useEffect(() => {\n fetchPlugins()\n }, [])\n\n const mergedMarketplacePlugins = [\n ...MARKETPLACE_PLUGINS,\n ...plugins.filter(p => !MARKETPLACE_PLUGINS.some(mp => mp.name.toLowerCase() === p.name.toLowerCase()))\n ]\n\n const displayList = (activeTab === 'installed'\n ? plugins.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()) || p.description?.toLowerCase().includes(searchQuery.toLowerCase()))\n : mergedMarketplacePlugins.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()) || p.description?.toLowerCase().includes(searchQuery.toLowerCase()))\n ) as PluginData[]\n\n const isInstalled = (name: string) => plugins.some((p) => p.name.toLowerCase() === name.toLowerCase())\n\n if (loading) {\n return (\n <div className=\"flex justify-center items-center h-[calc(100vh-64px)]\">\n <Loader2 size={32} className=\"animate-spin text-z-secondary\" />\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col h-[calc(100vh-64px)] overflow-hidden\">\n <PageHeader\n title=\"Plugin System\"\n actions={\n <div className=\"flex gap-2\">\n <div className={cn('flex p-1 border', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}>\n <button\n onClick={() => setActiveTab('installed')}\n className={cn('px-4 py-1.5 text-sm font-semibold transition-colors', activeTab === 'installed' ? (dark ? 'bg-white/15 text-white' : 'bg-z-primary text-z-inverse') : 'text-z-secondary')}\n >\n Installed\n </button>\n <button\n onClick={() => setActiveTab('marketplace')}\n className={cn('px-4 py-1.5 text-sm font-semibold transition-colors', activeTab === 'marketplace' ? (dark ? 'bg-white/15 text-white' : 'bg-z-primary text-z-inverse') : 'text-z-secondary')}\n >\n Marketplace\n </button>\n </div>\n <button\n onClick={fetchPlugins}\n className={cn('px-3 border text-z-secondary hover:text-z-primary transition-colors', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}\n >\n <RefreshCw size={14} />\n </button>\n </div>\n }\n />\n\n <div className=\"flex-1 overflow-auto p-6 md:p-8 space-y-6\">\n <div className=\"max-w-md\">\n <div className={cn('flex items-center gap-3 px-4 py-2 border transition-all', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}>\n <Search size={14} className=\"text-z-secondary\" />\n <input\n type=\"text\"\n placeholder={activeTab === 'installed' ? 'Search installed plugins...' : 'Search marketplace...'}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"bg-transparent border-none outline-none text-sm font-semibold text-z-secondary w-full placeholder:text-z-secondary\"\n />\n </div>\n </div>\n\n {displayList.length === 0 ? (\n <div className=\"text-center py-20 text-z-secondary\">\n <Box size={32} className=\"mx-auto mb-4 opacity-50\" />\n <p className=\"text-sm font-semibold\">No plugins found</p>\n </div>\n ) : (\n <div className=\"grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4\">\n <AnimatePresence>\n {displayList.map((plugin) => {\n const installed = isInstalled(plugin.name)\n return (\n <motion.div\n key={plugin.id || plugin.name}\n layout\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.95 }}\n >\n <Card className=\"h-full flex flex-col\">\n <CardContent className=\"p-5 flex flex-col h-full gap-4\">\n <div className=\"flex justify-between items-start\">\n <div className=\"flex gap-3 items-center\">\n <div className={cn('w-8 h-8 flex items-center justify-center border', dark ? 'bg-app border-z-border' : 'bg-z-input border-z-border')}>\n {plugin.icon}\n </div>\n <div>\n <h3 className=\"text-sm font-semibold\">{plugin.name}</h3>\n <p className=\"text-sm text-z-secondary font-bold\">\n {plugin.author === 'ROOT_KERNEL' ? 'Zenith Core' : plugin.author} • v{plugin.version}\n </p>\n </div>\n </div>\n </div>\n\n {/* Minimalist features instead of descriptive text */}\n <div className=\"flex-1\">\n <p className=\"text-sm text-z-secondary leading-relaxed line-clamp-2\">\n {plugin.description}\n </p>\n </div>\n\n <div className=\"flex items-center justify-between pt-4 border-t border-z-border\">\n <div className=\"flex items-center gap-3 text-sm text-z-secondary font-bold\">\n {plugin.verified && <span className=\"flex items-center gap-1\"><ShieldCheck size={10} /> Verified</span>}\n <span>{plugin.downloads?.toLocaleString()} DLs</span>\n </div>\n\n <div className=\"flex gap-2\">\n {activeTab === 'installed' ? (\n <>\n <button\n onClick={() => togglePlugin(plugin.id, plugin.status)}\n className={cn('px-3 py-1.5 border text-sm font-semibold transition-colors', plugin.status === 'active' ? 'text-red-500 border-red-500/20 hover:bg-red-500/10' : 'text-z-secondary border-z-border/20 hover:bg-z-panel')}\n >\n {plugin.status === 'active' ? 'Disable' : 'Enable'}\n </button>\n {plugin.author !== 'ROOT_KERNEL' && (\n <button onClick={() => toast.error('Core Protection: Delete via CLI')} className=\"p-1.5 text-red-500/70 hover:bg-red-500/10\">\n <Trash2 size={12} />\n </button>\n )}\n </>\n ) : (\n <button\n disabled={installed || installingId !== null}\n onClick={() => installMarketplacePlugin(plugin as any)}\n className={cn('px-4 py-1.5 border text-sm font-semibold transition-colors flex items-center gap-2', installed ? 'text-z-secondary border-z-border/20 cursor-not-allowed' : 'text-z-primary bg-z-accent border-z-accent hover:bg-z-accent shadow-sm')}\n >\n {installingId === plugin.id ? <Loader2 size={10} className=\"animate-spin\" /> : <DownloadCloud size={10} />}\n {installed ? 'Installed' : 'Install'}\n </button>\n )}\n </div>\n </div>\n </CardContent>\n </Card>\n </motion.div>\n )\n })}\n </AnimatePresence>\n </div>\n )}\n </div>\n\n {/* Plugin Install Guide Modal */}\n {installGuidePlugin && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4\">\n <div className={cn('max-w-2xl w-full border shadow-2xl flex flex-col', dark ? 'bg-app border-z-border' : 'bg-z-panel border-z-border')}>\n <div className=\"flex justify-between items-center p-5 border-b border-z-border\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-10 h-10 border border-z-border bg-z-input flex items-center justify-center\">\n {installGuidePlugin.icon}\n </div>\n <div>\n <h3 className=\"text-lg font-bold\">{installGuidePlugin.name}</h3>\n <p className=\"text-xs text-z-secondary font-semibold\">Official Installation Guide</p>\n </div>\n </div>\n <button onClick={() => setInstallGuidePlugin(null)} className=\"p-2 text-z-secondary hover:text-z-primary transition-colors\">\n <X size={20} />\n </button>\n </div>\n \n <div className=\"p-6 space-y-6 overflow-y-auto max-h-[70vh] custom-editor-scrollbar\">\n <p className=\"text-sm text-z-secondary leading-relaxed\">\n To keep Zenith extremely lightweight and production-safe, plugins are installed via your terminal instead of the web dashboard. This guarantees your code repository matches your production environment.\n </p>\n\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-bold flex items-center gap-2 text-z-primary\">\n <span className=\"w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full\">1</span> \n Install the NPM Package\n </h4>\n <div className=\"relative group\">\n <pre className=\"bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap\">\n pnpm add @zenith-open/{installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}\n </pre>\n <button \n onClick={() => copyToClipboard(`pnpm add @zenith-open/${installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}`)}\n className=\"absolute top-2 right-2 p-1.5 bg-[rgba(255,255,255,0.1)] text-white hover:bg-[rgba(255,255,255,0.2)] opacity-0 group-hover:opacity-100 transition-opacity\"\n >\n {copiedCode ? <Check size={14} /> : <Copy size={14} />}\n </button>\n </div>\n </div>\n\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-bold flex items-center gap-2 text-z-primary\">\n <span className=\"w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full\">2</span> \n Enable in your config\n </h4>\n <p className=\"text-xs text-z-secondary\">Open <code className=\"bg-z-input px-1.5 py-0.5 text-z-primary font-mono border border-z-border\">cms.config.ts</code> and add the plugin to the array.</p>\n <div className=\"relative group\">\n <pre className=\"bg-black text-[#A1B3CD] p-4 text-xs font-mono border border-[rgba(255,255,255,0.1)] overflow-x-auto whitespace-pre-wrap\">\n{`import { zenithPlugin } from '@zenith-open/${installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}'\n\nexport default buildConfig({\n collections: [...],\n plugins: [\n zenithPlugin({\n // Provide any necessary config options here\n })\n ]\n})`}\n </pre>\n <button \n onClick={() => copyToClipboard(`import { zenithPlugin } from '@zenith-open/${installGuidePlugin.id || installGuidePlugin.name.toLowerCase().replace(/\\s+/g, '-')}'\\n\\nexport default buildConfig({\\n collections: [...],\\n plugins: [\\n zenithPlugin({\\n // Provide any necessary config options here\\n })\\n ]\\n})`)}\n className=\"absolute top-2 right-2 p-1.5 bg-[rgba(255,255,255,0.1)] text-white hover:bg-[rgba(255,255,255,0.2)] opacity-0 group-hover:opacity-100 transition-opacity\"\n >\n {copiedCode ? <Check size={14} /> : <Copy size={14} />}\n </button>\n </div>\n </div>\n\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-bold flex items-center gap-2 text-z-primary\">\n <span className=\"w-5 h-5 flex items-center justify-center bg-z-accent text-z-primary text-xs rounded-full\">3</span> \n Restart the Server\n </h4>\n <p className=\"text-xs text-z-secondary\">Restart your development server and the plugin will be instantly active!</p>\n </div>\n\n </div>\n <div className=\"p-4 border-t border-z-border bg-z-input flex justify-end\">\n <button \n onClick={() => setInstallGuidePlugin(null)}\n className=\"px-6 py-2 bg-z-panel text-z-primary border border-z-border text-sm font-bold shadow-sm hover:bg-z-input transition-colors\"\n >\n Done\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport default PluginsPage\n"],"mappings":"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"}
|