diff --git a/README.md b/README.md index 341a35a..e313213 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - Business logic: `TaskService` + models in `backend/tasks.py` - Frontend: React 18, Vite, FluentUI components, feature-first structure under `frontend/src/features` - Tests: Playwright E2E (`tests/e2e/app.spec.js`) - +test ## Documentation All deep-dive guides now live under `docs/` for easier discovery: diff --git a/frontend/package.json b/frontend/package.json index f5ef78e..08c02c4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,8 @@ "@fluentui/react-icons": "^2.0.239", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^7.9.6" + "react-router-dom": "^7.9.6", + "recharts": "^3.5.0" }, "devDependencies": { "@playwright/test": "^1.42.1", @@ -24,4 +25,4 @@ "@vitejs/plugin-react": "^4.2.1", "vite": "^5.1.0" } -} \ No newline at end of file +} diff --git a/frontend/src/features/dashboard/Dashboard.jsx b/frontend/src/features/dashboard/Dashboard.jsx index 427567d..bc2eceb 100644 --- a/frontend/src/features/dashboard/Dashboard.jsx +++ b/frontend/src/features/dashboard/Dashboard.jsx @@ -2,7 +2,7 @@ * Dashboard Component * * Displays real-time server information using Server-Sent Events - * Demonstrates FluentUI Card and Text components + * Demonstrates FluentUI Card and Text components with colorful metrics and charts */ import { useEffect, useState } from 'react' @@ -13,9 +13,37 @@ import { makeStyles, tokens, Spinner, + Badge, } from '@fluentui/react-components' -import { Clock24Regular, CalendarLtr24Regular } from '@fluentui/react-icons' +import { + Clock24Regular, + CalendarLtr24Regular, + ChartMultiple24Regular, + DataUsage24Regular, + People24Regular, + Checkmark24Regular, + ArrowUp24Regular, + Server24Regular, +} from '@fluentui/react-icons' import { connectToTimeStream, getCurrentDate } from '../../services/api' +import { + LineChart, + Line, + AreaChart, + Area, + BarChart, + Bar, + PieChart, + Pie, + Cell, + ResponsiveContainer, + XAxis, + YAxis, + Tooltip, + Legend, + RadialBarChart, + RadialBar, +} from 'recharts' const useStyles = makeStyles({ dashboard: { @@ -27,6 +55,31 @@ const useStyles = makeStyles({ card: { padding: tokens.spacingVerticalL, }, + cardSuccess: { + padding: tokens.spacingVerticalL, + backgroundColor: tokens.colorPaletteGreenBackground2, + borderLeft: `4px solid ${tokens.colorPaletteGreenBorder2}`, + }, + cardWarning: { + padding: tokens.spacingVerticalL, + backgroundColor: tokens.colorPaletteYellowBackground2, + borderLeft: `4px solid ${tokens.colorPaletteYellowBorder2}`, + }, + cardInfo: { + padding: tokens.spacingVerticalL, + backgroundColor: tokens.colorPaletteBlueBorder1, + borderLeft: `4px solid ${tokens.colorPaletteBlueBorder2}`, + }, + cardPurple: { + padding: tokens.spacingVerticalL, + backgroundColor: tokens.colorPalettePurpleBackground2, + borderLeft: `4px solid ${tokens.colorPalettePurpleBorder2}`, + }, + cardRed: { + padding: tokens.spacingVerticalL, + backgroundColor: tokens.colorPaletteRedBackground2, + borderLeft: `4px solid ${tokens.colorPaletteRedBorder2}`, + }, timeDisplay: { fontSize: '48px', fontWeight: 'bold', @@ -37,6 +90,15 @@ const useStyles = makeStyles({ fontSize: '24px', color: tokens.colorNeutralForeground2, }, + metricValue: { + fontSize: '36px', + fontWeight: 'bold', + color: tokens.colorBrandForeground1, + }, + metricChange: { + fontSize: '14px', + fontWeight: 'semibold', + }, label: { fontSize: '14px', color: tokens.colorNeutralForeground3, @@ -47,8 +109,48 @@ const useStyles = makeStyles({ flexDirection: 'column', gap: tokens.spacingVerticalM, }, + chartContainer: { + height: '200px', + marginTop: tokens.spacingVerticalM, + }, + metricRow: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, }) +// Sample data generators +const generateWeeklyData = () => { + const days = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'] + return days.map((day) => ({ + name: day, + wert: Math.floor(Math.random() * 100) + 50, + ziel: 80, + })) +} + +const generateMonthlyData = () => { + const months = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun'] + return months.map((month) => ({ + name: month, + umsatz: Math.floor(Math.random() * 5000) + 3000, + kosten: Math.floor(Math.random() * 3000) + 1500, + })) +} + +const generateCategoryData = () => [ + { name: 'Frontend', value: 35, color: '#0078d4' }, + { name: 'Backend', value: 28, color: '#50e6ff' }, + { name: 'DevOps', value: 20, color: '#00b7c3' }, + { name: 'Design', value: 17, color: '#8764b8' }, +] + +const generatePerformanceData = () => [ + { name: 'Performance', value: 85, fill: '#10b981' }, + { name: 'Verbleibend', value: 15, fill: '#e5e7eb' }, +] + export default function Dashboard() { const styles = useStyles() const [liveTime, setLiveTime] = useState(null) @@ -56,6 +158,22 @@ export default function Dashboard() { const [isConnected, setIsConnected] = useState(false) const [error, setError] = useState(null) + // Fictional metrics state + const [weeklyData] = useState(generateWeeklyData()) + const [monthlyData] = useState(generateMonthlyData()) + const [categoryData] = useState(generateCategoryData()) + const [performanceData] = useState(generatePerformanceData()) + const [systemMetrics] = useState({ + activeUsers: Math.floor(Math.random() * 500) + 1200, + userChange: '+12%', + completedTasks: Math.floor(Math.random() * 200) + 450, + taskChange: '+8%', + serverUptime: '99.9%', + uptimeChange: '+0.1%', + avgResponseTime: Math.floor(Math.random() * 50) + 120, + responseChange: '-15ms', + }) + // Fetch initial server date useEffect(() => { getCurrentDate() @@ -82,72 +200,222 @@ export default function Dashboard() { return (