kempo 4.5.2 → 4.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +5 -0
  2. package/dist/admin/components/Dashboard.js +67 -0
  3. package/dist/admin/components/Dashboard.js.map +1 -0
  4. package/dist/admin/components/dashboardLayout.js +2 -0
  5. package/dist/admin/components/dashboardLayout.js.map +1 -0
  6. package/dist/admin/components/widgets/Content.js +4 -0
  7. package/dist/admin/components/widgets/Content.js.map +1 -0
  8. package/dist/admin/components/widgets/Extensions.js +5 -0
  9. package/dist/admin/components/widgets/Extensions.js.map +1 -0
  10. package/dist/admin/components/widgets/Notifications.js +4 -0
  11. package/dist/admin/components/widgets/Notifications.js.map +1 -0
  12. package/dist/admin/components/widgets/Pages.js +23 -0
  13. package/dist/admin/components/widgets/Pages.js.map +1 -0
  14. package/dist/admin/components/widgets/Users.js +5 -0
  15. package/dist/admin/components/widgets/Users.js.map +1 -0
  16. package/dist/admin/index.page.html +2 -1
  17. package/dist/kempo/api/dashboard/widgets/GET.js +2 -0
  18. package/dist/kempo/api/dashboard/widgets/GET.js.map +1 -0
  19. package/dist/kempo/components/DashboardWidget.js +18 -0
  20. package/dist/kempo/components/DashboardWidget.js.map +1 -0
  21. package/dist/kempo/sdk.js +1 -1
  22. package/dist/kempo/sdk.js.map +1 -1
  23. package/package.json +1 -1
  24. package/server/db/schema.js +34 -1
  25. package/server/sdk.js +8 -0
  26. package/server/utils/dashboard/coreWidgets.js +87 -0
  27. package/server/utils/dashboard/helpers.js +144 -0
  28. package/server/utils/dashboard/listDashboardWidgets.js +63 -0
  29. package/server/utils/dashboard/registerDashboardWidget.js +40 -0
  30. package/server/utils/dashboard/unregisterDashboardWidget.js +24 -0
  31. package/server/utils/extensions/installExtension.js +13 -0
  32. package/server/utils/extensions/uninstallExtension.js +2 -1
  33. package/server/utils/extensions/updateExtension.js +18 -0
package/README.md CHANGED
@@ -178,6 +178,7 @@ Kempo provides these tables out of the box:
178
178
  | `realtimeChannel` | How far each realtime channel has been pruned, to tell a client it missed messages |
179
179
  | `notification` | Notifications raised by core or extensions: title, message, level, actions, dedupe key |
180
180
  | `notificationRecipient` | One row per person a notification was sent to, with their own read, handled and dismissed state |
181
+ | `dashboardWidget` | Widgets extensions add to the admin dashboard: element, module script, size limits, permission |
181
182
 
182
183
  ## Frontend SDK
183
184
 
@@ -356,6 +357,10 @@ Settings can be managed through the admin panel at `/admin/settings` or the SDK.
356
357
 
357
358
  Extensions can push live updates to browsers: declare a channel, publish to it from any server code, and every subscribed browser on any kempo process receives it. Channels can persist messages so a client that reconnects replays what it missed, and a browser client handles connecting and reconnecting. Browsers can also send to a channel, where the extension's own handler receives and answers it, and extensions observe connections through hooks and act on them through SDK functions, so interactive features (chat, collaboration, games) can be built as extensions without touching a socket. Fast traffic can use an in-memory channel that skips the database, and connections are limited and backpressured. It uses a Postgres `LISTEN`/`NOTIFY` bus, so it needs no extra service. See [docs/realtime.md](docs/realtime.md).
358
359
 
360
+ ## Admin Dashboard
361
+
362
+ The admin home page is a grid of widgets one to four blocks wide and one to three tall. Kempo provides widgets for pages, content, users, notifications and extensions; extensions add their own with `kempo.widgets` or `registerDashboardWidget`. Each person chooses which are shown, their order and size, kept in their browser's localStorage, and a widget only loads its script and data once it is shown. Existing sites need `npx drizzle-kit push` to add the `dashboardWidget` table (until then only Kempo's own widgets appear). See [docs/dashboard.md](docs/dashboard.md).
363
+
359
364
  ## Notifications
360
365
 
361
366
  Core and extensions can notify users with `createNotification`: to specific users, everyone holding a permission, or a group. Each person has their own read state and history, a notification can carry buttons that link somewhere or call an API as the signed-in user, and a `dedupeKey` keeps a repeating event to one notification. The admin has a bell with an unread count and a Notifications page; a public site can use `<k-notification-bell>`. Existing sites need `npx drizzle-kit push` to add the two new tables. See [docs/notifications.md](docs/notifications.md).
