@qwickapps/server 1.2.0 → 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.
- package/README.md +238 -0
- package/dist/core/control-panel.d.ts +7 -2
- package/dist/core/control-panel.d.ts.map +1 -1
- package/dist/core/control-panel.js +92 -54
- package/dist/core/control-panel.js.map +1 -1
- package/dist/core/gateway.d.ts +159 -79
- package/dist/core/gateway.d.ts.map +1 -1
- package/dist/core/gateway.js +679 -319
- package/dist/core/gateway.js.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/plugin-registry.d.ts +271 -0
- package/dist/core/plugin-registry.d.ts.map +1 -0
- package/dist/core/plugin-registry.js +326 -0
- package/dist/core/plugin-registry.js.map +1 -0
- package/dist/core/types.d.ts +16 -33
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -7
- package/dist/index.js.map +1 -1
- package/dist/plugins/auth/adapters/auth0-adapter.d.ts +14 -0
- package/dist/plugins/auth/adapters/auth0-adapter.d.ts.map +1 -0
- package/dist/plugins/auth/adapters/auth0-adapter.js +179 -0
- package/dist/plugins/auth/adapters/auth0-adapter.js.map +1 -0
- package/dist/plugins/auth/adapters/basic-adapter.d.ts +13 -0
- package/dist/plugins/auth/adapters/basic-adapter.d.ts.map +1 -0
- package/dist/plugins/auth/adapters/basic-adapter.js +51 -0
- package/dist/plugins/auth/adapters/basic-adapter.js.map +1 -0
- package/dist/plugins/auth/adapters/index.d.ts +9 -0
- package/dist/plugins/auth/adapters/index.d.ts.map +1 -0
- package/dist/plugins/auth/adapters/index.js +9 -0
- package/dist/plugins/auth/adapters/index.js.map +1 -0
- package/dist/plugins/auth/adapters/supabase-adapter.d.ts +13 -0
- package/dist/plugins/auth/adapters/supabase-adapter.d.ts.map +1 -0
- package/dist/plugins/auth/adapters/supabase-adapter.js +109 -0
- package/dist/plugins/auth/adapters/supabase-adapter.js.map +1 -0
- package/dist/plugins/auth/auth-plugin.d.ts +40 -0
- package/dist/plugins/auth/auth-plugin.d.ts.map +1 -0
- package/dist/plugins/auth/auth-plugin.js +255 -0
- package/dist/plugins/auth/auth-plugin.js.map +1 -0
- package/dist/plugins/auth/auth-plugin.test.d.ts +9 -0
- package/dist/plugins/auth/auth-plugin.test.d.ts.map +1 -0
- package/dist/plugins/auth/auth-plugin.test.js +147 -0
- package/dist/plugins/auth/auth-plugin.test.js.map +1 -0
- package/dist/plugins/auth/index.d.ts +12 -0
- package/dist/plugins/auth/index.d.ts.map +1 -0
- package/dist/plugins/auth/index.js +13 -0
- package/dist/plugins/auth/index.js.map +1 -0
- package/dist/plugins/auth/types.d.ts +148 -0
- package/dist/plugins/auth/types.d.ts.map +1 -0
- package/dist/plugins/auth/types.js +14 -0
- package/dist/plugins/auth/types.js.map +1 -0
- package/dist/plugins/bans/bans-plugin.d.ts +59 -0
- package/dist/plugins/bans/bans-plugin.d.ts.map +1 -0
- package/dist/plugins/bans/bans-plugin.js +428 -0
- package/dist/plugins/bans/bans-plugin.js.map +1 -0
- package/dist/plugins/bans/index.d.ts +9 -0
- package/dist/plugins/bans/index.d.ts.map +1 -0
- package/dist/plugins/bans/index.js +10 -0
- package/dist/plugins/bans/index.js.map +1 -0
- package/dist/plugins/bans/stores/index.d.ts +7 -0
- package/dist/plugins/bans/stores/index.d.ts.map +1 -0
- package/dist/plugins/bans/stores/index.js +7 -0
- package/dist/plugins/bans/stores/index.js.map +1 -0
- package/dist/plugins/bans/stores/postgres-store.d.ts +29 -0
- package/dist/plugins/bans/stores/postgres-store.d.ts.map +1 -0
- package/dist/plugins/bans/stores/postgres-store.js +132 -0
- package/dist/plugins/bans/stores/postgres-store.js.map +1 -0
- package/dist/plugins/bans/types.d.ts +128 -0
- package/dist/plugins/bans/types.d.ts.map +1 -0
- package/dist/plugins/bans/types.js +11 -0
- package/dist/plugins/bans/types.js.map +1 -0
- package/dist/plugins/cache-plugin.d.ts +14 -3
- package/dist/plugins/cache-plugin.d.ts.map +1 -1
- package/dist/plugins/cache-plugin.js +27 -7
- package/dist/plugins/cache-plugin.js.map +1 -1
- package/dist/plugins/cache-plugin.test.js +96 -32
- package/dist/plugins/cache-plugin.test.js.map +1 -1
- package/dist/plugins/config-plugin.d.ts +3 -2
- package/dist/plugins/config-plugin.d.ts.map +1 -1
- package/dist/plugins/config-plugin.js +17 -10
- package/dist/plugins/config-plugin.js.map +1 -1
- package/dist/plugins/diagnostics-plugin.d.ts +2 -2
- package/dist/plugins/diagnostics-plugin.d.ts.map +1 -1
- package/dist/plugins/diagnostics-plugin.js +17 -10
- package/dist/plugins/diagnostics-plugin.js.map +1 -1
- package/dist/plugins/entitlements/entitlements-plugin.d.ts +95 -0
- package/dist/plugins/entitlements/entitlements-plugin.d.ts.map +1 -0
- package/dist/plugins/entitlements/entitlements-plugin.js +707 -0
- package/dist/plugins/entitlements/entitlements-plugin.js.map +1 -0
- package/dist/plugins/entitlements/index.d.ts +12 -0
- package/dist/plugins/entitlements/index.d.ts.map +1 -0
- package/dist/plugins/entitlements/index.js +16 -0
- package/dist/plugins/entitlements/index.js.map +1 -0
- package/dist/plugins/entitlements/sources/index.d.ts +9 -0
- package/dist/plugins/entitlements/sources/index.d.ts.map +1 -0
- package/dist/plugins/entitlements/sources/index.js +9 -0
- package/dist/plugins/entitlements/sources/index.js.map +1 -0
- package/dist/plugins/entitlements/sources/postgres-source.d.ts +29 -0
- package/dist/plugins/entitlements/sources/postgres-source.d.ts.map +1 -0
- package/dist/plugins/entitlements/sources/postgres-source.js +169 -0
- package/dist/plugins/entitlements/sources/postgres-source.js.map +1 -0
- package/dist/plugins/entitlements/types.d.ts +232 -0
- package/dist/plugins/entitlements/types.d.ts.map +1 -0
- package/dist/plugins/entitlements/types.js +11 -0
- package/dist/plugins/entitlements/types.js.map +1 -0
- package/dist/plugins/frontend-app-plugin.d.ts +9 -3
- package/dist/plugins/frontend-app-plugin.d.ts.map +1 -1
- package/dist/plugins/frontend-app-plugin.js +14 -9
- package/dist/plugins/frontend-app-plugin.js.map +1 -1
- package/dist/plugins/health-plugin.d.ts +5 -2
- package/dist/plugins/health-plugin.d.ts.map +1 -1
- package/dist/plugins/health-plugin.js +20 -5
- package/dist/plugins/health-plugin.js.map +1 -1
- package/dist/plugins/index.d.ts +8 -2
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +8 -2
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/logs-plugin.d.ts +3 -2
- package/dist/plugins/logs-plugin.d.ts.map +1 -1
- package/dist/plugins/logs-plugin.js +21 -12
- package/dist/plugins/logs-plugin.js.map +1 -1
- package/dist/plugins/postgres-plugin.d.ts +3 -3
- package/dist/plugins/postgres-plugin.d.ts.map +1 -1
- package/dist/plugins/postgres-plugin.js +9 -7
- package/dist/plugins/postgres-plugin.js.map +1 -1
- package/dist/plugins/postgres-plugin.test.js +47 -29
- package/dist/plugins/postgres-plugin.test.js.map +1 -1
- package/dist/plugins/users/index.d.ts +12 -0
- package/dist/plugins/users/index.d.ts.map +1 -0
- package/dist/plugins/users/index.js +13 -0
- package/dist/plugins/users/index.js.map +1 -0
- package/dist/plugins/users/stores/index.d.ts +7 -0
- package/dist/plugins/users/stores/index.d.ts.map +1 -0
- package/dist/plugins/users/stores/index.js +7 -0
- package/dist/plugins/users/stores/index.js.map +1 -0
- package/dist/plugins/users/stores/postgres-store.d.ts +28 -0
- package/dist/plugins/users/stores/postgres-store.d.ts.map +1 -0
- package/dist/plugins/users/stores/postgres-store.js +157 -0
- package/dist/plugins/users/stores/postgres-store.js.map +1 -0
- package/dist/plugins/users/types.d.ts +189 -0
- package/dist/plugins/users/types.d.ts.map +1 -0
- package/dist/plugins/users/types.js +12 -0
- package/dist/plugins/users/types.js.map +1 -0
- package/dist/plugins/users/users-plugin.d.ts +39 -0
- package/dist/plugins/users/users-plugin.d.ts.map +1 -0
- package/dist/plugins/users/users-plugin.js +242 -0
- package/dist/plugins/users/users-plugin.js.map +1 -0
- package/dist-ui/assets/index-Bsp2ntcw.js +465 -0
- package/dist-ui/assets/index-Bsp2ntcw.js.map +1 -0
- package/dist-ui/index.html +1 -1
- package/dist-ui-lib/api/controlPanelApi.d.ts +232 -0
- package/dist-ui-lib/components/ControlPanelApp.d.ts +61 -0
- package/dist-ui-lib/components/index.d.ts +18 -0
- package/dist-ui-lib/config/AppConfig.d.ts +7 -0
- package/dist-ui-lib/dashboard/DashboardWidgetRegistry.d.ts +62 -0
- package/dist-ui-lib/dashboard/DashboardWidgetRenderer.d.ts +8 -0
- package/dist-ui-lib/dashboard/PluginWidgetRenderer.d.ts +19 -0
- package/dist-ui-lib/dashboard/WidgetComponentRegistry.d.ts +44 -0
- package/dist-ui-lib/dashboard/builtInWidgets.d.ts +19 -0
- package/dist-ui-lib/dashboard/index.d.ts +13 -0
- package/dist-ui-lib/dashboard/widgets/ServiceHealthWidget.d.ts +12 -0
- package/dist-ui-lib/dashboard/widgets/index.d.ts +6 -0
- package/dist-ui-lib/index.js +6441 -0
- package/dist-ui-lib/index.js.map +1 -0
- package/dist-ui-lib/pages/ConfigPage.d.ts +1 -0
- package/dist-ui-lib/pages/DashboardPage.d.ts +1 -0
- package/dist-ui-lib/pages/DiagnosticsPage.d.ts +1 -0
- package/dist-ui-lib/pages/EntitlementsPage.d.ts +17 -0
- package/dist-ui-lib/pages/LogsPage.d.ts +1 -0
- package/dist-ui-lib/pages/NotFoundPage.d.ts +1 -0
- package/dist-ui-lib/pages/PluginPage.d.ts +15 -0
- package/dist-ui-lib/pages/SystemPage.d.ts +1 -0
- package/dist-ui-lib/pages/UsersPage.d.ts +22 -0
- package/package.json +18 -6
- package/src/core/control-panel.ts +114 -61
- package/src/core/gateway.ts +863 -403
- package/src/core/index.ts +21 -2
- package/src/core/plugin-registry.ts +653 -0
- package/src/core/types.ts +31 -37
- package/src/index.ts +118 -19
- package/src/plugins/auth/adapters/auth0-adapter.ts +214 -0
- package/src/plugins/auth/adapters/basic-adapter.ts +61 -0
- package/src/plugins/auth/adapters/index.ts +9 -0
- package/src/plugins/auth/adapters/supabase-adapter.ts +141 -0
- package/src/plugins/auth/auth-plugin.test.ts +176 -0
- package/src/plugins/auth/auth-plugin.ts +303 -0
- package/src/plugins/auth/index.ts +33 -0
- package/src/plugins/auth/types.ts +165 -0
- package/src/plugins/bans/bans-plugin.ts +485 -0
- package/src/plugins/bans/index.ts +31 -0
- package/src/plugins/bans/stores/index.ts +7 -0
- package/src/plugins/bans/stores/postgres-store.ts +195 -0
- package/src/plugins/bans/types.ts +141 -0
- package/src/plugins/cache-plugin.test.ts +105 -32
- package/src/plugins/cache-plugin.ts +40 -9
- package/src/plugins/config-plugin.ts +23 -12
- package/src/plugins/diagnostics-plugin.ts +22 -12
- package/src/plugins/entitlements/entitlements-plugin.ts +820 -0
- package/src/plugins/entitlements/index.ts +51 -0
- package/src/plugins/entitlements/sources/index.ts +9 -0
- package/src/plugins/entitlements/sources/postgres-source.ts +253 -0
- package/src/plugins/entitlements/types.ts +256 -0
- package/src/plugins/frontend-app-plugin.ts +24 -12
- package/src/plugins/health-plugin.ts +27 -7
- package/src/plugins/index.ts +106 -4
- package/src/plugins/logs-plugin.ts +28 -14
- package/src/plugins/postgres-plugin.test.ts +49 -29
- package/src/plugins/postgres-plugin.ts +11 -9
- package/src/plugins/users/index.ts +35 -0
- package/src/plugins/users/stores/index.ts +7 -0
- package/src/plugins/users/stores/postgres-store.ts +225 -0
- package/src/plugins/users/types.ts +209 -0
- package/src/plugins/users/users-plugin.ts +281 -0
- package/ui/src/App.tsx +185 -31
- package/ui/src/api/controlPanelApi.ts +354 -1
- package/ui/src/components/ControlPanelApp.tsx +209 -0
- package/ui/src/components/index.ts +62 -0
- package/ui/src/dashboard/DashboardWidgetRegistry.tsx +129 -0
- package/ui/src/dashboard/DashboardWidgetRenderer.tsx +34 -0
- package/ui/src/dashboard/PluginWidgetRenderer.tsx +115 -0
- package/ui/src/dashboard/WidgetComponentRegistry.tsx +116 -0
- package/ui/src/dashboard/builtInWidgets.tsx +29 -0
- package/ui/src/dashboard/index.ts +35 -0
- package/ui/src/dashboard/widgets/ServiceHealthWidget.tsx +140 -0
- package/ui/src/dashboard/widgets/index.ts +7 -0
- package/ui/src/pages/DashboardPage.tsx +28 -149
- package/ui/src/pages/EntitlementsPage.tsx +557 -0
- package/ui/src/pages/LogsPage.tsx +174 -8
- package/ui/src/pages/PluginPage.tsx +148 -0
- package/ui/src/pages/SystemPage.tsx +445 -0
- package/ui/src/pages/UsersPage.tsx +837 -0
- package/ui/tsconfig.lib.json +11 -0
- package/ui/vite.lib.config.ts +51 -0
- package/dist-ui/assets/index-CW1BviRn.js +0 -465
- package/dist-ui/assets/index-CW1BviRn.js.map +0 -1
- package/ui/src/pages/HealthPage.tsx +0 -204
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
1
|
+
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
4
|
Card,
|
|
@@ -19,9 +19,21 @@ import {
|
|
|
19
19
|
MenuItem,
|
|
20
20
|
IconButton,
|
|
21
21
|
Pagination,
|
|
22
|
+
Tooltip,
|
|
23
|
+
Grid,
|
|
24
|
+
ToggleButton,
|
|
25
|
+
ToggleButtonGroup,
|
|
22
26
|
} from '@mui/material';
|
|
23
27
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
|
24
28
|
import SearchIcon from '@mui/icons-material/Search';
|
|
29
|
+
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
|
30
|
+
import PauseIcon from '@mui/icons-material/Pause';
|
|
31
|
+
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
|
|
32
|
+
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
|
33
|
+
import ErrorIcon from '@mui/icons-material/Error';
|
|
34
|
+
import WarningIcon from '@mui/icons-material/Warning';
|
|
35
|
+
import InfoIcon from '@mui/icons-material/Info';
|
|
36
|
+
import BugReportIcon from '@mui/icons-material/BugReport';
|
|
25
37
|
import { api, LogEntry, LogSource } from '../api/controlPanelApi';
|
|
26
38
|
|
|
27
39
|
function getLevelColor(level: string): string {
|
|
@@ -54,7 +66,21 @@ export function LogsPage() {
|
|
|
54
66
|
const [total, setTotal] = useState(0);
|
|
55
67
|
const limit = 50;
|
|
56
68
|
|
|
57
|
-
|
|
69
|
+
// New features
|
|
70
|
+
const [autoRefresh, setAutoRefresh] = useState(false);
|
|
71
|
+
const [sortOrder, setSortOrder] = useState<'desc' | 'asc'>('desc'); // desc = newest first
|
|
72
|
+
const autoRefreshRef = useRef<NodeJS.Timeout | null>(null);
|
|
73
|
+
|
|
74
|
+
// Stats - computed from current logs (could be fetched from API if available)
|
|
75
|
+
const stats = {
|
|
76
|
+
total: total,
|
|
77
|
+
errors: logs.filter(l => l.level.toLowerCase() === 'error').length,
|
|
78
|
+
warnings: logs.filter(l => ['warn', 'warning'].includes(l.level.toLowerCase())).length,
|
|
79
|
+
info: logs.filter(l => l.level.toLowerCase() === 'info').length,
|
|
80
|
+
debug: logs.filter(l => l.level.toLowerCase() === 'debug').length,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const fetchLogs = useCallback(async () => {
|
|
58
84
|
setLoading(true);
|
|
59
85
|
try {
|
|
60
86
|
const data = await api.getLogs({
|
|
@@ -64,7 +90,13 @@ export function LogsPage() {
|
|
|
64
90
|
limit,
|
|
65
91
|
page,
|
|
66
92
|
});
|
|
67
|
-
|
|
93
|
+
// Sort logs based on sortOrder
|
|
94
|
+
const sortedLogs = [...data.logs].sort((a, b) => {
|
|
95
|
+
const dateA = new Date(a.timestamp).getTime();
|
|
96
|
+
const dateB = new Date(b.timestamp).getTime();
|
|
97
|
+
return sortOrder === 'desc' ? dateB - dateA : dateA - dateB;
|
|
98
|
+
});
|
|
99
|
+
setLogs(sortedLogs);
|
|
68
100
|
setTotal(data.total);
|
|
69
101
|
setError(null);
|
|
70
102
|
} catch (err) {
|
|
@@ -72,7 +104,7 @@ export function LogsPage() {
|
|
|
72
104
|
} finally {
|
|
73
105
|
setLoading(false);
|
|
74
106
|
}
|
|
75
|
-
};
|
|
107
|
+
}, [selectedSource, selectedLevel, searchQuery, page, sortOrder]);
|
|
76
108
|
|
|
77
109
|
const fetchSources = async () => {
|
|
78
110
|
try {
|
|
@@ -89,13 +121,34 @@ export function LogsPage() {
|
|
|
89
121
|
|
|
90
122
|
useEffect(() => {
|
|
91
123
|
fetchLogs();
|
|
92
|
-
}, [
|
|
124
|
+
}, [fetchLogs]);
|
|
125
|
+
|
|
126
|
+
// Auto-refresh effect
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (autoRefresh) {
|
|
129
|
+
autoRefreshRef.current = setInterval(fetchLogs, 5000);
|
|
130
|
+
} else if (autoRefreshRef.current) {
|
|
131
|
+
clearInterval(autoRefreshRef.current);
|
|
132
|
+
autoRefreshRef.current = null;
|
|
133
|
+
}
|
|
134
|
+
return () => {
|
|
135
|
+
if (autoRefreshRef.current) {
|
|
136
|
+
clearInterval(autoRefreshRef.current);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}, [autoRefresh, fetchLogs]);
|
|
93
140
|
|
|
94
141
|
const handleSearch = () => {
|
|
95
142
|
setPage(1);
|
|
96
143
|
fetchLogs();
|
|
97
144
|
};
|
|
98
145
|
|
|
146
|
+
const handleSortOrderChange = (_event: React.MouseEvent<HTMLElement>, newOrder: 'desc' | 'asc' | null) => {
|
|
147
|
+
if (newOrder !== null) {
|
|
148
|
+
setSortOrder(newOrder);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
99
152
|
const totalPages = Math.ceil(total / limit);
|
|
100
153
|
|
|
101
154
|
return (
|
|
@@ -107,6 +160,84 @@ export function LogsPage() {
|
|
|
107
160
|
View and search application logs
|
|
108
161
|
</Typography>
|
|
109
162
|
|
|
163
|
+
{/* Stats Widgets */}
|
|
164
|
+
<Grid container spacing={2} sx={{ mb: 3 }}>
|
|
165
|
+
<Grid size={{ xs: 6, sm: 3, md: 2.4 }}>
|
|
166
|
+
<Card sx={{ bgcolor: 'var(--theme-surface)' }}>
|
|
167
|
+
<CardContent sx={{ py: 1.5, px: 2, '&:last-child': { pb: 1.5 } }}>
|
|
168
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
169
|
+
<Typography variant="h5" sx={{ color: 'var(--theme-text-primary)', fontWeight: 600 }}>
|
|
170
|
+
{stats.total.toLocaleString()}
|
|
171
|
+
</Typography>
|
|
172
|
+
</Box>
|
|
173
|
+
<Typography variant="caption" sx={{ color: 'var(--theme-text-secondary)' }}>
|
|
174
|
+
Total Logs
|
|
175
|
+
</Typography>
|
|
176
|
+
</CardContent>
|
|
177
|
+
</Card>
|
|
178
|
+
</Grid>
|
|
179
|
+
<Grid size={{ xs: 6, sm: 3, md: 2.4 }}>
|
|
180
|
+
<Card sx={{ bgcolor: 'var(--theme-surface)' }}>
|
|
181
|
+
<CardContent sx={{ py: 1.5, px: 2, '&:last-child': { pb: 1.5 } }}>
|
|
182
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
183
|
+
<ErrorIcon sx={{ color: 'var(--theme-error)', fontSize: 20 }} />
|
|
184
|
+
<Typography variant="h5" sx={{ color: 'var(--theme-error)', fontWeight: 600 }}>
|
|
185
|
+
{stats.errors}
|
|
186
|
+
</Typography>
|
|
187
|
+
</Box>
|
|
188
|
+
<Typography variant="caption" sx={{ color: 'var(--theme-text-secondary)' }}>
|
|
189
|
+
Errors
|
|
190
|
+
</Typography>
|
|
191
|
+
</CardContent>
|
|
192
|
+
</Card>
|
|
193
|
+
</Grid>
|
|
194
|
+
<Grid size={{ xs: 6, sm: 3, md: 2.4 }}>
|
|
195
|
+
<Card sx={{ bgcolor: 'var(--theme-surface)' }}>
|
|
196
|
+
<CardContent sx={{ py: 1.5, px: 2, '&:last-child': { pb: 1.5 } }}>
|
|
197
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
198
|
+
<WarningIcon sx={{ color: 'var(--theme-warning)', fontSize: 20 }} />
|
|
199
|
+
<Typography variant="h5" sx={{ color: 'var(--theme-warning)', fontWeight: 600 }}>
|
|
200
|
+
{stats.warnings}
|
|
201
|
+
</Typography>
|
|
202
|
+
</Box>
|
|
203
|
+
<Typography variant="caption" sx={{ color: 'var(--theme-text-secondary)' }}>
|
|
204
|
+
Warnings
|
|
205
|
+
</Typography>
|
|
206
|
+
</CardContent>
|
|
207
|
+
</Card>
|
|
208
|
+
</Grid>
|
|
209
|
+
<Grid size={{ xs: 6, sm: 3, md: 2.4 }}>
|
|
210
|
+
<Card sx={{ bgcolor: 'var(--theme-surface)' }}>
|
|
211
|
+
<CardContent sx={{ py: 1.5, px: 2, '&:last-child': { pb: 1.5 } }}>
|
|
212
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
213
|
+
<InfoIcon sx={{ color: 'var(--theme-info)', fontSize: 20 }} />
|
|
214
|
+
<Typography variant="h5" sx={{ color: 'var(--theme-info)', fontWeight: 600 }}>
|
|
215
|
+
{stats.info}
|
|
216
|
+
</Typography>
|
|
217
|
+
</Box>
|
|
218
|
+
<Typography variant="caption" sx={{ color: 'var(--theme-text-secondary)' }}>
|
|
219
|
+
Info
|
|
220
|
+
</Typography>
|
|
221
|
+
</CardContent>
|
|
222
|
+
</Card>
|
|
223
|
+
</Grid>
|
|
224
|
+
<Grid size={{ xs: 6, sm: 3, md: 2.4 }}>
|
|
225
|
+
<Card sx={{ bgcolor: 'var(--theme-surface)' }}>
|
|
226
|
+
<CardContent sx={{ py: 1.5, px: 2, '&:last-child': { pb: 1.5 } }}>
|
|
227
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
228
|
+
<BugReportIcon sx={{ color: 'var(--theme-text-secondary)', fontSize: 20 }} />
|
|
229
|
+
<Typography variant="h5" sx={{ color: 'var(--theme-text-primary)', fontWeight: 600 }}>
|
|
230
|
+
{stats.debug}
|
|
231
|
+
</Typography>
|
|
232
|
+
</Box>
|
|
233
|
+
<Typography variant="caption" sx={{ color: 'var(--theme-text-secondary)' }}>
|
|
234
|
+
Debug
|
|
235
|
+
</Typography>
|
|
236
|
+
</CardContent>
|
|
237
|
+
</Card>
|
|
238
|
+
</Grid>
|
|
239
|
+
</Grid>
|
|
240
|
+
|
|
110
241
|
{/* Filters */}
|
|
111
242
|
<Card sx={{ mb: 3, bgcolor: 'var(--theme-surface)' }}>
|
|
112
243
|
<CardContent>
|
|
@@ -162,9 +293,44 @@ export function LogsPage() {
|
|
|
162
293
|
}}
|
|
163
294
|
/>
|
|
164
295
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
296
|
+
{/* Sort Order Toggle */}
|
|
297
|
+
<ToggleButtonGroup
|
|
298
|
+
value={sortOrder}
|
|
299
|
+
exclusive
|
|
300
|
+
onChange={handleSortOrderChange}
|
|
301
|
+
size="small"
|
|
302
|
+
aria-label="sort order"
|
|
303
|
+
>
|
|
304
|
+
<ToggleButton value="desc" aria-label="newest first">
|
|
305
|
+
<Tooltip title="Newest First">
|
|
306
|
+
<ArrowDownwardIcon fontSize="small" />
|
|
307
|
+
</Tooltip>
|
|
308
|
+
</ToggleButton>
|
|
309
|
+
<ToggleButton value="asc" aria-label="oldest first">
|
|
310
|
+
<Tooltip title="Oldest First">
|
|
311
|
+
<ArrowUpwardIcon fontSize="small" />
|
|
312
|
+
</Tooltip>
|
|
313
|
+
</ToggleButton>
|
|
314
|
+
</ToggleButtonGroup>
|
|
315
|
+
|
|
316
|
+
{/* Auto Refresh Toggle */}
|
|
317
|
+
<Tooltip title={autoRefresh ? 'Pause auto-refresh' : 'Enable auto-refresh (5s)'}>
|
|
318
|
+
<IconButton
|
|
319
|
+
onClick={() => setAutoRefresh(!autoRefresh)}
|
|
320
|
+
sx={{
|
|
321
|
+
color: autoRefresh ? 'var(--theme-success)' : 'var(--theme-text-secondary)',
|
|
322
|
+
bgcolor: autoRefresh ? 'var(--theme-success)20' : 'transparent',
|
|
323
|
+
}}
|
|
324
|
+
>
|
|
325
|
+
{autoRefresh ? <PauseIcon /> : <PlayArrowIcon />}
|
|
326
|
+
</IconButton>
|
|
327
|
+
</Tooltip>
|
|
328
|
+
|
|
329
|
+
<Tooltip title="Refresh">
|
|
330
|
+
<IconButton onClick={fetchLogs} sx={{ color: 'var(--theme-primary)' }}>
|
|
331
|
+
<RefreshIcon />
|
|
332
|
+
</IconButton>
|
|
333
|
+
</Tooltip>
|
|
168
334
|
</Box>
|
|
169
335
|
</CardContent>
|
|
170
336
|
</Card>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PluginPage Component
|
|
3
|
+
*
|
|
4
|
+
* A generic page component for plugin-contributed routes.
|
|
5
|
+
* Fetches and displays plugin-specific content from the API.
|
|
6
|
+
*
|
|
7
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useState, useEffect } from 'react';
|
|
11
|
+
import {
|
|
12
|
+
Box,
|
|
13
|
+
Typography,
|
|
14
|
+
Card,
|
|
15
|
+
CardContent,
|
|
16
|
+
CircularProgress,
|
|
17
|
+
Alert,
|
|
18
|
+
} from '@mui/material';
|
|
19
|
+
import { api } from '../api/controlPanelApi';
|
|
20
|
+
|
|
21
|
+
interface PluginPageProps {
|
|
22
|
+
pluginId: string;
|
|
23
|
+
title: string;
|
|
24
|
+
route: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface PluginPageData {
|
|
28
|
+
content?: string;
|
|
29
|
+
cards?: Array<{
|
|
30
|
+
title: string;
|
|
31
|
+
value: string | number;
|
|
32
|
+
subtitle?: string;
|
|
33
|
+
color?: string;
|
|
34
|
+
}>;
|
|
35
|
+
sections?: Array<{
|
|
36
|
+
title: string;
|
|
37
|
+
content: string;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function PluginPage({ pluginId, title, route }: PluginPageProps) {
|
|
42
|
+
const [loading, setLoading] = useState(true);
|
|
43
|
+
const [error, setError] = useState<string | null>(null);
|
|
44
|
+
const [pageData, setPageData] = useState<PluginPageData | null>(null);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const fetchPageData = async () => {
|
|
48
|
+
try {
|
|
49
|
+
// Try to fetch plugin-specific page data
|
|
50
|
+
const response = await fetch(`${api['baseUrl']}/api${route}`);
|
|
51
|
+
if (response.ok) {
|
|
52
|
+
const data = await response.json();
|
|
53
|
+
setPageData(data);
|
|
54
|
+
setError(null);
|
|
55
|
+
} else if (response.status === 404) {
|
|
56
|
+
// No dedicated page endpoint, show placeholder
|
|
57
|
+
setPageData(null);
|
|
58
|
+
setError(null);
|
|
59
|
+
} else {
|
|
60
|
+
setError(`Failed to load page: ${response.statusText}`);
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
setError(err instanceof Error ? err.message : 'Failed to load page');
|
|
64
|
+
} finally {
|
|
65
|
+
setLoading(false);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
fetchPageData();
|
|
70
|
+
}, [route]);
|
|
71
|
+
|
|
72
|
+
if (loading) {
|
|
73
|
+
return (
|
|
74
|
+
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '50vh' }}>
|
|
75
|
+
<CircularProgress />
|
|
76
|
+
</Box>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<Box>
|
|
82
|
+
<Typography variant="h4" sx={{ mb: 1, color: 'var(--theme-text-primary)' }}>
|
|
83
|
+
{title}
|
|
84
|
+
</Typography>
|
|
85
|
+
<Typography variant="body2" sx={{ mb: 4, color: 'var(--theme-text-secondary)' }}>
|
|
86
|
+
Plugin: {pluginId}
|
|
87
|
+
</Typography>
|
|
88
|
+
|
|
89
|
+
{error && (
|
|
90
|
+
<Alert severity="error" sx={{ mb: 3 }}>
|
|
91
|
+
{error}
|
|
92
|
+
</Alert>
|
|
93
|
+
)}
|
|
94
|
+
|
|
95
|
+
{pageData?.cards && pageData.cards.length > 0 && (
|
|
96
|
+
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 2, mb: 4 }}>
|
|
97
|
+
{pageData.cards.map((card, index) => (
|
|
98
|
+
<Card key={index} sx={{ bgcolor: 'var(--theme-surface)' }}>
|
|
99
|
+
<CardContent>
|
|
100
|
+
<Typography variant="h4" sx={{ color: card.color || 'var(--theme-text-primary)' }}>
|
|
101
|
+
{card.value}
|
|
102
|
+
</Typography>
|
|
103
|
+
<Typography variant="body2" sx={{ color: 'var(--theme-text-secondary)' }}>
|
|
104
|
+
{card.title}
|
|
105
|
+
</Typography>
|
|
106
|
+
{card.subtitle && (
|
|
107
|
+
<Typography variant="caption" sx={{ color: 'var(--theme-text-secondary)' }}>
|
|
108
|
+
{card.subtitle}
|
|
109
|
+
</Typography>
|
|
110
|
+
)}
|
|
111
|
+
</CardContent>
|
|
112
|
+
</Card>
|
|
113
|
+
))}
|
|
114
|
+
</Box>
|
|
115
|
+
)}
|
|
116
|
+
|
|
117
|
+
{pageData?.sections && pageData.sections.length > 0 && (
|
|
118
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
|
119
|
+
{pageData.sections.map((section, index) => (
|
|
120
|
+
<Card key={index} sx={{ bgcolor: 'var(--theme-surface)' }}>
|
|
121
|
+
<CardContent>
|
|
122
|
+
<Typography variant="h6" sx={{ mb: 2, color: 'var(--theme-text-primary)' }}>
|
|
123
|
+
{section.title}
|
|
124
|
+
</Typography>
|
|
125
|
+
<Typography variant="body2" sx={{ color: 'var(--theme-text-secondary)', whiteSpace: 'pre-wrap' }}>
|
|
126
|
+
{section.content}
|
|
127
|
+
</Typography>
|
|
128
|
+
</CardContent>
|
|
129
|
+
</Card>
|
|
130
|
+
))}
|
|
131
|
+
</Box>
|
|
132
|
+
)}
|
|
133
|
+
|
|
134
|
+
{!pageData && !error && (
|
|
135
|
+
<Card sx={{ bgcolor: 'var(--theme-surface)' }}>
|
|
136
|
+
<CardContent>
|
|
137
|
+
<Typography variant="body1" sx={{ color: 'var(--theme-text-secondary)' }}>
|
|
138
|
+
This plugin page is available. Configure the plugin to add content here.
|
|
139
|
+
</Typography>
|
|
140
|
+
<Typography variant="body2" sx={{ mt: 2, color: 'var(--theme-text-secondary)' }}>
|
|
141
|
+
API endpoint: <code>/api{route}</code>
|
|
142
|
+
</Typography>
|
|
143
|
+
</CardContent>
|
|
144
|
+
</Card>
|
|
145
|
+
)}
|
|
146
|
+
</Box>
|
|
147
|
+
);
|
|
148
|
+
}
|