MuSProt / frontend /src /components /datasets /protein /FunctionPanel.tsx
wenruifan's picture
Deploy MuSProt React and FastAPI application
3993320
Raw
History Blame Contribute Delete
2.57 kB
import React, { useEffect, useState } from 'react';
import { proteinApi } from '../../../api/protein';
interface FunctionPanelProps {
pdbId: string;
chainId: string;
}
const FunctionPanel: React.FC<FunctionPanelProps> = ({ pdbId, chainId }) => {
const [functions, setFunctions] = useState<string[] | null>(null);
const [loading, setLoading] = useState(true);
const [notFound, setNotFound] = useState(false);
useEffect(() => {
let cancelled = false;
setLoading(true);
setNotFound(false);
setFunctions(null);
proteinApi.getChainFunctions(pdbId, chainId)
.then(fns => { if (!cancelled) setFunctions(fns); })
.catch(() => { if (!cancelled) setNotFound(true); })
.finally(() => { if (!cancelled) setLoading(false); });
return () => { cancelled = true; };
}, [pdbId, chainId]);
return (
<div className="border border-gray-200 rounded-2xl bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
{/* Header */}
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-100">
<h3 className="text-xl font-semibold text-slate-900">Ranked Functions</h3>
{functions && (
<span className="text-sm text-slate-500">
{functions.length} annotation{functions.length !== 1 ? 's' : ''}
</span>
)}
</div>
{/* Scrollable body */}
<div className="px-6 py-4" style={{ overflowY: 'auto', overflowX: 'hidden', maxHeight: '14rem' }}>
{loading && (
<p className="text-sm text-slate-500 text-center py-4">Loading functions…</p>
)}
{!loading && (notFound || (functions && functions.length === 0)) && (
<p className="text-sm text-slate-500 text-center py-4">No functional annotation available</p>
)}
{!loading && functions && functions.length > 0 && (
<ol className="space-y-3">
{functions.map((fn, idx) => (
<li key={idx} className="flex gap-3 text-sm text-slate-700" style={{ minWidth: 0 }}>
<span className="shrink-0 mt-0.5 w-5 h-5 rounded-full bg-slate-100 text-slate-500 text-[10px] font-semibold flex items-center justify-center">
{idx + 1}
</span>
<span style={{ flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{fn}</span>
</li>
))}
</ol>
)}
</div>
</div>
);
};
export default FunctionPanel;