// --- Components ---
function Header() {
return (
)
}
function ModelCard({ model, loading }) {
if (loading) return
Henter model…
;
if (!model) return Ingen aktiv model
;
const created = new Date(model.created_time_utc).toLocaleString(["da-DK"]);
return (
{model.title}
{model.description}
- Dato for oprettelse
- {created}
- Excelfil
- {model.data_filename}
);
}
function Layout({ children }) {
return (
{children}
);
}
// --- Pages ---
function AdminModelCard({ model, isActive }) {
if (!model) return null;
const [error, setError] = React.useState(null);
const [busy, setBusy] = React.useState(false);
const created = new Date(model.created_time_utc).toLocaleString(["da-DK"]);
async function handleActivate() {
setBusy(true);
setError(null);
try {
await api.updateModel(model.model_id, true);
location.reload();
} catch (err) {
setError(err.message);
setBusy(false);
}
}
async function handleDelete() {
if (!confirm(`Slet "${model.title}"?`)) return;
setBusy(true);
setError(null);
try {
await api.deleteModel(model.model_id);
location.reload();
} catch (err) {
setError(err.message);
setBusy(false);
}
}
return (
{model.title}
{isActive && AKTIV}
{model.description}
- Dato for oprettelse
- {created}
- Excelfil
- {model.data_filename}
- Title
- {model.title}
- Beskrivelse
- {model.description}
- Modelinfo
- {model.message}
- ID
- {model.model_id}
{!isActive && (
<>
>
)
}
{error &&
{error}
}
);
}
const OTHERS_PAGE_SIZE = 3;
function AdminPage() {
const [models, setModels] = React.useState([]);
const [activeId, setActiveId] = React.useState(null);
const [uploadError, setUploadError] = React.useState(null);
const [uploading, setUploading] = React.useState(false);
const [othersPage, setOthersPage] = React.useState(0);
function loadModels() {
return api.listModels().then(setModels).catch(console.error);
}
React.useEffect(() => {
loadModels();
api.getActiveModel().then(m => m && setActiveId(m.model_id)).catch(() => {});
}, []);
async function handleUpload(e) {
e.preventDefault();
const form = new FormData(e.target);
const file = form.get("file");
const title = form.get("title");
const description = form.get("description") || null;
setUploading(true);
setUploadError(null);
try {
await api.createModel(file, title, description);
location.reload();
} catch (err) {
setUploadError(err.message);
setUploading(false);
}
}
return (
Admin
Modeller
{models.length === 0
? Ingen modeller fundet.
: (() => {
const active = models.find(m => m.model_id === activeId);
const others = models.filter(m => m.model_id !== activeId);
return (
<>
{active
?
: Ingen aktiv model valgt.
}
{others.length > 0 && (() => {
const pageCount = Math.ceil(others.length / OTHERS_PAGE_SIZE);
const page = Math.min(othersPage, pageCount - 1);
const pageItems = others.slice(page * OTHERS_PAGE_SIZE, (page + 1) * OTHERS_PAGE_SIZE);
return (
Øvrige modeller ({others.length})
{pageItems.map(m =>
)}
{pageCount > 1 && (
Side {page + 1} af {pageCount}
)}
);
})()}
>
);
})()
}
Upload ny model
);
}
function DashboardPage() {
const [result, setResult] = React.useState(null);
const [error, setError] = React.useState(null);
const [active, setActive] = React.useState(null);
const [loadingActive, setLoadingActive] = React.useState(true);
const [treatment1, setTreatment1] = React.useState("");
const [treatment2, setTreatment2] = React.useState("null");
async function fetchActive() {
try {
const response = await api.getActiveModel();
setActive(response);
const primCats = response?.primary_categories || [];
const secCats = response?.secondary_categories || [];
if (primCats.length > 0) {
const pacli = primCats.find(c => c.code === "Paclitaxel");
setTreatment1((pacli || primCats[0]).code);
}
if (secCats.length > 0) {
const ec = secCats.find(c => c.code === "EC_Epirubicin_Cyclofosfamide");
setTreatment2((ec || secCats[0]).code);
}
} catch (e) {
setActive(null);
} finally {
setLoadingActive(false);
}
}
React.useEffect(() => {
fetchActive()
}, []);
async function handleSubmit(e) {
e.preventDefault();
const form = new FormData(e.target);
const body = {
treatment1: form.get("treatment1"),
treatment2: form.get("treatment2"),
thick_hair: form.get("thick_hair")
};
try {
const res = await api.predict(active.model_id, body);
console.log(res)
setResult(res);
setError(null);
} catch (err) {
setError(err.message);
setResult(null);
}
}
const primaryCategories = active?.primary_categories || [];
const secondaryCategories = active?.secondary_categories || [];
return (
Dashboard
Aktive model
Estimer succesrate
{result && Sandsynlighed for at beholde mere end 50% af håret: {100 * result.p.toFixed(2)}%
}
{result && For denne kombination af behandlinger er {result.n_obs} observationer i data, hvor af {result.n_success} er en succes.
}
{result && Bemærk: Resultatet er et estimat og behæftet med usikkerheder.
}
{error && {error}
}
);
}
function ModelPage() {
return
Model
}
function LoginPage() {
return (
)
}
// --- Mount ---
const PAGES = {
"Dashboard": DashboardPage,
"Admin": AdminPage,
"Model": ModelPage,
"Login": LoginPage
};
const root = document.getElementById('root');
const Page = PAGES[root.dataset.page];
ReactDOM.createRoot(root).render();