@@ -0,0 +1,67 @@
1
+ import t from"/kempo-ui/components/LightComponent.js";import{html as e,nothing as i,repeat as s,keyed as a}from"/kempo-ui/lit-all.min.js";import"/kempo-ui/components/Icon.js";import"/kempo-ui/components/Dialog.js";import"/kempo-ui/components/Sortable.js";import{getDashboardWidgets as n}from"/kempo/sdk.js";import{resolveLayout as l,serializeLayout as o,reorder as r,readStored as d,writeStored as h,clearStored as c}from"./dashboardLayout.js";const m="min(32rem, 70vw)",u=(t,e)=>Array.from({length:e-t+1},(e,i)=>t+i);export default class p extends t{static properties={heading:{type:String},layout:{state:!0},draft:{state:!0},sortVersion:{state:!0},columns:{state:!0},loading:{state:!0},failure:{state:!0},failures:{state:!0}};#t=[];#e=[];#i=!1;#s=new Map;#a=new Set;#n=null;constructor(){super(),this.heading="",this.layout=[],this.draft=[],this.sortVersion=0,this.columns=4,this.loading=!0,this.failure="",this.failures={}}connectedCallback(){super.connectedCallback(),this.classList.add("d-b"),this.#n=new ResizeObserver(([t])=>{const{width:e}=t.contentRect,i=e>=880?4:e>=560?2:1;i!==this.columns&&(this.columns=i)}),this.#n.observe(this),this.load()}disconnectedCallback(){super.disconnectedCallback(),this.#n?.disconnect()}updated(t){super.updated(t),this.layout.forEach(({widget:t,visible:e,width:i,height:s})=>{const a=this.#s.get(t.id);e&&a&&(a.width=Math.min(i,this.columns),a.height=s)})}load=async()=>{const[t,e]=await n();this.loading=!1,t?this.failure=`Could not load the dashboard: ${t.msg}`:(this.failure="",this.#t=e.widgets,this.#e=e.unavailable??[],this.layout=l(this.#t,d()),this.mountVisible())};mount=async({widget:t})=>{if(!this.#s.has(t.id)&&!this.#a.has(t.id)){this.#a.add(t.id);try{if(await(import(t.module)),!customElements.get(t.element))throw new Error(`${t.module} did not define <${t.element}>`);const e=document.createElement(t.element);e.definition=t,this.#s.set(t.id,e)}catch(e){this.failures={...this.failures,[t.id]:`is not available: its script ${t.module} could not be loaded, which usually means the extension that provides it needs updating.`},console.error(`Dashboard widget ${t.id} failed to load:`,e)}this.#a.delete(t.id),this.requestUpdate()}};mountVisible=()=>this.layout.filter(({visible:t})=>t).forEach(this.mount);commit=t=>{const e=o(t,{stored:this.#i?null:d(),unavailable:this.#e});this.layout=t,JSON.stringify(e)===JSON.stringify(o(l(this.#t,null)))?c():h(e),t.filter(({visible:t})=>!t).forEach(({widget:t})=>{if(this.#s.delete(t.id),this.failures[t.id]){const{[t.id]:e,...i}=this.failures;this.failures=i}}),this.mountVisible()};openCustomize=()=>{this.#i=!1,this.draft=this.layout.map(t=>({...t})),this.sortVersion++,this.querySelector("k-dialog").open()};save=()=>this.commit(this.draft);resetDraft=()=>{this.#i=!0,this.draft=l(this.#t,null),this.sortVersion++};changeDraft=(t,e)=>{this.draft=this.draft.map(i=>i.widget.id===t?{...i,...e}:i)};handleSort=t=>{this.draft=r(this.draft,[...t.currentTarget.children].map(t=>t.dataset.widgetId)),this.sortVersion++};renderDraftRow=({widget:t,visible:s,width:a,height:n})=>e`
2
+ <k-sortable-item class="mbh" data-widget-id=${t.id}>
3
+ <div style="display: flex; flex-wrap: nowrap; align-items: center; gap: var(--spacer); width: ${m}; min-height: 3.5rem;">
4
+ <div class="flex" style="display: flex; flex-wrap: nowrap; align-items: center; gap: var(--spacer_h); min-width: 0;">
5
+ ${t.icon?e`<k-icon class=${s?"":"tc-muted"} name=${t.icon}></k-icon>`:i}
6
+ <strong class=${s?"":"tc-muted"}>${t.title}</strong>
7
+ </div>
8
+ <select style="width: 4.5rem;" aria-label="${t.title} width" ?disabled=${!s} @change=${e=>this.changeDraft(t.id,{width:Number(e.target.value)})}>
9
+ ${u(t.minWidth,t.maxWidth).map(t=>e`<option value=${t} ?selected=${t===a}>${t}</option>`)}
10
+ </select>
11
+ <select style="width: 4.5rem;" aria-label="${t.title} height" ?disabled=${!s} @change=${e=>this.changeDraft(t.id,{height:Number(e.target.value)})}>
12
+ ${u(t.minHeight,t.maxHeight).map(t=>e`<option value=${t} ?selected=${t===n}>${t}</option>`)}
13
+ </select>
14
+ <button
15
+ class="no-btn pq"
16
+ style="width: 2.5rem;"
17
+ title=${s?"Hide":"Show"}
18
+ aria-label="${s?"Hide":"Show"} ${t.title}"
19
+ @click=${()=>this.changeDraft(t.id,{visible:!s})}
20
+ ><k-icon name=${s?"hide":"show"}></k-icon></button>
21
+ </div>
22
+ </k-sortable-item>
23
+ `;renderDraftHeader=()=>e`
24
+ <div class="small tc-muted mbh" style="display: flex; flex-wrap: nowrap; gap: var(--spacer); width: calc(${m} + 3.7rem); padding-left: 3.7rem;">
25
+ <span class="flex">Widget</span>
26
+ <span style="width: 4.5rem;">Width</span>
27
+ <span style="width: 4.5rem;">Height</span>
28
+ <span style="width: 2.5rem;"></span>
29
+ </div>
30
+ `;renderCustomizeDialog=()=>e`
31
+ <k-dialog
32
+ confirm-text="Save"
33
+ confirm-classes="primary ml"
34
+ cancel-text="Cancel"
35
+ .confirmAction=${this.save}
36
+ >
37
+ <h5 slot="title" class="pyh px m0">Customize dashboard</h5>
38
+ <div class="p">
39
+ <p class="small tc-muted">Drag to reorder. Hidden widgets are not loaded. Sizes are in blocks; the dashboard is four blocks wide.</p>
40
+ ${this.renderDraftHeader()}
41
+ ${a(this.sortVersion,e`
42
+ <k-sortable @sort=${this.handleSort}>${this.draft.map(this.renderDraftRow)}</k-sortable>
43
+ `)}
44
+ <button class="mt" @click=${this.resetDraft}><k-icon name="restart_alt"></k-icon> Reset to defaults</button>
45
+ </div>
46
+ </k-dialog>
47
+ `;renderCell=({widget:t,width:i,height:s})=>{const a=this.#s.get(t.id),n=this.failures[t.id];return e`
48
+ <div
49
+ data-widget-id=${t.id}
50
+ style="grid-column: span ${Math.min(i,this.columns)}; grid-row: span ${s}; min-width: 0;"
51
+ >
52
+ <section class="b r" style="height: 100%; min-height: 0; overflow: hidden;">
53
+ ${n?e`<p class="p tc-danger" role="alert"><strong>${t.title}</strong> ${n}</p>`:a??e`<p class="p tc-muted"><strong>${t.title}</strong> Loading&hellip;</p>`}
54
+ </section>
55
+ </div>
56
+ `};renderLightDom(){if(this.failure)return e`<p class="tc-danger" role="alert">${this.failure}</p>`;if(this.loading)return e`<p class="tc-muted">Loading&hellip;</p>`;const t=this.layout.filter(t=>t.visible);return e`
57
+ <div class="d-f mb" style="align-items: center;">
58
+ <h1 class="flex m0">${this.heading}</h1>
59
+ <button @click=${this.openCustomize}><k-icon name="edit"></k-icon> Customize</button>
60
+ </div>
61
+ ${t.length?i:e`<p class="tc-muted">No widgets are shown. Choose Customize to add some.</p>`}
62
+ <div class="d-g" style="grid-template-columns: repeat(${this.columns}, minmax(0, 1fr)); grid-auto-rows: 14rem; grid-auto-flow: dense; gap: var(--spacer);">
63
+ ${s(t,t=>t.widget.id,this.renderCell)}
64
+ </div>
65
+ ${this.renderCustomizeDialog()}
66
+ `}}customElements.define("k-dashboard",p);
67
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\admin\components\Dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\admin\\components\\Dashboard.js","names":["LightComponent","html","nothing","repeat","keyed","getDashboardWidgets","resolveLayout","serializeLayout","reorder","readStored","writeStored","clearStored","ROW_WIDTH","range","from","to","Array","length","_","index","Dashboard","static","heading","type","String","layout","state","draft","sortVersion","columns","loading","failure","failures","definitions","unavailable","resetting","elements","Map","pending","Set","observer","constructor","super","this","connectedCallback","classList","add","ResizeObserver","entry","width","contentRect","observe","load","disconnectedCallback","disconnect","updated","changed","forEach","widget","visible","height","element","get","id","Math","min","async","error","data","msg","widgets","mountVisible","mount","has","module","customElements","Error","document","createElement","definition","set","console","delete","requestUpdate","filter","commit","stored","JSON","stringify","dropped","rest","openCustomize","map","querySelector","open","save","resetDraft","changeDraft","changes","handleSort","event","currentTarget","children","item","dataset","widgetId","renderDraftRow","icon","title","Number","target","value","minWidth","maxWidth","minHeight","maxHeight","renderDraftHeader","renderCustomizeDialog","renderCell","renderLightDom","define"],"sources":["0"],"sourcesContent":["import LightComponent from '/kempo-ui/components/LightComponent.js';\r\nimport { html, nothing, repeat, keyed } from '/kempo-ui/lit-all.min.js';\r\nimport '/kempo-ui/components/Icon.js';\r\nimport '/kempo-ui/components/Dialog.js';\r\nimport '/kempo-ui/components/Sortable.js';\r\nimport { getDashboardWidgets } from '/kempo/sdk.js';\r\nimport { resolveLayout, serializeLayout, reorder, readStored, writeStored, clearStored } from './dashboardLayout.js';\r\n\r\n// Wide enough for the columns; narrower screens shrink it rather than scroll.\r\nconst ROW_WIDTH = 'min(32rem, 70vw)';\r\n\r\nconst range = (from, to) => Array.from({ length: to - from + 1 }, (_, index) => from + index);\r\n\r\n/*\r\n The admin dashboard: a grid four blocks wide of widgets one to four blocks wide and one to three tall.\r\n\r\n The server only says which widgets exist and their size bounds. Which of them are shown, in what order\r\n and at what size is the user's own and lives in this browser's localStorage, changed through the\r\n Customize dialog and applied only when it is saved. A widget's script is not imported, and so nothing\r\n it needs is fetched, until it is both on the user's dashboard and rendered; a widget the user hides is\r\n torn down and starts fresh when it is shown again.\r\n\r\n Attributes: `heading`, the title shown above the grid.\r\n*/\r\nexport default class Dashboard extends LightComponent {\r\n static properties = {\r\n heading: { type: String },\r\n layout: { state: true },\r\n draft: { state: true },\r\n sortVersion: { state: true },\r\n columns: { state: true },\r\n loading: { state: true },\r\n failure: { state: true },\r\n failures: { state: true }\r\n };\r\n\r\n #definitions = [];\r\n #unavailable = [];\r\n #resetting = false;\r\n #elements = new Map();\r\n #pending = new Set();\r\n #observer = null;\r\n\r\n constructor(){\r\n super();\r\n this.heading = '';\r\n this.layout = [];\r\n this.draft = [];\r\n this.sortVersion = 0;\r\n this.columns = 4;\r\n this.loading = true;\r\n this.failure = '';\r\n this.failures = {};\r\n }\r\n\r\n /*\r\n Lifecycle Callbacks\r\n */\r\n\r\n connectedCallback(){\r\n super.connectedCallback();\r\n this.classList.add('d-b');\r\n this.#observer = new ResizeObserver(([entry]) => {\r\n const { width } = entry.contentRect;\r\n const columns = width >= 880 ? 4 : width >= 560 ? 2 : 1;\r\n if(columns !== this.columns) this.columns = columns;\r\n });\r\n this.#observer.observe(this);\r\n this.load();\r\n }\r\n\r\n disconnectedCallback(){\r\n super.disconnectedCallback();\r\n this.#observer?.disconnect();\r\n }\r\n\r\n updated(changed){\r\n super.updated(changed);\r\n this.layout.forEach(({ widget, visible, width, height }) => {\r\n const element = this.#elements.get(widget.id);\r\n if(!visible || !element) return;\r\n element.width = Math.min(width, this.columns);\r\n element.height = height;\r\n });\r\n }\r\n\r\n /*\r\n Public Methods\r\n */\r\n\r\n load = async () => {\r\n const [error, data] = await getDashboardWidgets();\r\n this.loading = false;\r\n\r\n if(error){\r\n this.failure = `Could not load the dashboard: ${error.msg}`;\r\n return;\r\n }\r\n\r\n this.failure = '';\r\n this.#definitions = data.widgets;\r\n this.#unavailable = data.unavailable ?? [];\r\n this.layout = resolveLayout(this.#definitions, readStored());\r\n this.mountVisible();\r\n };\r\n\r\n /*\r\n Utility Functions\r\n */\r\n\r\n /*\r\n Imports a widget's module and creates its element, once. The element fetches its own data when it\r\n connects to the page, so nothing is requested for a widget that is hidden.\r\n */\r\n mount = async ({ widget }) => {\r\n if(this.#elements.has(widget.id) || this.#pending.has(widget.id)) return;\r\n this.#pending.add(widget.id);\r\n\r\n try {\r\n await import(widget.module);\r\n if(!customElements.get(widget.element)){\r\n throw new Error(`${widget.module} did not define <${widget.element}>`);\r\n }\r\n const element = document.createElement(widget.element);\r\n element.definition = widget;\r\n this.#elements.set(widget.id, element);\r\n } catch(error) {\r\n this.failures = { ...this.failures, [widget.id]: `is not available: its script ${widget.module} could not be loaded, which usually means the extension that provides it needs updating.` };\r\n console.error(`Dashboard widget ${widget.id} failed to load:`, error);\r\n }\r\n\r\n this.#pending.delete(widget.id);\r\n this.requestUpdate();\r\n };\r\n\r\n mountVisible = () => this.layout.filter(({ visible }) => visible).forEach(this.mount);\r\n\r\n /*\r\n Applies a layout, remembers it, and mounts anything newly shown. Widgets no longer shown are released\r\n here so showing them again loads fresh data. A layout identical to the defaults is not stored, so the\r\n user keeps following the defaults as they change.\r\n */\r\n commit = layout => {\r\n const stored = serializeLayout(layout, { stored: this.#resetting ? null : readStored(), unavailable: this.#unavailable });\r\n this.layout = layout;\r\n\r\n if(JSON.stringify(stored) === JSON.stringify(serializeLayout(resolveLayout(this.#definitions, null)))){\r\n clearStored();\r\n } else {\r\n writeStored(stored);\r\n }\r\n\r\n layout.filter(({ visible }) => !visible).forEach(({ widget }) => {\r\n this.#elements.delete(widget.id);\r\n if(this.failures[widget.id]){\r\n const { [widget.id]: dropped, ...rest } = this.failures;\r\n this.failures = rest;\r\n }\r\n });\r\n\r\n this.mountVisible();\r\n };\r\n\r\n /*\r\n Event Handlers\r\n */\r\n\r\n openCustomize = () => {\r\n this.#resetting = false;\r\n this.draft = this.layout.map(entry => ({ ...entry }));\r\n this.sortVersion++;\r\n this.querySelector('k-dialog').open();\r\n };\r\n\r\n save = () => this.commit(this.draft);\r\n\r\n resetDraft = () => {\r\n this.#resetting = true;\r\n this.draft = resolveLayout(this.#definitions, null);\r\n this.sortVersion++;\r\n };\r\n\r\n changeDraft = (id, changes) => {\r\n this.draft = this.draft.map(entry => entry.widget.id === id ? { ...entry, ...changes } : entry);\r\n };\r\n\r\n /*\r\n The sortable moves its own items in the DOM. The new order is read from there and the list is then\r\n rebuilt from the draft (the `keyed` version changes) so Lit never disagrees with the DOM.\r\n */\r\n handleSort = event => {\r\n this.draft = reorder(this.draft, [...event.currentTarget.children].map(item => item.dataset.widgetId));\r\n this.sortVersion++;\r\n };\r\n\r\n /*\r\n Rendering\r\n */\r\n\r\n /*\r\n The item sizes its content to fit, so the row sets its own width. Its drag handle is 3.5rem tall, so\r\n the row is the same height with its controls centered, and the columns have fixed widths that the\r\n header row above the list repeats. `.d-f` is avoided because it wraps.\r\n */\r\n renderDraftRow = ({ widget, visible, width, height }) => html`\r\n <k-sortable-item class=\"mbh\" data-widget-id=${widget.id}>\r\n <div style=\"display: flex; flex-wrap: nowrap; align-items: center; gap: var(--spacer); width: ${ROW_WIDTH}; min-height: 3.5rem;\">\r\n <div class=\"flex\" style=\"display: flex; flex-wrap: nowrap; align-items: center; gap: var(--spacer_h); min-width: 0;\">\r\n ${widget.icon ? html`<k-icon class=${visible ? '' : 'tc-muted'} name=${widget.icon}></k-icon>` : nothing}\r\n <strong class=${visible ? '' : 'tc-muted'}>${widget.title}</strong>\r\n </div>\r\n <select style=\"width: 4.5rem;\" aria-label=\"${widget.title} width\" ?disabled=${!visible} @change=${event => this.changeDraft(widget.id, { width: Number(event.target.value) })}>\r\n ${range(widget.minWidth, widget.maxWidth).map(value => html`<option value=${value} ?selected=${value === width}>${value}</option>`)}\r\n </select>\r\n <select style=\"width: 4.5rem;\" aria-label=\"${widget.title} height\" ?disabled=${!visible} @change=${event => this.changeDraft(widget.id, { height: Number(event.target.value) })}>\r\n ${range(widget.minHeight, widget.maxHeight).map(value => html`<option value=${value} ?selected=${value === height}>${value}</option>`)}\r\n </select>\r\n <button\r\n class=\"no-btn pq\"\r\n style=\"width: 2.5rem;\"\r\n title=${visible ? 'Hide' : 'Show'}\r\n aria-label=\"${visible ? 'Hide' : 'Show'} ${widget.title}\"\r\n @click=${() => this.changeDraft(widget.id, { visible: !visible })}\r\n ><k-icon name=${visible ? 'hide' : 'show'}></k-icon></button>\r\n </div>\r\n </k-sortable-item>\r\n `;\r\n\r\n /*\r\n Column titles for the rows. The left offset is the sortable item's border and drag handle, so the\r\n columns line up with the rows below.\r\n */\r\n renderDraftHeader = () => html`\r\n <div class=\"small tc-muted mbh\" style=\"display: flex; flex-wrap: nowrap; gap: var(--spacer); width: calc(${ROW_WIDTH} + 3.7rem); padding-left: 3.7rem;\">\r\n <span class=\"flex\">Widget</span>\r\n <span style=\"width: 4.5rem;\">Width</span>\r\n <span style=\"width: 4.5rem;\">Height</span>\r\n <span style=\"width: 2.5rem;\"></span>\r\n </div>\r\n `;\r\n\r\n renderCustomizeDialog = () => html`\r\n <k-dialog\r\n confirm-text=\"Save\"\r\n confirm-classes=\"primary ml\"\r\n cancel-text=\"Cancel\"\r\n .confirmAction=${this.save}\r\n >\r\n <h5 slot=\"title\" class=\"pyh px m0\">Customize dashboard</h5>\r\n <div class=\"p\">\r\n <p class=\"small tc-muted\">Drag to reorder. Hidden widgets are not loaded. Sizes are in blocks; the dashboard is four blocks wide.</p>\r\n ${this.renderDraftHeader()}\r\n ${keyed(this.sortVersion, html`\r\n <k-sortable @sort=${this.handleSort}>${this.draft.map(this.renderDraftRow)}</k-sortable>\r\n `)}\r\n <button class=\"mt\" @click=${this.resetDraft}><k-icon name=\"restart_alt\"></k-icon> Reset to defaults</button>\r\n </div>\r\n </k-dialog>\r\n `;\r\n\r\n /*\r\n The grid assigns the spans and row height, so they are set here as values rather than classes: they\r\n depend on each user's saved sizes and the width the grid currently has. A widget wider than the\r\n columns available is drawn as wide as the grid is; its saved width is kept for when there is room.\r\n */\r\n renderCell = ({ widget, width, height }) => {\r\n const element = this.#elements.get(widget.id);\r\n const failure = this.failures[widget.id];\r\n return html`\r\n <div\r\n data-widget-id=${widget.id}\r\n style=\"grid-column: span ${Math.min(width, this.columns)}; grid-row: span ${height}; min-width: 0;\"\r\n >\r\n <section class=\"b r\" style=\"height: 100%; min-height: 0; overflow: hidden;\">\r\n ${failure\r\n ? html`<p class=\"p tc-danger\" role=\"alert\"><strong>${widget.title}</strong> ${failure}</p>`\r\n : element ?? html`<p class=\"p tc-muted\"><strong>${widget.title}</strong> Loading&hellip;</p>`}\r\n </section>\r\n </div>\r\n `;\r\n };\r\n\r\n renderLightDom(){\r\n if(this.failure) return html`<p class=\"tc-danger\" role=\"alert\">${this.failure}</p>`;\r\n if(this.loading) return html`<p class=\"tc-muted\">Loading&hellip;</p>`;\r\n\r\n const visible = this.layout.filter(entry => entry.visible);\r\n\r\n return html`\r\n <div class=\"d-f mb\" style=\"align-items: center;\">\r\n <h1 class=\"flex m0\">${this.heading}</h1>\r\n <button @click=${this.openCustomize}><k-icon name=\"edit\"></k-icon> Customize</button>\r\n </div>\r\n ${visible.length ? nothing : html`<p class=\"tc-muted\">No widgets are shown. Choose Customize to add some.</p>`}\r\n <div class=\"d-g\" style=\"grid-template-columns: repeat(${this.columns}, minmax(0, 1fr)); grid-auto-rows: 14rem; grid-auto-flow: dense; gap: var(--spacer);\">\r\n ${repeat(visible, entry => entry.widget.id, this.renderCell)}\r\n </div>\r\n ${this.renderCustomizeDialog()}\r\n `;\r\n }\r\n}\r\n\r\ncustomElements.define('k-dashboard', Dashboard);\r\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,2 @@
1
+ export const STORAGE_KEY="kempo:admin:dashboard";const e=(e,t,i,r)=>Number.isInteger(e)?Math.min(Math.max(e,t),i):r;export const resolveLayout=(t,i)=>{const r=new Map(t.map(e=>[e.id,e])),d=i&&"object"==typeof i?i:{},s=(Array.isArray(d.order)?d.order:[]).filter((e,t,i)=>r.has(e)&&i.indexOf(e)===t),o=new Set(s),a=new Set(Array.isArray(d.hidden)?d.hidden:[]),n=d.sizes&&"object"==typeof d.sizes?d.sizes:{};return[...s,...t.map(e=>e.id).filter(e=>!o.has(e))].map(t=>{const i=r.get(t),d=n[t]&&"object"==typeof n[t]?n[t]:{};return{widget:i,visible:o.has(t)?!a.has(t):!1!==i.defaultVisible,width:e(d.width,i.minWidth,i.maxWidth,i.defaultWidth),height:e(d.height,i.minHeight,i.maxHeight,i.defaultHeight)}})};export const serializeLayout=(e,{stored:t,unavailable:i=[]}={})=>{const r=e.map(({widget:e})=>e.id),d=e.filter(({visible:e})=>!e).map(({widget:e})=>e.id),s=Object.fromEntries(e.filter(({widget:e,width:t,height:i})=>t!==e.defaultWidth||i!==e.defaultHeight).map(({widget:e,width:t,height:i})=>[e.id,{width:t,height:i}])),o=t&&"object"==typeof t?t:{},a=Array.isArray(o.order)?o.order:[],n=new Set(i);return a.forEach((e,t)=>{if(!n.has(e)||r.includes(e))return;const i=a.slice(0,t).reverse().find(e=>r.includes(e));r.splice(void 0===i?0:r.indexOf(i)+1,0,e),Array.isArray(o.hidden)&&o.hidden.includes(e)&&d.push(e),o.sizes?.[e]&&(s[e]=o.sizes[e])}),{order:r,hidden:d,sizes:s}};export const reorder=(e,t)=>{const i=e=>-1===t.indexOf(e)?t.length:t.indexOf(e);return e.slice().sort((e,t)=>i(e.widget.id)-i(t.widget.id))};export const readStored=()=>{try{return JSON.parse(localStorage.getItem(STORAGE_KEY))}catch{return null}};export const writeStored=e=>{try{localStorage.setItem(STORAGE_KEY,JSON.stringify(e))}catch{}};export const clearStored=()=>{try{localStorage.removeItem(STORAGE_KEY)}catch{}};
2
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\admin\components\dashboardLayout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\admin\\components\\dashboardLayout.js","names":["STORAGE_KEY","clamp","value","min","max","fallback","Number","isInteger","Math","resolveLayout","widgets","stored","byId","Map","map","widget","id","remembered","order","Array","isArray","filter","index","all","has","indexOf","known","Set","hidden","sizes","get","size","visible","defaultVisible","width","minWidth","maxWidth","defaultWidth","height","minHeight","maxHeight","defaultHeight","serializeLayout","layout","unavailable","Object","fromEntries","previous","previousOrder","kept","forEach","includes","before","slice","reverse","find","other","splice","undefined","push","reorder","ids","position","length","sort","a","b","readStored","JSON","parse","localStorage","getItem","writeStored","setItem","stringify","clearStored","removeItem"],"sources":["0"],"sourcesContent":["export const STORAGE_KEY = 'kempo:admin:dashboard';\r\n\r\nconst clamp = (value, min, max, fallback) => Number.isInteger(value) ? Math.min(Math.max(value, min), max) : fallback;\r\n\r\n/*\r\n A layout is an ordered array of `{ widget, visible, width, height }`, one per widget the server\r\n offered. What the browser remembers is smaller (see `serializeLayout`) and is reconciled with what the\r\n server offers on every load, so a widget that is new, gone, or resized in the definition never needs a\r\n migration.\r\n*/\r\n\r\n/*\r\n Merges the server's widget list with what the browser remembered. Widgets it has not seen before are\r\n appended in the server's order, shown or not according to `defaultVisible`; remembered ids the server no\r\n longer offers are dropped; a remembered size is clamped to the widget's current bounds.\r\n*/\r\nexport const resolveLayout = (widgets, stored) => {\r\n const byId = new Map(widgets.map(widget => [widget.id, widget]));\r\n const remembered = stored && typeof stored === 'object' ? stored : {};\r\n const order = (Array.isArray(remembered.order) ? remembered.order : [])\r\n .filter((id, index, all) => byId.has(id) && all.indexOf(id) === index);\r\n const known = new Set(order);\r\n const hidden = new Set(Array.isArray(remembered.hidden) ? remembered.hidden : []);\r\n const sizes = remembered.sizes && typeof remembered.sizes === 'object' ? remembered.sizes : {};\r\n\r\n return [...order, ...widgets.map(widget => widget.id).filter(id => !known.has(id))].map(id => {\r\n const widget = byId.get(id);\r\n const size = sizes[id] && typeof sizes[id] === 'object' ? sizes[id] : {};\r\n return {\r\n widget,\r\n visible: known.has(id) ? !hidden.has(id) : widget.defaultVisible !== false,\r\n width: clamp(size.width, widget.minWidth, widget.maxWidth, widget.defaultWidth),\r\n height: clamp(size.height, widget.minHeight, widget.maxHeight, widget.defaultHeight)\r\n };\r\n });\r\n};\r\n\r\n/*\r\n What gets stored: the full order, the hidden ids, and only the sizes that differ from the widget's\r\n default, so a user who never resized a widget follows it when its default changes.\r\n\r\n `stored` is what was remembered before and `unavailable` the ids the server says exist but are not on\r\n offer right now (a disabled extension, a permission the user lost). Their entries are carried over so the\r\n widget comes back where it was: each is placed straight after the id that preceded it before, and its\r\n hidden state and size are kept. Anything else not in the layout is dropped, which is how an uninstalled\r\n extension is cleaned up.\r\n*/\r\nexport const serializeLayout = (layout, { stored, unavailable = [] } = {}) => {\r\n const order = layout.map(({ widget }) => widget.id);\r\n const hidden = layout.filter(({ visible }) => !visible).map(({ widget }) => widget.id);\r\n const sizes = Object.fromEntries(layout\r\n .filter(({ widget, width, height }) => width !== widget.defaultWidth || height !== widget.defaultHeight)\r\n .map(({ widget, width, height }) => [widget.id, { width, height }]));\r\n\r\n const previous = stored && typeof stored === 'object' ? stored : {};\r\n const previousOrder = Array.isArray(previous.order) ? previous.order : [];\r\n const kept = new Set(unavailable);\r\n\r\n previousOrder.forEach((id, index) => {\r\n if(!kept.has(id) || order.includes(id)) return;\r\n const before = previousOrder.slice(0, index).reverse().find(other => order.includes(other));\r\n order.splice(before === undefined ? 0 : order.indexOf(before) + 1, 0, id);\r\n if(Array.isArray(previous.hidden) && previous.hidden.includes(id)) hidden.push(id);\r\n if(previous.sizes?.[id]) sizes[id] = previous.sizes[id];\r\n });\r\n\r\n return { order, hidden, sizes };\r\n};\r\n\r\n/*\r\n Puts the layout in the order of `ids`. Entries whose id is not listed keep their relative order after\r\n the listed ones. Returns a new array.\r\n*/\r\nexport const reorder = (layout, ids) => {\r\n const position = id => ids.indexOf(id) === -1 ? ids.length : ids.indexOf(id);\r\n return layout.slice().sort((a, b) => position(a.widget.id) - position(b.widget.id));\r\n};\r\n\r\nexport const readStored = () => {\r\n try {\r\n return JSON.parse(localStorage.getItem(STORAGE_KEY));\r\n } catch {\r\n return null;\r\n }\r\n};\r\n\r\nexport const writeStored = value => {\r\n try {\r\n localStorage.setItem(STORAGE_KEY, JSON.stringify(value));\r\n } catch {}\r\n};\r\n\r\nexport const clearStored = () => {\r\n try {\r\n localStorage.removeItem(STORAGE_KEY);\r\n } catch {}\r\n};\r\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ import t from"/kempo/components/DashboardWidget.js";import{html as e}from"/kempo-ui/lit-all.min.js";import{listTemplates as a,listFragments as o,listGlobalContent as n}from"/kempo/sdk.js";export default class s extends t{static properties={...t.properties,counts:{state:!0}};constructor(){super(),this.counts=[],this.cta={label:"Manage content",icon:"content",href:"/admin/content/templates"}}load=async()=>{const[t,e,s]=await Promise.all([this.unwrap(a()),this.unwrap(o()),this.unwrap(n())]);this.counts=[{label:"Templates",total:t.total,href:"/admin/content/templates"},{label:"Fragments",total:e.total,href:"/admin/content/fragments"},{label:"Global content",total:s.total,href:"/admin/content/globals"}]};renderContent(){return this.counts.some(({total:t})=>t)?e`${this.counts.map(({label:t,total:a,href:o})=>e`
2
+ <a class="d-f pyq bb" href=${o}><span class="flex">${t}</span><strong>${a}</strong></a>
3
+ `)}`:e`<p class="tc-muted">No templates, fragments or global content yet.</p>`}}customElements.define("k-widget-content",s);
4
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\admin\components\widgets\Content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\admin\\components\\widgets\\Content.js","names":["DashboardWidget","html","listTemplates","listFragments","listGlobalContent","ContentWidget","static","properties","counts","state","constructor","super","this","cta","label","icon","href","load","async","templates","fragments","globals","Promise","all","unwrap","total","renderContent","some","map","customElements","define"],"sources":["0"],"sourcesContent":["import DashboardWidget from '/kempo/components/DashboardWidget.js';\r\nimport { html } from '/kempo-ui/lit-all.min.js';\r\nimport { listTemplates, listFragments, listGlobalContent } from '/kempo/sdk.js';\r\n\r\n/*\r\n How many templates, fragments and global content entries the site has, each linking to its manager.\r\n*/\r\nexport default class ContentWidget extends DashboardWidget {\r\n static properties = {\r\n ...DashboardWidget.properties,\r\n counts: { state: true }\r\n };\r\n\r\n constructor(){\r\n super();\r\n this.counts = [];\r\n this.cta = { label: 'Manage content', icon: 'content', href: '/admin/content/templates' };\r\n }\r\n\r\n /*\r\n Public Methods\r\n */\r\n\r\n load = async () => {\r\n const [templates, fragments, globals] = await Promise.all([\r\n this.unwrap(listTemplates()),\r\n this.unwrap(listFragments()),\r\n this.unwrap(listGlobalContent())\r\n ]);\r\n this.counts = [\r\n { label: 'Templates', total: templates.total, href: '/admin/content/templates' },\r\n { label: 'Fragments', total: fragments.total, href: '/admin/content/fragments' },\r\n { label: 'Global content', total: globals.total, href: '/admin/content/globals' }\r\n ];\r\n };\r\n\r\n /*\r\n Rendering\r\n */\r\n\r\n renderContent(){\r\n if(!this.counts.some(({ total }) => total)) return html`<p class=\"tc-muted\">No templates, fragments or global content yet.</p>`;\r\n return html`${this.counts.map(({ label, total, href }) => html`\r\n <a class=\"d-f pyq bb\" href=${href}><span class=\"flex\">${label}</span><strong>${total}</strong></a>\r\n `)}`;\r\n }\r\n}\r\n\r\ncustomElements.define('k-widget-content', ContentWidget);\r\n"],"mappings":"OAAOA,MAAqB,sDACnBC,MAAY,mDACZC,mBAAeC,uBAAeC,MAAyB,+BAKjD,MAAMC,UAAsBL,EACzCM,kBAAoB,IACfN,EAAgBO,WACnBC,OAAQ,CAAEC,OAAO,IAGnB,WAAAC,GACEC,QACAC,KAAKJ,OAAS,GACdI,KAAKC,IAAM,CAAEC,MAAO,iBAAkBC,KAAM,UAAWC,KAAM,2BAC/D,CAMAC,KAAOC,UACL,MAAOC,EAAWC,EAAWC,SAAiBC,QAAQC,IAAI,CACxDX,KAAKY,OAAOtB,KACZU,KAAKY,OAAOrB,KACZS,KAAKY,OAAOpB,OAEdQ,KAAKJ,OAAS,CACZ,CAAEM,MAAO,YAAaW,MAAON,EAAUM,MAAOT,KAAM,4BACpD,CAAEF,MAAO,YAAaW,MAAOL,EAAUK,MAAOT,KAAM,4BACpD,CAAEF,MAAO,iBAAkBW,MAAOJ,EAAQI,MAAOT,KAAM,4BAQ3D,aAAAU,GACE,OAAId,KAAKJ,OAAOmB,KAAK,EAAGF,WAAYA,GAC7BxB,CAAI,GAAGW,KAAKJ,OAAOoB,IAAI,EAAGd,QAAOW,QAAOT,UAAWf,CAAI;mCAC/Be,wBAA2BF,mBAAuBW;SAF9BxB,CAAI,wEAIzD,EAGF4B,eAAeC,OAAO,mBAAoBzB","ignoreList":[]}
@@ -0,0 +1,5 @@
1
+ import t from"/kempo/components/DashboardWidget.js";import{html as s}from"/kempo-ui/lit-all.min.js";import{listExtensions as e}from"/kempo/sdk.js";export default class i extends t{static properties={...t.properties,total:{state:!0},disabled:{state:!0}};constructor(){super(),this.total=0,this.disabled=0,this.cta={label:"Add extension",icon:"extension_add",permission:"system:extensions:install",href:"/admin/extensions"}}load=async()=>{const{items:t,total:s}=await this.unwrap(e({limit:100}));this.total=s,this.disabled=t.filter(t=>!t.enabled).length};renderContent(){return this.total?s`
2
+ <div class="h1 m0">${this.total}</div>
3
+ <div class="tc-muted">${1===this.total?"Extension":"Extensions"}${this.disabled?s`, ${this.disabled} disabled`:""}</div>
4
+ `:s`<p class="tc-muted">No extensions installed.</p>`}}customElements.define("k-widget-extensions",i);
5
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\admin\components\widgets\Extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\admin\\components\\widgets\\Extensions.js","names":["DashboardWidget","html","listExtensions","ExtensionsWidget","static","properties","total","state","disabled","constructor","super","this","cta","label","icon","permission","href","load","async","items","unwrap","limit","filter","item","enabled","length","renderContent","customElements","define"],"sources":["0"],"sourcesContent":["import DashboardWidget from '/kempo/components/DashboardWidget.js';\r\nimport { html } from '/kempo-ui/lit-all.min.js';\r\nimport { listExtensions } from '/kempo/sdk.js';\r\n\r\n/*\r\n How many extensions are installed and how many of those are switched off.\r\n*/\r\nexport default class ExtensionsWidget extends DashboardWidget {\r\n static properties = {\r\n ...DashboardWidget.properties,\r\n total: { state: true },\r\n disabled: { state: true }\r\n };\r\n\r\n constructor(){\r\n super();\r\n this.total = 0;\r\n this.disabled = 0;\r\n this.cta = { label: 'Add extension', icon: 'extension_add', permission: 'system:extensions:install', href: '/admin/extensions' };\r\n }\r\n\r\n /*\r\n Public Methods\r\n */\r\n\r\n load = async () => {\r\n const { items, total } = await this.unwrap(listExtensions({ limit: 100 }));\r\n this.total = total;\r\n this.disabled = items.filter(item => !item.enabled).length;\r\n };\r\n\r\n /*\r\n Rendering\r\n */\r\n\r\n renderContent(){\r\n if(!this.total) return html`<p class=\"tc-muted\">No extensions installed.</p>`;\r\n return html`\r\n <div class=\"h1 m0\">${this.total}</div>\r\n <div class=\"tc-muted\">${this.total === 1 ? 'Extension' : 'Extensions'}${this.disabled ? html`, ${this.disabled} disabled` : ''}</div>\r\n `;\r\n }\r\n}\r\n\r\ncustomElements.define('k-widget-extensions', ExtensionsWidget);\r\n"],"mappings":"OAAOA,MAAqB,sDACnBC,MAAY,oDACZC,MAAsB,+BAKhB,MAAMC,UAAyBH,EAC5CI,kBAAoB,IACfJ,EAAgBK,WACnBC,MAAO,CAAEC,OAAO,GAChBC,SAAU,CAAED,OAAO,IAGrB,WAAAE,GACEC,QACAC,KAAKL,MAAQ,EACbK,KAAKH,SAAW,EAChBG,KAAKC,IAAM,CAAEC,MAAO,gBAAiBC,KAAM,gBAAiBC,WAAY,4BAA6BC,KAAM,oBAC7G,CAMAC,KAAOC,UACL,MAAMC,MAAEA,EAAKb,MAAEA,SAAgBK,KAAKS,OAAOlB,EAAe,CAAEmB,MAAO,OACnEV,KAAKL,MAAQA,EACbK,KAAKH,SAAWW,EAAMG,OAAOC,IAASA,EAAKC,SAASC,QAOtD,aAAAC,GACE,OAAIf,KAAKL,MACFL,CAAI;2BACYU,KAAKL;8BACa,IAAfK,KAAKL,MAAc,YAAc,eAAeK,KAAKH,SAAWP,CAAI,KAAKU,KAAKH,oBAAsB;MAHvGP,CAAI,kDAK7B,EAGF0B,eAAeC,OAAO,sBAAuBzB","ignoreList":[]}
@@ -0,0 +1,4 @@
1
+ import t from"/kempo/components/DashboardWidget.js";import{html as i}from"/kempo-ui/lit-all.min.js";import"/kempo/components/NotificationList.js";import{markAllNotificationsRead as o}from"/kempo/sdk.js";export default class e extends t{constructor(){super(),this.cta={label:"Mark all read",icon:"done_all",onClick:()=>this.markAllRead()}}markAllRead=async()=>{await o(),await(this.querySelector("k-notification-list")?.refresh())};renderContent(){return i`
2
+ <k-notification-list compact unread-only page-size=${3*this.height}></k-notification-list>
3
+ `}}customElements.define("k-widget-notifications",e);
4
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\admin\components\widgets\Notifications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\admin\\components\\widgets\\Notifications.js","names":["DashboardWidget","html","markAllNotificationsRead","NotificationsWidget","constructor","super","this","cta","label","icon","onClick","markAllRead","async","querySelector","refresh","renderContent","height","customElements","define"],"sources":["0"],"sourcesContent":["import DashboardWidget from '/kempo/components/DashboardWidget.js';\r\nimport { html } from '/kempo-ui/lit-all.min.js';\r\nimport '/kempo/components/NotificationList.js';\r\nimport { markAllNotificationsRead } from '/kempo/sdk.js';\r\n\r\n/*\r\n The signed-in user's unread notifications. The list loads itself, so this widget has nothing to fetch.\r\n*/\r\nexport default class NotificationsWidget extends DashboardWidget {\r\n constructor(){\r\n super();\r\n this.cta = { label: 'Mark all read', icon: 'done_all', onClick: () => this.markAllRead() };\r\n }\r\n\r\n /*\r\n Event Handlers\r\n */\r\n\r\n markAllRead = async () => {\r\n await markAllNotificationsRead();\r\n await this.querySelector('k-notification-list')?.refresh();\r\n };\r\n\r\n /*\r\n Rendering\r\n */\r\n\r\n renderContent(){\r\n return html`\r\n <k-notification-list compact unread-only page-size=${this.height * 3}></k-notification-list>\r\n `;\r\n }\r\n}\r\n\r\ncustomElements.define('k-widget-notifications', NotificationsWidget);\r\n"],"mappings":"OAAOA,MAAqB,sDACnBC,MAAY,iCACd,2EACEC,MAAgC,+BAK1B,MAAMC,UAA4BH,EAC/C,WAAAI,GACEC,QACAC,KAAKC,IAAM,CAAEC,MAAO,gBAAiBC,KAAM,WAAYC,QAAS,IAAMJ,KAAKK,cAC7E,CAMAA,YAAcC,gBACNV,UACAI,KAAKO,cAAc,wBAAwBC,YAOnD,aAAAC,GACE,OAAOd,CAAI;2DAC0D,EAAdK,KAAKU;KAE9D,EAGFC,eAAeC,OAAO,yBAA0Bf","ignoreList":[]}
@@ -0,0 +1,23 @@
1
+ import e from"/kempo/components/DashboardWidget.js";import{html as t,nothing as a}from"/kempo-ui/lit-all.min.js";import{listPages as s,listTemplates as i,createPage as l}from"/kempo/sdk.js";export default class d extends e{static properties={...e.properties,total:{state:!0},disabled:{state:!0},recent:{state:!0}};constructor(){super(),this.total=0,this.disabled=0,this.recent=[],this.cta={label:"New page",icon:"add",permission:"system:pages:create",onClick:()=>this.newPage()}}load=async()=>{const{pages:e,total:t}=await this.unwrap(s({showHidden:!0}));this.total=t,this.disabled=e.filter(e=>e.disabled).length,this.recent=e.slice().sort((e,t)=>(Date.parse(t.updatedAt)||0)-(Date.parse(e.updatedAt)||0))};newPage=async()=>{const{templates:e}=await this.unwrap(i());this.openForm({title:"New page",confirmText:"Create",fields:[{name:"name",label:"Page name",placeholder:"My new page"},{name:"template",label:"Template",type:"select",value:"default",options:e.map(({name:e})=>({value:e,label:e}))}],submit:async({name:e,template:t})=>{const[a,s]=await l({directory:"",name:e,template:t});if(a)return a.msg||"Could not create the page.";window.location.href=`/admin/content/pages/edit?path=${encodeURIComponent(s.file.replace(/\.page\.html$/,""))}`}})};renderHeader(){return t`
2
+ <header class="d-f px pyh bb" style="align-items: center;">
3
+ ${this.renderTitle()}
4
+ ${this.renderActions()}
5
+ <button class="no-btn pq" title="Refresh" aria-label="Refresh pages" @click=${this.refresh}><k-icon name="restart_alt"></k-icon></button>
6
+ </header>
7
+ `}renderContent(){const e=this.recent.slice(0,3*this.height);return t`
8
+ <div class="d-f mb">
9
+ <div class="flex"><div class="h2 m0">${this.total}</div><div class="small tc-muted">Pages</div></div>
10
+ <div class="flex"><div class="h2 m0">${this.total-this.disabled}</div><div class="small tc-muted">Enabled</div></div>
11
+ <div class="flex"><div class="h2 m0">${this.disabled}</div><div class="small tc-muted">Disabled</div></div>
12
+ </div>
13
+ ${e.length?t`
14
+ <div class="small tc-muted mbq">Recently updated</div>
15
+ ${e.map(e=>t`
16
+ <a class="d-b py bb" href="/admin/content/pages/edit?path=${encodeURIComponent(e.file.replace(/\.page(-disabled)?\.html$/,""))}">
17
+ ${e.title||e.name||e.url}
18
+ <span class="small tc-muted">${e.url}</span>
19
+ </a>
20
+ `)}
21
+ `:a}
22
+ `}}customElements.define("k-widget-pages",d);
23
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\admin\components\widgets\Pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\admin\\components\\widgets\\Pages.js","names":["DashboardWidget","html","nothing","listPages","listTemplates","createPage","PagesWidget","static","properties","total","state","disabled","recent","constructor","super","this","cta","label","icon","permission","onClick","newPage","load","async","pages","unwrap","showHidden","filter","page","length","slice","sort","a","b","Date","parse","updatedAt","templates","openForm","title","confirmText","fields","name","placeholder","type","value","options","map","submit","template","error","data","directory","msg","window","location","href","encodeURIComponent","file","replace","renderHeader","renderTitle","renderActions","refresh","renderContent","rows","height","url","customElements","define"],"sources":["0"],"sourcesContent":["import DashboardWidget from '/kempo/components/DashboardWidget.js';\nimport { html, nothing } from '/kempo-ui/lit-all.min.js';\nimport { listPages, listTemplates, createPage } from '/kempo/sdk.js';\n\n/*\n Page totals and the pages edited most recently. Shows more rows the taller the tile is.\n*/\nexport default class PagesWidget extends DashboardWidget {\n static properties = {\n ...DashboardWidget.properties,\n total: { state: true },\n disabled: { state: true },\n recent: { state: true }\n };\n\n constructor(){\n super();\n this.total = 0;\n this.disabled = 0;\n this.recent = [];\n this.cta = { label: 'New page', icon: 'add', permission: 'system:pages:create', onClick: () => this.newPage() };\n }\n\n /*\n Public Methods\n */\n\n load = async () => {\n const { pages, total } = await this.unwrap(listPages({ showHidden: true }));\n this.total = total;\n this.disabled = pages.filter(page => page.disabled).length;\n this.recent = pages.slice().sort((a, b) => (Date.parse(b.updatedAt) || 0) - (Date.parse(a.updatedAt) || 0));\n };\n\n /*\n Event Handlers\n */\n\n /*\n Creates a page at the site root from a template and opens it in the editor. The Pages screen has the\n full dialog, including choosing another directory.\n */\n newPage = async () => {\n const { templates } = await this.unwrap(listTemplates());\n this.openForm({\n title: 'New page',\n confirmText: 'Create',\n fields: [\n { name: 'name', label: 'Page name', placeholder: 'My new page' },\n { name: 'template', label: 'Template', type: 'select', value: 'default', options: templates.map(({ name }) => ({ value: name, label: name })) }\n ],\n submit: async ({ name, template }) => {\n const [error, data] = await createPage({ directory: '', name, template });\n if(error) return error.msg || 'Could not create the page.';\n window.location.href = `/admin/content/pages/edit?path=${encodeURIComponent(data.file.replace(/\\.page\\.html$/, ''))}`;\n }\n });\n };\n\n /*\n Rendering\n */\n\n renderHeader(){\n return html`\n <header class=\"d-f px pyh bb\" style=\"align-items: center;\">\n ${this.renderTitle()}\n ${this.renderActions()}\n <button class=\"no-btn pq\" title=\"Refresh\" aria-label=\"Refresh pages\" @click=${this.refresh}><k-icon name=\"restart_alt\"></k-icon></button>\n </header>\n `;\n }\n\n renderContent(){\n const rows = this.recent.slice(0, this.height * 3);\n return html`\n <div class=\"d-f mb\">\n <div class=\"flex\"><div class=\"h2 m0\">${this.total}</div><div class=\"small tc-muted\">Pages</div></div>\n <div class=\"flex\"><div class=\"h2 m0\">${this.total - this.disabled}</div><div class=\"small tc-muted\">Enabled</div></div>\n <div class=\"flex\"><div class=\"h2 m0\">${this.disabled}</div><div class=\"small tc-muted\">Disabled</div></div>\n </div>\n ${rows.length ? html`\n <div class=\"small tc-muted mbq\">Recently updated</div>\n ${rows.map(page => html`\n <a class=\"d-b py bb\" href=\"/admin/content/pages/edit?path=${encodeURIComponent(page.file.replace(/\\.page(-disabled)?\\.html$/, ''))}\">\n ${page.title || page.name || page.url}\n <span class=\"small tc-muted\">${page.url}</span>\n </a>\n `)}\n ` : nothing}\n `;\n }\n}\n\ncustomElements.define('k-widget-pages', PagesWidget);\n"],"mappings":"OAAOA,MAAqB,sDACnBC,aAAMC,MAAe,+CACrBC,mBAAWC,gBAAeC,MAAkB,+BAKtC,MAAMC,UAAoBN,EACvCO,kBAAoB,IACfP,EAAgBQ,WACnBC,MAAO,CAAEC,OAAO,GAChBC,SAAU,CAAED,OAAO,GACnBE,OAAQ,CAAEF,OAAO,IAGnB,WAAAG,GACEC,QACAC,KAAKN,MAAQ,EACbM,KAAKJ,SAAW,EAChBI,KAAKH,OAAS,GACdG,KAAKC,IAAM,CAAEC,MAAO,WAAYC,KAAM,MAAOC,WAAY,sBAAuBC,QAAS,IAAML,KAAKM,UACtG,CAMAC,KAAOC,UACL,MAAMC,MAAEA,EAAKf,MAAEA,SAAgBM,KAAKU,OAAOtB,EAAU,CAAEuB,YAAY,KACnEX,KAAKN,MAAQA,EACbM,KAAKJ,SAAWa,EAAMG,OAAOC,GAAQA,EAAKjB,UAAUkB,OACpDd,KAAKH,OAASY,EAAMM,QAAQC,KAAK,CAACC,EAAGC,KAAOC,KAAKC,MAAMF,EAAEG,YAAc,IAAMF,KAAKC,MAAMH,EAAEI,YAAc,KAW1Gf,QAAUE,UACR,MAAMc,UAAEA,SAAoBtB,KAAKU,OAAOrB,KACxCW,KAAKuB,SAAS,CACZC,MAAO,WACPC,YAAa,SACbC,OAAQ,CACN,CAAEC,KAAM,OAAQzB,MAAO,YAAa0B,YAAa,eACjD,CAAED,KAAM,WAAYzB,MAAO,WAAY2B,KAAM,SAAUC,MAAO,UAAWC,QAAST,EAAUU,IAAI,EAAGL,WAAW,CAAGG,MAAOH,EAAMzB,MAAOyB,OAEvIM,OAAQzB,OAASmB,OAAMO,eACrB,MAAOC,EAAOC,SAAc9C,EAAW,CAAE+C,UAAW,GAAIV,OAAMO,aAC9D,GAAGC,EAAO,OAAOA,EAAMG,KAAO,6BAC9BC,OAAOC,SAASC,KAAO,kCAAkCC,mBAAmBN,EAAKO,KAAKC,QAAQ,gBAAiB,WASrH,YAAAC,GACE,OAAO3D,CAAI;;UAELc,KAAK8C;UACL9C,KAAK+C;sFACuE/C,KAAKgD;;KAGzF,CAEA,aAAAC,GACE,MAAMC,EAAOlD,KAAKH,OAAOkB,MAAM,EAAiB,EAAdf,KAAKmD,QACvC,OAAOjE,CAAI;;+CAEgCc,KAAKN;+CACLM,KAAKN,MAAQM,KAAKJ;+CAClBI,KAAKJ;;QAE5CsD,EAAKpC,OAAS5B,CAAI;;UAEhBgE,EAAKlB,IAAInB,GAAQ3B,CAAI;sEACuCwD,mBAAmB7B,EAAK8B,KAAKC,QAAQ,4BAA6B;cAC1H/B,EAAKW,OAASX,EAAKc,MAAQd,EAAKuC;2CACHvC,EAAKuC;;;QAGtCjE;KAER,EAGFkE,eAAeC,OAAO,iBAAkB/D","ignoreList":[]}
@@ -0,0 +1,5 @@
1
+ import e from"/kempo/components/DashboardWidget.js";import{html as t}from"/kempo-ui/lit-all.min.js";import{getUsers as s,createUser as a}from"/kempo/sdk.js";export default class i extends e{static properties={...e.properties,total:{state:!0}};constructor(){super(),this.total=0,this.cta={label:"Add user",icon:"person_add",permission:"system:user:create",onClick:()=>this.addUser()}}load=async()=>{this.total=(await this.unwrap(s({limit:1}))).total};addUser=()=>this.openForm({title:"Add user",confirmText:"Create",fields:[{name:"name",label:"Name"},{name:"email",label:"Email",type:"email"},{name:"password",label:"Password",type:"password"}],submit:async({name:e,email:t,password:s})=>{const[i]=await a({name:e,email:t,password:s,emailVerified:!0});if(i)return i.msg||"Could not create the user.";await this.refresh()}});renderContent(){return t`
2
+ <div class="h1 m0">${this.total}</div>
3
+ <div class="tc-muted">${1===this.total?"User":"Users"}</div>
4
+ `}}customElements.define("k-widget-users",i);
5
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\admin\components\widgets\Users.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\admin\\components\\widgets\\Users.js","names":["DashboardWidget","html","getUsers","createUser","UsersWidget","static","properties","total","state","constructor","super","this","cta","label","icon","permission","onClick","addUser","load","async","unwrap","limit","openForm","title","confirmText","fields","name","type","submit","email","password","error","emailVerified","msg","refresh","renderContent","customElements","define"],"sources":["0"],"sourcesContent":["import DashboardWidget from '/kempo/components/DashboardWidget.js';\r\nimport { html } from '/kempo-ui/lit-all.min.js';\r\nimport { getUsers, createUser } from '/kempo/sdk.js';\r\n\r\n/*\r\n The number of accounts on the site.\r\n*/\r\nexport default class UsersWidget extends DashboardWidget {\r\n static properties = {\r\n ...DashboardWidget.properties,\r\n total: { state: true }\r\n };\r\n\r\n constructor(){\r\n super();\r\n this.total = 0;\r\n this.cta = { label: 'Add user', icon: 'person_add', permission: 'system:user:create', onClick: () => this.addUser() };\r\n }\r\n\r\n /*\r\n Public Methods\r\n */\r\n\r\n load = async () => {\r\n this.total = (await this.unwrap(getUsers({ limit: 1 }))).total;\r\n };\r\n\r\n /*\r\n Event Handlers\r\n */\r\n\r\n addUser = () => this.openForm({\r\n title: 'Add user',\r\n confirmText: 'Create',\r\n fields: [\r\n { name: 'name', label: 'Name' },\r\n { name: 'email', label: 'Email', type: 'email' },\r\n { name: 'password', label: 'Password', type: 'password' }\r\n ],\r\n submit: async ({ name, email, password }) => {\r\n const [error] = await createUser({ name, email, password, emailVerified: true });\r\n if(error) return error.msg || 'Could not create the user.';\r\n await this.refresh();\r\n }\r\n });\r\n\r\n /*\r\n Rendering\r\n */\r\n\r\n renderContent(){\r\n return html`\r\n <div class=\"h1 m0\">${this.total}</div>\r\n <div class=\"tc-muted\">${this.total === 1 ? 'User' : 'Users'}</div>\r\n `;\r\n }\r\n}\r\n\r\ncustomElements.define('k-widget-users', UsersWidget);\r\n"],"mappings":"OAAOA,MAAqB,sDACnBC,MAAY,8CACZC,gBAAUC,MAAkB,+BAKtB,MAAMC,UAAoBJ,EACvCK,kBAAoB,IACfL,EAAgBM,WACnBC,MAAO,CAAEC,OAAO,IAGlB,WAAAC,GACEC,QACAC,KAAKJ,MAAQ,EACbI,KAAKC,IAAM,CAAEC,MAAO,WAAYC,KAAM,aAAcC,WAAY,qBAAsBC,QAAS,IAAML,KAAKM,UAC5G,CAMAC,KAAOC,UACLR,KAAKJ,aAAeI,KAAKS,OAAOlB,EAAS,CAAEmB,MAAO,MAAOd,OAO3DU,QAAU,IAAMN,KAAKW,SAAS,CAC5BC,MAAO,WACPC,YAAa,SACbC,OAAQ,CACN,CAAEC,KAAM,OAAQb,MAAO,QACvB,CAAEa,KAAM,QAASb,MAAO,QAASc,KAAM,SACvC,CAAED,KAAM,WAAYb,MAAO,WAAYc,KAAM,aAE/CC,OAAQT,OAASO,OAAMG,QAAOC,eAC5B,MAAOC,SAAe5B,EAAW,CAAEuB,OAAMG,QAAOC,WAAUE,eAAe,IACzE,GAAGD,EAAO,OAAOA,EAAME,KAAO,mCACxBtB,KAAKuB,aAQf,aAAAC,GACE,OAAOlC,CAAI;2BACYU,KAAKJ;8BACa,IAAfI,KAAKJ,MAAc,OAAS;KAExD,EAGF6B,eAAeC,OAAO,iBAAkBjC","ignoreList":[]}
@@ -4,6 +4,7 @@
4
4
  -->
5
5
  <page title="Admin Dashboard">
6
6
  <content>
7
- <h1>Dashboard</h1>
7
+ <script type="module" src="/admin/components/Dashboard.js"></script>
8
+ <k-dashboard heading="Dashboard"></k-dashboard>
8
9
  </content>
9
10
  </page>
@@ -0,0 +1,2 @@
1
+ import s from"../../../../../server/utils/auth/getSession.js";import e from"../../../../../server/utils/dashboard/listDashboardWidgets.js";export default async(r,t)=>{const[o,i]=await s({token:r.cookies.session_token});if(o)return t.status(500===o.code?500:401).json({error:500===o.code?o.msg:"Authentication required"});const[a,n]=await e({userId:i.user.id});if(a)return t.status(a.code).json({error:a.msg});t.json(n)};
2
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\kempo\api\dashboard\widgets\GET.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\kempo\\api\\dashboard\\widgets\\GET.js","names":["getSession","listDashboardWidgets","async","request","response","sessionError","sessionData","token","cookies","session_token","status","code","json","error","msg","result","userId","user","id"],"sources":["0"],"sourcesContent":["import getSession from '../../../../../server/utils/auth/getSession.js';\nimport listDashboardWidgets from '../../../../../server/utils/dashboard/listDashboardWidgets.js';\n\nexport default async (request, response) => {\n const [sessionError, sessionData] = await getSession({ token: request.cookies.session_token });\n\n if(sessionError){\n return response.status(sessionError.code === 500 ? 500 : 401).json({ error: sessionError.code === 500 ? sessionError.msg : 'Authentication required' });\n }\n\n const [error, result] = await listDashboardWidgets({ userId: sessionData.user.id });\n\n if(error){\n return response.status(error.code).json({ error: error.msg });\n }\n\n response.json(result);\n};\n"],"mappings":"OAAOA,MAAgB,wDAChBC,MAA0B,+EAElBC,MAAOC,EAASC,KAC7B,MAAOC,EAAcC,SAAqBN,EAAW,CAAEO,MAAOJ,EAAQK,QAAQC,gBAE9E,GAAGJ,EACD,OAAOD,EAASM,OAA6B,MAAtBL,EAAaM,KAAe,IAAM,KAAKC,KAAK,CAAEC,MAA6B,MAAtBR,EAAaM,KAAeN,EAAaS,IAAM,4BAG7H,MAAOD,EAAOE,SAAgBd,EAAqB,CAAEe,OAAQV,EAAYW,KAAKC,KAE9E,GAAGL,EACD,OAAOT,EAASM,OAAOG,EAAMF,MAAMC,KAAK,CAAEC,MAAOA,EAAMC,MAGzDV,EAASQ,KAAKG","ignoreList":[]}
@@ -0,0 +1,18 @@
1
+ import e from"/kempo-ui/components/LightComponent.js";import{html as t,nothing as i}from"/kempo-ui/lit-all.min.js";import"/kempo-ui/components/Icon.js";import n from"/kempo-ui/components/Dialog.js";import{currentUserHasPermission as s}from"/kempo/sdk.js";export default class a extends e{static properties={width:{type:Number,reflect:!0},height:{type:Number,reflect:!0},definition:{attribute:!1},loading:{state:!0},failure:{state:!0},ctaAllowed:{state:!0}};constructor(){super(),this.width=2,this.height=1,this.definition=null,this.cta=null,this.loading=!0,this.failure="",this.ctaAllowed=!1}connectedCallback(){super.connectedCallback(),this.classList.add("d-b"),this.style.height="100%",this.refresh(),this.checkCta()}load=async()=>{};refresh=async()=>{try{await this.load(),this.failure=""}catch(e){this.failure=e?.message||"Could not load this widget."}this.loading=!1};getCta(){return this.cta}actions(){return[]}unwrap=async e=>{const[t,i]=await e;if(t)throw new Error(t.msg||"Request failed");return i};openForm=({title:e,fields:t,confirmText:i="Save",submit:s})=>{const a=document.createElement("div");a.className="p";const r={};t.forEach(({name:e,label:t,type:i="text",placeholder:n="",value:s="",options:o=[]})=>{const l=document.createElement("div");l.className="mb";const c=Object.assign(document.createElement("label"),{className:"d-b mbq",textContent:t});let d;"select"===i?(d=document.createElement("select"),d.className="full",o.forEach(e=>d.append(Object.assign(document.createElement("option"),{value:e.value,textContent:e.label,selected:e.value===s})))):d=Object.assign(document.createElement("input"),{type:i,className:"full",placeholder:n,value:s}),r[e]=d,l.append(c,d),a.append(l)});const o=Object.assign(document.createElement("p"),{className:"tc-danger m0",role:"alert"});a.append(o);const l=n.create(a,{title:e,confirmText:i,cancelText:"Cancel",confirmAction:async e=>{e.keepDialogOpen=!0,o.textContent="";const i=Object.fromEntries(t.map(({name:e})=>[e,r[e].value.trim()])),n=t.find(({name:e,required:t=!0})=>t&&!i[e]);if(n)return void(o.textContent=`${n.label} is required.`);const a=await s(i);a?o.textContent=a:l.close()}});return Object.values(r)[0]?.focus(),l};checkCta=async()=>{const e=this.getCta()?.permission;if(!e)return void(this.ctaAllowed=!0);const[t,i]=await s({permission:e});this.ctaAllowed=!t&&!!i?.hasPermission};renderTitle=()=>t`
2
+ ${this.definition?.icon?t`<k-icon class="mr" name=${this.definition.icon}></k-icon>`:i}
3
+ <strong class="flex">${this.definition?.title??""}</strong>
4
+ `;renderActions=()=>{const e=this.width<2;return[...this.definition?.actions??[],...this.actions()].map(({label:n,icon:s,href:a,onClick:r,primary:o})=>{const l=s?t`<k-icon name=${s}></k-icon>${e?i:t` ${n}`}`:n,c="btn ml "+(o?"primary":"");return a?t`<a class=${c} href=${a} title=${n} aria-label=${n}>${l}</a>`:t`<button class=${c} title=${n} aria-label=${n} @click=${r}>${l}</button>`})};renderHeader(){return t`<header class="d-f px pyh bb" style="align-items: center;">${this.renderTitle()}${this.renderActions()}</header>`}renderContent(){return i}renderBody(){return this.failure?t`
5
+ <p class="tc-danger" role="alert">${this.failure}</p>
6
+ <button @click=${this.refresh}>Try again</button>
7
+ `:this.loading?t`<p class="tc-muted">Loading&hellip;</p>`:this.renderContent()}renderFooter=()=>{const e=this.getCta();if(!e||!this.ctaAllowed||this.failure)return i;const n=t`${e.icon?t`<k-icon name=${e.icon}></k-icon> `:i}${e.label}`;return t`
8
+ <footer class="d-f px pyh bt" style="justify-content: flex-end;">
9
+ ${e.href?t`<a class="btn primary" href=${e.href}>${n}</a>`:t`<button class="primary" @click=${e.onClick}>${n}</button>`}
10
+ </footer>
11
+ `};renderLightDom(){return t`
12
+ <div style="display: flex; flex-direction: column; height: 100%; min-height: 0;">
13
+ ${this.renderHeader()}
14
+ <div class="p" style="flex: 1; min-height: 0; overflow: auto;">${this.renderBody()}</div>
15
+ ${this.renderFooter()}
16
+ </div>
17
+ `}}
18
+ //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\kempo\components\DashboardWidget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src\\kempo\\components\\DashboardWidget.js","names":["LightComponent","html","nothing","Dialog","currentUserHasPermission","DashboardWidget","static","width","type","Number","reflect","height","definition","attribute","loading","state","failure","ctaAllowed","constructor","super","this","cta","connectedCallback","classList","add","style","refresh","checkCta","load","async","error","message","getCta","actions","unwrap","data","request","Error","msg","openForm","title","fields","confirmText","submit","content","document","createElement","className","inputs","forEach","name","label","placeholder","value","options","wrapper","caption","Object","assign","textContent","input","option","append","selected","role","dialog","create","cancelText","confirmAction","event","keepDialogOpen","values","fromEntries","map","trim","missing","find","required","close","focus","permission","hasPermission","renderTitle","icon","renderActions","compact","href","onClick","primary","classes","renderHeader","renderContent","renderBody","renderFooter","renderLightDom"],"sources":["0"],"sourcesContent":["import LightComponent from '/kempo-ui/components/LightComponent.js';\nimport { html, nothing } from '/kempo-ui/lit-all.min.js';\nimport '/kempo-ui/components/Icon.js';\nimport Dialog from '/kempo-ui/components/Dialog.js';\nimport { currentUserHasPermission } from '/kempo/sdk.js';\n\n/*\n The base class for an admin dashboard widget. Extend it, define your element, and register the widget\n with `registerDashboardWidget` (or `kempo.widgets` in package.json).\n\n The dashboard creates the element only when the user has the widget on screen, and sets `width` and\n `height` (in blocks) on it, so a widget never loads data for a tile that is hidden. Override:\n\n `load()` fetch what the widget needs and assign it to reactive properties. Throw to show\n the failure message with a retry button. Runs when the element connects and\n again on `refresh()`.\n `renderContent()` return the markup for a loaded widget. Use `width` and `height` to show more\n when there is room.\n\n Declare your own properties by spreading the base ones:\n `static properties = { ...DashboardWidget.properties, items: { state: true } }`.\n\n The widget fills a bordered tile and owns everything inside it, its header included. `renderHeader()`\n returns the header region; the default is the widget's icon and title from `definition` (the registered\n widget, set by the dashboard). Override it to add buttons, filters or a different title, and use\n `renderTitle()` to keep the default title inside your own header.\n\n Call to action. A widget may offer one primary thing to do, drawn the same way in every tile as a button\n in a footer. It is optional. Set `this.cta` in the constructor (or override `getCta()` when it depends on\n state) to `{ label, icon, href }` for a link, or `{ label, icon, onClick }` for a callback, and add\n `permission` to show it only to people who hold that permission. For a callback that needs input, call\n `openForm()` to get a standard dialog.\n\n Header actions are separate: links declared on the registration (`actions`), or returned from\n `actions()`, appear as small buttons beside the title.\n\n Call `super.updated(changed)` if you override `updated()`.\n*/\nexport default class DashboardWidget extends LightComponent {\n static properties = {\n width: { type: Number, reflect: true },\n height: { type: Number, reflect: true },\n definition: { attribute: false },\n loading: { state: true },\n failure: { state: true },\n ctaAllowed: { state: true }\n };\n\n constructor(){\n super();\n this.width = 2;\n this.height = 1;\n this.definition = null;\n this.cta = null;\n this.loading = true;\n this.failure = '';\n this.ctaAllowed = false;\n }\n\n /*\n Lifecycle Callbacks\n */\n\n connectedCallback(){\n super.connectedCallback();\n this.classList.add('d-b');\n this.style.height = '100%';\n this.refresh();\n this.checkCta();\n }\n\n /*\n Public Methods\n */\n\n load = async () => {};\n\n refresh = async () => {\n try {\n await this.load();\n this.failure = '';\n } catch(error) {\n this.failure = error?.message || 'Could not load this widget.';\n }\n this.loading = false;\n };\n\n getCta(){\n return this.cta;\n }\n\n actions(){\n return [];\n }\n\n /*\n Resolves an SDK call, which returns `[error, data]`, to its data, or throws the error's message so\n `load()` can stay a straight line.\n */\n unwrap = async request => {\n const [error, data] = await request;\n if(error) throw new Error(error.msg || 'Request failed');\n return data;\n };\n\n /*\n Opens a standard dialog with a few fields and a confirm button.\n\n `fields` are `{ name, label, type, placeholder, value, options, required }` where `type` is `text`\n (default), `email`, `password` or `select` (then `options` is `[{ value, label }]`). `submit` receives\n `{ [name]: value }` and returns nothing on success, which closes the dialog, or an error message, which\n is shown in the dialog and keeps it open with what was typed.\n */\n openForm = ({ title, fields, confirmText = 'Save', submit }) => {\n const content = document.createElement('div');\n content.className = 'p';\n const inputs = {};\n\n fields.forEach(({ name, label, type = 'text', placeholder = '', value = '', options = [] }) => {\n const wrapper = document.createElement('div');\n wrapper.className = 'mb';\n const caption = Object.assign(document.createElement('label'), { className: 'd-b mbq', textContent: label });\n let input;\n\n if(type === 'select'){\n input = document.createElement('select');\n input.className = 'full';\n options.forEach(option => input.append(Object.assign(document.createElement('option'), { value: option.value, textContent: option.label, selected: option.value === value })));\n } else {\n input = Object.assign(document.createElement('input'), { type, className: 'full', placeholder, value });\n }\n\n inputs[name] = input;\n wrapper.append(caption, input);\n content.append(wrapper);\n });\n\n const error = Object.assign(document.createElement('p'), { className: 'tc-danger m0', role: 'alert' });\n content.append(error);\n\n const dialog = Dialog.create(content, {\n title,\n confirmText,\n cancelText: 'Cancel',\n confirmAction: async event => {\n event.keepDialogOpen = true;\n error.textContent = '';\n const values = Object.fromEntries(fields.map(({ name }) => [name, inputs[name].value.trim()]));\n const missing = fields.find(({ name, required = true }) => required && !values[name]);\n\n if(missing){\n error.textContent = `${missing.label} is required.`;\n return;\n }\n\n const message = await submit(values);\n\n if(message) error.textContent = message;\n else dialog.close();\n }\n });\n\n Object.values(inputs)[0]?.focus();\n return dialog;\n };\n\n /*\n Utility Functions\n */\n\n checkCta = async () => {\n const permission = this.getCta()?.permission;\n\n if(!permission){\n this.ctaAllowed = true;\n return;\n }\n\n const [error, data] = await currentUserHasPermission({ permission });\n this.ctaAllowed = !error && !!data?.hasPermission;\n };\n\n /*\n Rendering\n */\n\n renderTitle = () => html`\n ${this.definition?.icon ? html`<k-icon class=\"mr\" name=${this.definition.icon}></k-icon>` : nothing}\n <strong class=\"flex\">${this.definition?.title ?? ''}</strong>\n `;\n\n renderActions = () => {\n const compact = this.width < 2;\n return [...(this.definition?.actions ?? []), ...this.actions()].map(({ label, icon, href, onClick, primary }) => {\n const content = icon\n ? html`<k-icon name=${icon}></k-icon>${compact ? nothing : html` ${label}`}`\n : label;\n const classes = `btn ml ${primary ? 'primary' : ''}`;\n return href\n ? html`<a class=${classes} href=${href} title=${label} aria-label=${label}>${content}</a>`\n : html`<button class=${classes} title=${label} aria-label=${label} @click=${onClick}>${content}</button>`;\n });\n };\n\n renderHeader(){\n return html`<header class=\"d-f px pyh bb\" style=\"align-items: center;\">${this.renderTitle()}${this.renderActions()}</header>`;\n }\n\n renderContent(){\n return nothing;\n }\n\n renderBody(){\n if(this.failure){\n return html`\n <p class=\"tc-danger\" role=\"alert\">${this.failure}</p>\n <button @click=${this.refresh}>Try again</button>\n `;\n }\n if(this.loading) return html`<p class=\"tc-muted\">Loading&hellip;</p>`;\n return this.renderContent();\n }\n\n renderFooter = () => {\n const cta = this.getCta();\n\n if(!cta || !this.ctaAllowed || this.failure) return nothing;\n\n const content = html`${cta.icon ? html`<k-icon name=${cta.icon}></k-icon> ` : nothing}${cta.label}`;\n\n return html`\n <footer class=\"d-f px pyh bt\" style=\"justify-content: flex-end;\">\n ${cta.href\n ? html`<a class=\"btn primary\" href=${cta.href}>${content}</a>`\n : html`<button class=\"primary\" @click=${cta.onClick}>${content}</button>`}\n </footer>\n `;\n };\n\n renderLightDom(){\n return html`\n <div style=\"display: flex; flex-direction: column; height: 100%; min-height: 0;\">\n ${this.renderHeader()}\n <div class=\"p\" style=\"flex: 1; min-height: 0; overflow: auto;\">${this.renderBody()}</div>\n ${this.renderFooter()}\n </div>\n `;\n }\n}\n"],"mappings":"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","ignoreList":[]}
package/dist/kempo/sdk.js CHANGED
@@ -1,2 +1,2 @@
1
- import{get as e,post as s,put as o,patch as n,del as t}from"./fetch.js";const a="/kempo/api";export const register=async({email:e,password:o,name:n})=>s(`${a}/auth/register/email`,{email:e,password:o,name:n});export const login=async({email:e,password:o})=>s(`${a}/auth/login/email`,{email:e,password:o});export const logout=async()=>s(`${a}/auth/logout`,{});export const getSession=async()=>e(`${a}/auth/session`);export const forgotPassword=async({email:e})=>s(`${a}/forgot-password`,{email:e});export const resetPassword=async({token:e,password:o,logoutAll:n=!1})=>s(`${a}/auth/reset-password`,{token:e,newPassword:o,logoutAll:n});export const changePassword=async({currentPassword:e,newPassword:o,logoutAll:n})=>s(`${a}/auth/change-password`,{currentPassword:e,newPassword:o,logoutAll:n});export const verifyEmail=async({token:e})=>s(`${a}/auth/verify-email`,{token:e});export const currentUserHasPermission=async({permission:s})=>e(`${a}/user/current/permissions`,{permission:s});export const currentUserHasAllPermissions=async({permissions:s})=>e(`${a}/user/current/permissions/all`,{permissions:s});export const currentUserHasSomePermissions=async({permissions:s})=>e(`${a}/user/current/permissions/some`,{permissions:s});export const getAllCurrentUserPermissions=async()=>e(`${a}/user/current/permissions/list`);export const userHasPermission=async({userid:s,permission:o})=>e(`${a}/user/${s}/permissions`,{permission:o});export const userHasAllPermissions=async({userid:s,permissions:o})=>e(`${a}/user/${s}/permissions/all`,{permissions:o});export const userHasSomePermissions=async({userid:s,permissions:o})=>e(`${a}/user/${s}/permissions/some`,{permissions:o});export const getAllUserPermissions=async({userid:s})=>e(`${a}/user/${s}/permissions/list`);export const getUsers=async({limit:s,offset:o}={})=>e(`${a}/user`,{limit:s,offset:o});export const searchUsers=async({q:s,limit:o,offset:n,inGroup:t,notInGroup:r}={})=>e(`${a}/user/search`,{q:s,limit:o,offset:n,inGroup:t,notInGroup:r});export const createUser=async({name:e,email:o,password:n,emailVerified:t})=>s(`${a}/user/create`,{name:e,email:o,password:n,emailVerified:t});export const deleteUsers=async e=>t(`${a}/user`,{ids:e});export const updateUser=async({id:e,name:s,email:o,emailVerified:t,createdAt:r})=>n(`${a}/user`,{id:e,name:s,email:o,emailVerified:t,createdAt:r});export const getUser=async s=>e(`${a}/user/${s}`);export const getUserSessions=async(s,{limit:o,offset:n}={})=>e(`${a}/user/${s}/sessions`,{limit:o,offset:n});export const deleteUserSession=async(e,s)=>t(`${a}/user/${e}/sessions`,{sessionToken:s});export const deleteExpiredUserSessions=async e=>t(`${a}/user/${e}/sessions/expired`);export const getUserGroups=async(s,{limit:o,offset:n}={})=>e(`${a}/user/${s}/groups`,{limit:o,offset:n});export const addUserToGroup=async(e,o)=>s(`${a}/user/${e}/groups`,{groupName:o});export const removeUserFromGroup=async(e,s)=>t(`${a}/user/${e}/groups`,{groupName:s});export const listGroups=async({limit:s,offset:o,owner:n}={})=>e(`${a}/groups`,{limit:s,offset:o,owner:n});export const createGroup=async({name:e,description:o})=>s(`${a}/groups`,{name:e,description:o});export const updateGroup=async({name:e,newName:s,description:o})=>n(`${a}/groups`,{name:e,newName:s,description:o});export const deleteGroups=async e=>t(`${a}/groups`,{names:e});export const getGroup=async s=>e(`${a}/groups/${encodeURIComponent(s)}`);export const getGroupMembers=async(s,{limit:o,offset:n}={})=>e(`${a}/groups/${encodeURIComponent(s)}/members`,{limit:o,offset:n});export const addMemberToGroup=async(e,o)=>s(`${a}/groups/${encodeURIComponent(e)}/members`,{userId:o});export const removeMemberFromGroup=async(e,s)=>t(`${a}/groups/${encodeURIComponent(e)}/members`,{userId:s});export const getGroupPermissions=async s=>e(`${a}/groups/${encodeURIComponent(s)}/permissions`);export const addPermissionToGroup=async(e,o)=>s(`${a}/groups/${encodeURIComponent(e)}/permissions`,{permissionName:o});export const removePermissionFromGroup=async(e,s)=>t(`${a}/groups/${encodeURIComponent(e)}/permissions`,{permissionName:s});export const listPermissions=async({limit:s,offset:o,owner:n}={})=>e(`${a}/permissions`,{limit:s,offset:o,owner:n});export const createPermissions=async e=>s(`${a}/permissions`,{permissions:e});export const updatePermission=async({name:e,description:s})=>n(`${a}/permissions`,{name:e,description:s});export const deletePermissions=async e=>t(`${a}/permissions`,{names:e});export const getSetting=async(s,o)=>e(`${a}/settings/${encodeURIComponent(s)}/${encodeURIComponent(o)}`);export const listSettings=async({owner:s,limit:o,offset:n}={})=>e(`${a}/settings/${encodeURIComponent(s)}`,{limit:o,offset:n});export const setSetting=async(e,o,n)=>s(`${a}/settings/${encodeURIComponent(e)}/${encodeURIComponent(o)}`,n);export const deleteSetting=async(e,s)=>t(`${a}/settings/${encodeURIComponent(e)}/${encodeURIComponent(s)}`);export const listPages=async({owner:s,showHidden:o,offset:n,limit:t}={})=>e(`${a}/pages`,{owner:s,showHidden:o,offset:n,limit:t});export const rescanPages=async()=>s(`${a}/pages/rescan`,{});export const listTemplates=async()=>e(`${a}/templates`);export const listDirectories=async()=>e(`${a}/pages/directories`);export const getPage=async s=>e(`${a}/pages/file`,{file:s});export const createPage=async({directory:e,name:o,template:n})=>s(`${a}/pages`,{directory:e,name:o,template:n});export const updatePage=async({file:e,name:s,title:n,description:t,author:r,template:i,contents:c,extraMetadata:l})=>o(`${a}/pages/file`,{file:e,name:s,title:n,description:t,author:r,template:i,contents:c,extraMetadata:l});export const deletePages=async e=>t(`${a}/pages`,{files:e});export const disablePage=async e=>o(`${a}/pages/disable`,{file:e});export const enablePage=async e=>o(`${a}/pages/enable`,{file:e});export const movePage=async({file:e,newFile:s})=>n(`${a}/pages/file`,{file:e,newFile:s});export const searchPagesByMetadata=async s=>e(`${a}/pages/search`,{query:JSON.stringify(s)});export const setPageLocked=async({file:e,locked:s})=>o(`${a}/pages/lock`,{file:e,locked:s});export const getTemplate=async s=>e(`${a}/templates/file`,{file:s});export const createTemplate=async({directory:e,name:o,copyFrom:n})=>s(`${a}/templates`,{directory:e,name:o,copyFrom:n});export const updateTemplate=async({file:e,name:s,author:n,markup:t})=>o(`${a}/templates/file`,{file:e,name:s,author:n,markup:t});export const deleteTemplates=async e=>t(`${a}/templates`,{files:e});export const disableTemplate=async e=>o(`${a}/templates/disable`,{file:e});export const enableTemplate=async e=>o(`${a}/templates/enable`,{file:e});export const moveTemplate=async({file:e,newFile:s})=>n(`${a}/templates/file`,{file:e,newFile:s});export const setTemplateLocked=async({file:e,locked:s})=>o(`${a}/templates/lock`,{file:e,locked:s});export const listFragments=async()=>e(`${a}/fragments`);export const getFragment=async s=>e(`${a}/fragments/file`,{file:s});export const createFragment=async({directory:e,name:o})=>s(`${a}/fragments`,{directory:e,name:o});export const updateFragment=async({file:e,author:s,markup:n})=>o(`${a}/fragments/file`,{file:e,author:s,markup:n});export const deleteFragments=async e=>t(`${a}/fragments`,{files:e});export const moveFragment=async({file:e,newFile:s})=>n(`${a}/fragments/file`,{file:e,newFile:s});export const disableFragment=async e=>o(`${a}/fragments/disable`,{file:e});export const enableFragment=async e=>o(`${a}/fragments/enable`,{file:e});export const setFragmentLocked=async({file:e,locked:s})=>o(`${a}/fragments/lock`,{file:e,locked:s});export const listGlobalContent=async()=>e(`${a}/globals`);export const getGlobalContent=async s=>e(`${a}/globals/entry`,{id:s});export const createGlobalContent=async({name:e,location:o,priority:n})=>s(`${a}/globals`,{name:e,location:o,priority:n});export const updateGlobalContent=async({id:e,name:s,location:n,priority:t,markup:r})=>o(`${a}/globals/entry`,{id:e,name:s,location:n,priority:t,markup:r});export const deleteGlobalContent=async e=>t(`${a}/globals`,{ids:e});export const disableGlobalContent=async e=>o(`${a}/globals/disable`,{id:e});export const enableGlobalContent=async e=>o(`${a}/globals/enable`,{id:e});export const setGlobalContentLocked=async({id:e,locked:s})=>o(`${a}/globals/lock`,{id:e,locked:s});export const listExtensions=async({limit:s,offset:o}={})=>e(`${a}/extensions`,{limit:s,offset:o});export const listAvailableExtensions=async()=>e(`${a}/extensions/available`);export const listKnownExtensions=async()=>e(`${a}/extensions/known`);export const getNotifications=async({unreadOnly:s=!1,limit:o,offset:n}={})=>e(`${a}/notifications`,{unreadOnly:s,limit:o,offset:n});export const getUnreadNotificationCount=async()=>e(`${a}/notifications/count`);export const markNotificationRead=async e=>s(`${a}/notifications/${encodeURIComponent(e)}/read`,{});export const markNotificationHandled=async e=>s(`${a}/notifications/${encodeURIComponent(e)}/handled`,{});export const markAllNotificationsRead=async()=>s(`${a}/notifications/read-all`,{});export const dismissNotification=async e=>t(`${a}/notifications/${encodeURIComponent(e)}`);export const listRealtimeConnections=async()=>e(`${a}/realtime/connections`);export const installExtension=async e=>s(`${a}/extensions`,{name:e});export const uninstallExtension=async(e,s=!1)=>t(`${a}/extensions`,{name:e,purgeData:s});export const enableExtension=async e=>s(`${a}/extensions/${encodeURIComponent(e)}/enable`,{});export const disableExtension=async e=>s(`${a}/extensions/${encodeURIComponent(e)}/disable`,{});export const listAdminGlobalContent=async()=>e(`${a}/admin-globals`);export const getAdminGlobalContent=async s=>e(`${a}/admin-globals/${encodeURIComponent(s)}`);export const createAdminGlobalContent=async({name:e,location:o,priority:n,owner:t,markup:r})=>s(`${a}/admin-globals`,{name:e,location:o,priority:n,owner:t,markup:r});export const updateAdminGlobalContent=async(e,{name:s,location:n,priority:t,markup:r})=>o(`${a}/admin-globals/${encodeURIComponent(e)}`,{name:s,location:n,priority:t,markup:r});export const deleteAdminGlobalContent=async e=>t(`${a}/admin-globals`,{ids:e});export const enableAdminGlobalContent=async e=>o(`${a}/admin-globals/${encodeURIComponent(e)}/enable`,{});export const disableAdminGlobalContent=async e=>o(`${a}/admin-globals/${encodeURIComponent(e)}/disable`,{});
1
+ import{get as e,post as s,put as o,patch as t,del as n}from"./fetch.js";const a="/kempo/api";export const register=async({email:e,password:o,name:t})=>s(`${a}/auth/register/email`,{email:e,password:o,name:t});export const login=async({email:e,password:o})=>s(`${a}/auth/login/email`,{email:e,password:o});export const logout=async()=>s(`${a}/auth/logout`,{});export const getSession=async()=>e(`${a}/auth/session`);export const forgotPassword=async({email:e})=>s(`${a}/forgot-password`,{email:e});export const resetPassword=async({token:e,password:o,logoutAll:t=!1})=>s(`${a}/auth/reset-password`,{token:e,newPassword:o,logoutAll:t});export const changePassword=async({currentPassword:e,newPassword:o,logoutAll:t})=>s(`${a}/auth/change-password`,{currentPassword:e,newPassword:o,logoutAll:t});export const verifyEmail=async({token:e})=>s(`${a}/auth/verify-email`,{token:e});export const currentUserHasPermission=async({permission:s})=>e(`${a}/user/current/permissions`,{permission:s});export const currentUserHasAllPermissions=async({permissions:s})=>e(`${a}/user/current/permissions/all`,{permissions:s});export const currentUserHasSomePermissions=async({permissions:s})=>e(`${a}/user/current/permissions/some`,{permissions:s});export const getAllCurrentUserPermissions=async()=>e(`${a}/user/current/permissions/list`);export const userHasPermission=async({userid:s,permission:o})=>e(`${a}/user/${s}/permissions`,{permission:o});export const userHasAllPermissions=async({userid:s,permissions:o})=>e(`${a}/user/${s}/permissions/all`,{permissions:o});export const userHasSomePermissions=async({userid:s,permissions:o})=>e(`${a}/user/${s}/permissions/some`,{permissions:o});export const getAllUserPermissions=async({userid:s})=>e(`${a}/user/${s}/permissions/list`);export const getUsers=async({limit:s,offset:o}={})=>e(`${a}/user`,{limit:s,offset:o});export const searchUsers=async({q:s,limit:o,offset:t,inGroup:n,notInGroup:r}={})=>e(`${a}/user/search`,{q:s,limit:o,offset:t,inGroup:n,notInGroup:r});export const createUser=async({name:e,email:o,password:t,emailVerified:n})=>s(`${a}/user/create`,{name:e,email:o,password:t,emailVerified:n});export const deleteUsers=async e=>n(`${a}/user`,{ids:e});export const updateUser=async({id:e,name:s,email:o,emailVerified:n,createdAt:r})=>t(`${a}/user`,{id:e,name:s,email:o,emailVerified:n,createdAt:r});export const getUser=async s=>e(`${a}/user/${s}`);export const getUserSessions=async(s,{limit:o,offset:t}={})=>e(`${a}/user/${s}/sessions`,{limit:o,offset:t});export const deleteUserSession=async(e,s)=>n(`${a}/user/${e}/sessions`,{sessionToken:s});export const deleteExpiredUserSessions=async e=>n(`${a}/user/${e}/sessions/expired`);export const getUserGroups=async(s,{limit:o,offset:t}={})=>e(`${a}/user/${s}/groups`,{limit:o,offset:t});export const addUserToGroup=async(e,o)=>s(`${a}/user/${e}/groups`,{groupName:o});export const removeUserFromGroup=async(e,s)=>n(`${a}/user/${e}/groups`,{groupName:s});export const listGroups=async({limit:s,offset:o,owner:t}={})=>e(`${a}/groups`,{limit:s,offset:o,owner:t});export const createGroup=async({name:e,description:o})=>s(`${a}/groups`,{name:e,description:o});export const updateGroup=async({name:e,newName:s,description:o})=>t(`${a}/groups`,{name:e,newName:s,description:o});export const deleteGroups=async e=>n(`${a}/groups`,{names:e});export const getGroup=async s=>e(`${a}/groups/${encodeURIComponent(s)}`);export const getGroupMembers=async(s,{limit:o,offset:t}={})=>e(`${a}/groups/${encodeURIComponent(s)}/members`,{limit:o,offset:t});export const addMemberToGroup=async(e,o)=>s(`${a}/groups/${encodeURIComponent(e)}/members`,{userId:o});export const removeMemberFromGroup=async(e,s)=>n(`${a}/groups/${encodeURIComponent(e)}/members`,{userId:s});export const getGroupPermissions=async s=>e(`${a}/groups/${encodeURIComponent(s)}/permissions`);export const addPermissionToGroup=async(e,o)=>s(`${a}/groups/${encodeURIComponent(e)}/permissions`,{permissionName:o});export const removePermissionFromGroup=async(e,s)=>n(`${a}/groups/${encodeURIComponent(e)}/permissions`,{permissionName:s});export const listPermissions=async({limit:s,offset:o,owner:t}={})=>e(`${a}/permissions`,{limit:s,offset:o,owner:t});export const createPermissions=async e=>s(`${a}/permissions`,{permissions:e});export const updatePermission=async({name:e,description:s})=>t(`${a}/permissions`,{name:e,description:s});export const deletePermissions=async e=>n(`${a}/permissions`,{names:e});export const getSetting=async(s,o)=>e(`${a}/settings/${encodeURIComponent(s)}/${encodeURIComponent(o)}`);export const listSettings=async({owner:s,limit:o,offset:t}={})=>e(`${a}/settings/${encodeURIComponent(s)}`,{limit:o,offset:t});export const setSetting=async(e,o,t)=>s(`${a}/settings/${encodeURIComponent(e)}/${encodeURIComponent(o)}`,t);export const deleteSetting=async(e,s)=>n(`${a}/settings/${encodeURIComponent(e)}/${encodeURIComponent(s)}`);export const listPages=async({owner:s,showHidden:o,offset:t,limit:n}={})=>e(`${a}/pages`,{owner:s,showHidden:o,offset:t,limit:n});export const rescanPages=async()=>s(`${a}/pages/rescan`,{});export const listTemplates=async()=>e(`${a}/templates`);export const listDirectories=async()=>e(`${a}/pages/directories`);export const getPage=async s=>e(`${a}/pages/file`,{file:s});export const createPage=async({directory:e,name:o,template:t})=>s(`${a}/pages`,{directory:e,name:o,template:t});export const updatePage=async({file:e,name:s,title:t,description:n,author:r,template:i,contents:c,extraMetadata:l})=>o(`${a}/pages/file`,{file:e,name:s,title:t,description:n,author:r,template:i,contents:c,extraMetadata:l});export const deletePages=async e=>n(`${a}/pages`,{files:e});export const disablePage=async e=>o(`${a}/pages/disable`,{file:e});export const enablePage=async e=>o(`${a}/pages/enable`,{file:e});export const movePage=async({file:e,newFile:s})=>t(`${a}/pages/file`,{file:e,newFile:s});export const searchPagesByMetadata=async s=>e(`${a}/pages/search`,{query:JSON.stringify(s)});export const setPageLocked=async({file:e,locked:s})=>o(`${a}/pages/lock`,{file:e,locked:s});export const getTemplate=async s=>e(`${a}/templates/file`,{file:s});export const createTemplate=async({directory:e,name:o,copyFrom:t})=>s(`${a}/templates`,{directory:e,name:o,copyFrom:t});export const updateTemplate=async({file:e,name:s,author:t,markup:n})=>o(`${a}/templates/file`,{file:e,name:s,author:t,markup:n});export const deleteTemplates=async e=>n(`${a}/templates`,{files:e});export const disableTemplate=async e=>o(`${a}/templates/disable`,{file:e});export const enableTemplate=async e=>o(`${a}/templates/enable`,{file:e});export const moveTemplate=async({file:e,newFile:s})=>t(`${a}/templates/file`,{file:e,newFile:s});export const setTemplateLocked=async({file:e,locked:s})=>o(`${a}/templates/lock`,{file:e,locked:s});export const listFragments=async()=>e(`${a}/fragments`);export const getFragment=async s=>e(`${a}/fragments/file`,{file:s});export const createFragment=async({directory:e,name:o})=>s(`${a}/fragments`,{directory:e,name:o});export const updateFragment=async({file:e,author:s,markup:t})=>o(`${a}/fragments/file`,{file:e,author:s,markup:t});export const deleteFragments=async e=>n(`${a}/fragments`,{files:e});export const moveFragment=async({file:e,newFile:s})=>t(`${a}/fragments/file`,{file:e,newFile:s});export const disableFragment=async e=>o(`${a}/fragments/disable`,{file:e});export const enableFragment=async e=>o(`${a}/fragments/enable`,{file:e});export const setFragmentLocked=async({file:e,locked:s})=>o(`${a}/fragments/lock`,{file:e,locked:s});export const listGlobalContent=async()=>e(`${a}/globals`);export const getGlobalContent=async s=>e(`${a}/globals/entry`,{id:s});export const createGlobalContent=async({name:e,location:o,priority:t})=>s(`${a}/globals`,{name:e,location:o,priority:t});export const updateGlobalContent=async({id:e,name:s,location:t,priority:n,markup:r})=>o(`${a}/globals/entry`,{id:e,name:s,location:t,priority:n,markup:r});export const deleteGlobalContent=async e=>n(`${a}/globals`,{ids:e});export const disableGlobalContent=async e=>o(`${a}/globals/disable`,{id:e});export const enableGlobalContent=async e=>o(`${a}/globals/enable`,{id:e});export const setGlobalContentLocked=async({id:e,locked:s})=>o(`${a}/globals/lock`,{id:e,locked:s});export const listExtensions=async({limit:s,offset:o}={})=>e(`${a}/extensions`,{limit:s,offset:o});export const listAvailableExtensions=async()=>e(`${a}/extensions/available`);export const listKnownExtensions=async()=>e(`${a}/extensions/known`);export const getDashboardWidgets=async()=>e(`${a}/dashboard/widgets`);export const getNotifications=async({unreadOnly:s=!1,limit:o,offset:t}={})=>e(`${a}/notifications`,{unreadOnly:s,limit:o,offset:t});export const getUnreadNotificationCount=async()=>e(`${a}/notifications/count`);export const markNotificationRead=async e=>s(`${a}/notifications/${encodeURIComponent(e)}/read`,{});export const markNotificationHandled=async e=>s(`${a}/notifications/${encodeURIComponent(e)}/handled`,{});export const markAllNotificationsRead=async()=>s(`${a}/notifications/read-all`,{});export const dismissNotification=async e=>n(`${a}/notifications/${encodeURIComponent(e)}`);export const listRealtimeConnections=async()=>e(`${a}/realtime/connections`);export const installExtension=async e=>s(`${a}/extensions`,{name:e});export const uninstallExtension=async(e,s=!1)=>n(`${a}/extensions`,{name:e,purgeData:s});export const enableExtension=async e=>s(`${a}/extensions/${encodeURIComponent(e)}/enable`,{});export const disableExtension=async e=>s(`${a}/extensions/${encodeURIComponent(e)}/disable`,{});export const listAdminGlobalContent=async()=>e(`${a}/admin-globals`);export const getAdminGlobalContent=async s=>e(`${a}/admin-globals/${encodeURIComponent(s)}`);export const createAdminGlobalContent=async({name:e,location:o,priority:t,owner:n,markup:r})=>s(`${a}/admin-globals`,{name:e,location:o,priority:t,owner:n,markup:r});export const updateAdminGlobalContent=async(e,{name:s,location:t,priority:n,markup:r})=>o(`${a}/admin-globals/${encodeURIComponent(e)}`,{name:s,location:t,priority:n,markup:r});export const deleteAdminGlobalContent=async e=>n(`${a}/admin-globals`,{ids:e});export const enableAdminGlobalContent=async e=>o(`${a}/admin-globals/${encodeURIComponent(e)}/enable`,{});export const disableAdminGlobalContent=async e=>o(`${a}/admin-globals/${encodeURIComponent(e)}/disable`,{});
2
2
  //# sourceMappingURL=C:\Users\dusti\dev\kempo\dist\kempo\sdk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"src\\kempo\\sdk.js","names":["get","post","put","patch","del","API_BASE","register","async","email","password","name","login","logout","getSession","forgotPassword","resetPassword","token","logoutAll","newPassword","changePassword","currentPassword","verifyEmail","currentUserHasPermission","permission","currentUserHasAllPermissions","permissions","currentUserHasSomePermissions","getAllCurrentUserPermissions","userHasPermission","userid","userHasAllPermissions","userHasSomePermissions","getAllUserPermissions","getUsers","limit","offset","searchUsers","q","inGroup","notInGroup","createUser","emailVerified","deleteUsers","ids","updateUser","id","createdAt","getUser","getUserSessions","deleteUserSession","sessionToken","deleteExpiredUserSessions","getUserGroups","addUserToGroup","groupName","removeUserFromGroup","listGroups","owner","createGroup","description","updateGroup","newName","deleteGroups","names","getGroup","encodeURIComponent","getGroupMembers","addMemberToGroup","userId","removeMemberFromGroup","getGroupPermissions","addPermissionToGroup","permissionName","removePermissionFromGroup","listPermissions","createPermissions","updatePermission","deletePermissions","getSetting","listSettings","setSetting","data","deleteSetting","listPages","showHidden","rescanPages","listTemplates","listDirectories","getPage","file","createPage","directory","template","updatePage","title","author","contents","extraMetadata","deletePages","files","disablePage","enablePage","movePage","newFile","searchPagesByMetadata","query","JSON","stringify","setPageLocked","locked","getTemplate","createTemplate","copyFrom","updateTemplate","markup","deleteTemplates","disableTemplate","enableTemplate","moveTemplate","setTemplateLocked","listFragments","getFragment","createFragment","updateFragment","deleteFragments","moveFragment","disableFragment","enableFragment","setFragmentLocked","listGlobalContent","getGlobalContent","createGlobalContent","location","priority","updateGlobalContent","deleteGlobalContent","disableGlobalContent","enableGlobalContent","setGlobalContentLocked","listExtensions","listAvailableExtensions","listKnownExtensions","getNotifications","unreadOnly","getUnreadNotificationCount","markNotificationRead","markNotificationHandled","markAllNotificationsRead","dismissNotification","listRealtimeConnections","installExtension","uninstallExtension","purgeData","enableExtension","disableExtension","listAdminGlobalContent","getAdminGlobalContent","createAdminGlobalContent","updateAdminGlobalContent","deleteAdminGlobalContent","enableAdminGlobalContent","disableAdminGlobalContent"],"sources":["0"],"sourcesContent":["import { get, post, put, patch, del } from './fetch.js';\nconst API_BASE = '/kempo/api';\n\nexport const register = async ({ email, password, name }) =>\n post(`${API_BASE}/auth/register/email`, { email, password, name });\n\nexport const login = async ({ email, password }) =>\n post(`${API_BASE}/auth/login/email`, { email, password });\n\nexport const logout = async () => post(`${API_BASE}/auth/logout`, {});\n\nexport const getSession = async () => get(`${API_BASE}/auth/session`);\n\nexport const forgotPassword = async ({ email }) =>\n post(`${API_BASE}/forgot-password`, { email });\n\nexport const resetPassword = async ({ token, password, logoutAll = false }) =>\n post(`${API_BASE}/auth/reset-password`, { token, newPassword: password, logoutAll });\n \nexport const changePassword = async ({ currentPassword, newPassword, logoutAll }) => \n post(`${API_BASE}/auth/change-password`, { currentPassword, newPassword, logoutAll });\n\nexport const verifyEmail = async ({ token }) =>\n post(`${API_BASE}/auth/verify-email`, { token });\n\nexport const currentUserHasPermission = async ({ permission }) =>\n get(`${API_BASE}/user/current/permissions`, { permission });\n\nexport const currentUserHasAllPermissions = async ({ permissions }) =>\n get(`${API_BASE}/user/current/permissions/all`, { permissions });\n\nexport const currentUserHasSomePermissions = async ({ permissions }) =>\n get(`${API_BASE}/user/current/permissions/some`, { permissions });\n\nexport const getAllCurrentUserPermissions = async () =>\n get(`${API_BASE}/user/current/permissions/list`);\n\nexport const userHasPermission = async ({ userid, permission }) =>\n get(`${API_BASE}/user/${userid}/permissions`, { permission });\n\nexport const userHasAllPermissions = async ({ userid, permissions }) =>\n get(`${API_BASE}/user/${userid}/permissions/all`, { permissions });\n\nexport const userHasSomePermissions = async ({ userid, permissions }) =>\n get(`${API_BASE}/user/${userid}/permissions/some`, { permissions });\n\nexport const getAllUserPermissions = async ({ userid }) =>\n get(`${API_BASE}/user/${userid}/permissions/list`);\n\nexport const getUsers = async ({ limit, offset } = {}) =>\n get(`${API_BASE}/user`, { limit, offset });\n\nexport const searchUsers = async ({ q, limit, offset, inGroup, notInGroup } = {}) =>\n get(`${API_BASE}/user/search`, { q, limit, offset, inGroup, notInGroup });\n\nexport const createUser = async ({ name, email, password, emailVerified }) =>\n post(`${API_BASE}/user/create`, { name, email, password, emailVerified });\n\nexport const deleteUsers = async (ids) =>\n del(`${API_BASE}/user`, { ids });\n\nexport const updateUser = async ({ id, name, email, emailVerified, createdAt }) =>\n patch(`${API_BASE}/user`, { id, name, email, emailVerified, createdAt });\n\nexport const getUser = async (userid) =>\n get(`${API_BASE}/user/${userid}`);\n\nexport const getUserSessions = async (userid, { limit, offset } = {}) =>\n get(`${API_BASE}/user/${userid}/sessions`, { limit, offset });\n\nexport const deleteUserSession = async (userid, sessionToken) =>\n del(`${API_BASE}/user/${userid}/sessions`, { sessionToken });\n\nexport const deleteExpiredUserSessions = async (userid) =>\n del(`${API_BASE}/user/${userid}/sessions/expired`);\n\nexport const getUserGroups = async (userid, { limit, offset } = {}) =>\n get(`${API_BASE}/user/${userid}/groups`, { limit, offset });\n\nexport const addUserToGroup = async (userid, groupName) =>\n post(`${API_BASE}/user/${userid}/groups`, { groupName });\n\nexport const removeUserFromGroup = async (userid, groupName) =>\n del(`${API_BASE}/user/${userid}/groups`, { groupName });\n\nexport const listGroups = async ({ limit, offset, owner } = {}) =>\n get(`${API_BASE}/groups`, { limit, offset, owner });\n\nexport const createGroup = async ({ name, description }) =>\n post(`${API_BASE}/groups`, { name, description });\n\nexport const updateGroup = async ({ name, newName, description }) =>\n patch(`${API_BASE}/groups`, { name, newName, description });\n\nexport const deleteGroups = async (names) =>\n del(`${API_BASE}/groups`, { names });\n\nexport const getGroup = async (name) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}`);\n\nexport const getGroupMembers = async (name, { limit, offset } = {}) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { limit, offset });\n\nexport const addMemberToGroup = async (name, userId) =>\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\n\nexport const removeMemberFromGroup = async (name, userId) =>\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\n\nexport const getGroupPermissions = async (name) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`);\n\nexport const addPermissionToGroup = async (name, permissionName) =>\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\n\nexport const removePermissionFromGroup = async (name, permissionName) =>\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\n\nexport const listPermissions = async ({ limit, offset, owner } = {}) =>\n get(`${API_BASE}/permissions`, { limit, offset, owner });\n\nexport const createPermissions = async (permissions) =>\n post(`${API_BASE}/permissions`, { permissions });\n\nexport const updatePermission = async ({ name, description }) =>\n patch(`${API_BASE}/permissions`, { name, description });\n\nexport const deletePermissions = async (names) =>\n del(`${API_BASE}/permissions`, { names });\n\nexport const getSetting = async (owner, name) =>\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\n\nexport const listSettings = async ({ owner, limit, offset } = {}) =>\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}`, { limit, offset });\n\nexport const setSetting = async (owner, name, data) =>\n post(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`, data);\n\nexport const deleteSetting = async (owner, name) =>\n del(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\n\n/*\n Pages\n*/\n\nexport const listPages = async ({ owner, showHidden, offset, limit } = {}) =>\n get(`${API_BASE}/pages`, { owner, showHidden, offset, limit });\n\nexport const rescanPages = async () =>\n post(`${API_BASE}/pages/rescan`, {});\n\nexport const listTemplates = async () =>\n get(`${API_BASE}/templates`);\n\nexport const listDirectories = async () =>\n get(`${API_BASE}/pages/directories`);\n\nexport const getPage = async (file) =>\n get(`${API_BASE}/pages/file`, { file });\n\nexport const createPage = async ({ directory, name, template }) =>\n post(`${API_BASE}/pages`, { directory, name, template });\n\nexport const updatePage = async ({ file, name, title, description, author, template, contents, extraMetadata }) =>\n put(`${API_BASE}/pages/file`, { file, name, title, description, author, template, contents, extraMetadata });\n\nexport const deletePages = async (files) =>\n del(`${API_BASE}/pages`, { files });\n\nexport const disablePage = async (file) =>\n put(`${API_BASE}/pages/disable`, { file });\n\nexport const enablePage = async (file) =>\n put(`${API_BASE}/pages/enable`, { file });\n\nexport const movePage = async ({ file, newFile }) =>\n patch(`${API_BASE}/pages/file`, { file, newFile });\n\nexport const searchPagesByMetadata = async (query) =>\n get(`${API_BASE}/pages/search`, { query: JSON.stringify(query) });\n\nexport const setPageLocked = async ({ file, locked }) =>\n put(`${API_BASE}/pages/lock`, { file, locked });\n\n/*\n Templates\n*/\n\nexport const getTemplate = async (file) =>\n get(`${API_BASE}/templates/file`, { file });\n\nexport const createTemplate = async ({ directory, name, copyFrom }) =>\n post(`${API_BASE}/templates`, { directory, name, copyFrom });\n\nexport const updateTemplate = async ({ file, name, author, markup }) =>\n put(`${API_BASE}/templates/file`, { file, name, author, markup });\n\nexport const deleteTemplates = async (files) =>\n del(`${API_BASE}/templates`, { files });\n\nexport const disableTemplate = async (file) =>\n put(`${API_BASE}/templates/disable`, { file });\n\nexport const enableTemplate = async (file) =>\n put(`${API_BASE}/templates/enable`, { file });\n\nexport const moveTemplate = async ({ file, newFile }) =>\n patch(`${API_BASE}/templates/file`, { file, newFile });\n\nexport const setTemplateLocked = async ({ file, locked }) =>\n put(`${API_BASE}/templates/lock`, { file, locked });\n\n/*\n Fragments\n*/\n\nexport const listFragments = async () =>\n get(`${API_BASE}/fragments`);\n\nexport const getFragment = async (file) =>\n get(`${API_BASE}/fragments/file`, { file });\n\nexport const createFragment = async ({ directory, name }) =>\n post(`${API_BASE}/fragments`, { directory, name });\n\nexport const updateFragment = async ({ file, author, markup }) =>\n put(`${API_BASE}/fragments/file`, { file, author, markup });\n\nexport const deleteFragments = async (files) =>\n del(`${API_BASE}/fragments`, { files });\n\nexport const moveFragment = async ({ file, newFile }) =>\n patch(`${API_BASE}/fragments/file`, { file, newFile });\n\nexport const disableFragment = async (file) =>\n put(`${API_BASE}/fragments/disable`, { file });\n\nexport const enableFragment = async (file) =>\n put(`${API_BASE}/fragments/enable`, { file });\n\nexport const setFragmentLocked = async ({ file, locked }) =>\n put(`${API_BASE}/fragments/lock`, { file, locked });\n\n/*\n Global Content\n*/\n\nexport const listGlobalContent = async () =>\n get(`${API_BASE}/globals`);\n\nexport const getGlobalContent = async (id) =>\n get(`${API_BASE}/globals/entry`, { id });\n\nexport const createGlobalContent = async ({ name, location, priority }) =>\n post(`${API_BASE}/globals`, { name, location, priority });\n\nexport const updateGlobalContent = async ({ id, name, location, priority, markup }) =>\n put(`${API_BASE}/globals/entry`, { id, name, location, priority, markup });\n\nexport const deleteGlobalContent = async (ids) =>\n del(`${API_BASE}/globals`, { ids });\n\nexport const disableGlobalContent = async (id) =>\n put(`${API_BASE}/globals/disable`, { id });\n\nexport const enableGlobalContent = async (id) =>\n put(`${API_BASE}/globals/enable`, { id });\n\nexport const setGlobalContentLocked = async ({ id, locked }) =>\n put(`${API_BASE}/globals/lock`, { id, locked });\n\n/*\n Extensions\n*/\n\nexport const listExtensions = async ({ limit, offset } = {}) =>\n get(`${API_BASE}/extensions`, { limit, offset });\n\nexport const listAvailableExtensions = async () =>\n get(`${API_BASE}/extensions/available`);\n\nexport const listKnownExtensions = async () =>\n get(`${API_BASE}/extensions/known`);\n\nexport const getNotifications = async ({ unreadOnly = false, limit, offset } = {}) =>\n get(`${API_BASE}/notifications`, { unreadOnly, limit, offset });\n\nexport const getUnreadNotificationCount = async () =>\n get(`${API_BASE}/notifications/count`);\n\nexport const markNotificationRead = async (id) =>\n post(`${API_BASE}/notifications/${encodeURIComponent(id)}/read`, {});\n\nexport const markNotificationHandled = async (id) =>\n post(`${API_BASE}/notifications/${encodeURIComponent(id)}/handled`, {});\n\nexport const markAllNotificationsRead = async () =>\n post(`${API_BASE}/notifications/read-all`, {});\n\nexport const dismissNotification = async (id) =>\n del(`${API_BASE}/notifications/${encodeURIComponent(id)}`);\n\nexport const listRealtimeConnections = async () =>\n get(`${API_BASE}/realtime/connections`);\n\nexport const installExtension = async (name) =>\n post(`${API_BASE}/extensions`, { name });\n\nexport const uninstallExtension = async (name, purgeData = false) =>\n del(`${API_BASE}/extensions`, { name, purgeData });\n\nexport const enableExtension = async (name) =>\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/enable`, {});\n\nexport const disableExtension = async (name) =>\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/disable`, {});\n\n/*\n Admin Global Content\n*/\n\nexport const listAdminGlobalContent = async () =>\n get(`${API_BASE}/admin-globals`);\n\nexport const getAdminGlobalContent = async (id) =>\n get(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`);\n\nexport const createAdminGlobalContent = async ({ name, location, priority, owner, markup }) =>\n post(`${API_BASE}/admin-globals`, { name, location, priority, owner, markup });\n\nexport const updateAdminGlobalContent = async (id, { name, location, priority, markup }) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`, { name, location, priority, markup });\n\nexport const deleteAdminGlobalContent = async (ids) =>\n del(`${API_BASE}/admin-globals`, { ids });\n\nexport const enableAdminGlobalContent = async (id) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/enable`, {});\n\nexport const disableAdminGlobalContent = async (id) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/disable`, {});\n\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"src\\kempo\\sdk.js","names":["get","post","put","patch","del","API_BASE","register","async","email","password","name","login","logout","getSession","forgotPassword","resetPassword","token","logoutAll","newPassword","changePassword","currentPassword","verifyEmail","currentUserHasPermission","permission","currentUserHasAllPermissions","permissions","currentUserHasSomePermissions","getAllCurrentUserPermissions","userHasPermission","userid","userHasAllPermissions","userHasSomePermissions","getAllUserPermissions","getUsers","limit","offset","searchUsers","q","inGroup","notInGroup","createUser","emailVerified","deleteUsers","ids","updateUser","id","createdAt","getUser","getUserSessions","deleteUserSession","sessionToken","deleteExpiredUserSessions","getUserGroups","addUserToGroup","groupName","removeUserFromGroup","listGroups","owner","createGroup","description","updateGroup","newName","deleteGroups","names","getGroup","encodeURIComponent","getGroupMembers","addMemberToGroup","userId","removeMemberFromGroup","getGroupPermissions","addPermissionToGroup","permissionName","removePermissionFromGroup","listPermissions","createPermissions","updatePermission","deletePermissions","getSetting","listSettings","setSetting","data","deleteSetting","listPages","showHidden","rescanPages","listTemplates","listDirectories","getPage","file","createPage","directory","template","updatePage","title","author","contents","extraMetadata","deletePages","files","disablePage","enablePage","movePage","newFile","searchPagesByMetadata","query","JSON","stringify","setPageLocked","locked","getTemplate","createTemplate","copyFrom","updateTemplate","markup","deleteTemplates","disableTemplate","enableTemplate","moveTemplate","setTemplateLocked","listFragments","getFragment","createFragment","updateFragment","deleteFragments","moveFragment","disableFragment","enableFragment","setFragmentLocked","listGlobalContent","getGlobalContent","createGlobalContent","location","priority","updateGlobalContent","deleteGlobalContent","disableGlobalContent","enableGlobalContent","setGlobalContentLocked","listExtensions","listAvailableExtensions","listKnownExtensions","getDashboardWidgets","getNotifications","unreadOnly","getUnreadNotificationCount","markNotificationRead","markNotificationHandled","markAllNotificationsRead","dismissNotification","listRealtimeConnections","installExtension","uninstallExtension","purgeData","enableExtension","disableExtension","listAdminGlobalContent","getAdminGlobalContent","createAdminGlobalContent","updateAdminGlobalContent","deleteAdminGlobalContent","enableAdminGlobalContent","disableAdminGlobalContent"],"sources":["0"],"sourcesContent":["import { get, post, put, patch, del } from './fetch.js';\nconst API_BASE = '/kempo/api';\n\nexport const register = async ({ email, password, name }) =>\n post(`${API_BASE}/auth/register/email`, { email, password, name });\n\nexport const login = async ({ email, password }) =>\n post(`${API_BASE}/auth/login/email`, { email, password });\n\nexport const logout = async () => post(`${API_BASE}/auth/logout`, {});\n\nexport const getSession = async () => get(`${API_BASE}/auth/session`);\n\nexport const forgotPassword = async ({ email }) =>\n post(`${API_BASE}/forgot-password`, { email });\n\nexport const resetPassword = async ({ token, password, logoutAll = false }) =>\n post(`${API_BASE}/auth/reset-password`, { token, newPassword: password, logoutAll });\n \nexport const changePassword = async ({ currentPassword, newPassword, logoutAll }) => \n post(`${API_BASE}/auth/change-password`, { currentPassword, newPassword, logoutAll });\n\nexport const verifyEmail = async ({ token }) =>\n post(`${API_BASE}/auth/verify-email`, { token });\n\nexport const currentUserHasPermission = async ({ permission }) =>\n get(`${API_BASE}/user/current/permissions`, { permission });\n\nexport const currentUserHasAllPermissions = async ({ permissions }) =>\n get(`${API_BASE}/user/current/permissions/all`, { permissions });\n\nexport const currentUserHasSomePermissions = async ({ permissions }) =>\n get(`${API_BASE}/user/current/permissions/some`, { permissions });\n\nexport const getAllCurrentUserPermissions = async () =>\n get(`${API_BASE}/user/current/permissions/list`);\n\nexport const userHasPermission = async ({ userid, permission }) =>\n get(`${API_BASE}/user/${userid}/permissions`, { permission });\n\nexport const userHasAllPermissions = async ({ userid, permissions }) =>\n get(`${API_BASE}/user/${userid}/permissions/all`, { permissions });\n\nexport const userHasSomePermissions = async ({ userid, permissions }) =>\n get(`${API_BASE}/user/${userid}/permissions/some`, { permissions });\n\nexport const getAllUserPermissions = async ({ userid }) =>\n get(`${API_BASE}/user/${userid}/permissions/list`);\n\nexport const getUsers = async ({ limit, offset } = {}) =>\n get(`${API_BASE}/user`, { limit, offset });\n\nexport const searchUsers = async ({ q, limit, offset, inGroup, notInGroup } = {}) =>\n get(`${API_BASE}/user/search`, { q, limit, offset, inGroup, notInGroup });\n\nexport const createUser = async ({ name, email, password, emailVerified }) =>\n post(`${API_BASE}/user/create`, { name, email, password, emailVerified });\n\nexport const deleteUsers = async (ids) =>\n del(`${API_BASE}/user`, { ids });\n\nexport const updateUser = async ({ id, name, email, emailVerified, createdAt }) =>\n patch(`${API_BASE}/user`, { id, name, email, emailVerified, createdAt });\n\nexport const getUser = async (userid) =>\n get(`${API_BASE}/user/${userid}`);\n\nexport const getUserSessions = async (userid, { limit, offset } = {}) =>\n get(`${API_BASE}/user/${userid}/sessions`, { limit, offset });\n\nexport const deleteUserSession = async (userid, sessionToken) =>\n del(`${API_BASE}/user/${userid}/sessions`, { sessionToken });\n\nexport const deleteExpiredUserSessions = async (userid) =>\n del(`${API_BASE}/user/${userid}/sessions/expired`);\n\nexport const getUserGroups = async (userid, { limit, offset } = {}) =>\n get(`${API_BASE}/user/${userid}/groups`, { limit, offset });\n\nexport const addUserToGroup = async (userid, groupName) =>\n post(`${API_BASE}/user/${userid}/groups`, { groupName });\n\nexport const removeUserFromGroup = async (userid, groupName) =>\n del(`${API_BASE}/user/${userid}/groups`, { groupName });\n\nexport const listGroups = async ({ limit, offset, owner } = {}) =>\n get(`${API_BASE}/groups`, { limit, offset, owner });\n\nexport const createGroup = async ({ name, description }) =>\n post(`${API_BASE}/groups`, { name, description });\n\nexport const updateGroup = async ({ name, newName, description }) =>\n patch(`${API_BASE}/groups`, { name, newName, description });\n\nexport const deleteGroups = async (names) =>\n del(`${API_BASE}/groups`, { names });\n\nexport const getGroup = async (name) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}`);\n\nexport const getGroupMembers = async (name, { limit, offset } = {}) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { limit, offset });\n\nexport const addMemberToGroup = async (name, userId) =>\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\n\nexport const removeMemberFromGroup = async (name, userId) =>\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/members`, { userId });\n\nexport const getGroupPermissions = async (name) =>\n get(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`);\n\nexport const addPermissionToGroup = async (name, permissionName) =>\n post(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\n\nexport const removePermissionFromGroup = async (name, permissionName) =>\n del(`${API_BASE}/groups/${encodeURIComponent(name)}/permissions`, { permissionName });\n\nexport const listPermissions = async ({ limit, offset, owner } = {}) =>\n get(`${API_BASE}/permissions`, { limit, offset, owner });\n\nexport const createPermissions = async (permissions) =>\n post(`${API_BASE}/permissions`, { permissions });\n\nexport const updatePermission = async ({ name, description }) =>\n patch(`${API_BASE}/permissions`, { name, description });\n\nexport const deletePermissions = async (names) =>\n del(`${API_BASE}/permissions`, { names });\n\nexport const getSetting = async (owner, name) =>\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\n\nexport const listSettings = async ({ owner, limit, offset } = {}) =>\n get(`${API_BASE}/settings/${encodeURIComponent(owner)}`, { limit, offset });\n\nexport const setSetting = async (owner, name, data) =>\n post(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`, data);\n\nexport const deleteSetting = async (owner, name) =>\n del(`${API_BASE}/settings/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`);\n\n/*\n Pages\n*/\n\nexport const listPages = async ({ owner, showHidden, offset, limit } = {}) =>\n get(`${API_BASE}/pages`, { owner, showHidden, offset, limit });\n\nexport const rescanPages = async () =>\n post(`${API_BASE}/pages/rescan`, {});\n\nexport const listTemplates = async () =>\n get(`${API_BASE}/templates`);\n\nexport const listDirectories = async () =>\n get(`${API_BASE}/pages/directories`);\n\nexport const getPage = async (file) =>\n get(`${API_BASE}/pages/file`, { file });\n\nexport const createPage = async ({ directory, name, template }) =>\n post(`${API_BASE}/pages`, { directory, name, template });\n\nexport const updatePage = async ({ file, name, title, description, author, template, contents, extraMetadata }) =>\n put(`${API_BASE}/pages/file`, { file, name, title, description, author, template, contents, extraMetadata });\n\nexport const deletePages = async (files) =>\n del(`${API_BASE}/pages`, { files });\n\nexport const disablePage = async (file) =>\n put(`${API_BASE}/pages/disable`, { file });\n\nexport const enablePage = async (file) =>\n put(`${API_BASE}/pages/enable`, { file });\n\nexport const movePage = async ({ file, newFile }) =>\n patch(`${API_BASE}/pages/file`, { file, newFile });\n\nexport const searchPagesByMetadata = async (query) =>\n get(`${API_BASE}/pages/search`, { query: JSON.stringify(query) });\n\nexport const setPageLocked = async ({ file, locked }) =>\n put(`${API_BASE}/pages/lock`, { file, locked });\n\n/*\n Templates\n*/\n\nexport const getTemplate = async (file) =>\n get(`${API_BASE}/templates/file`, { file });\n\nexport const createTemplate = async ({ directory, name, copyFrom }) =>\n post(`${API_BASE}/templates`, { directory, name, copyFrom });\n\nexport const updateTemplate = async ({ file, name, author, markup }) =>\n put(`${API_BASE}/templates/file`, { file, name, author, markup });\n\nexport const deleteTemplates = async (files) =>\n del(`${API_BASE}/templates`, { files });\n\nexport const disableTemplate = async (file) =>\n put(`${API_BASE}/templates/disable`, { file });\n\nexport const enableTemplate = async (file) =>\n put(`${API_BASE}/templates/enable`, { file });\n\nexport const moveTemplate = async ({ file, newFile }) =>\n patch(`${API_BASE}/templates/file`, { file, newFile });\n\nexport const setTemplateLocked = async ({ file, locked }) =>\n put(`${API_BASE}/templates/lock`, { file, locked });\n\n/*\n Fragments\n*/\n\nexport const listFragments = async () =>\n get(`${API_BASE}/fragments`);\n\nexport const getFragment = async (file) =>\n get(`${API_BASE}/fragments/file`, { file });\n\nexport const createFragment = async ({ directory, name }) =>\n post(`${API_BASE}/fragments`, { directory, name });\n\nexport const updateFragment = async ({ file, author, markup }) =>\n put(`${API_BASE}/fragments/file`, { file, author, markup });\n\nexport const deleteFragments = async (files) =>\n del(`${API_BASE}/fragments`, { files });\n\nexport const moveFragment = async ({ file, newFile }) =>\n patch(`${API_BASE}/fragments/file`, { file, newFile });\n\nexport const disableFragment = async (file) =>\n put(`${API_BASE}/fragments/disable`, { file });\n\nexport const enableFragment = async (file) =>\n put(`${API_BASE}/fragments/enable`, { file });\n\nexport const setFragmentLocked = async ({ file, locked }) =>\n put(`${API_BASE}/fragments/lock`, { file, locked });\n\n/*\n Global Content\n*/\n\nexport const listGlobalContent = async () =>\n get(`${API_BASE}/globals`);\n\nexport const getGlobalContent = async (id) =>\n get(`${API_BASE}/globals/entry`, { id });\n\nexport const createGlobalContent = async ({ name, location, priority }) =>\n post(`${API_BASE}/globals`, { name, location, priority });\n\nexport const updateGlobalContent = async ({ id, name, location, priority, markup }) =>\n put(`${API_BASE}/globals/entry`, { id, name, location, priority, markup });\n\nexport const deleteGlobalContent = async (ids) =>\n del(`${API_BASE}/globals`, { ids });\n\nexport const disableGlobalContent = async (id) =>\n put(`${API_BASE}/globals/disable`, { id });\n\nexport const enableGlobalContent = async (id) =>\n put(`${API_BASE}/globals/enable`, { id });\n\nexport const setGlobalContentLocked = async ({ id, locked }) =>\n put(`${API_BASE}/globals/lock`, { id, locked });\n\n/*\n Extensions\n*/\n\nexport const listExtensions = async ({ limit, offset } = {}) =>\n get(`${API_BASE}/extensions`, { limit, offset });\n\nexport const listAvailableExtensions = async () =>\n get(`${API_BASE}/extensions/available`);\n\nexport const listKnownExtensions = async () =>\n get(`${API_BASE}/extensions/known`);\n\n/*\n Dashboard\n*/\n\nexport const getDashboardWidgets = async () =>\n get(`${API_BASE}/dashboard/widgets`);\n\n/*\n Notifications\n*/\n\nexport const getNotifications = async ({ unreadOnly = false, limit, offset } = {}) =>\n get(`${API_BASE}/notifications`, { unreadOnly, limit, offset });\n\nexport const getUnreadNotificationCount = async () =>\n get(`${API_BASE}/notifications/count`);\n\nexport const markNotificationRead = async (id) =>\n post(`${API_BASE}/notifications/${encodeURIComponent(id)}/read`, {});\n\nexport const markNotificationHandled = async (id) =>\n post(`${API_BASE}/notifications/${encodeURIComponent(id)}/handled`, {});\n\nexport const markAllNotificationsRead = async () =>\n post(`${API_BASE}/notifications/read-all`, {});\n\nexport const dismissNotification = async (id) =>\n del(`${API_BASE}/notifications/${encodeURIComponent(id)}`);\n\nexport const listRealtimeConnections = async () =>\n get(`${API_BASE}/realtime/connections`);\n\nexport const installExtension = async (name) =>\n post(`${API_BASE}/extensions`, { name });\n\nexport const uninstallExtension = async (name, purgeData = false) =>\n del(`${API_BASE}/extensions`, { name, purgeData });\n\nexport const enableExtension = async (name) =>\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/enable`, {});\n\nexport const disableExtension = async (name) =>\n post(`${API_BASE}/extensions/${encodeURIComponent(name)}/disable`, {});\n\n/*\n Admin Global Content\n*/\n\nexport const listAdminGlobalContent = async () =>\n get(`${API_BASE}/admin-globals`);\n\nexport const getAdminGlobalContent = async (id) =>\n get(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`);\n\nexport const createAdminGlobalContent = async ({ name, location, priority, owner, markup }) =>\n post(`${API_BASE}/admin-globals`, { name, location, priority, owner, markup });\n\nexport const updateAdminGlobalContent = async (id, { name, location, priority, markup }) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}`, { name, location, priority, markup });\n\nexport const deleteAdminGlobalContent = async (ids) =>\n del(`${API_BASE}/admin-globals`, { ids });\n\nexport const enableAdminGlobalContent = async (id) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/enable`, {});\n\nexport const disableAdminGlobalContent = async (id) =>\n put(`${API_BASE}/admin-globals/${encodeURIComponent(id)}/disable`, {});\n\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kempo",
3
- "version": "4.5.2",
3
+ "version": "4.5.4",
4
4
  "type": "module",
5
5
  "description": "A fullstack CMS framework for creating websites with user authentication",
6
6
  "scripts": {
@@ -1,5 +1,5 @@
1
1
  import { sql } from 'drizzle-orm';
2
- import { pgTable, text, timestamp, boolean, index, uniqueIndex, primaryKey, jsonb, bigserial, bigint } from 'drizzle-orm/pg-core';
2
+ import { pgTable, text, timestamp, boolean, index, uniqueIndex, primaryKey, jsonb, bigserial, bigint, integer } from 'drizzle-orm/pg-core';
3
3
 
4
4
  /*
5
5
  Auth Tables
@@ -188,6 +188,39 @@ export const notificationRecipient = pgTable('notificationRecipient', {
188
188
  index('notificationRecipient_userId_idx').on(table.userId),
189
189
  ]);
190
190
 
191
+ /*
192
+ Dashboard Widget System
193
+
194
+ A tile an extension adds to the admin dashboard. `id` is `owner:key`, which is also what the browser
195
+ stores its layout against. `element` is the custom element the dashboard creates and `module` is the
196
+ script that defines it; the widget fetches its own data once it is on the page. Kempo's own widgets
197
+ are not rows: they live in code (`server/utils/dashboard/coreWidgets.js`) so they need no seeding.
198
+ */
199
+ export const dashboardWidget = pgTable('dashboardWidget', {
200
+ id: text('id').primaryKey(),
201
+ owner: text('owner').notNull(),
202
+ key: text('key').notNull(),
203
+ title: text('title').notNull(),
204
+ description: text('description'),
205
+ icon: text('icon'),
206
+ element: text('element').notNull(),
207
+ module: text('module').notNull(),
208
+ defaultWidth: integer('defaultWidth').notNull().default(2),
209
+ defaultHeight: integer('defaultHeight').notNull().default(1),
210
+ minWidth: integer('minWidth').notNull().default(1),
211
+ maxWidth: integer('maxWidth').notNull().default(4),
212
+ minHeight: integer('minHeight').notNull().default(1),
213
+ maxHeight: integer('maxHeight').notNull().default(3),
214
+ permission: text('permission'),
215
+ actions: jsonb('actions').notNull().default([]),
216
+ priority: integer('priority').notNull().default(100),
217
+ defaultVisible: boolean('defaultVisible').notNull().default(true),
218
+ createdAt: timestamp('createdAt').notNull(),
219
+ updatedAt: timestamp('updatedAt').notNull(),
220
+ }, table => [
221
+ uniqueIndex('dashboardWidget_owner_key_idx').on(table.owner, table.key),
222
+ ]);
223
+
191
224
  /*
192
225
  Triggers to prevent deletion of system resources
193
226
  */
package/server/sdk.js CHANGED
@@ -209,6 +209,14 @@ export { default as markHandled } from './utils/notifications/markHandled.js';
209
209
  export { default as markRead } from './utils/notifications/markRead.js';
210
210
  export { default as pruneNotifications } from './utils/notifications/pruneNotifications.js';
211
211
 
212
+ /*
213
+ Dashboard
214
+ */
215
+
216
+ export { default as listDashboardWidgets } from './utils/dashboard/listDashboardWidgets.js';
217
+ export { default as registerDashboardWidget } from './utils/dashboard/registerDashboardWidget.js';
218
+ export { default as unregisterDashboardWidget } from './utils/dashboard/unregisterDashboardWidget.js';
219
+
212
220
  /*
213
221
  Realtime
214
222
  */
@@ -0,0 +1,87 @@
1
+ import { CORE_OWNER } from './helpers.js';
2
+
3
+ const core = widget => ({
4
+ owner: CORE_OWNER,
5
+ description: null,
6
+ icon: null,
7
+ defaultWidth: 2,
8
+ defaultHeight: 1,
9
+ minWidth: 1,
10
+ maxWidth: 4,
11
+ minHeight: 1,
12
+ maxHeight: 3,
13
+ permission: null,
14
+ actions: [],
15
+ defaultVisible: true,
16
+ ...widget,
17
+ id: `${CORE_OWNER}:${widget.key}`
18
+ });
19
+
20
+ /*
21
+ The widgets Kempo itself puts on the dashboard. They are code, not rows, so an existing site gets
22
+ them on upgrade with nothing to seed, and `priority` leaves room for extensions on either side.
23
+ */
24
+ export default [
25
+ core({
26
+ key: 'pages',
27
+ title: 'Pages',
28
+ description: 'How many pages the site has and which changed most recently.',
29
+ icon: 'docs',
30
+ element: 'k-widget-pages',
31
+ module: '/admin/components/widgets/Pages.js',
32
+ defaultWidth: 2,
33
+ defaultHeight: 2,
34
+ minWidth: 2,
35
+ minHeight: 2,
36
+ permission: 'system:pages:read',
37
+ priority: 10
38
+ }),
39
+ core({
40
+ key: 'content',
41
+ title: 'Content',
42
+ description: 'Templates, fragments and global content at a glance.',
43
+ icon: 'content',
44
+ element: 'k-widget-content',
45
+ module: '/admin/components/widgets/Content.js',
46
+ defaultWidth: 1,
47
+ defaultHeight: 1,
48
+ permission: 'system:pages:read',
49
+ priority: 20
50
+ }),
51
+ core({
52
+ key: 'users',
53
+ title: 'Users',
54
+ description: 'How many people have an account.',
55
+ icon: 'person',
56
+ element: 'k-widget-users',
57
+ module: '/admin/components/widgets/Users.js',
58
+ defaultWidth: 1,
59
+ defaultHeight: 1,
60
+ permission: 'system:user:read',
61
+ priority: 30
62
+ }),
63
+ core({
64
+ key: 'notifications',
65
+ title: 'Notifications',
66
+ description: 'Your unread notifications.',
67
+ icon: 'notifications',
68
+ element: 'k-widget-notifications',
69
+ module: '/admin/components/widgets/Notifications.js',
70
+ defaultWidth: 2,
71
+ defaultHeight: 2,
72
+ minWidth: 2,
73
+ priority: 40
74
+ }),
75
+ core({
76
+ key: 'extensions',
77
+ title: 'Extensions',
78
+ description: 'Installed extensions and whether they are enabled.',
79
+ icon: 'extension',
80
+ element: 'k-widget-extensions',
81
+ module: '/admin/components/widgets/Extensions.js',
82
+ defaultWidth: 1,
83
+ defaultHeight: 1,
84
+ permission: 'system:extensions:read',
85
+ priority: 50
86
+ })
87
+ ];
@@ -0,0 +1,144 @@
1
+ import { isLocalPath } from '../notifications/helpers.js';
2
+
3
+ export const MAX_WIDTH = 4;
4
+ export const MAX_HEIGHT = 3;
5
+ export const CORE_OWNER = 'kempo';
6
+ export const MAX_ACTIONS = 3;
7
+
8
+ const ELEMENT_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
9
+ const KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
10
+
11
+ const isText = (value, max) => typeof value === 'string' && value.trim().length > 0 && value.length <= max;
12
+
13
+ const isBlocks = (value, max) => Number.isInteger(value) && value >= 1 && value <= max;
14
+
15
+ /*
16
+ Reduces a widget definition to the stored fields and fills in the defaults, so nothing a caller
17
+ passes beyond them is kept. Returns an error tuple for anything that would leave the dashboard
18
+ unable to render the widget: a size outside the grid, bounds that contradict each other, an element
19
+ name that is not a valid custom element, or a module that is not a path on this site.
20
+ */
21
+ export const normalizeWidget = ({
22
+ owner,
23
+ key,
24
+ title,
25
+ description = null,
26
+ icon = null,
27
+ element,
28
+ module,
29
+ defaultWidth = 2,
30
+ defaultHeight = 1,
31
+ minWidth = 1,
32
+ maxWidth = MAX_WIDTH,
33
+ minHeight = 1,
34
+ maxHeight = MAX_HEIGHT,
35
+ permission = null,
36
+ actions = [],
37
+ priority = 100,
38
+ defaultVisible = true
39
+ } = {}) => {
40
+ if(!isText(owner, 100)){
41
+ return [{ code: 400, msg: 'Owner is required' }, null];
42
+ }
43
+
44
+ if(typeof key !== 'string' || !KEY_PATTERN.test(key) || key.length > 100){
45
+ return [{ code: 400, msg: 'Key is required and may only contain letters, numbers, ".", "_" and "-"' }, null];
46
+ }
47
+
48
+ if(!isText(title, 100)){
49
+ return [{ code: 400, msg: 'Title is required and must be at most 100 characters' }, null];
50
+ }
51
+
52
+ if(description !== null && !(typeof description === 'string' && description.length <= 300)){
53
+ return [{ code: 400, msg: 'Description must be a string of at most 300 characters' }, null];
54
+ }
55
+
56
+ if(icon !== null && !isText(icon, 60)){
57
+ return [{ code: 400, msg: 'Icon must be an icon name' }, null];
58
+ }
59
+
60
+ if(typeof element !== 'string' || !ELEMENT_PATTERN.test(element)){
61
+ return [{ code: 400, msg: 'Element must be a custom element tag name such as "my-ext-widget"' }, null];
62
+ }
63
+
64
+ if(!isLocalPath(module)){
65
+ return [{ code: 400, msg: 'Module must be a path on this site, beginning with a single "/"' }, null];
66
+ }
67
+
68
+ if(!isBlocks(minWidth, MAX_WIDTH) || !isBlocks(maxWidth, MAX_WIDTH) || minWidth > maxWidth){
69
+ return [{ code: 400, msg: `minWidth and maxWidth must be whole numbers from 1 to ${MAX_WIDTH}, minWidth no greater than maxWidth` }, null];
70
+ }
71
+
72
+ if(!isBlocks(minHeight, MAX_HEIGHT) || !isBlocks(maxHeight, MAX_HEIGHT) || minHeight > maxHeight){
73
+ return [{ code: 400, msg: `minHeight and maxHeight must be whole numbers from 1 to ${MAX_HEIGHT}, minHeight no greater than maxHeight` }, null];
74
+ }
75
+
76
+ if(!Number.isInteger(defaultWidth) || defaultWidth < minWidth || defaultWidth > maxWidth){
77
+ return [{ code: 400, msg: 'defaultWidth must be a whole number between minWidth and maxWidth' }, null];
78
+ }
79
+
80
+ if(!Number.isInteger(defaultHeight) || defaultHeight < minHeight || defaultHeight > maxHeight){
81
+ return [{ code: 400, msg: 'defaultHeight must be a whole number between minHeight and maxHeight' }, null];
82
+ }
83
+
84
+ if(permission !== null && !isText(permission, 200)){
85
+ return [{ code: 400, msg: 'Permission must be a permission name' }, null];
86
+ }
87
+
88
+ if(!Array.isArray(actions) || actions.length > MAX_ACTIONS){
89
+ return [{ code: 400, msg: `actions must be an array of at most ${MAX_ACTIONS} items` }, null];
90
+ }
91
+
92
+ const cleanActions = [];
93
+
94
+ for(const action of actions){
95
+ if(!action || typeof action !== 'object' || Array.isArray(action)){
96
+ return [{ code: 400, msg: 'Each action must be an object' }, null];
97
+ }
98
+
99
+ const { label, href, icon: actionIcon = null, permission: actionPermission = null } = action;
100
+
101
+ if(!isText(label, 40)){
102
+ return [{ code: 400, msg: 'Each action needs a label of at most 40 characters' }, null];
103
+ }
104
+
105
+ if(!isLocalPath(href)){
106
+ return [{ code: 400, msg: 'An action href must be a path on this site, beginning with a single "/"' }, null];
107
+ }
108
+
109
+ if(actionIcon !== null && !isText(actionIcon, 60)){
110
+ return [{ code: 400, msg: 'An action icon must be an icon name' }, null];
111
+ }
112
+
113
+ if(actionPermission !== null && !isText(actionPermission, 200)){
114
+ return [{ code: 400, msg: 'An action permission must be a permission name' }, null];
115
+ }
116
+
117
+ cleanActions.push({ label: label.trim(), href, icon: actionIcon, permission: actionPermission });
118
+ }
119
+
120
+ if(!Number.isInteger(priority)){
121
+ return [{ code: 400, msg: 'Priority must be a whole number' }, null];
122
+ }
123
+
124
+ return [null, {
125
+ id: `${owner}:${key}`,
126
+ owner,
127
+ key,
128
+ title: title.trim(),
129
+ description,
130
+ icon,
131
+ element,
132
+ module,
133
+ defaultWidth,
134
+ defaultHeight,
135
+ minWidth,
136
+ maxWidth,
137
+ minHeight,
138
+ maxHeight,
139
+ permission,
140
+ actions: cleanActions,
141
+ priority,
142
+ defaultVisible: defaultVisible !== false
143
+ }];
144
+ };
@@ -0,0 +1,63 @@
1
+ import { eq } from 'drizzle-orm';
2
+ import db from '../../db/index.js';
3
+ import { dashboardWidget, extension } from '../../db/schema.js';
4
+ import userHasPermission from '../permissions/userHasPermission.js';
5
+ import coreWidgets from './coreWidgets.js';
6
+
7
+ const publicFields = ({ createdAt, updatedAt, ...widget }) => widget;
8
+
9
+ /*
10
+ Every widget the dashboard can show: Kempo's own plus those extensions registered. Widgets of a
11
+ disabled extension are left out, since the module they point at is no longer served.
12
+
13
+ Pass `userId` to also drop widgets that person lacks the `permission` for; the check runs once per
14
+ distinct permission, and administrators hold every one. The same applies to each action's own
15
+ `permission`. Without `userId` nothing is filtered by permission.
16
+
17
+ `unavailable` lists the ids of widgets that exist but are not offered to this person, because their
18
+ extension is disabled or they lack the permission. A client that remembers a layout keeps those entries
19
+ so the widget returns to its place when it becomes available again, and drops entries for ids that are in
20
+ neither list, which is what an uninstalled extension looks like.
21
+
22
+ Ordered by `priority`, then title. If the widget table has not been created yet (a site that upgraded
23
+ without applying the schema), Kempo's own widgets are still returned so the dashboard is not blank.
24
+ */
25
+ export default async ({ userId } = {}) => {
26
+ let registered = [];
27
+ const unavailable = [];
28
+
29
+ try {
30
+ const rows = await db
31
+ .select({ widget: dashboardWidget, enabled: extension.enabled, installed: extension.name })
32
+ .from(dashboardWidget)
33
+ .leftJoin(extension, eq(extension.name, dashboardWidget.owner));
34
+ rows.forEach(({ widget, enabled, installed }) => {
35
+ if(installed && !enabled) unavailable.push(widget.id);
36
+ else registered.push(publicFields(widget));
37
+ });
38
+ } catch(error) {
39
+ console.warn(`Dashboard widgets from extensions are unavailable: ${error.message}`);
40
+ }
41
+
42
+ let widgets = [...coreWidgets, ...registered];
43
+
44
+ if(userId){
45
+ const permissions = [...new Set(widgets.flatMap(widget => [widget.permission, ...(widget.actions || []).map(action => action.permission)]).filter(Boolean))];
46
+ const granted = new Set();
47
+
48
+ await Promise.all(permissions.map(async permission => {
49
+ const [error, allowed] = await userHasPermission(userId, permission);
50
+ if(!error && allowed) granted.add(permission);
51
+ }));
52
+
53
+ widgets.filter(widget => widget.permission && !granted.has(widget.permission)).forEach(widget => unavailable.push(widget.id));
54
+
55
+ widgets = widgets
56
+ .filter(widget => !widget.permission || granted.has(widget.permission))
57
+ .map(widget => ({ ...widget, actions: (widget.actions || []).filter(action => !action.permission || granted.has(action.permission)) }));
58
+ }
59
+
60
+ widgets.sort((a, b) => a.priority - b.priority || a.title.localeCompare(b.title));
61
+
62
+ return [null, { widgets, unavailable }];
63
+ };
@@ -0,0 +1,40 @@
1
+ import db from '../../db/index.js';
2
+ import { dashboardWidget } from '../../db/schema.js';
3
+ import { CORE_OWNER, normalizeWidget } from './helpers.js';
4
+
5
+ /*
6
+ Adds a widget to the admin dashboard, or updates it when `owner` and `key` already exist, so an
7
+ extension can call this from `install.js` and again from `update.js` without checking first.
8
+
9
+ `element` is the custom element the dashboard creates and `module` is the site path of the script
10
+ that defines it. The element loads its own data when it connects, so nothing here is fetched until a
11
+ user has the widget on their dashboard. `permission` hides it from people who do not hold it.
12
+
13
+ Sizes are in blocks: width 1 to 4, height 1 to 3. `minWidth`/`maxWidth`/`minHeight`/`maxHeight` bound
14
+ what a user may resize it to; `priority` orders it among the defaults, lowest first.
15
+ */
16
+ export default async definition => {
17
+ const [invalid, widget] = normalizeWidget(definition);
18
+
19
+ if(invalid){
20
+ return [invalid, null];
21
+ }
22
+
23
+ if(widget.owner === CORE_OWNER){
24
+ return [{ code: 400, msg: `Owner "${CORE_OWNER}" is reserved for Kempo's own widgets` }, null];
25
+ }
26
+
27
+ try {
28
+ const now = new Date();
29
+ const { id, ...changes } = widget;
30
+ const [saved] = await db
31
+ .insert(dashboardWidget)
32
+ .values({ ...widget, createdAt: now, updatedAt: now })
33
+ .onConflictDoUpdate({ target: dashboardWidget.id, set: { ...changes, updatedAt: now } })
34
+ .returning();
35
+
36
+ return [null, saved];
37
+ } catch(error) {
38
+ return [{ code: 500, msg: 'Failed to register dashboard widget' }, null];
39
+ }
40
+ };
@@ -0,0 +1,24 @@
1
+ import { and, eq } from 'drizzle-orm';
2
+ import db from '../../db/index.js';
3
+ import { dashboardWidget } from '../../db/schema.js';
4
+
5
+ /*
6
+ Removes one widget, or every widget the owner registered when `key` is omitted. Removing something
7
+ that is not there is not an error; `removed` says how many rows went.
8
+ */
9
+ export default async ({ owner, key } = {}) => {
10
+ if(!owner){
11
+ return [{ code: 400, msg: 'Owner is required' }, null];
12
+ }
13
+
14
+ try {
15
+ const removed = await db
16
+ .delete(dashboardWidget)
17
+ .where(key ? and(eq(dashboardWidget.owner, owner), eq(dashboardWidget.key, key)) : eq(dashboardWidget.owner, owner))
18
+ .returning({ id: dashboardWidget.id });
19
+
20
+ return [null, { removed: removed.length }];
21
+ } catch(error) {
22
+ return [{ code: 500, msg: 'Failed to remove dashboard widget' }, null];
23
+ }
24
+ };
@@ -8,6 +8,7 @@ import { readFile } from 'fs/promises';
8
8
  import { randomUUID } from 'crypto';
9
9
  import triggerHook, { clearHandlerCache } from '../hooks/triggerHook.js';
10
10
  import createHook from '../hooks/createHook.js';
11
+ import registerDashboardWidget from '../dashboard/registerDashboardWidget.js';
11
12
  import setSetting from '../settings/setSetting.js';
12
13
  import declaredValue from '../settings/declaredValue.js';
13
14
  import createTablesFromSchema from './createTablesFromSchema.js';
@@ -122,6 +123,18 @@ export default async ({ name }) => {
122
123
  await createHook({ owner: name, event, callback });
123
124
  }
124
125
 
126
+ /*
127
+ Register declarative dashboard widgets. A widget is decoration, so one that cannot be registered (an
128
+ invalid definition, or a site that has not applied the dashboardWidget table yet) is reported and
129
+ skipped rather than failing the install of an otherwise working extension.
130
+ */
131
+ for(const widget of kempoConfig.widgets || []){
132
+ const [widgetError] = await registerDashboardWidget({ ...widget, owner: name });
133
+ if(widgetError){
134
+ console.warn(`[${name}] dashboard widget "${widget.key}" was not registered: ${widgetError.msg}`);
135
+ }
136
+ }
137
+
125
138
  const now = new Date();
126
139
  const entry = {
127
140
  name,
@@ -1,5 +1,5 @@
1
1
  import db from '../../db/index.js';
2
- import { extension, hook, permission, setting, group, groupPermission, userGroup } from '../../db/schema.js';
2
+ import { extension, hook, permission, setting, group, groupPermission, userGroup, dashboardWidget } from '../../db/schema.js';
3
3
  import { eq, inArray, like } from 'drizzle-orm';
4
4
  import { join } from 'path';
5
5
  import { pathToFileURL } from 'url';
@@ -109,6 +109,7 @@ export default async ({ name, purgeData = false }) => {
109
109
 
110
110
  try {
111
111
  await db.delete(hook).where(eq(hook.owner, name));
112
+ await db.delete(dashboardWidget).where(eq(dashboardWidget.owner, name));
112
113
  await db.delete(extension).where(eq(extension.name, name));
113
114
  } catch(error) {
114
115
  return [{ code: 500, msg: 'Failed to remove extension' }, null];
@@ -8,6 +8,8 @@ import { randomUUID } from 'crypto';
8
8
  import { execSync } from 'child_process';
9
9
  import triggerHook, { clearHandlerCache } from '../hooks/triggerHook.js';
10
10
  import createHook from '../hooks/createHook.js';
11
+ import registerDashboardWidget from '../dashboard/registerDashboardWidget.js';
12
+ import unregisterDashboardWidget from '../dashboard/unregisterDashboardWidget.js';
11
13
  import setSetting from '../settings/setSetting.js';
12
14
  import declaredValue from '../settings/declaredValue.js';
13
15
  import createTablesFromSchema from './createTablesFromSchema.js';
@@ -141,6 +143,22 @@ export default async ({ name, version = 'latest' }) => {
141
143
  }
142
144
  }
143
145
 
146
+ /*
147
+ Sync dashboard widgets: register or refresh every declared one, remove those no longer declared
148
+ */
149
+ const newWidgetKeys = new Set((newKempo.widgets || []).map(w => w.key));
150
+ for(const widget of newKempo.widgets || []){
151
+ const [widgetError] = await registerDashboardWidget({ ...widget, owner: name });
152
+ if(widgetError){
153
+ console.warn(`[${name}] dashboard widget "${widget.key}" was not registered: ${widgetError.msg}`);
154
+ }
155
+ }
156
+ for(const { key } of oldKempo.widgets || []){
157
+ if(!newWidgetKeys.has(key)){
158
+ await unregisterDashboardWidget({ owner: name, key });
159
+ }
160
+ }
161
+
144
162
  const now = new Date();
145
163
  try {
146
164
  await db.update(extension).set({