hydration error

This commit is contained in:
Timo Knuth 2026-01-27 11:08:32 +01:00
parent 76a76258e8
commit be5db36b7f
7 changed files with 102 additions and 79 deletions

View File

@ -94,6 +94,13 @@ export default function CreatePage() {
} }
}, [contentType, frameOptions, frameType]); }, [contentType, frameOptions, frameType]);
// Force dynamic mode for COUPON and FEEDBACK types
useEffect(() => {
if (contentType === 'COUPON' || contentType === 'FEEDBACK') {
setIsDynamic(true);
}
}, [contentType]);
// Logo state // Logo state
const [logoUrl, setLogoUrl] = useState(''); const [logoUrl, setLogoUrl] = useState('');
const [logoSize, setLogoSize] = useState(24); const [logoSize, setLogoSize] = useState(24);
@ -712,14 +719,23 @@ export default function CreatePage() {
<span className="font-medium">Dynamic</span> <span className="font-medium">Dynamic</span>
<Badge variant="info" className="ml-2">Recommended</Badge> <Badge variant="info" className="ml-2">Recommended</Badge>
</label> </label>
<label className="flex items-center cursor-pointer"> <label className={cn(
"flex items-center",
(contentType === 'COUPON' || contentType === 'FEEDBACK')
? "opacity-50 cursor-not-allowed"
: "cursor-pointer"
)}>
<input <input
type="radio" type="radio"
checked={!isDynamic} checked={!isDynamic}
onChange={() => setIsDynamic(false)} onChange={() => setIsDynamic(false)}
disabled={contentType === 'COUPON' || contentType === 'FEEDBACK'}
className="mr-2" className="mr-2"
/> />
<span className="font-medium">Static</span> <span className="font-medium">Static</span>
{(contentType === 'COUPON' || contentType === 'FEEDBACK') && (
<Tooltip text="Coupon and Feedback QR codes require dynamic features for tracking and analytics." />
)}
</label> </label>
</div> </div>
<p className="text-sm text-gray-600 mt-2"> <p className="text-sm text-gray-600 mt-2">
@ -727,6 +743,13 @@ export default function CreatePage() {
? '✅ Dynamic: Track scans, edit URL later, view analytics. QR contains tracking link.' ? '✅ Dynamic: Track scans, edit URL later, view analytics. QR contains tracking link.'
: '⚡ Static: Direct to content, no tracking, cannot edit. QR contains actual content.'} : '⚡ Static: Direct to content, no tracking, cannot edit. QR contains actual content.'}
</p> </p>
{(contentType === 'COUPON' || contentType === 'FEEDBACK') && (
<div className="mt-3 p-3 bg-blue-50 border border-blue-200 rounded-lg">
<p className="text-sm text-blue-900">
<strong>Note:</strong> {contentType === 'COUPON' ? 'Coupon' : 'Feedback'} QR codes must be Dynamic to track redemptions, collect feedback, and view detailed analytics.
</p>
</div>
)}
</CardContent> </CardContent>
</Card> </Card>

View File

