feat: implement high-priority SEO fixes and German localization

This commit is contained in:
Timo Knuth
2026-01-12 13:35:10 +01:00
parent c6adc8567f
commit 038c8dddbc
33 changed files with 776 additions and 194 deletions

View File

@@ -0,0 +1,59 @@
'use client';
import { useEffect, useState } from 'react';
import { useSession } from 'next-auth/react';
interface AdBannerProps {
dataAdSlot: string;
dataAdFormat?: string;
fullWidthResponsive?: boolean;
className?: string;
}
export default function AdBanner({
dataAdSlot,
dataAdFormat = 'auto',
fullWidthResponsive = true,
className = '',
}: AdBannerProps) {
const { data: session, status } = useSession();
// Check if user has a paid plan
// Adjust 'pro' or 'premium' based on your actual plan values
const isPaidUser = session?.user && (
session.user.plan === 'PRO' ||
session.user.plan === 'PREMIUM' ||
session.user.plan === 'LIFETIME'
);
const [adLoaded, setAdLoaded] = useState(false);
useEffect(() => {
// Don't load if loading session or if user is paid
if (status === 'loading' || isPaidUser) return;
try {
// @ts-ignore
(window.adsbygoogle = window.adsbygoogle || []).push({});
setAdLoaded(true);
} catch (err) {
console.error('AdSense error:', err);
}
}, [isPaidUser, status]);
// Don't render anything if paid user
if (status === 'authenticated' && isPaidUser) return null;
return (
<div className={`ad-container my-8 flex justify-center items-center overflow-hidden ${className}`} aria-hidden={true}>
<ins
className="adsbygoogle"
style={{ display: 'block', minWidth: '300px', minHeight: '50px', width: '100%' }}
data-ad-client="ca-pub-2782770414424875"
data-ad-slot={dataAdSlot}
data-ad-format={dataAdFormat}
data-full-width-responsive={fullWidthResponsive}
/>
</div>
);
}

View File

@@ -8,6 +8,7 @@ import { Input } from '@/components/ui/Input';
import { Button } from '@/components/ui/Button';
import { Badge } from '@/components/ui/Badge';
import { calculateContrast } from '@/lib/utils';
import AdBanner from '@/components/ads/AdBanner';
interface InstantGeneratorProps {
t: any; // i18n translation function
@@ -276,6 +277,16 @@ export const InstantGenerator: React.FC<InstantGeneratorProps> = ({ t }) => {
</div>
</motion.div>
</div>
{/* Leaderboard Ad */}
<div className="mt-16 max-w-4xl mx-auto">
<div className="text-center text-xs text-gray-400 mb-2 uppercase tracking-wider">Advertisement</div>
<AdBanner
dataAdSlot="leaderboard-slot-id"
dataAdFormat="auto"
className="bg-white rounded-xl p-4 border border-gray-100 shadow-sm min-h-[90px]"
/>
</div>
</div>
</section>
);

View File

@@ -1,11 +1,15 @@
import Link from 'next/link';
import en from '@/i18n/en.json';
interface FooterProps {
variant?: 'marketing' | 'dashboard';
t?: typeof en; // Optional translation object
}
export function Footer({ variant = 'marketing' }: FooterProps) {
export function Footer({ variant = 'marketing', t }: FooterProps) {
const isDashboard = variant === 'dashboard';
// Fallback to English if no translation is provided or if keys are missing
const translations = t?.footer || en.footer;
return (
<footer className={`${isDashboard ? 'bg-gray-50 text-gray-600 border-t border-gray-200 mt-12' : 'bg-gray-900 text-white mt-20'} py-12`}>
@@ -17,34 +21,34 @@ export function Footer({ variant = 'marketing' }: FooterProps) {
<span className={`text-xl font-bold ${isDashboard ? 'text-gray-900' : ''}`}>QR Master</span>
</Link>
<p className={isDashboard ? 'text-gray-500' : 'text-gray-400'}>
Create custom QR codes in seconds with advanced tracking and analytics.
{translations.tagline}
</p>
</div>
<div>
<h3 className={`font-semibold mb-4 ${isDashboard ? 'text-gray-900' : ''}`}>Product</h3>
<h3 className={`font-semibold mb-4 ${isDashboard ? 'text-gray-900' : ''}`}>{translations.product}</h3>
<ul className={`space-y-2 ${isDashboard ? 'text-gray-500' : 'text-gray-400'}`}>
<li><Link href="/#features" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Features</Link></li>
<li><Link href="/#pricing" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Pricing</Link></li>
<li><Link href="/faq" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>FAQ</Link></li>
<li><Link href="/blog" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Blog</Link></li>
<li><Link href="/#features" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.features}</Link></li>
<li><Link href="/#pricing" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.pricing}</Link></li>
<li><Link href="/faq" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.faq}</Link></li>
<li><Link href="/blog" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.blog}</Link></li>
</ul>
</div>
<div>
<h3 className={`font-semibold mb-4 ${isDashboard ? 'text-gray-900' : ''}`}>Resources</h3>
<h3 className={`font-semibold mb-4 ${isDashboard ? 'text-gray-900' : ''}`}>{translations.resources}</h3>
<ul className={`space-y-2 ${isDashboard ? 'text-gray-500' : 'text-gray-400'}`}>
<li><Link href="/#pricing" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Full Pricing</Link></li>
<li><Link href="/faq" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>All Questions</Link></li>
<li><Link href="/blog" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>All Articles</Link></li>
<li><Link href="/signup" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Get Started</Link></li>
<li><Link href="/#pricing" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.full_pricing}</Link></li>
<li><Link href="/faq" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.all_questions}</Link></li>
<li><Link href="/blog" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.all_articles}</Link></li>
<li><Link href="/signup" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.get_started}</Link></li>
</ul>
</div>
<div>
<h3 className={`font-semibold mb-4 ${isDashboard ? 'text-gray-900' : ''}`}>Legal</h3>
<h3 className={`font-semibold mb-4 ${isDashboard ? 'text-gray-900' : ''}`}>{translations.legal}</h3>
<ul className={`space-y-2 ${isDashboard ? 'text-gray-500' : 'text-gray-400'}`}>
<li><Link href="/privacy" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Privacy Policy</Link></li>
<li><Link href="/privacy" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.privacy_policy}</Link></li>
</ul>
</div>
@@ -56,16 +60,17 @@ export function Footer({ variant = 'marketing' }: FooterProps) {
href="/newsletter"
className="text-[6px] text-gray-700 opacity-[0.03] hover:opacity-100 hover:text-white transition-opacity duration-300"
>
<span className="sr-only">Newsletter signup</span>
<span className="sr-only">{translations.newsletter}</span>
</Link>
) : (
<div></div>
)}
<p>&copy; 2025 QR Master. All rights reserved.</p>
<p>&copy; 2025 {translations.rights_reserved}</p>
<div className="w-12"></div>
</div>
</div>
</footer>
);
}