Website (14 monoliths split): - compliance/page.tsx (1,519 → 9), docs/audit (1,262 → 20) - quality (1,231 → 16), alerts (1,203 → 10), docs (1,202 → 11) - i18n.ts (1,173 → 8 language files) - unity-bridge (1,094 → 12), backlog (1,087 → 6) - training (1,066 → 8), rag (1,063 → 8) - Deleted index_original.ts (4,899 LOC dead backup) Studio-v2 (5 monoliths split): - meet/page.tsx (1,481 → 9), messages (1,166 → 9) - AlertsB2BContext.tsx (1,165 → 5 modules) - alerts-b2b/page.tsx (1,019 → 6), korrektur/archiv (1,001 → 6) All existing imports preserved. Zero new TypeScript errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
153 lines
6.2 KiB
TypeScript
153 lines
6.2 KiB
TypeScript
'use client'
|
|
|
|
import AdminLayout from '@/components/admin/AdminLayout'
|
|
import SystemInfoSection, { SYSTEM_INFO_CONFIGS } from '@/components/admin/SystemInfoSection'
|
|
import Link from 'next/link'
|
|
|
|
import { tabs } from './_components/tabs'
|
|
import { StatusBadge } from './_components/StatusBadge'
|
|
import { EditorTab } from './_components/EditorTab'
|
|
import { UnitsTab } from './_components/UnitsTab'
|
|
import { AnalyticsTab } from './_components/AnalyticsTab'
|
|
import { ContentTab } from './_components/ContentTab'
|
|
import { useUnityBridge } from './_components/useUnityBridge'
|
|
|
|
export default function UnityBridgePage() {
|
|
const bridge = useUnityBridge()
|
|
|
|
return (
|
|
<AdminLayout title="Unity AI Bridge" description="Externe Steuerung des Unity Editors">
|
|
{/* Header */}
|
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
|
|
<div className="flex items-center gap-4">
|
|
<StatusBadge status={bridge.status} error={bridge.error} />
|
|
{bridge.status && (
|
|
<span className="text-sm text-gray-500">
|
|
Unity {bridge.status.unity_version} - {bridge.status.project}
|
|
</span>
|
|
)}
|
|
</div>
|
|
<Link
|
|
href="/admin/unity-bridge/wizard"
|
|
className="inline-flex items-center gap-2 px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors"
|
|
>
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
|
</svg>
|
|
Wizard starten
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Offline Warning - only show on Editor tab */}
|
|
{bridge.activeTab === 'editor' && bridge.error && (
|
|
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg">
|
|
<div className="flex items-start gap-3">
|
|
<svg className="w-5 h-5 text-red-500 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
</svg>
|
|
<div>
|
|
<p className="font-medium text-red-800">{bridge.error}</p>
|
|
<p className="text-sm text-red-600 mt-1">
|
|
Starte den Server in Unity: <code className="bg-red-100 px-1 rounded">BreakpilotDrive → AI Bridge → Start Server</code>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Tab Navigation */}
|
|
<div className="mb-6 border-b border-gray-200">
|
|
<nav className="flex gap-1 -mb-px">
|
|
{tabs.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => bridge.setActiveTab(tab.id)}
|
|
className={`
|
|
flex items-center gap-2 px-4 py-3 text-sm font-medium border-b-2 transition-colors
|
|
${bridge.activeTab === tab.id
|
|
? 'border-primary-500 text-primary-600'
|
|
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
|
|
}
|
|
`}
|
|
>
|
|
{tab.icon}
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
|
|
{/* Tab Content */}
|
|
{bridge.activeTab === 'editor' && (
|
|
<EditorTab
|
|
status={bridge.status}
|
|
logs={bridge.logs}
|
|
diagnostics={bridge.diagnostics}
|
|
isLoadingLogs={bridge.isLoadingLogs}
|
|
isLoadingDiagnose={bridge.isLoadingDiagnose}
|
|
error={bridge.error}
|
|
onSendCommand={bridge.sendCommand}
|
|
onFetchLogs={bridge.fetchLogs}
|
|
onClearLogs={bridge.clearLogs}
|
|
onRunDiagnose={bridge.runDiagnose}
|
|
/>
|
|
)}
|
|
|
|
{bridge.activeTab === 'units' && (
|
|
<UnitsTab
|
|
units={bridge.units}
|
|
selectedUnit={bridge.selectedUnit}
|
|
isLoadingUnits={bridge.isLoadingUnits}
|
|
unitsError={bridge.unitsError}
|
|
onFetchUnits={bridge.fetchUnits}
|
|
onFetchUnitDetails={bridge.fetchUnitDetails}
|
|
onClearSelection={() => bridge.setSelectedUnit(null)}
|
|
/>
|
|
)}
|
|
|
|
{bridge.activeTab === 'sessions' && (
|
|
<div className="space-y-6">
|
|
<div className="text-center py-12 bg-gray-50 rounded-lg border border-gray-200">
|
|
<svg className="w-12 h-12 mx-auto mb-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
<h3 className="text-lg font-medium text-gray-900 mb-2">Session Monitor</h3>
|
|
<p className="text-gray-500 mb-4">Zeigt aktive Lern-Sessions in Echtzeit an</p>
|
|
<p className="text-sm text-gray-400">
|
|
Sessions werden erstellt, wenn Spieler ein UnitGate im Spiel passieren.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{bridge.activeTab === 'analytics' && (
|
|
<AnalyticsTab
|
|
analyticsOverview={bridge.analyticsOverview}
|
|
isLoadingAnalytics={bridge.isLoadingAnalytics}
|
|
onFetchAnalytics={bridge.fetchAnalytics}
|
|
/>
|
|
)}
|
|
|
|
{bridge.activeTab === 'content' && (
|
|
<ContentTab
|
|
units={bridge.units}
|
|
selectedUnit={bridge.selectedUnit}
|
|
generatedContent={bridge.generatedContent}
|
|
isGenerating={bridge.isGenerating}
|
|
onSelectUnit={bridge.setSelectedUnit}
|
|
onFetchUnits={bridge.fetchUnits}
|
|
onGenerateH5P={bridge.generateH5P}
|
|
onGenerateWorksheet={bridge.generateWorksheet}
|
|
onDownloadPdf={bridge.downloadPdf}
|
|
onClearContent={() => bridge.setGeneratedContent(null)}
|
|
/>
|
|
)}
|
|
|
|
{/* System Info Section */}
|
|
<div className="mt-8 border-t border-slate-200 pt-8">
|
|
<SystemInfoSection config={SYSTEM_INFO_CONFIGS.unityBridge} />
|
|
</div>
|
|
</AdminLayout>
|
|
)
|
|
}
|