diff --git a/Dockerfile b/Dockerfile
index 3ade151..89f9a89 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,64 +1,64 @@
-# ---- deps ----
-FROM node:20-alpine AS deps
-# Install OpenSSL for Prisma
-RUN apk add --no-cache openssl
-WORKDIR /app
-COPY package.json package-lock.json* pnpm-lock.yaml* yarn.lock* .npmrc* ./
-# Copy prisma schema for postinstall script
-COPY prisma ./prisma
-RUN \
- if [ -f pnpm-lock.yaml ]; then \
- npm i -g pnpm && pnpm i --frozen-lockfile; \
- elif [ -f yarn.lock ]; then \
- yarn --frozen-lockfile; \
- elif [ -f package-lock.json ]; then \
- npm ci; \
- else \
- npm install --legacy-peer-deps; \
- fi
-
-# ---- builder ----
-FROM node:20-alpine AS builder
-# Install OpenSSL for Prisma
-RUN apk add --no-cache openssl
-WORKDIR /app
-COPY --from=deps /app/node_modules ./node_modules
-COPY . .
-ENV NEXT_TELEMETRY_DISABLED=1
-# Add build-time environment variables with defaults
-ENV NEXTAUTH_URL="https://www.qrmaster.net"
-ENV NEXTAUTH_SECRET="build-time-secret"
-ENV IP_SALT="build-time-salt"
-ENV STRIPE_SECRET_KEY="sk_test_placeholder_for_build"
-ENV RESEND_API_KEY="re_placeholder_for_build"
-ENV NEXT_PUBLIC_APP_URL="https://www.qrmaster.net"
-# PostHog Analytics - REQUIRED at build time for client-side bundle
-ENV NEXT_PUBLIC_POSTHOG_KEY="phc_97JBJVVQlqqiZuTVRHuBnnG9HasOv3GSsdeVjossizJ"
-ENV NEXT_PUBLIC_POSTHOG_HOST="https://us.i.posthog.com"
-ENV NEXT_PUBLIC_INDEXABLE="true"
-ENV NEXT_PUBLIC_FACEBOOK_PIXEL_ID="1601718491252690"
-RUN npx prisma generate
-RUN npm run build
-
-# ---- runner ----
-FROM node:20-alpine AS runner
-# Install OpenSSL for Prisma runtime
-RUN apk add --no-cache openssl
-WORKDIR /app
-ENV NODE_ENV=production
-ENV NEXT_TELEMETRY_DISABLED=1
-
-RUN addgroup --system --gid 1001 nodejs
-RUN adduser --system --uid 1001 nextjs
-
-COPY --from=builder /app/node_modules ./node_modules
-COPY --from=builder /app/prisma ./prisma
-COPY --from=builder /app/.next/standalone ./
-COPY --from=builder /app/.next/static ./.next/static
-COPY --from=builder /app/public ./public
-
-USER nextjs
-
-EXPOSE 3000
-
+# ---- deps ----
+FROM node:20-alpine AS deps
+# Install OpenSSL for Prisma
+RUN apk add --no-cache openssl
+WORKDIR /app
+COPY package.json package-lock.json* pnpm-lock.yaml* yarn.lock* .npmrc* ./
+# Copy prisma schema for postinstall script
+COPY prisma ./prisma
+RUN \
+ if [ -f pnpm-lock.yaml ]; then \
+ npm i -g pnpm && pnpm i --frozen-lockfile; \
+ elif [ -f yarn.lock ]; then \
+ yarn --frozen-lockfile; \
+ elif [ -f package-lock.json ]; then \
+ npm ci; \
+ else \
+ npm install --legacy-peer-deps; \
+ fi
+
+# ---- builder ----
+FROM node:20-alpine AS builder
+# Install OpenSSL for Prisma
+RUN apk add --no-cache openssl
+WORKDIR /app
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+ENV NEXT_TELEMETRY_DISABLED=1
+# Add build-time environment variables with defaults
+ENV NEXTAUTH_URL="https://www.qrmaster.net"
+ENV NEXTAUTH_SECRET="build-time-secret"
+ENV IP_SALT="build-time-salt"
+ENV STRIPE_SECRET_KEY="sk_test_placeholder_for_build"
+ENV RESEND_API_KEY="re_placeholder_for_build"
+ENV NEXT_PUBLIC_APP_URL="https://www.qrmaster.net"
+# PostHog Analytics - REQUIRED at build time for client-side bundle
+ENV NEXT_PUBLIC_POSTHOG_KEY="phc_97JBJVVQlqqiZuTVRHuBnnG9HasOv3GSsdeVjossizJ"
+ENV NEXT_PUBLIC_POSTHOG_HOST="https://us.i.posthog.com"
+ENV NEXT_PUBLIC_INDEXABLE="true"
+ENV NEXT_PUBLIC_FACEBOOK_PIXEL_ID="1601718491252690"
+RUN npx prisma generate
+RUN npm run build
+
+# ---- runner ----
+FROM node:20-alpine AS runner
+# Install OpenSSL for Prisma runtime
+RUN apk add --no-cache openssl
+WORKDIR /app
+ENV NODE_ENV=production
+ENV NEXT_TELEMETRY_DISABLED=1
+
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+
+COPY --from=builder /app/node_modules ./node_modules
+COPY --from=builder /app/prisma ./prisma
+COPY --from=builder /app/.next/standalone ./
+COPY --from=builder /app/.next/static ./.next/static
+COPY --from=builder /app/public ./public
+
+USER nextjs
+
+EXPOSE 3000
+
CMD ["node", "server.js"]
\ No newline at end of file
diff --git a/public/bb6dfaacf1ed41a880281c426c54ed7c.txt b/public/bb6dfaacf1ed41a880281c426c54ed7c.txt
index f202b25..44c1146 100644
--- a/public/bb6dfaacf1ed41a880281c426c54ed7c.txt
+++ b/public/bb6dfaacf1ed41a880281c426c54ed7c.txt
@@ -1 +1 @@
-bb6dfaacf1ed41a880281c426c54ed7c
+bb6dfaacf1ed41a880281c426c54ed7c
diff --git a/scripts/validate-data.ts b/scripts/validate-data.ts
new file mode 100644
index 0000000..b9498d2
--- /dev/null
+++ b/scripts/validate-data.ts
@@ -0,0 +1,41 @@
+import { blogPosts } from '../src/lib/blog-data';
+import { authors } from '../src/lib/author-data';
+import { getPublishedPosts, getPostsByAuthor } from '../src/lib/content';
+
+console.log("Validating Author Data...");
+authors.forEach(author => {
+ console.log(`Checking author: ${author.slug}`);
+ if (!author.name) console.error(`Error: Author ${author.slug} missing name`);
+ if (!author.image) console.warn(`Warning: Author ${author.slug} missing image`);
+});
+
+console.log("\nValidating Blog Data...");
+blogPosts.forEach(post => {
+ try {
+ if (!post.slug) console.error("Error: Post missing slug", post);
+ if (!post.datePublished && !post.date) console.error(`Error: Post ${post.slug} missing date`);
+
+ const d = new Date(post.datePublished || post.date);
+ if (isNaN(d.getTime())) {
+ console.error(`Error: Post ${post.slug} has invalid date: ${post.datePublished || post.date}`);
+ }
+ } catch (e) {
+ console.error(`Exception checking post ${post.slug || 'unknown'}:`, e);
+ }
+});
+
+console.log("\nTesting Content Functions...");
+try {
+ const published = getPublishedPosts();
+ console.log(`getPublishedPosts returned ${published.length} posts.`);
+
+ authors.forEach(author => {
+ const posts = getPostsByAuthor(author.slug);
+ console.log(`Author ${author.slug} has ${posts.length} posts.`);
+ });
+
+} catch (e) {
+ console.error("Error running content functions:", e);
+}
+
+console.log("\nValidation Complete.");
diff --git a/src/app/(main)/(marketing)/authors/[slug]/page.tsx b/src/app/(main)/(marketing)/authors/[slug]/page.tsx
index ade63f7..773f9eb 100644
--- a/src/app/(main)/(marketing)/authors/[slug]/page.tsx
+++ b/src/app/(main)/(marketing)/authors/[slug]/page.tsx
@@ -81,7 +81,7 @@ export default function AuthorPage({ params }: { params: { slug: string } }) {
{posts.map(p => (
-
{p.date}
+
{new Date(p.datePublished || p.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
{p.title}
{p.description}
diff --git a/src/app/(main)/(marketing)/learn/[pillar]/page.tsx b/src/app/(main)/(marketing)/learn/[pillar]/page.tsx
index 891672e..ae581c4 100644
--- a/src/app/(main)/(marketing)/learn/[pillar]/page.tsx
+++ b/src/app/(main)/(marketing)/learn/[pillar]/page.tsx
@@ -55,7 +55,7 @@ export default function PillarPage({ params }: { params: { pillar: PillarKey } }
{posts.map(p => (
-
{p.date}
+
{new Date(p.datePublished || p.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
{p.title}
{p.description}
diff --git a/src/app/(main)/(marketing)/learn/page.tsx b/src/app/(main)/(marketing)/learn/page.tsx
index ed2664d..afd7eb0 100644
--- a/src/app/(main)/(marketing)/learn/page.tsx
+++ b/src/app/(main)/(marketing)/learn/page.tsx
@@ -10,7 +10,11 @@ export const metadata = {
export default function LearnHubPage() {
const posts = getPublishedPosts();
// Sort by date descending
- const topLatest = [...posts].sort((a, b) => (new Date(a.datePublished).getTime() < new Date(b.datePublished).getTime() ? 1 : -1)).slice(0, 6);
+ const topLatest = [...posts].sort((a, b) => {
+ const dateA = a.datePublished ? new Date(a.datePublished) : new Date(a.date);
+ const dateB = b.datePublished ? new Date(b.datePublished) : new Date(b.date);
+ return dateB.getTime() - dateA.getTime();
+ }).slice(0, 6);
return (
@@ -41,7 +45,7 @@ export default function LearnHubPage() {
{p.pillar?.toUpperCase() || 'GUIDE'}
-
{p.date}
+
{new Date(p.datePublished || p.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
{p.title}
{p.description}
diff --git a/src/app/(marketing-de)/whatsapp-qr-code/WhatsAppGeneratorDE.tsx b/src/app/(marketing-de)/whatsapp-qr-code/WhatsAppGeneratorDE.tsx
index e9cf1c1..9a91f79 100644
--- a/src/app/(marketing-de)/whatsapp-qr-code/WhatsAppGeneratorDE.tsx
+++ b/src/app/(marketing-de)/whatsapp-qr-code/WhatsAppGeneratorDE.tsx
@@ -1,265 +1,265 @@
-'use client';
-
-import React, { useState, useRef } from 'react';
-import Link from 'next/link';
-import { QRCodeSVG } from 'qrcode.react';
-import {
- Phone,
- Download,
- Check,
- Sparkles,
- MessageCircle,
- Send
-} from 'lucide-react';
-import { Button } from '@/components/ui/Button';
-import { Input } from '@/components/ui/Input';
-import { cn } from '@/lib/utils';
-import { Textarea } from '@/components/ui/Textarea';
-
-// Brand Colors
-const BRAND = {
- paleGrey: '#EBEBDF',
- richBlue: '#1A1265',
- richBlueLight: '#2A2275',
-};
-
-// QR Color Options - WhatsApp Theme
-const QR_COLORS = [
- { name: 'WhatsApp Grün', value: '#25D366' },
- { name: 'Teal', value: '#128C7E' },
- { name: 'Klassisches Schwarz', value: '#000000' },
- { name: 'Sattes Blau', value: '#1A1265' },
- { name: 'Lila', value: '#7C3AED' },
- { name: 'Smaragd', value: '#10B981' },
- { name: 'Rose', value: '#F43F5E' },
-];
-
-// Frame Options
-const FRAME_OPTIONS = [
- { id: 'none', label: 'Kein Rahmen' },
- { id: 'scanme', label: 'Scan Mich' },
- { id: 'chat', label: 'Chat starten' },
- { id: 'support', label: 'Support' },
-];
-
-export default function WhatsAppGeneratorDE() {
- const [phone, setPhone] = useState('');
- const [message, setMessage] = useState('');
- const [qrColor, setQrColor] = useState('#25D366');
- const [frameType, setFrameType] = useState('none');
-
- const qrRef = useRef(null);
-
- // WhatsApp URL: https://wa.me/number?text=message
- const getUrl = () => {
- const cleanPhone = phone.replace(/\D/g, ''); // Remove non-digits
- const encodedMessage = encodeURIComponent(message);
- return `https://wa.me/${cleanPhone}?text=${encodedMessage}`;
- };
-
- const handleDownload = async (format: 'png' | 'svg') => {
- if (!qrRef.current) return;
- try {
- if (format === 'png') {
- const { toPng } = await import('html-to-image');
- const dataUrl = await toPng(qrRef.current, { cacheBust: true, pixelRatio: 3 });
- const link = document.createElement('a');
- link.download = `whatsapp-qr-code.png`;
- link.href = dataUrl;
- link.click();
- } else {
- const svgData = qrRef.current.querySelector('svg')?.outerHTML;
- if (svgData) {
- const blob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
- const url = URL.createObjectURL(blob);
- const link = document.createElement('a');
- link.href = url;
- link.download = `whatsapp-qr-code.svg`;
- link.click();
- }
- }
- } catch (err) {
- console.error('Download failed', err);
- }
- };
-
- const getFrameLabel = () => {
- const frame = FRAME_OPTIONS.find(f => f.id === frameType);
- return frame?.id !== 'none' ? frame?.label : null;
- };
-
- return (
-
-
- {/* Main Generator Card */}
-
-
-
- {/* LEFT: Input Section */}
-
-
- {/* WhatsApp Details */}
-
-
-
- WhatsApp Details
-
-
-
-
-
setPhone(e.target.value)}
- className="h-12 text-base rounded-xl border-slate-200 focus:border-[#25D366] focus:ring-[#25D366]"
- />
-
Mit Ländervorwahl (z.B. 49 für DE). Kein '+' Symbol.
-
-
-
-
-
-
-
-
-
- {/* Design Options */}
-
-
-
- Design Optionen
-
-
- {/* Color Picker */}
-
-
-
- {QR_COLORS.map((c) => (
-
- ))}
-
-
-
- {/* Frame Selector */}
-
-
-
- {FRAME_OPTIONS.map((frame) => (
-
- ))}
-
-
-
-
-
- {/* RIGHT: Preview Section */}
-
-
- {/* QR Card with Frame */}
-
- {/* Frame Label */}
- {getFrameLabel() && (
-
- {getFrameLabel()}
-
- )}
-
- {/* QR Code */}
-
-
-
-
- {/* Info Preview */}
-
-
-
- {phone ? `+${phone}` : 'Nummer'}
-
-
Startet WhatsApp Chat
-
-
-
- {/* Download Buttons */}
-
-
-
-
-
-
- Der Scan startet sofort einen Chat mit dieser Nummer.
-
-
-
-
-
- {/* Upsell Banner */}
-
-
-
WhatsApp für Business nutzen?
-
- Analysieren Sie Kundenkontakte mit unseren Pro-Statistiken für dynamische QR Codes.
-
-
-
-
-
-
-
- );
-}
+'use client';
+
+import React, { useState, useRef } from 'react';
+import Link from 'next/link';
+import { QRCodeSVG } from 'qrcode.react';
+import {
+ Phone,
+ Download,
+ Check,
+ Sparkles,
+ MessageCircle,
+ Send
+} from 'lucide-react';
+import { Button } from '@/components/ui/Button';
+import { Input } from '@/components/ui/Input';
+import { cn } from '@/lib/utils';
+import { Textarea } from '@/components/ui/Textarea';
+
+// Brand Colors
+const BRAND = {
+ paleGrey: '#EBEBDF',
+ richBlue: '#1A1265',
+ richBlueLight: '#2A2275',
+};
+
+// QR Color Options - WhatsApp Theme
+const QR_COLORS = [
+ { name: 'WhatsApp Grün', value: '#25D366' },
+ { name: 'Teal', value: '#128C7E' },
+ { name: 'Klassisches Schwarz', value: '#000000' },
+ { name: 'Sattes Blau', value: '#1A1265' },
+ { name: 'Lila', value: '#7C3AED' },
+ { name: 'Smaragd', value: '#10B981' },
+ { name: 'Rose', value: '#F43F5E' },
+];
+
+// Frame Options
+const FRAME_OPTIONS = [
+ { id: 'none', label: 'Kein Rahmen' },
+ { id: 'scanme', label: 'Scan Mich' },
+ { id: 'chat', label: 'Chat starten' },
+ { id: 'support', label: 'Support' },
+];
+
+export default function WhatsAppGeneratorDE() {
+ const [phone, setPhone] = useState('');
+ const [message, setMessage] = useState('');
+ const [qrColor, setQrColor] = useState('#25D366');
+ const [frameType, setFrameType] = useState('none');
+
+ const qrRef = useRef(null);
+
+ // WhatsApp URL: https://wa.me/number?text=message
+ const getUrl = () => {
+ const cleanPhone = phone.replace(/\D/g, ''); // Remove non-digits
+ const encodedMessage = encodeURIComponent(message);
+ return `https://wa.me/${cleanPhone}?text=${encodedMessage}`;
+ };
+
+ const handleDownload = async (format: 'png' | 'svg') => {
+ if (!qrRef.current) return;
+ try {
+ if (format === 'png') {
+ const { toPng } = await import('html-to-image');
+ const dataUrl = await toPng(qrRef.current, { cacheBust: true, pixelRatio: 3 });
+ const link = document.createElement('a');
+ link.download = `whatsapp-qr-code.png`;
+ link.href = dataUrl;
+ link.click();
+ } else {
+ const svgData = qrRef.current.querySelector('svg')?.outerHTML;
+ if (svgData) {
+ const blob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = `whatsapp-qr-code.svg`;
+ link.click();
+ }
+ }
+ } catch (err) {
+ console.error('Download failed', err);
+ }
+ };
+
+ const getFrameLabel = () => {
+ const frame = FRAME_OPTIONS.find(f => f.id === frameType);
+ return frame?.id !== 'none' ? frame?.label : null;
+ };
+
+ return (
+
+
+ {/* Main Generator Card */}
+
+
+
+ {/* LEFT: Input Section */}
+
+
+ {/* WhatsApp Details */}
+
+
+
+ WhatsApp Details
+
+
+
+
+
setPhone(e.target.value)}
+ className="h-12 text-base rounded-xl border-slate-200 focus:border-[#25D366] focus:ring-[#25D366]"
+ />
+
Mit Ländervorwahl (z.B. 49 für DE). Kein '+' Symbol.
+
+
+
+
+
+
+
+
+
+ {/* Design Options */}
+
+
+
+ Design Optionen
+
+
+ {/* Color Picker */}
+
+
+
+ {QR_COLORS.map((c) => (
+
+ ))}
+
+
+
+ {/* Frame Selector */}
+
+
+
+ {FRAME_OPTIONS.map((frame) => (
+
+ ))}
+
+
+
+
+
+ {/* RIGHT: Preview Section */}
+
+
+ {/* QR Card with Frame */}
+
+ {/* Frame Label */}
+ {getFrameLabel() && (
+
+ {getFrameLabel()}
+
+ )}
+
+ {/* QR Code */}
+
+
+
+
+ {/* Info Preview */}
+
+
+
+ {phone ? `+${phone}` : 'Nummer'}
+
+
Startet WhatsApp Chat
+
+
+
+ {/* Download Buttons */}
+
+
+
+
+
+
+ Der Scan startet sofort einen Chat mit dieser Nummer.
+
+
+
+
+
+ {/* Upsell Banner */}
+
+
+
WhatsApp für Business nutzen?
+
+ Analysieren Sie Kundenkontakte mit unseren Pro-Statistiken für dynamische QR Codes.
+
+
+
+
+
+
+
+ );
+}