@qwickapps/server 1.1.9 → 1.3.0

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 (240) hide show
  1. package/README.md +318 -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 +99 -60
  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 +683 -315
  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 +271 -0
  15. package/dist/core/plugin-registry.d.ts.map +1 -0
  16. package/dist/core/plugin-registry.js +326 -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 +9 -0
  33. package/dist/plugins/auth/adapters/index.d.ts.map +1 -0
  34. package/dist/plugins/auth/adapters/index.js +9 -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/auth-plugin.d.ts +40 -0
  41. package/dist/plugins/auth/auth-plugin.d.ts.map +1 -0
  42. package/dist/plugins/auth/auth-plugin.js +255 -0
  43. package/dist/plugins/auth/auth-plugin.js.map +1 -0
  44. package/dist/plugins/auth/auth-plugin.test.d.ts +9 -0
  45. package/dist/plugins/auth/auth-plugin.test.d.ts.map +1 -0
  46. package/dist/plugins/auth/auth-plugin.test.js +147 -0
  47. package/dist/plugins/auth/auth-plugin.test.js.map +1 -0
  48. package/dist/plugins/auth/index.d.ts +12 -0
  49. package/dist/plugins/auth/index.d.ts.map +1 -0
  50. package/dist/plugins/auth/index.js +13 -0
  51. package/dist/plugins/auth/index.js.map +1 -0
  52. package/dist/plugins/auth/types.d.ts +148 -0
  53. package/dist/plugins/auth/types.d.ts.map +1 -0
  54. package/dist/plugins/auth/types.js +14 -0
  55. package/dist/plugins/auth/types.js.map +1 -0
  56. package/dist/plugins/bans/bans-plugin.d.ts +59 -0
  57. package/dist/plugins/bans/bans-plugin.d.ts.map +1 -0
  58. package/dist/plugins/bans/bans-plugin.js +428 -0
  59. package/dist/plugins/bans/bans-plugin.js.map +1 -0
  60. package/dist/plugins/bans/index.d.ts +9 -0
  61. package/dist/plugins/bans/index.d.ts.map +1 -0
  62. package/dist/plugins/bans/index.js +10 -0
  63. package/dist/plugins/bans/index.js.map +1 -0
  64. package/dist/plugins/bans/stores/index.d.ts +7 -0
  65. package/dist/plugins/bans/stores/index.d.ts.map +1 -0
  66. package/dist/plugins/bans/stores/index.js +7 -0
  67. package/dist/plugins/bans/stores/index.js.map +1 -0
  68. package/dist/plugins/bans/stores/postgres-store.d.ts +29 -0
  69. package/dist/plugins/bans/stores/postgres-store.d.ts.map +1 -0
  70. package/dist/plugins/bans/stores/postgres-store.js +132 -0
  71. package/dist/plugins/bans/stores/postgres-store.js.map +1 -0
  72. package/dist/plugins/bans/types.d.ts +128 -0
  73. package/dist/plugins/bans/types.d.ts.map +1 -0
  74. package/dist/plugins/bans/types.js +11 -0
  75. package/dist/plugins/bans/types.js.map +1 -0
  76. package/dist/plugins/cache-plugin.d.ts +14 -3
  77. package/dist/plugins/cache-plugin.d.ts.map +1 -1
  78. package/dist/plugins/cache-plugin.js +27 -7
  79. package/dist/plugins/cache-plugin.js.map +1 -1
  80. package/dist/plugins/cache-plugin.test.js +96 -32
  81. package/dist/plugins/cache-plugin.test.js.map +1 -1
  82. package/dist/plugins/config-plugin.d.ts +3 -2
  83. package/dist/plugins/config-plugin.d.ts.map +1 -1
  84. package/dist/plugins/config-plugin.js +17 -10
  85. package/dist/plugins/config-plugin.js.map +1 -1
  86. package/dist/plugins/diagnostics-plugin.d.ts +2 -2
  87. package/dist/plugins/diagnostics-plugin.d.ts.map +1 -1
  88. package/dist/plugins/diagnostics-plugin.js +17 -10
  89. package/dist/plugins/diagnostics-plugin.js.map +1 -1
  90. package/dist/plugins/entitlements/entitlements-plugin.d.ts +95 -0
  91. package/dist/plugins/entitlements/entitlements-plugin.d.ts.map +1 -0
  92. package/dist/plugins/entitlements/entitlements-plugin.js +707 -0
  93. package/dist/plugins/entitlements/entitlements-plugin.js.map +1 -0
  94. package/dist/plugins/entitlements/index.d.ts +12 -0
  95. package/dist/plugins/entitlements/index.d.ts.map +1 -0
  96. package/dist/plugins/entitlements/index.js +16 -0
  97. package/dist/plugins/entitlements/index.js.map +1 -0
  98. package/dist/plugins/entitlements/sources/index.d.ts +9 -0
  99. package/dist/plugins/entitlements/sources/index.d.ts.map +1 -0
  100. package/dist/plugins/entitlements/sources/index.js +9 -0
  101. package/dist/plugins/entitlements/sources/index.js.map +1 -0
  102. package/dist/plugins/entitlements/sources/postgres-source.d.ts +29 -0
  103. package/dist/plugins/entitlements/sources/postgres-source.d.ts.map +1 -0
  104. package/dist/plugins/entitlements/sources/postgres-source.js +169 -0
  105. package/dist/plugins/entitlements/sources/postgres-source.js.map +1 -0
  106. package/dist/plugins/entitlements/types.d.ts +232 -0
  107. package/dist/plugins/entitlements/types.d.ts.map +1 -0
  108. package/dist/plugins/entitlements/types.js +11 -0
  109. package/dist/plugins/entitlements/types.js.map +1 -0
  110. package/dist/plugins/frontend-app-plugin.d.ts +9 -3
  111. package/dist/plugins/frontend-app-plugin.d.ts.map +1 -1
  112. package/dist/plugins/frontend-app-plugin.js +14 -9
  113. package/dist/plugins/frontend-app-plugin.js.map +1 -1
  114. package/dist/plugins/health-plugin.d.ts +5 -2
  115. package/dist/plugins/health-plugin.d.ts.map +1 -1
  116. package/dist/plugins/health-plugin.js +20 -5
  117. package/dist/plugins/health-plugin.js.map +1 -1
  118. package/dist/plugins/index.d.ts +8 -2
  119. package/dist/plugins/index.d.ts.map +1 -1
  120. package/dist/plugins/index.js +8 -2
  121. package/dist/plugins/index.js.map +1 -1
  122. package/dist/plugins/logs-plugin.d.ts +3 -2
  123. package/dist/plugins/logs-plugin.d.ts.map +1 -1
  124. package/dist/plugins/logs-plugin.js +21 -12
  125. package/dist/plugins/logs-plugin.js.map +1 -1
  126. package/dist/plugins/postgres-plugin.d.ts +3 -3
  127. package/dist/plugins/postgres-plugin.d.ts.map +1 -1
  128. package/dist/plugins/postgres-plugin.js +9 -7
  129. package/dist/plugins/postgres-plugin.js.map +1 -1
  130. package/dist/plugins/postgres-plugin.test.js +47 -29
  131. package/dist/plugins/postgres-plugin.test.js.map +1 -1
  132. package/dist/plugins/users/index.d.ts +12 -0
  133. package/dist/plugins/users/index.d.ts.map +1 -0
  134. package/dist/plugins/users/index.js +13 -0
  135. package/dist/plugins/users/index.js.map +1 -0
  136. package/dist/plugins/users/stores/index.d.ts +7 -0
  137. package/dist/plugins/users/stores/index.d.ts.map +1 -0
  138. package/dist/plugins/users/stores/index.js +7 -0
  139. package/dist/plugins/users/stores/index.js.map +1 -0
  140. package/dist/plugins/users/stores/postgres-store.d.ts +28 -0
  141. package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -0
  142. package/dist/plugins/users/stores/postgres-store.js +157 -0
  143. package/dist/plugins/users/stores/postgres-store.js.map +1 -0
  144. package/dist/plugins/users/types.d.ts +189 -0
  145. package/dist/plugins/users/types.d.ts.map +1 -0
  146. package/dist/plugins/users/types.js +12 -0
  147. package/dist/plugins/users/types.js.map +1 -0
  148. package/dist/plugins/users/users-plugin.d.ts +39 -0
  149. package/dist/plugins/users/users-plugin.d.ts.map +1 -0
  150. package/dist/plugins/users/users-plugin.js +242 -0
  151. package/dist/plugins/users/users-plugin.js.map +1 -0
  152. package/dist-ui/assets/index-Bsp2ntcw.js +465 -0
  153. package/dist-ui/assets/index-Bsp2ntcw.js.map +1 -0
  154. package/dist-ui/index.html +1 -1
  155. package/dist-ui-lib/api/controlPanelApi.d.ts +232 -0
  156. package/dist-ui-lib/components/ControlPanelApp.d.ts +61 -0
  157. package/dist-ui-lib/components/index.d.ts +18 -0
  158. package/dist-ui-lib/config/AppConfig.d.ts +7 -0
  159. package/dist-ui-lib/dashboard/DashboardWidgetRegistry.d.ts +62 -0
  160. package/dist-ui-lib/dashboard/DashboardWidgetRenderer.d.ts +8 -0
  161. package/dist-ui-lib/dashboard/PluginWidgetRenderer.d.ts +19 -0
  162. package/dist-ui-lib/dashboard/WidgetComponentRegistry.d.ts +44 -0
  163. package/dist-ui-lib/dashboard/builtInWidgets.d.ts +19 -0
  164. package/dist-ui-lib/dashboard/index.d.ts +13 -0
  165. package/dist-ui-lib/dashboard/widgets/ServiceHealthWidget.d.ts +12 -0
  166. package/dist-ui-lib/dashboard/widgets/index.d.ts +6 -0
  167. package/dist-ui-lib/index.js +6441 -0
  168. package/dist-ui-lib/index.js.map +1 -0
  169. package/dist-ui-lib/pages/ConfigPage.d.ts +1 -0
  170. package/dist-ui-lib/pages/DashboardPage.d.ts +1 -0
  171. package/dist-ui-lib/pages/DiagnosticsPage.d.ts +1 -0
  172. package/dist-ui-lib/pages/EntitlementsPage.d.ts +17 -0
  173. package/dist-ui-lib/pages/LogsPage.d.ts +1 -0
  174. package/dist-ui-lib/pages/NotFoundPage.d.ts +1 -0
  175. package/dist-ui-lib/pages/PluginPage.d.ts +15 -0
  176. package/dist-ui-lib/pages/SystemPage.d.ts +1 -0
  177. package/dist-ui-lib/pages/UsersPage.d.ts +22 -0
  178. package/package.json +18 -6
  179. package/src/core/control-panel.ts +122 -68
  180. package/src/core/gateway.ts +870 -399
  181. package/src/core/index.ts +21 -2
  182. package/src/core/plugin-registry.ts +653 -0
  183. package/src/core/types.ts +31 -37
  184. package/src/index.ts +118 -19
  185. package/src/plugins/auth/adapters/auth0-adapter.ts +214 -0
  186. package/src/plugins/auth/adapters/basic-adapter.ts +61 -0
  187. package/src/plugins/auth/adapters/index.ts +9 -0
  188. package/src/plugins/auth/adapters/supabase-adapter.ts +141 -0
  189. package/src/plugins/auth/auth-plugin.test.ts +176 -0
  190. package/src/plugins/auth/auth-plugin.ts +303 -0
  191. package/src/plugins/auth/index.ts +33 -0
  192. package/src/plugins/auth/types.ts +165 -0
  193. package/src/plugins/bans/bans-plugin.ts +485 -0
  194. package/src/plugins/bans/index.ts +31 -0
  195. package/src/plugins/bans/stores/index.ts +7 -0
  196. package/src/plugins/bans/stores/postgres-store.ts +195 -0
  197. package/src/plugins/bans/types.ts +141 -0
  198. package/src/plugins/cache-plugin.test.ts +105 -32
  199. package/src/plugins/cache-plugin.ts +40 -9
  200. package/src/plugins/config-plugin.ts +23 -12
  201. package/src/plugins/diagnostics-plugin.ts +22 -12
  202. package/src/plugins/entitlements/entitlements-plugin.ts +820 -0
  203. package/src/plugins/entitlements/index.ts +51 -0
  204. package/src/plugins/entitlements/sources/index.ts +9 -0
  205. package/src/plugins/entitlements/sources/postgres-source.ts +253 -0
  206. package/src/plugins/entitlements/types.ts +256 -0
  207. package/src/plugins/frontend-app-plugin.ts +24 -12
  208. package/src/plugins/health-plugin.ts +27 -7
  209. package/src/plugins/index.ts +106 -4
  210. package/src/plugins/logs-plugin.ts +28 -14
  211. package/src/plugins/postgres-plugin.test.ts +49 -29
  212. package/src/plugins/postgres-plugin.ts +11 -9
  213. package/src/plugins/users/index.ts +35 -0
  214. package/src/plugins/users/stores/index.ts +7 -0
  215. package/src/plugins/users/stores/postgres-store.ts +225 -0
  216. package/src/plugins/users/types.ts +209 -0
  217. package/src/plugins/users/users-plugin.ts +281 -0
  218. package/ui/src/App.tsx +185 -31
  219. package/ui/src/api/controlPanelApi.ts +354 -1
  220. package/ui/src/components/ControlPanelApp.tsx +209 -0
  221. package/ui/src/components/index.ts +62 -0
  222. package/ui/src/dashboard/DashboardWidgetRegistry.tsx +129 -0
  223. package/ui/src/dashboard/DashboardWidgetRenderer.tsx +34 -0
  224. package/ui/src/dashboard/PluginWidgetRenderer.tsx +115 -0
  225. package/ui/src/dashboard/WidgetComponentRegistry.tsx +116 -0
  226. package/ui/src/dashboard/builtInWidgets.tsx +29 -0
  227. package/ui/src/dashboard/index.ts +35 -0
  228. package/ui/src/dashboard/widgets/ServiceHealthWidget.tsx +140 -0
  229. package/ui/src/dashboard/widgets/index.ts +7 -0
  230. package/ui/src/pages/DashboardPage.tsx +28 -149
  231. package/ui/src/pages/EntitlementsPage.tsx +557 -0
  232. package/ui/src/pages/LogsPage.tsx +174 -8
  233. package/ui/src/pages/PluginPage.tsx +148 -0
  234. package/ui/src/pages/SystemPage.tsx +445 -0
  235. package/ui/src/pages/UsersPage.tsx +837 -0
  236. package/ui/tsconfig.lib.json +11 -0
  237. package/ui/vite.lib.config.ts +51 -0
  238. package/dist-ui/assets/index-CW1BviRn.js +0 -465
  239. package/dist-ui/assets/index-CW1BviRn.js.map +0 -1
  240. package/ui/src/pages/HealthPage.tsx +0 -204
