Files
whyrating-engine-legacy/web/components/taxonomy/ProfilesSection.tsx
2026-02-02 18:19:00 +00:00

214 lines
7.0 KiB
TypeScript

'use client';
import { Check, X, Minus } from 'lucide-react';
import { taxonomy } from '@/lib/taxonomy/data';
const PROFILE_COLORS: Record<string, { bg: string; border: string; text: string; badge: string }> = {
lite: {
bg: 'bg-green-500/10',
border: 'border-green-500/30',
text: 'text-green-400',
badge: 'bg-green-500',
},
core: {
bg: 'bg-blue-500/10',
border: 'border-blue-500/30',
text: 'text-blue-400',
badge: 'bg-blue-500',
},
standard: {
bg: 'bg-purple-500/10',
border: 'border-purple-500/30',
text: 'text-purple-400',
badge: 'bg-purple-500',
},
full: {
bg: 'bg-amber-500/10',
border: 'border-amber-500/30',
text: 'text-amber-400',
badge: 'bg-amber-500',
},
};
const COMPLEXITY_COLORS: Record<string, string> = {
Minimal: 'text-green-400',
Low: 'text-blue-400',
Medium: 'text-purple-400',
High: 'text-amber-400',
};
const ALL_FIELDS = [
'primary_code',
'secondary_codes',
'valence',
'intensity',
'specificity',
'actionability',
'temporal',
'evidence',
'comparative',
'causal_chain',
'linked_spans',
'confidence',
'annotator_notes',
];
const FIELD_LABELS: Record<string, string> = {
primary_code: 'Primary Code',
secondary_codes: 'Secondary Codes',
valence: 'Valence',
intensity: 'Intensity',
specificity: 'Specificity',
actionability: 'Actionability',
temporal: 'Temporal',
evidence: 'Evidence',
comparative: 'Comparative',
causal_chain: 'Causal Chain',
linked_spans: 'Linked Spans',
confidence: 'Confidence',
annotator_notes: 'Annotator Notes',
};
export default function ProfilesSection() {
const profiles = Object.entries(taxonomy.profiles);
const getFieldStatus = (profile: typeof taxonomy.profiles.lite, field: string) => {
if (profile.required_fields.includes(field)) return 'required';
if (profile.optional_fields.includes(field)) return 'optional';
if (profile.forbidden_fields.includes(field)) return 'forbidden';
return 'forbidden';
};
return (
<div className="p-6 space-y-6">
{/* Header */}
<div>
<h2 className="text-xl font-bold text-gray-100">Implementation Profiles</h2>
<p className="text-gray-400 mt-1">
4 profiles for different implementation complexity levels
</p>
</div>
{/* Profile Cards */}
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-4">
{profiles.map(([profileKey, profile]) => {
const colors = PROFILE_COLORS[profileKey];
return (
<div
key={profileKey}
className={`rounded-lg border ${colors.border} ${colors.bg} p-4`}
>
{/* Profile Header */}
<div className="flex items-center justify-between mb-2">
<h3 className={`font-semibold ${colors.text}`}>{profile.name}</h3>
<span className={`w-3 h-3 rounded-full ${colors.badge}`} />
</div>
{/* Stats */}
<div className="flex items-center gap-3 mb-3 text-sm">
<span className="text-gray-300">{profile.code_count} codes</span>
<span className="text-gray-600">|</span>
<span className={COMPLEXITY_COLORS[profile.complexity]}>
{profile.complexity}
</span>
</div>
<p className="text-sm text-gray-400 mb-4">{profile.use_case}</p>
{/* Code pattern */}
<div className="mb-4">
<span className="text-xs text-gray-500">Code Level:</span>
<span className="ml-2 font-mono text-sm text-gray-300">
{profile.code_type}
</span>
</div>
{/* Pattern */}
<div className="bg-gray-800/50 rounded p-2 mb-3">
<span className="text-xs text-gray-500">Pattern:</span>
<code className="block font-mono text-xs text-gray-300 mt-1">
{profile.primary_code_pattern}
</code>
</div>
{/* Secondary codes */}
<div className="text-xs text-gray-400">
{profile.secondary_codes_allowed ? (
<span>
Up to {profile.secondary_codes_max} secondary codes (tier{' '}
{profile.secondary_codes_tier})
</span>
) : (
<span className="text-gray-500">No secondary codes</span>
)}
</div>
</div>
);
})}
</div>
{/* Field Comparison Table */}
<div className="mt-8">
<h3 className="text-lg font-semibold text-gray-200 mb-4">Field Requirements by Profile</h3>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-gray-700">
<th className="text-left py-2 px-3 text-gray-400 font-medium">Field</th>
{profiles.map(([profileKey, profile]) => (
<th
key={profileKey}
className={`text-center py-2 px-3 ${PROFILE_COLORS[profileKey].text} font-medium`}
>
{profile.name}
</th>
))}
</tr>
</thead>
<tbody>
{ALL_FIELDS.map((field) => (
<tr key={field} className="border-b border-gray-800 hover:bg-gray-800/30">
<td className="py-2 px-3 text-gray-300">{FIELD_LABELS[field]}</td>
{profiles.map(([profileKey, profile]) => {
const status = getFieldStatus(profile, field);
return (
<td key={profileKey} className="py-2 px-3 text-center">
{status === 'required' && (
<Check className="w-4 h-4 text-green-400 mx-auto" />
)}
{status === 'optional' && (
<Minus className="w-4 h-4 text-yellow-400 mx-auto" />
)}
{status === 'forbidden' && (
<X className="w-4 h-4 text-gray-600 mx-auto" />
)}
</td>
);
})}
</tr>
))}
</tbody>
</table>
</div>
{/* Legend */}
<div className="flex items-center gap-6 mt-4 text-xs text-gray-400">
<div className="flex items-center gap-2">
<Check className="w-4 h-4 text-green-400" />
<span>Required</span>
</div>
<div className="flex items-center gap-2">
<Minus className="w-4 h-4 text-yellow-400" />
<span>Optional</span>
</div>
<div className="flex items-center gap-2">
<X className="w-4 h-4 text-gray-600" />
<span>Not used</span>
</div>
</div>
</div>
</div>
);
}