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>
127 lines
5.4 KiB
TypeScript
127 lines
5.4 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Admin Panel fuer Website-Content
|
|
*
|
|
* Erlaubt das Bearbeiten aller Website-Texte:
|
|
* - Hero Section, Features, FAQ, Pricing, Trust Indicators, Testimonial
|
|
*
|
|
* NEU: Live-Preview der Website zeigt Kontext beim Bearbeiten
|
|
*/
|
|
|
|
import LanguageSelector from '@/components/LanguageSelector'
|
|
import AdminLayout from '@/components/admin/AdminLayout'
|
|
import { useContentEditor } from './_components/useContentEditor'
|
|
import ContentEditorTabs from './_components/ContentEditorTabs'
|
|
import LivePreviewPanel from './_components/LivePreviewPanel'
|
|
|
|
export default function AdminPage() {
|
|
const editor = useContentEditor()
|
|
|
|
if (editor.loading) {
|
|
return (
|
|
<AdminLayout title="Uebersetzungen" description="Website Content & Sprachen">
|
|
<div className="flex items-center justify-center py-12">
|
|
<div className="text-xl text-slate-600">{editor.t('admin_loading')}</div>
|
|
</div>
|
|
</AdminLayout>
|
|
)
|
|
}
|
|
|
|
if (!editor.content) {
|
|
return (
|
|
<AdminLayout title="Uebersetzungen" description="Website Content & Sprachen">
|
|
<div className="flex items-center justify-center py-12">
|
|
<div className="text-xl text-red-600">{editor.t('admin_error')}</div>
|
|
</div>
|
|
</AdminLayout>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<AdminLayout title="Uebersetzungen" description="Website Content & Sprachen">
|
|
<div className={editor.isRTL ? 'rtl' : ''} dir={editor.isRTL ? 'rtl' : 'ltr'}>
|
|
{/* Toolbar */}
|
|
<div className={`bg-white rounded-xl border border-slate-200 p-4 mb-6 flex items-center justify-between ${editor.isRTL ? 'flex-row-reverse' : ''}`}>
|
|
<div className="flex items-center gap-4">
|
|
<LanguageSelector currentLanguage={editor.language} onLanguageChange={editor.setLanguage} />
|
|
<button
|
|
onClick={() => editor.setShowPreview(!editor.showPreview)}
|
|
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors ${
|
|
editor.showPreview ? 'bg-blue-100 text-blue-700' : 'bg-slate-100 text-slate-600 hover:bg-slate-200'
|
|
}`}
|
|
title={editor.showPreview ? 'Preview ausblenden' : 'Preview einblenden'}
|
|
>
|
|
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
|
</svg>
|
|
Live-Preview
|
|
</button>
|
|
</div>
|
|
<div className={`flex items-center gap-4 ${editor.isRTL ? 'flex-row-reverse' : ''}`}>
|
|
{editor.message && (
|
|
<span className={`px-3 py-1 rounded text-sm ${
|
|
editor.message.type === 'success' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
|
}`}>
|
|
{editor.message.text}
|
|
</span>
|
|
)}
|
|
<button
|
|
onClick={editor.saveChanges}
|
|
disabled={editor.saving}
|
|
className="bg-primary-600 text-white px-6 py-2 rounded-lg font-medium hover:bg-primary-700 disabled:opacity-50 transition-colors"
|
|
>
|
|
{editor.saving ? editor.t('admin_saving') : editor.t('admin_save')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tabs */}
|
|
<div className="mb-6">
|
|
<div className={`flex gap-1 bg-slate-100 p-1 rounded-lg w-fit ${editor.isRTL ? 'flex-row-reverse' : ''}`}>
|
|
{(['hero', 'features', 'faq', 'pricing', 'other'] as const).map((tab) => (
|
|
<button
|
|
key={tab}
|
|
onClick={() => editor.setActiveTab(tab)}
|
|
className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
|
|
editor.activeTab === tab ? 'bg-white text-slate-900 shadow-sm' : 'text-slate-600 hover:text-slate-900'
|
|
}`}
|
|
>
|
|
{tab === 'hero' && editor.t('admin_tab_hero')}
|
|
{tab === 'features' && editor.t('admin_tab_features')}
|
|
{tab === 'faq' && editor.t('admin_tab_faq')}
|
|
{tab === 'pricing' && editor.t('admin_tab_pricing')}
|
|
{tab === 'other' && editor.t('admin_tab_other')}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Split Layout: Editor + Preview */}
|
|
<div className={`grid gap-6 ${editor.showPreview ? 'grid-cols-2' : 'grid-cols-1'}`}>
|
|
<div className="bg-white rounded-xl border border-slate-200 shadow-sm p-6 max-h-[calc(100vh-280px)] overflow-y-auto">
|
|
<ContentEditorTabs
|
|
activeTab={editor.activeTab}
|
|
content={editor.content}
|
|
isRTL={editor.isRTL}
|
|
t={editor.t}
|
|
updateHero={editor.updateHero}
|
|
updateFeature={editor.updateFeature}
|
|
updateFAQ={editor.updateFAQ}
|
|
addFAQ={editor.addFAQ}
|
|
removeFAQ={editor.removeFAQ}
|
|
updatePricing={editor.updatePricing}
|
|
updateTrust={editor.updateTrust}
|
|
updateTestimonial={editor.updateTestimonial}
|
|
/>
|
|
</div>
|
|
{editor.showPreview && (
|
|
<LivePreviewPanel activeTab={editor.activeTab} iframeRef={editor.iframeRef} />
|
|
)}
|
|
</div>
|
|
</div>
|
|
</AdminLayout>
|
|
)
|
|
}
|