@@ -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';
@@ -1,38 +1,19 @@
1
1
  import { useState, useEffect } from 'react';
2
+ import { useNavigate } from 'react-router-dom';
2
3
  import {
3
4
  Box,
4
5
  Card,
5
6
  CardContent,
7
+ CardActionArea,
6
8
  Typography,
7
- Grid,
8
9
  CircularProgress,
9
10
  Chip,
10
11
  } from '@mui/material';
11
12
  import CheckCircleIcon from '@mui/icons-material/CheckCircle';
12
13
  import ErrorIcon from '@mui/icons-material/Error';
13
14
  import WarningIcon from '@mui/icons-material/Warning';
14
- import AccessTimeIcon from '@mui/icons-material/AccessTime';
15
- import MemoryIcon from '@mui/icons-material/Memory';
16
- import StorageIcon from '@mui/icons-material/Storage';
17
15
  import { api, HealthResponse, InfoResponse } from '../api/controlPanelApi';
18
-
19
- function formatUptime(ms: number): string {
20
- const seconds = Math.floor(ms / 1000);
21
- const minutes = Math.floor(seconds / 60);
22
- const hours = Math.floor(minutes / 60);
23
- const days = Math.floor(hours / 24);
24
-
25
- if (days > 0) {
26
- return `${days}d ${hours % 24}h ${minutes % 60}m`;
27
- }
28
- if (hours > 0) {
29
- return `${hours}h ${minutes % 60}m`;
30
- }
31
- if (minutes > 0) {
32
- return `${minutes}m ${seconds % 60}s`;
33
- }
34
- return `${seconds}s`;
35
- }
16
+ import { DashboardWidgetRenderer, PluginWidgetRenderer } from '../dashboard';
36
17
 
