fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
602
website/app/admin/builds/wizard/page.tsx
Normal file
602
website/app/admin/builds/wizard/page.tsx
Normal file
@@ -0,0 +1,602 @@
|
||||
'use client'
|
||||
|
||||
/**
|
||||
* Build Pipeline Wizard
|
||||
*
|
||||
* Interactive guide for learning about the multi-platform
|
||||
* build and deployment process for Breakpilot Drive
|
||||
*/
|
||||
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import AdminLayout from '@/components/admin/AdminLayout'
|
||||
|
||||
// ========================================
|
||||
// Types
|
||||
// ========================================
|
||||
|
||||
type WizardStep =
|
||||
| 'welcome'
|
||||
| 'platforms'
|
||||
| 'github-actions'
|
||||
| 'webgl-build'
|
||||
| 'ios-build'
|
||||
| 'android-build'
|
||||
| 'deployment'
|
||||
| 'version-sync'
|
||||
| 'summary'
|
||||
|
||||
interface StepInfo {
|
||||
id: WizardStep
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Step Configuration
|
||||
// ========================================
|
||||
|
||||
const STEPS: StepInfo[] = [
|
||||
{ id: 'welcome', title: 'Willkommen', description: 'Build Pipeline Uebersicht' },
|
||||
{ id: 'platforms', title: 'Plattformen', description: 'WebGL, iOS, Android' },
|
||||
{ id: 'github-actions', title: 'GitHub Actions', description: 'CI/CD Workflow' },
|
||||
{ id: 'webgl-build', title: 'WebGL', description: 'Browser Build' },
|
||||
{ id: 'ios-build', title: 'iOS', description: 'App Store Build' },
|
||||
{ id: 'android-build', title: 'Android', description: 'Play Store Build' },
|
||||
{ id: 'deployment', title: 'Deployment', description: 'Store Upload' },
|
||||
{ id: 'version-sync', title: 'Versioning', description: 'Version Management' },
|
||||
{ id: 'summary', title: 'Zusammenfassung', description: 'Naechste Schritte' },
|
||||
]
|
||||
|
||||
// ========================================
|
||||
// Educational Content
|
||||
// ========================================
|
||||
|
||||
const EDUCATION_CONTENT: Record<WizardStep, { title: string; content: string; tips: string[] }> = {
|
||||
'welcome': {
|
||||
title: 'Multi-Platform Build Pipeline',
|
||||
content: `Breakpilot Drive wird fuer drei Plattformen gebaut:
|
||||
|
||||
**WebGL** - Browser-basiert, in Admin Panel eingebettet
|
||||
**iOS** - iPhone/iPad via App Store
|
||||
**Android** - Smartphones/Tablets via Google Play
|
||||
|
||||
Die Build-Pipeline nutzt **GitHub Actions** mit **game-ci/unity-builder**
|
||||
fuer automatisierte, reproduzierbare Builds.`,
|
||||
tips: [
|
||||
'WebGL ist die primaere Plattform fuer schnelles Testing',
|
||||
'Mobile Builds nur bei Tags (Releases)',
|
||||
'Alle Builds werden als Artifacts gespeichert'
|
||||
]
|
||||
},
|
||||
'platforms': {
|
||||
title: 'Unterstuetzte Plattformen',
|
||||
content: `Jede Plattform hat spezifische Anforderungen:
|
||||
|
||||
**WebGL (HTML5/WASM)**
|
||||
- Brotli-Kompression
|
||||
- 512MB Memory
|
||||
- Kein Threading (Browser-Limitation)
|
||||
|
||||
**iOS (iPhone/iPad)**
|
||||
- Min. iOS 14.0
|
||||
- ARM64 Architektur
|
||||
- App Store Distribution
|
||||
|
||||
**Android**
|
||||
- Min. Android 7.0 (API 24)
|
||||
- Target: Android 14 (API 34)
|
||||
- ARM64, AAB fuer Play Store`,
|
||||
tips: [
|
||||
'WebGL laeuft in allen modernen Browsern',
|
||||
'iOS erfordert Apple Developer Account ($99/Jahr)',
|
||||
'Android AAB ist Pflicht fuer Play Store'
|
||||
]
|
||||
},
|
||||
'github-actions': {
|
||||
title: 'GitHub Actions Workflow',
|
||||
content: `Der CI/CD Workflow ist in Jobs aufgeteilt:
|
||||
|
||||
**1. version** - Ermittelt Version aus Git Tag
|
||||
**2. build-webgl** - Baut Browser-Version
|
||||
**3. build-ios** - Baut Xcode Projekt
|
||||
**4. build-ios-ipa** - Erstellt signierte IPA
|
||||
**5. build-android** - Baut AAB/APK
|
||||
**6. deploy-webgl** - Deployed zu CDN
|
||||
**7. upload-ios** - Laedt zu App Store Connect
|
||||
**8. upload-android** - Laedt zu Google Play
|
||||
|
||||
Trigger:
|
||||
- **Tags (v*)**: Alle Plattformen + Upload
|
||||
- **Push main**: Nur WebGL
|
||||
- **Manual**: Auswahlbar`,
|
||||
tips: [
|
||||
'Unity License muss als Secret hinterlegt sein',
|
||||
'Signing-Zertifikate als Base64 Secrets',
|
||||
'Cache beschleunigt Builds erheblich'
|
||||
]
|
||||
},
|
||||
'webgl-build': {
|
||||
title: 'WebGL Build',
|
||||
content: `WebGL ist die schnellste Build-Variante:
|
||||
|
||||
**Build-Einstellungen:**
|
||||
- Kompression: Brotli (beste Kompression)
|
||||
- Memory: 512MB (ausreichend fuer Spiel)
|
||||
- Exceptions: Nur explizite (Performance)
|
||||
- Linker: WASM (WebAssembly)
|
||||
|
||||
**Output:**
|
||||
- index.html
|
||||
- Build/*.wasm.br (komprimiert)
|
||||
- Build/*.data.br (Assets)
|
||||
- Build/*.js (Loader)
|
||||
|
||||
**Deployment:**
|
||||
- S3 + CloudFront CDN
|
||||
- Cache: 1 Jahr fuer Assets, 1h fuer HTML`,
|
||||
tips: [
|
||||
'Brotli-Kompression spart ~70% Bandbreite',
|
||||
'Erste Ladung ~10-15MB, danach gecached',
|
||||
'Server muss Brotli-Headers unterstuetzen'
|
||||
]
|
||||
},
|
||||
'ios-build': {
|
||||
title: 'iOS Build',
|
||||
content: `iOS Build erfolgt in zwei Schritten:
|
||||
|
||||
**Schritt 1: Unity Build**
|
||||
- Erstellt Xcode Projekt
|
||||
- Setzt iOS-spezifische Einstellungen
|
||||
- Output: Unity-iPhone.xcodeproj
|
||||
|
||||
**Schritt 2: Xcode Build**
|
||||
- Importiert Signing-Zertifikate
|
||||
- Archiviert Projekt
|
||||
- Exportiert signierte IPA
|
||||
|
||||
**Voraussetzungen:**
|
||||
- Apple Developer Account
|
||||
- Distribution Certificate (.p12)
|
||||
- Provisioning Profile
|
||||
- App Store Connect API Key`,
|
||||
tips: [
|
||||
'Zertifikate alle 1 Jahr erneuern',
|
||||
'Provisioning Profile fuer jede App ID',
|
||||
'TestFlight fuer Beta-Tests nutzen'
|
||||
]
|
||||
},
|
||||
'android-build': {
|
||||
title: 'Android Build',
|
||||
content: `Android Build erzeugt AAB oder APK:
|
||||
|
||||
**AAB (App Bundle)** - Fuer Play Store
|
||||
- Google optimiert fuer jedes Geraet
|
||||
- Kleinere Downloads
|
||||
- Pflicht seit 2021
|
||||
|
||||
**APK** - Fuer direkten Download
|
||||
- Debug-Builds fuer Testing
|
||||
- Sideloading moeglich
|
||||
|
||||
**Signing:**
|
||||
- Keystore (.jks/.keystore)
|
||||
- Key Alias und Passwoerter
|
||||
- Play App Signing empfohlen
|
||||
|
||||
**Voraussetzungen:**
|
||||
- Google Play Console Account ($25 einmalig)
|
||||
- Keystore fuer App-Signatur`,
|
||||
tips: [
|
||||
'Keystore NIEMALS verlieren (keine Veroeffentlichung mehr)',
|
||||
'Play App Signing: Google verwaltet Upload-Key',
|
||||
'Internal Testing fuer schnelle Tests'
|
||||
]
|
||||
},
|
||||
'deployment': {
|
||||
title: 'Store Deployment',
|
||||
content: `Automatisches Deployment zu den Stores:
|
||||
|
||||
**WebGL -> CDN (S3/CloudFront)**
|
||||
- Sync zu S3 Bucket
|
||||
- CloudFront Invalidation
|
||||
- Versionierte URLs
|
||||
|
||||
**iOS -> App Store Connect**
|
||||
- Upload via altool
|
||||
- API Key Authentifizierung
|
||||
- TestFlight Auto-Distribution
|
||||
|
||||
**Android -> Google Play**
|
||||
- Upload via r0adkll/upload-google-play
|
||||
- Service Account Auth
|
||||
- Internal Track zuerst`,
|
||||
tips: [
|
||||
'WebGL ist sofort live nach Deploy',
|
||||
'iOS: Review dauert 1-3 Tage',
|
||||
'Android: Review dauert wenige Stunden'
|
||||
]
|
||||
},
|
||||
'version-sync': {
|
||||
title: 'Version Synchronisation',
|
||||
content: `Versionen werden zentral verwaltet:
|
||||
|
||||
**version.json** (Runtime)
|
||||
- version: Semantische Version
|
||||
- build_number: Inkrementell
|
||||
- platform: Build-Target
|
||||
- commit_hash: Git SHA
|
||||
- min_api_version: API Kompatibilitaet
|
||||
|
||||
**VersionManager.cs** (Unity)
|
||||
- Laedt version.json zur Laufzeit
|
||||
- Prueft API-Kompatibilitaet
|
||||
- Zeigt Update-Hinweise
|
||||
|
||||
**Git Tags**
|
||||
- v1.0.0 -> Version 1.0.0
|
||||
- Trigger fuer Release-Builds`,
|
||||
tips: [
|
||||
'build_number aus GitHub Run Number',
|
||||
'min_api_version fuer erzwungene Updates',
|
||||
'Semantic Versioning: MAJOR.MINOR.PATCH'
|
||||
]
|
||||
},
|
||||
'summary': {
|
||||
title: 'Zusammenfassung & Naechste Schritte',
|
||||
content: `Du hast gelernt:
|
||||
|
||||
✓ Build-Targets: WebGL, iOS, Android
|
||||
✓ GitHub Actions Workflow
|
||||
✓ Platform-spezifische Einstellungen
|
||||
✓ Store Deployment Prozess
|
||||
✓ Version Management
|
||||
|
||||
**Naechste Schritte:**
|
||||
1. GitHub Secrets konfigurieren
|
||||
2. Apple/Google Developer Accounts einrichten
|
||||
3. Keystore und Zertifikate erstellen
|
||||
4. Ersten Release-Tag erstellen`,
|
||||
tips: [
|
||||
'Dokumentation in BreakpilotDrive/ci/',
|
||||
'BuildScript.cs fuer lokale Builds',
|
||||
'version.json wird automatisch aktualisiert'
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Components
|
||||
// ========================================
|
||||
|
||||
function WizardStepper({ steps, currentStep, onStepClick }: {
|
||||
steps: StepInfo[]
|
||||
currentStep: WizardStep
|
||||
onStepClick: (step: WizardStep) => void
|
||||
}) {
|
||||
const currentIndex = steps.findIndex(s => s.id === currentStep)
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1 overflow-x-auto pb-2">
|
||||
{steps.map((step, index) => {
|
||||
const isActive = step.id === currentStep
|
||||
const isCompleted = index < currentIndex
|
||||
const isClickable = index <= currentIndex + 1
|
||||
|
||||
return (
|
||||
<button
|
||||
key={step.id}
|
||||
onClick={() => isClickable && onStepClick(step.id)}
|
||||
disabled={!isClickable}
|
||||
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-all whitespace-nowrap ${
|
||||
isActive
|
||||
? 'bg-green-600 text-white'
|
||||
: isCompleted
|
||||
? 'bg-green-100 text-green-700 hover:bg-green-200'
|
||||
: isClickable
|
||||
? 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
: 'bg-gray-50 text-gray-400 cursor-not-allowed'
|
||||
}`}
|
||||
>
|
||||
<span className={`w-6 h-6 rounded-full flex items-center justify-center text-xs ${
|
||||
isActive ? 'bg-white/20' : isCompleted ? 'bg-green-200' : 'bg-gray-200'
|
||||
}`}>
|
||||
{isCompleted ? '✓' : index + 1}
|
||||
</span>
|
||||
<span className="hidden md:inline">{step.title}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function EducationCard({ title, content, tips }: { title: string; content: string; tips: string[] }) {
|
||||
return (
|
||||
<div className="bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 className="text-xl font-bold text-gray-900 mb-4">{title}</h2>
|
||||
<div className="prose prose-sm max-w-none mb-6">
|
||||
{content.split('\n\n').map((paragraph, i) => (
|
||||
<p key={i} className="text-gray-700 whitespace-pre-line mb-3">
|
||||
{paragraph.split('**').map((part, j) =>
|
||||
j % 2 === 1 ? <strong key={j}>{part}</strong> : part
|
||||
)}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
<div className="bg-green-50 rounded-lg p-4">
|
||||
<h3 className="text-sm font-semibold text-green-800 mb-2">Tipps:</h3>
|
||||
<ul className="space-y-1">
|
||||
{tips.map((tip, i) => (
|
||||
<li key={i} className="flex items-start gap-2 text-sm text-green-700">
|
||||
<span className="text-green-500 mt-0.5">•</span>
|
||||
{tip}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function PlatformCards() {
|
||||
const platforms = [
|
||||
{
|
||||
name: 'WebGL',
|
||||
icon: '🌐',
|
||||
status: 'Aktiv',
|
||||
size: '~15 MB',
|
||||
features: ['Browser-basiert', 'Sofort spielbar', 'Admin Panel Embed']
|
||||
},
|
||||
{
|
||||
name: 'iOS',
|
||||
icon: '📱',
|
||||
status: 'Bereit',
|
||||
size: '~80 MB',
|
||||
features: ['iPhone & iPad', 'App Store', 'Push Notifications']
|
||||
},
|
||||
{
|
||||
name: 'Android',
|
||||
icon: '🤖',
|
||||
status: 'Bereit',
|
||||
size: '~60 MB',
|
||||
features: ['Play Store', 'AAB Format', 'Wide Device Support']
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-6">
|
||||
{platforms.map((platform) => (
|
||||
<div key={platform.name} className="bg-gradient-to-br from-green-50 to-emerald-50 rounded-lg p-4 border border-green-100">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<span className="text-3xl">{platform.icon}</span>
|
||||
<div>
|
||||
<h4 className="font-bold text-gray-900">{platform.name}</h4>
|
||||
<p className="text-sm text-gray-500">{platform.size}</p>
|
||||
</div>
|
||||
<span className="ml-auto px-2 py-1 bg-green-100 text-green-700 text-xs rounded-full">
|
||||
{platform.status}
|
||||
</span>
|
||||
</div>
|
||||
<ul className="space-y-1">
|
||||
{platform.features.map((feature, i) => (
|
||||
<li key={i} className="text-sm text-gray-600 flex items-center gap-2">
|
||||
<span className="text-green-500">✓</span> {feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function WorkflowDiagram() {
|
||||
const jobs = [
|
||||
{ name: 'version', icon: '🏷️', runner: 'ubuntu' },
|
||||
{ name: 'build-webgl', icon: '🌐', runner: 'ubuntu' },
|
||||
{ name: 'build-ios', icon: '📱', runner: 'macos' },
|
||||
{ name: 'build-android', icon: '🤖', runner: 'ubuntu' },
|
||||
{ name: 'deploy', icon: '🚀', runner: 'ubuntu' },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="mt-6 bg-gray-900 rounded-lg p-6">
|
||||
<h3 className="text-white font-semibold mb-4">Workflow Jobs</h3>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{jobs.map((job, i) => (
|
||||
<div key={job.name} className="flex items-center gap-2">
|
||||
<div className="bg-gray-800 rounded-lg p-3 text-center min-w-[100px]">
|
||||
<span className="text-2xl">{job.icon}</span>
|
||||
<p className="text-white text-sm font-medium mt-1">{job.name}</p>
|
||||
<p className="text-gray-500 text-xs">{job.runner}</p>
|
||||
</div>
|
||||
{i < jobs.length - 1 && (
|
||||
<span className="text-gray-600 text-xl">→</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SecretsChecklist() {
|
||||
const secrets = [
|
||||
{ name: 'UNITY_LICENSE', desc: 'Unity Personal/Pro License', required: true },
|
||||
{ name: 'UNITY_EMAIL', desc: 'Unity Account Email', required: true },
|
||||
{ name: 'UNITY_PASSWORD', desc: 'Unity Account Password', required: true },
|
||||
{ name: 'IOS_BUILD_CERTIFICATE_BASE64', desc: 'Apple Distribution Certificate', required: false },
|
||||
{ name: 'IOS_PROVISION_PROFILE_BASE64', desc: 'iOS Provisioning Profile', required: false },
|
||||
{ name: 'ANDROID_KEYSTORE_BASE64', desc: 'Android Signing Keystore', required: false },
|
||||
{ name: 'AWS_ACCESS_KEY_ID', desc: 'AWS fuer S3/CloudFront', required: false },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="mt-6 bg-white rounded-lg border border-gray-200 overflow-hidden">
|
||||
<div className="px-4 py-3 bg-gray-50 border-b">
|
||||
<h3 className="font-semibold text-gray-800">GitHub Secrets Checkliste</h3>
|
||||
</div>
|
||||
<ul className="divide-y">
|
||||
{secrets.map((secret) => (
|
||||
<li key={secret.name} className="px-4 py-3 flex items-center justify-between">
|
||||
<div>
|
||||
<code className="text-sm bg-gray-100 px-2 py-1 rounded">{secret.name}</code>
|
||||
<p className="text-sm text-gray-500 mt-1">{secret.desc}</p>
|
||||
</div>
|
||||
<span className={`text-xs px-2 py-1 rounded ${
|
||||
secret.required ? 'bg-red-100 text-red-700' : 'bg-gray-100 text-gray-600'
|
||||
}`}>
|
||||
{secret.required ? 'Pflicht' : 'Optional'}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Main Component
|
||||
// ========================================
|
||||
|
||||
export default function BuildPipelineWizardPage() {
|
||||
const [currentStep, setCurrentStep] = useState<WizardStep>('welcome')
|
||||
const currentStepIndex = STEPS.findIndex(s => s.id === currentStep)
|
||||
const education = EDUCATION_CONTENT[currentStep]
|
||||
|
||||
const goToNext = () => {
|
||||
const nextIndex = currentStepIndex + 1
|
||||
if (nextIndex < STEPS.length) {
|
||||
setCurrentStep(STEPS[nextIndex].id)
|
||||
}
|
||||
}
|
||||
|
||||
const goToPrevious = () => {
|
||||
const prevIndex = currentStepIndex - 1
|
||||
if (prevIndex >= 0) {
|
||||
setCurrentStep(STEPS[prevIndex].id)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<AdminLayout
|
||||
title="Build Pipeline Wizard"
|
||||
description="Lerne die Multi-Platform Build Pipeline kennen"
|
||||
>
|
||||
{/* Back Link */}
|
||||
<div className="mb-6">
|
||||
<Link
|
||||
href="/admin/game"
|
||||
className="inline-flex items-center gap-2 text-gray-600 hover:text-gray-900"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Zurueck zum Game Dashboard
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Stepper */}
|
||||
<div className="mb-8">
|
||||
<WizardStepper
|
||||
steps={STEPS}
|
||||
currentStep={currentStep}
|
||||
onStepClick={setCurrentStep}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* Main Content */}
|
||||
<div className="lg:col-span-2">
|
||||
<EducationCard
|
||||
title={education.title}
|
||||
content={education.content}
|
||||
tips={education.tips}
|
||||
/>
|
||||
|
||||
{/* Step-specific content */}
|
||||
{currentStep === 'platforms' && <PlatformCards />}
|
||||
{currentStep === 'github-actions' && <WorkflowDiagram />}
|
||||
{currentStep === 'deployment' && <SecretsChecklist />}
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex justify-between mt-8">
|
||||
<button
|
||||
onClick={goToPrevious}
|
||||
disabled={currentStepIndex === 0}
|
||||
className="px-6 py-3 rounded-lg font-medium bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Zurueck
|
||||
</button>
|
||||
<button
|
||||
onClick={goToNext}
|
||||
disabled={currentStepIndex === STEPS.length - 1}
|
||||
className="px-6 py-3 rounded-lg font-medium bg-green-600 text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{currentStepIndex === STEPS.length - 1 ? 'Fertig' : 'Weiter'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar */}
|
||||
<div className="space-y-6">
|
||||
{/* Progress */}
|
||||
<div className="bg-white rounded-xl shadow p-6">
|
||||
<h3 className="font-semibold text-gray-900 mb-4">Fortschritt</h3>
|
||||
<div className="relative pt-1">
|
||||
<div className="flex mb-2 items-center justify-between">
|
||||
<span className="text-xs font-semibold text-green-600">
|
||||
Schritt {currentStepIndex + 1} von {STEPS.length}
|
||||
</span>
|
||||
<span className="text-xs font-semibold text-green-600">
|
||||
{Math.round(((currentStepIndex + 1) / STEPS.length) * 100)}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="overflow-hidden h-2 mb-4 text-xs flex rounded bg-green-100">
|
||||
<div
|
||||
style={{ width: `${((currentStepIndex + 1) / STEPS.length) * 100}%` }}
|
||||
className="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-green-600 transition-all duration-300"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pipeline Overview */}
|
||||
<div className="bg-gradient-to-br from-green-500 to-emerald-600 rounded-xl shadow p-6 text-white">
|
||||
<h3 className="font-semibold mb-4">Pipeline Flow</h3>
|
||||
<div className="text-sm space-y-2 font-mono">
|
||||
<div className="bg-white/10 rounded px-2 py-1">Git Push/Tag</div>
|
||||
<div className="text-center text-green-200">↓</div>
|
||||
<div className="bg-white/10 rounded px-2 py-1">GitHub Actions</div>
|
||||
<div className="text-center text-green-200">↓</div>
|
||||
<div className="bg-white/10 rounded px-2 py-1">Unity Build</div>
|
||||
<div className="text-center text-green-200">↓</div>
|
||||
<div className="bg-white/10 rounded px-2 py-1">Deploy / Upload</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Links */}
|
||||
<div className="bg-white rounded-xl shadow p-6">
|
||||
<h3 className="font-semibold text-gray-900 mb-4">Wichtige Dateien</h3>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li className="text-gray-600">
|
||||
<span className="text-green-600">YAML:</span> ci/build-all-platforms.yml
|
||||
</li>
|
||||
<li className="text-gray-600">
|
||||
<span className="text-green-600">C#:</span> Assets/Editor/BuildScript.cs
|
||||
</li>
|
||||
<li className="text-gray-600">
|
||||
<span className="text-green-600">JSON:</span> Assets/Resources/version.json
|
||||
</li>
|
||||
<li className="text-gray-600">
|
||||
<span className="text-green-600">Plist:</span> ci/ios-export-options.plist
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AdminLayout>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user