@qwickapps/server 1.2.0 → 1.3.1

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 (299) hide show
  1. package/README.md +392 -0
  2. package/dist/core/control-panel.d.ts +7 -2
  3. package/dist/core/control-panel.d.ts.map +1 -1
  4. package/dist/core/control-panel.js +120 -54
  5. package/dist/core/control-panel.js.map +1 -1
  6. package/dist/core/gateway.d.ts +159 -79
  7. package/dist/core/gateway.d.ts.map +1 -1
  8. package/dist/core/gateway.js +679 -319
  9. package/dist/core/gateway.js.map +1 -1
  10. package/dist/core/index.d.ts +3 -1
  11. package/dist/core/index.d.ts.map +1 -1
  12. package/dist/core/index.js +2 -0
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/core/plugin-registry.d.ts +307 -0
  15. package/dist/core/plugin-registry.d.ts.map +1 -0
  16. package/dist/core/plugin-registry.js +352 -0
  17. package/dist/core/plugin-registry.js.map +1 -0
  18. package/dist/core/types.d.ts +16 -33
  19. package/dist/core/types.d.ts.map +1 -1
  20. package/dist/index.d.ts +8 -5
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +15 -7
  23. package/dist/index.js.map +1 -1
  24. package/dist/plugins/auth/adapters/auth0-adapter.d.ts +14 -0
  25. package/dist/plugins/auth/adapters/auth0-adapter.d.ts.map +1 -0
  26. package/dist/plugins/auth/adapters/auth0-adapter.js +179 -0
  27. package/dist/plugins/auth/adapters/auth0-adapter.js.map +1 -0
  28. package/dist/plugins/auth/adapters/basic-adapter.d.ts +13 -0
  29. package/dist/plugins/auth/adapters/basic-adapter.d.ts.map +1 -0
  30. package/dist/plugins/auth/adapters/basic-adapter.js +51 -0
  31. package/dist/plugins/auth/adapters/basic-adapter.js.map +1 -0
  32. package/dist/plugins/auth/adapters/index.d.ts +10 -0
  33. package/dist/plugins/auth/adapters/index.d.ts.map +1 -0
  34. package/dist/plugins/auth/adapters/index.js +10 -0
  35. package/dist/plugins/auth/adapters/index.js.map +1 -0
  36. package/dist/plugins/auth/adapters/supabase-adapter.d.ts +13 -0
  37. package/dist/plugins/auth/adapters/supabase-adapter.d.ts.map +1 -0
  38. package/dist/plugins/auth/adapters/supabase-adapter.js +109 -0
  39. package/dist/plugins/auth/adapters/supabase-adapter.js.map +1 -0
  40. package/dist/plugins/auth/adapters/supertokens-adapter.d.ts +18 -0
  41. package/dist/plugins/auth/adapters/supertokens-adapter.d.ts.map +1 -0
  42. package/dist/plugins/auth/adapters/supertokens-adapter.js +267 -0
  43. package/dist/plugins/auth/adapters/supertokens-adapter.js.map +1 -0
  44. package/dist/plugins/auth/auth-plugin.d.ts +40 -0
  45. package/dist/plugins/auth/auth-plugin.d.ts.map +1 -0
  46. package/dist/plugins/auth/auth-plugin.js +255 -0
  47. package/dist/plugins/auth/auth-plugin.js.map +1 -0
  48. package/dist/plugins/auth/auth-plugin.test.d.ts +9 -0
  49. package/dist/plugins/auth/auth-plugin.test.d.ts.map +1 -0
  50. package/dist/plugins/auth/auth-plugin.test.js +147 -0
  51. package/dist/plugins/auth/auth-plugin.test.js.map +1 -0
  52. package/dist/plugins/auth/env-config.d.ts +88 -0
  53. package/dist/plugins/auth/env-config.d.ts.map +1 -0
  54. package/dist/plugins/auth/env-config.js +489 -0
  55. package/dist/plugins/auth/env-config.js.map +1 -0
  56. package/dist/plugins/auth/index.d.ts +14 -0
  57. package/dist/plugins/auth/index.d.ts.map +1 -0
  58. package/dist/plugins/auth/index.js +16 -0
  59. package/dist/plugins/auth/index.js.map +1 -0
  60. package/dist/plugins/auth/supertokens-adapter.test.d.ts +10 -0
  61. package/dist/plugins/auth/supertokens-adapter.test.d.ts.map +1 -0
  62. package/dist/plugins/auth/supertokens-adapter.test.js +486 -0
  63. package/dist/plugins/auth/supertokens-adapter.test.js.map +1 -0
  64. package/dist/plugins/auth/types.d.ts +218 -0
  65. package/dist/plugins/auth/types.d.ts.map +1 -0
  66. package/dist/plugins/auth/types.js +14 -0
  67. package/dist/plugins/auth/types.js.map +1 -0
  68. package/dist/plugins/bans/bans-plugin.d.ts +59 -0
  69. package/dist/plugins/bans/bans-plugin.d.ts.map +1 -0
  70. package/dist/plugins/bans/bans-plugin.js +428 -0
  71. package/dist/plugins/bans/bans-plugin.js.map +1 -0
  72. package/dist/plugins/bans/index.d.ts +9 -0
  73. package/dist/plugins/bans/index.d.ts.map +1 -0
  74. package/dist/plugins/bans/index.js +10 -0
  75. package/dist/plugins/bans/index.js.map +1 -0
  76. package/dist/plugins/bans/stores/index.d.ts +7 -0
  77. package/dist/plugins/bans/stores/index.d.ts.map +1 -0
  78. package/dist/plugins/bans/stores/index.js +7 -0
  79. package/dist/plugins/bans/stores/index.js.map +1 -0
  80. package/dist/plugins/bans/stores/postgres-store.d.ts +29 -0
  81. package/dist/plugins/bans/stores/postgres-store.d.ts.map +1 -0
  82. package/dist/plugins/bans/stores/postgres-store.js +132 -0
  83. package/dist/plugins/bans/stores/postgres-store.js.map +1 -0
  84. package/dist/plugins/bans/types.d.ts +128 -0
  85. package/dist/plugins/bans/types.d.ts.map +1 -0
  86. package/dist/plugins/bans/types.js +11 -0
  87. package/dist/plugins/bans/types.js.map +1 -0
  88. package/dist/plugins/cache-plugin.d.ts +14 -3
  89. package/dist/plugins/cache-plugin.d.ts.map +1 -1
  90. package/dist/plugins/cache-plugin.js +27 -7
  91. package/dist/plugins/cache-plugin.js.map +1 -1
  92. package/dist/plugins/cache-plugin.test.js +99 -32
  93. package/dist/plugins/cache-plugin.test.js.map +1 -1
  94. package/dist/plugins/config-plugin.d.ts +3 -2
  95. package/dist/plugins/config-plugin.d.ts.map +1 -1
  96. package/dist/plugins/config-plugin.js +17 -10
  97. package/dist/plugins/config-plugin.js.map +1 -1
  98. package/dist/plugins/diagnostics-plugin.d.ts +2 -2
  99. package/dist/plugins/diagnostics-plugin.d.ts.map +1 -1
  100. package/dist/plugins/diagnostics-plugin.js +17 -10
  101. package/dist/plugins/diagnostics-plugin.js.map +1 -1
  102. package/dist/plugins/entitlements/entitlements-plugin.d.ts +95 -0
  103. package/dist/plugins/entitlements/entitlements-plugin.d.ts.map +1 -0
  104. package/dist/plugins/entitlements/entitlements-plugin.js +707 -0
  105. package/dist/plugins/entitlements/entitlements-plugin.js.map +1 -0
  106. package/dist/plugins/entitlements/index.d.ts +12 -0
  107. package/dist/plugins/entitlements/index.d.ts.map +1 -0
  108. package/dist/plugins/entitlements/index.js +16 -0
  109. package/dist/plugins/entitlements/index.js.map +1 -0
  110. package/dist/plugins/entitlements/sources/index.d.ts +9 -0
  111. package/dist/plugins/entitlements/sources/index.d.ts.map +1 -0
  112. package/dist/plugins/entitlements/sources/index.js +9 -0
  113. package/dist/plugins/entitlements/sources/index.js.map +1 -0
  114. package/dist/plugins/entitlements/sources/postgres-source.d.ts +29 -0
  115. package/dist/plugins/entitlements/sources/postgres-source.d.ts.map +1 -0
  116. package/dist/plugins/entitlements/sources/postgres-source.js +169 -0
  117. package/dist/plugins/entitlements/sources/postgres-source.js.map +1 -0
  118. package/dist/plugins/entitlements/types.d.ts +232 -0
  119. package/dist/plugins/entitlements/types.d.ts.map +1 -0
  120. package/dist/plugins/entitlements/types.js +11 -0
  121. package/dist/plugins/entitlements/types.js.map +1 -0
  122. package/dist/plugins/frontend-app-plugin.d.ts +9 -3
  123. package/dist/plugins/frontend-app-plugin.d.ts.map +1 -1
  124. package/dist/plugins/frontend-app-plugin.js +14 -9
  125. package/dist/plugins/frontend-app-plugin.js.map +1 -1
  126. package/dist/plugins/health-plugin.d.ts +5 -2
  127. package/dist/plugins/health-plugin.d.ts.map +1 -1
  128. package/dist/plugins/health-plugin.js +20 -5
  129. package/dist/plugins/health-plugin.js.map +1 -1
  130. package/dist/plugins/index.d.ts +10 -2
  131. package/dist/plugins/index.d.ts.map +1 -1
  132. package/dist/plugins/index.js +10 -2
  133. package/dist/plugins/index.js.map +1 -1
  134. package/dist/plugins/logs-plugin.d.ts +3 -2
  135. package/dist/plugins/logs-plugin.d.ts.map +1 -1
  136. package/dist/plugins/logs-plugin.js +21 -12
  137. package/dist/plugins/logs-plugin.js.map +1 -1
  138. package/dist/plugins/postgres-plugin.d.ts +3 -3
  139. package/dist/plugins/postgres-plugin.d.ts.map +1 -1
  140. package/dist/plugins/postgres-plugin.js +9 -7
  141. package/dist/plugins/postgres-plugin.js.map +1 -1
  142. package/dist/plugins/postgres-plugin.test.js +50 -29
  143. package/dist/plugins/postgres-plugin.test.js.map +1 -1
  144. package/dist/plugins/preferences/__tests__/deep-merge.test.d.ts +7 -0
  145. package/dist/plugins/preferences/__tests__/deep-merge.test.d.ts.map +1 -0
  146. package/dist/plugins/preferences/__tests__/deep-merge.test.js +215 -0
  147. package/dist/plugins/preferences/__tests__/deep-merge.test.js.map +1 -0
  148. package/dist/plugins/preferences/__tests__/preferences-plugin.test.d.ts +7 -0
  149. package/dist/plugins/preferences/__tests__/preferences-plugin.test.d.ts.map +1 -0
  150. package/dist/plugins/preferences/__tests__/preferences-plugin.test.js +265 -0
  151. package/dist/plugins/preferences/__tests__/preferences-plugin.test.js.map +1 -0
  152. package/dist/plugins/preferences/index.d.ts +12 -0
  153. package/dist/plugins/preferences/index.d.ts.map +1 -0
  154. package/dist/plugins/preferences/index.js +13 -0
  155. package/dist/plugins/preferences/index.js.map +1 -0
  156. package/dist/plugins/preferences/preferences-plugin.d.ts +39 -0
  157. package/dist/plugins/preferences/preferences-plugin.d.ts.map +1 -0
  158. package/dist/plugins/preferences/preferences-plugin.js +226 -0
  159. package/dist/plugins/preferences/preferences-plugin.js.map +1 -0
  160. package/dist/plugins/preferences/stores/index.d.ts +9 -0
  161. package/dist/plugins/preferences/stores/index.d.ts.map +1 -0
  162. package/dist/plugins/preferences/stores/index.js +9 -0
  163. package/dist/plugins/preferences/stores/index.js.map +1 -0
  164. package/dist/plugins/preferences/stores/postgres-store.d.ts +41 -0
  165. package/dist/plugins/preferences/stores/postgres-store.d.ts.map +1 -0
  166. package/dist/plugins/preferences/stores/postgres-store.js +181 -0
  167. package/dist/plugins/preferences/stores/postgres-store.js.map +1 -0
  168. package/dist/plugins/preferences/types.d.ts +91 -0
  169. package/dist/plugins/preferences/types.d.ts.map +1 -0
  170. package/dist/plugins/preferences/types.js +10 -0
  171. package/dist/plugins/preferences/types.js.map +1 -0
  172. package/dist/plugins/users/__tests__/users-plugin.test.d.ts +9 -0
  173. package/dist/plugins/users/__tests__/users-plugin.test.d.ts.map +1 -0
  174. package/dist/plugins/users/__tests__/users-plugin.test.js +546 -0
  175. package/dist/plugins/users/__tests__/users-plugin.test.js.map +1 -0
  176. package/dist/plugins/users/index.d.ts +12 -0
  177. package/dist/plugins/users/index.d.ts.map +1 -0
  178. package/dist/plugins/users/index.js +13 -0
  179. package/dist/plugins/users/index.js.map +1 -0
  180. package/dist/plugins/users/stores/index.d.ts +7 -0
  181. package/dist/plugins/users/stores/index.d.ts.map +1 -0
  182. package/dist/plugins/users/stores/index.js +7 -0
  183. package/dist/plugins/users/stores/index.js.map +1 -0
  184. package/dist/plugins/users/stores/postgres-store.d.ts +28 -0
  185. package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -0
  186. package/dist/plugins/users/stores/postgres-store.js +157 -0
  187. package/dist/plugins/users/stores/postgres-store.js.map +1 -0
  188. package/dist/plugins/users/types.d.ts +225 -0
  189. package/dist/plugins/users/types.d.ts.map +1 -0
  190. package/dist/plugins/users/types.js +12 -0
  191. package/dist/plugins/users/types.js.map +1 -0
  192. package/dist/plugins/users/users-plugin.d.ts +45 -0
  193. package/dist/plugins/users/users-plugin.d.ts.map +1 -0
  194. package/dist/plugins/users/users-plugin.js +359 -0
  195. package/dist/plugins/users/users-plugin.js.map +1 -0
  196. package/dist-ui/assets/index-BY8OxNgO.js +465 -0
  197. package/dist-ui/assets/index-BY8OxNgO.js.map +1 -0
  198. package/dist-ui/index.html +1 -1
  199. package/dist-ui-lib/api/controlPanelApi.d.ts +278 -0
  200. package/dist-ui-lib/components/ControlPanelApp.d.ts +61 -0
  201. package/dist-ui-lib/components/index.d.ts +18 -0
  202. package/dist-ui-lib/config/AppConfig.d.ts +7 -0
  203. package/dist-ui-lib/dashboard/DashboardWidgetRegistry.d.ts +62 -0
  204. package/dist-ui-lib/dashboard/DashboardWidgetRenderer.d.ts +8 -0
  205. package/dist-ui-lib/dashboard/PluginWidgetRenderer.d.ts +19 -0
  206. package/dist-ui-lib/dashboard/WidgetComponentRegistry.d.ts +48 -0
  207. package/dist-ui-lib/dashboard/builtInWidgets.d.ts +25 -0
  208. package/dist-ui-lib/dashboard/index.d.ts +13 -0
  209. package/dist-ui-lib/dashboard/widgets/ServiceHealthWidget.d.ts +12 -0
  210. package/dist-ui-lib/dashboard/widgets/index.d.ts +6 -0
  211. package/dist-ui-lib/index.js +5172 -0
  212. package/dist-ui-lib/index.js.map +1 -0
  213. package/dist-ui-lib/pages/AuthPage.d.ts +1 -0
  214. package/dist-ui-lib/pages/ConfigPage.d.ts +1 -0
  215. package/dist-ui-lib/pages/DashboardPage.d.ts +1 -0
  216. package/dist-ui-lib/pages/DiagnosticsPage.d.ts +1 -0
  217. package/dist-ui-lib/pages/EntitlementsPage.d.ts +17 -0
  218. package/dist-ui-lib/pages/LogsPage.d.ts +1 -0
  219. package/dist-ui-lib/pages/NotFoundPage.d.ts +1 -0
  220. package/dist-ui-lib/pages/PluginPage.d.ts +15 -0
  221. package/dist-ui-lib/pages/PluginsPage.d.ts +1 -0
  222. package/dist-ui-lib/pages/SystemPage.d.ts +1 -0
  223. package/dist-ui-lib/pages/UsersPage.d.ts +22 -0
  224. package/package.json +24 -7
  225. package/src/core/control-panel.ts +145 -61
  226. package/src/core/gateway.ts +863 -403
  227. package/src/core/index.ts +21 -2
  228. package/src/core/plugin-registry.ts +716 -0
  229. package/src/core/types.ts +31 -37
  230. package/src/index.ts +125 -19
  231. package/src/plugins/auth/adapters/auth0-adapter.ts +214 -0
  232. package/src/plugins/auth/adapters/basic-adapter.ts +61 -0
  233. package/src/plugins/auth/adapters/index.ts +10 -0
  234. package/src/plugins/auth/adapters/supabase-adapter.ts +149 -0
  235. package/src/plugins/auth/adapters/supertokens-adapter.ts +326 -0
  236. package/src/plugins/auth/auth-plugin.test.ts +176 -0
  237. package/src/plugins/auth/auth-plugin.ts +303 -0
  238. package/src/plugins/auth/env-config.ts +572 -0
  239. package/src/plugins/auth/index.ts +42 -0
  240. package/src/plugins/auth/supertokens-adapter.test.ts +621 -0
  241. package/src/plugins/auth/types.ts +245 -0
  242. package/src/plugins/bans/bans-plugin.ts +485 -0
  243. package/src/plugins/bans/index.ts +31 -0
  244. package/src/plugins/bans/stores/index.ts +7 -0
  245. package/src/plugins/bans/stores/postgres-store.ts +195 -0
  246. package/src/plugins/bans/types.ts +141 -0
  247. package/src/plugins/cache-plugin.test.ts +108 -32
  248. package/src/plugins/cache-plugin.ts +40 -9
  249. package/src/plugins/config-plugin.ts +23 -12
  250. package/src/plugins/diagnostics-plugin.ts +22 -12
  251. package/src/plugins/entitlements/entitlements-plugin.ts +820 -0
  252. package/src/plugins/entitlements/index.ts +51 -0
  253. package/src/plugins/entitlements/sources/index.ts +9 -0
  254. package/src/plugins/entitlements/sources/postgres-source.ts +253 -0
  255. package/src/plugins/entitlements/types.ts +256 -0
  256. package/src/plugins/frontend-app-plugin.ts +24 -12
  257. package/src/plugins/health-plugin.ts +27 -7
  258. package/src/plugins/index.ts +132 -4
  259. package/src/plugins/logs-plugin.ts +28 -14
  260. package/src/plugins/postgres-plugin.test.ts +52 -29
  261. package/src/plugins/postgres-plugin.ts +11 -9
  262. package/src/plugins/preferences/__tests__/deep-merge.test.ts +242 -0
  263. package/src/plugins/preferences/__tests__/preferences-plugin.test.ts +350 -0
  264. package/src/plugins/preferences/index.ts +30 -0
  265. package/src/plugins/preferences/preferences-plugin.ts +270 -0
  266. package/src/plugins/preferences/stores/index.ts +9 -0
  267. package/src/plugins/preferences/stores/postgres-store.ts +252 -0
  268. package/src/plugins/preferences/types.ts +100 -0
  269. package/src/plugins/users/__tests__/users-plugin.test.ts +690 -0
  270. package/src/plugins/users/index.ts +38 -0
  271. package/src/plugins/users/stores/index.ts +7 -0
  272. package/src/plugins/users/stores/postgres-store.ts +225 -0
  273. package/src/plugins/users/types.ts +247 -0
  274. package/src/plugins/users/users-plugin.ts +418 -0
  275. package/ui/src/App.tsx +188 -31
  276. package/ui/src/api/controlPanelApi.ts +453 -1
  277. package/ui/src/components/ControlPanelApp.tsx +212 -0
  278. package/ui/src/components/index.ts +62 -0
  279. package/ui/src/dashboard/DashboardWidgetRegistry.tsx +129 -0
  280. package/ui/src/dashboard/DashboardWidgetRenderer.tsx +34 -0
  281. package/ui/src/dashboard/PluginWidgetRenderer.tsx +118 -0
  282. package/ui/src/dashboard/WidgetComponentRegistry.tsx +120 -0
  283. package/ui/src/dashboard/builtInWidgets.tsx +35 -0
  284. package/ui/src/dashboard/index.ts +35 -0
  285. package/ui/src/dashboard/widgets/ServiceHealthWidget.tsx +140 -0
  286. package/ui/src/dashboard/widgets/index.ts +7 -0
  287. package/ui/src/pages/AuthPage.tsx +259 -0
  288. package/ui/src/pages/DashboardPage.tsx +28 -149
  289. package/ui/src/pages/EntitlementsPage.tsx +557 -0
  290. package/ui/src/pages/LogsPage.tsx +174 -8
  291. package/ui/src/pages/PluginPage.tsx +148 -0
  292. package/ui/src/pages/PluginsPage.tsx +394 -0
  293. package/ui/src/pages/SystemPage.tsx +445 -0
  294. package/ui/src/pages/UsersPage.tsx +837 -0
  295. package/ui/tsconfig.lib.json +11 -0
  296. package/ui/vite.lib.config.ts +56 -0
  297. package/dist-ui/assets/index-CW1BviRn.js +0 -465
  298. package/dist-ui/assets/index-CW1BviRn.js.map +0 -1
  299. package/ui/src/pages/HealthPage.tsx +0 -204
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Control Panel UI Components
3
+ *
4
+ * Re-exports all public UI components for use by consumers.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+
9
+ export { ControlPanelApp, type ControlPanelAppProps } from './ControlPanelApp';
10
+
11
+ // Re-export MenuItem from react-framework for convenience
12
+ export type { MenuItem } from '@qwickapps/react-framework';
13
+
14
+ // Re-export base pages for consumers who want to use them directly
15
+ export { DashboardPage } from '../pages/DashboardPage';
16
+ export { LogsPage } from '../pages/LogsPage';
17
+ export { SystemPage } from '../pages/SystemPage';
18
+ export { NotFoundPage } from '../pages/NotFoundPage';
19
+ export { UsersPage, type UsersPageProps } from '../pages/UsersPage';
20
+ export { EntitlementsPage, type EntitlementsPageProps } from '../pages/EntitlementsPage';
21
+
22
+ // Re-export dashboard widget system (legacy context-based + new plugin-based)
23
+ export {
24
+ // Legacy context-based widget system
25
+ DashboardWidgetProvider,
26
+ useDashboardWidgets,
27
+ useRegisterWidget,
28
+ DashboardWidgetRenderer,
29
+ type DashboardWidget,
30
+ type DashboardWidgetProviderProps,
31
+ // New plugin-based widget system
32
+ WidgetComponentRegistryProvider,
33
+ useWidgetComponentRegistry,
34
+ PluginWidgetRenderer,
35
+ getBuiltInWidgetComponents,
36
+ ServiceHealthWidget,
37
+ type WidgetComponent,
38
+ type WidgetComponentRegistryProviderProps,
39
+ } from '../dashboard';
40
+
41
+ // Re-export API client and types
42
+ export { api } from '../api/controlPanelApi';
43
+ export type {
44
+ HealthCheck,
45
+ HealthResponse,
46
+ InfoResponse,
47
+ DiagnosticsResponse,
48
+ ConfigResponse,
49
+ LogEntry,
50
+ LogsResponse,
51
+ LogSource,
52
+ // User management types
53
+ User,
54
+ UsersResponse,
55
+ Ban,
56
+ BansResponse,
57
+ EntitlementDefinition,
58
+ EntitlementResult,
59
+ EntitlementSourceInfo,
60
+ EntitlementsStatus,
61
+ PluginFeatures,
62
+ } from '../api/controlPanelApi';
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Dashboard Widget Registry
3
+ *
4
+ * A context-based registry for dashboard widgets that allows:
5
+ * - Registration of widgets at runtime
6
+ * - Dynamic adding/removing of widgets
7
+ * - Priority-based ordering of widgets
8
+ *
9
+ * Usage:
10
+ * ```tsx
11
+ * // In your app setup:
12
+ * const { registerWidget } = useDashboardWidgets();
13
+ * registerWidget({
14
+ * id: 'user-stats',
15
+ * title: 'User Statistics',
16
+ * component: <UserStatsWidget />,
17
+ * priority: 10,
18
+ * });
19
+ *
20
+ * // Or via the provider:
21
+ * <DashboardWidgetProvider initialWidgets={[...]} />
22
+ * ```
23
+ *
24
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
25
+ */
26
+
27
+ import { createContext, useContext, useState, useCallback, ReactNode } from 'react';
28
+
29
+ export interface DashboardWidget {
30
+ /** Unique identifier for the widget */
31
+ id: string;
32
+ /** Display title for the widget section */
33
+ title?: string;
34
+ /** The widget component to render */
35
+ component: ReactNode;
36
+ /** Priority for ordering (lower = first, default: 100) */
37
+ priority?: number;
38
+ /** Whether the widget is visible */
39
+ visible?: boolean;
40
+ }
41
+
42
+ interface DashboardWidgetContextValue {
43
+ /** All registered widgets */
44
+ widgets: DashboardWidget[];
45
+ /** Register a new widget */
46
+ registerWidget: (widget: DashboardWidget) => void;
47
+ /** Unregister a widget by ID */
48
+ unregisterWidget: (id: string) => void;
49
+ /** Toggle widget visibility */
50
+ toggleWidget: (id: string, visible?: boolean) => void;
51
+ /** Get visible widgets sorted by priority */
52
+ getVisibleWidgets: () => DashboardWidget[];
53
+ }
54
+
55
+ const DashboardWidgetContext = createContext<DashboardWidgetContextValue | null>(null);
56
+
57
+ export interface DashboardWidgetProviderProps {
58
+ /** Initial widgets to register */
59
+ initialWidgets?: DashboardWidget[];
60
+ children: ReactNode;
61
+ }
62
+
63
+ export function DashboardWidgetProvider({ initialWidgets = [], children }: DashboardWidgetProviderProps) {
64
+ const [widgets, setWidgets] = useState<DashboardWidget[]>(
65
+ initialWidgets.map(w => ({ ...w, visible: w.visible !== false, priority: w.priority ?? 100 }))
66
+ );
67
+
68
+ const registerWidget = useCallback((widget: DashboardWidget) => {
69
+ setWidgets(prev => {
70
+ // Check if widget already exists
71
+ const exists = prev.some(w => w.id === widget.id);
72
+ if (exists) {
73
+ // Update existing widget
74
+ return prev.map(w => w.id === widget.id ? { ...widget, visible: widget.visible !== false, priority: widget.priority ?? 100 } : w);
75
+ }
76
+ // Add new widget
77
+ return [...prev, { ...widget, visible: widget.visible !== false, priority: widget.priority ?? 100 }];
78
+ });
79
+ }, []);
80
+
81
+ const unregisterWidget = useCallback((id: string) => {
82
+ setWidgets(prev => prev.filter(w => w.id !== id));
83
+ }, []);
84
+
85
+ const toggleWidget = useCallback((id: string, visible?: boolean) => {
86
+ setWidgets(prev => prev.map(w => {
87
+ if (w.id === id) {
88
+ return { ...w, visible: visible ?? !w.visible };
89
+ }
90
+ return w;
91
+ }));
92
+ }, []);
93
+
94
+ const getVisibleWidgets = useCallback(() => {
95
+ return widgets
96
+ .filter(w => w.visible !== false)
97
+ .sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
98
+ }, [widgets]);
99
+
100
+ return (
101
+ <DashboardWidgetContext.Provider value={{ widgets, registerWidget, unregisterWidget, toggleWidget, getVisibleWidgets }}>
102
+ {children}
103
+ </DashboardWidgetContext.Provider>
104
+ );
105
+ }
106
+
107
+ export function useDashboardWidgets() {
108
+ const context = useContext(DashboardWidgetContext);
109
+ if (!context) {
110
+ throw new Error('useDashboardWidgets must be used within a DashboardWidgetProvider');
111
+ }
112
+ return context;
113
+ }
114
+
115
+ /**
116
+ * Hook to register a widget on mount and unregister on unmount
117
+ */
118
+ export function useRegisterWidget(widget: DashboardWidget) {
119
+ const { registerWidget, unregisterWidget } = useDashboardWidgets();
120
+
121
+ // Register on mount
122
+ useState(() => {
123
+ registerWidget(widget);
124
+ return null;
125
+ });
126
+
127
+ // Return unregister function for manual cleanup
128
+ return () => unregisterWidget(widget.id);
129
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Dashboard Widget Renderer
3
+ *
4
+ * Renders all visible dashboard widgets from the registry.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+
9
+ import { Box, Typography } from '@mui/material';
10
+ import { useDashboardWidgets } from './DashboardWidgetRegistry';
11
+
12
+ export function DashboardWidgetRenderer() {
13
+ const { getVisibleWidgets } = useDashboardWidgets();
14
+ const widgets = getVisibleWidgets();
15
+
16
+ if (widgets.length === 0) {
17
+ return null;
18
+ }
19
+
20
+ return (
21
+ <>
22
+ {widgets.map(widget => (
23
+ <Box key={widget.id} sx={{ mt: 4 }}>
24
+ {widget.title && (
25
+ <Typography variant="h6" sx={{ mb: 2, color: 'var(--theme-text-primary)' }}>
26
+ {widget.title}
27
+ </Typography>
28
+ )}
29
+ {widget.component}
30
+ </Box>
31
+ ))}
32
+ </>
33
+ );
34
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Plugin Widget Renderer
3
+ *
4
+ * Fetches widget contributions from the server API and renders them using
5
+ * the WidgetComponentRegistry to resolve component names to React components.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ import { useState, useEffect } from 'react';
11
+ import { Box, Typography, CircularProgress, Alert } from '@mui/material';
12
+ import { api } from '../api/controlPanelApi';
13
+ import { useWidgetComponentRegistry } from './WidgetComponentRegistry';
14
+
15
+ /**
16
+ * Widget contribution from the server
17
+ */
18
+ interface WidgetContribution {
19
+ id: string;
20
+ title: string;
21
+ component: string;
22
+ priority?: number;
23
+ showByDefault?: boolean;
24
+ pluginId: string;
25
+ }
26
+
27
+ interface PluginWidgetRendererProps {
28
+ /** Only show widgets marked as showByDefault (default: true) */
29
+ defaultOnly?: boolean;
30
+ /** Additional widget IDs to show (beyond showByDefault) */
31
+ additionalWidgetIds?: string[];
32
+ }
33
+
34
+ /**
35
+ * Renders widgets from plugins that have registered them via the server API
36
+ */
37
+ export function PluginWidgetRenderer({
38
+ defaultOnly = true,
39
+ additionalWidgetIds = [],
40
+ }: PluginWidgetRendererProps) {
41
+ const [widgets, setWidgets] = useState<WidgetContribution[]>([]);
42
+ const [loading, setLoading] = useState(true);
43
+ const [error, setError] = useState<string | null>(null);
44
+ const { getComponent, hasComponent } = useWidgetComponentRegistry();
45
+
46
+ useEffect(() => {
47
+ const fetchWidgets = async () => {
48
+ try {
49
+ const data = await api.getUiContributions();
50
+ setWidgets(data.widgets || []);
51
+ setError(null);
52
+ } catch (err) {
53
+ setError(err instanceof Error ? err.message : 'Failed to fetch widgets');
54
+ } finally {
55
+ setLoading(false);
56
+ }
57
+ };
58
+
59
+ fetchWidgets();
60
+ }, []);
61
+
62
+ if (loading) {
63
+ return (
64
+ <Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
65
+ <CircularProgress size={24} />
66
+ </Box>
67
+ );
68
+ }
69
+
70
+ if (error) {
71
+ return (
72
+ <Alert severity="error" sx={{ mt: 2 }}>
73
+ {error}
74
+ </Alert>
75
+ );
76
+ }
77
+
78
+ // Filter widgets to show
79
+ const visibleWidgets = widgets
80
+ .filter(widget => {
81
+ // Show if marked as default, or if in additionalWidgetIds
82
+ if (defaultOnly) {
83
+ return widget.showByDefault || additionalWidgetIds.includes(widget.id);
84
+ }
85
+ return true;
86
+ })
87
+ .filter(widget => {
88
+ // Only show widgets that have a registered component
89
+ if (!hasComponent(widget.component)) {
90
+ console.warn(`Widget "${widget.id}" references unregistered component "${widget.component}"`);
91
+ return false;
92
+ }
93
+ return true;
94
+ })
95
+ .sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
96
+
97
+ if (visibleWidgets.length === 0) {
98
+ return null;
99
+ }
100
+
101
+ return (
102
+ <>
103
+ {visibleWidgets.map(widget => {
104
+ const Component = getComponent(widget.component);
105
+ return (
106
+ <Box key={widget.id} sx={{ mt: 4 }}>
107
+ {widget.title && (
108
+ <Typography variant="h6" sx={{ mb: 2, color: 'var(--theme-text-primary)' }}>
109
+ {widget.title}
110
+ </Typography>
111
+ )}
112
+ {Component && <Component />}
113
+ </Box>
114
+ );
115
+ })}
116
+ </>
117
+ );
118
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Widget Component Registry
3
+ *
4
+ * Maps widget component names (from server-side WidgetContribution) to actual React components.
5
+ * Plugins register their widgets here so the dashboard can render them.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ import React, { createContext, useContext, useState, useCallback, useMemo, type ReactNode } from 'react';
11
+
12
+ /**
13
+ * Widget component definition
14
+ *
15
+ * IMPORTANT: We store component functions (ComponentType), not JSX instances (ReactNode).
16
+ * This ensures cross-React-version compatibility when the library is used in apps
17
+ * with different React versions.
18
+ */
19
+ export interface WidgetComponent {
20
+ /** Component name (must match server-side WidgetContribution.component) */
21
+ name: string;
22
+ /** The React component function to render */
23
+ component: React.ComponentType;
24
+ }
25
+
26
+ interface WidgetComponentRegistryContextValue {
27
+ /** Register a widget component */
28
+ registerComponent: (name: string, component: React.ComponentType) => void;
29
+ /** Register multiple widget components */
30
+ registerComponents: (components: WidgetComponent[]) => void;
31
+ /** Get a component by name */
32
+ getComponent: (name: string) => React.ComponentType | null;
33
+ /** Check if a component is registered */
34
+ hasComponent: (name: string) => boolean;
35
+ /** Get all registered component names */
36
+ getRegisteredNames: () => string[];
37
+ }
38
+
39
+ const WidgetComponentRegistryContext = createContext<WidgetComponentRegistryContextValue | null>(null);
40
+
41
+ export interface WidgetComponentRegistryProviderProps {
42
+ /** Initial components to register */
43
+ initialComponents?: WidgetComponent[];
44
+ children: ReactNode;
45
+ }
46
+
47
+ /**
48
+ * Provider for the widget component registry
49
+ */
50
+ export function WidgetComponentRegistryProvider({
51
+ initialComponents = [],
52
+ children,
53
+ }: WidgetComponentRegistryProviderProps) {
54
+ const [components, setComponents] = useState<Map<string, React.ComponentType>>(() => {
55
+ const map = new Map<string, React.ComponentType>();
56
+ for (const comp of initialComponents) {
57
+ map.set(comp.name, comp.component);
58
+ }
59
+ return map;
60
+ });
61
+
62
+ const registerComponent = useCallback((name: string, component: React.ComponentType) => {
63
+ setComponents(prev => {
64
+ const next = new Map(prev);
65
+ next.set(name, component);
66
+ return next;
67
+ });
68
+ }, []);
69
+
70
+ const registerComponents = useCallback((comps: WidgetComponent[]) => {
71
+ setComponents(prev => {
72
+ const next = new Map(prev);
73
+ for (const comp of comps) {
74
+ next.set(comp.name, comp.component);
75
+ }
76
+ return next;
77
+ });
78
+ }, []);
79
+
80
+ const getComponent = useCallback((name: string): React.ComponentType | null => {
81
+ return components.get(name) ?? null;
82
+ }, [components]);
83
+
84
+ const hasComponent = useCallback((name: string): boolean => {
85
+ return components.has(name);
86
+ }, [components]);
87
+
88
+ const getRegisteredNames = useCallback((): string[] => {
89
+ return Array.from(components.keys());
90
+ }, [components]);
91
+
92
+ // Memoize context value to prevent unnecessary re-renders of consumers
93
+ const contextValue = useMemo(
94
+ () => ({
95
+ registerComponent,
96
+ registerComponents,
97
+ getComponent,
98
+ hasComponent,
99
+ getRegisteredNames,
100
+ }),
101
+ [registerComponent, registerComponents, getComponent, hasComponent, getRegisteredNames]
102
+ );
103
+
104
+ return (
105
+ <WidgetComponentRegistryContext.Provider value={contextValue}>
106
+ {children}
107
+ </WidgetComponentRegistryContext.Provider>
108
+ );
109
+ }
110
+
111
+ /**
112
+ * Hook to access the widget component registry
113
+ */
114
+ export function useWidgetComponentRegistry() {
115
+ const context = useContext(WidgetComponentRegistryContext);
116
+ if (!context) {
117
+ throw new Error('useWidgetComponentRegistry must be used within a WidgetComponentRegistryProvider');
118
+ }
119
+ return context;
120
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Built-in Widget Components
3
+ *
4
+ * Maps built-in widget component names to their React components.
5
+ * These are the widgets that qwickapps-server provides out of the box.
6
+ *
7
+ * IMPORTANT: We export component functions, not JSX instances.
8
+ * This ensures cross-React-version compatibility.
9
+ *
10
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
11
+ */
12
+
13
+ import { ServiceHealthWidget } from './widgets';
14
+ import type { WidgetComponent } from './WidgetComponentRegistry';
15
+
16
+ /**
17
+ * Map of built-in widget component names to their React component functions.
18
+ * Use this when you need to look up a component by name.
19
+ */
20
+ export const builtInWidgetComponents: Record<string, React.ComponentType> = {
21
+ ServiceHealthWidget: ServiceHealthWidget,
22
+ };
23
+
24
+ /**
25
+ * Get built-in widget components as WidgetComponent array.
26
+ * Use this when registering with WidgetComponentRegistryProvider.
27
+ *
28
+ * Returns component functions (not JSX instances) to ensure compatibility
29
+ * across different React versions.
30
+ */
31
+ export function getBuiltInWidgetComponents(): WidgetComponent[] {
32
+ return [
33
+ { name: 'ServiceHealthWidget', component: ServiceHealthWidget },
34
+ ];
35
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Dashboard Module
3
+ *
4
+ * Exports the dashboard widget system for dynamic dashboard customization.
5
+ *
6
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
7
+ */
8
+
9
+ // Legacy context-based widget system (for backwards compatibility)
10
+ export {
11
+ DashboardWidgetProvider,
12
+ useDashboardWidgets,
13
+ useRegisterWidget,
14
+ type DashboardWidget,
15
+ type DashboardWidgetProviderProps,
16
+ } from './DashboardWidgetRegistry';
17
+
18
+ export { DashboardWidgetRenderer } from './DashboardWidgetRenderer';
19
+
20
+ // New plugin-based widget system
21
+ export {
22
+ WidgetComponentRegistryProvider,
23
+ useWidgetComponentRegistry,
24
+ type WidgetComponent,
25
+ type WidgetComponentRegistryProviderProps,
26
+ } from './WidgetComponentRegistry';
27
+
28
+ export { PluginWidgetRenderer } from './PluginWidgetRenderer';
29
+
30
+ // Built-in widgets
31
+ export { ServiceHealthWidget } from './widgets';
32
+
33
+ // Built-in widget component map (component name -> React component function)
34
+ // Product code should use getBuiltInWidgetComponents() to get the full list with JSX
35
+ export { builtInWidgetComponents, getBuiltInWidgetComponents } from './builtInWidgets';
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Service Health Widget
3
+ *
4
+ * Displays health check status with latency for all registered health checks.
5
+ * This is a built-in widget provided by qwickapps-server.
6
+ *
7
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
8
+ */
9
+
10
+ import { useState, useEffect } from 'react';
11
+ import { Box, Card, CardContent, Chip } from '@mui/material';
12
+ import { GridLayout, Text } from '@qwickapps/react-framework';
13
+ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
14
+ import ErrorIcon from '@mui/icons-material/Error';
15
+ import WarningIcon from '@mui/icons-material/Warning';
16
+ import { api, HealthResponse } from '../../api/controlPanelApi';
17
+
18
+ function getStatusIcon(status: string) {
19
+ switch (status) {
20
+ case 'healthy':
21
+ return <CheckCircleIcon sx={{ fontSize: 24, color: 'var(--theme-success)' }} />;
22
+ case 'degraded':
23
+ return <WarningIcon sx={{ fontSize: 24, color: 'var(--theme-warning)' }} />;
24
+ case 'unhealthy':
25
+ return <ErrorIcon sx={{ fontSize: 24, color: 'var(--theme-error)' }} />;
26
+ default:
27
+ return <WarningIcon sx={{ fontSize: 24, color: 'var(--theme-text-secondary)' }} />;
28
+ }
29
+ }
30
+
31
+ function getStatusColor(status: string): string {
32
+ switch (status) {
33
+ case 'healthy':
34
+ return 'var(--theme-success)';
35
+ case 'degraded':
36
+ return 'var(--theme-warning)';
37
+ case 'unhealthy':
38
+ return 'var(--theme-error)';
39
+ default:
40
+ return 'var(--theme-text-secondary)';
41
+ }
42
+ }
43
+
44
+ /** Get grid columns count (1-4) based on item count */
45
+ function getGridColumns(count: number): 1 | 2 | 3 | 4 {
46
+ if (count <= 1) return 1;
47
+ if (count === 2) return 2;
48
+ if (count === 3) return 3;
49
+ return 4;
50
+ }
51
+
52
+ /**
53
+ * Service Health Widget Component
54
+ */
55
+ export function ServiceHealthWidget() {
56
+ const [health, setHealth] = useState<HealthResponse | null>(null);
57
+ const [error, setError] = useState<string | null>(null);
58
+
59
+ useEffect(() => {
60
+ const fetchHealth = async () => {
61
+ try {
62
+ const data = await api.getHealth();
63
+ setHealth(data);
64
+ setError(null);
65
+ } catch (err) {
66
+ setError(err instanceof Error ? err.message : 'Failed to fetch health');
67
+ }
68
+ };
69
+
70
+ fetchHealth();
71
+ const interval = setInterval(fetchHealth, 10000);
72
+ return () => clearInterval(interval);
73
+ }, []);
74
+
75
+ if (error) {
76
+ return (
77
+ <Card sx={{ bgcolor: 'var(--theme-surface)', border: '1px solid var(--theme-error)' }}>
78
+ <CardContent>
79
+ <Text variant="body2" customColor="var(--theme-error)" content={error} />
80
+ </CardContent>
81
+ </Card>
82
+ );
83
+ }
84
+
85
+ const healthChecks = health ? Object.entries(health.checks) : [];
86
+
87
+ if (healthChecks.length === 0) {
88
+ return (
89
+ <Card sx={{ bgcolor: 'var(--theme-surface)' }}>
90
+ <CardContent>
91
+ <Text variant="body2" customColor="var(--theme-text-secondary)" content="No health checks configured" />
92
+ </CardContent>
93
+ </Card>
94
+ );
95
+ }
96
+
97
+ // Determine grid columns based on number of health checks (max 4)
98
+ const columns = getGridColumns(healthChecks.length);
99
+
100
+ return (
101
+ <GridLayout columns={columns} spacing="medium" equalHeight>
102
+ {healthChecks.map(([name, check]) => (
103
+ <Card key={name} sx={{ bgcolor: 'var(--theme-surface)' }}>
104
+ <CardContent>
105
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
106
+ {getStatusIcon(check.status)}
107
+ <Box sx={{ flex: 1, minWidth: 0 }}>
108
+ <Text
109
+ variant="body1"
110
+ fontWeight="500"
111
+ content={name.charAt(0).toUpperCase() + name.slice(1)}
112
+ customColor="var(--theme-text-primary)"
113
+ />
114
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mt: 0.5 }}>
115
+ <Chip
116
+ label={check.status}
117
+ size="small"
118
+ sx={{
119
+ bgcolor: getStatusColor(check.status) + '20',
120
+ color: getStatusColor(check.status),
121
+ fontSize: '0.75rem',
122
+ height: 20,
123
+ }}
124
+ />
125
+ {check.latency !== undefined && (
126
+ <Text
127
+ variant="caption"
128
+ content={`${check.latency}ms`}
129
+ customColor="var(--theme-text-secondary)"
130
+ />
131
+ )}
132
+ </Box>
133
+ </Box>
134
+ </Box>
135
+ </CardContent>
136
+ </Card>
137
+ ))}
138
+ </GridLayout>
139
+ );
140
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Built-in Dashboard Widgets
3
+ *
4
+ * Copyright (c) 2025 QwickApps.com. All rights reserved.
5
+ */
6
+
7
+ export { ServiceHealthWidget } from './ServiceHealthWidget';