SolveForce UI drop-in (React/Next.js)

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.