Migrate from Vite to Next.js 15 with SSR

- Replace Vite + React Router with Next.js 15 App Router
- Implement i18n with next-intl (URL-based: /de, /en, /sr)
- Add SSR/SSG for all pages (48 static pages generated)
- Setup Supabase SSR client for auth
- Migrate all pages: Home, About, Portfolio, Blog, Contact, Login, Dashboard, Imprint, Privacy, Terms
- Add Docker support with standalone output
- Replace i18next with next-intl JSON translations
- Use next/image for optimized images

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-17 01:00:33 +01:00
co-authored by Claude Opus 4.5
parent a66f51b9a2
commit b1ec7b4d61
281 changed files with 8024 additions and 8901 deletions
+99
View File
@@ -0,0 +1,99 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { supabase } from '../utils/supabaseClient';
import { Lock } from 'lucide-react';
import SEO from '../components/SEO';
import PageTransition from '../components/PageTransition';
const LoginPage = () => {
const navigate = useNavigate();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError(null);
try {
const { error } = await supabase.auth.signInWithPassword({
email,
password
});
if (error) throw error;
navigate('/dashboard');
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
return (
<PageTransition>
<SEO
title="Login"
description="Login to access the dashboard"
/>
<div className="min-h-screen flex items-center justify-center py-24 px-4">
<div className="max-w-md w-full space-y-8">
<div className="text-center">
<Lock className="mx-auto h-12 w-12 text-orange-500" />
<h2 className="mt-6 text-3xl font-bold">Sign in to Dashboard</h2>
</div>
<form className="mt-8 space-y-6" onSubmit={handleLogin}>
{error && (
<div className="bg-red-500/10 border border-red-500/20 text-red-500 p-4 rounded-lg text-sm">
{error}
</div>
)}
<div className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium text-white/80 mb-2">
Email address
</label>
<input
id="email"
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full bg-gray-900/50 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-orange-500"
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-white/80 mb-2">
Password
</label>
<input
id="password"
type="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full bg-gray-900/50 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-orange-500"
/>
</div>
</div>
<button
type="submit"
disabled={loading}
className="w-full bg-orange-500 hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed text-white font-medium py-3 rounded-lg transition-colors"
>
{loading ? 'Signing in...' : 'Sign in'}
</button>
</form>
</div>
</div>
</PageTransition>
);
};
export default LoginPage;