neuer versuch
This commit is contained in:
parent
1747922b29
commit
f68b7a331c
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"firecrawl": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"firecrawl-mcp"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"FIRECRAWL_API_KEY": "fc-268826f038ad4bf0a38c48690ba9c1fa"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
/** @type {import('next-sitemap').IConfig} */
|
|
||||||
module.exports = {
|
|
||||||
siteUrl: 'https://www.qrmaster.net',
|
|
||||||
generateRobotsTxt: true,
|
|
||||||
robotsTxtOptions: {
|
|
||||||
policies: [
|
|
||||||
{
|
|
||||||
userAgent: '*',
|
|
||||||
allow: '/',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
transform: async (config, path) => {
|
|
||||||
// Custom priority and changefreq based on path
|
|
||||||
let priority = 0.7;
|
|
||||||
let changefreq = 'weekly';
|
|
||||||
|
|
||||||
if (path === '/') {
|
|
||||||
priority = 0.9;
|
|
||||||
changefreq = 'daily';
|
|
||||||
} else if (path === '/blog') {
|
|
||||||
priority = 0.7;
|
|
||||||
changefreq = 'daily';
|
|
||||||
} else if (path === '/pricing') {
|
|
||||||
priority = 0.8;
|
|
||||||
changefreq = 'weekly';
|
|
||||||
} else if (path === '/faq') {
|
|
||||||
priority = 0.6;
|
|
||||||
changefreq = 'weekly';
|
|
||||||
} else if (path.startsWith('/blog/')) {
|
|
||||||
priority = 0.6;
|
|
||||||
changefreq = 'weekly';
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
loc: path,
|
|
||||||
changefreq,
|
|
||||||
priority,
|
|
||||||
lastmod: new Date().toISOString(),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -27,11 +27,7 @@ const nextConfig = {
|
||||||
destination: '/blog/bulk-qr-code-generator-excel',
|
destination: '/blog/bulk-qr-code-generator-excel',
|
||||||
permanent: true,
|
permanent: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
source: '/blog/bulk-qr-codes-excel',
|
|
||||||
destination: '/blog/bulk-qr-code-generator-excel',
|
|
||||||
permanent: true,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 3050",
|
"dev": "next dev -p 3050",
|
||||||
"build": "prisma generate && next build",
|
"build": "prisma generate && next build",
|
||||||
|
"submit:indexnow": "tsx scripts/submit-indexnow.ts",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"db:generate": "prisma generate",
|
"db:generate": "prisma generate",
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 MiB After Width: | Height: | Size: 215 KiB |
|
|
@ -1,19 +0,0 @@
|
||||||
# QR Master - robots.txt
|
|
||||||
# Allow all search engines to crawl all pages
|
|
||||||
|
|
||||||
User-agent: *
|
|
||||||
Allow: /
|
|
||||||
|
|
||||||
# Sitemap location
|
|
||||||
Sitemap: https://www.qrmaster.net/sitemap.xml
|
|
||||||
|
|
||||||
# Crawl-delay (optional, be nice to servers)
|
|
||||||
Crawl-delay: 1
|
|
||||||
|
|
||||||
# Disallow admin/api routes
|
|
||||||
Disallow: /api/
|
|
||||||
Disallow: /dashboard/
|
|
||||||
Disallow: /_next/
|
|
||||||
|
|
||||||
# Allow all free tools explicitly
|
|
||||||
Allow: /tools/
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
||||||
<url>
|
|
||||||
<loc>https://www.qrmaster.net/</loc>
|
|
||||||
<lastmod>2025-10-16T00:00:00Z</lastmod>
|
|
||||||
<changefreq>daily</changefreq>
|
|
||||||
<priority>0.9</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.qrmaster.net/blog</loc>
|
|
||||||
<lastmod>2025-10-16T00:00:00Z</lastmod>
|
|
||||||
<changefreq>daily</changefreq>
|
|
||||||
<priority>0.7</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.qrmaster.net/pricing</loc>
|
|
||||||
<lastmod>2025-10-16T00:00:00Z</lastmod>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.8</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.qrmaster.net/faq</loc>
|
|
||||||
<lastmod>2025-10-16T00:00:00Z</lastmod>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.6</priority>
|
|
||||||
</url>
|
|
||||||
<url>
|
|
||||||
<loc>https://www.qrmaster.net/blog/qr-code-analytics</loc>
|
|
||||||
<lastmod>2025-10-16T00:00:00Z</lastmod>
|
|
||||||
<changefreq>weekly</changefreq>
|
|
||||||
<priority>0.6</priority>
|
|
||||||
</url>
|
|
||||||
</urlset>
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
|
||||||
|
import { blogPostList } from '../src/lib/blog-data';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const HOST = 'www.qrmaster.net';
|
||||||
|
const KEY = '1234567890abcdef';
|
||||||
|
const KEY_LOCATION = `https://${HOST}/${KEY}.txt`;
|
||||||
|
const INDEXNOW_ENDPOINT = 'https://api.indexnow.org/indexnow';
|
||||||
|
|
||||||
|
async function submitIndexNow() {
|
||||||
|
console.log('🚀 Starting IndexNow submission...');
|
||||||
|
|
||||||
|
// 1. Gather all URLs
|
||||||
|
const baseUrl = `https://${HOST}`;
|
||||||
|
const staticPages = [
|
||||||
|
'',
|
||||||
|
'/pricing',
|
||||||
|
'/faq',
|
||||||
|
'/blog',
|
||||||
|
'/signup',
|
||||||
|
'/login',
|
||||||
|
'/privacy',
|
||||||
|
'/qr-code-erstellen',
|
||||||
|
'/qr-code-tracking',
|
||||||
|
'/dynamic-qr-code-generator',
|
||||||
|
'/bulk-qr-code-generator',
|
||||||
|
'/newsletter',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Dynamically get tool slugs from directory
|
||||||
|
const toolsDir = path.join(process.cwd(), 'src/app/(marketing)/tools');
|
||||||
|
let freeTools: string[] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
freeTools = fs.readdirSync(toolsDir).filter(file => {
|
||||||
|
return fs.statSync(path.join(toolsDir, file)).isDirectory();
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('⚠️ Could not read tools directory:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolTypeUrls = freeTools.map(slug => `/tools/${slug}`);
|
||||||
|
const blogUrls = blogPostList.map(post => `/blog/${post.slug}`);
|
||||||
|
|
||||||
|
const allPaths = [...staticPages, ...toolTypeUrls, ...blogUrls];
|
||||||
|
const urlList = allPaths.map(path => `${baseUrl}${path}`);
|
||||||
|
|
||||||
|
console.log(`📝 Found ${urlList.length} URLs to submit.`);
|
||||||
|
|
||||||
|
// 2. Prepare payload
|
||||||
|
const payload = {
|
||||||
|
host: HOST,
|
||||||
|
key: KEY,
|
||||||
|
keyLocation: KEY_LOCATION,
|
||||||
|
urlList: urlList,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 3. Send Request
|
||||||
|
const response = await fetch(INDEXNOW_ENDPOINT, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 200 || response.status === 202) {
|
||||||
|
console.log('✅ IndexNow submission successful!');
|
||||||
|
} else {
|
||||||
|
console.error(`❌ IndexNow submission failed. Status: ${response.status}`);
|
||||||
|
console.error(await response.text());
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error submitting to IndexNow:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submitIndexNow();
|
||||||
|
|
@ -1,6 +1,38 @@
|
||||||
|
|
||||||
|
Other
|
||||||
|
Structured data has schema.org validation error
|
||||||
|
38
|
||||||
|
4
|
||||||
|
3
|
||||||
|
1
|
||||||
|
0
|
||||||
|
0
|
||||||
|
|
||||||
|
|
||||||
|
Pages to submit to IndexNow
|
||||||
|
5
|
||||||
|
25
|
||||||
|
—
|
||||||
|
—
|
||||||
|
—
|
||||||
|
—
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Issues
|
Issues
|
||||||
/
|
/
|
||||||
H1 tag missing or empty
|
Structured data has schema.org validation error
|
||||||
|
|
||||||
Why and how to fix
|
Why and how to fix
|
||||||
|
|
||||||
|
|
@ -34,18 +66,20 @@ Crawl history
|
||||||
Hide chart
|
Hide chart
|
||||||
12 Jan
|
12 Jan
|
||||||
13 Jan
|
13 Jan
|
||||||
|
13 Jan
|
||||||
|
14 Jan
|
||||||
0
|
0
|
||||||
1
|
10
|
||||||
2
|
20
|
||||||
3
|
30
|
||||||
4
|
40
|
||||||
All filter results
|
All filter results
|
||||||
|
|
||||||
All filter results
|
All filter results
|
||||||
2
|
38
|
||||||
|
|
||||||
Lost from filter results
|
Lost from filter results
|
||||||
1
|
0
|
||||||
|
|
||||||
Lost
|
Lost
|
||||||
0
|
0
|
||||||
|
|
@ -60,31 +94,418 @@ Export
|
||||||
PR
|
PR
|
||||||
URL
|
URL
|
||||||
Organic traffic
|
Organic traffic
|
||||||
HTTP status code
|
Schema items
|
||||||
Depth
|
Structured data issues
|
||||||
H1
|
|
||||||
H1 length
|
|
||||||
No. of H1
|
|
||||||
Is indexable page
|
Is indexable page
|
||||||
12
|
40
|
||||||
html
|
html
|
||||||
Admin Dashboard | QR Master | QR Master
|
QR Master: Dynamic QR Generator
|
||||||
|
https://www.qrmaster.net/
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
36
|
||||||
|
html
|
||||||
|
QR Code Erstellen – Kostenlos | QR Master
|
||||||
|
https://www.qrmaster.net/qr-code-erstellen
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free SMS QR Code Generator | SMS QR Code Erstellen | QR Master
|
||||||
|
https://www.qrmaster.net/tools/sms-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Twitter (X) QR Code Generator | Follow & Connect | QR Master
|
||||||
|
https://www.qrmaster.net/tools/twitter-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free WhatsApp QR Code Generator | Start Chats Instantly | QR Master
|
||||||
|
https://www.qrmaster.net/tools/whatsapp-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
QR Insights: Latest QR Strategies | QR Master
|
||||||
|
https://www.qrmaster.net/blog
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Event QR Code Generator | Termin & Kalender QR | QR Master
|
||||||
|
https://www.qrmaster.net/tools/event-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free WiFi QR Code Generator | WLAN QR Code | QR Master
|
||||||
|
https://www.qrmaster.net/tools/wifi-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Privacy Policy | QR Master | QR Master
|
||||||
|
https://www.qrmaster.net/privacy
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free YouTube QR Code Generator | Get Views & Subscribers | QR Master
|
||||||
|
https://www.qrmaster.net/tools/youtube-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Pricing Plans | QR Master
|
||||||
|
https://www.qrmaster.net/pricing
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Zoom QR Code Generator | Join Meetings Instantly | QR Master
|
||||||
|
https://www.qrmaster.net/tools/zoom-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Bulk QR Code Generator | Create from Excel | QR Master | QR Master
|
||||||
|
https://www.qrmaster.net/bulk-qr-code-generator
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Bulk QR Codes from Excel: 2025 Guide | QR Master
|
||||||
|
https://www.qrmaster.net/blog/bulk-qr-code-generator-excel
|
||||||
|
0
|
||||||
|
BlogPosting
|
||||||
|
BreadcrumbList
|
||||||
|
HowTo
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
QR Master FAQ: Dynamic & Bulk QR | QR Master
|
||||||
|
https://www.qrmaster.net/faq
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Geolocation QR Code Generator | Standort & Map Links | QR Master
|
||||||
|
https://www.qrmaster.net/tools/geolocation-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free TikTok QR Code Generator | Get Followers | QR Master
|
||||||
|
https://www.qrmaster.net/tools/tiktok-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Instagram QR Code Generator | Get More Followers | QR Master
|
||||||
|
https://www.qrmaster.net/tools/instagram-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Phone QR Code Generator | Anruf & Telefon QR | QR Master
|
||||||
|
https://www.qrmaster.net/tools/phone-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Facebook QR Code Generator | Get Likes & Follows | QR Master
|
||||||
|
https://www.qrmaster.net/tools/facebook-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Microsoft Teams QR Code Generator | Join Meetings | QR Master
|
||||||
|
https://www.qrmaster.net/tools/teams-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free PayPal QR Code Generator | Accept Payments Instantly | QR Master
|
||||||
|
https://www.qrmaster.net/tools/paypal-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free URL QR Code Generator | Link to Any Website | QR Master
|
||||||
|
https://www.qrmaster.net/tools/url-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Email QR Code Generator | Email QR Code Erstellen | QR Master
|
||||||
|
https://www.qrmaster.net/tools/email-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free vCard QR Code Generator | Digitale Visitenkarte Erstellen | QR Master
|
||||||
|
https://www.qrmaster.net/tools/vcard-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Text QR Code Generator | Text zu QR Code | QR Master
|
||||||
|
https://www.qrmaster.net/tools/text-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
35
|
||||||
|
html
|
||||||
|
Free Crypto QR Code Generator | Krypto QR Code Erstellen | QR Master
|
||||||
|
https://www.qrmaster.net/tools/crypto-qr-code
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
33
|
||||||
|
html
|
||||||
|
Free vCard QR Generator: Digital Cards | QR Master
|
||||||
|
https://www.qrmaster.net/blog/vcard-qr-code-generator
|
||||||
|
0
|
||||||
|
BlogPosting
|
||||||
|
BreadcrumbList
|
||||||
|
HowTo
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
33
|
||||||
|
html
|
||||||
|
Restaurant Menu QR Codes: 2025 Guide | QR Master
|
||||||
|
https://www.qrmaster.net/blog/qr-code-restaurant-menu
|
||||||
|
0
|
||||||
|
BlogPosting
|
||||||
|
BreadcrumbList
|
||||||
|
HowTo
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
33
|
||||||
|
html
|
||||||
|
QR Code Analytics: The Complete Guide | QR Master
|
||||||
|
https://www.qrmaster.net/blog/qr-code-analytics
|
||||||
|
0
|
||||||
|
BlogPosting
|
||||||
|
BreadcrumbList
|
||||||
|
HowTo
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
33
|
||||||
|
html
|
||||||
|
Static vs Dynamic QR Codes: The Ultimate Guide (2025) | QR Master
|
||||||
|
https://www.qrmaster.net/blog/dynamic-vs-static-qr-codes
|
||||||
|
0
|
||||||
|
BlogPosting
|
||||||
|
BreadcrumbList
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
33
|
||||||
|
html
|
||||||
|
QR Code Print Size Guide: Minimum Sizes for Every Use Case | QR Master
|
||||||
|
https://www.qrmaster.net/blog/qr-code-print-size-guide
|
||||||
|
0
|
||||||
|
BlogPosting
|
||||||
|
BreadcrumbList
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
33
|
||||||
|
html
|
||||||
|
Best QR Code Generator for Small Business 2025 | QR Master
|
||||||
|
https://www.qrmaster.net/blog/qr-code-small-business
|
||||||
|
0
|
||||||
|
BlogPosting
|
||||||
|
BreadcrumbList
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
33
|
||||||
|
html
|
||||||
|
QR Code Tracking: Complete Guide 2025 | QR Master
|
||||||
|
https://www.qrmaster.net/blog/qr-code-tracking-guide-2025
|
||||||
|
0
|
||||||
|
BlogPosting
|
||||||
|
BreadcrumbList
|
||||||
|
HowTo
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
27
|
||||||
|
html
|
||||||
|
Dynamic QR Code Generator | Edit & Track QR | QR Master | QR Master
|
||||||
|
https://www.qrmaster.net/dynamic-qr-code-generator
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
22
|
||||||
|
html
|
||||||
|
QR Code Tracking & Analytics - Track Every Scan | QR Master | QR Master
|
||||||
|
https://www.qrmaster.net/qr-code-tracking
|
||||||
|
0
|
||||||
|
Organization
|
||||||
|
WebSite
|
||||||
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
|
Yes
|
||||||
|
15
|
||||||
|
html
|
||||||
|
Newsletter Admin | QR Master | QR Master
|
||||||
https://www.qrmaster.net/newsletter
|
https://www.qrmaster.net/newsletter
|
||||||
0
|
0
|
||||||
200
|
Organization
|
||||||
1
|
WebSite
|
||||||
0
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
No
|
No
|
||||||
2
|
2
|
||||||
html
|
html
|
||||||
Post Not Found | QR Master
|
QR Master – Smart QR Generator & Analytics
|
||||||
https://www.qrmaster.net/blog/3-body.png
|
https://www.qrmaster.net/blog/3-body.png
|
||||||
0
|
0
|
||||||
200
|
Organization
|
||||||
2
|
WebSite
|
||||||
0
|
Schema.org validation error
|
||||||
|
View issues
|
||||||
No
|
No
|
||||||
Showing 2 of 2
|
Showing 38 of 38
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -100,7 +521,7 @@ Showing 2 of 2
|
||||||
|
|
||||||
Issues
|
Issues
|
||||||
/
|
/
|
||||||
Low word count
|
Pages to submit to IndexNow
|
||||||
|
|
||||||
Why and how to fix
|
Why and how to fix
|
||||||
|
|
||||||
|
|
@ -134,25 +555,25 @@ Crawl history
|
||||||
Hide chart
|
Hide chart
|
||||||
12 Jan
|
12 Jan
|
||||||
13 Jan
|
13 Jan
|
||||||
|
13 Jan
|
||||||
|
14 Jan
|
||||||
0
|
0
|
||||||
1
|
8
|
||||||
2
|
16
|
||||||
3
|
24
|
||||||
4
|
32
|
||||||
All filter results
|
All filter results
|
||||||
|
|
||||||
All filter results
|
All filter results
|
||||||
2
|
5
|
||||||
|
|
||||||
Lost from filter results
|
Lost from filter results
|
||||||
1
|
|
||||||
|
|
||||||
Lost
|
Lost
|
||||||
0
|
|
||||||
|
|
||||||
Patches: Show all
|
Patches: Show all
|
||||||
|
|
||||||
Changes: Don't show
|
Changes: Absolute
|
||||||
|
|
||||||
Columns
|
Columns
|
||||||
|
|
||||||
|
|
@ -160,8 +581,10 @@ Export
|
||||||
PR
|
PR
|
||||||
URL
|
URL
|
||||||
Organic traffic
|
Organic traffic
|
||||||
Depth
|
Changes
|
||||||
No. of content words
|
HTTP status code
|
||||||
|
Content type
|
||||||
|
Is indexable page
|
||||||
Title
|
Title
|
||||||
Patch it
|
Patch it
|
||||||
|
|
||||||
|
|
@ -171,141 +594,149 @@ Patch it
|
||||||
|
|
||||||
Batch AI
|
Batch AI
|
||||||
H1
|
H1
|
||||||
Is indexable page
|
H2
|
||||||
12
|
No. of content words
|
||||||
|
Changes
|
||||||
|
No. of internal outlinks
|
||||||
|
Changes
|
||||||
|
No. of external outlinks
|
||||||
|
Changes
|
||||||
|
Page text
|
||||||
|
First found at
|
||||||
|
35
|
||||||
html
|
html
|
||||||
Admin Dashboard | QR Master | QR Master
|
Free WiFi QR Code Generator | WLAN QR Code | QR Master
|
||||||
https://www.qrmaster.net/newsletter
|
https://www.qrmaster.net/tools/wifi-qr-code
|
||||||
0
|
|
||||||
1
|
|
||||||
8
|
|
||||||
Admin Dashboard | QR Master | QR Master
|
|
||||||
Enter new title
|
|
||||||
Admin restricted area.
|
|
||||||
Enter new meta description
|
|
||||||
No
|
|
||||||
2
|
|
||||||
html
|
|
||||||
Post Not Found | QR Master
|
|
||||||
https://www.qrmaster.net/blog/3-body.png
|
|
||||||
0
|
|
||||||
2
|
|
||||||
6
|
|
||||||
Post Not Found | QR Master
|
|
||||||
Enter new title
|
|
||||||
Create dynamic QR codes, track scans, and scale campaigns with secure analytics.
|
|
||||||
Enter new meta description
|
|
||||||
No
|
|
||||||
Showing 2 of 2
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Issues
|
|
||||||
/
|
|
||||||
Meta description too short
|
|
||||||
|
|
||||||
Why and how to fix
|
|
||||||
|
|
||||||
Submit to IndexNow
|
|
||||||
|
|
||||||
Create new issue
|
|
||||||
|
|
||||||
All URLs
|
|
||||||
|
|
||||||
Pages
|
|
||||||
|
|
||||||
Resources
|
|
||||||
|
|
||||||
Content
|
|
||||||
|
|
||||||
Links
|
|
||||||
|
|
||||||
Redirects
|
|
||||||
|
|
||||||
Indexability
|
|
||||||
|
|
||||||
Sitemaps
|
|
||||||
|
|
||||||
Ahrefs metrics
|
|
||||||
Word or phrase
|
|
||||||
|
|
||||||
URL
|
|
||||||
|
|
||||||
Advanced filter
|
|
||||||
Crawl history
|
|
||||||
Hide chart
|
|
||||||
12 Jan
|
|
||||||
13 Jan
|
|
||||||
0
|
|
||||||
1
|
|
||||||
2
|
|
||||||
3
|
|
||||||
4
|
|
||||||
All filter results
|
|
||||||
|
|
||||||
All filter results
|
|
||||||
2
|
|
||||||
|
|
||||||
Lost from filter results
|
|
||||||
1
|
|
||||||
|
|
||||||
Lost
|
|
||||||
0
|
|
||||||
|
|
||||||
Patches: Show all
|
|
||||||
|
|
||||||
Changes: Don't show
|
|
||||||
|
|
||||||
Columns
|
|
||||||
|
|
||||||
Export
|
|
||||||
PR
|
|
||||||
URL
|
|
||||||
Organic traffic
|
|
||||||
HTTP status code
|
|
||||||
Depth
|
|
||||||
Meta description
|
|
||||||
Patch it
|
|
||||||
|
|
||||||
Batch AI
|
|
||||||
Meta description length
|
|
||||||
No. of meta descriptions
|
|
||||||
Is indexable page
|
|
||||||
12
|
|
||||||
html
|
|
||||||
Admin Dashboard | QR Master | QR Master
|
|
||||||
https://www.qrmaster.net/newsletter
|
|
||||||
0
|
0
|
||||||
200
|
200
|
||||||
1
|
text/html; charset=utf-8
|
||||||
Admin restricted area.
|
Yes
|
||||||
|
Free WiFi QR Code Generator | WLAN QR Code | QR Master
|
||||||
|
Free WiFi QR Code Generator | WLAN QR Code Erstellen | QR Master
|
||||||
|
Enter new title
|
||||||
|
Create a WiFi QR code in seconds. Erstelle kostenlos deinen WLAN QR Code ohne Passwort-Eingabe. Guests scan to connect instantly. 100% Secure & Free.
|
||||||
Enter new meta description
|
Enter new meta description
|
||||||
|
12
|
||||||
|
13
|
||||||
|
−1
|
||||||
|
0
|
||||||
|
0
|
||||||
|
View changes
|
||||||
|
54 B
|
||||||
|
64 B
|
||||||
|
https://www.qrmaster.net/blog/qr-code-analytics
|
||||||
|
33
|
||||||
|
html
|
||||||
|
Free vCard QR Generator: Digital Cards | QR Master
|
||||||
|
https://www.qrmaster.net/blog/vcard-qr-code-generator
|
||||||
|
0
|
||||||
|
200
|
||||||
|
308
|
||||||
|
text/html; charset=utf-8
|
||||||
|
text/plain; charset=utf-8
|
||||||
|
Yes
|
||||||
|
No
|
||||||
|
Free vCard QR Generator: Digital Cards | QR Master
|
||||||
|
Enter new title
|
||||||
|
Create professional vCard QR codes for digital business cards. Share contact info instantly with a scan—includes templates and best practices.
|
||||||
|
Enter new meta description
|
||||||
|
Free vCard QR Generator: Digital Cards
|
||||||
|
Quick Answer
|
||||||
|
What is a vCard QR Code?
|
||||||
|
Why Use a Digital Business Card QR Code?
|
||||||
|
Information You Can Include in a vCard
|
||||||
|
Static vs Dynamic vCard QR Codes
|
||||||
|
All 13
|
||||||
|
1,149
|
||||||
|
0
|
||||||
|
+1,149
|
||||||
|
36
|
||||||
|
0
|
||||||
|
+36
|
||||||
|
0
|
||||||
|
View text
|
||||||
|
7 KB
|
||||||
|
https://www.qrmaster.net/blog/qr-code-analytics
|
||||||
|
33
|
||||||
|
html
|
||||||
|
Restaurant Menu QR Codes: 2025 Guide | QR Master
|
||||||
|
https://www.qrmaster.net/blog/qr-code-restaurant-menu
|
||||||
|
0
|
||||||
|
200
|
||||||
|
308
|
||||||
|
text/html; charset=utf-8
|
||||||
|
text/plain; charset=utf-8
|
||||||
|
Yes
|
||||||
|
No
|
||||||
|
Restaurant Menu QR Codes: 2025 Guide | QR Master
|
||||||
|
Enter new title
|
||||||
|
Step-by-step guide to creating digital menu QR codes for your restaurant. Learn best practices for touchless menus, placement tips, and tracking.
|
||||||
|
Enter new meta description
|
||||||
|
Restaurant Menu QR Codes: 2025 Guide
|
||||||
|
Quick Answer
|
||||||
|
Why Restaurants Need QR Code Menus in 2025
|
||||||
|
Step 1: Prepare Your Digital Menu
|
||||||
|
Step 2: Create Your QR Code with QR Master
|
||||||
|
Step 3: Customize Your Restaurant QR Code
|
||||||
|
All 13
|
||||||
|
1,256
|
||||||
|
0
|
||||||
|
+1,256
|
||||||
|
38
|
||||||
|
0
|
||||||
|
+38
|
||||||
|
0
|
||||||
|
View text
|
||||||
|
8 KB
|
||||||
|
https://www.qrmaster.net/blog/qr-code-analytics
|
||||||
|
33
|
||||||
|
html
|
||||||
|
Best QR Code Generator for Small Business 2025 | QR Master
|
||||||
|
https://www.qrmaster.net/blog/qr-code-small-business
|
||||||
|
0
|
||||||
|
200
|
||||||
|
308
|
||||||
|
text/html; charset=utf-8
|
||||||
|
text/plain; charset=utf-8
|
||||||
|
Yes
|
||||||
|
No
|
||||||
|
Best QR Code Generator for Small Business 2025 | QR Master
|
||||||
|
Enter new title
|
||||||
|
Find the best QR code solution for your small business. Compare features, pricing, and use cases for marketing, payments, and operations.
|
||||||
|
Enter new meta description
|
||||||
|
Best QR Code Generator for Small Business 2025
|
||||||
|
Quick Answer
|
||||||
|
Why Small Businesses Need QR Codes
|
||||||
|
Top 10 QR Code Use Cases for Small Business
|
||||||
|
What to Look for in a Small Business QR Solution
|
||||||
|
QR Master for Small Business
|
||||||
|
All 11
|
||||||
|
1,048
|
||||||
|
0
|
||||||
|
+1,048
|
||||||
|
36
|
||||||
|
0
|
||||||
|
+36
|
||||||
|
0
|
||||||
|
View text
|
||||||
|
7 KB
|
||||||
|
https://www.qrmaster.net/blog/qr-code-analytics
|
||||||
22
|
22
|
||||||
1
|
|
||||||
No
|
|
||||||
2
|
|
||||||
html
|
html
|
||||||
Post Not Found | QR Master
|
QR Code Tracking & Analytics - Track Every Scan | QR Master | QR Master
|
||||||
https://www.qrmaster.net/blog/3-body.png
|
https://www.qrmaster.net/qr-code-tracking
|
||||||
0
|
0
|
||||||
200
|
200
|
||||||
2
|
text/html; charset=utf-8
|
||||||
Create dynamic QR codes, track scans, and scale campaigns with secure analytics.
|
Yes
|
||||||
|
QR Code Tracking & Analytics - Track Every Scan | QR Master | QR Master
|
||||||
|
Enter new title
|
||||||
|
Track QR code scans with real-time analytics. Monitor location, device, time, and user behavior. Free QR code tracking software with detailed reports.
|
||||||
Enter new meta description
|
Enter new meta description
|
||||||
80
|
15
|
||||||
1
|
0
|
||||||
No
|
0
|
||||||
Showing 2 of 2
|
View text
|
||||||
|
71 B
|
||||||
|
https://www.qrmaster.net/blog/qr-code-restaurant-menu
|
||||||
|
Showing 5 of 5
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import '@/styles/globals.css';
|
import '@/styles/globals.css';
|
||||||
|
import { Suspense } from 'react';
|
||||||
import { Providers } from '@/components/Providers';
|
import { Providers } from '@/components/Providers';
|
||||||
import AppLayout from './AppLayout';
|
import AppLayout from './AppLayout';
|
||||||
|
|
||||||
|
|
@ -18,9 +19,11 @@ export default function RootAppLayout({
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body className="font-sans">
|
<body className="font-sans">
|
||||||
<Providers>
|
<Providers>
|
||||||
<AppLayout>
|
<Suspense fallback={null}>
|
||||||
{children}
|
<AppLayout>
|
||||||
</AppLayout>
|
{children}
|
||||||
|
</AppLayout>
|
||||||
|
</Suspense>
|
||||||
</Providers>
|
</Providers>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import type { Metadata } from 'next';
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Authentication | QR Master',
|
title: 'Authentication | QR Master',
|
||||||
description: 'Securely login or sign up to QR Master to manage your dynamic QR codes, track analytics, and access premium features. Your gateway to professional QR management.',
|
description: 'Securely login or sign up to QR Master to manage your dynamic QR codes, track analytics, and access premium features. Your gateway to professional QR management.',
|
||||||
robots: { index: false, follow: true },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function AuthRootLayout({
|
export default function AuthRootLayout({
|
||||||
|
|
@ -19,6 +19,11 @@ export default function AuthRootLayout({
|
||||||
<Providers>
|
<Providers>
|
||||||
<div className="min-h-screen bg-gradient-to-br from-primary-50 to-white">
|
<div className="min-h-screen bg-gradient-to-br from-primary-50 to-white">
|
||||||
{children}
|
{children}
|
||||||
|
<div className="py-6 text-center text-sm text-slate-500 space-x-4">
|
||||||
|
<a href="/" className="hover:text-primary-600 transition-colors">Home</a>
|
||||||
|
<a href="/privacy" className="hover:text-primary-600 transition-colors">Privacy</a>
|
||||||
|
<a href="/faq" className="hover:text-primary-600 transition-colors">FAQ</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Providers>
|
</Providers>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -72,116 +72,93 @@ export default function LoginClientPage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-br from-primary-50 to-white flex items-center justify-center p-4">
|
<Card>
|
||||||
<div className="w-full max-w-md">
|
<CardContent className="p-6">
|
||||||
<div className="text-center mb-8">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<Link href="/" className="inline-flex items-center space-x-2 mb-6">
|
{error && (
|
||||||
<img src="/logo.svg" alt="QR Master" className="w-10 h-10" />
|
<div className="bg-red-50 text-red-600 p-3 rounded-lg text-sm">
|
||||||
<span className="text-2xl font-bold text-gray-900">QR Master</span>
|
{error}
|
||||||
</Link>
|
|
||||||
<h1 className="text-3xl font-bold text-gray-900">Welcome Back</h1>
|
|
||||||
<p className="text-gray-600 mt-2">Sign in to your account</p>
|
|
||||||
<Link href="/" className="text-sm text-primary-600 hover:text-primary-700 font-medium mt-2 inline-block border border-primary-600 hover:border-primary-700 px-4 py-2 rounded-lg transition-colors">
|
|
||||||
← Back to Home
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardContent className="p-6">
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
|
||||||
{error && (
|
|
||||||
<div className="bg-red-50 text-red-600 p-3 rounded-lg text-sm">
|
|
||||||
{error}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Email"
|
|
||||||
type="email"
|
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
placeholder="you@example.com"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Password"
|
|
||||||
type="password"
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
placeholder="••••••••"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input type="checkbox" className="mr-2" />
|
|
||||||
<span className="text-sm text-gray-600">Remember me</span>
|
|
||||||
</label>
|
|
||||||
<Link href="/forgot-password" className="text-sm text-primary-600 hover:text-primary-700">
|
|
||||||
Forgot password?
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button type="submit" className="w-full" loading={loading} disabled={csrfLoading || loading}>
|
|
||||||
{csrfLoading ? 'Loading...' : 'Sign In'}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<div className="relative my-6">
|
|
||||||
<div className="absolute inset-0 flex items-center">
|
|
||||||
<div className="w-full border-t border-gray-300"></div>
|
|
||||||
</div>
|
|
||||||
<div className="relative flex justify-center text-sm">
|
|
||||||
<span className="px-2 bg-white text-gray-500">Or continue with</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
className="w-full"
|
|
||||||
onClick={handleGoogleSignIn}
|
|
||||||
>
|
|
||||||
<svg className="w-5 h-5 mr-2" viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="#4285F4"
|
|
||||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="#34A853"
|
|
||||||
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="#FBBC05"
|
|
||||||
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="#EA4335"
|
|
||||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Sign in with Google
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div className="mt-6 text-center">
|
|
||||||
<p className="text-sm text-gray-600">
|
|
||||||
Don't have an account?{' '}
|
|
||||||
<Link href="/signup" className="text-primary-600 hover:text-primary-700 font-medium">
|
|
||||||
Sign up
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
)}
|
||||||
</Card>
|
|
||||||
|
|
||||||
<p className="text-center text-sm text-gray-500 mt-6">
|
<Input
|
||||||
By signing in, you agree to our{' '}
|
label="Email"
|
||||||
<Link href="/privacy" className="text-primary-600 hover:text-primary-700">
|
type="email"
|
||||||
Privacy Policy
|
value={email}
|
||||||
</Link>
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
</p>
|
placeholder="you@example.com"
|
||||||
</div>
|
required
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label="Password"
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
placeholder="••••••••"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<label className="flex items-center">
|
||||||
|
<input type="checkbox" className="mr-2" />
|
||||||
|
<span className="text-sm text-gray-600">Remember me</span>
|
||||||
|
</label>
|
||||||
|
<Link href="/forgot-password" className="text-sm text-primary-600 hover:text-primary-700">
|
||||||
|
Forgot password?
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button type="submit" className="w-full" loading={loading} disabled={csrfLoading || loading}>
|
||||||
|
{csrfLoading ? 'Loading...' : 'Sign In'}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div className="relative my-6">
|
||||||
|
<div className="absolute inset-0 flex items-center">
|
||||||
|
<div className="w-full border-t border-gray-300"></div>
|
||||||
|
</div>
|
||||||
|
<div className="relative flex justify-center text-sm">
|
||||||
|
<span className="px-2 bg-white text-gray-500">Or continue with</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
className="w-full"
|
||||||
|
onClick={handleGoogleSignIn}
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5 mr-2" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="#4285F4"
|
||||||
|
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#34A853"
|
||||||
|
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#FBBC05"
|
||||||
|
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#EA4335"
|
||||||
|
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Sign in with Google
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div className="mt-6 text-center">
|
||||||
|
<p className="text-sm text-gray-600">
|
||||||
|
Don't have an account?{' '}
|
||||||
|
<Link href="/signup" className="text-primary-600 hover:text-primary-700 font-medium">
|
||||||
|
Sign up
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React, { Suspense } from 'react';
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
import Link from 'next/link';
|
||||||
import LoginClientPage from './ClientPage';
|
import LoginClientPage from './ClientPage';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -12,16 +13,38 @@ export const metadata: Metadata = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="min-h-screen bg-gradient-to-br from-primary-50 to-white flex items-center justify-center p-4">
|
||||||
<h1 className="sr-only">Login to QR Master – Create & Track QR Codes</h1>
|
<div className="w-full max-w-md">
|
||||||
<div className="sr-only">
|
<div className="text-center mb-8">
|
||||||
<h2>Secure Dashboard Access</h2>
|
<Link href="/" className="inline-flex items-center space-x-2 mb-6">
|
||||||
<p>Log in to manage your dynamic QR codes, view scan analytics, and update destination URLs instantly.</p>
|
<img src="/logo.svg" alt="QR Master" className="w-10 h-10" />
|
||||||
<a href="/">Back to Home</a>
|
<span className="text-2xl font-bold text-gray-900">QR Master</span>
|
||||||
|
</Link>
|
||||||
|
<h1 className="text-3xl font-bold text-gray-900">Welcome Back</h1>
|
||||||
|
<p className="text-gray-600 mt-2">Sign in to your account</p>
|
||||||
|
<Link href="/" className="text-sm text-primary-600 hover:text-primary-700 font-medium mt-2 inline-block border border-primary-600 hover:border-primary-700 px-4 py-2 rounded-lg transition-colors">
|
||||||
|
← Back to Home
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Suspense fallback={
|
||||||
|
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-8 flex items-center justify-center min-h-[400px]">
|
||||||
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<LoginClientPage />
|
||||||
|
</Suspense>
|
||||||
|
|
||||||
|
<p className="text-center text-sm text-gray-500 mt-6">
|
||||||
|
By signing in, you agree to our{' '}
|
||||||
|
<Link href="/privacy" className="text-primary-600 hover:text-primary-700">
|
||||||
|
Privacy Policy
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<LoginClientPage />
|
</div>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -8,7 +8,9 @@ import { Input } from '@/components/ui/Input';
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import { useCsrf } from '@/hooks/useCsrf';
|
import { useCsrf } from '@/hooks/useCsrf';
|
||||||
|
|
||||||
export default function ResetPasswordPage() {
|
import { Suspense } from 'react';
|
||||||
|
|
||||||
|
function ResetPasswordContent() {
|
||||||
const { fetchWithCsrf, loading: csrfLoading } = useCsrf();
|
const { fetchWithCsrf, loading: csrfLoading } = useCsrf();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -206,3 +208,11 @@ export default function ResetPasswordPage() {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function ResetPasswordPage() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<div className="min-h-screen flex items-center justify-center">Loading...</div>}>
|
||||||
|
<ResetPasswordContent />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,124 +85,101 @@ export default function SignupClientPage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-br from-primary-50 to-white flex items-center justify-center p-4">
|
<Card>
|
||||||
<div className="w-full max-w-md">
|
<CardContent className="p-6">
|
||||||
<div className="text-center mb-8">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<Link href="/" className="inline-flex items-center space-x-2 mb-6">
|
{error && (
|
||||||
<img src="/logo.svg" alt="QR Master" className="w-10 h-10" />
|
<div className="bg-red-50 text-red-600 p-3 rounded-lg text-sm">
|
||||||
<span className="text-2xl font-bold text-gray-900">QR Master</span>
|
{error}
|
||||||
</Link>
|
|
||||||
<h1 className="text-3xl font-bold text-gray-900">Create Account</h1>
|
|
||||||
<p className="text-gray-600 mt-2">Start creating QR codes in seconds</p>
|
|
||||||
<Link href="/" className="text-sm text-primary-600 hover:text-primary-700 font-medium mt-2 inline-block border border-primary-600 hover:border-primary-700 px-4 py-2 rounded-lg transition-colors">
|
|
||||||
← Back to Home
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardContent className="p-6">
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
|
||||||
{error && (
|
|
||||||
<div className="bg-red-50 text-red-600 p-3 rounded-lg text-sm">
|
|
||||||
{error}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Full Name"
|
|
||||||
type="text"
|
|
||||||
value={name}
|
|
||||||
onChange={(e) => setName(e.target.value)}
|
|
||||||
placeholder="John Doe"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Email"
|
|
||||||
type="email"
|
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
placeholder="you@example.com"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Password"
|
|
||||||
type="password"
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
placeholder="••••••••"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label="Confirm Password"
|
|
||||||
type="password"
|
|
||||||
value={confirmPassword}
|
|
||||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
||||||
placeholder="••••••••"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button type="submit" className="w-full" loading={loading}>
|
|
||||||
Create Account
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<div className="relative my-6">
|
|
||||||
<div className="absolute inset-0 flex items-center">
|
|
||||||
<div className="w-full border-t border-gray-300"></div>
|
|
||||||
</div>
|
|
||||||
<div className="relative flex justify-center text-sm">
|
|
||||||
<span className="px-2 bg-white text-gray-500">Or continue with</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
className="w-full"
|
|
||||||
onClick={handleGoogleSignIn}
|
|
||||||
>
|
|
||||||
<svg className="w-5 h-5 mr-2" viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="#4285F4"
|
|
||||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="#34A853"
|
|
||||||
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="#FBBC05"
|
|
||||||
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
fill="#EA4335"
|
|
||||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Sign up with Google
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div className="mt-6 text-center">
|
|
||||||
<p className="text-sm text-gray-600">
|
|
||||||
Already have an account?{' '}
|
|
||||||
<Link href="/login" className="text-primary-600 hover:text-primary-700 font-medium">
|
|
||||||
Sign in
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
)}
|
||||||
</Card>
|
|
||||||
|
|
||||||
<p className="text-center text-sm text-gray-500 mt-6">
|
<Input
|
||||||
By signing up, you agree to our{' '}
|
label="Full Name"
|
||||||
<Link href="/privacy" className="text-primary-600 hover:text-primary-700">
|
type="text"
|
||||||
Privacy Policy
|
value={name}
|
||||||
</Link>
|
onChange={(e) => setName(e.target.value)}
|
||||||
</p>
|
placeholder="John Doe"
|
||||||
</div>
|
required
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label="Email"
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
placeholder="you@example.com"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label="Password"
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
placeholder="••••••••"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label="Confirm Password"
|
||||||
|
type="password"
|
||||||
|
value={confirmPassword}
|
||||||
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||||
|
placeholder="••••••••"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button type="submit" className="w-full" loading={loading}>
|
||||||
|
Create Account
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div className="relative my-6">
|
||||||
|
<div className="absolute inset-0 flex items-center">
|
||||||
|
<div className="w-full border-t border-gray-300"></div>
|
||||||
|
</div>
|
||||||
|
<div className="relative flex justify-center text-sm">
|
||||||
|
<span className="px-2 bg-white text-gray-500">Or continue with</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
className="w-full"
|
||||||
|
onClick={handleGoogleSignIn}
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5 mr-2" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="#4285F4"
|
||||||
|
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#34A853"
|
||||||
|
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#FBBC05"
|
||||||
|
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#EA4335"
|
||||||
|
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Sign up with Google
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div className="mt-6 text-center">
|
||||||
|
<p className="text-sm text-gray-600">
|
||||||
|
Already have an account?{' '}
|
||||||
|
<Link href="/login" className="text-primary-600 hover:text-primary-700 font-medium">
|
||||||
|
Sign in
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React, { Suspense } from 'react';
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
import Link from 'next/link';
|
||||||
import SignupClientPage from './ClientPage';
|
import SignupClientPage from './ClientPage';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -12,16 +13,39 @@ export const metadata: Metadata = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function SignupPage() {
|
export default function SignupPage() {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="min-h-screen bg-gradient-to-br from-primary-50 to-white flex items-center justify-center p-4">
|
||||||
<h1 className="sr-only">Sign Up for QR Master – Free QR Code Generator</h1>
|
<div className="w-full max-w-md">
|
||||||
<div className="sr-only">
|
<div className="text-center mb-8">
|
||||||
<h2>Get Started Free</h2>
|
<Link href="/" className="inline-flex items-center space-x-2 mb-6">
|
||||||
<p>Create your account today. No credit card required for the free tier. Start generating trackable QR codes in minutes.</p>
|
<img src="/logo.svg" alt="QR Master" className="w-10 h-10" />
|
||||||
<a href="/">Back to Home</a>
|
<span className="text-2xl font-bold text-gray-900">QR Master</span>
|
||||||
|
</Link>
|
||||||
|
<h1 className="text-3xl font-bold text-gray-900">Create Account</h1>
|
||||||
|
<p className="text-gray-600 mt-2">Start creating QR codes in seconds</p>
|
||||||
|
<Link href="/" className="text-sm text-primary-600 hover:text-primary-700 font-medium mt-2 inline-block border border-primary-600 hover:border-primary-700 px-4 py-2 rounded-lg transition-colors">
|
||||||
|
← Back to Home
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Suspense fallback={
|
||||||
|
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-8 flex items-center justify-center min-h-[500px]">
|
||||||
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
<SignupClientPage />
|
||||||
|
</Suspense>
|
||||||
|
|
||||||
|
<p className="text-center text-sm text-gray-500 mt-6">
|
||||||
|
By signing up, you agree to our{' '}
|
||||||
|
<Link href="/privacy" className="text-primary-600 hover:text-primary-700">
|
||||||
|
Privacy Policy
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<SignupClientPage />
|
</div>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -7,6 +7,7 @@ import { websiteSchema, breadcrumbSchema } from '@/lib/schema';
|
||||||
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/Card';
|
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/Card';
|
||||||
import { Badge } from '@/components/ui/Badge';
|
import { Badge } from '@/components/ui/Badge';
|
||||||
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
|
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
|
||||||
|
import { blogPostList } from '@/lib/blog-data';
|
||||||
|
|
||||||
function truncateAtWord(text: string, maxLength: number): string {
|
function truncateAtWord(text: string, maxLength: number): string {
|
||||||
if (text.length <= maxLength) return text;
|
if (text.length <= maxLength) return text;
|
||||||
|
|
@ -37,6 +38,14 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||||
description,
|
description,
|
||||||
url: 'https://www.qrmaster.net/blog',
|
url: 'https://www.qrmaster.net/blog',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: 'https://www.qrmaster.net/og-image.png',
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: 'QR Insights - QR Code Marketing & Analytics Blog',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title,
|
title,
|
||||||
|
|
@ -45,82 +54,7 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const blogPosts = [
|
|
||||||
// NEW POSTS (January 2026)
|
|
||||||
{
|
|
||||||
slug: 'qr-code-restaurant-menu',
|
|
||||||
title: 'How to Create a QR Code for Restaurant Menu',
|
|
||||||
excerpt: 'Step-by-step guide to creating digital menu QR codes for your restaurant. Learn best practices for touchless menus, placement tips, and tracking.',
|
|
||||||
date: 'January 5, 2026',
|
|
||||||
readTime: '12 Min',
|
|
||||||
category: 'Restaurant',
|
|
||||||
image: '/blog/restaurant-qr-menu.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'vcard-qr-code-generator',
|
|
||||||
title: 'Free vCard QR Code Generator: Digital Business Cards',
|
|
||||||
excerpt: 'Create professional vCard QR codes for digital business cards. Share contact info instantly with a scan—includes templates and best practices.',
|
|
||||||
date: 'January 5, 2026',
|
|
||||||
readTime: '10 Min',
|
|
||||||
category: 'Business Cards',
|
|
||||||
image: '/blog/vcard-qr-code.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'qr-code-small-business',
|
|
||||||
title: 'Best QR Code Generator for Small Business: 2025 Guide',
|
|
||||||
excerpt: 'Find the best QR code solution for your small business. Compare features, pricing, and use cases for marketing, payments, and operations.',
|
|
||||||
date: 'January 5, 2026',
|
|
||||||
readTime: '14 Min',
|
|
||||||
category: 'Business',
|
|
||||||
image: '/blog/small-business-qr.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'qr-code-print-size-guide',
|
|
||||||
title: 'QR Code Print Size Guide: Minimum Sizes for Every Use Case',
|
|
||||||
excerpt: 'Complete guide to QR code print sizes. Learn minimum dimensions for business cards, posters, banners, and more to ensure reliable scanning.',
|
|
||||||
date: 'January 5, 2026',
|
|
||||||
readTime: '8 Min',
|
|
||||||
category: 'Printing',
|
|
||||||
image: '/blog/qr-print-sizes.png',
|
|
||||||
},
|
|
||||||
// EXISTING POSTS
|
|
||||||
{
|
|
||||||
slug: 'qr-code-tracking-guide-2025',
|
|
||||||
title: 'QR Code Tracking: Complete Guide 2025',
|
|
||||||
excerpt: 'Learn how to track QR code scans with real-time analytics. Compare free vs paid tracking tools, setup Google Analytics, and measure ROI.',
|
|
||||||
date: 'October 18, 2025',
|
|
||||||
readTime: '12 Min',
|
|
||||||
category: 'Tracking & Analytics',
|
|
||||||
image: '/blog/1-hero.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'dynamic-vs-static-qr-codes',
|
|
||||||
title: 'Dynamic vs Static QR Codes: Which Should You Use?',
|
|
||||||
excerpt: 'Understand the difference between static and dynamic QR codes. Learn when to use each type, pros/cons, and how dynamic QR codes save money.',
|
|
||||||
date: 'October 17, 2025',
|
|
||||||
readTime: '10 Min',
|
|
||||||
category: 'QR Code Basics',
|
|
||||||
image: '/blog/2-hero.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'bulk-qr-code-generator-excel',
|
|
||||||
title: 'How to Generate Bulk QR Codes from Excel',
|
|
||||||
excerpt: 'Generate hundreds of QR codes from Excel or CSV files in minutes. Step-by-step guide with templates, best practices, and free tools.',
|
|
||||||
date: 'October 16, 2025',
|
|
||||||
readTime: '13 Min',
|
|
||||||
category: 'Bulk Generation',
|
|
||||||
image: '/blog/3-hero.png',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
slug: 'qr-code-analytics',
|
|
||||||
title: 'QR Code Analytics: Track, Measure & Optimize Campaigns',
|
|
||||||
excerpt: 'Learn how to leverage scan analytics, campaign tracking, and dashboard insights to maximize QR code ROI.',
|
|
||||||
date: 'October 16, 2025',
|
|
||||||
readTime: '15 Min',
|
|
||||||
category: 'Analytics',
|
|
||||||
image: '/blog/4-hero.png',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function BlogPage() {
|
export default function BlogPage() {
|
||||||
const breadcrumbItems: BreadcrumbItem[] = [
|
const breadcrumbItems: BreadcrumbItem[] = [
|
||||||
|
|
@ -145,7 +79,7 @@ export default function BlogPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto">
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto">
|
||||||
{blogPosts.map((post: any) => (
|
{blogPostList.map((post: any) => (
|
||||||
<Link key={post.slug} href={post.link || `/blog/${post.slug}`}>
|
<Link key={post.slug} href={post.link || `/blog/${post.slug}`}>
|
||||||
<Card hover className="h-full overflow-hidden shadow-md hover:shadow-xl transition-all duration-300">
|
<Card hover className="h-full overflow-hidden shadow-md hover:shadow-xl transition-all duration-300">
|
||||||
<div className="relative h-56 overflow-hidden">
|
<div className="relative h-56 overflow-hidden">
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,14 @@ export const metadata: Metadata = {
|
||||||
description: 'Generate hundreds of QR codes at once from CSV or Excel files. Perfect for products, events, and inventory.',
|
description: 'Generate hundreds of QR codes at once from CSV or Excel files. Perfect for products, events, and inventory.',
|
||||||
url: 'https://www.qrmaster.net/bulk-qr-code-generator',
|
url: 'https://www.qrmaster.net/bulk-qr-code-generator',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: 'https://www.qrmaster.net/og-image.png',
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: 'Bulk QR Code Generator - QR Master',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title: 'Bulk QR Code Generator - Create 1000s of QR Codes from Excel',
|
title: 'Bulk QR Code Generator - Create 1000s of QR Codes from Excel',
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,14 @@ export const metadata: Metadata = {
|
||||||
description: 'Create dynamic QR codes that can be edited after printing. Change URLs, track scans, and update content anytime.',
|
description: 'Create dynamic QR codes that can be edited after printing. Change URLs, track scans, and update content anytime.',
|
||||||
url: 'https://www.qrmaster.net/dynamic-qr-code-generator',
|
url: 'https://www.qrmaster.net/dynamic-qr-code-generator',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: 'https://www.qrmaster.net/og-image.png',
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: 'Dynamic QR Code Generator - QR Master',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title: 'Dynamic QR Code Generator - Edit QR Codes Anytime | QR Master',
|
title: 'Dynamic QR Code Generator - Edit QR Codes Anytime | QR Master',
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,14 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||||
description,
|
description,
|
||||||
url: 'https://www.qrmaster.net/faq',
|
url: 'https://www.qrmaster.net/faq',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: 'https://www.qrmaster.net/og-image.png',
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: 'QR Master FAQ',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title,
|
title,
|
||||||
|
|
|
||||||
|
|
@ -199,8 +199,8 @@ export default function NewsletterClient() {
|
||||||
<p className="text-muted-foreground text-sm">
|
<p className="text-muted-foreground text-sm">
|
||||||
Sign in to access admin panel
|
Sign in to access admin panel
|
||||||
</p>
|
</p>
|
||||||
<Link href="/" className="sr-only">
|
<Link href="/" className="text-sm text-slate-500 hover:text-slate-900 block mt-2">
|
||||||
Back to Home
|
← Back to Home
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,20 @@ export const metadata: Metadata = {
|
||||||
index: true,
|
index: true,
|
||||||
follow: true,
|
follow: true,
|
||||||
},
|
},
|
||||||
|
openGraph: {
|
||||||
|
title: 'Pricing Plans | QR Master',
|
||||||
|
description: 'Choose the perfect QR code plan for your needs.',
|
||||||
|
url: 'https://www.qrmaster.net/pricing',
|
||||||
|
type: 'website',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: 'https://www.qrmaster.net/og-image.png',
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: 'QR Master Pricing Plans',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PricingPage() {
|
export default function PricingPage() {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,23 @@ import Link from 'next/link';
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: 'Privacy Policy | QR Master',
|
title: 'Privacy Policy | QR Master',
|
||||||
description: 'Read our Privacy Policy to understand how QR Master collects, uses, and protects your data. We are committed to GDPR compliance and data security.',
|
description: 'Read our Privacy Policy to understand how QR Master collects, uses, and protects your data. We are committed to GDPR compliance and data security.',
|
||||||
|
alternates: {
|
||||||
|
canonical: 'https://www.qrmaster.net/privacy',
|
||||||
|
},
|
||||||
|
openGraph: {
|
||||||
|
title: 'Privacy Policy | QR Master',
|
||||||
|
description: 'Read our Privacy Policy to understand how QR Master collects, uses, and protects your data.',
|
||||||
|
url: 'https://www.qrmaster.net/privacy',
|
||||||
|
type: 'website',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: 'https://www.qrmaster.net/og-image.png',
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: 'QR Master Privacy Policy',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PrivacyPage() {
|
export default function PrivacyPage() {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
|
||||||
import { breadcrumbSchema } from '@/lib/schema';
|
import { breadcrumbSchema } from '@/lib/schema';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'QR Code Tracking & Analytics - Track Every Scan | QR Master',
|
title: 'QR Code Tracking & Analytics - Track Scans',
|
||||||
description: 'Track QR code scans with real-time analytics. Monitor location, device, time, and user behavior. Free QR code tracking software with detailed reports.',
|
description: 'Track QR code scans with real-time analytics. Monitor location, device, time, and user behavior. Free QR code tracking software with detailed reports.',
|
||||||
keywords: 'qr code tracking, qr code analytics, track qr scans, qr code statistics, free qr tracking, qr code monitoring',
|
keywords: 'qr code tracking, qr code analytics, track qr scans, qr code statistics, free qr tracking, qr code monitoring',
|
||||||
alternates: {
|
alternates: {
|
||||||
|
|
@ -23,6 +23,14 @@ export const metadata: Metadata = {
|
||||||
description: 'Track QR code scans with real-time analytics. Monitor location, device, time, and user behavior.',
|
description: 'Track QR code scans with real-time analytics. Monitor location, device, time, and user behavior.',
|
||||||
url: 'https://www.qrmaster.net/qr-code-tracking',
|
url: 'https://www.qrmaster.net/qr-code-tracking',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: 'https://www.qrmaster.net/og-image.png',
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: 'QR Code Tracking & Analytics - QR Master',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title: 'QR Code Tracking & Analytics - Track Every Scan | QR Master',
|
title: 'QR Code Tracking & Analytics - Track Every Scan | QR Master',
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Bitcoin, Shield, Zap, Smartphone, Wallet, Coins, Sparkles, Download, Sh
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,12 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Crypto QR Code Generator',
|
||||||
name: 'Crypto QR Code Generator',
|
'Generate QR codes that contain your cryptocurrency wallet address for easy payments.',
|
||||||
applicationCategory: 'FinanceApplication',
|
'/og-crypto-generator.png',
|
||||||
operatingSystem: 'Web Browser',
|
'FinanceApplication'
|
||||||
offers: {
|
),
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.9',
|
|
||||||
ratingCount: '870',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that contain your cryptocurrency wallet address for easy payments.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a Crypto QR Code',
|
name: 'How to Create a Crypto QR Code',
|
||||||
|
|
@ -93,51 +83,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Is it safe to share my wallet address?': {
|
||||||
mainEntity: [
|
question: 'Is it safe to share my wallet address?',
|
||||||
{
|
answer: 'Yes. Your public wallet address is designed to be shared so you can receive funds. Never share your private key.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is it safe to share my wallet address?',
|
'Which currencies are supported?': {
|
||||||
acceptedAnswer: {
|
question: 'Which currencies are supported?',
|
||||||
'@type': 'Answer',
|
answer: 'Our generator supports standard URI schemes for Bitcoin, Ethereum, Solana, and can generally store any wallet string for other coins.',
|
||||||
text: 'Yes. Your public wallet address is designed to be shared so you can receive funds. Never share your private key.',
|
},
|
||||||
},
|
'Can I add a specific amount?': {
|
||||||
},
|
question: 'Can I add a specific amount?',
|
||||||
{
|
answer: 'Yes, you can pre-fill an amount so when the user scans, their wallet app automatically suggests the correct payment value.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Which currencies are supported?',
|
'Does it work with all wallets?': {
|
||||||
acceptedAnswer: {
|
question: 'Does it work with all wallets?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes, standard crypto QR codes are universally readable by almost all modern wallet apps (Coinbase, MetaMask, Trust Wallet, etc.).',
|
||||||
text: 'Our generator supports standard URI schemes for Bitcoin, Ethereum, Solana, and can generally store any wallet string for other coins.',
|
},
|
||||||
},
|
'Are there any fees?': {
|
||||||
},
|
question: 'Are there any fees?',
|
||||||
{
|
answer: 'No. This generator is completely free. We do not charge any fees for generating codes or for the transactions made using them.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Can I add a specific amount?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, you can pre-fill an amount so when the user scans, their wallet app automatically suggests the correct payment value.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Does it work with all wallets?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, standard crypto QR codes are universally readable by almost all modern wallet apps (Coinbase, MetaMask, Trust Wallet, etc.).',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Are there any fees?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'No. This generator is completely free. We do not charge any fees for generating codes or for the transactions made using them.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Mail, Zap, Smartphone, Lock, Download, Sparkles } from 'lucide-react';
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -29,14 +30,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Email QR Code Generator',
|
||||||
name: 'Email QR Code Generator',
|
'Generate Email QR codes for mailto links with subject and body.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-email-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
|
||||||
description: 'Generate Email QR codes for mailto links with subject and body.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create an Email QR Code',
|
name: 'How to Create an Email QR Code',
|
||||||
|
|
@ -49,36 +47,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'How does it work?': {
|
||||||
mainEntity: [
|
question: 'How does it work?',
|
||||||
{
|
answer: 'When scanned, it opens the user\'s default email app (like Gmail or Outlook) with a new draft composed to your address.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'How does it work?',
|
'Can I add a subject line?': {
|
||||||
acceptedAnswer: { '@type': 'Answer', text: 'When scanned, it opens the user\'s default email app (like Gmail or Outlook) with a new draft composed to your address.' }
|
question: 'Can I add a subject line?',
|
||||||
},
|
answer: 'Yes! You can pre-fill the subject line and the body content so the sender just has to hit send.',
|
||||||
{
|
},
|
||||||
'@type': 'Question',
|
'Is it free?': {
|
||||||
name: 'Can I add a subject line?',
|
question: 'Is it free?',
|
||||||
acceptedAnswer: { '@type': 'Answer', text: 'Yes! You can pre-fill the subject line and the body content so the sender just has to hit send.' }
|
answer: 'Yes, 100% free with unlimited scans.',
|
||||||
},
|
},
|
||||||
{
|
'Does it work with attachments?': {
|
||||||
'@type': 'Question',
|
question: 'Does it work with attachments?',
|
||||||
name: 'Is it free?',
|
answer: 'No. The standard mailto format does not support attaching files automatically. Users will have to attach files manually.',
|
||||||
acceptedAnswer: { '@type': 'Answer', text: 'Yes, 100% free with unlimited scans.' }
|
},
|
||||||
},
|
'Is it private?': {
|
||||||
{
|
question: 'Is it private?',
|
||||||
'@type': 'Question',
|
answer: 'Yes. The data is encoded directly into the QR code. We do not store your email or message data.',
|
||||||
name: 'Does it work with attachments?',
|
},
|
||||||
acceptedAnswer: { '@type': 'Answer', text: 'No. The standard mailto format does not support attaching files automatically. Users will have to attach files manually.' }
|
}),
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is it private?',
|
|
||||||
acceptedAnswer: { '@type': 'Answer', text: 'Yes. The data is encoded directly into the QR code. We do not store your email or message data.' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Calendar, Shield, Zap, Smartphone, Clock, UserCheck, Download, Share2,
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Event QR Code Generator',
|
||||||
name: 'Event QR Code Generator',
|
'Generate QR codes that add event details to the user\'s digital calendar.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-event-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.8',
|
|
||||||
ratingCount: '760',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that add event details to the user\'s digital calendar.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create an Event QR Code',
|
name: 'How to Create an Event QR Code',
|
||||||
|
|
@ -93,51 +82,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT45S',
|
totalTime: 'PT45S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Which calendars does it support?': {
|
||||||
mainEntity: [
|
question: 'Which calendars does it support?',
|
||||||
{
|
answer: 'The QR code uses the standard iCalendar (ICS) format. It works with Apple Calendar, Google Calendar, Outlook, and most other mobile calendar apps.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Which calendars does it support?',
|
'Can I change the date after printing?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I change the date after printing?',
|
||||||
'@type': 'Answer',
|
answer: 'No. This is a static QR code, meaning the event details are permanently embedded in the image. If the date changes, you must create a new QR code. Use our Dynamic QR Code to edit events anytime.',
|
||||||
text: 'The QR code uses the standard iCalendar (ICS) format. It works with Apple Calendar, Google Calendar, Outlook, and most other mobile calendar apps.',
|
},
|
||||||
},
|
'Is there a limit to the description length?': {
|
||||||
},
|
question: 'Is there a limit to the description length?',
|
||||||
{
|
answer: 'Yes, because the data is stored in the QR code pattern. We recommend keeping descriptions concise (under 300 characters) to ensure the code remains easy to scan.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Can I change the date after printing?',
|
'Do users need an app?': {
|
||||||
acceptedAnswer: {
|
question: 'Do users need an app?',
|
||||||
'@type': 'Answer',
|
answer: 'No special app is needed. Standard camera apps on iOS and Android can read the code and will prompt the user to "Add to Calendar".',
|
||||||
text: 'No. This is a static QR code, meaning the event details are permanently embedded in the image. If the date changes, you must create a new QR code. Use our Dynamic QR Code to edit events anytime.',
|
},
|
||||||
},
|
'Is it free?': {
|
||||||
},
|
question: 'Is it free?',
|
||||||
{
|
answer: 'Yes. Creating and scanning the code is completely free and requires no signup.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is there a limit to the description length?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, because the data is stored in the QR code pattern. We recommend keeping descriptions concise (under 300 characters) to ensure the code remains easy to scan.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Do users need an app?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'No special app is needed. Standard camera apps on iOS and Android can read the code and will prompt the user to "Add to Calendar".',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is it free?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes. Creating and scanning the code is completely free and requires no signup.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Facebook, Shield, Zap, Smartphone, ThumbsUp, Users, Download, Share2 }
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Facebook QR Code Generator',
|
||||||
name: 'Facebook QR Code Generator',
|
'Generate QR codes that direct users to a Facebook page, profile, or post.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-facebook-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.8',
|
|
||||||
ratingCount: '1120',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that direct users to a Facebook page, profile, or post.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a Facebook QR Code',
|
name: 'How to Create a Facebook QR Code',
|
||||||
|
|
@ -93,51 +82,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Does it open the Facebook app?': {
|
||||||
mainEntity: [
|
question: 'Does it open the Facebook app?',
|
||||||
{
|
answer: 'Yes! On most mobile devices, standard Facebook links are automatically detected and opened in the Facebook app if it is installed.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it open the Facebook app?',
|
'Can I link to a specific post?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I link to a specific post?',
|
||||||
'@type': 'Answer',
|
answer: 'Absolutely. Just paste the direct link to the post (click the timestamp on the post to get the link).',
|
||||||
text: 'Yes! On most mobile devices, standard Facebook links are automatically detected and opened in the Facebook app if it is installed.',
|
},
|
||||||
},
|
'Does it work for Facebook Events?': {
|
||||||
},
|
question: 'Does it work for Facebook Events?',
|
||||||
{
|
answer: 'Yes. Simply copy the full URL of your Facebook Event and paste it into the generator.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Can I link to a specific post?',
|
'Is it free?': {
|
||||||
acceptedAnswer: {
|
question: 'Is it free?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes, this generator is 100% free to use for personal or business purposes.',
|
||||||
text: 'Absolutely. Just paste the direct link to the post (click the timestamp on the post to get the link).',
|
},
|
||||||
},
|
'Can I track scans?': {
|
||||||
},
|
question: 'Can I track scans?',
|
||||||
{
|
answer: 'This static QR code does not include analytics. To track how many people scan your code, you should use our Dynamic QR Code service.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it work for Facebook Events?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes. Simply copy the full URL of your Facebook Event and paste it into the generator.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is it free?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, this generator is 100% free to use for personal or business purposes.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I track scans?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'This static QR code does not include analytics. To track how many people scan your code, you should use our Dynamic QR Code service.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { MapPin, Shield, Zap, Smartphone, Navigation, Map, Download, Share2 } fr
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Geolocation QR Code Generator',
|
||||||
name: 'Geolocation QR Code Generator',
|
'Generate QR codes that open specific geographic coordinates in map applications.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-geolocation-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.7',
|
|
||||||
ratingCount: '890',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that open specific geographic coordinates in map applications.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a Location QR Code',
|
name: 'How to Create a Location QR Code',
|
||||||
|
|
@ -93,51 +82,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT45S',
|
totalTime: 'PT45S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Which map app does it open?': {
|
||||||
mainEntity: [
|
question: 'Which map app does it open?',
|
||||||
{
|
answer: 'Our generator creates a universal Google Maps link. On most devices, this will open the Google Maps app if installed, or the browser version if not. It is the most compatible method.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Which map app does it open?',
|
'How do I find my Latitude and Longitude?': {
|
||||||
acceptedAnswer: {
|
question: 'How do I find my Latitude and Longitude?',
|
||||||
'@type': 'Answer',
|
answer: 'On Google Maps desktop: Right-click any spot on the map. The first item in the menu is the coordinates. Click to copy them.',
|
||||||
text: 'Our generator creates a universal Google Maps link. On most devices, this will open the Google Maps app if installed, or the browser version if not. It is the most compatible method.',
|
},
|
||||||
},
|
'Does it work offline?': {
|
||||||
},
|
question: 'Does it work offline?',
|
||||||
{
|
answer: 'The QR code itself can be scanned offline, but the user will likely need an internet connection to load the map and get directions.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'How do I find my Latitude and Longitude?',
|
'Can I use an address instead?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I use an address instead?',
|
||||||
'@type': 'Answer',
|
answer: 'For precise results, we use coordinates. However, you can use our URL Generator and paste a link to your Google Maps address search result if you prefer.',
|
||||||
text: 'On Google Maps desktop: Right-click any spot on the map. The first item in the menu is the coordinates. Click to copy them.',
|
},
|
||||||
},
|
'Is it free?': {
|
||||||
},
|
question: 'Is it free?',
|
||||||
{
|
answer: 'Yes, generating this location QR code is completely free and requires no signup.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it work offline?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'The QR code itself can be scanned offline, but the user will likely need an internet connection to load the map and get directions.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I use an address instead?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'For precise results, we use coordinates. However, you can use our URL Generator and paste a link to your Google Maps address search result if you prefer.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is it free?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, generating this location QR code is completely free and requires no signup.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Instagram, Shield, Zap, Smartphone, Camera, Heart, Download, Share2 } f
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Instagram QR Code Generator',
|
||||||
name: 'Instagram QR Code Generator',
|
'Generate QR codes that direct users to an Instagram profile or post.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-instagram-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.9',
|
|
||||||
ratingCount: '2150',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that direct users to an Instagram profile or post.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create an Instagram QR Code',
|
name: 'How to Create an Instagram QR Code',
|
||||||
|
|
@ -93,51 +82,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Is this an Instagram Nametag?': {
|
||||||
mainEntity: [
|
question: 'Is this an Instagram Nametag?',
|
||||||
{
|
answer: 'It works similarly! While Instagram has its own internal "Nametag" or "QR Code" feature, our generator allows you to create a standard QR code that is more customizable and can be tracked with our Dynamic plans.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is this an Instagram Nametag?',
|
'Does it open the Instagram app?': {
|
||||||
acceptedAnswer: {
|
question: 'Does it open the Instagram app?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes. When scanned on a mobile device with Instagram installed, it will deep-link directly to the profile in the app.',
|
||||||
text: 'It works similarly! While Instagram has its own internal "Nametag" or "QR Code" feature, our generator allows you to create a standard QR code that is more customizable and can be tracked with our Dynamic plans.',
|
},
|
||||||
},
|
'Can I link to a specific photo or reel?': {
|
||||||
},
|
question: 'Can I link to a specific photo or reel?',
|
||||||
{
|
answer: 'Yes! Instead of your username, just paste the full link to the specific post or reel.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it open the Instagram app?',
|
'Is it free?': {
|
||||||
acceptedAnswer: {
|
question: 'Is it free?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes, generating this QR code is 100% free.',
|
||||||
text: 'Yes. When scanned on a mobile device with Instagram installed, it will deep-link directly to the profile in the app.',
|
},
|
||||||
},
|
'Can I track scans?': {
|
||||||
},
|
question: 'Can I track scans?',
|
||||||
{
|
answer: 'Not with this static tool. If you need scan analytics, consider using our Dynamic QR Code solution.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Can I link to a specific photo or reel?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes! Instead of your username, just paste the full link to the specific post or reel.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is it free?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, generating this QR code is 100% free.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I track scans?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Not with this static tool. If you need scan analytics, consider using our Dynamic QR Code solution.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { CreditCard, Shield, Zap, Smartphone, DollarSign, Download, Share2, Bank
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,12 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'PayPal QR Code Generator',
|
||||||
name: 'PayPal QR Code Generator',
|
'Generate QR codes that link to your PayPal.me page for instant payments.',
|
||||||
applicationCategory: 'FinanceApplication',
|
'/og-paypal-generator.png',
|
||||||
operatingSystem: 'Web Browser',
|
'FinanceApplication'
|
||||||
offers: {
|
),
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.9',
|
|
||||||
ratingCount: '980',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that link to your PayPal.me page for instant payments.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a PayPal QR Code',
|
name: 'How to Create a PayPal QR Code',
|
||||||
|
|
@ -93,51 +83,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'How does the PayPal QR code work?': {
|
||||||
mainEntity: [
|
question: 'How does the PayPal QR code work?',
|
||||||
{
|
answer: 'When scanned, it opens the PayPal app or website with your PayPal.me link. If you set an amount, it will be pre-filled for the payer.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'How does the PayPal QR code work?',
|
'Do I need a PayPal Business account?': {
|
||||||
acceptedAnswer: {
|
question: 'Do I need a PayPal Business account?',
|
||||||
'@type': 'Answer',
|
answer: 'No. Any PayPal account with a PayPal.me link can use this generator. Personal accounts work fine for tips and donations.',
|
||||||
text: 'When scanned, it opens the PayPal app or website with your PayPal.me link. If you set an amount, it will be pre-filled for the payer.',
|
},
|
||||||
},
|
'Is there a fee for using the QR code?': {
|
||||||
},
|
question: 'Is there a fee for using the QR code?',
|
||||||
{
|
answer: 'This generator is 100% free. PayPal may charge their standard transaction fees when you receive payments.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Do I need a PayPal Business account?',
|
'Can I change the amount later?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I change the amount later?',
|
||||||
'@type': 'Answer',
|
answer: 'No, this is a static QR code. The amount is encoded permanently. For variable amounts, leave the amount field empty.',
|
||||||
text: 'No. Any PayPal account with a PayPal.me link can use this generator. Personal accounts work fine for tips and donations.',
|
},
|
||||||
},
|
'What currencies are supported?': {
|
||||||
},
|
question: 'What currencies are supported?',
|
||||||
{
|
answer: 'We support EUR, USD, GBP, and CHF. PayPal handles currency conversion automatically.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is there a fee for using the QR code?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'This generator is 100% free. PayPal may charge their standard transaction fees when you receive payments.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I change the amount later?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'No, this is a static QR code. The amount is encoded permanently. For variable amounts, leave the amount field empty.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'What currencies are supported?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'We support EUR, USD, GBP, and CHF. PayPal handles currency conversion automatically.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Phone, Shield, Zap, Smartphone, PhoneCall, Download } from 'lucide-reac
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Phone QR Code Generator',
|
||||||
name: 'Phone QR Code Generator',
|
'Generate QR codes that trigger a phone call when scanned on a mobile device.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-phone-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.8',
|
|
||||||
ratingCount: '1500',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that trigger a phone call when scanned on a mobile device.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a Phone QR Code',
|
name: 'How to Create a Phone QR Code',
|
||||||
|
|
@ -93,51 +82,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Does it call automatically?': {
|
||||||
mainEntity: [
|
question: 'Does it call automatically?',
|
||||||
{
|
answer: 'Scanning the QR code opens the phone dialer with the number pre-filled. The user must tap the call button to initiate the call.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it call automatically?',
|
'Does it work internationally?': {
|
||||||
acceptedAnswer: {
|
question: 'Does it work internationally?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes! We recommend entering your number in international format (starting with +) to ensure it works anywhere in the world.',
|
||||||
text: 'Scanning the QR code opens the phone dialer with the number pre-filled. The user must tap the call button to initiate the call.',
|
},
|
||||||
},
|
'Is my phone number private?': {
|
||||||
},
|
question: 'Is my phone number private?',
|
||||||
{
|
answer: 'Yes. We do not store your number. It is encoded directly into the QR code image.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it work internationally?',
|
'Can I track calls?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I track calls?',
|
||||||
'@type': 'Answer',
|
answer: 'This static QR code cannot track calls. For tracking scans and analytics, consider using our Dynamic QR Code solution.',
|
||||||
text: 'Yes! We recommend entering your number in international format (starting with +) to ensure it works anywhere in the world.',
|
},
|
||||||
},
|
'Is it free?': {
|
||||||
},
|
question: 'Is it free?',
|
||||||
{
|
answer: 'Yes, completely free. We do not charge for generating or scanning the code.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is my phone number private?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes. We do not store your number. It is encoded directly into the QR code image.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I track calls?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'This static QR code cannot track calls. For tracking scans and analytics, consider using our Dynamic QR Code solution.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is it free?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, completely free. We do not charge for generating or scanning the code.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { MessageSquare, Shield, Zap, Smartphone, Send } from 'lucide-react';
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'SMS QR Code Generator',
|
||||||
name: 'SMS QR Code Generator',
|
'Generate QR codes that open the user\'s SMS app with a pre-filled message.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-sms-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.8',
|
|
||||||
ratingCount: '1350',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that open the user\'s SMS app with a pre-filled message.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create an SMS QR Code',
|
name: 'How to Create an SMS QR Code',
|
||||||
|
|
@ -81,43 +70,24 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Does the text send automatically?': {
|
||||||
mainEntity: [
|
question: 'Does the text send automatically?',
|
||||||
{
|
answer: 'No. The QR code opens the messaging app with the text typed out. The user must simply tap "Send". This is a security feature of all smartphones.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does the text send automatically?',
|
'Is there a cost?': {
|
||||||
acceptedAnswer: {
|
question: 'Is there a cost?',
|
||||||
'@type': 'Answer',
|
answer: 'Generating the code is free. Standard SMS rates apply for the person sending the text message, depending on their carrier plan.',
|
||||||
text: 'No. The QR code opens the messaging app with the text typed out. The user must simply tap "Send". This is a security feature of all smartphones.',
|
},
|
||||||
},
|
'Can I change the message later?': {
|
||||||
},
|
question: 'Can I change the message later?',
|
||||||
{
|
answer: 'No. Static QR codes have the message embedded in them. To change the message, you need a new QR code.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is there a cost?',
|
'What uses are there for SMS QR codes?': {
|
||||||
acceptedAnswer: {
|
question: 'What uses are there for SMS QR codes?',
|
||||||
'@type': 'Answer',
|
answer: 'They are great for SMS marketing opt-ins ("Text JOIN to 12345"), customer support requests, or voting via text.',
|
||||||
text: 'Generating the code is free. Standard SMS rates apply for the person sending the text message, depending on their carrier plan.',
|
},
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I change the message later?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'No. Static QR codes have the message embedded in them. To change the message, you need a new QR code.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'What uses are there for SMS QR codes?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'They are great for SMS marketing opt-ins ("Text JOIN to 12345"), customer support requests, or voting via text.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Users, Shield, Zap, Video, MessageCircle, Download, Share2 } from 'luci
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,12 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Microsoft Teams QR Code Generator',
|
||||||
name: 'Microsoft Teams QR Code Generator',
|
'Generate QR codes that let people join your Microsoft Teams meeting with one scan.',
|
||||||
applicationCategory: 'BusinessApplication',
|
'/og-teams-generator.png',
|
||||||
operatingSystem: 'Web Browser',
|
'BusinessApplication'
|
||||||
offers: {
|
),
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.9',
|
|
||||||
ratingCount: '890',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that let people join your Microsoft Teams meeting with one scan.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a Microsoft Teams QR Code',
|
name: 'How to Create a Microsoft Teams QR Code',
|
||||||
|
|
@ -87,43 +77,24 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'What happens when someone scans the QR code?': {
|
||||||
mainEntity: [
|
question: 'What happens when someone scans the QR code?',
|
||||||
{
|
answer: 'Microsoft Teams opens and the user is prompted to join the meeting. Works on desktop, mobile, and web.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'What happens when someone scans the QR code?',
|
'Does it work for recurring meetings?': {
|
||||||
acceptedAnswer: {
|
question: 'Does it work for recurring meetings?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes! If your recurring meeting uses the same meeting link, the QR code will work for all sessions.',
|
||||||
text: 'Microsoft Teams opens and the user is prompted to join the meeting. Works on desktop, mobile, and web.',
|
},
|
||||||
},
|
'Can guests without Teams accounts join?': {
|
||||||
},
|
question: 'Can guests without Teams accounts join?',
|
||||||
{
|
answer: 'Yes. Guests can join Teams meetings via the web browser without needing a Microsoft account.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it work for recurring meetings?',
|
'Is this for personal or business Teams?': {
|
||||||
acceptedAnswer: {
|
question: 'Is this for personal or business Teams?',
|
||||||
'@type': 'Answer',
|
answer: 'Both! Works with Microsoft Teams for work, school, and personal accounts.',
|
||||||
text: 'Yes! If your recurring meeting uses the same meeting link, the QR code will work for all sessions.',
|
},
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can guests without Teams accounts join?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes. Guests can join Teams meetings via the web browser without needing a Microsoft account.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is this for personal or business Teams?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Both! Works with Microsoft Teams for work, school, and personal accounts.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Type, Shield, Zap, Smartphone, FileText, QrCode, Download, Share2 } fro
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Text QR Code Generator',
|
||||||
name: 'Text QR Code Generator',
|
'Generate QR codes for plain text messages. Works offline once generated. No data collection.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-text-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.8',
|
|
||||||
ratingCount: '1240',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes for plain text messages. Works offline once generated. No data collection.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a Text QR Code',
|
name: 'How to Create a Text QR Code',
|
||||||
|
|
@ -93,51 +82,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Is there a character limit?': {
|
||||||
mainEntity: [
|
question: 'Is there a character limit?',
|
||||||
{
|
answer: 'Yes, we recommend keeping it under 300 characters for optimal scanning. While QR codes can hold more, more text makes the code denser and harder to scan.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is there a character limit?',
|
'Do I need internet to scan a Text QR code?': {
|
||||||
acceptedAnswer: {
|
question: 'Do I need internet to scan a Text QR code?',
|
||||||
'@type': 'Answer',
|
answer: 'No. Text QR codes work completely offline. The text content is embedded directly into the QR code pattern.',
|
||||||
text: 'Yes, we recommend keeping it under 300 characters for optimal scanning. While QR codes can hold more, more text makes the code denser and harder to scan.',
|
},
|
||||||
},
|
'Is my text private?': {
|
||||||
},
|
question: 'Is my text private?',
|
||||||
{
|
answer: 'Yes. This generator runs 100% in your browser. We do not store or see the text you type.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Do I need internet to scan a Text QR code?',
|
'How do I scan a text QR code?': {
|
||||||
acceptedAnswer: {
|
question: 'How do I scan a text QR code?',
|
||||||
'@type': 'Answer',
|
answer: 'Open your phone camera or a QR scanner app and point it at the code. The text will appear on your screen automatically.',
|
||||||
text: 'No. Text QR codes work completely offline. The text content is embedded directly into the QR code pattern.',
|
},
|
||||||
},
|
'Can I edit the text later?': {
|
||||||
},
|
question: 'Can I edit the text later?',
|
||||||
{
|
answer: 'No, this is a static QR code. The text is permanent. If you need to change it, you must create a new QR code.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is my text private?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes. This generator runs 100% in your browser. We do not store or see the text you type.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'How do I scan a text QR code?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Open your phone camera or a QR scanner app and point it at the code. The text will appear on your screen automatically.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I edit the text later?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'No, this is a static QR code. The text is permanent. If you need to change it, you must create a new QR code.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Music, Shield, Zap, Smartphone, Video, Heart, Download, Share2 } from '
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'TikTok QR Code Generator',
|
||||||
name: 'TikTok QR Code Generator',
|
'Generate QR codes that direct users to a TikTok profile.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-tiktok-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.9',
|
|
||||||
ratingCount: '1560',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that direct users to a TikTok profile.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a TikTok QR Code',
|
name: 'How to Create a TikTok QR Code',
|
||||||
|
|
@ -93,51 +82,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Does this replace the in-app QR code?': {
|
||||||
mainEntity: [
|
question: 'Does this replace the in-app QR code?',
|
||||||
{
|
answer: 'You can use either! The advantage of our generator is that you can print high-resolution versions for large posters, customize the color/frame, and it works with any standard QR scanner.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it open the TikTok app?',
|
'Can I link to a specific video?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I link to a specific video?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes, just paste the full video URL (e.g. tiktok.com/@user/video/123...) instead of your username.',
|
||||||
text: 'Yes! If the app is installed, the QR code will deep-link directly to your profile in the TikTok app.',
|
},
|
||||||
},
|
'Is it free?': {
|
||||||
},
|
question: 'Is it free?',
|
||||||
{
|
answer: 'Yes, completely free from start to finish.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'What is a TikCode?',
|
'Can I track who scanned my code?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I track who scanned my code?',
|
||||||
'@type': 'Answer',
|
answer: 'No, this is a static QR code. For analytics, you need a Dynamic QR Code.',
|
||||||
text: 'TikCode was TikTok\'s proprietary QR code system. They have moved towards standard QR codes, which is what our tool generates. These are more compatible with standard camera apps.',
|
},
|
||||||
},
|
'Is it safe?': {
|
||||||
},
|
question: 'Is it safe?',
|
||||||
{
|
answer: 'Yes. The QR code simply contains a link to your TikTok profile. No personal data is collected.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is it free?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, this generator is completely free.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I track who scanned my code?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'No, this is a static QR code. For analytics, you need a Dynamic QR Code.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is it safe?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes. The QR code simply contains a link to your TikTok profile. No personal data is collected.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Twitter, Shield, Zap, Smartphone, MessageCircle, UserPlus, Download, Sh
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Twitter (X) QR Code Generator',
|
||||||
name: 'Twitter (X) QR Code Generator',
|
'Generate QR codes that direct users to an X (Twitter) profile or tweet.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-twitter-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.8',
|
|
||||||
ratingCount: '980',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that direct users to an X (Twitter) profile or tweet.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a Twitter QR Code',
|
name: 'How to Create a Twitter QR Code',
|
||||||
|
|
@ -93,51 +82,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Does it work for both Twitter and X?': {
|
||||||
mainEntity: [
|
question: 'Does it work for both Twitter and X?',
|
||||||
{
|
answer: 'Yes, they are the same platform. The QR code links to x.com, which is the current standard, but works for twitter.com links too.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it work for both Twitter and X?',
|
'Can I link to a specific tweet?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I link to a specific tweet?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes! Just paste the full URL of the tweet into the input field instead of your username.',
|
||||||
text: 'Yes, they are the same platform. The QR code links to x.com, which is the current standard, but works for twitter.com links too.',
|
},
|
||||||
},
|
'Is it free?': {
|
||||||
},
|
question: 'Is it free?',
|
||||||
{
|
answer: 'Yes, generating this QR code is completely free and requires no signup.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Can I link to a specific tweet?',
|
'Can I track scans?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I track scans?',
|
||||||
'@type': 'Answer',
|
answer: 'This is a static QR code, so tracking is not included. Use our Dynamic QR Code generator for analytics.',
|
||||||
text: 'Yes! Just paste the full URL of the tweet into the input field instead of your username.',
|
},
|
||||||
},
|
'What if I change my handle?': {
|
||||||
},
|
question: 'What if I change my handle?',
|
||||||
{
|
answer: 'If you change your handle, the link in the QR code will break. You will need to generate a new QR code.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is it free?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, generating this QR code is completely free and requires no signup.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I track scans?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'This is a static QR code, so tracking is not included. Use our Dynamic QR Code generator for analytics.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'What if I change my handle?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'If you change your handle, the link in the QR code will break. You will need to generate a new QR code.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Link as LinkIcon, Shield, Zap, Smartphone, Globe } from 'lucide-react';
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'URL QR Code Generator',
|
||||||
name: 'URL QR Code Generator',
|
'Generate QR codes for URLs and websites. Direct linking, no redirects.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-url-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.9',
|
|
||||||
ratingCount: '3100',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes for URLs and websites. Direct linking, no redirects.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a URL QR Code',
|
name: 'How to Create a URL QR Code',
|
||||||
|
|
@ -81,43 +70,24 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT20S',
|
totalTime: 'PT20S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Do these QR codes expire?': {
|
||||||
mainEntity: [
|
question: 'Do these QR codes expire?',
|
||||||
{
|
answer: 'No. These are static QR codes. They directly encode your URL and will work forever as long as your website is online.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Do these QR codes expire?',
|
'Can I track how many people scan it?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I track how many people scan it?',
|
||||||
'@type': 'Answer',
|
answer: 'No, static QR codes cannot be tracked. If you need scan usage analytics (location, device, time), you should use our Dynamic QR Code generator.',
|
||||||
text: 'No. These are static QR codes. They directly encode your URL and will work forever as long as your website is online.',
|
},
|
||||||
},
|
'Can I change the destination URL later?': {
|
||||||
},
|
question: 'Can I change the destination URL later?',
|
||||||
{
|
answer: 'No. Once a static QR code is printed, it cannot be changed. If you change your website URL, you will need to print a new code. Use Dynamic QR Codes if you need flexibility.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Can I track how many people scan it?',
|
'Is there a scan limit?': {
|
||||||
acceptedAnswer: {
|
question: 'Is there a scan limit?',
|
||||||
'@type': 'Answer',
|
answer: 'No. There are zero limits on how many times your QR code can be scanned.',
|
||||||
text: 'No, static QR codes cannot be tracked. If you need scan usage analytics (location, device, time), you should use our Dynamic QR Code generator.',
|
},
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I change the destination URL later?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'No. Once a static QR code is printed, it cannot be changed. If you change your website URL, you will need to print a new code. Use Dynamic QR Codes if you need flexibility.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is there a scan limit?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'No. There are zero limits on how many times your QR code can be scanned.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,12 @@ import { User, Shield, Zap, Smartphone, Contact, Share2, Check, UserPlus } from
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: {
|
title: {
|
||||||
absolute: 'Free vCard QR Code Generator | Digitale Visitenkarte Erstellen | QR Master',
|
absolute: 'vCard QR Code Generator | Digitale Visitenkarte | QR Master',
|
||||||
},
|
},
|
||||||
description: 'Create a vCard QR code for your business card. Erstelle deine elektronische Visitenkarte kostenlos. Share contact details instantly. Free & No App Required.',
|
description: 'Create a vCard QR code for your business card. Erstelle deine elektronische Visitenkarte kostenlos. Share contact details instantly. Free & No App Required.',
|
||||||
keywords: ['vcard qr code', 'business card qr code', 'contact qr generator', 'digital business card', 'add to contacts qr', 'visitenkarte qr code', 'digitale visitenkarte erstellen', 'kontakt qr code', 'elektronische visitenkarte', 'vcard erstellen kostenlos'],
|
keywords: ['vcard qr code', 'business card qr code', 'contact qr generator', 'digital business card', 'add to contacts qr', 'visitenkarte qr code', 'digitale visitenkarte erstellen', 'kontakt qr code', 'elektronische visitenkarte', 'vcard erstellen kostenlos'],
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'vCard QR Code Generator',
|
||||||
name: 'vCard QR Code Generator',
|
'Generate vCard (VCF) QR codes for business cards. Scanners can save contact info instantly.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-vcard-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.9',
|
|
||||||
ratingCount: '4200',
|
|
||||||
},
|
|
||||||
description: 'Generate vCard (VCF) QR codes for business cards. Scanners can save contact info instantly.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a vCard QR Code',
|
name: 'How to Create a vCard QR Code',
|
||||||
|
|
@ -81,43 +70,24 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT1M',
|
totalTime: 'PT1M',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'How does a vCard QR code work?': {
|
||||||
mainEntity: [
|
question: 'How does a vCard QR code work?',
|
||||||
{
|
answer: 'A vCard QR code contains your contact information in a standardized format (VCF). When scanned, the phone recognizes it as a contact card and prompts the user to "Save Contact" to their address book.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'How does a vCard QR code work?',
|
'Is there a limit to how much info I can add?': {
|
||||||
acceptedAnswer: {
|
question: 'Is there a limit to how much info I can add?',
|
||||||
'@type': 'Answer',
|
answer: 'Static QR codes hold data directly in the pattern. The more data you add (long addresses, bio), the denser and harder to scan the QR code becomes. We recommend sticking to essential contact info for static codes.',
|
||||||
text: 'A vCard QR code contains your contact information in a standardized format (VCF). When scanned, the phone recognizes it as a contact card and prompts the user to "Save Contact" to their address book.',
|
},
|
||||||
},
|
'Can I update my info later?': {
|
||||||
},
|
question: 'Can I update my info later?',
|
||||||
{
|
answer: 'No. This is a static vCard QR code. Once created, the info cannot be changed. If you move jobs or change numbers, you must print a new code. For editable cards, use our Dynamic vCard Plus.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is there a limit to how much info I can add?',
|
'Does it work on iPhone and Android?': {
|
||||||
acceptedAnswer: {
|
question: 'Does it work on iPhone and Android?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes. Both iOS (Camera app) and Android (Camera or Google Lens) natively support vCard QR codes and correctly import the contact data.',
|
||||||
text: 'Static QR codes hold data directly in the pattern. The more data you add (long addresses, bio), the denser and harder to scan the QR code becomes. We recommend sticking to essential contact info for static codes.',
|
},
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I update my info later?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'No. This is a static vCard QR code. Once created, the info cannot be changed. If you move jobs or change numbers, you must print a new code. For editable cards, use our Dynamic vCard Plus.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Does it work on iPhone and Android?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes. Both iOS (Camera app) and Android (Camera or Google Lens) natively support vCard QR codes and correctly import the contact data.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { MessageCircle, Shield, Zap, Smartphone, Send, Phone, Download, Check }
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'WhatsApp QR Code Generator',
|
||||||
name: 'WhatsApp QR Code Generator',
|
'Generate QR codes that start a WhatsApp conversation with a specific number.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-whatsapp-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.8',
|
|
||||||
ratingCount: '2300',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that start a WhatsApp conversation with a specific number.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a WhatsApp QR Code',
|
name: 'How to Create a WhatsApp QR Code',
|
||||||
|
|
@ -93,51 +82,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT45S',
|
totalTime: 'PT45S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Do users need to save my number first?': {
|
||||||
mainEntity: [
|
question: 'Do users need to save my number first?',
|
||||||
{
|
answer: 'No! That is the best part. Scanning the code opens the chat immediately without them needing to save you as a contact first.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Do users need to save my number first?',
|
'Can I use this for WhatsApp Business?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I use this for WhatsApp Business?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes, it works perfectly for both personal and business accounts.',
|
||||||
text: 'No! That is the best part. Scanning the code opens the chat immediately without them needing to save you as a contact first.',
|
},
|
||||||
},
|
'What is the Pre-filled Message?': {
|
||||||
},
|
question: 'What is the Pre-filled Message?',
|
||||||
{
|
answer: 'It is text that automatically appears in the user\'s typing field when they scan the code (e.g., "Hello, I want to order pizza"). They just have to hit send.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Can I use this for WhatsApp Business?',
|
'Is it free?': {
|
||||||
acceptedAnswer: {
|
question: 'Is it free?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes, this generator is completely free.',
|
||||||
text: 'Yes, it works perfectly for both personal and business accounts.',
|
},
|
||||||
},
|
'Can I track scans?': {
|
||||||
},
|
question: 'Can I track scans?',
|
||||||
{
|
answer: 'This is a static QR code, so tracking is not included. Use our Dynamic QR Code generator for analytics.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'What is the Pre-filled Message?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'It is text that automatically appears in the user\'s typing field when they scan the code (e.g., "Hello, I want to order pizza"). They just have to hit send.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is it free?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, this generator is completely free.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I track scans?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'This is a static QR code, so tracking is not included. Use our Dynamic QR Code generator for analytics.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Wifi, Shield, Zap, Smartphone, Lock, QrCode, Download, Share2 } from 'l
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -39,23 +40,11 @@ const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
// SoftwareApplication Schema
|
// SoftwareApplication Schema
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'WiFi QR Code Generator',
|
||||||
name: 'WiFi QR Code Generator',
|
'Generate QR codes for WiFi networks. Guests scan to connect without typing passwords.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-wifi-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.9',
|
|
||||||
ratingCount: '2847',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes for WiFi networks. Guests scan to connect without typing passwords.',
|
|
||||||
},
|
|
||||||
// HowTo Schema for Featured Snippets
|
// HowTo Schema for Featured Snippets
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
|
|
@ -96,51 +85,28 @@ const jsonLd = {
|
||||||
totalTime: 'PT1M',
|
totalTime: 'PT1M',
|
||||||
},
|
},
|
||||||
// FAQPage Schema
|
// FAQPage Schema
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Is it safe to enter my WiFi password?': {
|
||||||
mainEntity: [
|
question: 'Is it safe to enter my WiFi password?',
|
||||||
{
|
answer: 'Yes, completely safe. This tool processes everything in your browser (client-side). Your password never leaves your device and is not sent to any server.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Is it safe to enter my WiFi password?',
|
'Do WiFi QR codes work on iPhone and Android?': {
|
||||||
acceptedAnswer: {
|
question: 'Do WiFi QR codes work on iPhone and Android?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes. Both iOS (11+) and Android devices can scan WiFi QR codes using their built-in camera app. No additional apps required.',
|
||||||
text: 'Yes, completely safe. This tool processes everything in your browser (client-side). Your password never leaves your device and is not sent to any server.',
|
},
|
||||||
},
|
'What happens if I change my WiFi password?': {
|
||||||
},
|
question: 'What happens if I change my WiFi password?',
|
||||||
{
|
answer: 'If you change your WiFi password, the old QR code will stop working. You\'ll need to generate a new QR code with the updated credentials.For frequently changing passwords, consider using dynamic QR codes.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Do WiFi QR codes work on iPhone and Android?',
|
'Can I customize the QR code design?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I customize the QR code design?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes. You can change the QR code color and add frame labels like "Scan Me" or "WiFi" to make it more recognizable and user-friendly.',
|
||||||
text: 'Yes. Both iOS (11+) and Android devices can scan WiFi QR codes using their built-in camera app. No additional apps required.',
|
},
|
||||||
},
|
'Does it work for hidden networks?': {
|
||||||
},
|
question: 'Does it work for hidden networks?',
|
||||||
{
|
answer: 'Yes, just check the "Hidden Network" box if your SSID is hidden. The QR code contains the standard WiFi string configuration.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'What happens if I change my WiFi password?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'You will need to generate a new QR code with the updated password. Consider using dynamic QR codes if you change passwords frequently.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Can I customize the QR code design?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes. You can change the QR code color and add frame labels like "Scan Me" or "WiFi" to make it more recognizable.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Does it work for hidden networks?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, just check the "Hidden Network" box if your SSID is hidden. The QR code contains the standard WiFi string configuration.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Youtube, Shield, Zap, Smartphone, Play, Radio, Download, Share2 } from
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,11 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'YouTube QR Code Generator',
|
||||||
name: 'YouTube QR Code Generator',
|
'Generate QR codes that direct users to a YouTube video or channel.',
|
||||||
applicationCategory: 'UtilitiesApplication',
|
'/og-youtube-generator.png'
|
||||||
operatingSystem: 'Web Browser',
|
),
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.8',
|
|
||||||
ratingCount: '1340',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that direct users to a YouTube video or channel.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a YouTube QR Code',
|
name: 'How to Create a YouTube QR Code',
|
||||||
|
|
@ -93,51 +82,28 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'Does it open the YouTube app?': {
|
||||||
mainEntity: [
|
question: 'Does it open the YouTube app?',
|
||||||
{
|
answer: 'Yes! If the user has the YouTube app installed, the QR code will automatically launch the app and play the video.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it open the YouTube app?',
|
'Can I link to a specific timestamp?': {
|
||||||
acceptedAnswer: {
|
question: 'Can I link to a specific timestamp?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes. If you include the timestamp in your YouTube link (e.g., ?t=60s), the video will start playing from that exact moment.',
|
||||||
text: 'Yes! If the user has the YouTube app installed, the QR code will automatically launch the app and play the video.',
|
},
|
||||||
},
|
'Can I use this for a playlist?': {
|
||||||
},
|
question: 'Can I use this for a playlist?',
|
||||||
{
|
answer: 'Absolutely. Just paste the playlist URL, and users will be taken to the full list of videos.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Can I link to a specific timestamp?',
|
'Is it free?': {
|
||||||
acceptedAnswer: {
|
question: 'Is it free?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes, this tool is 100% free forever.',
|
||||||
text: 'Yes. If you include the timestamp in your YouTube link (e.g., ?t=60s), the video will start playing from that exact moment.',
|
},
|
||||||
},
|
'Does it work for YouTube Shorts?': {
|
||||||
},
|
question: 'Does it work for YouTube Shorts?',
|
||||||
{
|
answer: 'Yes, just paste the "Share" link from any YouTube Short.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Can I use this for a playlist?',
|
}),
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Absolutely. Just paste the playlist URL, and users will be taken to the full list of videos.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Is it free?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, this tool is 100% free forever.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Does it work for YouTube Shorts?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes, just paste the "Share" link from any YouTube Short.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Video, Shield, Zap, Smartphone, Users, Download, Share2 } from 'lucide-
|
||||||
import { QRCodeSVG } from 'qrcode.react';
|
import { QRCodeSVG } from 'qrcode.react';
|
||||||
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
import { ToolBreadcrumb } from '@/components/seo/BreadcrumbSchema';
|
||||||
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
import { RelatedTools } from '@/components/marketing/RelatedTools';
|
||||||
|
import { generateSoftwareAppSchema, generateFaqSchema } from '@/lib/schema-utils';
|
||||||
|
|
||||||
// SEO Optimized Metadata
|
// SEO Optimized Metadata
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
|
|
@ -38,23 +39,12 @@ export const metadata: Metadata = {
|
||||||
const jsonLd = {
|
const jsonLd = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@graph': [
|
'@graph': [
|
||||||
{
|
generateSoftwareAppSchema(
|
||||||
'@type': 'SoftwareApplication',
|
'Zoom QR Code Generator',
|
||||||
name: 'Zoom QR Code Generator',
|
'Generate QR codes that let people join your Zoom meeting with one scan.',
|
||||||
applicationCategory: 'BusinessApplication',
|
'/og-zoom-generator.png',
|
||||||
operatingSystem: 'Web Browser',
|
'BusinessApplication'
|
||||||
offers: {
|
),
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
aggregateRating: {
|
|
||||||
'@type': 'AggregateRating',
|
|
||||||
ratingValue: '4.8',
|
|
||||||
ratingCount: '720',
|
|
||||||
},
|
|
||||||
description: 'Generate QR codes that let people join your Zoom meeting with one scan.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
name: 'How to Create a Zoom QR Code',
|
name: 'How to Create a Zoom QR Code',
|
||||||
|
|
@ -87,43 +77,24 @@ const jsonLd = {
|
||||||
],
|
],
|
||||||
totalTime: 'PT30S',
|
totalTime: 'PT30S',
|
||||||
},
|
},
|
||||||
{
|
generateFaqSchema({
|
||||||
'@type': 'FAQPage',
|
'What happens when someone scans the QR code?': {
|
||||||
mainEntity: [
|
question: 'What happens when someone scans the QR code?',
|
||||||
{
|
answer: 'The Zoom app opens directly with your meeting ID and passcode pre-filled. They just tap "Join" to enter the meeting.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'What happens when someone scans the QR code?',
|
'Does it work for recurring meetings?': {
|
||||||
acceptedAnswer: {
|
question: 'Does it work for recurring meetings?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes! If your recurring meeting uses a fixed Personal Meeting ID (PMI), the QR code will work for all sessions.',
|
||||||
text: 'The Zoom app opens directly with your meeting ID and passcode pre-filled. They just tap "Join" to enter the meeting.',
|
},
|
||||||
},
|
'What if the meeting ID changes?': {
|
||||||
},
|
question: 'What if the meeting ID changes?',
|
||||||
{
|
answer: 'Static QR codes cannot be updated. You\'ll need to generate a new code. For changeable meetings, consider our Dynamic QR Codes.',
|
||||||
'@type': 'Question',
|
},
|
||||||
name: 'Does it work for recurring meetings?',
|
'Does it work on all devices?': {
|
||||||
acceptedAnswer: {
|
question: 'Does it work on all devices?',
|
||||||
'@type': 'Answer',
|
answer: 'Yes. The QR code works on iOS, Android, and can also open Zoom on desktop computers if the Zoom app is installed.',
|
||||||
text: 'Yes! If your recurring meeting uses a fixed Personal Meeting ID (PMI), the QR code will work for all sessions.',
|
},
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'What if the meeting ID changes?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Static QR codes cannot be updated. You\'ll need to generate a new code. For changeable meetings, consider our Dynamic QR Codes.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'@type': 'Question',
|
|
||||||
name: 'Does it work on all devices?',
|
|
||||||
acceptedAnswer: {
|
|
||||||
'@type': 'Answer',
|
|
||||||
text: 'Yes. The QR code works on iOS, Android, and can also open Zoom on desktop computers if the Zoom app is installed.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,14 @@ export async function generateMetadata(): Promise<Metadata> {
|
||||||
url: 'https://www.qrmaster.net/qr-code-erstellen',
|
url: 'https://www.qrmaster.net/qr-code-erstellen',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
locale: 'de_DE',
|
locale: 'de_DE',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: 'https://www.qrmaster.net/og-image.png',
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: 'QR Code Erstellen - Kostenlos & Sofort',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
title: 'QR Code Erstellen – Kostenlos | QR Master',
|
title: 'QR Code Erstellen – Kostenlos | QR Master',
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { MetadataRoute } from 'next';
|
import { MetadataRoute } from 'next';
|
||||||
|
import { blogPostList } from '../lib/blog-data';
|
||||||
|
|
||||||
export default function sitemap(): MetadataRoute.Sitemap {
|
export default function sitemap(): MetadataRoute.Sitemap {
|
||||||
const baseUrl = 'https://www.qrmaster.net';
|
const baseUrl = 'https://www.qrmaster.net';
|
||||||
|
|
@ -27,20 +28,9 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
||||||
];
|
];
|
||||||
|
|
||||||
// All blog posts
|
// All blog posts
|
||||||
const blogPosts = [
|
const blogPages = blogPostList.map((post) => ({
|
||||||
'qr-code-restaurant-menu',
|
url: `${baseUrl}/blog/${post.slug}`,
|
||||||
'vcard-qr-code-generator',
|
lastModified: post.dateModified ? new Date(post.dateModified) : new Date(),
|
||||||
'qr-code-small-business',
|
|
||||||
'qr-code-print-size-guide',
|
|
||||||
'qr-code-tracking-guide-2025',
|
|
||||||
'dynamic-vs-static-qr-codes',
|
|
||||||
'bulk-qr-code-generator-excel',
|
|
||||||
'qr-code-analytics',
|
|
||||||
];
|
|
||||||
|
|
||||||
const blogPages = blogPosts.map((slug) => ({
|
|
||||||
url: `${baseUrl}/blog/${slug}`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: 'monthly' as const,
|
changeFrequency: 'monthly' as const,
|
||||||
priority: 0.8,
|
priority: 0.8,
|
||||||
}));
|
}));
|
||||||
|
|
@ -119,12 +109,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
||||||
changeFrequency: 'yearly',
|
changeFrequency: 'yearly',
|
||||||
priority: 0.4,
|
priority: 0.4,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
url: `${baseUrl}/terms`,
|
|
||||||
lastModified: new Date(),
|
|
||||||
changeFrequency: 'yearly',
|
|
||||||
priority: 0.4,
|
|
||||||
},
|
|
||||||
...toolPages,
|
...toolPages,
|
||||||
...blogPages,
|
...blogPages,
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,30 @@ import { useEffect, useState, useRef } from 'react';
|
||||||
import { usePathname, useSearchParams } from 'next/navigation';
|
import { usePathname, useSearchParams } from 'next/navigation';
|
||||||
import posthog from 'posthog-js';
|
import posthog from 'posthog-js';
|
||||||
|
|
||||||
export function PostHogProvider({ children }: { children: React.ReactNode }) {
|
export function PostHogPageView() {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const [isInitialized, setIsInitialized] = useState(false);
|
|
||||||
|
// Track page views
|
||||||
|
useEffect(() => {
|
||||||
|
const cookieConsent = localStorage.getItem('cookieConsent');
|
||||||
|
|
||||||
|
if (cookieConsent === 'accepted' && pathname) {
|
||||||
|
let url = window.origin + pathname;
|
||||||
|
if (searchParams && searchParams.toString()) {
|
||||||
|
url = url + `?${searchParams.toString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
posthog.capture('$pageview', {
|
||||||
|
$current_url: url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [pathname, searchParams]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PostHogProvider({ children }: { children: React.ReactNode }) {
|
||||||
const initializationAttempted = useRef(false);
|
const initializationAttempted = useRef(false);
|
||||||
|
|
||||||
// Initialize PostHog once
|
// Initialize PostHog once
|
||||||
|
|
@ -18,6 +38,9 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
|
||||||
const cookieConsent = localStorage.getItem('cookieConsent');
|
const cookieConsent = localStorage.getItem('cookieConsent');
|
||||||
|
|
||||||
|
// Check if we should initialize based on consent
|
||||||
|
// If not accepted yet, we don't init. CookieBanner deals with setting 'accepted' and reloading or calling init.
|
||||||
|
// Ideally we should listen to consent changes, but for now matching previous behavior.
|
||||||
if (cookieConsent === 'accepted') {
|
if (cookieConsent === 'accepted') {
|
||||||
const apiKey = process.env.NEXT_PUBLIC_POSTHOG_KEY;
|
const apiKey = process.env.NEXT_PUBLIC_POSTHOG_KEY;
|
||||||
const apiHost = process.env.NEXT_PUBLIC_POSTHOG_HOST;
|
const apiHost = process.env.NEXT_PUBLIC_POSTHOG_HOST;
|
||||||
|
|
@ -27,49 +50,24 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if already initialized (using _loaded property)
|
|
||||||
if (!(posthog as any)._loaded) {
|
if (!(posthog as any)._loaded) {
|
||||||
posthog.init(apiKey, {
|
posthog.init(apiKey, {
|
||||||
api_host: apiHost || 'https://us.i.posthog.com',
|
api_host: apiHost || 'https://us.i.posthog.com',
|
||||||
person_profiles: 'identified_only',
|
person_profiles: 'identified_only',
|
||||||
capture_pageview: false, // Manual pageview tracking
|
capture_pageview: false, // We handle this manually
|
||||||
capture_pageleave: true,
|
capture_pageleave: true,
|
||||||
autocapture: true,
|
autocapture: true,
|
||||||
respect_dnt: true,
|
respect_dnt: true,
|
||||||
opt_out_capturing_by_default: false,
|
opt_out_capturing_by_default: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enable debug mode in development
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
posthog.debug();
|
posthog.debug();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set initialized immediately after init
|
|
||||||
setIsInitialized(true);
|
|
||||||
} else {
|
|
||||||
setIsInitialized(true); // Already loaded
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NO cleanup function - PostHog should persist across page navigation
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Track page views ONLY after PostHog is initialized
|
|
||||||
useEffect(() => {
|
|
||||||
const cookieConsent = localStorage.getItem('cookieConsent');
|
|
||||||
|
|
||||||
if (cookieConsent === 'accepted' && pathname && isInitialized) {
|
|
||||||
let url = window.origin + pathname;
|
|
||||||
if (searchParams && searchParams.toString()) {
|
|
||||||
url = url + `?${searchParams.toString()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
posthog.capture('$pageview', {
|
|
||||||
$current_url: url,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [pathname, searchParams, isInitialized]); // Added isInitialized dependency
|
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,20 @@
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
import { ToastContainer } from '@/components/ui/Toast';
|
import { ToastContainer } from '@/components/ui/Toast';
|
||||||
import AuthProvider from '@/components/SessionProvider';
|
import AuthProvider from '@/components/SessionProvider';
|
||||||
import { PostHogProvider } from '@/components/PostHogProvider';
|
import { PostHogProvider, PostHogPageView } from '@/components/PostHogProvider';
|
||||||
import CookieBanner from '@/components/CookieBanner';
|
import CookieBanner from '@/components/CookieBanner';
|
||||||
|
|
||||||
export function Providers({ children }: { children: React.ReactNode }) {
|
export function Providers({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={null}>
|
<PostHogProvider>
|
||||||
<PostHogProvider>
|
<Suspense fallback={null}>
|
||||||
<AuthProvider>
|
<PostHogPageView />
|
||||||
{children}
|
</Suspense>
|
||||||
</AuthProvider>
|
<AuthProvider>
|
||||||
<CookieBanner />
|
{children}
|
||||||
<ToastContainer />
|
</AuthProvider>
|
||||||
</PostHogProvider>
|
<CookieBanner />
|
||||||
</Suspense>
|
<ToastContainer />
|
||||||
|
</PostHogProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export function Footer({ variant = 'marketing', t }: FooterProps) {
|
||||||
<div className="grid md:grid-cols-4 gap-8">
|
<div className="grid md:grid-cols-4 gap-8">
|
||||||
<div>
|
<div>
|
||||||
<Link href="/" className="flex items-center space-x-2 mb-4 hover:opacity-80 transition-opacity">
|
<Link href="/" className="flex items-center space-x-2 mb-4 hover:opacity-80 transition-opacity">
|
||||||
<img src="/logo.svg" alt="" className="w-10 h-10" />
|
<img src="/logo.svg" alt="QR Master Logo" className="w-10 h-10" />
|
||||||
<span className={`text-xl font-bold ${isDashboard ? 'text-gray-900' : ''}`}>QR Master</span>
|
<span className={`text-xl font-bold ${isDashboard ? 'text-gray-900' : ''}`}>QR Master</span>
|
||||||
</Link>
|
</Link>
|
||||||
<p className={isDashboard ? 'text-gray-500' : 'text-gray-400'}>
|
<p className={isDashboard ? 'text-gray-500' : 'text-gray-400'}>
|
||||||
|
|
@ -30,6 +30,7 @@ export function Footer({ variant = 'marketing', t }: FooterProps) {
|
||||||
<ul className={`space-y-2 ${isDashboard ? 'text-gray-500' : 'text-gray-400'}`}>
|
<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'}>{translations.features}</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="/#pricing" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.pricing}</Link></li>
|
||||||
|
<li><Link href="/qr-code-tracking" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>QR Analytics</Link></li>
|
||||||
<li><Link href="/faq" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.faq}</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>
|
<li><Link href="/blog" className={isDashboard ? 'hover:text-primary-600' : 'hover:text-white'}>{translations.blog}</Link></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -13,3 +13,30 @@ export function generateFaqSchema(questions: Record<string, { question: string;
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateSoftwareAppSchema(
|
||||||
|
name: string,
|
||||||
|
description: string,
|
||||||
|
imagePath: string,
|
||||||
|
applicationCategory: string = 'UtilitiesApplication'
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'SoftwareApplication',
|
||||||
|
name,
|
||||||
|
applicationCategory,
|
||||||
|
operatingSystem: 'Web Browser',
|
||||||
|
image: `https://www.qrmaster.net${imagePath}`,
|
||||||
|
offers: {
|
||||||
|
'@type': 'Offer',
|
||||||
|
price: '0',
|
||||||
|
priceCurrency: 'USD',
|
||||||
|
},
|
||||||
|
aggregateRating: {
|
||||||
|
'@type': 'AggregateRating',
|
||||||
|
ratingValue: '4.8',
|
||||||
|
ratingCount: '1250',
|
||||||
|
},
|
||||||
|
description,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,21 +40,28 @@ export interface HowToTask {
|
||||||
totalTime?: string;
|
totalTime?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BASE_URL = 'https://www.qrmaster.net';
|
||||||
|
|
||||||
|
function toAbsoluteUrl(path: string): string {
|
||||||
|
if (path.startsWith('http')) return path;
|
||||||
|
return `${BASE_URL}${path.startsWith('/') ? '' : '/'}${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function organizationSchema() {
|
export function organizationSchema() {
|
||||||
return {
|
return {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'Organization',
|
'@type': 'Organization',
|
||||||
'@id': 'https://www.qrmaster.net/#organization',
|
'@id': `${BASE_URL}/#organization`,
|
||||||
name: 'QR Master',
|
name: 'QR Master',
|
||||||
alternateName: 'QRMaster',
|
alternateName: 'QRMaster',
|
||||||
url: 'https://www.qrmaster.net',
|
url: BASE_URL,
|
||||||
logo: {
|
logo: {
|
||||||
'@type': 'ImageObject',
|
'@type': 'ImageObject',
|
||||||
url: 'https://www.qrmaster.net/static/og-image.png',
|
url: `${BASE_URL}/og-image.png`,
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630,
|
height: 630,
|
||||||
},
|
},
|
||||||
image: 'https://www.qrmaster.net/static/og-image.png',
|
image: `${BASE_URL}/og-image.png`,
|
||||||
sameAs: [
|
sameAs: [
|
||||||
'https://twitter.com/qrmaster',
|
'https://twitter.com/qrmaster',
|
||||||
],
|
],
|
||||||
|
|
@ -68,8 +75,6 @@ export function organizationSchema() {
|
||||||
slogan: 'Dynamic QR codes that work smarter',
|
slogan: 'Dynamic QR codes that work smarter',
|
||||||
foundingDate: '2025',
|
foundingDate: '2025',
|
||||||
areaServed: 'Worldwide',
|
areaServed: 'Worldwide',
|
||||||
serviceType: 'Software as a Service',
|
|
||||||
priceRange: '$0 - $29',
|
|
||||||
knowsAbout: [
|
knowsAbout: [
|
||||||
'QR Code Generation',
|
'QR Code Generation',
|
||||||
'Marketing Analytics',
|
'Marketing Analytics',
|
||||||
|
|
@ -121,8 +126,7 @@ export function organizationSchema() {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
inLanguage: 'en',
|
mainEntityOfPage: BASE_URL,
|
||||||
mainEntityOfPage: 'https://www.qrmaster.net',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,19 +134,19 @@ export function websiteSchema() {
|
||||||
return {
|
return {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
'@id': 'https://www.qrmaster.net/#website',
|
'@id': `${BASE_URL}/#website`,
|
||||||
name: 'QR Master',
|
name: 'QR Master',
|
||||||
url: 'https://www.qrmaster.net',
|
url: BASE_URL,
|
||||||
inLanguage: 'en',
|
inLanguage: 'en',
|
||||||
mainEntityOfPage: 'https://www.qrmaster.net',
|
mainEntityOfPage: BASE_URL,
|
||||||
publisher: {
|
publisher: {
|
||||||
'@id': 'https://www.qrmaster.net/#organization',
|
'@id': `${BASE_URL}/#organization`,
|
||||||
},
|
},
|
||||||
potentialAction: {
|
potentialAction: {
|
||||||
'@type': 'SearchAction',
|
'@type': 'SearchAction',
|
||||||
target: {
|
target: {
|
||||||
'@type': 'EntryPoint',
|
'@type': 'EntryPoint',
|
||||||
urlTemplate: 'https://www.qrmaster.net/blog?q={search_term_string}',
|
urlTemplate: `${BASE_URL}/blog?q={search_term_string}`,
|
||||||
},
|
},
|
||||||
'query-input': 'required name=search_term_string',
|
'query-input': 'required name=search_term_string',
|
||||||
},
|
},
|
||||||
|
|
@ -153,14 +157,14 @@ export function breadcrumbSchema(items: BreadcrumbItem[]) {
|
||||||
return {
|
return {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'BreadcrumbList',
|
'@type': 'BreadcrumbList',
|
||||||
'@id': `https://www.qrmaster.net${items[items.length - 1]?.url}#breadcrumb`,
|
'@id': `${BASE_URL}${items[items.length - 1]?.url}#breadcrumb`,
|
||||||
inLanguage: 'en',
|
inLanguage: 'en',
|
||||||
mainEntityOfPage: `https://www.qrmaster.net${items[items.length - 1]?.url}`,
|
mainEntityOfPage: `${BASE_URL}${items[items.length - 1]?.url}`,
|
||||||
itemListElement: items.map((item, index) => ({
|
itemListElement: items.map((item, index) => ({
|
||||||
'@type': 'ListItem',
|
'@type': 'ListItem',
|
||||||
position: index + 1,
|
position: index + 1,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
item: `https://www.qrmaster.net${item.url}`,
|
item: toAbsoluteUrl(item.url),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -169,14 +173,14 @@ export function blogPostingSchema(post: BlogPost) {
|
||||||
return {
|
return {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'BlogPosting',
|
'@type': 'BlogPosting',
|
||||||
'@id': `https://www.qrmaster.net/blog/${post.slug}#article`,
|
'@id': `${BASE_URL}/blog/${post.slug}#article`,
|
||||||
headline: post.title,
|
headline: post.title,
|
||||||
description: post.description,
|
description: post.description,
|
||||||
image: post.image,
|
image: toAbsoluteUrl(post.image),
|
||||||
datePublished: post.datePublished,
|
datePublished: post.datePublished,
|
||||||
dateModified: post.dateModified,
|
dateModified: post.dateModified,
|
||||||
inLanguage: 'en',
|
inLanguage: 'en',
|
||||||
mainEntityOfPage: `https://www.qrmaster.net/blog/${post.slug}`,
|
mainEntityOfPage: `${BASE_URL}/blog/${post.slug}`,
|
||||||
author: {
|
author: {
|
||||||
'@type': 'Person',
|
'@type': 'Person',
|
||||||
name: post.author,
|
name: post.author,
|
||||||
|
|
@ -185,19 +189,19 @@ export function blogPostingSchema(post: BlogPost) {
|
||||||
publisher: {
|
publisher: {
|
||||||
'@type': 'Organization',
|
'@type': 'Organization',
|
||||||
name: 'QR Master',
|
name: 'QR Master',
|
||||||
url: 'https://www.qrmaster.net',
|
url: BASE_URL,
|
||||||
logo: {
|
logo: {
|
||||||
'@type': 'ImageObject',
|
'@type': 'ImageObject',
|
||||||
url: 'https://www.qrmaster.net/static/og-image.png',
|
url: `${BASE_URL}/og-image.png`,
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630,
|
height: 630,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isPartOf: {
|
isPartOf: {
|
||||||
'@type': 'Blog',
|
'@type': 'Blog',
|
||||||
'@id': 'https://www.qrmaster.net/blog#blog',
|
'@id': `${BASE_URL}/blog#blog`,
|
||||||
name: 'QR Master Blog',
|
name: 'QR Master Blog',
|
||||||
url: 'https://www.qrmaster.net/blog',
|
url: `${BASE_URL}/blog`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -206,9 +210,9 @@ export function faqPageSchema(faqs: FAQItem[]) {
|
||||||
return {
|
return {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'FAQPage',
|
'@type': 'FAQPage',
|
||||||
'@id': 'https://www.qrmaster.net/faq#faqpage',
|
'@id': `${BASE_URL}/faq#faqpage`,
|
||||||
inLanguage: 'en',
|
inLanguage: 'en',
|
||||||
mainEntityOfPage: 'https://www.qrmaster.net/faq',
|
mainEntityOfPage: `${BASE_URL}/faq`,
|
||||||
mainEntity: faqs.map((faq) => ({
|
mainEntity: faqs.map((faq) => ({
|
||||||
'@type': 'Question',
|
'@type': 'Question',
|
||||||
name: faq.question,
|
name: faq.question,
|
||||||
|
|
@ -224,11 +228,11 @@ export function productSchema(product: { name: string; description: string; offe
|
||||||
return {
|
return {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'Product',
|
'@type': 'Product',
|
||||||
'@id': 'https://www.qrmaster.net/pricing#product',
|
'@id': `${BASE_URL}/pricing#product`,
|
||||||
name: product.name,
|
name: product.name,
|
||||||
description: product.description,
|
description: product.description,
|
||||||
inLanguage: 'en',
|
inLanguage: 'en',
|
||||||
mainEntityOfPage: 'https://www.qrmaster.net/pricing',
|
mainEntityOfPage: `${BASE_URL}/pricing`,
|
||||||
brand: {
|
brand: {
|
||||||
'@type': 'Organization',
|
'@type': 'Organization',
|
||||||
name: 'QR Master',
|
name: 'QR Master',
|
||||||
|
|
@ -239,7 +243,7 @@ export function productSchema(product: { name: string; description: string; offe
|
||||||
price: offer.price,
|
price: offer.price,
|
||||||
priceCurrency: offer.priceCurrency,
|
priceCurrency: offer.priceCurrency,
|
||||||
availability: offer.availability,
|
availability: offer.availability,
|
||||||
url: offer.url,
|
url: toAbsoluteUrl(offer.url),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -248,18 +252,18 @@ export function howToSchema(task: HowToTask) {
|
||||||
return {
|
return {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'HowTo',
|
'@type': 'HowTo',
|
||||||
'@id': `https://www.qrmaster.net/blog/${task.name.toLowerCase().replace(/\s+/g, '-')}#howto`,
|
'@id': `${BASE_URL}/blog/${task.name.toLowerCase().replace(/\s+/g, '-')}#howto`,
|
||||||
name: task.name,
|
name: task.name,
|
||||||
description: task.description,
|
description: task.description,
|
||||||
inLanguage: 'en',
|
inLanguage: 'en',
|
||||||
mainEntityOfPage: `https://www.qrmaster.net/blog/${task.name.toLowerCase().replace(/\s+/g, '-')}`,
|
mainEntityOfPage: `${BASE_URL}/blog/${task.name.toLowerCase().replace(/\s+/g, '-')}`,
|
||||||
totalTime: task.totalTime || 'PT5M',
|
totalTime: task.totalTime || 'PT5M',
|
||||||
step: task.steps.map((step, index) => ({
|
step: task.steps.map((step, index) => ({
|
||||||
'@type': 'HowToStep',
|
'@type': 'HowToStep',
|
||||||
position: index + 1,
|
position: index + 1,
|
||||||
name: step.name,
|
name: step.name,
|
||||||
text: step.text,
|
text: step.text,
|
||||||
url: step.url,
|
url: step.url ? toAbsoluteUrl(step.url) : undefined,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue