fix: polish CertificatesPage layout
- Truncate fingerprint with hover tooltip - Remove duplicate warning icon in stale banner - Style file inputs to match design system - Bump grid min-width for better card spacing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
65
ui/src/pages/vendor/CertificatesPage.tsx
vendored
65
ui/src/pages/vendor/CertificatesPage.tsx
vendored
@@ -7,7 +7,7 @@ import {
|
||||
Spinner,
|
||||
useToast,
|
||||
} from '@cameleer/design-system';
|
||||
import { Upload, ShieldCheck, RotateCcw, Trash2, AlertTriangle } from 'lucide-react';
|
||||
import { Upload, ShieldCheck, RotateCcw, Trash2 } from 'lucide-react';
|
||||
import {
|
||||
useVendorCertificates,
|
||||
useStageCertificate,
|
||||
@@ -35,6 +35,12 @@ function expiryColor(iso: string | null | undefined): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function shortenFingerprint(fp: string | null | undefined): string {
|
||||
if (!fp) return '—';
|
||||
// Show first 23 chars (8 hex pairs) + ellipsis
|
||||
return fp.length > 23 ? fp.slice(0, 23) + '...' : fp;
|
||||
}
|
||||
|
||||
function CertCard({
|
||||
cert,
|
||||
title,
|
||||
@@ -68,8 +74,8 @@ function CertCard({
|
||||
</div>
|
||||
<div className={styles.kvRow}>
|
||||
<span className={styles.kvLabel}>Fingerprint</span>
|
||||
<span className={styles.kvValueMono} style={{ fontSize: '0.7rem', maxWidth: 220, textAlign: 'right' }}>
|
||||
{cert.fingerprint}
|
||||
<span className={styles.kvValueMono} title={cert.fingerprint} style={{ fontSize: '0.7rem' }}>
|
||||
{shortenFingerprint(cert.fingerprint)}
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.kvRow}>
|
||||
@@ -94,6 +100,34 @@ function CertCard({
|
||||
);
|
||||
}
|
||||
|
||||
function FileField({ label, inputRef, accept }: {
|
||||
label: string;
|
||||
inputRef: React.RefObject<HTMLInputElement | null>;
|
||||
accept: string;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<label style={{ fontSize: 13, color: 'var(--text-secondary)', display: 'block', marginBottom: 4 }}>
|
||||
{label}
|
||||
</label>
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="file"
|
||||
accept={accept}
|
||||
style={{
|
||||
fontSize: 13,
|
||||
color: 'var(--text-primary)',
|
||||
background: 'var(--bg-inset)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 6,
|
||||
padding: '6px 8px',
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function CertificatesPage() {
|
||||
const { toast } = useToast();
|
||||
const { data, isLoading, isError } = useVendorCertificates();
|
||||
@@ -143,7 +177,6 @@ export function CertificatesPage() {
|
||||
const result = await stageMutation.mutateAsync(formData);
|
||||
if (result.valid) {
|
||||
toast({ title: 'Certificate staged successfully', variant: 'success' });
|
||||
// Clear file inputs
|
||||
if (certInputRef.current) certInputRef.current.value = '';
|
||||
if (keyInputRef.current) keyInputRef.current.value = '';
|
||||
if (caInputRef.current) caInputRef.current.value = '';
|
||||
@@ -192,13 +225,12 @@ export function CertificatesPage() {
|
||||
|
||||
{data.staleTenantCount > 0 && (
|
||||
<Alert variant="warning" title="CA bundle updated">
|
||||
<AlertTriangle size={14} style={{ verticalAlign: 'middle', marginRight: 6 }} />
|
||||
{data.staleTenantCount} tenant{data.staleTenantCount > 1 ? 's' : ''} need a restart
|
||||
to pick up the updated CA bundle.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))', gap: 16 }}>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(340px, 1fr))', gap: 16 }}>
|
||||
{data.active && (
|
||||
<CertCard cert={data.active} title="Active Certificate" />
|
||||
)}
|
||||
@@ -251,24 +283,9 @@ export function CertificatesPage() {
|
||||
{/* Upload card */}
|
||||
<Card title="Upload Certificate">
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<div>
|
||||
<label style={{ fontSize: 13, color: 'var(--text-secondary)', display: 'block', marginBottom: 4 }}>
|
||||
Certificate (PEM) *
|
||||
</label>
|
||||
<input ref={certInputRef} type="file" accept=".pem,.crt,.cer" />
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ fontSize: 13, color: 'var(--text-secondary)', display: 'block', marginBottom: 4 }}>
|
||||
Private Key (PEM) *
|
||||
</label>
|
||||
<input ref={keyInputRef} type="file" accept=".pem,.key" />
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ fontSize: 13, color: 'var(--text-secondary)', display: 'block', marginBottom: 4 }}>
|
||||
CA Bundle (PEM, optional)
|
||||
</label>
|
||||
<input ref={caInputRef} type="file" accept=".pem,.crt,.cer" />
|
||||
</div>
|
||||
<FileField label="Certificate (PEM) *" inputRef={certInputRef} accept=".pem,.crt,.cer" />
|
||||
<FileField label="Private Key (PEM) *" inputRef={keyInputRef} accept=".pem,.key" />
|
||||
<FileField label="CA Bundle (PEM, optional)" inputRef={caInputRef} accept=".pem,.crt,.cer" />
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleUpload}
|
||||
|
||||
Reference in New Issue
Block a user