| |
| |
| |
|
|
| import React, { useState } from 'react';
|
| import { ChainMetadata } from '../../../types/protein';
|
| import { formatText, formatPH, formatTemperature, truncateText } from '../../../utils/format';
|
|
|
| interface ExperimentalConditionsPanelProps {
|
| metadata: ChainMetadata | null;
|
| isLoading?: boolean;
|
| }
|
|
|
| const ExperimentalConditionsPanel: React.FC<ExperimentalConditionsPanelProps> = ({ metadata, isLoading }) => {
|
| const [showFullDetails, setShowFullDetails] = useState(false);
|
|
|
| if (isLoading) {
|
| return (
|
| <section className="space-y-4">
|
| <h3 className="text-xl font-semibold text-slate-900">Experimental Conditions</h3>
|
| <div className="text-center py-10 text-slate-600">Loading conditions...</div>
|
| </section>
|
| );
|
| }
|
|
|
| if (!metadata) {
|
| return (
|
| <section className="space-y-4">
|
| <h3 className="text-xl font-semibold text-slate-900">Experimental Conditions</h3>
|
| <div className="text-center py-10 text-slate-600">No experimental data available</div>
|
| </section>
|
| );
|
| }
|
|
|
|
|
| const pdbId = metadata.pdb_id || metadata.entry?.rcsb_id;
|
| const website = pdbId ? `https://www.rcsb.org/structure/${String(pdbId).toUpperCase()}` : null;
|
| const exptlMethod = metadata.entry?.exptl_method;
|
| const pH = metadata.entry?.crystal_grow?.pH;
|
| const temp = metadata.entry?.crystal_grow?.temp;
|
| const details = metadata.entry?.crystal_grow?.pdbx_details;
|
|
|
|
|
| const formattedMethod = formatText(exptlMethod);
|
| const formattedPH = formatPH(pH);
|
| const formattedTemp = formatTemperature(temp);
|
| const formattedDetails = formatText(details, '');
|
|
|
| const detailsPreview = formattedDetails ? truncateText(formattedDetails, 120) : '';
|
| const hasLongDetails = formattedDetails && formattedDetails.length > 120;
|
|
|
| return (
|
| <section className="space-y-4">
|
| {/* Title + Method Chip */}
|
| <div className="flex items-start justify-between">
|
| <h3 className="text-xl font-semibold text-slate-900">Experimental Conditions</h3>
|
| {formattedMethod !== '—' && (
|
| <span className="px-3 py-1.5 bg-green-50 text-green-700 text-xs font-medium rounded-full border border-green-200">
|
| {formattedMethod}
|
| </span>
|
| )}
|
| </div>
|
|
|
| {/* Definition List */}
|
| <dl className="space-y-3 pt-2">
|
| {website && (
|
| <div className="flex justify-between items-center py-2 border-b border-slate-100">
|
| <dt className="text-xs font-medium text-slate-500 uppercase tracking-wider">Website</dt>
|
| <dd>
|
| <a href={website} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1 text-sm text-blue-600 hover:text-blue-800 font-medium hover:underline transition-colors">
|
| RCSB PDB <span className="text-xs">↗</span>
|
| </a>
|
| </dd>
|
| </div>
|
| )}
|
|
|
| <div className="flex justify-between items-center py-2 border-b border-slate-100">
|
| <dt className="text-xs font-medium text-slate-500 uppercase tracking-wider">Method</dt>
|
| <dd className="text-sm font-semibold text-slate-900">{formattedMethod}</dd>
|
| </div>
|
|
|
| <div className="flex justify-between items-center py-2 border-b border-slate-100">
|
| <dt className="text-xs font-medium text-slate-500 uppercase tracking-wider">pH</dt>
|
| <dd className="text-sm font-semibold text-slate-900">{formattedPH}</dd>
|
| </div>
|
|
|
| <div className="flex justify-between items-center py-2 border-b border-slate-100">
|
| <dt className="text-xs font-medium text-slate-500 uppercase tracking-wider">Temperature</dt>
|
| <dd className="text-sm font-semibold text-slate-900">{formattedTemp}</dd>
|
| </div>
|
|
|
| <div className="py-2 border-b border-slate-100">
|
| {formattedDetails && (
|
| <div className="experimental-item experimental-details min-w-0">
|
| <dt className="text-xs font-medium text-slate-500 uppercase tracking-wider">Details</dt>
|
| <dd className="text-sm font-semibold text-slate-900">
|
| <div className="details-content wrap-anywhere mt-1">
|
| {showFullDetails ? formattedDetails : detailsPreview}
|
| </div>
|
| {hasLongDetails && (
|
| <button
|
| className="details-toggle"
|
| onClick={() => setShowFullDetails(!showFullDetails)}
|
| >
|
| {showFullDetails ? '▲ Show less' : '▼ Show more'}
|
| </button>
|
| )}
|
| </dd>
|
| </div>
|
| )}
|
| </div>
|
| </dl>
|
| </section>
|
| );
|
| };
|
|
|
| export default ExperimentalConditionsPanel;
|
|
|