Files
breakpilot-lehrer/admin-lehrer/app/(admin)/infrastructure/ci-cd/page.tsx
Benjamin Admin 9ba420fa91
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 42s
CI / test-go-edu-search (push) Successful in 34s
CI / test-python-klausur (push) Failing after 2m51s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 29s
Fix: Remove broken getKlausurApiUrl and clean up empty lines
sed replacement left orphaned hostname references in story page
and empty lines in getApiBase functions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 16:02:04 +02:00

193 lines
8.7 KiB
TypeScript

'use client'
/**
* CI/CD Dashboard
*
* Zentrale Uebersicht fuer:
* - Gitea Actions Pipelines
* - Runner Status
* - Container Deployments
* - Pipeline Konfiguration
*/
import { PagePurpose } from '@/components/common/PagePurpose'
import { DevOpsPipelineSidebarResponsive } from '@/components/infrastructure/DevOpsPipelineSidebar'
import { useCiCdData } from './useCiCdData'
import type { TabType } from './types'
import { OverviewTab } from './_components/OverviewTab'
import { WoodpeckerTab } from './_components/WoodpeckerTab'
import { PipelinesTab } from './_components/PipelinesTab'
import { DeploymentsTab } from './_components/DeploymentsTab'
import { SetupTab } from './_components/SetupTab'
import { SchedulerTab } from './_components/SchedulerTab'
// ============================================================================
// Tab Definitions
// ============================================================================
const TABS: { id: TabType; name: string; icon: React.ReactNode }[] = [
{ id: 'overview', name: 'Uebersicht', icon: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
)},
{ id: 'woodpecker', name: 'Woodpecker CI', icon: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
)},
{ id: 'pipelines', name: 'Gitea Pipelines', icon: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
)},
{ id: 'deployments', name: 'Deployments', icon: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2" />
</svg>
)},
{ id: 'setup', name: 'Konfiguration', icon: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
)},
{ id: 'scheduler', name: 'BQAS Scheduler', icon: (
<svg className="w-5 h-5" 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>
)},
]
// ============================================================================
// Main Component
// ============================================================================
export default function CICDPage() {
const data = useCiCdData()
return (
<div>
<PagePurpose
title="CI/CD Dashboard"
purpose="Zentrale Uebersicht fuer Gitea Actions Pipelines, Runner-Status und Container-Deployments. Starten Sie Pipelines manuell und verwalten Sie Docker-Container."
audience={['DevOps', 'Entwickler']}
architecture={{
services: ['Gitea Actions', 'act_runner', 'Docker'],
databases: [],
}}
relatedPages={[
{ name: 'SBOM', href: '/infrastructure/sbom', description: 'Software Bill of Materials' },
{ name: 'Security', href: '/infrastructure/security', description: 'DevSecOps Dashboard' },
{ name: 'Test Quality', href: '/ai/test-quality', description: 'BQAS Test Dashboard' },
]}
collapsible={true}
defaultCollapsed={true}
/>
<DevOpsPipelineSidebarResponsive currentTool="ci-cd" />
{/* Messages */}
{data.error && (
<div className="bg-red-50 border border-red-200 rounded-xl p-4 mb-6">
<div className="flex items-center gap-2 text-red-800">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="font-medium">{data.error}</span>
</div>
</div>
)}
{data.message && (
<div className="bg-green-50 border border-green-200 rounded-xl p-4 mb-6">
<div className="flex items-center gap-2 text-green-800">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
<span className="font-medium">{data.message}</span>
</div>
</div>
)}
{/* Main Content */}
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
{/* Tabs */}
<div className="flex border-b border-slate-200">
<nav className="flex">
{TABS.map((tab) => (
<button
key={tab.id}
onClick={() => data.setActiveTab(tab.id)}
className={`px-6 py-4 text-sm font-medium border-b-2 transition-colors flex items-center gap-2 ${
data.activeTab === tab.id
? 'border-orange-500 text-orange-600'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
}`}
>
{tab.icon}
{tab.name}
</button>
))}
</nav>
</div>
{/* Tab Content */}
<div className="p-6">
{data.loading ? (
<div className="flex justify-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-orange-600" />
</div>
) : (
<>
{data.activeTab === 'overview' && (
<OverviewTab
pipelineStatus={data.pipelineStatus}
pipelineHistory={data.pipelineHistory}
systemStats={data.systemStats}
dockerStats={data.dockerStats}
woodpeckerStatus={data.woodpeckerStatus}
triggeringWoodpecker={data.triggeringWoodpecker}
triggerWoodpeckerPipeline={data.triggerWoodpeckerPipeline}
setActiveTab={data.setActiveTab}
/>
)}
{data.activeTab === 'woodpecker' && (
<WoodpeckerTab
woodpeckerStatus={data.woodpeckerStatus}
triggeringWoodpecker={data.triggeringWoodpecker}
triggerWoodpeckerPipeline={data.triggerWoodpeckerPipeline}
/>
)}
{data.activeTab === 'pipelines' && (
<PipelinesTab
pipelineHistory={data.pipelineHistory}
triggeringPipeline={data.triggeringPipeline}
triggerPipeline={data.triggerPipeline}
/>
)}
{data.activeTab === 'deployments' && (
<DeploymentsTab
dockerStats={data.dockerStats}
filteredContainers={data.filteredContainers}
containerFilter={data.containerFilter}
setContainerFilter={data.setContainerFilter}
actionLoading={data.actionLoading}
containerAction={data.containerAction}
loadContainerData={data.loadContainerData}
/>
)}
{data.activeTab === 'setup' && (
<SetupTab pipelineStatus={data.pipelineStatus} />
)}
{data.activeTab === 'scheduler' && (
<SchedulerTab />
)}
</>
)}
</div>
</div>
</div>
)
}