add badge + pagination components
This commit is contained in:
19
dashboard/src/components/common/LoadingSpinner.tsx
Normal file
19
dashboard/src/components/common/LoadingSpinner.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
47
dashboard/src/components/common/Pagination.tsx
Normal file
47
dashboard/src/components/common/Pagination.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
32
dashboard/src/components/common/SeverityBadge.tsx
Normal file
32
dashboard/src/components/common/SeverityBadge.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
53
dashboard/src/components/deliberation/VerdictBadge.tsx
Normal file
53
dashboard/src/components/deliberation/VerdictBadge.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user