import { useState } from 'react'; import { Shield, ChevronDown, ChevronRight, AlertCircle } from 'lucide-react'; import { Card } from './ui/Card'; import { Badge } from './ui/Badge'; import { Progress } from './ui/Progress'; interface SecurityScoreDisplayProps { score: number; maxScore?: number; breakdown?: Record; suggestions?: string[]; size?: 'sm' | 'md' | 'lg'; showDetails?: boolean; } const CATEGORY_LABELS: Record = { hsts: 'HSTS', csp: 'Content Security Policy', x_frame_options: 'X-Frame-Options', x_content_type_options: 'X-Content-Type-Options', referrer_policy: 'Referrer Policy', permissions_policy: 'Permissions Policy', cross_origin: 'Cross-Origin Headers', }; const CATEGORY_DESCRIPTIONS: Record = { hsts: 'HTTP Strict Transport Security enforces HTTPS connections', csp: 'Content Security Policy prevents XSS and injection attacks', x_frame_options: 'Prevents clickjacking by controlling iframe embedding', x_content_type_options: 'Prevents MIME type sniffing attacks', referrer_policy: 'Controls referrer information sent with requests', permissions_policy: 'Restricts browser features and APIs', cross_origin: 'Cross-Origin isolation headers for enhanced security', }; export function SecurityScoreDisplay({ score, maxScore = 100, breakdown = {}, suggestions = [], size = 'md', showDetails = true, }: SecurityScoreDisplayProps) { const [expandedBreakdown, setExpandedBreakdown] = useState(false); const [expandedSuggestions, setExpandedSuggestions] = useState(false); const percentage = Math.round((score / maxScore) * 100); const getScoreColor = () => { if (percentage >= 75) return 'text-green-600 dark:text-green-400'; if (percentage >= 50) return 'text-yellow-600 dark:text-yellow-400'; return 'text-red-600 dark:text-red-400'; }; const getScoreBgColor = () => { if (percentage >= 75) return 'bg-green-100 dark:bg-green-900/20'; if (percentage >= 50) return 'bg-yellow-100 dark:bg-yellow-900/20'; return 'bg-red-100 dark:bg-red-900/20'; }; const getScoreVariant = (): 'success' | 'warning' | 'error' => { if (percentage >= 75) return 'success'; if (percentage >= 50) return 'warning'; return 'error'; }; const sizeClasses = { sm: 'w-12 h-12 text-sm', md: 'w-20 h-20 text-2xl', lg: 'w-32 h-32 text-4xl', }; if (size === 'sm') { return (
{score}
/ {maxScore}
); } return (
{/* Circular Score Display */}
{score} /{maxScore}
Security
{/* Score Info */}

Security Score

{percentage}%
{/* Overall Progress Bar */} {showDetails && ( <> {/* Breakdown Section */} {Object.keys(breakdown).length > 0 && (
{expandedBreakdown && (
{Object.entries(breakdown).map(([category, categoryScore]) => { const categoryMax = getCategoryMax(category); const categoryPercent = Math.round((categoryScore / categoryMax) * 100); return (
{CATEGORY_LABELS[category] || category} {categoryScore}/{categoryMax}
= 70 ? 'success' : categoryPercent >= 40 ? 'warning' : 'error'} />
); })}
)}
)} {/* Suggestions Section */} {suggestions.length > 0 && (
{expandedSuggestions && (
    {suggestions.map((suggestion, index) => (
  • {suggestion}
  • ))}
)}
)} )}
); } // Helper function to determine max score for each category function getCategoryMax(category: string): number { const maxScores: Record = { hsts: 25, csp: 25, x_frame_options: 10, x_content_type_options: 10, referrer_policy: 10, permissions_policy: 10, cross_origin: 10, }; return maxScores[category] || 10; }