myke69's picture
Add files using upload-large-folder tool
3752e5d verified
Raw
History Blame Contribute Delete
3.73 kB
import React from 'react'
const CAT_LABEL = {
termination: 'Termination',
auto_renewal: 'Auto-renewal',
term: 'Term / Duration',
liability_cap: 'Liability Cap',
indemnification: 'Indemnification',
governing_law: 'Governing Law',
confidentiality: 'Confidentiality',
payment_terms: 'Payment Terms',
sla: 'Service Levels',
data_protection: 'Data Protection',
ip_ownership: 'IP Ownership',
insurance: 'Insurance',
audit_rights: 'Audit Rights',
assignment: 'Assignment',
exclusivity: 'Exclusivity',
force_majeure: 'Force Majeure',
warranty: 'Warranty',
notice: 'Notice',
deliverables: 'Deliverables',
}
const COLS = ['HIGH', 'MEDIUM', 'LOW']
function cellBg(level, count) {
if (count === 0) return 'transparent'
const alpha = Math.min(0.18 + count * 0.28, 0.92)
if (level === 'HIGH') return `rgba(201,42,42,${alpha})`
if (level === 'MEDIUM') return `rgba(217,117,11,${alpha})`
return `rgba(91,100,120,${alpha})`
}
function cellColor(level, count) {
if (count === 0) return '#ccc'
if (count >= 2) return '#fff'
if (level === 'HIGH') return '#c92a2a'
if (level === 'MEDIUM') return '#d9750b'
return '#5b6478'
}
export default function RiskHeatmap({ risks, onSelect }) {
if (!risks.length) return <p className="muted pad">No risks to display.</p>
// build grid: category -> { HIGH, MEDIUM, LOW } -> [risk, ...]
const grid = {}
risks.forEach((r) => {
if (!grid[r.category]) grid[r.category] = { HIGH: [], MEDIUM: [], LOW: [] }
grid[r.category][r.level].push(r)
})
const cats = Object.keys(grid).sort((a, b) => {
const maxSev = (cat) => Math.max(...risks.filter(r => r.category === cat).map(r => r.severity))
return maxSev(b) - maxSev(a)
})
return (
<div className="heatmap-wrap">
<div className="heatmap-legend">
<span className="hm-dot hm-high" /> HIGH
<span className="hm-dot hm-med" /> MEDIUM
<span className="hm-dot hm-low" /> LOW
<span className="hm-hint">— click a cell to see its risks</span>
</div>
<table className="heatmap">
<thead>
<tr>
<th className="hm-cat-hdr">Category</th>
{COLS.map(c => (
<th key={c} className={`hm-col-hdr hm-col-${c.toLowerCase()}`}>{c}</th>
))}
<th className="hm-col-hdr">Total</th>
</tr>
</thead>
<tbody>
{cats.map((cat) => {
const row = grid[cat]
const total = COLS.reduce((s, l) => s + row[l].length, 0)
return (
<tr key={cat}>
<td className="hm-cat">{CAT_LABEL[cat] || cat}</td>
{COLS.map((level) => {
const n = row[level].length
const firstRisk = row[level][0]
return (
<td
key={level}
className="hm-cell"
style={{
background: cellBg(level, n),
color: cellColor(level, n),
cursor: n > 0 ? 'pointer' : 'default',
}}
title={n > 0 ? row[level].map(r => r.title).join('; ') : ''}
onClick={() => n > 0 && firstRisk && onSelect(firstRisk.clause_id)}
>
{n > 0 ? n : <span className="hm-empty">·</span>}
</td>
)
})}
<td className="hm-total">{total}</td>
</tr>
)
})}
</tbody>
</table>
</div>
)
}