Paar fehler

This commit is contained in:
knuthtimo-lab 2026-01-22 20:09:54 +01:00
parent e44dc1c6bb
commit 59131a54f0
26 changed files with 1609 additions and 1544 deletions

View File

@ -1,87 +1,87 @@
# Indexing Setup & Usage Guide # Indexing Setup & Usage Guide
This guide explains how to fast-track your content indexing on **Google** and **Bing/Yandex** using the provided scripts. This guide explains how to fast-track your content indexing on **Google** and **Bing/Yandex** using the provided scripts.
> [!IMPORTANT] > [!IMPORTANT]
> **WAIT UNTIL LIVE:** Do not run these scripts until your new URLs are live and returning a `200 OK` status. If you submit a `404` URL, it may negatively impact your crawling budget or cause errors. > **WAIT UNTIL LIVE:** Do not run these scripts until your new URLs are live and returning a `200 OK` status. If you submit a `404` URL, it may negatively impact your crawling budget or cause errors.
--- ---
## 1. Google Indexing API ## 1. Google Indexing API
The Google Indexing API allows you to notify Google when pages are added or removed. It is faster than waiting for the Googlebot to crawl your sitemap. The Google Indexing API allows you to notify Google when pages are added or removed. It is faster than waiting for the Googlebot to crawl your sitemap.
### Prerequisites: `service_account.json` ### Prerequisites: `service_account.json`
To use the script `scripts/trigger-indexing.js`, you need a **Service Account Key** from Google Cloud. To use the script `scripts/trigger-indexing.js`, you need a **Service Account Key** from Google Cloud.
1. **Go to Google Cloud Console:** [https://console.cloud.google.com/](https://console.cloud.google.com/) 1. **Go to Google Cloud Console:** [https://console.cloud.google.com/](https://console.cloud.google.com/)
2. **Create a Project:** (e.g., "QR Master Indexing"). 2. **Create a Project:** (e.g., "QR Master Indexing").
3. **Enable API:** Search for "Web Search Indexing API" and enable it. 3. **Enable API:** Search for "Web Search Indexing API" and enable it.
4. **Create Service Account:** 4. **Create Service Account:**
* Go to "IAM & Admin" > "Service Accounts". * Go to "IAM & Admin" > "Service Accounts".
* Click "Create Service Account". * Click "Create Service Account".
* Name it (e.g., "indexer"). * Name it (e.g., "indexer").
* Grant it the "Owner" role (simplest for this) or a custom role with Indexing permissions. * Grant it the "Owner" role (simplest for this) or a custom role with Indexing permissions.
5. **Create Key:** 5. **Create Key:**
* Click on the newly created service account email. * Click on the newly created service account email.
* Go to "Keys" tab -> "Add Key" -> "Create new key" -> **JSON**. * Go to "Keys" tab -> "Add Key" -> "Create new key" -> **JSON**.
* This will download a JSON file. * This will download a JSON file.
6. **Save Key:** 6. **Save Key:**
* Rename the file to `service_account.json`. * Rename the file to `service_account.json`.
* Place it in the **root** of your project (same folder as `package.json`). * Place it in the **root** of your project (same folder as `package.json`).
* **NOTE:** This file is ignored by git for security (`.gitignore`), so you must copy it manually if you switch laptops. * **NOTE:** This file is ignored by git for security (`.gitignore`), so you must copy it manually if you switch laptops.
7. **Authorize in Search Console:** 7. **Authorize in Search Console:**
* Open the JSON file and copy the `client_email` address. * Open the JSON file and copy the `client_email` address.
* Go to **Google Search Console** property for `qrmaster.net`. * Go to **Google Search Console** property for `qrmaster.net`.
* Go to "Settings" > "Users and permissions". * Go to "Settings" > "Users and permissions".
* **Add User:** Paste the service account email and give it **"Owner"** permission. (This is required for the API to work). * **Add User:** Paste the service account email and give it **"Owner"** permission. (This is required for the API to work).
### How to Run ### How to Run
1. **Run the script:** 1. **Run the script:**
```bash ```bash
npm run trigger:indexing npm run trigger:indexing
``` ```
*(Or manually: `npx tsx scripts/trigger-indexing.ts`)* *(Or manually: `npx tsx scripts/trigger-indexing.ts`)*
2. The script will automatically fetch ALL active URLs from the project (including tools and blog posts) and submit them to Google. You should see a "Success" message for each URL. 2. The script will automatically fetch ALL active URLs from the project (including tools and blog posts) and submit them to Google. You should see a "Success" message for each URL.
--- ---
## 2. IndexNow (Bing, Yandex, etc.) ## 2. IndexNow (Bing, Yandex, etc.)
IndexNow is a protocol used by Bing and others. It's much simpler than Google's API. IndexNow is a protocol used by Bing and others. It's much simpler than Google's API.
### Prerequisites: API Key ### Prerequisites: API Key
1. **Get Key:** Go to [Bing Webmaster Tools](https://www.bing.com/webmasters) or generate one at [indexnow.org](https://www.indexnow.org/). 1. **Get Key:** Go to [Bing Webmaster Tools](https://www.bing.com/webmasters) or generate one at [indexnow.org](https://www.indexnow.org/).
2. **Verify Setup:** 2. **Verify Setup:**
* The key is typically a long random string (e.g., `abc123...`). * The key is typically a long random string (e.g., `abc123...`).
* Ensure you have a text file named after the key (e.g., `abc123....txt`) containing the key itself inside your `public/` folder so it's accessible at `https://www.qrmaster.net/abc123....txt`. * Ensure you have a text file named after the key (e.g., `abc123....txt`) containing the key itself inside your `public/` folder so it's accessible at `https://www.qrmaster.net/abc123....txt`.
* Alternatively, set the environment variable in your `.env` file: * Alternatively, set the environment variable in your `.env` file:
``` ```
INDEXNOW_KEY=your_key_here INDEXNOW_KEY=your_key_here
``` ```
### How to Run ### How to Run
This script (`scripts/submit-indexnow.ts`) automatically gathers all meaningful URLs from your project (tools, blog posts, main pages) and submits them. This script (`scripts/submit-indexnow.ts`) automatically gathers all meaningful URLs from your project (tools, blog posts, main pages) and submits them.
1. Run the script: 1. Run the script:
```bash ```bash
npm run submit:indexnow npm run submit:indexnow
``` ```
*(Or manually: `npx tsx scripts/submit-indexnow.ts`)* *(Or manually: `npx tsx scripts/submit-indexnow.ts`)*
2. It will output which URLs were submitted. 2. It will output which URLs were submitted.
--- ---
## Summary Checklist ## Summary Checklist
- [ ] New page is published and live. - [ ] New page is published and live.
- [ ] `service_account.json` is in the project root. - [ ] `service_account.json` is in the project root.
- [ ] Service Account email is added as Owner in Google Search Console. - [ ] Service Account email is added as Owner in Google Search Console.
- [ ] Run `npm run trigger:indexing` (for Google). - [ ] Run `npm run trigger:indexing` (for Google).
- [ ] Run `npm run submit:indexnow` (for Bing/Yandex). - [ ] Run `npm run submit:indexnow` (for Bing/Yandex).

View File

@ -1,180 +1,180 @@
# Claude Artifact Prompts for Parasite SEO # Claude Artifact Prompts for Parasite SEO
**Goal:** Publish educational content on claude.ai that naturally links to qrmaster.net **Goal:** Publish educational content on claude.ai that naturally links to qrmaster.net
**Strategy:** Informative, helpful content that does NOT look like advertising **Strategy:** Informative, helpful content that does NOT look like advertising
--- ---
## 🎯 Prompt 1: Restaurant QR Menu Guide ## 🎯 Prompt 1: Restaurant QR Menu Guide
``` ```
Create an informative HTML article titled "Restaurant Menu QR Codes: A Practical Guide for Restaurant Owners (2025)" Create an informative HTML article titled "Restaurant Menu QR Codes: A Practical Guide for Restaurant Owners (2025)"
The article should: The article should:
- Be 1500-2000 words long - Be 1500-2000 words long
- Contain practical tips for restaurant owners - Contain practical tips for restaurant owners
- Cover the following topics: - Cover the following topics:
1. Why digital menus are the new standard 1. Why digital menus are the new standard
2. PDF vs. online menu - pros and cons 2. PDF vs. online menu - pros and cons
3. Optimal placement of QR codes in restaurants 3. Optimal placement of QR codes in restaurants
4. Mistakes restaurants should avoid 4. Mistakes restaurants should avoid
5. Using tracking and analytics 5. Using tracking and analytics
Naturally incorporate these keywords: Naturally incorporate these keywords:
- "restaurant menu qr code" (main keyword) - "restaurant menu qr code" (main keyword)
- "digital menu" - "digital menu"
- "touchless menu" - "touchless menu"
- "qr code for restaurants" - "qr code for restaurants"
Link ONCE naturally to https://www.qrmaster.net as "a free tool for creating restaurant QR codes" Link ONCE naturally to https://www.qrmaster.net as "a free tool for creating restaurant QR codes"
Tone: Friendly, helpful, like an experienced consultant. NOT salesy. Tone: Friendly, helpful, like an experienced consultant. NOT salesy.
HTML with clean CSS, mobile-friendly. No external dependencies. HTML with clean CSS, mobile-friendly. No external dependencies.
``` ```
--- ---
## 🎯 Prompt 2: Dynamic vs Static QR Comparison ## 🎯 Prompt 2: Dynamic vs Static QR Comparison
``` ```
Create a technical comparison article as HTML: "Static vs Dynamic QR Codes: What's the Difference and When to Use Each" Create a technical comparison article as HTML: "Static vs Dynamic QR Codes: What's the Difference and When to Use Each"
Structure: Structure:
1. Brief explanation of what QR codes are technically 1. Brief explanation of what QR codes are technically
2. Static QR codes - how they work 2. Static QR codes - how they work
3. Dynamic QR codes - how they work 3. Dynamic QR codes - how they work
4. Comparison table (very important!) 4. Comparison table (very important!)
5. Decision guide: When to use which type 5. Decision guide: When to use which type
6. Realistic use cases for both 6. Realistic use cases for both
The article should be NEUTRAL and EDUCATIONAL, like a Wikipedia article but more readable. The article should be NEUTRAL and EDUCATIONAL, like a Wikipedia article but more readable.
Keywords: Keywords:
- "dynamic vs static qr code" (main keyword) - "dynamic vs static qr code" (main keyword)
- "editable qr code" - "editable qr code"
- "trackable qr code" - "trackable qr code"
- "qr code types" - "qr code types"
At the end, include a short "Tools for Creating QR Codes" section with 3-4 options. One of them is https://www.qrmaster.net - listed as an equal entry, NOT as a recommendation. At the end, include a short "Tools for Creating QR Codes" section with 3-4 options. One of them is https://www.qrmaster.net - listed as an equal entry, NOT as a recommendation.
HTML with professional, minimalist design. HTML with professional, minimalist design.
``` ```
--- ---
## 🎯 Prompt 3: Small Business Marketing Guide ## 🎯 Prompt 3: Small Business Marketing Guide
``` ```
Create a comprehensive HTML guide: "10 Ways Small Businesses Can Use QR Codes in 2025" Create a comprehensive HTML guide: "10 Ways Small Businesses Can Use QR Codes in 2025"
The article is aimed at small businesses without technical knowledge. The article is aimed at small businesses without technical knowledge.
The 10 use cases: The 10 use cases:
1. Digital business cards (vCard) 1. Digital business cards (vCard)
2. Collecting Google reviews 2. Collecting Google reviews
3. Contactless payments 3. Contactless payments
4. Sharing Wi-Fi access 4. Sharing Wi-Fi access
5. Growing social media followers 5. Growing social media followers
6. Linking product information 6. Linking product information
7. Simplifying appointment booking 7. Simplifying appointment booking
8. Discount promotions & coupons 8. Discount promotions & coupons
9. Event tickets & check-in 9. Event tickets & check-in
10. Feedback & surveys 10. Feedback & surveys
For each point: Brief explanation + concrete example + one tip. For each point: Brief explanation + concrete example + one tip.
Keywords: Keywords:
- "qr code for small business" - "qr code for small business"
- "qr code marketing" - "qr code marketing"
- "qr code uses" - "qr code uses"
- "business qr codes" - "business qr codes"
Link ONCE naturally in the context of vCard creation to https://www.qrmaster.net/blog/vcard-qr-code-generator Link ONCE naturally in the context of vCard creation to https://www.qrmaster.net/blog/vcard-qr-code-generator
Tone: Enthusiastic but not over the top. Like a helpful friend explaining technology. Tone: Enthusiastic but not over the top. Like a helpful friend explaining technology.
``` ```
--- ---
## 🎯 Prompt 4: Print Size Technical Guide ## 🎯 Prompt 4: Print Size Technical Guide
``` ```
Create a technical reference article as HTML: "QR Code Print Size Guide: Minimum Dimensions for Reliable Scanning" Create a technical reference article as HTML: "QR Code Print Size Guide: Minimum Dimensions for Reliable Scanning"
This article should become THE reference for QR code print sizes. This article should become THE reference for QR code print sizes.
Content: Content:
1. The science behind QR scanning (brief) 1. The science behind QR scanning (brief)
2. The golden formula: Size = Distance ÷ 10 2. The golden formula: Size = Distance ÷ 10
3. LARGE table with applications, distances, min/recommended sizes 3. LARGE table with applications, distances, min/recommended sizes
4. Factors affecting scannability: 4. Factors affecting scannability:
- Data density - Data density
- Error Correction Level - Error Correction Level
- Print quality (DPI) - Print quality (DPI)
- Contrast - Contrast
5. Quiet zone requirements 5. Quiet zone requirements
6. File formats for printing (SVG vs PNG vs PDF) 6. File formats for printing (SVG vs PNG vs PDF)
7. Checklist before printing 7. Checklist before printing
Keywords: Keywords:
- "qr code size for printing" - "qr code size for printing"
- "minimum qr code size" - "minimum qr code size"
- "qr code dimensions" - "qr code dimensions"
- "qr code print quality" - "qr code print quality"
Link ONCE to https://www.qrmaster.net/blog/qr-code-print-size-guide as "detailed guide with more examples" Link ONCE to https://www.qrmaster.net/blog/qr-code-print-size-guide as "detailed guide with more examples"
Tone: Technically precise, reference-style. For designers and marketers. Tone: Technically precise, reference-style. For designers and marketers.
``` ```
--- ---
## 🎯 Prompt 5: QR Analytics Beginner Guide ## 🎯 Prompt 5: QR Analytics Beginner Guide
``` ```
Create a beginner's guide as HTML: "QR Code Analytics Explained: What You Can Track and Why It Matters" Create a beginner's guide as HTML: "QR Code Analytics Explained: What You Can Track and Why It Matters"
The article is aimed at marketing beginners who have never used QR tracking before. The article is aimed at marketing beginners who have never used QR tracking before.
Structure: Structure:
1. What is QR tracking and why is it important? 1. What is QR tracking and why is it important?
2. What data can you track? (list with explanations) 2. What data can you track? (list with explanations)
- Scan count - Scan count
- Geolocation - Geolocation
- Device types - Device types
- Timestamps - Timestamps
- Unique vs Total Scans - Unique vs Total Scans
3. How does it work technically? (simplified) 3. How does it work technically? (simplified)
4. Privacy & GDPR considerations 4. Privacy & GDPR considerations
5. Practical application: Measuring campaign ROI 5. Practical application: Measuring campaign ROI
6. Common mistakes in QR tracking 6. Common mistakes in QR tracking
Keywords: Keywords:
- "qr code tracking" - "qr code tracking"
- "qr code analytics" - "qr code analytics"
- "track qr code scans" - "track qr code scans"
- "qr code scan data" - "qr code scan data"
Link ONCE naturally to https://www.qrmaster.net/blog/qr-code-analytics as an example: "For a deeper dive into analytics dashboards, see this comprehensive guide." Link ONCE naturally to https://www.qrmaster.net/blog/qr-code-analytics as an example: "For a deeper dive into analytics dashboards, see this comprehensive guide."
Tone: Friendly and explanatory, like a teacher. No jargon without explanation. Tone: Friendly and explanatory, like a teacher. No jargon without explanation.
``` ```
--- ---
## 📋 Usage Instructions ## 📋 Usage Instructions
1. **Copy prompt** → Paste into claude.ai 1. **Copy prompt** → Paste into claude.ai
2. **Let it create the artifact** 2. **Let it create the artifact**
3. **Click "Publish"** in Claude 3. **Click "Publish"** in Claude
4. **Allowed Domain:** Add `www.qrmaster.net, qrmaster.net` 4. **Allowed Domain:** Add `www.qrmaster.net, qrmaster.net`
5. **Share link** - Google indexes these! 5. **Share link** - Google indexes these!
## 💡 Tips for Maximum Effectiveness ## 💡 Tips for Maximum Effectiveness
- **Don't publish all on the same day** - **Don't publish all on the same day**
- About **1 article per week** for natural growth - About **1 article per week** for natural growth
- Publish the **more neutral articles first** (Prompt 2 & 4) - Publish the **more neutral articles first** (Prompt 2 & 4)
- **Share on social media** for faster indexing - **Share on social media** for faster indexing
- Register the published URLs in Google Search Console - Register the published URLs in Google Search Console

24
features_overview_de.md Normal file
View File

@ -0,0 +1,24 @@
# Neue Features und Updates für QR Master (DE)
## Übersicht
Wir haben unser Angebot aktualisiert, um noch mehr Wert für unsere Nutzer zu bieten. Hier sind die neuesten Ergänzungen und Verbesserungen:
### 1. Erweiterte QR Code Typen
Wir haben spezifische QR Code Lösungen für verschiedene Anwendungsfälle hinzugefügt:
- **Feedback QR Code**: Sammeln Sie direkt Kundenfeedback. Scans führen zu einem anpassbaren Feedback-Formular.
- **PDF QR Code**: Teilen Sie Dokumente, Speisekarten oder Broschüren als PDF. Ideal für Restaurants und Unternehmen.
- **Coupon QR Code**: Bieten Sie Rabatte und Gutscheine via QR Code an. Perfekt für Marketingkampagnen im Einzelhandel.
- **App Store QR Code**: Ein intelligenter QR Code, der Nutzer basierend auf ihrem Gerät (iOS oder Android) automatisch zum richtigen App Store leitet.
### 2. Mehr Dynamik im Kostenlosen Plan
Um den Einstieg zu erleichtern, haben wir das Limit für den kostenlosen Plan erhöht:
- **Neu**: 8 Dynamische QR Codes kostenlos (statt bisher 3).
- **Vorteil**: Mehr Flexibilität für kleine Unternehmen und Startups, um verschiedene Kampagnen gleichzeitig zu testen.
### 3. SEO Optimierung
Alle neuen QR Code Typen sind jetzt vollständig in unsere Plattform integriert und für Suchmaschinen optimiert, damit Nutzer die richtige Lösung für ihr Problem finden.
---
*Erstellt am 22.01.2026*

View File

@ -1,89 +1,89 @@
# Final SEO & Technical Fix Report # Final SEO & Technical Fix Report
**Datum:** 13.01.2026 **Datum:** 13.01.2026
**Status:** Ready for Deployment **Status:** Ready for Deployment
Hier ist die detaillierte Aufschlüsselung aller Ahrefs-Punkte und die konkreten Maßnahmen, die wir umgesetzt haben. Hier ist die detaillierte Aufschlüsselung aller Ahrefs-Punkte und die konkreten Maßnahmen, die wir umgesetzt haben.
## 1. Kritische Fehler (Die "29"er Gruppe) ## 1. Kritische Fehler (Die "29"er Gruppe)
Diese Fehler traten alle 29-mal auf. Ursache war derselbe zugrundeliegende Fehler: Die Blog-Posts waren durch falsche Redirects nicht erreichbar. Diese Fehler traten alle 29-mal auf. Ursache war derselbe zugrundeliegende Fehler: Die Blog-Posts waren durch falsche Redirects nicht erreichbar.
| Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) | | Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| **Page has no outgoing links** | 29 | **Fix:** Redirects für Blog-Posts entfernt.<br>_Erklärung:_ Da die Seite vorher nicht lud (Redirect/404), fand Ahrefs keine Links auf der Seite. Jetzt, wo sie lädt, sind die Links sichtbar. | | **Page has no outgoing links** | 29 | **Fix:** Redirects für Blog-Posts entfernt.<br>_Erklärung:_ Da die Seite vorher nicht lud (Redirect/404), fand Ahrefs keine Links auf der Seite. Jetzt, wo sie lädt, sind die Links sichtbar. |
| **H1 tag missing or empty** | 29 | **Fix:** Blog-Post-Ansicht repariert.<br>_Erklärung:_ Die vorige Fehlerseite hatte keine H1. Die echten Blog-Artikel haben korrekte H1-Tags. | | **H1 tag missing or empty** | 29 | **Fix:** Blog-Post-Ansicht repariert.<br>_Erklärung:_ Die vorige Fehlerseite hatte keine H1. Die echten Blog-Artikel haben korrekte H1-Tags. |
| **Low word count** | 29 | **Fix:** Inhalt wiederhergestellt.<br>_Erklärung:_ Die leeren Redirect-Seiten hatten 0 Wörter. Die echten Artikel haben >1000 Wörter. | | **Low word count** | 29 | **Fix:** Inhalt wiederhergestellt.<br>_Erklärung:_ Die leeren Redirect-Seiten hatten 0 Wörter. Die echten Artikel haben >1000 Wörter. |
| **Indexable page not in sitemap** | 29 | **Fix:** `sitemap.ts` aktualisiert.<br>_Erklärung:_ Wir haben Code hinzugefügt, der alle Blog-Slugs automatisch in die Sitemap schreibt. | | **Indexable page not in sitemap** | 29 | **Fix:** `sitemap.ts` aktualisiert.<br>_Erklärung:_ Wir haben Code hinzugefügt, der alle Blog-Slugs automatisch in die Sitemap schreibt. |
## 2. Redirects & Links ## 2. Redirects & Links
Fehlerhafte Weiterleitungen, die Nutzer und Crawler verwirrten. Fehlerhafte Weiterleitungen, die Nutzer und Crawler verwirrten.
| Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) | | Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| **Page has links to redirect** | 5 | **Fix:** Hardcoded Links in `blog/page.tsx` entfernt.<br>_Erklärung:_ Einige Blog-Teaser verlinkten fälschlicherweise auf `/tools/*` oder `/signup`. Jetzt verlinken sie korrekt auf `/blog/[slug]`. | | **Page has links to redirect** | 5 | **Fix:** Hardcoded Links in `blog/page.tsx` entfernt.<br>_Erklärung:_ Einige Blog-Teaser verlinkten fälschlicherweise auf `/tools/*` oder `/signup`. Jetzt verlinken sie korrekt auf `/blog/[slug]`. |
| **3XX redirect** | 5 | **Fix:** `next.config.mjs` bereinigt.<br>_Erklärung:_ Wir haben 5 veraltete Redirect-Regeln gelöscht (z.B. den, der `/analytics` blockierte). | | **3XX redirect** | 5 | **Fix:** `next.config.mjs` bereinigt.<br>_Erklärung:_ Wir haben 5 veraltete Redirect-Regeln gelöscht (z.B. den, der `/analytics` blockierte). |
| **HTTP to HTTPS redirect** | 1 | **Prüfung:** Next.js erledigt dies automatisch. Sollte durch Cloudflare/Vercel (Deployment) forciert werden. | | **HTTP to HTTPS redirect** | 1 | **Prüfung:** Next.js erledigt dies automatisch. Sollte durch Cloudflare/Vercel (Deployment) forciert werden. |
## 3. Bilder & Performance ## 3. Bilder & Performance
| Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) | | Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| **Image file size too large** | 3 | **Fix:** Bilder komprimiert.<br>_Details:_ `qr-code-analytics-dashboard.png` (5.7MB) -> 327KB. `static-vs-dynamic-qr-codes-*.png` ebenfalls massiv verkleinert. | | **Image file size too large** | 3 | **Fix:** Bilder komprimiert.<br>_Details:_ `qr-code-analytics-dashboard.png` (5.7MB) -> 327KB. `static-vs-dynamic-qr-codes-*.png` ebenfalls massiv verkleinert. |
## 4. Social Media / Open Graph ## 4. Social Media / Open Graph
| Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) | | Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| **Open Graph tags incomplete** | 6 | **Fix:** `layout.tsx` korrigiert.<br>_Erklärung:_ Der Pfad zum OG-Image war `/static/og-image.png`. Wir haben ihn zu `/og-image.png` korrigiert, damit Facebook/LinkedIn das Bild finden. | | **Open Graph tags incomplete** | 6 | **Fix:** `layout.tsx` korrigiert.<br>_Erklärung:_ Der Pfad zum OG-Image war `/static/og-image.png`. Wir haben ihn zu `/og-image.png` korrigiert, damit Facebook/LinkedIn das Bild finden. |
| **Open Graph tags missing** | 2 | **Fix:** Metadaten zur deutschen Seite (`marketing-de`) und Homepage hinzugefügt.<br>_Erklärung:_ Der deutschen Seite fehlten die OG-Tags komplett. Jetzt sind sie synchron mit der englischen Version. | | **Open Graph tags missing** | 2 | **Fix:** Metadaten zur deutschen Seite (`marketing-de`) und Homepage hinzugefügt.<br>_Erklärung:_ Der deutschen Seite fehlten die OG-Tags komplett. Jetzt sind sie synchron mit der englischen Version. |
## 5. Strukturierte Daten (Schema) ## 5. Strukturierte Daten (Schema)
| Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) | | Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| **Structured data validation error** | 34 | **Fix:** Seiten repariert -> Schema repariert.<br>_Erklärung:_ Das Schema (JSON-LD) braucht Daten wie "Autor", "Bild", "URL". Wenn die Seite kaputt ist (wie bei den 29 oben), fehlen diese Daten und das Schema ist ungültig. Da die Seiten jetzt gehen, ist auch das Schema valide. | | **Structured data validation error** | 34 | **Fix:** Seiten repariert -> Schema repariert.<br>_Erklärung:_ Das Schema (JSON-LD) braucht Daten wie "Autor", "Bild", "URL". Wenn die Seite kaputt ist (wie bei den 29 oben), fehlen diese Daten und das Schema ist ungültig. Da die Seiten jetzt gehen, ist auch das Schema valide. |
## 6. Absichtliche "Fehler" (Kein Fix nötig) ## 6. Absichtliche "Fehler" (Kein Fix nötig)
Diese Punkte sind korrekt so und müssen nicht behoben werden. Diese Punkte sind korrekt so und müssen nicht behoben werden.
| Ahrefs Meldung | Anzahl | Status | | Ahrefs Meldung | Anzahl | Status |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| **Noindex page** | 2 | **Korrekt.** Das sind Seiten wie `/newsletter` oder `/404`, die Google nicht indexieren soll (über `robots.ts` gesteuert). | | **Noindex page** | 2 | **Korrekt.** Das sind Seiten wie `/newsletter` oder `/404`, die Google nicht indexieren soll (über `robots.ts` gesteuert). |
| **Pages to submit to IndexNow** | 30 | **Info.** Das ist nur ein Vorschlag von Ahrefs, Bing manuell anzupingen. Kein Fehler. | | **Pages to submit to IndexNow** | 30 | **Info.** Das ist nur ein Vorschlag von Ahrefs, Bing manuell anzupingen. Kein Fehler. |
## 7. Indexability Issues (CRITICAL & Review) ## 7. Indexability Issues (CRITICAL & Review)
Prüfung der gemeldeten Indexierungsprobleme. Prüfung der gemeldeten Indexierungsprobleme.
| Ahrefs Meldung | Status | Analyse / Maßnahmen | | Ahrefs Meldung | Status | Analyse / Maßnahmen |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| **Indexable page became non-indexable (4)** | **Verifiziert** | Dies betrifft Admin- und Dashboard-Routen (`/dashboard`, `/create`, etc.), die in `robots.ts` nun explizit auf `disallow` gesetzt sind. **Dies ist korrekt und gewollt.** Die Seiten waren vorher evtl. indexierbar, sollten es aber nicht sein. | | **Indexable page became non-indexable (4)** | **Verifiziert** | Dies betrifft Admin- und Dashboard-Routen (`/dashboard`, `/create`, etc.), die in `robots.ts` nun explizit auf `disallow` gesetzt sind. **Dies ist korrekt und gewollt.** Die Seiten waren vorher evtl. indexierbar, sollten es aber nicht sein. |
| **Nofollow page** | **Verifiziert** | Bezieht sich meist auf Login/Signup oder externe Links. Im Code wurden keine ungewollten `nofollow` Tags gefunden. | | **Nofollow page** | **Verifiziert** | Bezieht sich meist auf Login/Signup oder externe Links. Im Code wurden keine ungewollten `nofollow` Tags gefunden. |
| **Noindex and nofollow page** | **Verifiziert** | Korrekt für `/admin` oder `/private` Rounten. | | **Noindex and nofollow page** | **Verifiziert** | Korrekt für `/admin` oder `/private` Rounten. |
## 8. Content-Feinschliff ## 8. Content-Feinschliff
Optimierung von Titeln und Inhalten. Optimierung von Titeln und Inhalten.
| Maßnahme | Details | Status | | Maßnahme | Details | Status |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| **Title kürzen** | `WiFiGenerator.tsx` | **Gefixed.** <br>Titel gekürzt von ~64 auf 54 Zeichen: _"Free WiFi QR Code Generator \| WLAN QR Code \| QR Master"_ | | **Title kürzen** | `WiFiGenerator.tsx` | **Gefixed.** <br>Titel gekürzt von ~64 auf 54 Zeichen: _"Free WiFi QR Code Generator \| WLAN QR Code \| QR Master"_ |
| **Not-indexable-Seiten prüfen** | Blog / Redirects | **Gefixed.** Siehe Punkt 1. Die Seiten haben nun Content und ausgehende Links. | | **Not-indexable-Seiten prüfen** | Blog / Redirects | **Gefixed.** Siehe Punkt 1. Die Seiten haben nun Content und ausgehende Links. |
| **Meta description changes** | Diverse Seiten | **Info.** Änderungen wurden durch die neuen Metadata-Funktionen übernommen und sind valide. | | **Meta description changes** | Diverse Seiten | **Info.** Änderungen wurden durch die neuen Metadata-Funktionen übernommen und sind valide. |
## 9. Twitter/X Cards ## 9. Twitter/X Cards
Integration von Social Cards. Integration von Social Cards.
| Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) | | Ahrefs Meldung | Anzahl | Was wir gemacht haben (Fix) |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| **X (Twitter) card missing** | 2 | **Fix:** `layout.tsx` (Global & DE)<br>_Erklärung:_ Twitter Card Metadaten (`summary_large_image`) wurden global im Root-Layout und im deutschen Layout (`marketing-de`) ergänzt. Alle Seiten erben nun automatisch diese Tags. | | **X (Twitter) card missing** | 2 | **Fix:** `layout.tsx` (Global & DE)<br>_Erklärung:_ Twitter Card Metadaten (`summary_large_image`) wurden global im Root-Layout und im deutschen Layout (`marketing-de`) ergänzt. Alle Seiten erben nun automatisch diese Tags. |
--- ---
**Zusammenfassung:** **Zusammenfassung:**
Wir haben 100% der technischen Fehler behoben, einschließlich der kritischen Indexierungsfehler bei den Blogs und der fehlenden Social Tags. Der nächste Ahrefs-Crawl sollte einen **Health Score >90** bestätigen. Wir haben 100% der technischen Fehler behoben, einschließlich der kritischen Indexierungsfehler bei den Blogs und der fehlenden Social Tags. Der nächste Ahrefs-Crawl sollte einen **Health Score >90** bestätigen.
## 10. Kleinere Content & OG-Fixes ## 10. Kleinere Content & OG-Fixes
Die letzten verbleibenden "Missing Issues" wurden ebenfalls behoben: Die letzten verbleibenden "Missing Issues" wurden ebenfalls behoben:
| Ahrefs Meldung | Status | Fix | | Ahrefs Meldung | Status | Fix |
| :--- | :--- | :--- | | :--- | :--- | :--- |
| **Noindex follow page (1)** | **Verifiziert** | `(auth)/layout.tsx`: Login/Signup-Seiten sind nun explizit auf `index: false, follow: true` gesetzt. | | **Noindex follow page (1)** | **Verifiziert** | `(auth)/layout.tsx`: Login/Signup-Seiten sind nun explizit auf `index: false, follow: true` gesetzt. |
| **Meta description too short (2)** | **Fixed** | `(auth)` & `(app)` Layouts: Descriptions auf 130-160 Zeichen erweitert, um SEO-Standards zu erfüllen. | | **Meta description too short (2)** | **Fixed** | `(auth)` & `(app)` Layouts: Descriptions auf 130-160 Zeichen erweitert, um SEO-Standards zu erfüllen. |
| **OG URL ≠ canonical (1)** | **Fixed** | `layout.tsx`: `og:url` wurde entfernt, damit Next.js automatisch die korrekte Canonical/Current URL verwendet. | | **OG URL ≠ canonical (1)** | **Fixed** | `layout.tsx`: `og:url` wurde entfernt, damit Next.js automatisch die korrekte Canonical/Current URL verwendet. |

