AEO / SEO

This commit is contained in:
Timo Knuth
2026-01-23 14:39:27 +01:00
parent b00697bc37
commit cb521f2aee
39 changed files with 2217 additions and 114 deletions

View File

@@ -149,11 +149,11 @@ const TOOLS = [
},
{
icon: Phone,
name: 'Phone',
description: 'Call phone number',
href: '/tools/phone-qr-code',
color: 'text-blue-400',
bg: 'bg-blue-50'
name: 'Call',
description: 'Start a phone call',
href: '/tools/call-qr-code-generator',
color: 'text-violet-500',
bg: 'bg-violet-50'
},
{
icon: CreditCard,

View File

@@ -0,0 +1,163 @@
'use client';
import React, { useState, useEffect } from 'react';
import { QRCodeSVG } from 'qrcode.react';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { Download, RefreshCw, Smartphone, Image as ImageIcon, ScanLine } from 'lucide-react';
import Link from 'next/link';
import { cn } from '@/lib/utils';
export function MiniGenerator() {
const [url, setUrl] = useState('');
const [color, setColor] = useState('#000000');
const [withLogo, setWithLogo] = useState(false);
const [frame, setFrame] = useState<'none' | 'scan_me' | 'phone'>('none');
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
// Prevent hydration mismatch
if (!mounted) {
return (
<div className="bg-gray-100 rounded-lg p-8 mb-6 flex items-center justify-center min-h-[300px] animate-pulse">
<Smartphone className="w-16 h-16 text-gray-300" />
</div>
);
}
const renderQR = () => (
<QRCodeSVG
value={url || 'https://www.qrmaster.net'}
size={180}
fgColor={color}
bgColor="#ffffff"
level="H" // High error correction for logo
includeMargin={false}
imageSettings={withLogo ? {
src: "/logo.svg", // Assuming this exists, or use a placeholder
x: undefined,
y: undefined,
height: 40,
width: 40,
excavate: true,
} : undefined}
/>
);
return (
<div className="flex flex-col h-full">
<h3 className="font-semibold text-xl mb-6 text-center">Live Preview</h3>
<div className="flex-1 flex flex-col items-center justify-center space-y-6">
<div className="relative group">
{/* Frame Rendering */}
<div className={cn(
"bg-white p-4 rounded-xl shadow-lg transition-all duration-300",
frame === 'scan_me' && "pt-12 pb-4 px-4 bg-black rounded-lg",
frame === 'phone' && "p-2 border-8 border-gray-800 rounded-[2rem]"
)}>
{frame === 'scan_me' && (
<div className="absolute top-3 left-0 right-0 text-center text-white font-bold tracking-wider text-sm">
SCAN ME
</div>
)}
{/* White background for QR inside frames */}
<div className={cn(
"bg-white",
frame === 'scan_me' && "p-2 rounded",
frame === 'phone' && "rounded-2xl overflow-hidden"
)}>
{renderQR()}
</div>
</div>
</div>
<div className="w-full space-y-4">
<div>
<Input
placeholder="Enter your website URL..."
value={url}
onChange={(e) => setUrl(e.target.value)}
className="text-center"
/>
</div>
{/* Controls */}
<div className="flex flex-wrap gap-2 justify-center">
{/* Color Picker */}
<div className="flex items-center space-x-2 bg-gray-50 px-3 py-1.5 rounded-full border border-gray-200" title="Choose Color">
<div className="relative overflow-hidden w-6 h-6 rounded-full border border-gray-300 shadow-sm cursor-pointer hover:scale-110 transition-transform">
<input
type="color"
value={color}
onChange={(e) => setColor(e.target.value)}
className="absolute inset-0 w-[150%] h-[150%] -top-1/4 -left-1/4 cursor-pointer p-0 border-0"
/>
</div>
</div>
{/* Logo Toggle */}
<Button
variant={withLogo ? 'secondary' : 'outline'}
size="sm"
onClick={() => setWithLogo(!withLogo)}
className="text-xs h-9"
title="Toggle Logo"
>
<ImageIcon className="w-4 h-4 mr-1" />
{withLogo ? 'Logo On' : 'Logo'}
</Button>
{/* Frame Toggle */}
<Button
variant={frame !== 'none' ? 'secondary' : 'outline'}
size="sm"
onClick={() => setFrame(prev => prev === 'none' ? 'scan_me' : prev === 'scan_me' ? 'phone' : 'none')}
className="text-xs h-9"
title="Change Frame"
>
<ScanLine className="w-4 h-4 mr-1" />
Frame
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => {
setUrl('');
setColor('#000000');
setWithLogo(false);
setFrame('none');
}}
className="text-xs text-gray-500 hover:text-gray-900 h-9 px-2"
title="Reset"
>
<RefreshCw className="w-4 h-4" />
</Button>
</div>
</div>
</div>
<div className="mt-8 space-y-3">
<Link href="/create" className="block">
<Button className="w-full" size="lg">
<Download className="w-4 h-4 mr-2" />
Download High-Res
</Button>
</Link>
<div className="text-center">
<p className="text-xs text-gray-500">
Unlock gradients, custom shapes & analytics {' '}
<Link href="/signup" className="text-primary-600 hover:underline font-medium">
Try Pro Free
</Link>
</p>
</div>
</div>
</div>
);
}

View File

@@ -30,6 +30,7 @@ const tools = [
{ name: 'Email', href: '/tools/email-qr-code', icon: Mail, color: 'text-amber-500', bgColor: 'bg-amber-50' },
{ name: 'SMS', href: '/tools/sms-qr-code', icon: MessageSquare, color: 'text-cyan-500', bgColor: 'bg-cyan-50' },
{ name: 'Instagram', href: '/tools/instagram-qr-code', icon: Instagram, color: 'text-pink-600', bgColor: 'bg-pink-50' },
{ name: 'Call', href: '/tools/call-qr-code-generator', icon: Phone, color: 'text-emerald-500', bgColor: 'bg-emerald-50' },
{ name: 'Barcode', href: '/tools/barcode-generator', icon: Barcode, color: 'text-slate-900', bgColor: 'bg-slate-100' },
];

View File

@@ -73,7 +73,7 @@ export function BreadcrumbSchema({ items, showUI = false }: BreadcrumbSchemaProp
export function ToolBreadcrumb({ toolName, toolSlug }: { toolName: string; toolSlug: string }) {
const items: BreadcrumbItem[] = [
{ name: 'Home', url: 'https://qrmaster.io/' },
{ name: 'Free QR Code Tools', url: 'https://qrmaster.io/#tools' },
{ name: 'Free QR Code Tools', url: 'https://qrmaster.io/tools' },
{ name: toolName, url: `https://qrmaster.io/tools/${toolSlug}` },
];

View File

@@ -1,3 +1,5 @@
'use client';
import React from 'react';
import { cn } from '@/lib/utils';
@@ -10,7 +12,7 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant = 'primary', size = 'md', loading, children, disabled, ...props }, ref) => {
const baseClasses = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed';
const variants = {
primary: 'bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-500',

View File

@@ -47,6 +47,9 @@ export function Footer({ variant = 'marketing', t }: FooterProps) {
<li><Link href="/reprint-calculator" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Reprint Cost Calculator</Link></li>
<li><Link href="/qr-code-tracking" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Our Analytics</Link></li>
<li><Link href="/manage-qr-codes" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Manage QR Codes</Link></li>
<li><Link href="/custom-qr-code-generator" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Custom QR</Link></li>
<li><Link href="/tools/barcode-generator" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>Barcode Generator</Link></li>
</ul>
</div>