import React, { useState, useRef } from 'react';
import { THEME } from '../theme.js';
import { MediaConfBar, OxygenConfArc, IntervalBar, MonoTag, SourceBadge } from './Primitives.jsx';
import { tempColor, pHColor, saltColor } from '../theme.js';
const QUICK_TRY = [
{ label: 'Thermus thermophilus', value: 'Thermus thermophilus' },
{ label: 'GCF_000005845.2', value: 'GCF_000005845.2' },
{ label: 'Pyrococcus furiosus', value: 'Pyrococcus furiosus' },
];
export default function PredictBar({ result, setResult }) {
const [query, setQuery] = useState('');
const [busy, setBusy] = useState(false);
const [error, setError] = useState(null);
const [hits, setHits] = useState(null);
const fileRef = useRef(null);
function isAccession(s) {
const u = s.trim().toUpperCase();
return u.startsWith('GCA_') || u.startsWith('GCF_');
}
async function runPredict(target) {
setBusy(true);
setError(null);
setHits(null);
try {
const r = await fetch('/api/predict', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ target, top_k: 8 }),
});
if (!r.ok) {
const t = await r.text();
throw new Error(t || `HTTP ${r.status}`);
}
const data = await r.json();
setResult(data);
} catch (e) {
setError(String(e.message || e));
} finally {
setBusy(false);
}
}
async function onSubmit() {
const q = query.trim();
if (!q) return;
if (isAccession(q)) {
await runPredict(q);
return;
}
// Treat as organism name → NCBI search → user picks
setBusy(true);
setError(null);
try {
const r = await fetch(`/api/ncbi-search?q=${encodeURIComponent(q)}`);
const data = await r.json();
if (!data.hits?.length) {
setError(`No NCBI Assembly hits for "${q}".`);
} else if (data.hits.length === 1) {
await runPredict(data.hits[0].accession);
} else {
setHits(data.hits);
}
} catch (e) {
setError(String(e.message || e));
} finally {
setBusy(false);
}
}
async function onFile(file) {
if (!file) return;
const text = await file.text();
await runPredict(text);
}
return (
▶ Predict a medium
paste a name, NCBI accession, FASTA, or upload a genome
⌕
setQuery(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && onSubmit()}
placeholder='e.g. "Thermus thermophilus" GCF_000008125.1 >my_mag'
style={{ flex: 1, border: 'none', background: 'transparent', outline: 'none', font: `400 13px ${THEME.font}`, color: THEME.ink, height: 22 }}
/>
onFile(e.target.files?.[0])} />
try:
{QUICK_TRY.map((q) => (
))}
{error && (
{error}
)}
{hits && (
{hits.length} NCBI matches — pick one
{hits.map((h) => (
))}
)}
{result &&
setResult(null)} />}
);
}
function PredictResultBanner({ result, onClear }) {
const p = result.phenotypes || {};
const top = result.media?.[0];
const T = p.optimal_temperature_c || {};
const pH = p.optimal_ph || {};
const O2 = p.oxygen_requirement || {};
const salt = p.salt_tolerance_pct || {};
return (
Prediction · {result.accession} · {result.n_contigs} contigs · {result.n_cds} CDS · GC {(result.gc * 100).toFixed(1)}%
Try {top?.name}
{top && <> {top.medium_id}>}
{top?.recipe && (
{top.recipe}
)}
{T.prediction !== undefined && (
)}
{pH.prediction !== undefined && (
)}
{O2.prediction !== undefined && (
)}
{salt.prediction !== undefined && (
)}
);
}
function PhenoStat({ label, value, sub, color }) {
return (
);
}