[split-required] Split 700-870 LOC files across all services

backend-lehrer (11 files):
- llm_gateway/routes/schools.py (867 → 5), recording_api.py (848 → 6)
- messenger_api.py (840 → 5), print_generator.py (824 → 5)
- unit_analytics_api.py (751 → 5), classroom/routes/context.py (726 → 4)
- llm_gateway/routes/edu_search_seeds.py (710 → 4)

klausur-service (12 files):
- ocr_labeling_api.py (845 → 4), metrics_db.py (833 → 4)
- legal_corpus_api.py (790 → 4), page_crop.py (758 → 3)
- mail/ai_service.py (747 → 4), github_crawler.py (767 → 3)
- trocr_service.py (730 → 4), full_compliance_pipeline.py (723 → 4)
- dsfa_rag_api.py (715 → 4), ocr_pipeline_auto.py (705 → 4)

website (6 pages):
- audit-checklist (867 → 8), content (806 → 6)
- screen-flow (790 → 4), scraper (789 → 5)
- zeugnisse (776 → 5), modules (745 → 4)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-25 08:01:18 +02:00
parent b6983ab1dc
commit 34da9f4cda
106 changed files with 16500 additions and 16947 deletions

View File

@@ -0,0 +1,173 @@
'use client'
import { useState, useEffect, useRef, useCallback } from 'react'
import { WebsiteContent, HeroContent, FeatureContent } from '@/lib/content-types'
import { useLanguage } from '@/lib/LanguageContext'
import { ADMIN_KEY, SECTION_MAP, ContentTab } from './types'
export function useContentEditor() {
const { language, setLanguage, t, isRTL } = useLanguage()
const [content, setContent] = useState<WebsiteContent | null>(null)
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null)
const [activeTab, setActiveTab] = useState<ContentTab>('hero')
const [showPreview, setShowPreview] = useState(true)
const iframeRef = useRef<HTMLIFrameElement>(null)
const scrollToSection = useCallback((tab: string) => {
if (!iframeRef.current?.contentWindow) return
const section = SECTION_MAP[tab]
if (section) {
try {
iframeRef.current.contentWindow.postMessage(
{ type: 'scrollTo', section: section.scrollTo },
'*'
)
} catch {
// Same-origin policy - fallback
}
}
}, [])
useEffect(() => {
scrollToSection(activeTab)
}, [activeTab, scrollToSection])
useEffect(() => {
loadContent()
}, [])
async function loadContent() {
try {
const res = await fetch('/api/content')
if (res.ok) {
const data = await res.json()
setContent(data)
} else {
setMessage({ type: 'error', text: t('admin_error') })
}
} catch (error) {
setMessage({ type: 'error', text: t('admin_error') })
} finally {
setLoading(false)
}
}
async function saveChanges() {
if (!content) return
setSaving(true)
setMessage(null)
try {
const res = await fetch('/api/content', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-admin-key': ADMIN_KEY,
},
body: JSON.stringify(content),
})
if (res.ok) {
setMessage({ type: 'success', text: t('admin_saved') })
} else {
const error = await res.json()
setMessage({ type: 'error', text: error.error || t('admin_error') })
}
} catch (error) {
setMessage({ type: 'error', text: t('admin_error') })
} finally {
setSaving(false)
}
}
function updateHero(field: keyof HeroContent, value: string) {
if (!content) return
setContent({ ...content, hero: { ...content.hero, [field]: value } })
}
function updateFeature(index: number, field: keyof FeatureContent, value: string) {
if (!content) return
const newFeatures = [...content.features]
newFeatures[index] = { ...newFeatures[index], [field]: value }
setContent({ ...content, features: newFeatures })
}
function updateFAQ(index: number, field: 'question' | 'answer', value: string | string[]) {
if (!content) return
const newFAQ = [...content.faq]
if (field === 'answer' && typeof value === 'string') {
newFAQ[index] = { ...newFAQ[index], answer: value.split('\n') }
} else if (field === 'question' && typeof value === 'string') {
newFAQ[index] = { ...newFAQ[index], question: value }
}
setContent({ ...content, faq: newFAQ })
}
function addFAQ() {
if (!content) return
setContent({
...content,
faq: [...content.faq, { question: 'Neue Frage?', answer: ['Antwort hier...'] }],
})
}
function removeFAQ(index: number) {
if (!content) return
const newFAQ = content.faq.filter((_, i) => i !== index)
setContent({ ...content, faq: newFAQ })
}
function updatePricing(index: number, field: string, value: string | number | boolean) {
if (!content) return
const newPricing = [...content.pricing]
if (field === 'price') {
newPricing[index] = { ...newPricing[index], price: Number(value) }
} else if (field === 'popular') {
newPricing[index] = { ...newPricing[index], popular: Boolean(value) }
} else if (field.startsWith('features.')) {
const subField = field.replace('features.', '')
if (subField === 'included' && typeof value === 'string') {
newPricing[index] = {
...newPricing[index],
features: { ...newPricing[index].features, included: value.split('\n') },
}
} else {
newPricing[index] = {
...newPricing[index],
features: { ...newPricing[index].features, [subField]: value },
}
}
} else {
newPricing[index] = { ...newPricing[index], [field]: value }
}
setContent({ ...content, pricing: newPricing })
}
function updateTrust(key: 'item1' | 'item2' | 'item3', field: 'value' | 'label', value: string) {
if (!content) return
setContent({
...content,
trust: { ...content.trust, [key]: { ...content.trust[key], [field]: value } },
})
}
function updateTestimonial(field: 'quote' | 'author' | 'role', value: string) {
if (!content) return
setContent({
...content,
testimonial: { ...content.testimonial, [field]: value },
})
}
return {
language, setLanguage, t, isRTL,
content, loading, saving, message,
activeTab, setActiveTab,
showPreview, setShowPreview,
iframeRef,
saveChanges,
updateHero, updateFeature, updateFAQ,
addFAQ, removeFAQ, updatePricing,
updateTrust, updateTestimonial,
}
}