add badge + pagination components

This commit is contained in:
2025-04-19 14:04:38 +00:00
parent f8e498557e
commit 46e9b4adca
4 changed files with 151 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
interface LoadingSpinnerProps {
size?: 'sm' | 'md' | 'lg';
}
const sizeClasses = {
sm: 'w-4 h-4',
md: 'w-8 h-8',
lg: 'w-12 h-12',
};
export function LoadingSpinner({ size = 'md' }: LoadingSpinnerProps) {
return (
<div className="flex items-center justify-center">
<div
className={`${sizeClasses[size]} border-2 border-gray-200 border-t-indigo-600 rounded-full animate-spin`}
/>
</div>
);
}

View File

@@ -0,0 +1,47 @@
interface PaginationProps {
page: number;
pages: number;
total: number;
pageSize: number;
onPageChange: (page: number) => void;
}
export function Pagination({
page,
pages,
total,
pageSize,
onPageChange,
}: PaginationProps) {
const start = (page - 1) * pageSize + 1;
const end = Math.min(page * pageSize, total);
return (
<div className="flex items-center justify-between px-4 py-3 bg-white border-t border-gray-200">
<div className="text-sm text-gray-700">
Showing <span className="font-medium">{start}</span> to{' '}
<span className="font-medium">{end}</span> of{' '}
<span className="font-medium">{total}</span> results
</div>
<div className="flex items-center gap-2">
<button
onClick={() => onPageChange(page - 1)}
disabled={page <= 1}
className="px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
>
Previous
</button>
<span className="text-sm text-gray-700">
Page {page} of {pages}
</span>
<button
onClick={() => onPageChange(page + 1)}
disabled={page >= pages}
className="px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
>
Next
</button>
</div>
</div>
);
}

View File

@@ -0,0 +1,32 @@
import type { Severity } from '../../types/api';
interface SeverityBadgeProps {
severity: Severity;
showLabel?: boolean;
}
const severityConfig: Record<
Severity,
{ bg: string; text: string; label: string }
> = {
critical: { bg: 'bg-red-100', text: 'text-red-800', label: 'Critical' },
high: { bg: 'bg-orange-100', text: 'text-orange-800', label: 'High' },
medium: { bg: 'bg-yellow-100', text: 'text-yellow-800', label: 'Medium' },
low: { bg: 'bg-blue-100', text: 'text-blue-800', label: 'Low' },
info: { bg: 'bg-gray-100', text: 'text-gray-800', label: 'Info' },
};
export function SeverityBadge({
severity,
showLabel = true,
}: SeverityBadgeProps) {
const config = severityConfig[severity];
return (
<span
className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${config.bg} ${config.text}`}
>
{showLabel && config.label}
</span>
);
}

View File

@@ -0,0 +1,53 @@
import type { Verdict } from '../../types/api';
interface VerdictBadgeProps {
verdict: Verdict | null;
confidence?: number | null;
size?: 'sm' | 'md';
}
const verdictConfig: Record<Verdict, { bg: string; text: string; icon: string; label: string }> = {
approve: {
bg: 'bg-green-100',
text: 'text-green-800',
icon: '✓',
label: 'Approved',
},
request_changes: {
bg: 'bg-red-100',
text: 'text-red-800',
icon: '✗',
label: 'Changes Requested',
},
comment: {
bg: 'bg-blue-100',
text: 'text-blue-800',
icon: '💬',
label: 'Comment',
},
};
export function VerdictBadge({ verdict, confidence, size = 'md' }: VerdictBadgeProps) {
if (!verdict) {
return (
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-600">
Pending
</span>
);
}
const config = verdictConfig[verdict];
const sizeClasses = size === 'sm' ? 'px-2 py-0.5 text-xs' : 'px-3 py-1 text-sm';
return (
<span
className={`inline-flex items-center gap-1.5 rounded font-medium ${config.bg} ${config.text} ${sizeClasses}`}
>
<span>{config.icon}</span>
<span>{config.label}</span>
{confidence !== undefined && confidence !== null && (
<span className="opacity-75">({Math.round(confidence * 100)}%)</span>
)}
</span>
);
}