// --- Components --- function Header() { return (
Dashboard Admin
) } 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

{uploadError &&

{uploadError}

}
); } 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 (
Rapunzel
) } // --- 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();