Initial commit: Portfolio Website

Vollständige Next.js 15 Portfolio-Website mit:
- Blog-System mit 100+ Artikeln
- Supabase-Integration
- Responsive Design mit Tailwind CSS
- TypeScript-Konfiguration
- Testing-Setup mit Vitest und Playwright

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-01 15:07:20 +01:00
co-authored by Claude Opus 4.5
commit e1bbe5455d
315 changed files with 94124 additions and 0 deletions
+154
View File
@@ -0,0 +1,154 @@
'use client';
import { useRouter } from 'next/navigation';
import { BarChart2, Users, Eye, Clock, TrendingUp, TrendingDown, LogOut } from 'lucide-react';
import { useTranslations, useLocale } from 'next-intl';
import { createClient } from '@/lib/supabase/client';
interface MetricCard {
title: string;
value: string;
subtitle: string;
trend?: 'up' | 'down';
trendValue?: string;
icon: React.ReactNode;
}
export default function DashboardContent() {
const t = useTranslations('dashboard');
const locale = useLocale();
const router = useRouter();
const handleLogout = async () => {
const supabase = createClient();
await supabase.auth.signOut();
router.push(`/${locale}`);
};
const metrics: MetricCard[] = [
{
title: t('metrics.pageViews.title'),
value: '12,543',
subtitle: '45 ' + t('metrics.pageViews.perMinute'),
trend: 'up',
trendValue: '+12.5%',
icon: <Eye className="h-5 w-5" />,
},
{
title: t('metrics.uniqueVisitors.title'),
value: '3,721',
subtitle: '127 ' + t('metrics.uniqueVisitors.currentlyActive'),
trend: 'up',
trendValue: '+8.2%',
icon: <Users className="h-5 w-5" />,
},
{
title: t('metrics.conversions.title'),
value: '156',
subtitle: '4.2% ' + t('metrics.conversions.rate'),
trend: 'down',
trendValue: '-2.1%',
icon: <BarChart2 className="h-5 w-5" />,
},
{
title: t('metrics.timeOnSite.title'),
value: '4:32',
subtitle: '32% ' + t('metrics.timeOnSite.bounceRate'),
trend: 'up',
trendValue: '+15.3%',
icon: <Clock className="h-5 w-5" />,
},
];
const topPages = [
{ path: '/', views: 4521, change: '+12%' },
{ path: '/portfolio', views: 2341, change: '+8%' },
{ path: '/about', views: 1823, change: '+5%' },
{ path: '/blog', views: 1456, change: '-2%' },
{ path: '/contact', views: 892, change: '+3%' },
];
return (
<main className="min-h-screen py-16 sm:py-20 lg:py-24">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Header */}
<div className="flex items-center justify-between mb-12">
<div>
<h1 className="text-3xl font-bold text-white">{t('header.title')}</h1>
<p className="text-zinc-400 mt-2">
127 {t('header.activeUsers')}
</p>
</div>
<button
onClick={handleLogout}
className="flex items-center gap-2 px-4 py-2 bg-zinc-800 hover:bg-zinc-700 rounded-lg text-zinc-300 transition-colors"
>
<LogOut className="h-4 w-4" />
Logout
</button>
</div>
{/* Metrics Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-12">
{metrics.map((metric, index) => (
<div
key={index}
className="bg-zinc-900/50 backdrop-blur-sm rounded-xl p-6 ring-1 ring-zinc-800"
>
<div className="flex items-center justify-between mb-4">
<div className="p-2 bg-zinc-800 rounded-lg text-zinc-400">
{metric.icon}
</div>
{metric.trend && (
<div className={`flex items-center gap-1 text-sm ${
metric.trend === 'up' ? 'text-green-500' : 'text-red-500'
}`}>
{metric.trend === 'up' ? (
<TrendingUp className="h-4 w-4" />
) : (
<TrendingDown className="h-4 w-4" />
)}
{metric.trendValue}
</div>
)}
</div>
<h3 className="text-sm text-zinc-400 mb-1">{metric.title}</h3>
<p className="text-2xl font-bold text-white">{metric.value}</p>
<p className="text-sm text-zinc-500 mt-1">{metric.subtitle}</p>
</div>
))}
</div>
{/* Top Pages */}
<div className="bg-zinc-900/50 backdrop-blur-sm rounded-xl ring-1 ring-zinc-800">
<div className="p-6 border-b border-zinc-800">
<h2 className="text-xl font-semibold text-white">{t('topPages.title')}</h2>
</div>
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="text-left text-sm text-zinc-500 border-b border-zinc-800">
<th className="px-6 py-4 font-medium">{t('topPages.columns.path')}</th>
<th className="px-6 py-4 font-medium">{t('topPages.columns.views')}</th>
<th className="px-6 py-4 font-medium">{t('topPages.columns.change')}</th>
</tr>
</thead>
<tbody>
{topPages.map((page, index) => (
<tr key={index} className="border-b border-zinc-800/50 last:border-0">
<td className="px-6 py-4 text-white font-mono text-sm">{page.path}</td>
<td className="px-6 py-4 text-zinc-300">{page.views.toLocaleString()}</td>
<td className={`px-6 py-4 ${
page.change.startsWith('+') ? 'text-green-500' : 'text-red-500'
}`}>
{page.change}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</main>
);
}