diff --git a/frontend/src/pages/Analytics.jsx b/frontend/src/pages/Analytics.jsx
new file mode 100644
index 0000000..bf70523
--- /dev/null
+++ b/frontend/src/pages/Analytics.jsx
@@ -0,0 +1,181 @@
+import { useState, useEffect } from 'react';
+import { useAuth } from '../context/AuthContext';
+import {
+ fetchAnalyticsOverview, fetchAnalyticsPageviews,
+ fetchAnalyticsUsers, fetchAnalyticsActivity
+} from '../api';
+
+function BarChart({ data, height = 200 }) {
+ if (!data.length) return
+ {data.map((d, i) => (
+
+
{d.count}
+
0 ? '4px' : '0' }}
+ title={`${d.date}: ${d.count}`}
+ />
+
+ {d.date.slice(5)}
+
+
+ ))}
+
+ );
+}
+
+function StatCard({ label, value, color = 'blue' }) {
+ const colors = {
+ blue: 'text-blue-600',
+ green: 'text-green-600',
+ orange: 'text-orange-500',
+ red: 'text-red-600',
+ purple: 'text-purple-600',
+ };
+ return (
+
+ );
+}
+
+const EVENT_LABELS = {
+ page_view: 'Seitenaufruf',
+ recipe_created: 'Rezept erstellt',
+ recipe_updated: 'Rezept bearbeitet',
+ recipe_deleted: 'Rezept gelöscht',
+ user_login: 'Login',
+ user_register: 'Registrierung',
+};
+
+const EVENT_COLORS = {
+ page_view: 'bg-blue-100 text-blue-700',
+ recipe_created: 'bg-green-100 text-green-700',
+ recipe_updated: 'bg-yellow-100 text-yellow-700',
+ recipe_deleted: 'bg-red-100 text-red-700',
+ user_login: 'bg-purple-100 text-purple-700',
+ user_register: 'bg-indigo-100 text-indigo-700',
+};
+
+export default function Analytics() {
+ const { token } = useAuth();
+ const [overview, setOverview] = useState(null);
+ const [pageviews, setPageviews] = useState([]);
+ const [users, setUsers] = useState([]);
+ const [activity, setActivity] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ load();
+ }, []);
+
+ const load = async () => {
+ try {
+ const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
+ const [ov, pv, us, act] = await Promise.all([
+ fetchAnalyticsOverview(token),
+ fetchAnalyticsPageviews(token, thirtyDaysAgo),
+ fetchAnalyticsUsers(token),
+ fetchAnalyticsActivity(token),
+ ]);
+ setOverview(ov);
+ setPageviews(pv);
+ setUsers(us);
+ setActivity(act);
+ } catch (e) {
+ console.error('Analytics Fehler:', e);
+ }
+ setLoading(false);
+ };
+
+ if (loading) return
Laden...
;
+ if (!overview) return
Fehler beim Laden der Analytics-Daten.
;
+
+ return (
+
+
Analytics Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Pageviews (letzte 30 Tage)
+
+
+
+
+
+
Aktivste User
+ {users.length === 0 ? (
+
Keine Daten vorhanden.
+ ) : (
+
+
+
+
+ | User |
+ Logins |
+ Erstellt |
+ Bearbeitet |
+ Gesamt |
+
+
+
+ {users.map(u => (
+
+ | {u.username} |
+ {u.logins} |
+ {u.recipes_created} |
+ {u.recipes_updated} |
+ {u.total_events} |
+
+ ))}
+
+
+
+ )}
+
+
+
+
Letzte Aktivitäten
+ {activity.length === 0 ? (
+
Keine Aktivitäten vorhanden.
+ ) : (
+
+ {activity.map(e => (
+
+
+ {EVENT_LABELS[e.type] || e.type}
+
+
+ {e.username || 'Anonym'}
+ {e.path && {e.path}}
+ {e.recipe_name && "{e.recipe_name}"}
+
+
+ {new Date(e.timestamp).toLocaleString('de-DE', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' })}
+
+
+ ))}
+
+ )}
+
+
+
+ );
+}