37
18
  function getStatusIcon(status: string) {
38
19
  switch (status) {
@@ -61,6 +42,7 @@ function getStatusColor(status: string): string {
61
42
  }
62
43
 
63
44
  export function DashboardPage() {
45
+ const navigate = useNavigate();
64
46
  const [health, setHealth] = useState<HealthResponse | null>(null);
65
47
  const [info, setInfo] = useState<InfoResponse | null>(null);
66
48
  const [loading, setLoading] = useState(true);
@@ -119,7 +101,7 @@ export function DashboardPage() {
119
101
  Real-time overview of {info?.product || 'your service'}
120
102
  </Typography>
121
103
 
122
- {/* Status Banner */}
104
+ {/* Service Status Banner - clickable to navigate to Health page */}
123
105
  <Card
124
106
  sx={{
125
107
  mb: 4,
@@ -127,138 +109,35 @@ export function DashboardPage() {
127
109
  border: `2px solid ${getStatusColor(health?.status || 'unknown')}`,
128
110
  }}
129
111
  >
130
- <CardContent sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
131
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
132
- {getStatusIcon(health?.status || 'unknown')}
133
- <Box>
134
- <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)' }}>
135
- Service Status: {health?.status?.charAt(0).toUpperCase()}{health?.status?.slice(1)}
136
- </Typography>
137
- <Typography variant="body2" sx={{ color: 'var(--theme-text-secondary)' }}>
138
- Last updated: {health?.timestamp ? new Date(health.timestamp).toLocaleString() : 'N/A'}
139
- </Typography>
140
- </Box>
141
- </Box>
142
- <Chip
143
- label={`${healthyCount}/${totalCount} checks passing`}
144
- sx={{
145
- bgcolor: getStatusColor(health?.status || 'unknown') + '20',
146
- color: getStatusColor(health?.status || 'unknown'),
147
- }}
148
- />
149
- </CardContent>
150
- </Card>
151
-
152
- {/* Stats Grid */}
153
- <Grid container spacing={3} sx={{ mb: 4 }}>
154
- <Grid size={{ xs: 12, sm: 6, md: 3 }}>
155
- <Card sx={{ bgcolor: 'var(--theme-surface)', height: '100%' }}>
156
- <CardContent>
157
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 2 }}>
158
- <AccessTimeIcon sx={{ color: 'var(--theme-primary)' }} />
159
- <Typography variant="body2" sx={{ color: 'var(--theme-text-secondary)' }}>
160
- Uptime
112
+ <CardActionArea onClick={() => navigate('/health')}>
113
+ <CardContent sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
114
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
115
+ {getStatusIcon(health?.status || 'unknown')}
116
+ <Box>
117
+ <Typography variant="h6" sx={{ color: 'var(--theme-text-primary)' }}>
118
+ Service Status: {health?.status?.charAt(0).toUpperCase()}{health?.status?.slice(1)}
161
119
  </Typography>
162
- </Box>
163
- <Typography variant="h4" sx={{ color: 'var(--theme-primary)' }}>
164
- {health ? formatUptime(health.uptime) : 'N/A'}
165
- </Typography>
166
- </CardContent>
167
- </Card>
168
- </Grid>
169
-
170
- <Grid size={{ xs: 12, sm: 6, md: 3 }}>
171
- <Card sx={{ bgcolor: 'var(--theme-surface)', height: '100%' }}>
172
- <CardContent>
173
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 2 }}>
174
- <CheckCircleIcon sx={{ color: 'var(--theme-success)' }} />
175
120
  <Typography variant="body2" sx={{ color: 'var(--theme-text-secondary)' }}>
