- 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>
62 lines
2.0 KiB
TypeScript
62 lines
2.0 KiB
TypeScript
import { useState } from 'react';
|
|
import { ImageOff } from 'lucide-react';
|
|
|
|
declare module 'react' {
|
|
interface ImgHTMLAttributes<T> extends React.HTMLAttributes<T> {
|
|
fetchpriority?: 'high' | 'low' | 'auto';
|
|
}
|
|
}
|
|
|
|
interface ImageWithFallbackProps {
|
|
src: string;
|
|
alt: string;
|
|
className?: string;
|
|
sizes?: string;
|
|
loading?: 'lazy' | 'eager';
|
|
fetchpriority?: 'high' | 'low' | 'auto';
|
|
}
|
|
|
|
const ImageWithFallback: React.FC<ImageWithFallbackProps> = ({
|
|
src,
|
|
alt,
|
|
className = '',
|
|
sizes = '100vw',
|
|
loading = 'lazy',
|
|
fetchpriority = 'auto'
|
|
}) => {
|
|
const [error, setError] = useState(false);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
if (error) {
|
|
return (
|
|
<div className={`flex items-center justify-center bg-gray-900 ${className}`}>
|
|
<div className="text-center p-4">
|
|
<ImageOff className="h-8 w-8 mx-auto mb-2 text-gray-500" />
|
|
<span className="text-sm text-gray-500">{alt}</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="relative">
|
|
{isLoading && (
|
|
<div className={`absolute inset-0 bg-gray-900/50 animate-pulse ${className}`} />
|
|
)}
|
|
<img
|
|
src={src}
|
|
alt={alt}
|
|
className={`${className} ${isLoading ? 'opacity-0' : 'opacity-100'} transition-opacity duration-300`}
|
|
loading={loading}
|
|
fetchpriority={fetchpriority}
|
|
sizes={sizes}
|
|
onLoad={() => setIsLoading(false)}
|
|
onError={() => setError(true)}
|
|
decoding="async"
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ImageWithFallback;
|