This commit is contained in:
2026-07-10 13:07:15 +02:00
parent 854670197b
commit bb528d6ef9
4 changed files with 276 additions and 6 deletions

View File

@@ -5,7 +5,9 @@ import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { Button } from '@/components/ui/Button';
import { Footer } from '@/components/ui/Footer';
import en from '@/i18n/en.json';
import en from '@/i18n/en.json';
import { allUseCases } from '@/lib/growth-pages';
import { LayoutGrid } from 'lucide-react';
import { ChevronDown, BookOpen, Building2, Wifi, Contact, MessageCircle, Link2, Type, Mail, MessageSquare, Phone, Calendar, MapPin, Facebook, Instagram, Twitter, Youtube, Music, Bitcoin, CreditCard, Video, Users, Barcode as BarcodeIcon, Star } from 'lucide-react';
import { cn } from '@/lib/utils';
import { AnimatePresence, motion } from 'framer-motion';
@@ -18,9 +20,11 @@ export default function MarketingLayout({
}) {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
const [toolsOpen, setToolsOpen] = useState(false);
const [toolsOpen, setToolsOpen] = useState(false);
const [useCasesOpen, setUseCasesOpen] = useState(false);
const [resourcesOpen, setResourcesOpen] = useState(false);
const [mobileToolsOpen, setMobileToolsOpen] = useState(false);
const [mobileToolsOpen, setMobileToolsOpen] = useState(false);
const [mobileUseCasesOpen, setMobileUseCasesOpen] = useState(false);
const [mobileResourcesOpen, setMobileResourcesOpen] = useState(false);
const pathname = usePathname();
@@ -36,9 +40,11 @@ export default function MarketingLayout({
// Close simple menus when path changes
useEffect(() => {
setMobileMenuOpen(false);
setToolsOpen(false);
setToolsOpen(false);
setUseCasesOpen(false);
setResourcesOpen(false);
setMobileToolsOpen(false);
setMobileToolsOpen(false);
setMobileUseCasesOpen(false);
setMobileResourcesOpen(false);
}, [pathname]);
@@ -145,6 +151,50 @@ export default function MarketingLayout({
</AnimatePresence>
</div>
{/* Use Cases Dropdown */}
<div
className="relative group px-3 py-2"
onMouseEnter={() => setUseCasesOpen(true)}
onMouseLeave={() => setUseCasesOpen(false)}
>
<button className="flex items-center space-x-1 text-sm font-medium text-slate-600 group-hover:text-slate-900 transition-colors">
<span>Use Cases</span>
<ChevronDown className={cn("w-4 h-4 transition-transform duration-200", useCasesOpen && "rotate-180")} />
</button>
<AnimatePresence>
{useCasesOpen && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
transition={{ duration: 0.15 }}
className="absolute left-1/2 -translate-x-1/2 top-full mt-2 w-[700px] bg-white rounded-2xl shadow-lg border border-slate-100 p-4 overflow-hidden"
>
<div className="grid grid-cols-2 gap-2">
{allUseCases.map((uc) => (
<Link
key={uc.slug}
href={uc.href}
className="flex items-center space-x-3 p-2.5 rounded-xl transition-colors hover:bg-slate-50"
>
<div className="p-2 rounded-lg shrink-0 bg-indigo-50 text-indigo-500">
<LayoutGrid className="w-4 h-4" />
</div>
<div>
<div className="text-sm font-semibold text-slate-900">{uc.title}</div>
</div>
</Link>
))}
</div>
<div className="mt-3 pt-3 border-t border-slate-100 -mx-4 -mb-4 px-4 py-3 text-center bg-slate-50/50">
<Link href="/use-cases" className="text-xs text-indigo-600 font-medium hover:underline">View All Use Cases &rarr;</Link>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
{/* Tools Dropdown */}
<div
className="relative group px-3 py-2"
@@ -279,6 +329,39 @@ export default function MarketingLayout({
)}
</AnimatePresence>
{/* Use Cases Accordion */}
<button
onClick={() => setMobileUseCasesOpen(!mobileUseCasesOpen)}
className="flex items-center justify-between w-full px-4 py-3 rounded-xl hover:bg-slate-50 text-slate-700 font-semibold"
>
<span>Use Cases</span>
<ChevronDown className={cn("w-5 h-5 transition-transform", mobileUseCasesOpen && "rotate-180")} />
</button>
<AnimatePresence>
{mobileUseCasesOpen && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="overflow-hidden"
>
<div className="max-h-[50vh] overflow-y-auto pl-4 space-y-1 border-l-2 border-slate-100 ml-4">
{allUseCases.map((uc) => (
<Link
key={uc.slug}
href={uc.href}
className="block px-4 py-2.5 rounded-lg hover:bg-slate-50 text-slate-600 text-sm"
onClick={() => { setMobileMenuOpen(false); setMobileUseCasesOpen(false); }}
>
{uc.title}
</Link>
))}
</div>
</motion.div>
)}
</AnimatePresence>
{/* Free Tools Accordion */}
<button
onClick={() => setMobileToolsOpen(!mobileToolsOpen)}

View File

@@ -17,7 +17,8 @@ import { ScrollToTop } from '@/components/ui/ScrollToTop';
import { FreeToolsGrid } from '@/components/marketing/FreeToolsGrid';
import { TestimonialsCarousel } from '@/components/marketing/TestimonialsCarousel';
import { testimonials } from '@/lib/testimonial-data';
import en from '@/i18n/en.json';
import en from '@/i18n/en.json';
import { allUseCases } from '@/lib/growth-pages';
export default function HomePageClient() {
// Always use English for marketing pages
@@ -120,6 +121,41 @@ export default function HomePageClient() {
</div>
</section>
{/* Popular Use Cases Section */}
<section className="bg-slate-50 py-16 border-y border-slate-200">
<div className="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="mb-8 max-w-3xl">
<h2 className="text-3xl font-bold text-slate-900">
Popular Use Cases
</h2>
<p className="mt-3 text-base leading-7 text-slate-600">
Discover how businesses across different industries use dynamic QR codes to track offline engagement and simplify customer journeys.
</p>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{allUseCases.slice(0, 8).map((item) => (
<Link
key={item.href}
href={item.href}
className="group rounded-lg border border-slate-200 bg-white p-5 shadow-sm transition-all hover:shadow-md hover:border-blue-300"
>
<h3 className="text-lg font-semibold text-slate-900 group-hover:text-blue-700">
{item.title}
</h3>
<p className="mt-2 text-sm leading-6 text-slate-600 line-clamp-2">
{item.summary}
</p>
</Link>
))}
</div>
<div className="mt-8 text-center">
<Link href="/use-cases">
<Button variant="outline" className="font-semibold text-slate-700">View All Use Cases</Button>
</Link>
</div>
</div>
</section>
{/* Testimonials Carousel */}
<TestimonialsCarousel testimonials={testimonials} />

50
tmp-homepage.js Normal file
View File

@@ -0,0 +1,50 @@
const fs = require('fs');
const file = 'src/components/marketing/HomePageClient.tsx';
let content = fs.readFileSync(file, 'utf8');
if (!content.includes("import { allUseCases }")) {
content = content.replace("import en from '@/i18n/en.json';", "import en from '@/i18n/en.json';\nimport { allUseCases } from '@/lib/growth-pages';");
}
const section = ` {/* Popular Use Cases Section */}
<section className="bg-slate-50 py-16 border-y border-slate-200">
<div className="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="mb-8 max-w-3xl">
<h2 className="text-3xl font-bold text-slate-900">
Popular Use Cases
</h2>
<p className="mt-3 text-base leading-7 text-slate-600">
Discover how businesses across different industries use dynamic QR codes to track offline engagement and simplify customer journeys.
</p>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{allUseCases.slice(0, 8).map((item) => (
<Link
key={item.href}
href={item.href}
className="group rounded-lg border border-slate-200 bg-white p-5 shadow-sm transition-all hover:shadow-md hover:border-blue-300"
>
<h3 className="text-lg font-semibold text-slate-900 group-hover:text-blue-700">
{item.title}
</h3>
<p className="mt-2 text-sm leading-6 text-slate-600 line-clamp-2">
{item.summary}
</p>
</Link>
))}
</div>
<div className="mt-8 text-center">
<Link href="/use-cases">
<Button variant="outline" className="font-semibold text-slate-700">View All Use Cases</Button>
</Link>
</div>
</div>
</section>
{/* Testimonials Carousel */}`;
if (!content.includes("Popular Use Cases Section")) {
content = content.replace("{/* Testimonials Carousel */}", section);
}
fs.writeFileSync(file, content);
console.log("Updated HomePageClient.tsx");

101
tmp-nav.js Normal file
View File

@@ -0,0 +1,101 @@
const fs = require('fs');
let code = fs.readFileSync('src/app/(main)/(marketing)/MarketingLayout.tsx', 'utf8');
if (!code.includes("import { allUseCases }")) {
code = code.replace("import en from '@/i18n/en.json';", "import en from '@/i18n/en.json';\nimport { allUseCases } from '@/lib/growth-pages';\nimport { LayoutGrid } from 'lucide-react';");
}
if (!code.includes("const [useCasesOpen")) {
code = code.replace("const [toolsOpen, setToolsOpen] = useState(false);", "const [toolsOpen, setToolsOpen] = useState(false);\n const [useCasesOpen, setUseCasesOpen] = useState(false);");
code = code.replace("const [mobileToolsOpen, setMobileToolsOpen] = useState(false);", "const [mobileToolsOpen, setMobileToolsOpen] = useState(false);\n const [mobileUseCasesOpen, setMobileUseCasesOpen] = useState(false);");
code = code.replace("setToolsOpen(false);", "setToolsOpen(false);\n setUseCasesOpen(false);");
code = code.replace("setMobileToolsOpen(false);", "setMobileToolsOpen(false);\n setMobileUseCasesOpen(false);");
}
const useCasesDropdown = ` {/* Use Cases Dropdown */}
<div
className="relative group px-3 py-2"
onMouseEnter={() => setUseCasesOpen(true)}
onMouseLeave={() => setUseCasesOpen(false)}
>
<button className="flex items-center space-x-1 text-sm font-medium text-slate-600 group-hover:text-slate-900 transition-colors">
<span>Use Cases</span>
<ChevronDown className={cn("w-4 h-4 transition-transform duration-200", useCasesOpen && "rotate-180")} />
</button>
<AnimatePresence>
{useCasesOpen && (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
transition={{ duration: 0.15 }}
className="absolute left-1/2 -translate-x-1/2 top-full mt-2 w-[700px] bg-white rounded-2xl shadow-lg border border-slate-100 p-4 overflow-hidden"
>
<div className="grid grid-cols-2 gap-2">
{allUseCases.map((uc) => (
<Link
key={uc.slug}
href={uc.href}
className="flex items-center space-x-3 p-2.5 rounded-xl transition-colors hover:bg-slate-50"
>
<div className="p-2 rounded-lg shrink-0 bg-indigo-50 text-indigo-500">
<LayoutGrid className="w-4 h-4" />
</div>
<div>
<div className="text-sm font-semibold text-slate-900">{uc.title}</div>
</div>
</Link>
))}
</div>
<div className="mt-3 pt-3 border-t border-slate-100 -mx-4 -mb-4 px-4 py-3 text-center bg-slate-50/50">
<Link href="/use-cases" className="text-xs text-indigo-600 font-medium hover:underline">View All Use Cases &rarr;</Link>
</div>
</motion.div>
)}
</AnimatePresence>
</div>`;
if (!code.includes("<span>Use Cases</span>")) {
code = code.replace("{/* Tools Dropdown */}", useCasesDropdown + "\n\n {/* Tools Dropdown */}");
}
const mobileUseCasesAccordion = ` {/* Use Cases Accordion */}
<button
onClick={() => setMobileUseCasesOpen(!mobileUseCasesOpen)}
className="flex items-center justify-between w-full px-4 py-3 rounded-xl hover:bg-slate-50 text-slate-700 font-semibold"
>
<span>Use Cases</span>
<ChevronDown className={cn("w-5 h-5 transition-transform", mobileUseCasesOpen && "rotate-180")} />
</button>
<AnimatePresence>
{mobileUseCasesOpen && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="overflow-hidden"
>
<div className="max-h-[50vh] overflow-y-auto pl-4 space-y-1 border-l-2 border-slate-100 ml-4">
{allUseCases.map((uc) => (
<Link
key={uc.slug}
href={uc.href}
className="block px-4 py-2.5 rounded-lg hover:bg-slate-50 text-slate-600 text-sm"
onClick={() => { setMobileMenuOpen(false); setMobileUseCasesOpen(false); }}
>
{uc.title}
</Link>
))}
</div>
</motion.div>
)}
</AnimatePresence>`;
if (!code.includes("{/* Use Cases Accordion */}")) {
code = code.replace("{/* Free Tools Accordion */}", mobileUseCasesAccordion + "\n\n {/* Free Tools Accordion */}");
}
fs.writeFileSync('src/app/(main)/(marketing)/MarketingLayout.tsx', code);
console.log("Updated MarketingLayout.tsx");