176
- Health Checks
121
+ Click to view detailed health information
177
122
  </Typography>
178
123
  </Box>
179
- <Typography variant="h4" sx={{ color: 'var(--theme-success)' }}>
180
- {healthyCount}/{totalCount}
181
- </Typography>
182
- </CardContent>
183
- </Card>
184
- </Grid>
185
-
186
- <Grid size={{ xs: 12, sm: 6, md: 3 }}>
187
- <Card sx={{ bgcolor: 'var(--theme-surface)', height: '100%' }}>
188
- <CardContent>
189
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 2 }}>
190
- <MemoryIcon sx={{ color: 'var(--theme-warning)' }} />
191
- <Typography variant="body2" sx={{ color: 'var(--theme-text-secondary)' }}>
192
- Version
193
- </Typography>
194
- </Box>
195
- <Typography variant="h4" sx={{ color: 'var(--theme-text-primary)' }}>
196
- {info?.version || 'N/A'}
197
- </Typography>
198
- </CardContent>
199
- </Card>
200
- </Grid>
124
+ </Box>
125
+ <Chip
126
+ label={`${healthyCount}/${totalCount} checks passing`}
127
+ sx={{
128
+ bgcolor: getStatusColor(health?.status || 'unknown') + '20',
129
+ color: getStatusColor(health?.status || 'unknown'),
130
+ }}
131
+ />
132
+ </CardContent>
133
+ </CardActionArea>
134
+ </Card>
201
135
 
