Overview
import React, { useEffect, useMemo, useState } from "react";
// Minimal styling — replace with your design system
const cell = "px-3 py-2 border-b";
const th = "px-3 py-2 border-b font-semibold text-left";
const inputClass = "border rounded px-2 py-1";
type Row = {
element_z: number;
element_symbol: string;
known_isotopes: number;
stable_isotopes_strict: number;
unstable_isotopes: number;
predicted_isotopes_est: number;
gap_pred_minus_known: number;
};
export default function IsotopeMasterTable() {
const [rows, setRows] = useState<Row[]>([]);
const [q, setQ] = useState("");
const [sortKey, setSortKey] = useState<keyof Row>("gap_pred_minus_known");
const [asc, setAsc] = useState(false);
useEffect(() => {
// Option A: API
fetch("/api/isotopes")
.then(r => r.json())
.then(setRows)
.catch(console.error);
// Option B: load CSV directly (uncomment, remove Option A).
// fetch("/files/isotope_master_table_known_vs_predicted_with_gap.csv")
// .then(r => r.text())
// .then(text => csvToRows(text))
// .then(setRows)
// .catch(console.error);
}, []);
const filtered = useMemo(() => {
const term = q.trim().toLowerCase();
const base = term
? rows.filter(r =>
r.element_symbol.toLowerCase().includes(term) ||
String(r.element_z).includes(term)
)
: rows.slice();
const sorted = base.sort((a, b) => {
const A = a[sortKey], B = b[sortKey];
if (A < B) return asc ? -1 : 1;
if (A > B) return asc ? 1 : -1;
return 0;
});
return sorted;
}, [rows, q, sortKey, asc]);
const totals = useMemo(() => {
const reduce = (k: keyof Row) => filtered.reduce((s, r) => s + Number(r[k] || 0), 0);
return {
elements: filtered.length,
known: reduce("known_isotopes"),
stable: reduce("stable_isotopes_strict"),
unstable: reduce("unstable_isotopes"),
predicted: reduce("predicted_isotopes_est"),
gap: reduce("gap_pred_minus_known"),
};
}, [filtered]);
const headers: { key: keyof Row; label: string }[] = [
{ key: "element_z", label: "Z" },
{ key: "element_symbol", label: "Element" },
{ key: "known_isotopes", label: "Known" },
{ key: "stable_isotopes_strict", label: "Stable (strict)" },
{ key: "unstable_isotopes", label: "Unstable" },
{ key: "predicted_isotopes_est", label: "Predicted" },
{ key: "gap_pred_minus_known", label: "Gap" },
];
return (
<div className="max-w-full">
<div className="flex gap-3 items-center mb-3">
<input
className={inputClass}
placeholder="Filter by Z or symbol (e.g. 26 or Fe)"
value={q}
onChange={e => setQ(e.target.value)}
/>
<select
className={inputClass}
value={String(sortKey)}
onChange={e => setSortKey(e.target.value as keyof Row)}
>
{headers.map(h => (
<option key={String(h.key)} value={String(h.key)}>{h.label}</option>
))}
</select>
<button className={inputClass} onClick={() => setAsc(a => !a)}>
Sort: {asc ? "Asc" : "Desc"}
</button>
<a className={inputClass} href="/files/isotope_master_table_known_vs_predicted_with_gap.csv" download>
Download CSV
</a>
<a className={inputClass} href="/files/isotope_master_table_known_vs_predicted_with_gap.md" download>
Download MD
</a>
</div>
<div className="mb-2 text-sm">
<b>Totals (filtered):</b> Elements {totals.elements} • Known {totals.known} • Stable {totals.stable} • Unstable {totals.unstable} • Predicted {totals.predicted} • Gap {totals.gap}
</div>
<div className="overflow-auto border rounded">
<table className="min-w-[900px] w-full text-sm">
<thead>
<tr>
{headers.map(h => (
<th key={String(h.key)} className={th}>{h.label}</th>
))}
</tr>
</thead>
<tbody>
{filtered.map(r => (
<tr key={r.element_z}>
<td className={cell}>{r.element_z}</td>
<td className={cell}>{r.element_symbol}</td>
<td className={cell}>{r.known_isotopes}</td>
<td className={cell}>{r.stable_isotopes_strict}</td>
<td className={cell}>{r.unstable_isotopes}</td>
<td className={cell}>{r.predicted_isotopes_est}</td>
<td className={cell}>{r.gap_pred_minus_known}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
// Optional CSV loader helper if you serve files instead of an API
function csvToRows(text: string): Row[] {
const [head, ...lines] = text.split(/\r?\n/).filter(Boolean);
const cols = head.split(",");
const idx = (name: string) => cols.indexOf(name);
return lines.map(line => {
const t = line.split(",");
const label = t[idx("Element (Z)")];
const symbol = label.split(" ")[0];
const z = Number(label.match(/\((\d+)\)/)?.[1] || 0);
return {
element_z: z,
element_symbol: symbol,
known_isotopes: Number(t[idx("Isotopes Known")]),
stable_isotopes_strict: Number(t[idx("Stable")]),
unstable_isotopes: Number(t[idx("Unstable")]),
predicted_isotopes_est: Number(t[idx("Predicted Isotopes (est.)")]),
gap_pred_minus_known: Number(t[idx("Gap (Predicted - Known)")]),
};
});
}
Key terms in plain language
Open a term for a concise explanation of language used on this page.
API
An application programming interface is a defined way for software systems to exchange data or request functions from one another.
Artificial Intelligence (AI)
Software designed to perform tasks involving prediction, classification, generation, reasoning, or decision support. Business use still requires clear data, governance, security, and human accountability.
Cloud Computing
Computing resources—such as applications, servers, storage, or databases—delivered from remote infrastructure and scaled as requirements change.
Cybersecurity
The practices and controls used to protect identities, devices, networks, applications, and data from unauthorized access, disruption, or manipulation.
Identity and Access Management (IAM)
The systems and policies that determine who a user is, what resources they may access, and how that access is authenticated and reviewed.
Bandwidth
The amount of data a connection can carry in a given time, usually measured in Mbps or Gbps. More bandwidth supports more users, devices, and simultaneous applications.