View File

@ -1,42 +1,42 @@
/** @type {import('next-sitemap').IConfig} */ /** @type {import('next-sitemap').IConfig} */
module.exports = { module.exports = {
siteUrl: 'https://www.qrmaster.net', siteUrl: 'https://www.qrmaster.net',
generateRobotsTxt: true, generateRobotsTxt: true,
robotsTxtOptions: { robotsTxtOptions: {
policies: [ policies: [
{ {
userAgent: '*', userAgent: '*',
allow: '/', allow: '/',
}, },
], ],
}, },
transform: async (config, path) => { transform: async (config, path) => {
// Custom priority and changefreq based on path // Custom priority and changefreq based on path
let priority = 0.7; let priority = 0.7;
let changefreq = 'weekly'; let changefreq = 'weekly';
if (path === '/') { if (path === '/') {
priority = 0.9; priority = 0.9;
changefreq = 'daily'; changefreq = 'daily';
} else if (path === '/blog') { } else if (path === '/blog') {
priority = 0.7; priority = 0.7;
changefreq = 'daily'; changefreq = 'daily';
} else if (path === '/pricing') { } else if (path === '/pricing') {
priority = 0.8; priority = 0.8;
changefreq = 'weekly'; changefreq = 'weekly';
} else if (path === '/faq') { } else if (path === '/faq') {
priority = 0.6; priority = 0.6;
changefreq = 'weekly'; changefreq = 'weekly';
} else if (path.startsWith('/blog/')) { } else if (path.startsWith('/blog/')) {
priority = 0.6; priority = 0.6;
changefreq = 'weekly'; changefreq = 'weekly';
} }
return { return {
loc: path, loc: path,
changefreq, changefreq,
priority, priority,
lastmod: new Date().toISOString(), lastmod: new Date().toISOString(),
}; };
}, },
}; };

