website (17 pages + 3 components): - multiplayer/wizard, middleware/wizard+test-wizard, communication - builds/wizard, staff-search, voice, sbom/wizard - foerderantrag, mail/tasks, tools/communication, sbom - compliance/evidence, uni-crawler, brandbook (already done) - CollectionsTab, IngestionTab, RiskHeatmap backend-lehrer (5 files): - letters_api (641 → 2), certificates_api (636 → 2) - alerts_agent/db/models (636 → 3) - llm_gateway/communication_service (614 → 2) - game/database already done in prior batch klausur-service (2 files): - hybrid_vocab_extractor (664 → 2) - klausur-service/frontend: api.ts (620 → 3), EHUploadWizard (591 → 2) voice-service (3 files): - bqas/rag_judge (618 → 3), runner (529 → 2) - enhanced_task_orchestrator (519 → 2) studio-v2 (6 files): - korrektur/[klausurId] (578 → 4), fairness (569 → 2) - AlertsWizard (552 → 2), OnboardingWizard (513 → 2) - korrektur/api.ts (506 → 3), geo-lernwelt (501 → 2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
91 lines
4.7 KiB
TypeScript
91 lines
4.7 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Voice Service Admin Page
|
|
*
|
|
* Displays:
|
|
* - Voice-First Architecture Overview
|
|
* - Developer Guide Content
|
|
* - Live Voice Demo (embedded from studio-v2)
|
|
* - Task State Machine Documentation
|
|
* - DSGVO Compliance Information
|
|
*/
|
|
|
|
import { useState } from 'react'
|
|
import AdminLayout from '@/components/admin/AdminLayout'
|
|
import Link from 'next/link'
|
|
import { TabType, TABS } from './_components/constants'
|
|
import { TabOverview } from './_components/TabOverview'
|
|
import { TabDemo, TabTasks, TabIntents, TabDsgvo, TabApi } from './_components/TabContent'
|
|
|
|
export default function VoicePage() {
|
|
const [activeTab, setActiveTab] = useState<TabType>('overview')
|
|
|
|
return (
|
|
<AdminLayout title="Voice Service" description="Voice-First Interface mit PersonaPlex-7B & TaskOrchestrator">
|
|
{/* Quick Links */}
|
|
<div className="mb-6 flex flex-wrap gap-3">
|
|
<Link href="/voice-test" target="_blank" className="flex items-center gap-2 px-4 py-2 bg-teal-600 text-white rounded-lg hover:bg-teal-700 transition-colors">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" />
|
|
</svg>
|
|
Voice Test (Studio)
|
|
</Link>
|
|
<a href="http://localhost:8091/health" target="_blank" rel="noopener noreferrer" className="flex items-center gap-2 px-4 py-2 bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition-colors">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
Health Check
|
|
</a>
|
|
<Link href="/admin/docs" className="flex items-center gap-2 px-4 py-2 bg-slate-100 text-slate-700 rounded-lg hover:bg-slate-200 transition-colors">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
Developer Docs
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Stats Overview */}
|
|
<div className="grid grid-cols-2 md:grid-cols-6 gap-4 mb-6">
|
|
<div className="bg-white rounded-lg shadow p-4"><div className="text-3xl font-bold text-teal-600">8091</div><div className="text-sm text-slate-500">Port</div></div>
|
|
<div className="bg-white rounded-lg shadow p-4"><div className="text-3xl font-bold text-blue-600">22</div><div className="text-sm text-slate-500">Task Types</div></div>
|
|
<div className="bg-white rounded-lg shadow p-4"><div className="text-3xl font-bold text-purple-600">9</div><div className="text-sm text-slate-500">Task States</div></div>
|
|
<div className="bg-white rounded-lg shadow p-4"><div className="text-3xl font-bold text-green-600">24kHz</div><div className="text-sm text-slate-500">Audio Rate</div></div>
|
|
<div className="bg-white rounded-lg shadow p-4"><div className="text-3xl font-bold text-orange-600">80ms</div><div className="text-sm text-slate-500">Frame Size</div></div>
|
|
<div className="bg-white rounded-lg shadow p-4"><div className="text-3xl font-bold text-red-600">0</div><div className="text-sm text-slate-500">Audio Persist</div></div>
|
|
</div>
|
|
|
|
{/* Tabs */}
|
|
<div className="bg-white rounded-lg shadow mb-6">
|
|
<div className="border-b border-slate-200 px-4">
|
|
<div className="flex gap-1 overflow-x-auto">
|
|
{TABS.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => setActiveTab(tab.id as TabType)}
|
|
className={`px-4 py-3 text-sm font-medium whitespace-nowrap transition-colors border-b-2 ${
|
|
activeTab === tab.id
|
|
? 'border-teal-600 text-teal-600'
|
|
: 'border-transparent text-slate-500 hover:text-slate-700'
|
|
}`}
|
|
>
|
|
<span className="mr-2">{tab.icon}</span>
|
|
{tab.name}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-6">
|
|
{activeTab === 'overview' && <TabOverview />}
|
|
{activeTab === 'demo' && <TabDemo />}
|
|
{activeTab === 'tasks' && <TabTasks />}
|
|
{activeTab === 'intents' && <TabIntents />}
|
|
{activeTab === 'dsgvo' && <TabDsgvo />}
|
|
{activeTab === 'api' && <TabApi />}
|
|
</div>
|
|
</div>
|
|
</AdminLayout>
|
|
)
|
|
}
|