Feature: Plugin system, site search, notifications, webhooks, caching & loading states
Local Build and Deploy / deploy (push) Successful in 1m2s

- Plugin/module system with registry, hooks for admin nav/permissions/i18n
- Example Games plugin demonstrating the plugin system
- Site-wide search with Cmd+K command palette (cmdk-based)
- Real-time notification system (SSE + Redis pub/sub)
- Extensible webhook system with admin management UI
- Caching strategy: tiered data cache (unstable_cache + Redis)
- Theme import/export as JSON
- Loading states (spinner/skeleton) for all public pages
- Mobile responsive fixes (admin dashboard, guilds page)
- Webhooks admin page under Settings hub
This commit is contained in:
openhands committed 2026-07-18 13:37:19 +02:00
1 parent 09f1bc2bd6
commit 98d2b92218
85 files changed
+1889 -7

No files matched your search

+45 -1
View File
@@ -8,6 +8,7 @@ import {
LayoutDashboard,
Monitor,
Newspaper,
Puzzle,
Radio,
Settings,
Shield,
@@ -16,6 +17,7 @@ import {
Users,
Wrench,
} from "lucide-react";
import { pluginRegistry } from "@/lib/plugins";
export interface AdminHubTab {
href: string;
@@ -267,6 +269,7 @@ export const ADMIN_HUBS: AdminHubDefinition[] = [
"/admin/emulator",
"/admin/email-templates",
"/admin/housekeeping",
"/admin/webhooks",
],
tabs: [
{ href: "/admin/settings", labelKey: "cms" },
@@ -276,6 +279,7 @@ export const ADMIN_HUBS: AdminHubDefinition[] = [
{ href: "/admin/emulator", labelKey: "emulator" },
{ href: "/admin/email-templates", labelKey: "email" },
{ href: "/admin/housekeeping", labelKey: "housekeeping" },
{ href: "/admin/webhooks", labelKey: "webhooks" },
],
},
{
@@ -331,14 +335,54 @@ export const ADMIN_HUBS: AdminHubDefinition[] = [
},
];
export function allHubs(): AdminHubDefinition[] {
const pluginHubs: AdminHubDefinition[] = pluginRegistry.getHubs().map((h) => ({
id: h.id,
titleKey: h.titleKey,
subtitleKey: h.subtitleKey,
icon: h.icon,
prefixes: h.prefixes,
tabs: h.tabs.map((t) => ({
href: t.href,
labelKey: t.labelKey,
match: t.match,
group: t.group,
})),
}));
return [...ADMIN_HUBS, ...pluginHubs];
}
export function findAdminHub(pathname: string): AdminHubDefinition | null {
const hubs = allHubs();
return (
ADMIN_HUBS.find((hub) =>
hubs.find((hub) =>
hub.prefixes.some((p) => pathname === p || pathname.startsWith(`${p}/`)),
) ?? null
);
}
export function allNavGroups(): AdminNavGroup[] {
const pluginGroups: AdminNavGroup[] = pluginRegistry.getNavGroups().map((g) => ({
labelKey: g.labelKey,
icon: g.icon,
items: g.items.map((item) => ({
href: item.href,
labelKey: item.labelKey,
icon: item.icon,
matchPrefixes: item.matchPrefixes,
})),
}));
if (pluginGroups.length === 0) return ADMIN_NAV_GROUPS;
return [
...ADMIN_NAV_GROUPS,
{
labelKey: "plugins",
icon: Puzzle,
items: pluginGroups.flatMap((g) => g.items),
},
];
}
function hubPrefixes(id: string): string[] {
return ADMIN_HUBS.find((h) => h.id === id)?.prefixes ?? [];
}