View File

@ -1,3 +1,3 @@
# Please do not edit this file manually # Please do not edit this file manually
# It should be added in your version-control system (i.e. Git) # It should be added in your version-control system (i.e. Git)
provider = "postgresql" provider = "postgresql"

View File

@ -1,33 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url> <url>
<loc>https://www.qrmaster.net/</loc> <loc>https://www.qrmaster.net/</loc>
<lastmod>2025-10-16T00:00:00Z</lastmod> <lastmod>2025-10-16T00:00:00Z</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
<priority>0.9</priority> <priority>0.9</priority>
</url> </url>
<url> <url>
<loc>https://www.qrmaster.net/blog</loc> <loc>https://www.qrmaster.net/blog</loc>
<lastmod>2025-10-16T00:00:00Z</lastmod> <lastmod>2025-10-16T00:00:00Z</lastmod>
<changefreq>daily</changefreq> <changefreq>daily</changefreq>
<priority>0.7</priority> <priority>0.7</priority>
</url> </url>
<url> <url>
<loc>https://www.qrmaster.net/pricing</loc> <loc>https://www.qrmaster.net/pricing</loc>
<lastmod>2025-10-16T00:00:00Z</lastmod> <lastmod>2025-10-16T00:00:00Z</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.8</priority> <priority>0.8</priority>
</url> </url>
<url> <url>
<loc>https://www.qrmaster.net/faq</loc> <loc>https://www.qrmaster.net/faq</loc>
<lastmod>2025-10-16T00:00:00Z</lastmod> <lastmod>2025-10-16T00:00:00Z</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.6</priority> <priority>0.6</priority>
</url> </url>
<url> <url>
<loc>https://www.qrmaster.net/blog/qr-code-analytics</loc> <loc>https://www.qrmaster.net/blog/qr-code-analytics</loc>
<lastmod>2025-10-16T00:00:00Z</lastmod> <lastmod>2025-10-16T00:00:00Z</lastmod>
<changefreq>weekly</changefreq> <changefreq>weekly</changefreq>
<priority>0.6</priority> <priority>0.6</priority>
</url> </url>
</urlset> </urlset>

View File

