| |
| |
| |
|
|
| import { ChainMetadata } from '../../../types/protein';
|
|
|
| interface ChainBasicInfoCardsProps {
|
| metadata: ChainMetadata | null;
|
| isLoading?: boolean;
|
| }
|
|
|
| const ChainBasicInfoCards: React.FC<ChainBasicInfoCardsProps> = ({ metadata, isLoading }) => {
|
| const getUniProtId = () => {
|
| if (!metadata?.polymer_entity?.reference_sequences) return 'β';
|
|
|
| const refSeqs = metadata.polymer_entity.reference_sequences;
|
| const uniprotRefs = refSeqs.filter((ref: any) => ref.database_name === 'UniProt');
|
|
|
| if (uniprotRefs.length > 0) {
|
| const first = uniprotRefs[0].database_accession;
|
| if (uniprotRefs.length > 1) {
|
| return (
|
| <span title={uniprotRefs.map((r: any) => r.database_accession).join(', ')}>
|
| {first} <span className="text-gray-500 text-xs">+{uniprotRefs.length - 1} more</span>
|
| </span>
|
| );
|
| }
|
| return first;
|
| }
|
|
|
|
|
| if (refSeqs.length > 0) {
|
| return refSeqs[0].database_accession || 'β';
|
| }
|
|
|
| return 'β';
|
| };
|
|
|
| if (isLoading) {
|
| return (
|
| <div className="border border-gray-200 rounded-2xl p-6 bg-white shadow-sm">
|
| <div className="flex items-center gap-3">
|
| <div className="w-8 h-8 border-4 border-slate-200 border-t-slate-900 rounded-full animate-spin"></div>
|
| <span className="text-slate-600 text-sm">Loading chain metadata...</span>
|
| </div>
|
| </div>
|
| );
|
| }
|
|
|
| const pdbId = metadata?.entry?.rcsb_id || metadata?.pdb_id;
|
| const chainId = metadata?.chain_id || metadata?.polymer_entity_instance?.auth_asym_id;
|
|
|
| return (
|
| <div className="h-full w-full border border-gray-200 rounded-2xl p-6 bg-white shadow-sm hover:border-slate-900 hover:shadow-lg hover:-translate-y-0.5 transition-all duration-300">
|
| <div className="flex items-start justify-between mb-4">
|
| <h3 className="text-xl font-semibold text-slate-900">Chain Metadata</h3>
|
| <div className="flex flex-wrap gap-2">
|
| <span className="px-3 py-1.5 bg-blue-50 text-blue-700 text-xs font-medium rounded-full border border-blue-200">
|
| {pdbId || 'N/A'}
|
| </span>
|
| {chainId && (
|
| <span className="px-3 py-1.5 bg-purple-50 text-purple-700 text-xs font-medium rounded-full border border-purple-200">
|
| Chain {chainId}
|
| </span>
|
| )}
|
| </div>
|
| </div>
|
|
|
| {/* Chips Row */}
|
|
|
| {/* Compact three-column metadata flow */}
|
| <div className="gap-4" style={{ columnCount: 3, columnGap: '1rem' }}>
|
| <div className="break-inside-avoid mb-3" style={{ breakInside: 'avoid' }}>
|
| <div className="text-xs font-medium text-slate-500 uppercase tracking-wider mb-1">Protein Name</div>
|
| <div className="text-sm font-semibold text-slate-900 font-mono" title={metadata?.entry?.title || ''}>
|
| {metadata?.entry?.title || 'β'}
|
| </div>
|
| </div>
|
|
|
| <div className="break-inside-avoid mb-3" style={{ breakInside: 'avoid' }}>
|
| <div className="text-xs font-medium text-slate-500 uppercase tracking-wider mb-1">PDB ID</div>
|
| <div className="text-sm font-semibold text-slate-900">
|
| <a
|
| href={`https://www.rcsb.org/structure/${pdbId}`}
|
| target="_blank"
|
| rel="noopener noreferrer"
|
| className="text-blue-600 hover:text-blue-800 font-mono hover:underline inline-flex items-center gap-1 transition-colors"
|
| >
|
| {pdbId || 'β'}
|
| <span className="text-xs">β</span>
|
| </a>
|
| </div>
|
| </div>
|
|
|
| <div className="break-inside-avoid mb-3" style={{ breakInside: 'avoid' }}>
|
| <div className="text-xs font-medium text-slate-500 uppercase tracking-wider mb-1">Chain ID</div>
|
| <div className="text-sm font-semibold text-slate-900 font-mono">{chainId || 'β'}</div>
|
| </div>
|
|
|
| <div className="break-inside-avoid mb-3" style={{ breakInside: 'avoid' }}>
|
| <div className="text-xs font-medium text-slate-500 uppercase tracking-wider mb-1">UniProt ID</div>
|
| <div className="text-sm font-semibold text-slate-900 font-mono">{getUniProtId()}</div>
|
| </div>
|
|
|
| <div className="break-inside-avoid mb-3" style={{ breakInside: 'avoid' }}>
|
| <div className="text-xs font-medium text-slate-500 uppercase tracking-wider mb-1">Binding Status</div>
|
| <div className="text-sm font-semibold text-slate-900 capitalize">{metadata?.binding_status || 'β'}</div>
|
| </div>
|
|
|
| <div className="break-inside-avoid mb-3" style={{ breakInside: 'avoid' }}>
|
| <div className="text-xs font-medium text-slate-500 uppercase tracking-wider mb-1">CATH ID</div>
|
| <div className="text-sm font-semibold text-slate-900 font-mono">{metadata?.cath_id || 'uncategorized'}</div>
|
| </div>
|
|
|
| <div className="break-inside-avoid mb-3" style={{ breakInside: 'avoid' }}>
|
| <div className="text-xs font-medium text-slate-500 uppercase tracking-wider mb-1">CATH Domain</div>
|
| <div className="text-sm font-semibold text-slate-900 font-mono">
|
| {metadata?.cath_superfamily ? (
|
| <a
|
| href={`https://www.cathdb.info/version/latest/superfamily/${metadata.cath_superfamily}`}
|
| target="_blank"
|
| rel="noopener noreferrer"
|
| className="text-blue-600 hover:text-blue-800 hover:underline inline-flex items-center gap-1 transition-colors"
|
| >
|
| {metadata.cath_superfamily}
|
| <span className="text-xs">β</span>
|
| </a>
|
| ) : 'β'}
|
| </div>
|
| </div>
|
|
|
| <div className="break-inside-avoid mb-3" style={{ breakInside: 'avoid' }}>
|
| <div className="text-xs font-medium text-slate-500 uppercase tracking-wider mb-1">State ID</div>
|
| <div className="text-sm font-semibold text-slate-900 font-mono">{metadata?.state_id || 'β'}</div>
|
| </div>
|
| </div>
|
| </div>
|
| );
|
| };
|
|
|
| export default ChainBasicInfoCards;
|
|
|