fix: Erstbewertung aus risk_assessment + Pagination + Projektname
- Erstbewertung S/E/P liest jetzt aus risk_assessment statt hazard - Hazards: Pagination 50 pro Seite mit < > Navigation - Massnahmen: Lazy-Load 50 pro Accordion mit "Mehr laden" - Sidebar: Projektname (z.B. "Kniehebelpresse HP-500") prominent - Uebersicht: Nur 2 API-Calls (keine schweren Listen) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+27
-7
@@ -139,13 +139,28 @@ export function RiskAssessmentTable({ projectId, hazards, onReassess, decisions,
|
||||
finally { setSaving(null) }
|
||||
}
|
||||
|
||||
const PAGE_SIZE = 50
|
||||
const [page, setPage] = useState(0)
|
||||
const sorted = [...hazards].sort((a, b) => (b.r_inherent || 0) - (a.r_inherent || 0))
|
||||
const totalPages = Math.ceil(sorted.length / PAGE_SIZE)
|
||||
const paged = sorted.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE)
|
||||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||
<div className="px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<h2 className="text-sm font-semibold text-gray-900 dark:text-white">Risikobewertungstabelle (ISO 12100)</h2>
|
||||
<span className="text-xs text-gray-500">{hazards.length} Gefaehrdungen</span>
|
||||
<div className="flex items-center gap-3">
|
||||
{totalPages > 1 && (
|
||||
<div className="flex items-center gap-1 text-xs">
|
||||
<button onClick={() => setPage(Math.max(0, page - 1))} disabled={page === 0}
|
||||
className="px-2 py-1 rounded border border-gray-200 hover:bg-gray-50 disabled:opacity-30 disabled:cursor-not-allowed"><</button>
|
||||
<span className="px-2 text-gray-600">Seite {page + 1} / {totalPages}</span>
|
||||
<button onClick={() => setPage(Math.min(totalPages - 1, page + 1))} disabled={page >= totalPages - 1}
|
||||
className="px-2 py-1 rounded border border-gray-200 hover:bg-gray-50 disabled:opacity-30 disabled:cursor-not-allowed">></button>
|
||||
</div>
|
||||
)}
|
||||
<span className="text-xs text-gray-500">{hazards.length} Gefaehrdungen</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
@@ -185,15 +200,20 @@ export function RiskAssessmentTable({ projectId, hazards, onReassess, decisions,
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-100 dark:divide-gray-700">
|
||||
{sorted.map(h => {
|
||||
{paged.map(h => {
|
||||
const ra = (h as Record<string, unknown>).risk_assessment as Record<string, number> | null
|
||||
const initS = ra?.severity || h.severity || 3
|
||||
const initE = ra?.exposure || h.exposure || 3
|
||||
const initP = ra?.probability || h.probability || 3
|
||||
const initA = h.avoidance || 0
|
||||
const e = edits[h.id]
|
||||
const initRpz = h.r_inherent || rpz(h.severity, h.exposure, h.probability, h.avoidance)
|
||||
const initRpz = rpz(initS, initE, initP, initA)
|
||||
const afterRpz = e ? rpz(e.severity, e.exposure, e.probability, e.avoidance) : initRpz
|
||||
const afterLevel = getRiskLevelISO(afterRpz)
|
||||
const sil = silFromRpz(afterRpz)
|
||||
const pl = plFromRpz(afterRpz)
|
||||
const mc = mitCounts[h.id] || 0
|
||||
const changed = e && (e.severity !== h.severity || e.exposure !== h.exposure || e.probability !== h.probability || e.avoidance !== (h.avoidance || 3))
|
||||
const changed = e && (e.severity !== initS || e.exposure !== initE || e.probability !== initP)
|
||||
|
||||
return (
|
||||
<tr key={h.id} className="hover:bg-gray-50 dark:hover:bg-gray-750 transition-colors">
|
||||
@@ -208,9 +228,9 @@ export function RiskAssessmentTable({ projectId, hazards, onReassess, decisions,
|
||||
</span>
|
||||
</td>
|
||||
{/* Initial S/E/P/RPZ/Risk */}
|
||||
<td className="px-2 py-2 text-center text-gray-700 dark:text-gray-300">{h.severity}</td>
|
||||
<td className="px-2 py-2 text-center text-gray-700 dark:text-gray-300">{h.exposure}</td>
|
||||
<td className="px-2 py-2 text-center text-gray-700 dark:text-gray-300">{h.probability}</td>
|
||||
<td className="px-2 py-2 text-center text-gray-700 dark:text-gray-300">{initS}</td>
|
||||
<td className="px-2 py-2 text-center text-gray-700 dark:text-gray-300">{initE}</td>
|
||||
<td className="px-2 py-2 text-center text-gray-700 dark:text-gray-300">{initP}</td>
|
||||
<td className="px-2 py-2 text-center font-bold text-gray-900 dark:text-white">{initRpz}</td>
|
||||
<td className="px-2 py-2 text-center border-r border-gray-200 dark:border-gray-600">
|
||||
<span className={`inline-block px-1.5 py-0.5 rounded-full text-[10px] font-medium border ${getRiskColor(h.risk_level)}`}>
|
||||
|
||||
@@ -27,6 +27,7 @@ export default function MitigationsPage() {
|
||||
const [libraryFilter, setLibraryFilter] = useState<string | undefined>()
|
||||
const [showSuggest, setShowSuggest] = useState(false)
|
||||
const [expanded, setExpanded] = useState<Record<string, boolean>>({ design: true, protection: true, information: true })
|
||||
const [mitPages, setMitPages] = useState<Record<string, number>>({ design: 1, protection: 1, information: 1 })
|
||||
const [selected, setSelected] = useState<Set<string>>(new Set())
|
||||
const [batchAction, setBatchAction] = useState<'verify' | 'delete' | null>(null)
|
||||
|
||||
@@ -183,8 +184,8 @@ export default function MitigationsPage() {
|
||||
<div>Gefaehrdung</div>
|
||||
<div>Status</div>
|
||||
</div>
|
||||
{/* Rows */}
|
||||
{items.map((m) => (
|
||||
{/* Rows — paginated */}
|
||||
{items.slice(0, (mitPages[type] || 1) * 50).map((m) => (
|
||||
<div key={m.id}
|
||||
className={`grid grid-cols-[24px_2fr_1fr_80px] gap-2 px-4 py-2 border-t border-gray-50 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-750 transition-colors ${selected.has(m.id) ? 'bg-purple-50 dark:bg-purple-900/10' : ''}`}>
|
||||
<div className="pt-0.5">
|
||||
@@ -203,6 +204,12 @@ export default function MitigationsPage() {
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{items.length > (mitPages[type] || 1) * 50 && (
|
||||
<button onClick={() => setMitPages(prev => ({ ...prev, [type]: (prev[type] || 1) + 1 }))}
|
||||
className="w-full py-2 text-xs text-purple-600 hover:bg-purple-50 border-t border-gray-100 transition-colors">
|
||||
Weitere {Math.min(50, items.length - (mitPages[type] || 1) * 50)} von {items.length} laden...
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user