feat: implement high-priority SEO fixes and German localization
This commit is contained in:
59
src/components/ads/AdBanner.tsx
Normal file
59
src/components/ads/AdBanner.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>© 2025 QR Master. All rights reserved.</p>
|
||||
<p>© 2025 {translations.rights_reserved}</p>
|
||||
<div className="w-12"></div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user