202
- <Grid size={{ xs: 12, sm: 6, md: 3 }}>
203
- <Card sx={{ bgcolor: 'var(--theme-surface)', height: '100%' }}>
204
- <CardContent>
205
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 2 }}>
206
- <StorageIcon sx={{ color: 'var(--theme-info)' }} />
207
- <Typography variant="body2" sx={{ color: 'var(--theme-text-secondary)' }}>
208
- Product
209
- </Typography>
210
- </Box>
211
- <Typography variant="h5" sx={{ color: 'var(--theme-text-primary)' }}>
212
- {info?.product || 'N/A'}
213
- </Typography>
214
- </CardContent>
215
- </Card>
216
- </Grid>
217
- </Grid>
136
+ {/* Plugin widgets (from server-side widget contributions) */}
137
+ <PluginWidgetRenderer />
218
138
 
219
- {/* Health Checks Detail */}
220
- <Card sx={{ bgcolor: 'var(--theme-surface)' }}>
221
- <CardContent>
222
- <Typography variant="h6" sx={{ mb: 3, color: 'var(--theme-text-primary)' }}>
223
- Health Checks
224
- </Typography>
225
- <Grid container spacing={2}>
226
- {healthChecks.map(([name, check]) => (
227
- <Grid size={{ xs: 12, sm: 6, md: 4 }} key={name}>
228
- <Box
229
- sx={{
230
- p: 2,
231
- borderRadius: 1,
232
- bgcolor: 'var(--theme-background)',
233
- border: `1px solid ${getStatusColor(check.status)}40`,
234
- }}
235
- >
236
- <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
237
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
238
- {getStatusIcon(check.status)}
239
- <Typography sx={{ color: 'var(--theme-text-primary)', fontWeight: 500 }}>
240
- {name}
241
- </Typography>
242
- </Box>
243
- {check.latency !== undefined && (
244
- <Chip
245
- label={`${check.latency}ms`}
246
- size="small"
247
- sx={{ bgcolor: 'var(--theme-surface)', color: 'var(--theme-text-secondary)' }}
248
- />
249
- )}
250
- </Box>
251
- {check.error && (
252
- <Typography variant="caption" sx={{ color: 'var(--theme-error)' }}>
253
- {check.error}
254
- </Typography>
255
- )}
256
- </Box>
257
- </Grid>
258
- ))}
259
- </Grid>
260
- </CardContent>
261
- </Card>
139
+ {/* Legacy widgets from context (for backwards compatibility) */}
140
+ <DashboardWidgetRenderer />
262
141
  </Box>
263
142
  );
264
143
  }