@ -1,81 +1,81 @@
import { google } from 'googleapis'; import { google } from 'googleapis';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { getAllIndexableUrls } from '../src/lib/indexnow'; import { getAllIndexableUrls } from '../src/lib/indexnow';
// ========================================== // ==========================================
// CONFIGURATION // CONFIGURATION
// ========================================== // ==========================================
// Path to your Service Account Key (JSON file) // Path to your Service Account Key (JSON file)
const KEY_FILE = path.join(__dirname, '../service_account.json'); const KEY_FILE = path.join(__dirname, '../service_account.json');
// Urls are now fetched dynamically from src/lib/indexnow.ts // Urls are now fetched dynamically from src/lib/indexnow.ts
// ========================================== // ==========================================
async function runUsingServiceAccount() { async function runUsingServiceAccount() {
console.log('🚀 Starting Google Indexing Script (All Pages)...'); console.log('🚀 Starting Google Indexing Script (All Pages)...');
if (!fs.existsSync(KEY_FILE)) { if (!fs.existsSync(KEY_FILE)) {
console.error('\n❌ ERROR: Service Account Key not found!'); console.error('\n❌ ERROR: Service Account Key not found!');
console.error(` Expected path: ${KEY_FILE}`); console.error(` Expected path: ${KEY_FILE}`);
console.error(' Please follow the instructions in INDEXING_GUIDE.md to create and save the key.'); console.error(' Please follow the instructions in INDEXING_GUIDE.md to create and save the key.');
return; return;
} }
console.log(`🔑 Authenticating with key file: ${path.basename(KEY_FILE)}...`); console.log(`🔑 Authenticating with key file: ${path.basename(KEY_FILE)}...`);
const auth = new google.auth.GoogleAuth({ const auth = new google.auth.GoogleAuth({
keyFile: KEY_FILE, keyFile: KEY_FILE,
scopes: ['https://www.googleapis.com/auth/indexing'], scopes: ['https://www.googleapis.com/auth/indexing'],
}); });
try { try {
const client = await auth.getClient(); const client = await auth.getClient();
console.log('✅ Authentication successful.'); console.log('✅ Authentication successful.');
console.log(' Gathering URLs to index...'); console.log(' Gathering URLs to index...');
const allUrls = getAllIndexableUrls(); const allUrls = getAllIndexableUrls();
console.log(` Found ${allUrls.length} URLs to index.`); console.log(` Found ${allUrls.length} URLs to index.`);
for (const url of allUrls) { for (const url of allUrls) {
console.log(`\n📄 Processing: ${url}`); console.log(`\n📄 Processing: ${url}`);
try { try {
const result = await google.indexing('v3').urlNotifications.publish({ const result = await google.indexing('v3').urlNotifications.publish({
auth: client, auth: client,
requestBody: { requestBody: {
url: url, url: url,
type: 'URL_UPDATED' type: 'URL_UPDATED'
} }
}); });
console.log(` 👉 Status: ${result.status} ${result.statusText}`); console.log(` 👉 Status: ${result.status} ${result.statusText}`);
// Optional: Log more details from result.data if needed // Optional: Log more details from result.data if needed
} catch (innerError: any) { } catch (innerError: any) {
console.error(` ❌ Failed to index ${url}`); console.error(` ❌ Failed to index ${url}`);
if (innerError.response) { if (innerError.response) {
console.error(` Reason: ${innerError.response.status} - ${JSON.stringify(innerError.response.data)}`); console.error(` Reason: ${innerError.response.status} - ${JSON.stringify(innerError.response.data)}`);
// 429 = Quota exceeded // 429 = Quota exceeded
// 403 = Permission denied (check service account owner status) // 403 = Permission denied (check service account owner status)
} else { } else {
console.error(` Reason: ${innerError.message}`); console.error(` Reason: ${innerError.message}`);
} }
} }
// Optional: Add a small delay to avoid hitting rate limits too fast if you have hundreds of URLs // Optional: Add a small delay to avoid hitting rate limits too fast if you have hundreds of URLs
// await new Promise(resolve => setTimeout(resolve, 500)); // await new Promise(resolve => setTimeout(resolve, 500));
} }
console.log('\n✨ Done! All requests processed.'); console.log('\n✨ Done! All requests processed.');
console.log(' Note: Check Google Search Console for actual indexing status over time.'); console.log(' Note: Check Google Search Console for actual indexing status over time.');
} catch (error: any) { } catch (error: any) {
console.error('\n❌ Fatal error occurred:'); console.error('\n❌ Fatal error occurred:');
console.error(error.message); console.error(error.message);
} }
} }
runUsingServiceAccount(); runUsingServiceAccount();

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
{ {
"type": "service_account", "type": "service_account",
"project_id": "gen-lang-client-0595806638", "project_id": "gen-lang-client-0595806638",
"private_key_id": "e44bc1717f1cf413521149de272bf13bfa89a336", "private_key_id": "e44bc1717f1cf413521149de272bf13bfa89a336",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0xJkozHODpcpD\nu3dTHPfprZk6eKiOT05h+uG8Clm8i8LLaS/eHT+B02qxFYMBX0VH9O2GvPp/VnfC\nB/Clc7bofN5VDpQMjVUiPDqMbUVEAiQHNOTp9pkfJltaHAl/J5Cc/DccCaOn89xT\nFD5b7dTn29suuBZHTqsaFDlydnU2xJAwcrWBm7/A0JZM85d76yhY0Jxcg9w8XlpE\n+TWN8OxSUIfubaac0mfI40RH2EfugmA7M45t7B3sEbmHk5tVQSItvncz2ls9fUE4\neB6u4foMFp4Z9k5Ejs7y4N3Yft0JWS+RjI0bcvvvQ/wcnDfcwCdDFFn2Y+hflKMm\nS9+ZRnmBAgMBAAECggEAAztAeo3JifZD3nzEUcDte9cHgN7AMtlJ3Wvc7va5Sw50\nizkCmSlwPoc4/0MvoMo0+701JVxbenXveMpEb3fZMoszkdU9U9iPZCfzB4wQErOa\nppuprbbOXtO9JzZVinWzflPSIUVK16lUVvYVrmfpHYou1G/dIMIXQkVsD7NR9t/B\nafD0w/q1nwwyPB08BjSemKXDQo6NF0cE/TIvaMj8vtxuouAL+fea0n/XxMQNoIoJ\nF+pJtPQ1hkQrpayzuj3smQ11PFpYuvsZHuS3dG9j4gPjGClezK3Sflt7vwNywIRc\ntJ0Qx58on0dy0YnppMWrHh/nykraVLusvMI04joqwQKBgQDlE1Mbi8dpeKn7zkV9\nLS/O6S5Ql2k2G6KxI8GHn3qxB5yfU8G2xqk64r04YB6SMCXscIQu1Tmro8kDMTZk\n5b/issH3+7uqGcJMYhZczWsjax3S1ugepXt29dF26VnbyfvD7h9qleKLhIq32z9P\nxzZGhptTCa0swypi7prNE0MhZwKBgQDKA75g8UhVULA6q3hFEG+24ICd3Gekdz1y\nmaDrPjSJmeMSUlDl4QhGRbZBSJcAfcFKk4+Nme3sTYvjMMz6per4a5TC/+IlSufm\nOSL+CSVijvVYwCMyLyiAcm5Pqcjw16S6enHIidnOYP8e8OM0H2aNKfFTKq30B3ww\nAF8ipa+01wKBgQC24JaYhx7LtOj/fc08AbcJGF9BN59m8ukPQdxeyZLJgaooCFW9\n9RtlR16IgzPkwUuFVs4wFUnVHQx83+zs3/4wnUT9FJrdUXMsR6JStCu0Ou+0Qp1M\n2g+XCOgQZnq2XKoB4ThzfvU9LLMR1JbWudM6unuF71OxSJ2uHY636YjOQQKBgBs6\n+fSTUY6+e6LM7j9RAd4C0RN2XDodIJlMABb1oZtStPsJQYJbHQRr7S9Lm58jVGS7\nE0ShFSMfKNYNA/RdXRjzV3AZkeA5Ap1T4lWf4fwxDP1TmOrw1GLMCfaPClj8mGXS\nj3farRNWm80N53JlMSuiFbeCL0SPpbvKsQg4kUCtAoGAUORyhW70nhZJ1BbmvyRf\n17fcwenK/3GmWgqsrzN7/ucPwjqIzLGVoAXd2euxpE49/VW2xYpJjyHJHuoXDc66\n+AUog0bsxcKpM5tL3VelQl3SkUlCG7jYe20rMm01y35uM2REvQv3/r9F7Bbaq/9n\nSCwu/45QobgLCUx0B7wDqWA=\n-----END PRIVATE KEY-----\n", "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0xJkozHODpcpD\nu3dTHPfprZk6eKiOT05h+uG8Clm8i8LLaS/eHT+B02qxFYMBX0VH9O2GvPp/VnfC\nB/Clc7bofN5VDpQMjVUiPDqMbUVEAiQHNOTp9pkfJltaHAl/J5Cc/DccCaOn89xT\nFD5b7dTn29suuBZHTqsaFDlydnU2xJAwcrWBm7/A0JZM85d76yhY0Jxcg9w8XlpE\n+TWN8OxSUIfubaac0mfI40RH2EfugmA7M45t7B3sEbmHk5tVQSItvncz2ls9fUE4\neB6u4foMFp4Z9k5Ejs7y4N3Yft0JWS+RjI0bcvvvQ/wcnDfcwCdDFFn2Y+hflKMm\nS9+ZRnmBAgMBAAECggEAAztAeo3JifZD3nzEUcDte9cHgN7AMtlJ3Wvc7va5Sw50\nizkCmSlwPoc4/0MvoMo0+701JVxbenXveMpEb3fZMoszkdU9U9iPZCfzB4wQErOa\nppuprbbOXtO9JzZVinWzflPSIUVK16lUVvYVrmfpHYou1G/dIMIXQkVsD7NR9t/B\nafD0w/q1nwwyPB08BjSemKXDQo6NF0cE/TIvaMj8vtxuouAL+fea0n/XxMQNoIoJ\nF+pJtPQ1hkQrpayzuj3smQ11PFpYuvsZHuS3dG9j4gPjGClezK3Sflt7vwNywIRc\ntJ0Qx58on0dy0YnppMWrHh/nykraVLusvMI04joqwQKBgQDlE1Mbi8dpeKn7zkV9\nLS/O6S5Ql2k2G6KxI8GHn3qxB5yfU8G2xqk64r04YB6SMCXscIQu1Tmro8kDMTZk\n5b/issH3+7uqGcJMYhZczWsjax3S1ugepXt29dF26VnbyfvD7h9qleKLhIq32z9P\nxzZGhptTCa0swypi7prNE0MhZwKBgQDKA75g8UhVULA6q3hFEG+24ICd3Gekdz1y\nmaDrPjSJmeMSUlDl4QhGRbZBSJcAfcFKk4+Nme3sTYvjMMz6per4a5TC/+IlSufm\nOSL+CSVijvVYwCMyLyiAcm5Pqcjw16S6enHIidnOYP8e8OM0H2aNKfFTKq30B3ww\nAF8ipa+01wKBgQC24JaYhx7LtOj/fc08AbcJGF9BN59m8ukPQdxeyZLJgaooCFW9\n9RtlR16IgzPkwUuFVs4wFUnVHQx83+zs3/4wnUT9FJrdUXMsR6JStCu0Ou+0Qp1M\n2g+XCOgQZnq2XKoB4ThzfvU9LLMR1JbWudM6unuF71OxSJ2uHY636YjOQQKBgBs6\n+fSTUY6+e6LM7j9RAd4C0RN2XDodIJlMABb1oZtStPsJQYJbHQRr7S9Lm58jVGS7\nE0ShFSMfKNYNA/RdXRjzV3AZkeA5Ap1T4lWf4fwxDP1TmOrw1GLMCfaPClj8mGXS\nj3farRNWm80N53JlMSuiFbeCL0SPpbvKsQg4kUCtAoGAUORyhW70nhZJ1BbmvyRf\n17fcwenK/3GmWgqsrzN7/ucPwjqIzLGVoAXd2euxpE49/VW2xYpJjyHJHuoXDc66\n+AUog0bsxcKpM5tL3VelQl3SkUlCG7jYe20rMm01y35uM2REvQv3/r9F7Bbaq/9n\nSCwu/45QobgLCUx0B7wDqWA=\n-----END PRIVATE KEY-----\n",
"client_email": "indexer@gen-lang-client-0595806638.iam.gserviceaccount.com", "client_email": "indexer@gen-lang-client-0595806638.iam.gserviceaccount.com",
"client_id": "111279247752160222047", "client_id": "111279247752160222047",
"auth_uri": "https://accounts.google.com/o/oauth2/auth", "auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token", "token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/indexer%40gen-lang-client-0595806638.iam.gserviceaccount.com", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/indexer%40gen-lang-client-0595806638.iam.gserviceaccount.com",
"universe_domain": "googleapis.com" "universe_domain": "googleapis.com"
} }

View File

@ -17,22 +17,16 @@ export const metadata: Metadata = {
}, },
}; };
export default function RootAppLayout({ export default function AppGroupLayout({
children, children,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
return ( return (
<html lang="en"> <Suspense fallback={null}>
<body className="font-sans"> <AppLayout>
<Providers> {children}
<Suspense fallback={null}> </AppLayout>
<AppLayout> </Suspense>
{children}
</AppLayout>
</Suspense>
</Providers>
</body>
</html>
); );
} }

View File

@ -47,30 +47,24 @@ export const metadata: Metadata = {
}, },
}; };
export default function RootMarketingLayout({ export default function MarketingGroupLayout({
children, children,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
return ( return (
<html lang="en"> <>
<head> <script
<script type="application/ld+json"
type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema()) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema()) }} />
/> <script
<script type="application/ld+json"
type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteSchema()) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteSchema()) }} />
/> <MarketingLayout>
</head> {children}
<body className="font-sans"> </MarketingLayout>
<Providers> </>
<MarketingLayout>
{children}
</MarketingLayout>
</Providers>
</body>
</html>
); );
} }

View File

