Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2687f673fe | ||
|
|
afcded170b |
@@ -82,3 +82,6 @@ supabase/.temp/
|
|||||||
|
|
||||||
# Source images (originals before optimization)
|
# Source images (originals before optimization)
|
||||||
source-images/
|
source-images/
|
||||||
|
|
||||||
|
# Auto Claude data directory
|
||||||
|
.auto-claude/
|
||||||
|
|||||||
+10
-12
@@ -1,25 +1,23 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { getTranslations } from 'next-intl/server';
|
|
||||||
import { defaultLocale } from '@/i18n/config';
|
|
||||||
|
|
||||||
export default async function NotFound() {
|
|
||||||
const t = await getTranslations('pages.notfound');
|
|
||||||
|
|
||||||
|
export default function NotFound() {
|
||||||
return (
|
return (
|
||||||
<div className="bg-zinc-900 text-zinc-50 min-h-screen flex items-center justify-center">
|
<html lang="de">
|
||||||
|
<body className="bg-zinc-900 text-zinc-50 min-h-screen flex items-center justify-center">
|
||||||
<div className="text-center px-4">
|
<div className="text-center px-4">
|
||||||
<h1 className="text-6xl font-bold mb-4">404</h1>
|
<h1 className="text-6xl font-bold mb-4">404</h1>
|
||||||
<h2 className="text-2xl font-semibold mb-4">{t('title')}</h2>
|
<h2 className="text-2xl font-semibold mb-4">Page Not Found</h2>
|
||||||
<p className="text-zinc-400 mb-8">
|
<p className="text-zinc-400 mb-8">
|
||||||
{t('description')}
|
The page you are looking for does not exist or has been moved.
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
href={`/${defaultLocale}`}
|
href="/de"
|
||||||
className="px-6 py-3 bg-white text-zinc-900 hover:bg-zinc-200 rounded-lg transition-colors inline-block font-medium"
|
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors inline-block"
|
||||||
>
|
>
|
||||||
{t('backHome')}
|
Go to Homepage
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</body>
|
||||||
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,46 @@ interface Skill {
|
|||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SkeletonProps {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Skeleton: React.FC<SkeletonProps> = ({ className }) => (
|
||||||
|
<div className={`animate-pulse bg-zinc-700/50 rounded ${className}`}></div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const SkillsSkeleton = () => (
|
||||||
|
<section id="skills" className="py-24 relative" aria-label="Loading skills" aria-busy="true">
|
||||||
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<Skeleton className="h-8 w-48 mb-12" />
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
|
{/* Skills Progress Bars Skeleton */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
{[1, 2, 3, 4, 5, 6, 7, 8].map((i) => (
|
||||||
|
<div key={i} className="space-y-2">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<Skeleton className="h-4 w-24" />
|
||||||
|
<Skeleton className="h-4 w-12" />
|
||||||
|
</div>
|
||||||
|
<Skeleton className="h-2 w-full rounded-full" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Skills Icons Grid Skeleton */}
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
|
||||||
|
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||||
|
<div key={i} className="p-6 rounded-xl bg-zinc-800/30 backdrop-blur-sm">
|
||||||
|
<Skeleton className="w-8 h-8 mx-auto mb-3" />
|
||||||
|
<Skeleton className="h-4 w-16 mx-auto" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
|
||||||
const iconMap: Record<string, React.ReactNode> = {
|
const iconMap: Record<string, React.ReactNode> = {
|
||||||
'AI & LLMs': <Bot className="w-8 h-8" />,
|
'AI & LLMs': <Bot className="w-8 h-8" />,
|
||||||
'Automation': <Workflow className="w-8 h-8" />,
|
'Automation': <Workflow className="w-8 h-8" />,
|
||||||
@@ -29,6 +69,8 @@ const Skills = () => {
|
|||||||
const [skills, setSkills] = useState<Skill[]>([]);
|
const [skills, setSkills] = useState<Skill[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Temporary delay to see skeleton loading state
|
||||||
|
const timer = setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
const translatedSkills = t.raw('skills') as Skill[];
|
const translatedSkills = t.raw('skills') as Skill[];
|
||||||
if (Array.isArray(translatedSkills)) {
|
if (Array.isArray(translatedSkills)) {
|
||||||
@@ -38,10 +80,13 @@ const Skills = () => {
|
|||||||
console.error('Error loading skills:', error);
|
console.error('Error loading skills:', error);
|
||||||
setSkills([]);
|
setSkills([]);
|
||||||
}
|
}
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
}, [t]);
|
}, [t]);
|
||||||
|
|
||||||
if (skills.length === 0) {
|
if (skills.length === 0) {
|
||||||
return <div className="py-24 text-center text-zinc-400">Loading skills...</div>;
|
return <SkillsSkeleton />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user