@ -26,9 +26,6 @@ export default function MarketingLayout({
setScrolled(window.scrollY > 20); setScrolled(window.scrollY > 20);
}; };
// Check immediately on mount
handleScroll();
window.addEventListener('scroll', handleScroll, { passive: true }); window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll);
}, []); }, []);
@ -37,6 +34,7 @@ export default function MarketingLayout({
useEffect(() => { useEffect(() => {
setMobileMenuOpen(false); setMobileMenuOpen(false);
setToolsOpen(false); setToolsOpen(false);
setMobileToolsOpen(false);
}, [pathname]); }, [pathname]);
// Default to English for general marketing pages // Default to English for general marketing pages

View File

@ -81,7 +81,7 @@ export default function AuthorPage({ params }: { params: { slug: string } }) {
<div className="space-y-4"> <div className="space-y-4">
{posts.map(p => ( {posts.map(p => (
<Link key={p.slug} href={`/blog/${p.slug}`} className="block group p-6 rounded-xl border border-gray-200 bg-white hover:border-blue-200 hover:shadow-sm transition-all"> <Link key={p.slug} href={`/blog/${p.slug}`} className="block group p-6 rounded-xl border border-gray-200 bg-white hover:border-blue-200 hover:shadow-sm transition-all">
<div className="text-sm text-gray-400 mb-1">{new Date(p.datePublished || p.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}</div> <div className="text-sm text-gray-400 mb-1">{p.date}</div>
<h3 className="text-xl font-bold text-gray-900 group-hover:text-blue-700 transition-colors mb-2">{p.title}</h3> <h3 className="text-xl font-bold text-gray-900 group-hover:text-blue-700 transition-colors mb-2">{p.title}</h3>
<p className="text-gray-600">{p.description}</p> <p className="text-gray-600">{p.description}</p>
</Link> </Link>

View File

@ -55,7 +55,7 @@ export default function PillarPage({ params }: { params: { pillar: PillarKey } }
<div className="grid md:grid-cols-2 gap-6"> <div className="grid md:grid-cols-2 gap-6">
{posts.map(p => ( {posts.map(p => (
<Link key={p.slug} href={`/blog/${p.slug}`} className="group block rounded-xl border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md hover:border-blue-200 transition-all"> <Link key={p.slug} href={`/blog/${p.slug}`} className="group block rounded-xl border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md hover:border-blue-200 transition-all">
<div className="text-xs text-gray-400 mb-2">{new Date(p.datePublished || p.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}</div> <div className="text-xs text-gray-400 mb-2">{p.date}</div>
<div className="text-lg font-bold text-gray-900 mb-2 group-hover:text-blue-700">{p.title}</div> <div className="text-lg font-bold text-gray-900 mb-2 group-hover:text-blue-700">{p.title}</div>
<div className="text-sm text-gray-600 line-clamp-2">{p.description}</div> <div className="text-sm text-gray-600 line-clamp-2">{p.description}</div>
</Link> </Link>

View File

@ -45,7 +45,7 @@ export default function LearnHubPage() {
<Link key={p.slug} href={`/blog/${p.slug}`} className="group block rounded-2xl border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md hover:border-blue-200 transition-all"> <Link key={p.slug} href={`/blog/${p.slug}`} className="group block rounded-2xl border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md hover:border-blue-200 transition-all">
<div className="flex justify-between items-center mb-3"> <div className="flex justify-between items-center mb-3">
<div className="text-xs font-semibold px-2 py-1 rounded bg-gray-100 text-gray-600">{p.pillar?.toUpperCase() || 'GUIDE'}</div> <div className="text-xs font-semibold px-2 py-1 rounded bg-gray-100 text-gray-600">{p.pillar?.toUpperCase() || 'GUIDE'}</div>
<div className="text-xs text-gray-400">{new Date(p.datePublished || p.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}</div> <div className="text-xs text-gray-400">{p.date}</div>
</div> </div>
<div className="text-xl font-bold text-gray-900 mb-2 group-hover:text-blue-700 line-clamp-2">{p.title}</div> <div className="text-xl font-bold text-gray-900 mb-2 group-hover:text-blue-700 line-clamp-2">{p.title}</div>
<div className="text-gray-600 line-clamp-2">{p.description}</div> <div className="text-gray-600 line-clamp-2">{p.description}</div>

View File

@ -31,9 +31,9 @@ export default function CookieBanner() {
setShowBanner(false); setShowBanner(false);
}; };
if (!showBanner) return null;
return ( return (
<div suppressHydrationWarning>
{showBanner && (
<> <>
{/* Cookie Banner - Bottom Left Corner */} {/* Cookie Banner - Bottom Left Corner */}
<div className="fixed bottom-4 left-4 z-50 max-w-md animate-slide-in"> <div className="fixed bottom-4 left-4 z-50 max-w-md animate-slide-in">
@ -112,5 +112,7 @@ export default function CookieBanner() {
} }
`}</style> `}</style>
</> </>
)}
</div>
); );
} }