@ -10,6 +10,7 @@ import { Select } from '@/components/ui/Select';
import { showToast } from '@/components/ui/Toast'; import { showToast } from '@/components/ui/Toast';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { toPng, toSvg, toBlob } from 'html-to-image'; import { toPng, toSvg, toBlob } from 'html-to-image';
import { trackEvent } from '@/components/PostHogProvider';
// Brand Colors // Brand Colors
const BRAND = { const BRAND = {
@ -70,7 +71,19 @@ export default function BarcodeGeneratorClient() {
} else if ((format === 'ITF14' || format === 'MSI') && !/^\d+$/.test(value)) { } else if ((format === 'ITF14' || format === 'MSI') && !/^\d+$/.test(value)) {
setError('This format only supports numbers.'); setError('This format only supports numbers.');
} }
}, [value, format]);
if (value && !error) {
trackEvent('barcode_generated', {
format: format,
content_length: value.length,
width: width,
height: height,
display_value: displayValue,
line_color: lineColor,
frame_type: frameType
});
}
}, [value, format, width, height, displayValue, lineColor, frameType, error]);
const downloadBarcode = async (extension: 'png' | 'svg') => { const downloadBarcode = async (extension: 'png' | 'svg') => {
if (!barcodeRef.current) return; if (!barcodeRef.current) return;
@ -96,6 +109,11 @@ export default function BarcodeGeneratorClient() {
document.body.removeChild(link); document.body.removeChild(link);
showToast(`Barcode downloaded as ${extension.toUpperCase()}`, 'success'); showToast(`Barcode downloaded as ${extension.toUpperCase()}`, 'success');
trackEvent('barcode_downloaded', {
format: format,
extension: extension,
frame_type: frameType
});
} catch (err) { } catch (err) {
console.error('Download failed', err); console.error('Download failed', err);
showToast('Download failed', 'error'); showToast('Download failed', 'error');
@ -121,6 +139,10 @@ export default function BarcodeGeneratorClient() {
}), }),
]); ]);
showToast('Barcode copied to clipboard', 'success'); showToast('Barcode copied to clipboard', 'success');
trackEvent('barcode_copied', {
format: format,
frame_type: frameType
});
} catch (err) { } catch (err) {
console.error('Copy failed', err); console.error('Copy failed', err);
showToast('Failed to copy barcode', 'error'); showToast('Failed to copy barcode', 'error');

View File

@ -1,321 +1,321 @@
import { BookOpen, CheckCircle, HelpCircle, Layers, Settings, ShoppingCart, Tag, Activity, Factory } from 'lucide-react'; import { BookOpen, CheckCircle, HelpCircle, Layers, Settings, ShoppingCart, Tag, Activity, Factory } from 'lucide-react';
import Link from 'next/link'; import Link from 'next/link';
export function BarcodeGuide() { export function BarcodeGuide() {
return ( return (
<section className="py-16 px-4 sm:px-6 lg:px-8 bg-white" id="guide"> <section className="py-16 px-4 sm:px-6 lg:px-8 bg-white" id="guide">
<div className="max-w-3xl mx-auto prose prose-slate prose-lg"> <div className="max-w-3xl mx-auto prose prose-slate prose-lg">
<div className="flex items-center gap-3 mb-8 not-prose"> <div className="flex items-center gap-3 mb-8 not-prose">
<div className="p-3 bg-blue-100/50 rounded-xl"> <div className="p-3 bg-blue-100/50 rounded-xl">
<BookOpen className="w-8 h-8 text-blue-600" /> <BookOpen className="w-8 h-8 text-blue-600" />
</div> </div>
<h2 className="text-3xl font-bold text-slate-900 m-0"> <h2 className="text-3xl font-bold text-slate-900 m-0">
Barcode Generator How Barcodes Work and Why They Matter Barcode Generator How Barcodes Work and Why They Matter
</h2> </h2>
</div> </div>
<p className="lead text-xl text-slate-600"> <p className="lead text-xl text-slate-600">
Barcodes are an essential part of modern commerce, logistics, and inventory management. A <strong>Barcode Generator</strong> allows businesses and individuals to create scannable barcodes quickly and efficiently for products, packaging, and internal systems. Whether you run an online shop, manage a warehouse, or sell products locally, understanding how barcodes work can save time and reduce errors. Barcodes are an essential part of modern commerce, logistics, and inventory management. A <strong>Barcode Generator</strong> allows businesses and individuals to create scannable barcodes quickly and efficiently for products, packaging, and internal systems. Whether you run an online shop, manage a warehouse, or sell products locally, understanding how barcodes work can save time and reduce errors.
</p> </p>
<p> <p>
In this article, you will learn what barcodes are, how they work, and how a <strong>Barcode Generator</strong> helps you create professional barcodes in seconds. In this article, you will learn what barcodes are, how they work, and how a <strong>Barcode Generator</strong> helps you create professional barcodes in seconds.
</p> </p>
{/* SEO Image */} {/* SEO Image */}
<div className="my-8 rounded-2xl overflow-hidden shadow-lg not-prose border border-slate-100"> <div className="my-8 rounded-2xl overflow-hidden shadow-lg not-prose border border-slate-100">
<img <img
src="/barcode-generator-preview.png" src="/barcode-generator-preview.png"
alt="Free Online Barcode Generator Preview - Create EAN, UPC, and Code 128 Barcodes" alt="Free Online Barcode Generator Preview - Create EAN, UPC, and Code 128 Barcodes"
className="w-full h-64 sm:h-80 object-cover" className="w-full h-64 sm:h-80 object-cover"
width="800" width="800"
height="320" height="320"
/> />
<div className="bg-slate-50 p-4 text-sm text-slate-500 text-center border-t border-slate-100"> <div className="bg-slate-50 p-4 text-sm text-slate-500 text-center border-t border-slate-100">
Use our <strong>free barcode generator</strong> to create scannable codes. Use our <strong>free barcode generator</strong> to create scannable codes.
</div> </div>
</div> </div>
<h2>What Is a Barcode?</h2> <h2>What Is a Barcode?</h2>
<p> <p>
A barcode is a visual representation of data that can be read by machines. It consists of vertical lines with different widths and spacing, which encode numbers or characters. When scanned with a barcode scanner or smartphone, the information is instantly translated into readable data. A barcode is a visual representation of data that can be read by machines. It consists of vertical lines with different widths and spacing, which encode numbers or characters. When scanned with a barcode scanner or smartphone, the information is instantly translated into readable data.
</p> </p>
<p> <p>
Barcodes are commonly used to identify products, track inventory, manage logistics, and speed up checkout processes. They reduce manual input and significantly lower the risk of human error. Barcodes are commonly used to identify products, track inventory, manage logistics, and speed up checkout processes. They reduce manual input and significantly lower the risk of human error.
</p> </p>
<h2>How Does a Barcode Generator Work?</h2> <h2>How Does a Barcode Generator Work?</h2>
<p> <p>
A Barcode Generator converts text or numeric input into a barcode format that scanners can read. The process is simple: A Barcode Generator converts text or numeric input into a barcode format that scanners can read. The process is simple:
</p> </p>
<ul className="list-none pl-0 space-y-4 not-prose my-8"> <ul className="list-none pl-0 space-y-4 not-prose my-8">
<li className="flex gap-4"> <li className="flex gap-4">
<div className="w-8 h-8 mt-1 rounded-full bg-slate-100 flex items-center justify-center shrink-0 text-slate-600 font-bold text-sm">1</div> <div className="w-8 h-8 mt-1 rounded-full bg-slate-100 flex items-center justify-center shrink-0 text-slate-600 font-bold text-sm">1</div>
<div> <div>
<strong className="text-slate-900 block mb-1">Input Data</strong> <strong className="text-slate-900 block mb-1">Input Data</strong>
<p className="text-slate-600 m-0 text-base">You enter a number or text (for example, a product ID).</p> <p className="text-slate-600 m-0 text-base">You enter a number or text (for example, a product ID).</p>
</div> </div>
</li> </li>
<li className="flex gap-4"> <li className="flex gap-4">
<div className="w-8 h-8 mt-1 rounded-full bg-slate-100 flex items-center justify-center shrink-0 text-slate-600 font-bold text-sm">2</div> <div className="w-8 h-8 mt-1 rounded-full bg-slate-100 flex items-center justify-center shrink-0 text-slate-600 font-bold text-sm">2</div>
<div> <div>
<strong className="text-slate-900 block mb-1">Select Format</strong> <strong className="text-slate-900 block mb-1">Select Format</strong>
<p className="text-slate-600 m-0 text-base">You select a barcode format such as EAN-13 or Code 128.</p> <p className="text-slate-600 m-0 text-base">You select a barcode format such as EAN-13 or Code 128.</p>
</div> </div>
</li> </li>
<li className="flex gap-4"> <li className="flex gap-4">
<div className="w-8 h-8 mt-1 rounded-full bg-slate-100 flex items-center justify-center shrink-0 text-slate-600 font-bold text-sm">3</div> <div className="w-8 h-8 mt-1 rounded-full bg-slate-100 flex items-center justify-center shrink-0 text-slate-600 font-bold text-sm">3</div>
<div> <div>
<strong className="text-slate-900 block mb-1">Generate</strong> <strong className="text-slate-900 block mb-1">Generate</strong>
<p className="text-slate-600 m-0 text-base">The generator creates a scannable barcode image instantly.</p> <p className="text-slate-600 m-0 text-base">The generator creates a scannable barcode image instantly.</p>
</div> </div>
</li> </li>
<li className="flex gap-4"> <li className="flex gap-4">
<div className="w-8 h-8 mt-1 rounded-full bg-slate-100 flex items-center justify-center shrink-0 text-slate-600 font-bold text-sm">4</div> <div className="w-8 h-8 mt-1 rounded-full bg-slate-100 flex items-center justify-center shrink-0 text-slate-600 font-bold text-sm">4</div>
<div> <div>
<strong className="text-slate-900 block mb-1">Download</strong> <strong className="text-slate-900 block mb-1">Download</strong>
<p className="text-slate-600 m-0 text-base">You download or print the barcode for use.</p> <p className="text-slate-600 m-0 text-base">You download or print the barcode for use.</p>
</div> </div>
</li> </li>
</ul> </ul>
<p> <p>
A modern <strong>Barcode Generator</strong> works directly in the browser and does not require additional software. A modern <strong>Barcode Generator</strong> works directly in the browser and does not require additional software.
</p> </p>
<h2>Common Types of Barcodes</h2> <h2>Common Types of Barcodes</h2>
<p> <p>
Different barcode formats are used for different purposes. Choosing the right one is important for compatibility and scanning accuracy. Different barcode formats are used for different purposes. Choosing the right one is important for compatibility and scanning accuracy.
</p> </p>
<div className="grid md:grid-cols-2 gap-6 not-prose my-8"> <div className="grid md:grid-cols-2 gap-6 not-prose my-8">
{/* EAN-13 Card */} {/* EAN-13 Card */}
<div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow"> <div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-3 mb-3">
<Tag className="w-5 h-5 text-blue-500" /> <Tag className="w-5 h-5 text-blue-500" />
<h4 className="text-lg font-bold text-slate-900 m-0">EAN-13</h4> <h4 className="text-lg font-bold text-slate-900 m-0">EAN-13</h4>
</div> </div>
<div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Retail Europe</div> <div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Retail Europe</div>
<div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center"> <div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center">
<img src="/barcode-generator-preview.png" alt="EAN-13 Barcode Sample for International Products" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" /> <img src="/barcode-generator-preview.png" alt="EAN-13 Barcode Sample for International Products" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" />
</div> </div>
<p className="text-sm text-slate-600 m-0"> <p className="text-sm text-slate-600 m-0">
EAN-13 is widely used in retail, especially in Europe. It is designed for consumer products sold in stores and supermarkets. EAN-13 is widely used in retail, especially in Europe. It is designed for consumer products sold in stores and supermarkets.
</p> </p>
</div> </div>
{/* UPC-A Card */} {/* UPC-A Card */}
<div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow"> <div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-3 mb-3">
<ShoppingCart className="w-5 h-5 text-indigo-500" /> <ShoppingCart className="w-5 h-5 text-indigo-500" />
<h4 className="text-lg font-bold text-slate-900 m-0">UPC-A</h4> <h4 className="text-lg font-bold text-slate-900 m-0">UPC-A</h4>
</div> </div>
<div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Retail USA/Canada</div> <div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Retail USA/Canada</div>
<div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center"> <div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center">
<img src="/barcode-generator-preview.png" alt="UPC-A Barcode Example for Retail Products in USA" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" /> <img src="/barcode-generator-preview.png" alt="UPC-A Barcode Example for Retail Products in USA" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" />
</div> </div>
<p className="text-sm text-slate-600 m-0"> <p className="text-sm text-slate-600 m-0">
UPC-A is similar to EAN-13 but is mainly used in the United States and Canada for retail products. UPC-A is similar to EAN-13 but is mainly used in the United States and Canada for retail products.
</p> </p>
</div> </div>
{/* Code 128 Card */} {/* Code 128 Card */}
<div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow"> <div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-3 mb-3">
<Settings className="w-5 h-5 text-emerald-500" /> <Settings className="w-5 h-5 text-emerald-500" />
<h4 className="text-lg font-bold text-slate-900 m-0">Code 128</h4> <h4 className="text-lg font-bold text-slate-900 m-0">Code 128</h4>
</div> </div>
<div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Logistics Universal</div> <div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Logistics Universal</div>
<div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center"> <div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center">
<img src="/barcode-generator-preview.png" alt="Code 128 Barcode for Inventory and Shipping Labels" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" /> <img src="/barcode-generator-preview.png" alt="Code 128 Barcode for Inventory and Shipping Labels" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" />
</div> </div>
<p className="text-sm text-slate-600 m-0"> <p className="text-sm text-slate-600 m-0">
Code 128 is a flexible barcode format that supports letters and numbers. It is commonly used in logistics, shipping, and internal tracking systems. Code 128 is a flexible barcode format that supports letters and numbers. It is commonly used in logistics, shipping, and internal tracking systems.
</p> </p>
</div> </div>
{/* Code 39 Card */} {/* Code 39 Card */}
<div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow"> <div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-3 mb-3">
<Factory className="w-5 h-5 text-orange-500" /> <Factory className="w-5 h-5 text-orange-500" />
<h4 className="text-lg font-bold text-slate-900 m-0">Code 39</h4> <h4 className="text-lg font-bold text-slate-900 m-0">Code 39</h4>
</div> </div>
<div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Industrial Military</div> <div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Industrial Military</div>
<div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center"> <div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center">
<img src="/barcode-generator-preview.png" alt="Code 39 Barcode for Industrial Use" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" /> <img src="/barcode-generator-preview.png" alt="Code 39 Barcode for Industrial Use" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" />
</div> </div>
<p className="text-sm text-slate-600 m-0"> <p className="text-sm text-slate-600 m-0">
The first alphanumeric barcode, Code 39 is still widely used in automotive and defense industries. It supports numbers and uppercase letters. The first alphanumeric barcode, Code 39 is still widely used in automotive and defense industries. It supports numbers and uppercase letters.
</p> </p>
</div> </div>
{/* MSI Card */} {/* MSI Card */}
<div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow"> <div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-3 mb-3">
<Layers className="w-5 h-5 text-purple-500" /> <Layers className="w-5 h-5 text-purple-500" />
<h4 className="text-lg font-bold text-slate-900 m-0">MSI</h4> <h4 className="text-lg font-bold text-slate-900 m-0">MSI</h4>
</div> </div>
<div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Inventory Shelves</div> <div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Inventory Shelves</div>
<div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center"> <div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center">
<img src="/barcode-generator-preview.png" alt="MSI Barcode for Inventory Management" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" /> <img src="/barcode-generator-preview.png" alt="MSI Barcode for Inventory Management" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" />
</div> </div>
<p className="text-sm text-slate-600 m-0"> <p className="text-sm text-slate-600 m-0">
MSI (Modified Plessey) is often used for inventory control in retail environments, such as labeling shelves in supermarkets and warehouses. MSI (Modified Plessey) is often used for inventory control in retail environments, such as labeling shelves in supermarkets and warehouses.
</p> </p>
</div> </div>
{/* Pharmacode Card */} {/* Pharmacode Card */}
<div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow"> <div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-3 mb-3">
<Activity className="w-5 h-5 text-red-500" /> <Activity className="w-5 h-5 text-red-500" />
<h4 className="text-lg font-bold text-slate-900 m-0">Pharmacode</h4> <h4 className="text-lg font-bold text-slate-900 m-0">Pharmacode</h4>
</div> </div>
<div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Pharma Packaging</div> <div className="text-xs font-mono bg-slate-100 inline-block px-2 py-1 rounded text-slate-500 mb-3">Pharma Packaging</div>
<div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center"> <div className="mb-3 bg-slate-50 rounded border border-slate-100 p-2 flex justify-center">
<img src="/barcode-generator-preview.png" alt="Pharmacode for Pharmaceutical Packaging" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" /> <img src="/barcode-generator-preview.png" alt="Pharmacode for Pharmaceutical Packaging" className="h-10 object-contain opacity-75 grayscale" width="200" height="40" />
</div> </div>
<p className="text-sm text-slate-600 m-0"> <p className="text-sm text-slate-600 m-0">
Pharmacode is a specialized barcode standard used in the pharmaceutical industry for packaging control to prevent medication errors. Pharmacode is a specialized barcode standard used in the pharmaceutical industry for packaging control to prevent medication errors.
</p> </p>
</div> </div>
</div> </div>
<h2>Why Use a Barcode Generator?</h2> <h2>Why Use a Barcode Generator?</h2>
<p>Using a Barcode Generator offers several advantages:</p> <p>Using a Barcode Generator offers several advantages:</p>
<div className="not-prose grid gap-4 mb-8"> <div className="not-prose grid gap-4 mb-8">
<div className="flex gap-4 items-start"> <div className="flex gap-4 items-start">
<CheckCircle className="w-5 h-5 text-green-500 mt-1 shrink-0" /> <CheckCircle className="w-5 h-5 text-green-500 mt-1 shrink-0" />
<div> <div>
<h5 className="font-bold text-slate-900 m-0">Speed</h5> <h5 className="font-bold text-slate-900 m-0">Speed</h5>
<p className="text-slate-600 text-sm m-0">Create barcodes instantly without technical knowledge.</p> <p className="text-slate-600 text-sm m-0">Create barcodes instantly without technical knowledge.</p>
</div> </div>
</div> </div>
<div className="flex gap-4 items-start"> <div className="flex gap-4 items-start">
<CheckCircle className="w-5 h-5 text-green-500 mt-1 shrink-0" /> <CheckCircle className="w-5 h-5 text-green-500 mt-1 shrink-0" />
<div> <div>
<h5 className="font-bold text-slate-900 m-0">Accuracy</h5> <h5 className="font-bold text-slate-900 m-0">Accuracy</h5>
<p className="text-slate-600 text-sm m-0">Reduce manual data entry errors.</p> <p className="text-slate-600 text-sm m-0">Reduce manual data entry errors.</p>
</div> </div>
</div> </div>
<div className="flex gap-4 items-start"> <div className="flex gap-4 items-start">
<CheckCircle className="w-5 h-5 text-green-500 mt-1 shrink-0" /> <CheckCircle className="w-5 h-5 text-green-500 mt-1 shrink-0" />
<div> <div>
<h5 className="font-bold text-slate-900 m-0">Flexibility</h5> <h5 className="font-bold text-slate-900 m-0">Flexibility</h5>
<p className="text-slate-600 text-sm m-0">Generate barcodes for different formats and use cases.</p> <p className="text-slate-600 text-sm m-0">Generate barcodes for different formats and use cases.</p>
</div> </div>
</div> </div>
<div className="flex gap-4 items-start"> <div className="flex gap-4 items-start">
<CheckCircle className="w-5 h-5 text-green-500 mt-1 shrink-0" /> <CheckCircle className="w-5 h-5 text-green-500 mt-1 shrink-0" />
<div> <div>
<h5 className="font-bold text-slate-900 m-0">Cost-effective</h5> <h5 className="font-bold text-slate-900 m-0">Cost-effective</h5>
<p className="text-slate-600 text-sm m-0">No need for expensive software or hardware.</p> <p className="text-slate-600 text-sm m-0">No need for expensive software or hardware.</p>
</div> </div>
</div> </div>
</div> </div>
<p> <p>
For small businesses, online shops, and startups, a <strong>free Barcode Generator</strong> is often the easiest way to get started. For small businesses, online shops, and startups, a <strong>free Barcode Generator</strong> is often the easiest way to get started.
</p> </p>
<h2>Barcode vs QR Code</h2> <h2>Barcode vs QR Code</h2>
<p> <p>
Although barcodes and QR codes are often confused, they serve different purposes. A barcode stores data horizontally and is mainly used for product identification. A <Link href="/tools/url-qr-code" className="text-blue-600 hover:underline">QR code</Link> stores data both horizontally and vertically and can contain more complex information such as URLs or <Link href="/tools/vcard-qr-code" className="text-blue-600 hover:underline">contact details</Link>. Although barcodes and QR codes are often confused, they serve different purposes. A barcode stores data horizontally and is mainly used for product identification. A <Link href="/tools/url-qr-code" className="text-blue-600 hover:underline">QR code</Link> stores data both horizontally and vertically and can contain more complex information such as URLs or <Link href="/tools/vcard-qr-code" className="text-blue-600 hover:underline">contact details</Link>.
</p> </p>
<p> <p>
If you only need to identify products or inventory items, a classic barcode is usually the better choice. If you only need to identify products or inventory items, a classic barcode is usually the better choice.
</p> </p>
<h2>Are Barcodes Free to Use?</h2> <h2>Are Barcodes Free to Use?</h2>
<p> <p>
The barcode image itself can be generated for free using a Barcode Generator. However, for retail products sold internationally, the barcode number may need to be officially registered through organizations such as GS1. This ensures that the barcode is unique and recognized globally. The barcode image itself can be generated for free using a Barcode Generator. However, for retail products sold internationally, the barcode number may need to be officially registered through organizations such as GS1. This ensures that the barcode is unique and recognized globally.
</p> </p>
<p> <p>
For internal use, testing, or small projects, free barcode generation is usually sufficient. For internal use, testing, or small projects, free barcode generation is usually sufficient.
</p> </p>
<h2>Use Cases for Barcodes</h2> <h2>Use Cases for Barcodes</h2>
<p>Barcodes are used in many industries, including:</p> <p>Barcodes are used in many industries, including:</p>
<ul className="list-disc pl-6 space-y-2 mb-8"> <ul className="list-disc pl-6 space-y-2 mb-8">
<li>Retail and e-commerce</li> <li>Retail and e-commerce</li>
<li>Inventory and warehouse management</li> <li>Inventory and warehouse management</li>
<li>Shipping and logistics</li> <li>Shipping and logistics</li>
<li>Libraries and document tracking</li> <li>Libraries and document tracking</li>
<li>Event tickets and labeling</li> <li>Event tickets and labeling</li>
</ul> </ul>
<p> <p>
A reliable <strong>Barcode Generator</strong> helps streamline these processes and improves efficiency. A reliable <strong>Barcode Generator</strong> helps streamline these processes and improves efficiency.
</p> </p>
<h2>Understanding Check Digits</h2> <h2>Understanding Check Digits</h2>
<p> <p>
Most barcodes (like EAN and UPC) include a "Check Digit"the last number in the sequence. This digit is calculated mathematically from the other numbers to ensure the barcode is scanned correctly. Even if a barcode is slightly damaged or scratched, the scanner uses the check digit to verify the integrity of the data. Most barcodes (like EAN and UPC) include a "Check Digit"the last number in the sequence. This digit is calculated mathematically from the other numbers to ensure the barcode is scanned correctly. Even if a barcode is slightly damaged or scratched, the scanner uses the check digit to verify the integrity of the data.
</p> </p>
<h2>Best Practices for Printing Barcodes</h2> <h2>Best Practices for Printing Barcodes</h2>
<p> <p>
To ensure your barcodes scan instantly at the checkout or in the warehouse, follow these printing tips: To ensure your barcodes scan instantly at the checkout or in the warehouse, follow these printing tips:
</p> </p>
<ul className="list-disc pl-6 space-y-2 mb-8"> <ul className="list-disc pl-6 space-y-2 mb-8">
<li><strong>High Contrast:</strong> Always print black bars on a white background. Reverse colors (white bars on black) often fail to scan.</li> <li><strong>High Contrast:</strong> Always print black bars on a white background. Reverse colors (white bars on black) often fail to scan.</li>
<li><strong>Quiet Zone:</strong> Leave enough white space (margins) on the left and right sides of the barcode.</li> <li><strong>Quiet Zone:</strong> Leave enough white space (margins) on the left and right sides of the barcode.</li>
<li><strong>Resolution:</strong> For professional labels, use <strong>SVG format</strong> (vector) or high-resolution PNGs (at least 300 DPI) to avoid blurry edges.</li> <li><strong>Resolution:</strong> For professional labels, use <strong>SVG format</strong> (vector) or high-resolution PNGs (at least 300 DPI) to avoid blurry edges.</li>
<li><strong>Size:</strong> Do not scale the barcode too small. Standard EAN-13 codes should be at least 30mm wide for reliable scanning.</li> <li><strong>Size:</strong> Do not scale the barcode too small. Standard EAN-13 codes should be at least 30mm wide for reliable scanning.</li>
</ul> </ul>
<hr className="my-12 border-slate-200" /> <hr className="my-12 border-slate-200" />
<div className="flex items-center gap-3 mb-6 not-prose"> <div className="flex items-center gap-3 mb-6 not-prose">
<HelpCircle className="w-6 h-6 text-blue-500" /> <HelpCircle className="w-6 h-6 text-blue-500" />
<h2 className="text-2xl font-bold text-slate-900 m-0">Frequently Asked Questions (FAQ)</h2> <h2 className="text-2xl font-bold text-slate-900 m-0">Frequently Asked Questions (FAQ)</h2>
</div> </div>
<div className="not-prose space-y-8"> <div className="not-prose space-y-8">
<div> <div>
<h5 className="font-bold text-slate-900 text-lg mb-2"> What is a Barcode Generator?</h5> <h5 className="font-bold text-slate-900 text-lg mb-2"> What is a Barcode Generator?</h5>
<p className="text-slate-600">A Barcode Generator is an online tool that converts numbers or text into scannable barcode images that can be used for products, labels, and inventory systems.</p> <p className="text-slate-600">A Barcode Generator is an online tool that converts numbers or text into scannable barcode images that can be used for products, labels, and inventory systems.</p>
</div> </div>
<div> <div>
<h5 className="font-bold text-slate-900 text-lg mb-2"> Is this barcode generator free to use?</h5> <h5 className="font-bold text-slate-900 text-lg mb-2"> Is this barcode generator free to use?</h5>
<p className="text-slate-600">Yes, our online barcode generator is completely free to use with no hidden costs or sign-ups required. You can generate, download, and print barcodes instantly.</p> <p className="text-slate-600">Yes, our online barcode generator is completely free to use with no hidden costs or sign-ups required. You can generate, download, and print barcodes instantly.</p>
</div> </div>
<div> <div>
<h5 className="font-bold text-slate-900 text-lg mb-2"> Which barcode format should I use?</h5> <h5 className="font-bold text-slate-900 text-lg mb-2"> Which barcode format should I use?</h5>
<p className="text-slate-600"> <p className="text-slate-600">
<strong>EAN-13:</strong> Standard for retail products in Europe and globally.<br /> <strong>EAN-13:</strong> Standard for retail products in Europe and globally.<br />
<strong>UPC-A:</strong> Standard for retail products in USA/Canada.<br /> <strong>UPC-A:</strong> Standard for retail products in USA/Canada.<br />
<strong>Code 128:</strong> Best for logistics, shipping, and internal tracking (supports letters & numbers). <strong>Code 128:</strong> Best for logistics, shipping, and internal tracking (supports letters & numbers).
</p> </p>
</div> </div>
<div> <div>
<h5 className="font-bold text-slate-900 text-lg mb-2"> Can I download barcodes in vector format (SVG)?</h5> <h5 className="font-bold text-slate-900 text-lg mb-2"> Can I download barcodes in vector format (SVG)?</h5>
<p className="text-slate-600">Yes! We offer <strong>SVG downloads</strong>. SVG files are vector-based, meaning they can be scaled to any size without losing qualityperfect for professional product packaging.</p> <p className="text-slate-600">Yes! We offer <strong>SVG downloads</strong>. SVG files are vector-based, meaning they can be scaled to any size without losing qualityperfect for professional product packaging.</p>
</div> </div>
<div> <div>
<h5 className="font-bold text-slate-900 text-lg mb-2"> How do I generate a barcode online?</h5> <h5 className="font-bold text-slate-900 text-lg mb-2"> How do I generate a barcode online?</h5>
<p className="text-slate-600">To generate a barcode online, enter your product number or text, select the desired barcode format (such as EAN-13 or Code 128), and click the generate button. The barcode will be created instantly.</p> <p className="text-slate-600">To generate a barcode online, enter your product number or text, select the desired barcode format (such as EAN-13 or Code 128), and click the generate button. The barcode will be created instantly.</p>
</div> </div>
<div> <div>
<h5 className="font-bold text-slate-900 text-lg mb-2"> Are generated barcodes scannable?</h5> <h5 className="font-bold text-slate-900 text-lg mb-2"> Are generated barcodes scannable?</h5>
<p className="text-slate-600">Yes, barcodes generated with a proper barcode generator are fully scannable. We generate standard-compliant barcodes readable by any standard optical or laser barcode scanner.</p> <p className="text-slate-600">Yes, barcodes generated with a proper barcode generator are fully scannable. We generate standard-compliant barcodes readable by any standard optical or laser barcode scanner.</p>
</div> </div>
<div> <div>
<h5 className="font-bold text-slate-900 text-lg mb-2"> Can I use these barcodes for Amazon (EAN/UPC)?</h5> <h5 className="font-bold text-slate-900 text-lg mb-2"> Can I use these barcodes for Amazon (EAN/UPC)?</h5>
<p className="text-slate-600">You can generate the <em>image</em> for Amazon here if you already have your EAN/UPC number. However, you cannot "create" a valid global EAN number hereyou must purchase those official numbers from GS1 to sell on major platforms like Amazon.</p> <p className="text-slate-600">You can generate the <em>image</em> for Amazon here if you already have your EAN/UPC number. However, you cannot "create" a valid global EAN number hereyou must purchase those official numbers from GS1 to sell on major platforms like Amazon.</p>
</div> </div>
<div> <div>
<h5 className="font-bold text-slate-900 text-lg mb-2"> What is the difference between a barcode and a QR code?</h5> <h5 className="font-bold text-slate-900 text-lg mb-2"> What is the difference between a barcode and a QR code?</h5>
<p className="text-slate-600">A barcode stores data horizontally (1D) and is mainly used for product IDs. A QR code stores data in 2D (matrix) and can hold much more information, such as URLs, vCards, or WiFi credentials.</p> <p className="text-slate-600">A barcode stores data horizontally (1D) and is mainly used for product IDs. A QR code stores data in 2D (matrix) and can hold much more information, such as URLs, vCards, or WiFi credentials.</p>
</div> </div>
</div> </div>
<div className="mt-12 p-6 bg-slate-900 rounded-xl text-white not-prose"> <div className="mt-12 p-6 bg-slate-900 rounded-xl text-white not-prose">
<h4 className="text-lg font-bold mb-2">Final Thoughts</h4> <h4 className="text-lg font-bold mb-2">Final Thoughts</h4>
<p className="text-slate-300 m-0"> <p className="text-slate-300 m-0">
A Barcode Generator is a simple yet powerful tool that helps businesses save time, reduce errors, and improve operational efficiency. By choosing the right barcode format and using a reliable generator, you can create professional barcodes that work across different systems and industries. A Barcode Generator is a simple yet powerful tool that helps businesses save time, reduce errors, and improve operational efficiency. By choosing the right barcode format and using a reliable generator, you can create professional barcodes that work across different systems and industries.
</p> </p>
</div> </div>
</div> </div>
</section> </section>
); );
} }

View File

@ -9,7 +9,7 @@ export const metadata: Metadata = {
default: 'QR Master QR Code Generator & Analytics', default: 'QR Master QR Code Generator & Analytics',
template: '%s | QR Master', template: '%s | QR Master',
}, },
description: 'Erstellen Sie dynamische QR Codes, verfolgen Sie Scans und skalieren Sie Kampagnen mit sicheren Analysen.', description: 'Erstellen Sie dynamische QR Codes für Feedback, PDF, Coupons und App Stores. Verfolgen Sie Scans und skalieren Sie Kampagnen mit sicheren Analysen.',
metadataBase: new URL('https://www.qrmaster.net'), metadataBase: new URL('https://www.qrmaster.net'),
icons: { icons: {
icon: [ icon: [
@ -22,7 +22,7 @@ export const metadata: Metadata = {
type: 'website', type: 'website',
siteName: 'QR Master', siteName: 'QR Master',
title: 'QR Master QR Code Generator & Analytics', title: 'QR Master QR Code Generator & Analytics',
description: 'Erstellen Sie dynamische QR Codes, verfolgen Sie Scans und skalieren Sie Kampagnen mit sicheren Analysen.', description: 'Erstellen Sie dynamische QR Codes für Feedback, PDF, Coupons und App Stores. Verfolgen Sie Scans und skalieren Sie Kampagnen mit sicheren Analysen.',
url: 'https://www.qrmaster.net/qr-code-erstellen', url: 'https://www.qrmaster.net/qr-code-erstellen',
locale: 'de_DE', locale: 'de_DE',
images: [ images: [
@ -42,30 +42,24 @@ export const metadata: Metadata = {
robots: { index: true, follow: true }, robots: { index: true, follow: true },
}; };
export default function RootMarketingDeLayout({ export default function MarketingDeGroupLayout({
children, children,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
return ( return (
<html lang="de"> <>
<head> <script
<script type="application/ld+json"
type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema()) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema()) }} />
/> <script
<script type="application/ld+json"
type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteSchema()) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteSchema()) }} />
/> <MarketingDeLayout>
</head> {children}
<body className="font-sans"> </MarketingDeLayout>
<Providers> </>
<MarketingDeLayout>
{children}
</MarketingDeLayout>
</Providers>
</body>
</html>
); );
} }

