migrate to nextjs

This commit is contained in:
2026-01-29 18:50:43 +01:00
parent 019b59602f
commit 4906fc0b7b
43 changed files with 2795 additions and 3515 deletions

View File

@@ -0,0 +1,120 @@
'use client';
import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
interface BlogPostClientProps {
readingTime: number;
title: string;
}
export const BlogPostClient: React.FC<BlogPostClientProps> = ({ readingTime, title }) => {
const router = useRouter();
const [isScrolled, setIsScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 100);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const handleBack = () => {
// Lovely exit animation
const content = document.getElementById('post-content');
if (content) {
content.style.transition = 'opacity 0.5s ease-out, transform 0.5s ease-out';
content.style.opacity = '0';
content.style.transform = 'translateY(20px) scale(0.98)';
}
const topNav = document.getElementById('top-nav');
if (topNav) {
topNav.style.transition = 'opacity 0.4s ease-out';
topNav.style.opacity = '0';
}
const overlay = document.createElement('div');
overlay.className = 'fixed inset-0 bg-gradient-to-br from-white via-slate-50 to-white z-[100] opacity-0 transition-opacity duration-500';
document.body.appendChild(overlay);
setTimeout(() => {
overlay.style.opacity = '1';
}, 100);
setTimeout(() => {
router.push('/?from=post');
}, 500);
};
const handleShare = async () => {
const url = window.location.href;
if (navigator.share) {
try {
await navigator.share({ title, url });
} catch (err) {
console.error('Share failed:', err);
}
} else {
// Fallback: copy to clipboard
try {
await navigator.clipboard.writeText(url);
alert('Link copied to clipboard!');
} catch (err) {
console.error('Copy failed:', err);
}
}
};
return (
<>
<nav
id="top-nav"
className={`fixed top-0 left-0 right-0 z-40 border-b border-slate-200 transition-all duration-300 ${
isScrolled ? 'bg-white/95 backdrop-blur-md' : 'bg-white/80 backdrop-blur-sm'
}`}
>
<div className="max-w-4xl mx-auto px-6 py-4 flex items-center justify-between">
<button
onClick={handleBack}
className="flex items-center gap-2 text-slate-700 hover:text-blue-600 transition-colors duration-200 group"
aria-label="Back to home"
>
<svg className="w-5 h-5 group-hover:-translate-x-1 transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
<span className="font-medium">Back</span>
</button>
<div className="flex items-center gap-3">
<span className="text-sm text-slate-500 font-sans hidden sm:inline">
{readingTime} min read
</span>
<button
onClick={handleShare}
className="share-button-top flex items-center gap-1.5 px-3 py-1.5 bg-slate-100 hover:bg-blue-50 border border-slate-200 hover:border-blue-300 rounded-full transition-all duration-200"
aria-label="Share this post"
>
<svg className="w-4 h-4 text-slate-600 group-hover:text-blue-600 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"/>
</svg>
</button>
</div>
</div>
</nav>
<div className="mt-16 pt-8 border-t border-slate-200 text-center">
<button
onClick={handleBack}
className="inline-flex items-center gap-2 px-6 py-3 bg-white border-2 border-slate-200 text-slate-700 rounded-full hover:border-blue-400 hover:text-blue-600 hover:shadow-md transition-all duration-300 font-medium group"
>
<svg className="w-5 h-5 group-hover:-translate-x-1 transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
Back to all posts
</button>
</div>
</>
);
};