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>
148 lines
5.6 KiB
TypeScript
148 lines
5.6 KiB
TypeScript
'use client'
|
|
|
|
import AdminLayout from '@/components/admin/AdminLayout'
|
|
import { useBacklog } from './_components/useBacklog'
|
|
import { BacklogItemCard } from './_components/BacklogItemCard'
|
|
|
|
export default function BacklogPage() {
|
|
const backlog = useBacklog()
|
|
const progress = backlog.getProgress()
|
|
|
|
return (
|
|
<AdminLayout
|
|
title="Production Readiness Backlog"
|
|
description="Alle Aufgaben vor dem Go-Live"
|
|
>
|
|
{/* Overall Progress */}
|
|
<div className="bg-white rounded-xl border border-slate-200 p-6 mb-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div>
|
|
<h2 className="text-lg font-semibold text-slate-900">Gesamtfortschritt</h2>
|
|
<p className="text-sm text-slate-500">
|
|
{progress.completed} von {progress.total} Aufgaben abgeschlossen
|
|
</p>
|
|
</div>
|
|
<div className="text-3xl font-bold text-primary-600">{progress.percentage}%</div>
|
|
</div>
|
|
<div className="w-full bg-slate-200 rounded-full h-3">
|
|
<div
|
|
className="bg-primary-600 h-3 rounded-full transition-all duration-500"
|
|
style={{ width: `${progress.percentage}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Category Cards */}
|
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 mb-6">
|
|
{backlog.categories.map((cat) => {
|
|
const catProgress = backlog.getCategoryProgress(cat.id)
|
|
return (
|
|
<button
|
|
key={cat.id}
|
|
onClick={() => backlog.setSelectedCategory(backlog.selectedCategory === cat.id ? null : cat.id)}
|
|
className={`p-4 rounded-xl border-2 text-left transition-all ${
|
|
backlog.selectedCategory === cat.id
|
|
? `${cat.color} ring-2 ring-offset-2`
|
|
: 'bg-white border-slate-200 hover:border-slate-300'
|
|
}`}
|
|
>
|
|
<div className="flex items-center gap-3 mb-2">
|
|
<span className={backlog.selectedCategory === cat.id ? '' : 'text-slate-600'}>
|
|
{cat.icon}
|
|
</span>
|
|
<span className="font-medium text-sm">{cat.name}</span>
|
|
</div>
|
|
<div className="text-xs text-slate-500">
|
|
{catProgress.completed}/{catProgress.total} erledigt
|
|
</div>
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
|
|
{/* Filters & Search */}
|
|
<div className="flex flex-wrap gap-4 mb-6">
|
|
<div className="flex-1 min-w-[200px]">
|
|
<input
|
|
type="text"
|
|
placeholder="Suchen..."
|
|
value={backlog.searchQuery}
|
|
onChange={(e) => backlog.setSearchQuery(e.target.value)}
|
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
|
|
/>
|
|
</div>
|
|
<select
|
|
value={backlog.selectedPriority || ''}
|
|
onChange={(e) => backlog.setSelectedPriority(e.target.value || null)}
|
|
className="px-4 py-2 border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
|
|
>
|
|
<option value="">Alle Prioritaeten</option>
|
|
<option value="critical">Kritisch</option>
|
|
<option value="high">Hoch</option>
|
|
<option value="medium">Mittel</option>
|
|
<option value="low">Niedrig</option>
|
|
</select>
|
|
{(backlog.selectedCategory || backlog.selectedPriority || backlog.searchQuery) && (
|
|
<button
|
|
onClick={backlog.clearFilters}
|
|
className="px-4 py-2 text-sm text-slate-600 hover:text-slate-900"
|
|
>
|
|
Filter zuruecksetzen
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Backlog Items */}
|
|
<div className="space-y-4">
|
|
{backlog.filteredItems.map((item) => {
|
|
const category = backlog.categories.find((c) => c.id === item.category)
|
|
return (
|
|
<BacklogItemCard
|
|
key={item.id}
|
|
item={item}
|
|
category={category}
|
|
isExpanded={backlog.expandedItems.has(item.id)}
|
|
onToggleExpand={() => backlog.toggleExpand(item.id)}
|
|
onUpdateStatus={(status) => backlog.updateItemStatus(item.id, status)}
|
|
onToggleSubtask={(subtaskId) => backlog.toggleSubtask(item.id, subtaskId)}
|
|
/>
|
|
)
|
|
})}
|
|
</div>
|
|
|
|
{backlog.filteredItems.length === 0 && (
|
|
<div className="text-center py-12 text-slate-500">
|
|
Keine Aufgaben gefunden. Versuche einen anderen Filter.
|
|
</div>
|
|
)}
|
|
|
|
{/* Info Box */}
|
|
<div className="mt-8 bg-amber-50 border border-amber-200 rounded-xl p-6">
|
|
<div className="flex items-start gap-4">
|
|
<svg
|
|
className="w-6 h-6 text-amber-600 flex-shrink-0 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>
|
|
<h3 className="font-semibold text-amber-900">Wichtiger Hinweis</h3>
|
|
<p className="text-sm text-amber-800 mt-1">
|
|
Diese Backlog-Liste muss vollstaendig abgearbeitet sein, bevor BreakPilot in den
|
|
Produktivbetrieb gehen kann. Alle kritischen Items muessen abgeschlossen sein.
|
|
Der Fortschritt wird lokal im Browser gespeichert.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AdminLayout>
|
|
)
|
|
}
|