backend-lehrer (5 files): - alerts_agent/db/repository.py (992 → 5), abitur_docs_api.py (956 → 3) - teacher_dashboard_api.py (951 → 3), services/pdf_service.py (916 → 3) - mail/mail_db.py (987 → 6) klausur-service (5 files): - legal_templates_ingestion.py (942 → 3), ocr_pipeline_postprocess.py (929 → 4) - ocr_pipeline_words.py (876 → 3), ocr_pipeline_ocr_merge.py (616 → 2) - KorrekturPage.tsx (956 → 6) website (5 pages): - mail (985 → 9), edu-search (958 → 8), mac-mini (950 → 7) - ocr-labeling (946 → 7), audit-workspace (871 → 4) studio-v2 (5 files + 1 deleted): - page.tsx (946 → 5), MessagesContext.tsx (925 → 4) - korrektur (914 → 6), worksheet-cleanup (899 → 6) - useVocabWorksheet.ts (888 → 3) - Deleted dead page-original.tsx (934 LOC) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
128 lines
5.0 KiB
TypeScript
128 lines
5.0 KiB
TypeScript
'use client'
|
||
|
||
import type { MacMiniStatus } from '../types'
|
||
|
||
export default function PowerControls({
|
||
status,
|
||
loading,
|
||
actionLoading,
|
||
message,
|
||
error,
|
||
onWake,
|
||
onRestart,
|
||
onShutdown,
|
||
onRefresh,
|
||
}: {
|
||
status: MacMiniStatus | null
|
||
loading: boolean
|
||
actionLoading: string | null
|
||
message: string | null
|
||
error: string | null
|
||
onWake: () => void
|
||
onRestart: () => void
|
||
onShutdown: () => void
|
||
onRefresh: () => void
|
||
}) {
|
||
const getStatusBadge = (online: boolean) => {
|
||
return online
|
||
? 'px-3 py-1 rounded-full text-sm font-semibold bg-green-100 text-green-800'
|
||
: 'px-3 py-1 rounded-full text-sm font-semibold bg-red-100 text-red-800'
|
||
}
|
||
|
||
const getServiceStatus = (ok: boolean) => {
|
||
return ok
|
||
? 'flex items-center gap-2 text-green-600'
|
||
: 'flex items-center gap-2 text-red-500'
|
||
}
|
||
|
||
return (
|
||
<div className="bg-white rounded-xl border border-slate-200 p-6 mb-6">
|
||
<div className="flex items-center justify-between mb-6">
|
||
<div className="flex items-center gap-4">
|
||
<div className="text-4xl">🖥️</div>
|
||
<div>
|
||
<h2 className="text-xl font-bold text-slate-900">Mac Mini Headless</h2>
|
||
<p className="text-slate-500 text-sm">IP: {status?.ip || '192.168.178.100'}</p>
|
||
</div>
|
||
</div>
|
||
<span className={getStatusBadge(status?.online || false)}>
|
||
{loading ? 'Laden...' : status?.online ? 'Online' : 'Offline'}
|
||
</span>
|
||
</div>
|
||
|
||
{/* Power Buttons */}
|
||
<div className="flex items-center gap-4 mb-6">
|
||
<button
|
||
onClick={onWake}
|
||
disabled={actionLoading !== null}
|
||
className="px-4 py-2 bg-green-600 text-white rounded-lg font-medium hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||
>
|
||
{actionLoading === 'wake' ? '...' : '⚡ Wake on LAN'}
|
||
</button>
|
||
<button
|
||
onClick={onRestart}
|
||
disabled={actionLoading !== null || !status?.online}
|
||
className="px-4 py-2 bg-yellow-600 text-white rounded-lg font-medium hover:bg-yellow-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||
>
|
||
{actionLoading === 'restart' ? '...' : '🔄 Neustart'}
|
||
</button>
|
||
<button
|
||
onClick={onShutdown}
|
||
disabled={actionLoading !== null || !status?.online}
|
||
className="px-4 py-2 bg-red-600 text-white rounded-lg font-medium hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||
>
|
||
{actionLoading === 'shutdown' ? '...' : '⏻ Herunterfahren'}
|
||
</button>
|
||
<button
|
||
onClick={onRefresh}
|
||
disabled={loading}
|
||
className="px-4 py-2 border border-slate-300 text-slate-700 rounded-lg font-medium hover:bg-slate-50 disabled:opacity-50 transition-colors"
|
||
>
|
||
{loading ? '...' : '🔍 Status aktualisieren'}
|
||
</button>
|
||
|
||
{message && <span className="ml-4 text-sm text-green-600 font-medium">{message}</span>}
|
||
{error && <span className="ml-4 text-sm text-red-600 font-medium">{error}</span>}
|
||
</div>
|
||
|
||
{/* Service Status Grid */}
|
||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
||
<div className="bg-slate-50 rounded-lg p-4">
|
||
<div className="text-sm text-slate-500 mb-1">Ping</div>
|
||
<div className={getServiceStatus(status?.ping || false)}>
|
||
<span className={`w-2 h-2 rounded-full ${status?.ping ? 'bg-green-500' : 'bg-red-500'}`}></span>
|
||
{status?.ping ? 'Erreichbar' : 'Nicht erreichbar'}
|
||
</div>
|
||
</div>
|
||
<div className="bg-slate-50 rounded-lg p-4">
|
||
<div className="text-sm text-slate-500 mb-1">SSH</div>
|
||
<div className={getServiceStatus(status?.ssh || false)}>
|
||
<span className={`w-2 h-2 rounded-full ${status?.ssh ? 'bg-green-500' : 'bg-red-500'}`}></span>
|
||
{status?.ssh ? 'Verbunden' : 'Getrennt'}
|
||
</div>
|
||
</div>
|
||
<div className="bg-slate-50 rounded-lg p-4">
|
||
<div className="text-sm text-slate-500 mb-1">Docker</div>
|
||
<div className={getServiceStatus(status?.docker || false)}>
|
||
<span className={`w-2 h-2 rounded-full ${status?.docker ? 'bg-green-500' : 'bg-red-500'}`}></span>
|
||
{status?.docker ? 'Aktiv' : 'Inaktiv'}
|
||
</div>
|
||
</div>
|
||
<div className="bg-slate-50 rounded-lg p-4">
|
||
<div className="text-sm text-slate-500 mb-1">Ollama</div>
|
||
<div className={getServiceStatus(status?.ollama || false)}>
|
||
<span className={`w-2 h-2 rounded-full ${status?.ollama ? 'bg-green-500' : 'bg-red-500'}`}></span>
|
||
{status?.ollama ? 'Bereit' : 'Nicht bereit'}
|
||
</div>
|
||
</div>
|
||
<div className="bg-slate-50 rounded-lg p-4">
|
||
<div className="text-sm text-slate-500 mb-1">Uptime</div>
|
||
<div className="font-semibold text-slate-700">
|
||
{status?.uptime || '-'}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|