diff --git a/adminforth/documentation/docs/tutorial/03-Customization/10-menuConfiguration.md b/adminforth/documentation/docs/tutorial/03-Customization/10-menuConfiguration.md index 8817e59a..e8686d66 100644 --- a/adminforth/documentation/docs/tutorial/03-Customization/10-menuConfiguration.md +++ b/adminforth/documentation/docs/tutorial/03-Customization/10-menuConfiguration.md @@ -207,8 +207,12 @@ Most times you need to refresh the badge from some backend API or hook. To do th resourceId: 'posts', //diff-add itemId: 'postsMenuItem', - badge: async (adminUser: AdminUser) => { - return 10 + //diff-add + badge: async (adminUser: AdminUser, adminForth: IAdminForth) => { + //diff-add + const newCount = await adminforth.resource('posts').count(Filters.EQ('verified', false)); + //diff-add + return newCount; }, badgeTooltip: 'Unverified posts', // explain user what this badge means ... @@ -226,15 +230,13 @@ Most times you need to refresh the badge from some backend API or hook. To do th table: 'posts', hooks: { edit: { -//diff-add + //diff-add afterSave: async ({ record, adminUser, resource, adminforth }) => { -//diff-add - const newCount = await adminforth.resource('posts').count(Filters.EQ('verified', false)); -//diff-add - adminforth.websocket.publish(`/opentopic/update-menu-badge/postsMenuItem`, { badge: newCount }); -//diff-add + //diff-add + adminforth.refreshMenuBadge('postsMenuItem', adminUser); + //diff-add return { ok: true } -//diff-add + //diff-add } } } diff --git a/adminforth/index.ts b/adminforth/index.ts index 1a7da702..6248fddb 100644 --- a/adminforth/index.ts +++ b/adminforth/index.ts @@ -128,6 +128,20 @@ class AdminForth implements IAdminForth { websocket: IWebSocketBroker; + async refreshMenuBadge(menuItemId: string, adminUser: AdminUser) { + const menuItem = this.config.menu.find((item) => item.itemId === menuItemId); + if (!menuItem) { + afLogger.error(`Cannot refresh badge for menu item with id "${menuItemId}" because it was not found in config.menu`); + return; + } + if (!menuItem.badge) { + afLogger.error(`Cannot refresh badge for menu item with id "${menuItemId}" because it does not have badge function in config.menu`); + return; + } + const badgeValue = typeof menuItem.badge === 'function' ? await menuItem.badge(adminUser, this) : menuItem.badge; + this.websocket.publish(`/opentopic/update-menu-badge/${menuItemId}`, { badge: badgeValue }); + } + operationalResources: { [resourceId: string]: IOperationalResource, } diff --git a/adminforth/modules/restApi.ts b/adminforth/modules/restApi.ts index 6fc2f499..7a2ae67b 100644 --- a/adminforth/modules/restApi.ts +++ b/adminforth/modules/restApi.ts @@ -504,11 +504,13 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { const badgeFunctions = []; + const adminforth = this.adminforth; + function processMenuItem(menuItem) { if (menuItem.badge) { if (typeof menuItem.badge === 'function') { badgeFunctions.push(async () => { - badges[menuItem.itemId] = await menuItem.badge(adminUser); + badges[menuItem.itemId] = await menuItem.badge(adminUser, adminforth); }); } else { badges[menuItem.itemId] = menuItem.badge; diff --git a/adminforth/types/Back.ts b/adminforth/types/Back.ts index e7010020..3079439e 100644 --- a/adminforth/types/Back.ts +++ b/adminforth/types/Back.ts @@ -456,6 +456,8 @@ export interface IAdminForth { * ``` */ getPluginById(id: string): T; + + refreshMenuBadge(menuItemId: string, adminUser: AdminUser): Promise; } diff --git a/adminforth/types/Common.ts b/adminforth/types/Common.ts index 0df6cd07..ce8b3989 100644 --- a/adminforth/types/Common.ts +++ b/adminforth/types/Common.ts @@ -1164,7 +1164,7 @@ export interface AdminForthConfigMenuItem { * Optional callback which will be called before rendering the menu for each item. * Result of callback if not null will be used as a small badge near the menu item. */ - badge?: string | ((user: AdminUser) => Promise), + badge?: string | number | ((user: AdminUser, adminForth: IAdminForth) => Promise | string | number), /** * Tooltip shown on hover for badge diff --git a/dev-demo/api.ts b/dev-demo/api.ts index 68d43806..a5bc1a3a 100644 --- a/dev-demo/api.ts +++ b/dev-demo/api.ts @@ -1,10 +1,12 @@ import { Express } from "express"; -import { IAdminForth } from "adminforth"; +import { IAdminForth, IAdminUserExpressRequest } from "adminforth"; export function initApi(app: Express, admin: IAdminForth) { app.get(`${admin.config.baseUrl}/api/hello/`, - (req, res) => { + admin.express.authorize( + async (req:IAdminUserExpressRequest, res: any) => { + admin.refreshMenuBadge('menuTimestamp', req.adminUser); res.json({ message: "Hello from AdminForth API!" }); } - ); + )); } \ No newline at end of file diff --git a/dev-demo/custom/AfComponents.vue b/dev-demo/custom/AfComponents.vue index 26e0ec5e..426a4af4 100644 --- a/dev-demo/custom/AfComponents.vue +++ b/dev-demo/custom/AfComponents.vue @@ -377,6 +377,18 @@ :expandDepth="2" /> + + + + + + @@ -522,4 +534,13 @@ watch(numberInput, (newVal) => { watch(textInput, (newVal) => { console.log('Text input changed:', newVal, typeof newVal); }); + +async function callHelloWorldApi() { + try { + const response = await callApi({ path: '/api/hello/', method: 'GET' }); + console.log('API response:', response); + } catch (error) { + console.error('API error:', error); + } +} \ No newline at end of file diff --git a/dev-demo/index.ts b/dev-demo/index.ts index 1a81f443..d9f2c96f 100644 --- a/dev-demo/index.ts +++ b/dev-demo/index.ts @@ -1,5 +1,5 @@ import express from 'express'; -import AdminForth, { Filters } from '../adminforth/index.js'; +import AdminForth, { AdminUser, Filters, IAdminForth } from '../adminforth/index.js'; import usersResource from "./resources/adminuser.js"; import { fileURLToPath } from 'url'; import path from 'path'; @@ -143,6 +143,12 @@ export const admin = new AdminForth({ icon: 'flowbite:chart-pie-solid', component: '@@/AfComponents.vue', path: '/af-components', + itemId: 'menuTimestamp', + badge: async (adminUser: AdminUser, adminForth: IAdminForth) => { + const now = new Date(); + return now.getSeconds(); + }, + badgeTooltip: 'Seconds in current minute', }, { type: 'divider'