/** * Website Content Management (Server-only) * * Loads website texts from JSON files. * Admin can edit texts via /development/content * * IMPORTANT: This file may only be used in Server Components! * For Client Components use @/lib/content-types */ import { readFileSync, writeFileSync, existsSync, mkdirSync, accessSync, constants } from 'fs' import { join, dirname } from 'path' // Re-export types from content-types for backward compatibility export type { HeroContent, FeatureContent, FAQItem, PricingPlan, WebsiteContent, } from './content-types' import type { WebsiteContent } from './content-types' // Content directory - use environment variable or relative path function getContentDir(): string { // Check environment variable first if (process.env.CONTENT_DIR) { return process.env.CONTENT_DIR } // Try various possible paths const possiblePaths = [ join(process.cwd(), 'content'), // Standard: CWD/content join(process.cwd(), 'admin-v2', 'content'), // If CWD is project root '/app/content', // Docker container join(dirname(__filename), '..', 'content'), // Relative to this file ] // Check if any path exists and is writable for (const path of possiblePaths) { try { if (existsSync(path)) { accessSync(path, constants.W_OK) return path } } catch { // Path not writable, try next } } // Fallback: Create in CWD const fallbackPath = join(process.cwd(), 'content') try { mkdirSync(fallbackPath, { recursive: true, mode: 0o755 }) console.log(`[Content] Created content directory at: ${fallbackPath}`) } catch (err) { console.error(`[Content] Failed to create content directory: ${err}`) } return fallbackPath } const CONTENT_DIR = getContentDir() // Default Content const defaultContent: WebsiteContent = { hero: { badge: 'Entwickelt fuer deutsche Lehrkraefte', title: 'Korrigieren Sie', titleHighlight1: 'schneller', titleHighlight2: 'besser', subtitle: 'BreakPilot unterstuetzt Lehrkraefte mit intelligenter KI bei der Bewertung von Aufgaben. Sparen Sie bis zu 50% Ihrer Korrekturzeit und geben Sie besseres Feedback.', ctaPrimary: '7 Tage kostenlos testen', ctaSecondary: 'Mehr erfahren', ctaHint: 'Keine Kreditkarte fuer den Start erforderlich', }, features: [ { id: 'ai-correction', icon: '✍️', title: 'KI-gestuetzte Korrektur', description: 'Intelligente Analyse von Schuelerantworten mit Verbesserungsvorschlaegen und automatischer Bewertung nach Ihren Kriterien.', }, { id: 'templates', icon: '📋', title: 'Dokumentvorlagen', description: 'Erstellen und verwalten Sie Ihre eigenen Arbeitsblatt-Vorlagen. Wiederverwendbar fuer verschiedene Klassen und Jahrgaenge.', }, { id: 'analytics', icon: '📊', title: 'Fortschrittsanalyse', description: 'Verfolgen Sie die Entwicklung Ihrer Schueler ueber Zeit. Erkennen Sie Staerken und Schwaechen fruehzeitig.', }, { id: 'gdpr', icon: '🔒', title: 'DSGVO-konform', description: 'Hosting in Deutschland, volle Datenschutzkonformitaet. Ihre Daten und die Ihrer Schueler sind sicher.', }, { id: 'team', icon: '👥', title: 'Team-Funktionen', description: 'Arbeiten Sie im Fachbereich zusammen. Teilen Sie Vorlagen, Bewertungskriterien und Best Practices.', }, { id: 'mobile', icon: '📱', title: 'Ueberall verfuegbar', description: 'Browserbasiert und responsive. Funktioniert auf Desktop, Tablet und Smartphone - ohne Installation.', }, ], faq: [ { question: 'Was ist bei Breakpilot eine „Aufgabe"?', answer: [ 'Eine Aufgabe ist ein abgeschlossener Arbeitsauftrag, den du mit Breakpilot erledigst.', 'Typische Beispiele:', '• eine Klassenarbeit korrigieren (egal wie viele Seiten)', '• mehrere Klassenarbeiten in einer Serie korrigieren', '• einen Elternbrief erstellen', 'Wichtig: Die Anzahl der Seiten, Dateien oder Uploads spielt dabei keine Rolle.', ], }, { question: 'Kann ich Breakpilot kostenlos testen?', answer: [ 'Ja.', '• Du kannst Breakpilot 7 Tage kostenlos testen', '• Dafuer ist eine Kreditkarte erforderlich', '• Wenn du innerhalb der Testphase kuendigst, entstehen keine Kosten', ], }, { question: 'Werden meine Daten fuer KI-Training verwendet?', answer: [ 'Nein.', '• Deine Inhalte werden nicht fuer das Training oeffentlicher KI-Modelle genutzt', '• Die Verarbeitung erfolgt DSGVO-konform', '• Deine Daten bleiben unter deiner Kontrolle', ], }, { question: 'Kann ich meinen Tarif jederzeit aendern oder kuendigen?', answer: [ 'Ja.', '• Upgrades sind jederzeit moeglich', '• Downgrades greifen zum naechsten Abrechnungszeitraum', '• Kuendigungen sind jederzeit moeglich', ], }, ], pricing: [ { id: 'basic', name: 'Basic', description: 'Perfekt fuer den Einstieg', price: 9.90, currency: 'EUR', interval: 'Monat', features: { tasks: '30 Aufgaben', taskDescription: 'pro Monat', included: [ 'KI-gestuetzte Korrektur', 'Basis-Dokumentvorlagen', 'E-Mail Support', ], }, }, { id: 'standard', name: 'Standard', description: 'Fuer regelmaessige Nutzer', price: 19.90, currency: 'EUR', interval: 'Monat', popular: true, features: { tasks: '100 Aufgaben', taskDescription: 'pro Monat', included: [ 'Alles aus Basic', 'Eigene Vorlagen erstellen', 'Batch-Verarbeitung', 'Bis zu 3 Teammitglieder', 'Prioritaets-Support', ], }, }, { id: 'premium', name: 'Premium', description: 'Sorglos-Tarif fuer Vielnutzer', price: 39.90, currency: 'EUR', interval: 'Monat', features: { tasks: 'Unbegrenzt', taskDescription: 'Fair Use', included: [ 'Alles aus Standard', 'Unbegrenzte Aufgaben (Fair Use)', 'Bis zu 10 Teammitglieder', 'Admin-Panel & Audit-Log', 'API-Zugang', 'Eigenes Branding', 'Dedizierter Support', ], }, }, ], trust: { item1: { value: 'DSGVO', label: 'Konform & sicher' }, item2: { value: '7 Tage', label: 'Kostenlos testen' }, item3: { value: '100%', label: 'Made in Germany' }, }, testimonial: { quote: 'BreakPilot hat meine Korrekturzeit halbiert. Ich habe endlich wieder Zeit fuer das Wesentliche: meine Schueler.', author: 'Maria S.', role: 'Deutschlehrerin, Gymnasium', }, } /** * Loads content from JSON file or returns default */ export function getContent(): WebsiteContent { const contentPath = join(CONTENT_DIR, 'website.json') try { if (existsSync(contentPath)) { const fileContent = readFileSync(contentPath, 'utf-8') return JSON.parse(fileContent) as WebsiteContent } } catch (error) { console.error('Error loading content:', error) } return defaultContent } /** * Saves content to JSON file * @returns Object with success status and optional error message */ export function saveContent(content: WebsiteContent): { success: boolean; error?: string } { const contentPath = join(CONTENT_DIR, 'website.json') try { // Ensure directory exists if (!existsSync(CONTENT_DIR)) { console.log(`[Content] Creating directory: ${CONTENT_DIR}`) mkdirSync(CONTENT_DIR, { recursive: true, mode: 0o755 }) } // Check write permissions try { accessSync(CONTENT_DIR, constants.W_OK) } catch { const error = `Directory not writable: ${CONTENT_DIR}` console.error(`[Content] ${error}`) return { success: false, error } } // Write file writeFileSync(contentPath, JSON.stringify(content, null, 2), 'utf-8') console.log(`[Content] Saved successfully to: ${contentPath}`) return { success: true } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error' console.error(`[Content] Error saving: ${errorMessage}`) return { success: false, error: errorMessage } } } /** * Returns default content (for reset) */ export function getDefaultContent(): WebsiteContent { return defaultContent }