diff --git a/frontend/src/components/StatusBadge.tsx b/frontend/src/components/StatusBadge.tsx new file mode 100644 index 00000000..9a355fc8 --- /dev/null +++ b/frontend/src/components/StatusBadge.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { CheckCircle, Clock, Lock, Pause, XCircle } from 'lucide-react'; + +interface StatusBadgeProps { + status: 'active' | 'revoked' | 'expired' | 'frozen' | 'suspended'; + className?: string; +} + +const statusConfig: Record = { + active: { + bg: 'bg-green-100 dark:bg-green-900/30', + text: 'text-green-700 dark:text-green-400', + icon: , + label: 'Active', + }, + revoked: { + bg: 'bg-red-100 dark:bg-red-900/30', + text: 'text-red-700 dark:text-red-400', + icon: , + label: 'Revoked', + }, + expired: { + bg: 'bg-gray-100 dark:bg-gray-900/30', + text: 'text-gray-700 dark:text-gray-400', + icon: , + label: 'Expired', + }, + frozen: { + bg: 'bg-blue-100 dark:bg-blue-900/30', + text: 'text-blue-700 dark:text-blue-400', + icon: , + label: 'Frozen', + }, + suspended: { + bg: 'bg-amber-100 dark:bg-amber-900/30', + text: 'text-amber-700 dark:text-amber-400', + icon: , + label: 'Suspended', + }, +}; + +/** + * Renders a status badge for a certificate + * Displays the certificate status with appropriate color, icon, and label + */ +export const StatusBadge: React.FC = ({ status, className = '' }) => { + const config = statusConfig[status] || statusConfig.active; + + return ( + + {config.icon} + {config.label} + + ); +}; + +/** + * Utility function to check if a certificate status is active (revocable) + */ +export const isRevocableStatus = (status: string): boolean => { + return status === 'active'; +}; + +/** + * Utility function to get a user-friendly message for why a certificate cannot be revoked + */ +export const getRevocationBlockedMessage = (status: string): string => { + switch (status) { + case 'revoked': + return 'This certificate has already been revoked.'; + case 'expired': + return 'Cannot revoke an expired certificate.'; + case 'frozen': + return 'Cannot revoke a frozen certificate. Please unfreeze it first.'; + case 'suspended': + return 'Cannot revoke a suspended certificate. Please reinstate it first.'; + default: + return 'Cannot revoke this certificate.'; + } +}; + +export default StatusBadge; diff --git a/frontend/src/pages/RevokeCertificate.tsx b/frontend/src/pages/RevokeCertificate.tsx index 7c831991..dbcce9ec 100644 --- a/frontend/src/pages/RevokeCertificate.tsx +++ b/frontend/src/pages/RevokeCertificate.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { findCertBySerialNumber, revokeCertificate } from '../api'; import { AlertTriangle, Search, ShieldAlert, CheckCircle, XCircle, Loader2 } from 'lucide-react'; +import { StatusBadge, isRevocableStatus, getRevocationBlockedMessage } from '../components/StatusBadge'; import type { Certificate } from '../api'; interface Message { type: 'success' | 'error' | 'warning' | ''; @@ -66,6 +67,9 @@ const RevokeCertificate = () => { const result = await findCertBySerialNumber(serialNumber.trim()); if (result && result.status === 'revoked') { setMessage({ type: 'warning', text: 'This certificate has already been revoked.' }); + } else if (result && !isRevocableStatus(result.status)) { + // Block revocation for non-active statuses (expired, frozen, suspended) + setMessage({ type: 'warning', text: getRevocationBlockedMessage(result.status) }); } else if (result) { setCertificate({ id: result.id, @@ -190,7 +194,7 @@ const RevokeCertificate = () => { 2 Certificate Found - Active +