press releases

This commit is contained in:
Timo Knuth
2026-01-27 12:29:44 +01:00
parent be5db36b7f
commit 4dc7c29134
19 changed files with 606 additions and 16 deletions

View File

@@ -1,7 +1,8 @@
import React from 'react';
import type { Metadata } from 'next';
import SeoJsonLd from '@/components/SeoJsonLd';
import { organizationSchema, websiteSchema, softwareApplicationSchema } from '@/lib/schema';
import { organizationSchema, websiteSchema, softwareApplicationSchema, reviewSchema, aggregateRatingSchema } from '@/lib/schema';
import { getFeaturedTestimonials, getAggregateRating } from '@/lib/testimonial-data';
import HomePageClient from '@/components/marketing/HomePageClient';
function truncateAtWord(text: string, maxLength: number): string {
@@ -52,9 +53,19 @@ export async function generateMetadata(): Promise<Metadata> {
}
export default function HomePage() {
const featuredTestimonials = getFeaturedTestimonials();
const aggregateRating = getAggregateRating();
const reviewSchemas = featuredTestimonials.map(t => reviewSchema(t));
return (
<>
<SeoJsonLd data={[websiteSchema(), organizationSchema(), softwareApplicationSchema()]} />
<SeoJsonLd data={[
websiteSchema(),
organizationSchema(),
softwareApplicationSchema(),
aggregateRatingSchema(aggregateRating),
...reviewSchemas
]} />
{/* Server-rendered SEO content for crawlers */}
<div className="sr-only" aria-hidden="false">

View File

@@ -0,0 +1,139 @@
import React from 'react';
import { Metadata } from 'next';
import Link from 'next/link';
import { Button } from '@/components/ui/Button';
import SeoJsonLd from '@/components/SeoJsonLd';
import { organizationSchema, websiteSchema } from '@/lib/schema';
import { ChevronRight, ExternalLink, Newspaper, Award, Calendar } from 'lucide-react';
export const metadata: Metadata = {
title: 'Press & News | QR Master',
description: 'Latest news, press releases, and updates from QR Master. Stay informed about new features, company announcements, and industry insights.',
keywords: ['qr master press', 'qr code generator news', 'company updates', 'press releases'],
openGraph: {
title: 'Press & News | QR Master',
description: 'Latest news, press releases, and updates from QR Master.',
url: 'https://www.qrmaster.net/press',
type: 'website',
}
};
export default function PressPage() {
const pressReleases = [
{
id: "launch-2026",
title: "qrmaster.net Launches Free, Professional QR Code Generator for Global Users",
date: "January 27, 2026",
excerpt: "Duesseldorf-based startup unveils qrmaster.net, a comprehensive, free online QR code generator designed to serve as the ultimate bridge between the physical and digital worlds.",
bullets: [
"Advanced tracking capabilities with UTM builder for GA4 integration",
"100% Free professional templates and design customization",
"Privacy-focused architecture with no data selling",
"Dynamic QR codes that can be edited after printing"
],
link: "https://www.prlog.org/13123883-qrmasternet-launches-free-professional-qr-code-generator-for-global-users.html",
source: "PRLog"
}
];
return (
<>
<SeoJsonLd data={[organizationSchema(), websiteSchema()]} />
<div className="bg-white min-h-screen">
{/* Hero Section */}
<section className="relative overflow-hidden bg-gradient-to-br from-gray-900 to-gray-800 text-white py-20 sm:py-24">
<div className="absolute inset-0 bg-[url('/grid-pattern.svg')] opacity-10"></div>
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-7xl relative z-10">
<div className="max-w-3xl">
<h1 className="text-4xl sm:text-5xl font-bold mb-6 tracking-tight">
Newsroom
</h1>
<p className="text-xl text-gray-300 max-w-2xl leading-relaxed">
Latest updates, press releases, and announcements from the QR Master team.
</p>
</div>
</div>
</section>
{/* Press Releases List */}
<section className="py-16 sm:py-24">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-5xl">
<div className="flex items-center gap-3 mb-12">
<Newspaper className="w-6 h-6 text-blue-600" />
<h2 className="text-2xl font-bold text-gray-900">Latest Releases</h2>
</div>
<div className="space-y-12">
{pressReleases.map((pr) => (
<div key={pr.id} className="group relative bg-white border border-gray-100 rounded-2xl shadow-sm hover:shadow-md transition-all duration-300 overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-blue-600 opacity-0 group-hover:opacity-100 transition-opacity"></div>
<div className="p-8 sm:p-10">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6">
<div className="flex items-center gap-2 text-sm text-gray-500 font-medium">
<Calendar className="w-4 h-4" />
{pr.date}
</div>
<span className="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-blue-50 text-blue-700">
Press Release
</span>
</div>
<h3 className="text-2xl sm:text-3xl font-bold text-gray-900 mb-4 group-hover:text-blue-600 transition-colors">
<a href={pr.link} target="_blank" rel="noopener noreferrer" className="focus:outline-none">
<span className="absolute inset-0" aria-hidden="true"></span>
{pr.title}
</a>
</h3>
<p className="text-gray-600 text-lg mb-8 leading-relaxed">
{pr.excerpt}
</p>
<div className="bg-gray-50 rounded-xl p-6 mb-8">
<h4 className="text-sm font-semibold text-gray-900 uppercase tracking-wider mb-4">Highlights</h4>
<ul className="grid sm:grid-cols-2 gap-4">
{pr.bullets.map((bullet, idx) => (
<li key={idx} className="flex items-start gap-2 text-gray-700">
<span className="mt-1.5 w-1.5 h-1.5 rounded-full bg-blue-500 flex-shrink-0"></span>
<span>{bullet}</span>
</li>
))}
</ul>
</div>
<div className="flex items-center justify-between mt-auto">
<div className="flex items-center gap-2 text-sm text-gray-500">
<span>Source:</span>
<span className="font-semibold text-gray-900">{pr.source}</span>
</div>
<div className="flex items-center gap-1 text-blue-600 font-semibold group-hover:gap-2 transition-all">
Read full release <ExternalLink className="w-4 h-4" />
</div>
</div>
</div>
</div>
))}
</div>
</div>
</section>
{/* Media Kit CTA (Future proofing) */}
<section className="bg-gray-50 py-16 border-t border-gray-100">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-5xl text-center">
<h2 className="text-2xl font-bold text-gray-900 mb-4">Media Inquiries</h2>
<p className="text-gray-600 mb-8 max-w-xl mx-auto">
For press inquiries, assets, or interview requests from our leadership team.
</p>
<a href="mailto:press@qrmaster.net">
<Button variant="outline" size="lg">
Contact Press Team
</Button>
</a>
</div>
</section>
</div>
</>
);
}

View File

@@ -0,0 +1,142 @@
import React from 'react';
import Link from 'next/link';
import { Metadata } from 'next';
import { Button } from '@/components/ui/Button';
import SeoJsonLd from '@/components/SeoJsonLd';
import { organizationSchema, reviewSchema, aggregateRatingSchema } from '@/lib/schema';
import { testimonials, getAggregateRating } from '@/lib/testimonial-data';
import { Testimonials } from '@/components/marketing/Testimonials';
import { Star } from 'lucide-react';
function truncateAtWord(text: string, maxLength: number): string {
if (text.length <= maxLength) return text;
const truncated = text.slice(0, maxLength);
const lastSpace = truncated.lastIndexOf(' ');
return lastSpace > 0 ? truncated.slice(0, lastSpace) : truncated;
}
export async function generateMetadata(): Promise<Metadata> {
const title = truncateAtWord('Customer Testimonials | QR Master Reviews', 60);
const description = truncateAtWord(
'Read what our customers say about QR Master. Real reviews from businesses using dynamic QR codes for restaurants, pottery, retail, events, and more.',
160
);
return {
title,
description,
keywords: ['qr master reviews', 'qr code testimonials', 'customer reviews', 'qr code generator reviews', 'dynamic qr code reviews'],
alternates: {
canonical: 'https://www.qrmaster.net/testimonials',
},
openGraph: {
title,
description,
url: 'https://www.qrmaster.net/testimonials',
type: 'website',
images: [
{
url: 'https://www.qrmaster.net/og-image.png',
width: 1200,
height: 630,
alt: 'QR Master Customer Testimonials',
},
],
},
twitter: {
title,
description,
},
};
}
export default function TestimonialsPage() {
const aggregateRating = getAggregateRating();
const reviewSchemas = testimonials.map(t => reviewSchema(t));
return (
<>
<SeoJsonLd data={[
organizationSchema(),
aggregateRatingSchema(aggregateRating),
...reviewSchemas
]} />
<div className="bg-white">
{/* Hero Section with Aggregate Rating */}
<section className="relative overflow-hidden bg-gradient-to-br from-blue-50 via-white to-purple-50 py-20 sm:py-24">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-5xl text-center">
<h1 className="text-4xl sm:text-5xl font-bold text-gray-900 leading-tight mb-6">
Customer <span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-600 to-purple-600">Testimonials</span>
</h1>
<p className="text-xl text-gray-600 max-w-2xl mx-auto mb-8 leading-relaxed">
Real experiences from businesses using QR Master to create dynamic QR codes
</p>
{/* Aggregate Rating Display */}
<div className="flex flex-col items-center justify-center gap-3 mb-10">
<div className="flex gap-1" aria-label={`${aggregateRating.ratingValue} out of 5 stars`}>
{[...Array(5)].map((_, index) => (
<Star
key={index}
className={`w-8 h-8 ${
index < aggregateRating.ratingValue
? 'fill-yellow-400 text-yellow-400'
: 'fill-gray-200 text-gray-200'
}`}
/>
))}
</div>
<p className="text-lg text-gray-700">
<span className="font-bold text-2xl">{aggregateRating.ratingValue}</span> out of 5 stars
</p>
<p className="text-sm text-gray-500">
Based on {aggregateRating.reviewCount} {aggregateRating.reviewCount === 1 ? 'review' : 'reviews'}
</p>
</div>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
<Link href="/signup">
<Button size="lg" className="text-lg px-8 py-6 shadow-lg shadow-blue-500/25">
Get Started Free
</Button>
</Link>
</div>
</div>
</section>
{/* Testimonials Grid */}
<Testimonials
testimonials={testimonials}
showAll={true}
title="What Our Customers Are Saying"
subtitle="Discover how businesses use QR Master for their unique needs"
/>
{/* CTA Section */}
<section className="py-20 bg-gradient-to-br from-blue-50 via-white to-purple-50">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-4xl text-center">
<h2 className="text-3xl sm:text-4xl font-bold text-gray-900 mb-6">
Ready to create your own QR codes?
</h2>
<p className="text-xl text-gray-600 mb-10 max-w-2xl mx-auto">
Join businesses using QR Master to create dynamic, trackable QR codes for their products, menus, events, and campaigns.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
<Link href="/signup">
<Button size="lg" className="text-lg px-8 py-6">
Start Free Today
</Button>
</Link>
<Link href="/pricing">
<Button variant="outline" size="lg" className="text-lg px-8 py-6">
View Pricing
</Button>
</Link>
</div>
</div>
</section>
</div>
</>
);
}