website (17 pages + 3 components): - multiplayer/wizard, middleware/wizard+test-wizard, communication - builds/wizard, staff-search, voice, sbom/wizard - foerderantrag, mail/tasks, tools/communication, sbom - compliance/evidence, uni-crawler, brandbook (already done) - CollectionsTab, IngestionTab, RiskHeatmap backend-lehrer (5 files): - letters_api (641 → 2), certificates_api (636 → 2) - alerts_agent/db/models (636 → 3) - llm_gateway/communication_service (614 → 2) - game/database already done in prior batch klausur-service (2 files): - hybrid_vocab_extractor (664 → 2) - klausur-service/frontend: api.ts (620 → 3), EHUploadWizard (591 → 2) voice-service (3 files): - bqas/rag_judge (618 → 3), runner (529 → 2) - enhanced_task_orchestrator (519 → 2) studio-v2 (6 files): - korrektur/[klausurId] (578 → 4), fairness (569 → 2) - AlertsWizard (552 → 2), OnboardingWizard (513 → 2) - korrektur/api.ts (506 → 3), geo-lernwelt (501 → 2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
135 lines
5.3 KiB
TypeScript
135 lines
5.3 KiB
TypeScript
import { StaffMember, Publication } from './types'
|
|
|
|
export function StaffDetailPanel({
|
|
selectedStaff,
|
|
publications,
|
|
}: {
|
|
selectedStaff: StaffMember | null
|
|
publications: Publication[]
|
|
}) {
|
|
if (!selectedStaff) {
|
|
return (
|
|
<div className="p-8 text-center text-slate-400">
|
|
Wahlen Sie eine Person aus der Liste
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{/* Header */}
|
|
<div className="p-4 border-b">
|
|
<div className="flex items-start gap-4">
|
|
{selectedStaff.photo_url ? (
|
|
<img
|
|
src={selectedStaff.photo_url}
|
|
alt={selectedStaff.full_name || selectedStaff.last_name}
|
|
className="w-16 h-16 rounded-full object-cover"
|
|
/>
|
|
) : (
|
|
<div className="w-16 h-16 rounded-full bg-slate-200 flex items-center justify-center text-slate-500 text-xl font-medium">
|
|
{(selectedStaff.first_name?.[0] || '') + (selectedStaff.last_name?.[0] || '')}
|
|
</div>
|
|
)}
|
|
<div>
|
|
<h3 className="font-semibold text-lg">
|
|
{selectedStaff.title && `${selectedStaff.title} `}
|
|
{selectedStaff.full_name || `${selectedStaff.first_name || ''} ${selectedStaff.last_name}`}
|
|
</h3>
|
|
<p className="text-sm text-slate-500">{selectedStaff.position}</p>
|
|
<p className="text-sm text-slate-400">{selectedStaff.university_name}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Contact */}
|
|
<div className="p-4 border-b space-y-2">
|
|
{selectedStaff.email && (
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<svg className="w-4 h-4 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
</svg>
|
|
<a href={`mailto:${selectedStaff.email}`} className="text-primary-600 hover:underline">
|
|
{selectedStaff.email}
|
|
</a>
|
|
</div>
|
|
)}
|
|
{selectedStaff.profile_url && (
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<svg className="w-4 h-4 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
|
|
</svg>
|
|
<a href={selectedStaff.profile_url} target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:underline truncate">
|
|
Profil
|
|
</a>
|
|
</div>
|
|
)}
|
|
{selectedStaff.orcid && (
|
|
<div className="flex items-center gap-2 text-sm">
|
|
<span className="w-4 h-4 text-green-600 font-bold text-xs">ID</span>
|
|
<a href={`https://orcid.org/${selectedStaff.orcid}`} target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:underline">
|
|
ORCID: {selectedStaff.orcid}
|
|
</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Research Interests */}
|
|
{selectedStaff.research_interests && selectedStaff.research_interests.length > 0 && (
|
|
<div className="p-4 border-b">
|
|
<h4 className="text-sm font-medium text-slate-700 mb-2">Forschungsgebiete</h4>
|
|
<div className="flex flex-wrap gap-1">
|
|
{selectedStaff.research_interests.map((interest, i) => (
|
|
<span key={i} className="px-2 py-0.5 text-xs bg-blue-50 text-blue-700 rounded-full">
|
|
{interest}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Publications */}
|
|
<div className="p-4">
|
|
<h4 className="text-sm font-medium text-slate-700 mb-2">
|
|
Publikationen ({publications.length})
|
|
</h4>
|
|
{publications.length > 0 ? (
|
|
<div className="space-y-3 max-h-64 overflow-y-auto">
|
|
{publications.map((pub) => (
|
|
<div key={pub.id} className="text-sm">
|
|
<p className="font-medium text-slate-800 line-clamp-2">{pub.title}</p>
|
|
<div className="flex items-center gap-2 text-xs text-slate-500 mt-1">
|
|
{pub.year && <span>{pub.year}</span>}
|
|
{pub.venue && <span>| {pub.venue}</span>}
|
|
{pub.citation_count > 0 && (
|
|
<span className="text-green-600">{pub.citation_count} Zitierungen</span>
|
|
)}
|
|
</div>
|
|
{pub.doi && (
|
|
<a
|
|
href={`https://doi.org/${pub.doi}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-xs text-primary-600 hover:underline"
|
|
>
|
|
DOI
|
|
</a>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-slate-400">Keine Publikationen gefunden</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
<div className="p-4 border-t bg-slate-50">
|
|
<button className="w-full px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 text-sm">
|
|
Als Kunde markieren
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|