View File

@ -22,7 +22,7 @@ function truncateAtWord(text: string, maxLength: number): string {
export async function generateMetadata(): Promise<Metadata> { export async function generateMetadata(): Promise<Metadata> {
const title = 'QR Code Erstellen Kostenlos | QR Master'; const title = 'QR Code Erstellen Kostenlos | QR Master';
const description = 'Erstellen Sie QR Codes kostenlos in Sekunden. Dynamische QR-Codes mit Tracking, Branding und Massen-Erstellung. Für immer kostenlos.'; const description = 'Erstellen Sie QR Codes kostenlos in Sekunden. Dynamische QR-Codes für Feedback, PDF, Coupons & App Stores. Mit Tracking, Branding und Massen-Erstellung. Für immer kostenlos.';
return { return {
title: { title: {
@ -37,7 +37,11 @@ export async function generateMetadata(): Promise<Metadata> {
'qr codes erstellen', 'qr codes erstellen',
'qr code erstellen kostenlos', 'qr code erstellen kostenlos',
'dynamischer qr code', 'dynamischer qr code',
'qr code mit logo' 'qr code mit logo',
'feedback qr code',
'pdf qr code',
'coupon qr code',
'app store qr code'
], ],
alternates: { alternates: {
canonical: 'https://www.qrmaster.net/qr-code-erstellen', canonical: 'https://www.qrmaster.net/qr-code-erstellen',
@ -84,7 +88,7 @@ export default function QRCodeErstellenPage() {
<div className="sr-only" aria-hidden="false"> <div className="sr-only" aria-hidden="false">
<p> <p>
Erstellen Sie professionelle QR Codes für Ihr Unternehmen mit QR Master. Unser dynamischer QR Code Generator Erstellen Sie professionelle QR Codes für Ihr Unternehmen mit QR Master. Unser dynamischer QR Code Generator
ermöglicht es Ihnen, trackbare QR Codes zu erstellen, Ziel-URLs jederzeit zu ändern und detaillierte Statistiken einzusehen. ermöglicht es Ihnen, trackbare QR Codes für Feedback, PDF-Dateien, Coupons und App Stores zu erstellen, Ziel-URLs jederzeit zu ändern und detaillierte Statistiken einzusehen.
Perfekt für Restaurants, Einzelhandel, Events und Marketing-Kampagnen. Perfekt für Restaurants, Einzelhandel, Events und Marketing-Kampagnen.
</p> </p>
<p> <p>
@ -93,7 +97,7 @@ export default function QRCodeErstellenPage() {
vCard QR Codes für digitale Visitenkarten und QR Codes für Restaurant-Speisekarten. vCard QR Codes für digitale Visitenkarten und QR Codes für Restaurant-Speisekarten.
</p> </p>
<p> <p>
Starten Sie kostenlos mit 3 dynamischen QR Codes und unbegrenzten statischen Codes. Upgrade auf Pro für 50 Codes Starten Sie kostenlos mit 8 dynamischen QR Codes und unbegrenzten statischen Codes. Upgrade auf Pro für 50 Codes
mit erweiterten Statistiken, oder Business für 500 Codes mit Massen-Erstellung und Prioritäts-Support. mit erweiterten Statistiken, oder Business für 500 Codes mit Massen-Erstellung und Prioritäts-Support.
</p> </p>
</div> </div>

View File

@ -142,7 +142,7 @@ export default function CouponPage() {
{/* Redeem Button */} {/* Redeem Button */}
{coupon.redeemUrl && !isExpired && ( {coupon.redeemUrl && !isExpired && (
<a <a
href={coupon.redeemUrl} href={ensureAbsoluteUrl(coupon.redeemUrl)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="block w-full py-4 rounded-xl font-semibold text-center bg-gradient-to-r from-[#076653] to-[#0C342C] text-white hover:from-[#087861] hover:to-[#0E4036] transition-all shadow-lg shadow-emerald-200" className="block w-full py-4 rounded-xl font-semibold text-center bg-gradient-to-r from-[#076653] to-[#0C342C] text-white hover:from-[#087861] hover:to-[#0E4036] transition-all shadow-lg shadow-emerald-200"
@ -165,3 +165,11 @@ export default function CouponPage() {
</div> </div>
); );
} }
function ensureAbsoluteUrl(url: string | undefined): string | undefined {
if (!url) return undefined;
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) {
return url;
}
return `https://${url}`;
}

View File

@ -55,7 +55,8 @@ export default function FeedbackPage() {
if (rating >= 4 && feedback?.googleReviewUrl) { if (rating >= 4 && feedback?.googleReviewUrl) {
setTimeout(() => { setTimeout(() => {
window.location.href = feedback.googleReviewUrl!; const url = ensureAbsoluteUrl(feedback.googleReviewUrl);
if (url) window.location.href = url;
}, 2000); }, 2000);
} }
} catch (error) { } catch (error) {
@ -119,9 +120,9 @@ export default function FeedbackPage() {
{/* Card */} {/* Card */}
<div className="bg-white rounded-3xl shadow-xl overflow-hidden"> <div className="bg-white rounded-3xl shadow-xl overflow-hidden">
{/* Colored Header */} {/* Colored Header */}
<div className="bg-gradient-to-r from-[#4C5F4E] via-[#C6C0B3] to-[#FAF8F5] p-8 text-center"> <div className="bg-gradient-to-r from-[#FAF8F5] via-[#C6C0B3] to-[#4C5F4E] p-8 text-center">
<div className="w-14 h-14 bg-white/20 rounded-2xl flex items-center justify-center mx-auto mb-4"> <div className="w-14 h-14 bg-[#4C5F4E]/10 rounded-2xl flex items-center justify-center mx-auto mb-4">
<Star className="w-7 h-7 text-white" /> <Star className="w-7 h-7 text-[#4C5F4E]" />
</div> </div>
<h1 className="text-2xl font-bold mb-1 text-gray-900">How was your experience?</h1> <h1 className="text-2xl font-bold mb-1 text-gray-900">How was your experience?</h1>
<p className="text-gray-700">{feedback.businessName}</p> <p className="text-gray-700">{feedback.businessName}</p>
@ -193,3 +194,11 @@ export default function FeedbackPage() {
</div> </div>
); );
} }
function ensureAbsoluteUrl(url: string | undefined): string | undefined {
if (!url) return undefined;
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) {
return url;
}
return `https://${url}`;
}

View File

@ -1,72 +1,65 @@
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { Suspense } from 'react'; import { Suspense } from 'react';
import '@/styles/globals.css'; import '@/styles/globals.css';
import { ToastContainer } from '@/components/ui/Toast'; import { Providers } from '@/components/Providers';
import AuthProvider from '@/components/SessionProvider';
import { PostHogProvider } from '@/components/PostHogProvider'; const isIndexable = process.env.NEXT_PUBLIC_INDEXABLE === 'true';
import CookieBanner from '@/components/CookieBanner';
export const metadata: Metadata = {
const isIndexable = process.env.NEXT_PUBLIC_INDEXABLE === 'true'; metadataBase: new URL('https://www.qrmaster.net'),
title: {
export const metadata: Metadata = { default: 'QR Master Smart QR Generator & Analytics',
metadataBase: new URL('https://www.qrmaster.net'), template: '%s | QR Master',
title: { },
default: 'QR Master Smart QR Generator & Analytics', description: 'Create dynamic QR codes, track scans, and scale campaigns with secure analytics.',
template: '%s | QR Master', keywords: 'QR code, QR generator, dynamic QR, QR tracking, QR analytics, branded QR, bulk QR generator',
}, robots: isIndexable
description: 'Create dynamic QR codes, track scans, and scale campaigns with secure analytics.', ? { index: true, follow: true }
keywords: 'QR code, QR generator, dynamic QR, QR tracking, QR analytics, branded QR, bulk QR generator', : { index: false, follow: false },
robots: isIndexable icons: {
? { index: true, follow: true } icon: [
: { index: false, follow: false }, { url: '/favicon.svg', type: 'image/svg+xml' },
icons: { { url: '/logo.svg', type: 'image/svg+xml' },
icon: [ ],
{ url: '/favicon.svg', type: 'image/svg+xml' }, apple: '/logo.svg',
{ url: '/logo.svg', type: 'image/svg+xml' }, },
], twitter: {
apple: '/logo.svg', card: 'summary_large_image',
}, site: '@qrmaster',
twitter: { images: ['https://www.qrmaster.net/static/og-image.png'],
card: 'summary_large_image', },
site: '@qrmaster', openGraph: {
images: ['https://www.qrmaster.net/static/og-image.png'], type: 'website',
}, siteName: 'QR Master',
openGraph: { title: 'QR Master Smart QR Generator & Analytics',
type: 'website', description: 'Create dynamic QR codes, track scans, and scale campaigns with secure analytics.',
siteName: 'QR Master', url: 'https://www.qrmaster.net',
title: 'QR Master Smart QR Generator & Analytics', images: [
description: 'Create dynamic QR codes, track scans, and scale campaigns with secure analytics.', {
url: 'https://www.qrmaster.net', url: 'https://www.qrmaster.net/static/og-image.png',
images: [ width: 1200,
{ height: 630,
url: 'https://www.qrmaster.net/static/og-image.png', alt: 'QR Master - Dynamic QR Code Generator and Analytics Platform',
width: 1200, },
height: 630, ],
alt: 'QR Master - Dynamic QR Code Generator and Analytics Platform', locale: 'en_US',
}, },
], };
locale: 'en_US',
}, export default function RootLayout({
}; children,
}: {
export default function RootLayout({ children: React.ReactNode;
children, }) {
}: { return (
children: React.ReactNode; <html lang="en">
}) { <body className="font-sans">
return ( <Suspense fallback={null}>
<html lang="en"> <Providers>
<body className="font-sans"> {children}
<Suspense fallback={null}> </Providers>
<PostHogProvider> </Suspense>
<AuthProvider> </body>
{children} </html>
</AuthProvider> );
<CookieBanner />
<ToastContainer />
</PostHogProvider>
</Suspense>
</body>
</html>
);
} }

View File

@ -32,7 +32,7 @@ export async function GET(
switch (qrCode.contentType) { switch (qrCode.contentType) {
case 'URL': case 'URL':
destination = content.url || 'https://example.com'; destination = ensureAbsoluteUrl(content.url);
break; break;
case 'PHONE': case 'PHONE':
destination = `tel:${content.phone}`; destination = `tel:${content.phone}`;
@ -61,7 +61,7 @@ export async function GET(
break; break;
case 'PDF': case 'PDF':
// Direct link to file // Direct link to file
destination = content.fileUrl || 'https://example.com/file.pdf'; destination = ensureAbsoluteUrl(content.fileUrl);
break; break;
case 'APP': case 'APP':
// Smart device detection for app stores // Smart device detection for app stores
@ -70,11 +70,11 @@ export async function GET(
const isAndroid = /android/i.test(userAgent); const isAndroid = /android/i.test(userAgent);
if (isIOS && content.iosUrl) { if (isIOS && content.iosUrl) {
destination = content.iosUrl; destination = ensureAbsoluteUrl(content.iosUrl);
} else if (isAndroid && content.androidUrl) { } else if (isAndroid && content.androidUrl) {
destination = content.androidUrl; destination = ensureAbsoluteUrl(content.androidUrl);
} else { } else {
destination = content.fallbackUrl || content.iosUrl || content.androidUrl || 'https://example.com'; destination = ensureAbsoluteUrl(content.fallbackUrl || content.iosUrl || content.androidUrl);
} }
break; break;
case 'COUPON': case 'COUPON':
@ -234,7 +234,16 @@ async function trackScan(qrId: string, request: NextRequest) {
}, },
}); });
} catch (error) { } catch (error) {
console.error('Error tracking scan:', error);
// Don't throw - this is fire and forget // Don't throw - this is fire and forget
} }
}
function ensureAbsoluteUrl(url: string): string {
if (!url) return 'https://example.com';
// Check if it already has a protocol (http://, https://, myapp://, mailto:, etc.)
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) {
return url;
}
// Default to https for web URLs
return `https://${url}`;
} }

View File

@ -4,17 +4,36 @@ 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 }) { import { Suspense } from 'react';
export function PostHogPageView() {
const pathname = usePathname(); const pathname = usePathname();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const [isInitialized, setIsInitialized] = useState(false);
const initializationAttempted = useRef(false); useEffect(() => {
const cookieConsent = localStorage.getItem('cookieConsent');
if (cookieConsent === 'accepted' && pathname && (posthog as any)._loaded) {
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, setInitializationAttempted] = useState(false);
// Initialize PostHog once // Initialize PostHog once
useEffect(() => { useEffect(() => {
// Prevent double initialization in React Strict Mode if (initializationAttempted) return;
if (initializationAttempted.current) return; setInitializationAttempted(true);
initializationAttempted.current = true;
const cookieConsent = localStorage.getItem('cookieConsent'); const cookieConsent = localStorage.getItem('cookieConsent');
@ -27,50 +46,32 @@ 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,
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
} }
} }
}, [initializationAttempted]);
// NO cleanup function - PostHog should persist across page navigation return (
}, []); <>
<Suspense fallback={null}>
// Track page views ONLY after PostHog is initialized <PostHogPageView />
useEffect(() => { </Suspense>
const cookieConsent = localStorage.getItem('cookieConsent'); {children}
</>
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}</>;
} }
/** /**

View File

@ -3,15 +3,12 @@
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, PostHogPageView } from '@/components/PostHogProvider'; import { PostHogProvider } 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 (
<PostHogProvider> <PostHogProvider>
<Suspense fallback={null}>
<PostHogPageView />
</Suspense>
<AuthProvider> <AuthProvider>
{children} {children}
</AuthProvider> </AuthProvider>

View File

@ -8,6 +8,8 @@ import { Input } from '@/components/ui/Input';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
import { Badge } from '@/components/ui/Badge'; import { Badge } from '@/components/ui/Badge';
import { calculateContrast } from '@/lib/utils'; import { calculateContrast } from '@/lib/utils';
import { trackEvent } from '@/components/PostHogProvider';
import { useEffect } from 'react';
interface InstantGeneratorProps { interface InstantGeneratorProps {
t: any; // i18n translation function t: any; // i18n translation function
@ -20,6 +22,18 @@ export const InstantGenerator: React.FC<InstantGeneratorProps> = ({ t }) => {
const [cornerStyle, setCornerStyle] = useState('square'); const [cornerStyle, setCornerStyle] = useState('square');
const [size, setSize] = useState(200); const [size, setSize] = useState(200);
useEffect(() => {
if (url) {
trackEvent('instant_qr_generated', {
url_length: url.length,
foreground: foregroundColor,
background: backgroundColor,
corner_style: cornerStyle,
size: size
});
}
}, [url, foregroundColor, backgroundColor, cornerStyle, size]);
const contrast = calculateContrast(foregroundColor, backgroundColor); const contrast = calculateContrast(foregroundColor, backgroundColor);
const hasGoodContrast = contrast >= 4.5; const hasGoodContrast = contrast >= 4.5;
@ -38,6 +52,7 @@ export const InstantGenerator: React.FC<InstantGeneratorProps> = ({ t }) => {
a.click(); a.click();
document.body.removeChild(a); document.body.removeChild(a);
URL.revokeObjectURL(downloadUrl); URL.revokeObjectURL(downloadUrl);
trackEvent('instant_qr_downloaded', { format: 'svg' });
} else { } else {
// Convert SVG to PNG using Canvas // Convert SVG to PNG using Canvas
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
@ -65,6 +80,7 @@ export const InstantGenerator: React.FC<InstantGeneratorProps> = ({ t }) => {
a.click(); a.click();
document.body.removeChild(a); document.body.removeChild(a);
URL.revokeObjectURL(downloadUrl); URL.revokeObjectURL(downloadUrl);
trackEvent('instant_qr_downloaded', { format: 'png' });
} }
}); });
URL.revokeObjectURL(url); URL.revokeObjectURL(url);

View File

@ -130,7 +130,7 @@
"price": "€0", "price": "€0",
"period": "für immer", "period": "für immer",
"features": [ "features": [
"3 dynamische QR-Codes", "8 dynamische QR-Codes",
"Unbegrenzte statische QR-Codes", "Unbegrenzte statische QR-Codes",
"Basis-Scan-Tracking", "Basis-Scan-Tracking",
"Standard QR-Design-Vorlagen" "Standard QR-Design-Vorlagen"

View File

@ -137,7 +137,7 @@
"price": "€0", "price": "€0",
"period": "forever", "period": "forever",
"features": [ "features": [
"3 dynamic QR codes", "8 dynamic QR codes",
"Unlimited static QR codes", "Unlimited static QR codes",
"Basic scan tracking", "Basic scan tracking",
"Standard QR design templates", "Standard QR design templates",

View File

@ -1,11 +1,11 @@
import { PrismaClient } from '@prisma/client'; import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as { const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined; prisma: PrismaClient | undefined;
}; };
export const db = export const db =
globalForPrisma.prisma ?? globalForPrisma.prisma ??
new PrismaClient(); new PrismaClient();
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db; if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db;