feat: group certificate pairs in CertificatesPage
All checks were successful
CI Pipeline / Rust Format Check (push) Successful in 5s
CI Pipeline / Clippy Lints (push) Successful in 52s
CI Pipeline / Rust Unit Tests (push) Successful in 1m11s
CI Pipeline / Security Audit (push) Successful in 5s
CI Pipeline / Frontend Lint & Type Check (push) Successful in 14s
CI Pipeline / Build .deb & Release (push) Has been skipped
All checks were successful
CI Pipeline / Rust Format Check (push) Successful in 5s
CI Pipeline / Clippy Lints (push) Successful in 52s
CI Pipeline / Rust Unit Tests (push) Successful in 1m11s
CI Pipeline / Security Audit (push) Successful in 5s
CI Pipeline / Frontend Lint & Type Check (push) Successful in 14s
CI Pipeline / Build .deb & Release (push) Has been skipped
This commit is contained in:
@ -463,58 +463,70 @@ export default function CertificatesPage() {
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{certs.map((cert) => {
|
{Object.values(
|
||||||
const expiring = cert.status === 'active' && isExpiringSoon(cert.expires_at)
|
certs.reduce((acc, cert) => {
|
||||||
|
const groupKey = cert.host_id || 'unassigned'
|
||||||
|
if (!acc[groupKey]) acc[groupKey] = []
|
||||||
|
acc[groupKey].push(cert)
|
||||||
|
return acc
|
||||||
|
}, {} as Record<string, Certificate[]>)
|
||||||
|
).map((group) => {
|
||||||
|
const primary = group[0]
|
||||||
|
const isPair = group.length > 1
|
||||||
|
const expiring = primary.status === 'active' && isExpiringSoon(primary.expires_at)
|
||||||
return (
|
return (
|
||||||
<TableRow key={cert.id} hover>
|
<TableRow key={primary.id} hover>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Typography variant="body2" fontWeight={500}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
{cert.common_name}
|
<Typography variant="body2" fontWeight={500}>
|
||||||
</Typography>
|
{primary.common_name}
|
||||||
|
</Typography>
|
||||||
|
{isPair && <Chip label={`${group.length} items`} size="small" color="secondary" variant="outlined" />}
|
||||||
|
</Box>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Typography variant="body2" sx={{ fontFamily: 'monospace', fontSize: 12 }}>
|
<Typography variant="body2" sx={{ fontFamily: 'monospace', fontSize: 12 }}>
|
||||||
{cert.serial_number}
|
{primary.serial_number}
|
||||||
</Typography>
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{statusChip(cert.status)}</TableCell>
|
<TableCell>{statusChip(primary.status)}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Typography variant="body2">{fmtDate(cert.issued_at)}</Typography>
|
<Typography variant="body2">{fmtDate(primary.issued_at)}</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
sx={{ color: expiring ? 'error.main' : 'inherit', fontWeight: expiring ? 600 : 400 }}
|
sx={{ color: expiring ? 'error.main' : 'inherit', fontWeight: expiring ? 600 : 400 }}
|
||||||
>
|
>
|
||||||
{fmtDate(cert.expires_at)}
|
{fmtDate(primary.expires_at)}
|
||||||
{expiring && ' ⚠️'}
|
{expiring && ' ⚠️'}
|
||||||
</Typography>
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Typography variant="body2" sx={{ fontFamily: 'monospace', fontSize: 11 }}>
|
<Typography variant="body2" sx={{ fontFamily: 'monospace', fontSize: 11 }}>
|
||||||
{cert.host_id ?? <em>Root CA</em>}
|
{primary.host_id ?? <em>Root CA</em>}
|
||||||
</Typography>
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="right">
|
<TableCell align="right">
|
||||||
{canWrite && (
|
{canWrite && (
|
||||||
<>
|
<>
|
||||||
<Tooltip title="Renew certificate">
|
<Tooltip title={`Renew certificate ${isPair ? 'pair' : ''}`}>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
sx={{ mr: 1 }}
|
sx={{ mr: 1 }}
|
||||||
onClick={() => handleRenew(cert.id)}
|
onClick={() => handleRenew(primary.id)}
|
||||||
>
|
>
|
||||||
Renew
|
Renew
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{cert.status === 'active' && (
|
{primary.status === 'active' && (
|
||||||
<Tooltip title="Revoke certificate">
|
<Tooltip title={`Revoke certificate ${isPair ? 'pair' : ''}`}>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="error"
|
color="error"
|
||||||
onClick={() => handleRevoke(cert.id)}
|
onClick={() => handleRevoke(primary.id)}
|
||||||
>
|
>
|
||||||
Revoke
|
Revoke
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user