import http from "node:http";
import { readFile } from "node:fs/promises";
import { extname } from "node:path";
const PORT = Number(process.env.PORT || 5173);
const API_BASE =
process.env.DOCLINK_API_BASE ||
"https://devbandungraya.aplikasi.web.id/one-api-doctor/doctor_mitra";
const sessionKey = "doclink_session";
const sampleLogin = {
username: "yogayogi",
doctorId: "31010002",
mouId: "2773",
password: "123456",
};
function escapeHtml(value) {
return String(value)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """);
}
function statusClass(status) {
const mapping = {
Processing: "warning",
Ready: "success",
"Needs review": "danger",
Released: "success",
Pending: "warning",
Review: "danger",
};
return mapping[status] || "neutral";
}
function statusBadge(text) {
return `${escapeHtml(text)} `;
}
function emptyState(title, text, action = "") {
return `
${escapeHtml(title)}
${escapeHtml(text)}
${action}
`;
}
function icon(name) {
const map = {
search:
' ',
plus:
' ',
bell:
' ',
arrow:
' ',
login:
' ',
};
return map[name] || "";
}
function layout(title, body, { authenticated = false, activePath = "/", subtitle = "", shell = true } = {}) {
const nav = authenticated ? desktopNav(activePath) : "";
const mobile = authenticated ? mobileNav(activePath) : "";
const header = authenticated ? topbar(activePath, subtitle) : "";
return `
${escapeHtml(title)}
${shell && authenticated ? `
${nav}
${header}
${body}
${mobile}
` : body}
`;
}
function topbar(activePath, subtitle) {
const titleMap = {
"/": ["Dashboard", "Overview of orders, results, and work queues."],
"/orders": ["Orders", "Search, review, and create patient orders."],
"/results": ["Results", "Monitor released and pending laboratory results."],
"/fpp": ["FPP", "Browse grouped reference packages and filters."],
"/patients": ["Patients", "Landing for patient registration and lookup."],
"/settings": ["Settings", "Account profile and password management."],
};
const [title, fallbackSubtitle] = titleMap[activePath] || ["DocLink Web", "Responsive clinical workflow shell."];
const searchForm = activePath === "/orders"
? `
`
: activePath === "/results"
? `
`
: `
Search orders
`;
return `
`;
}
function desktopNav(activePath) {
const items = [
["/", "Home", "Dashboard"],
["/orders", "Order", "Create & list"],
["/results", "Result", "History"],
["/fpp", "FPP", "Catalog"],
["/settings", "Akun", "Profile"],
];
const active = (href) => activePath === href || activePath.startsWith(`${href}/`);
return `
`;
}
function mobileNav(activePath) {
const items = [
["/", "Home", "Dashboard"],
["/orders", "Order", "Create"],
["/results", "Result", "History"],
["/fpp", "FPP", "Catalog"],
["/settings", "Akun", "Profile"],
];
const active = (href) => activePath === href || activePath.startsWith(`${href}/`);
return `
`;
}
function panelHeader(title, text, action = "") {
return `
`;
}
function resolveFppRouteIds(session) {
return {
doctorId: String(session?.doctorId || sampleLogin.doctorId || "1"),
mouId: String(session?.mouId || sampleLogin.mouId || "1"),
};
}
async function dashboardPage(session) {
const [orders, results] = await Promise.all([
loadOrders(session, "", "All"),
loadResults(session, ""),
]);
const stats = [
{ label: "Orders today", value: String(orders.length), trend: "Live", hint: "" },
{ label: "Results pending", value: String(results.filter((item) => item.status !== "Released").length), trend: "Live", hint: "" },
];
return `
${stats
.map(
(item) => `
${escapeHtml(item.label)}
${escapeHtml(item.trend)}
${escapeHtml(item.value)}
${item.hint ? `${escapeHtml(item.hint)} ` : ""}
`,
)
.join("")}
${panelHeader("Recent orders", "A compact snapshot of the latest patient orders and their state.", '
View all ')}
Patient Order Status Updated
${orders.length
? orders
.slice(0, 5)
.map(
(order) => `
${escapeHtml(order.patient || "Unknown patient")} ${escapeHtml(order.mode || "-")}
${escapeHtml(order.id)} ${escapeHtml(order.diagnosis || "-")}
${statusBadge(order.status)}
${escapeHtml(order.updated || "-")}
`,
)
.join("")
: `${emptyState("No orders returned", "The order endpoint did not return any rows for this session.")} `}
`;
}
function renderOrdersTable(orders, selectedOrderId, filter = "All") {
const selected = orders.find((item) => item.id === selectedOrderId) || orders[0] || null;
return `
${panelHeader("Search orders", "Use the filter to match the old app flow without giving up desktop readability.", 'Create order ')}
${
orders.length
? `
Patient Order ID Doctor Status Updated
${orders
.map(
(order) => `
${escapeHtml(order.patient || "Unknown patient")} ${escapeHtml(order.mode || "-")}
${escapeHtml(order.id)}
${escapeHtml(order.doctor || "-")}
${statusBadge(order.status)}
${escapeHtml(order.updated || "-")}
`,
)
.join("")}
${orders
.map(
(order) => `
${escapeHtml(order.patient || "Unknown patient")}
${escapeHtml(order.id || "-")} · ${escapeHtml(order.mode || "-")}
${statusBadge(order.status)}
${escapeHtml(order.doctor || "-")}
${escapeHtml(order.updated || "-")}
`,
)
.join("")}
`
: emptyState("No orders returned", "The order endpoint did not return any rows for this filter.")
}
${panelHeader("Selected order", "Master-detail layout keeps context visible while reviewing the list.", selected ? `Open detail ` : "")}
${
selected
? `
${escapeHtml(selected.patient || "Unknown patient")}
${escapeHtml(selected.id || "-")} · ${escapeHtml(selected.updated || "-")}
${statusBadge(selected.status)}
Doctor ${escapeHtml(selected.doctor || "-")}
${escapeHtml(selected.orderDate || selected.updated || "-")}
NIK ${escapeHtml(selected.orderNik || selected.diagnosis || "-")}
Phone ${escapeHtml(selected.orderHp || "-")}
Address ${escapeHtml(selected.orderAddress || selected.message || "-")}
`
: emptyState("No selected order", "The API returned no rows for this search.")
}
`;
}
function renderOrderDetail(order) {
return `
${panelHeader(
`${order.patient} · ${order.id}`,
"Order detail with the same structure as the proposed master-detail workflow.",
`Pesan khusus `,
)}
Status ${statusBadge(order.status)}
Patient ${escapeHtml(order.patient || "-")} ${escapeHtml(order.updated || "-")}
Doctor ID ${escapeHtml(order.doctor || "-")} Pramita Bandungraya
${panelHeader("Order details", "The source API returns identity fields, so the card copies stay aligned with the payload.")}
Order date ${escapeHtml(order.orderDate || order.updated || "-")}
NIK ${escapeHtml(order.orderNik || "-")}
Phone ${escapeHtml(order.orderHp || "-")}
Address ${escapeHtml(order.orderAddress || "-")}
${panelHeader("Clinical note", "The payload doesn't expose test bundle details, so this view stays identity-focused.")}
${escapeHtml(order.orderAddress || "-")}
${order.apiSaran ? `
${escapeHtml(typeof order.apiSaran === "string" ? order.apiSaran : JSON.stringify(order.apiSaran))}
` : ""}
Order ID ${escapeHtml(order.id || "-")}
Updated ${escapeHtml(order.updated || "-")}
`;
}
function renderOrderForm(step, stepKey = "demografi", fppTests = [], mouId = "") {
const fppGroups = groupFppTestsForSelection(fppTests);
const steps = [
["demografi", "Demografi", "Patient identity and contact details."],
["diagnosa", "Diagnosa", "Clinical indication and diagnosis."],
["pemeriksaan", "Pemeriksaan", "Choose examination groups."],
["qrcode", "QR Code", "Fast entry for existing records."],
["review", "Review", "Check before submit."],
];
const activeIndex = Math.max(0, steps.findIndex((item) => item[0] === stepKey));
const stepBody = {
demografi: `
${panelHeader("Mandatory", "These fields are required or expected by the backend before save.")}
`,
diagnosa: `
${panelHeader("Optional", "Notes are useful for the lab or front office.")}
Patient note
`,
pemeriksaan: `
${panelHeader("Mandatory", "Select examinations from the FPP catalog. Only doctortest=true rows are interactive.")}
Selected tests
0 selected
${
fppGroups.length
? `
${fppGroups
.map(
(group) => `
${escapeHtml(group.heading)}
${group.sections.reduce((sum, section) => sum + section.tests.length, 0)} tests
${group.sections
.map(
(section) => `
${section.title ? `${escapeHtml(section.title)}
` : ""}
`,
)
.join("")}
`,
)
.join("")}
`
: `
FPP catalog not available yet.
`
}
`,
qrcode: `
${panelHeader("Mandatory", "QR flow is optional in the backend, but keep the scan control at the top if used.")}
Scan existing patient QR
Use a QR code to pull patient and visit context instantly.
QR preview area
${panelHeader("Optional", "Manual fallback when scanning is not available.")}
`,
review: `
Summary All steps are merged into a final review before submit.
Diagnosis patient_diagnosa
`,
}[stepKey];
const prev = steps[activeIndex - 1];
const next = steps[activeIndex + 1];
return `
${panelHeader("Create new order", "The new project collapses the old step-heavy flow into a cleaner shell while keeping the same workflow.", 'Cancel ')}
`;
}
function renderResultsTable(results, selectedResultId) {
const selected = results.find((item) => item.id === selectedResultId) || results[0] || null;
return `
${panelHeader("Result history", "Desktop shows table detail. Mobile collapses into stacked cards.")}
Released ${results.filter((item) => item.status === "Released").length} items
Pending ${results.filter((item) => item.status === "Pending").length} items
Reviewed ${results.filter((item) => item.status === "Review").length} items
${
results.length
? `
Patient Result ID Test Status Date
${results
.map(
(result) => `
${escapeHtml(result.patient || "Unknown patient")} ${escapeHtml(result.summary || "-")}
${escapeHtml(result.id)}
${escapeHtml(result.test || "-")}
${statusBadge(result.status)}
${escapeHtml(result.date || "-")}
`,
)
.join("")}
${results
.map(
(result) => `
${escapeHtml(result.patient || "Unknown patient")}
${escapeHtml(result.test || "-")} · ${escapeHtml(result.date || "-")}
${statusBadge(result.status)}
Open detail
${escapeHtml(result.summary || "-")}
`,
)
.join("")}
`
: emptyState("No results returned", "The result endpoint did not return any rows for this search.")
}
`;
}
function renderResultDetail(result) {
if (!result) {
return `
${panelHeader("Result detail", "The API returned no matching result.", 'Back to results ')}
${emptyState("No result found", "No detail payload was returned for this result ID.")}
`;
}
return `
${panelHeader(`${result.patient} · ${result.id}`, "Result detail with summary, status, and interpretation fields.", 'Back to results ')}
Status ${statusBadge(result.status)}
Test ${escapeHtml(result.test)}
Value ${escapeHtml(result.value)}
${escapeHtml(result.summary)}
`;
}
function groupFppTestsForSelection(tests = []) {
const groups = new Map();
for (const test of tests) {
const heading = test.heading || "FPP";
const sectionTitle = test.subcategory || "";
if (!groups.has(heading)) {
groups.set(heading, { heading, sections: new Map() });
}
const group = groups.get(heading);
const sectionKey = sectionTitle || "__ungrouped__";
if (!group.sections.has(sectionKey)) {
group.sections.set(sectionKey, { title: sectionTitle, tests: [] });
}
group.sections.get(sectionKey).tests.push(test);
}
return Array.from(groups.values()).map((group) => ({
heading: group.heading,
sections: Array.from(group.sections.values()),
}));
}
function renderFpp(groups, activeGroup = "All") {
const availableGroups = groups.map((item) => item.heading);
const visibleGroups = activeGroup === "All" ? groups : groups.filter((item) => item.heading === activeGroup);
return `
${
visibleGroups.length
? `
Pemeriksaan
Hitam putih, padat, dan mengikuti pembagian grup seperti form manual.
${visibleGroups
.map(
(group) => `
${escapeHtml(group.heading)}
${group.sections.reduce((sum, section) => sum + section.tests.length, 0)} tests
${group.sections
.map(
(section) => `
${section.title ? `${escapeHtml(section.title)}
` : ""}
${section.tests
.map(
(test) => `
${escapeHtml(test.name)}
`,
)
.join("")}
`,
)
.join("")}
`,
)
.join("")}
`
: `
${emptyState("No FPP items", "The API returned no catalog rows for this filter.")} `
}
`;
}
async function renderPatients(session) {
const orders = await loadOrders(session, "", "All");
const recentPatients = Array.from(
new Map(orders.filter((order) => order.patient).map((order) => [order.patient, order])).values(),
).slice(0, 4);
return `
${panelHeader("Patient registration", "A landing zone for registration, lookup, and intake shortcuts.", 'Start registration ')}
${[
["Today's intake", String(orders.length), "From API response"],
["Active visits", String(orders.filter((item) => item.status === "Processing").length), "From API response"],
["QR scans", String(orders.filter((item) => String(item.mode).toLowerCase().includes("qr")).length), "From API response"],
["Needs review", String(orders.filter((item) => item.status === "Needs review").length), "From API response"],
]
.map(
([label, value, hint]) => `
${escapeHtml(label)}
Live
${escapeHtml(value)}
${escapeHtml(hint)}
`,
)
.join("")}
${panelHeader("Quick intake", "Route into the proper entry flow without forcing the user through extra screens.")}
New patient
Jump straight into demographic capture.
Use the registration stepper when the patient is not yet in the system.
`;
}
function renderSettings(session) {
return `
${panelHeader("Account", "Profile data and security entry points.")}
Name ${escapeHtml(session?.username || "-")}
Doctor ID ${escapeHtml(session?.doctorCode || session?.doctorId || "-")}
Role Doctor
Internal ID ${escapeHtml(session?.doctorId || "-")}
${panelHeader("Settings actions", "Keep the common account actions obvious.")}
`;
}
function renderChangePassword(session) {
return `
${panelHeader("Change password", "Inline validation and a straightforward submit path.")}
`;
}
function loginPage({ error = "" } = {}) {
return layout(
"DocLink Login",
`
`,
{ authenticated: false, shell: false, activePath: "/login" },
);
}
function splashPage() {
return layout(
"DocLink Splash",
`
`,
{ authenticated: false, shell: false, activePath: "/splash" },
);
}
function problemLoginPage() {
return layout(
"Problem Login",
`
`,
{ authenticated: false, shell: false, activePath: "/problem-login" },
);
}
async function orderNewPage(session, path) {
const step = path.split("/").filter(Boolean)[2] || "demografi";
const fppTests = await loadFppCatalog(session);
return layout("Create Order", `${renderOrderForm({}, step, fppTests, session?.mouId || "")}
`, {
authenticated: true,
activePath: "/orders",
subtitle: "The new project collapses the old step-heavy flow into a cleaner shell while keeping the same workflow.",
});
}
function ordersPage({ query = {}, orders = [], selectedOrderId = "" } = {}) {
return layout("Orders", `${renderOrdersTable(orders, selectedOrderId, query.status || "All")}
`, {
authenticated: true,
activePath: "/orders",
});
}
function resultsPage({ query = {}, results = [], selectedResultId = "" } = {}) {
return layout("Results", `${renderResultsTable(results, selectedResultId)}
`, {
authenticated: true,
activePath: "/results",
});
}
function fppPage({ group = "All", groups = [] } = {}) {
return layout("FPP", `${renderFpp(groups, group)}
`, {
authenticated: true,
activePath: "/fpp",
});
}
async function patientsPage(session) {
return layout("Patients", await renderPatients(session), { authenticated: true, activePath: "/patients" });
}
function settingsPage(session) {
return layout("Settings", renderSettings(session), { authenticated: true, activePath: "/settings" });
}
function changePasswordPage(session) {
return layout("Change Password", renderChangePassword(session), {
authenticated: true,
activePath: "/settings/change-password",
});
}
function orderDetailPage(order) {
return layout("Order Detail", renderOrderDetail(order), {
authenticated: true,
activePath: "/orders",
});
}
function resultDetailPage(result) {
return layout("Result Detail", renderResultDetail(result), {
authenticated: true,
activePath: "/results",
});
}
function specialMessagePage(order) {
return layout(
"Pesan Khusus",
`
${panelHeader("Pesan khusus", "Desktop can treat this as a modal-style panel; mobile reads it as a dedicated page.", `Back `)}
${escapeHtml(order.patient)}
${escapeHtml(order.id)}
${statusBadge(order.status)}
${escapeHtml(order.message)}
${order.apiSaran ? `
${escapeHtml(typeof order.apiSaran === "string" ? order.apiSaran : JSON.stringify(order.apiSaran))}
` : ""}
`,
{ authenticated: true, activePath: "/orders" },
);
}
function emptyRoute(path) {
return layout("Not Found", `Route not found ${escapeHtml(path)} is not part of the current rebuild scope.
Go to dashboard `, {
authenticated: true,
activePath: "/",
});
}
function cookieHeader(req) {
return req.headers.cookie || "";
}
function getCookie(req, name) {
const cookies = cookieHeader(req)
.split(";")
.map((part) => part.trim())
.filter(Boolean);
for (const item of cookies) {
const [key, ...rest] = item.split("=");
if (key === name) return decodeURIComponent(rest.join("="));
}
return "";
}
function setCookie(name, value, opts = {}) {
const parts = [`${name}=${encodeURIComponent(value)}`, "Path=/", "HttpOnly", "SameSite=Lax"];
if (opts.maxAge != null) parts.push(`Max-Age=${opts.maxAge}`);
if (opts.secure) parts.push("Secure");
return parts.join("; ");
}
function deleteCookie(name) {
return `${name}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`;
}
function readSession(req) {
const raw = getCookie(req, sessionKey);
if (!raw) return null;
try {
return JSON.parse(raw);
} catch {
return null;
}
}
function requireAuth(req, res) {
const session = readSession(req);
if (session) return session;
redirect(res, "/login");
return null;
}
async function fetchJson(url, options = {}) {
const response = await fetch(url, options);
const contentType = response.headers.get("content-type") || "";
const text = await response.text();
let body = text;
if (contentType.includes("application/json") || /^\s*[\[{]/.test(text)) {
try {
body = JSON.parse(text);
} catch {
body = text;
}
}
if (!response.ok) {
const error = new Error(`Upstream error ${response.status}`);
error.status = response.status;
error.body = body;
throw error;
}
return body;
}
async function apiPost(path, payload, token = "") {
const headers = { "Content-Type": "application/json" };
if (token) {
headers.Authorization = `Bearer ${token}`;
payload.token = payload.token || token;
}
const url = `${API_BASE}${path}`;
return fetchJson(url, {
method: "POST",
headers,
body: JSON.stringify(payload),
});
}
function normalizeSession(payload) {
const data = payload?.data || payload?.result || payload;
const user = data?.user || data?.data || {};
const token = data?.token || data?.access_token || data?.accessToken || payload?.token || "";
const username = user?.M_UserUsername || data?.username || data?.M_UserUsername || data?.name || "";
const doctorId = user?.M_UserM_DoctorID || data?.doctor_id || data?.doctorId || "";
const doctorCode = user?.M_UserM_DoctorCode || data?.doctor_code || "";
const mouId = user?.M_UserM_MouID || data?.M_UserM_MouID || data?.mou_id || "";
const userId = user?.M_UserID || data?.M_UserID || data?.user_id || "";
return {
token,
username,
doctorId,
doctorCode,
mouId,
userId,
raw: payload,
};
}
function extractArray(payload) {
if (Array.isArray(payload)) return payload;
if (!payload || typeof payload !== "object") return null;
for (const key of ["data", "result", "results", "items", "list"]) {
const value = payload[key];
const found = extractArray(value);
if (found) return found;
}
return null;
}
function normalizeOrder(raw, index = 0) {
const status = raw?.status || raw?.order_status || raw?.state || "Processing";
return {
id: raw?.order_patient_id || raw?.order_id || raw?.id || raw?.OrderPatientID || raw?.OrderID || "",
patient: raw?.order_name || raw?.patient_name || raw?.name || raw?.patient || raw?.patient_fullname || "",
doctor: raw?.doctor_id || raw?.doctor_name || raw?.doctor || raw?.M_UserUsername || "",
updated: raw?.order_date || raw?.updated_at || raw?.updated || raw?.created_at || "",
status,
tone: statusClass(status),
mode: raw?.mode || raw?.visit_type || raw?.patient_type || "",
age: String(raw?.age || raw?.patient_age || ""),
gender: raw?.gender || raw?.patient_gender || "",
tests: [],
diagnosis: raw?.order_nik || raw?.patient_diagnosa || raw?.diagnosis || raw?.note || "",
message: raw?.order_address || raw?.message || raw?.patient_note || "",
orderDate: raw?.order_date || "",
orderNik: raw?.order_nik || "",
orderHp: raw?.order_hp || "",
orderAddress: raw?.order_address || "",
orderDob: raw?.order_dob || "",
};
}
function normalizeResult(raw, index = 0) {
const status = raw?.status || raw?.result_status || raw?.order_status || "Pending";
const detailsSource = raw?.details || raw?.items || raw?.order_details || [];
const details = Array.isArray(detailsSource)
? detailsSource
.map((item) => {
if (typeof item === "string") return item;
return item?.test_name || item?.name || item?.detail_name || item?.exam || item?.label || "";
})
.filter(Boolean)
: [];
return {
id: raw?.result_id || raw?.order_id || raw?.id || raw?.hasil_id || "",
patient: raw?.order_name || raw?.patient_name || raw?.name || raw?.patient || "",
test: details.join(", ") || raw?.test_name || raw?.item_name || raw?.test || "",
status,
tone: statusClass(status),
date: raw?.order_date || raw?.date || raw?.created_at || raw?.updated_at || "",
summary: raw?.order_diagnosa || raw?.summary || raw?.note || raw?.result_summary || "",
value: raw?.order_note || raw?.value || raw?.result_value || raw?.result || "",
details,
orderCode: raw?.order_qrcode || "",
orderDob: raw?.order_dob || "",
orderAddress: raw?.order_address || "",
orderNik: raw?.order_nik || "",
orderHp: raw?.order_hp || "",
};
}
async function loadOrders(session, search = "", status = "All") {
const term = String(search || "").trim();
try {
const payload = await apiPost(
"/order/search_order_pasien_by_doktorid",
{
token: session.token,
OrderPatientM_DoctorID: session.doctorId || sampleLogin.doctorId,
search: term,
},
session.token,
);
const rows = extractArray(payload) || [];
return rows.map((row, index) => normalizeOrder(row, index)).filter((item) => {
if (!item.id) return false;
const matchesSearch =
!term ||
[item.id, item.patient, item.status, item.diagnosis, item.message].some((value) =>
String(value).toLowerCase().includes(term.toLowerCase()),
);
const matchesStatus = !status || status === "All" || item.status === status;
return matchesSearch && matchesStatus;
});
} catch {
return [];
}
return [];
}
async function loadResults(session, search = "") {
const term = String(search || "").trim();
try {
const orders = await loadOrders(session, "", "All");
const orderId = session.orderId || orders[0]?.id || "";
const payload = await apiPost(
"/order/hasil_belum_keluar_by_id",
{ token: session.token, order_id: orderId },
session.token,
);
const rawRows = extractArray(payload) || (payload?.data ? [payload.data] : []);
return rawRows.map((row, index) => normalizeResult(row, index)).filter((item) => {
if (!item.id) return false;
if (!term) return true;
return [item.id, item.patient, item.test, item.status, item.summary].some((value) =>
String(value).toLowerCase().includes(term.toLowerCase()),
);
});
} catch {
return [];
}
return [];
}
async function loadResultDetail(session, resultId) {
try {
const payload = await apiPost(
"/result/getResult",
{
token: session.token,
result_id: resultId,
order_id: resultId,
},
session.token,
);
const rows = extractArray(payload);
if (rows?.length) return normalizeResult(rows[0], 0);
if (payload?.data && typeof payload.data === "object") return normalizeResult(payload.data, 0);
if (payload && typeof payload === "object") return normalizeResult(payload, 0);
const related = await loadResults(session, resultId);
return related.find((item) => item.id === resultId) || null;
} catch {
const related = await loadResults(session, resultId);
return related.find((item) => item.id === resultId) || related[0] || null;
}
}
async function loadFpp(session, group = "All") {
try {
const payload = await apiPost("/Fpp/load/1/1", { token: session.token }, session.token);
const rows = extractArray(payload) || [];
const items = rows.map((row, index) => ({
group: row?.group || row?.kategori || row?.name || `GROUP-${index + 1}`,
count: Number(row?.count || row?.total || row?.qty || 1),
desc: row?.description || row?.desc || "Laboratory reference item.",
}));
const filtered = group === "All" ? items : items.filter((item) => item.group === group);
return { items: filtered, filter: group };
} catch {
return { items: [], filter: group };
}
}
async function loadFppCatalog(session) {
try {
const { doctorId, mouId } = resolveFppRouteIds(session);
const payload = await apiPost(`/Fpp/loadFPP/${doctorId}/${mouId}`, { token: session.token }, session.token);
const rows = extractArray(payload) || payload?.rows || [];
const headings = Array.isArray(rows) ? rows : [];
const tests = headings.flatMap((heading) =>
(heading?.details || []).flatMap((detail) =>
(detail?.tests || []).map((test) => ({
heading: heading?.heading || "FPP",
subcategory: detail?.subcategories || "",
testId: String(test?.testid || ""),
code: String(test?.code || ""),
name: String(test?.name || ""),
price: String(test?.price || "0"),
checked: Boolean(test?.checked),
doctortest: test?.doctortest !== false && test?.doctortest !== "false",
})),
),
);
return tests.filter((item) => item.name);
} catch {
return [];
}
}
async function loadFppPosterGroups(session) {
try {
const { doctorId, mouId } = resolveFppRouteIds(session);
const payload = await apiPost(`/Fpp/loadFPP/${doctorId}/${mouId}`, { token: session.token }, session.token);
const rows = extractArray(payload) || payload?.rows || [];
const headings = Array.isArray(rows) ? rows : [];
return headings
.map((heading) => ({
heading: heading?.heading || "FPP",
sections: (heading?.details || [])
.map((detail) => ({
title: detail?.subcategories || "",
tests: (detail?.tests || [])
.map((test) => ({
testId: String(test?.testid || ""),
code: String(test?.code || ""),
name: String(test?.name || ""),
price: String(test?.price || "0"),
checked: Boolean(test?.checked),
doctortest: test?.doctortest !== false && test?.doctortest !== "false",
}))
.filter((test) => test.name),
}))
.filter((section) => section.tests.length),
}))
.filter((group) => group.sections.length);
} catch {
return [];
}
}
async function loadOrderDetail(session, orderId) {
const [orders, saran, hasil] = await Promise.all([
loadOrders(session, "", "All"),
apiPost(
"/order/get_order_saran_by_order_patient_id",
{ token: session.token, order_patient_id: orderId },
session.token,
).catch(() => null),
apiPost(
"/order/hasil_belum_keluar_by_id",
{ token: session.token, order_id: orderId },
session.token,
).catch(() => null),
]);
const order = orders.find((item) => item.id === orderId) || orders[0] || null;
if (!order) return null;
const detail = { ...order };
try {
detail.apiSaran = saran ? extractArray(saran) || saran?.message || saran?.note || saran?.result || "" : "";
detail.apiHasil = hasil ? extractArray(hasil) || hasil?.message || hasil?.note || hasil?.result || "" : "";
} catch {
detail.apiSaran = "";
detail.apiHasil = "";
}
return detail;
}
function json(res, status, payload) {
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
res.end(JSON.stringify(payload));
}
function redirect(res, location, headers = {}) {
res.writeHead(302, { Location: location, ...headers });
res.end();
}
function html(res, status, body, headers = {}) {
res.writeHead(status, { "Content-Type": "text/html; charset=utf-8", ...headers });
res.end(body);
}
function isHtmx(req) {
return req.headers["hx-request"] === "true";
}
async function readBody(req) {
const chunks = [];
for await (const chunk of req) chunks.push(chunk);
const raw = Buffer.concat(chunks).toString("utf8");
const contentType = req.headers["content-type"] || "";
if (contentType.includes("application/json")) return raw ? JSON.parse(raw) : {};
const flat = Object.fromEntries(new URLSearchParams(raw));
const details = [];
for (const [key, value] of Object.entries(flat)) {
const match = key.match(/^details\[(\d+)\]\[(\w+)\]$/);
if (!match) continue;
const index = Number(match[1]);
const field = match[2];
details[index] ||= {};
details[index][field] = value;
delete flat[key];
}
if (details.length) flat.details = details.filter(Boolean);
return flat;
}
function fragmentOrdersTable(search = "", status = "All", ordersData = null) {
const orders = ordersData || [];
const selected = orders[0] || null;
return `
${panelHeader("Search orders", "Filter the list without full page refresh.")}
${["All", "Processing", "Ready", "Needs review"]
.map(
(item) => `
${escapeHtml(item)}
`,
)
.join("")}
${
orders.length
? `
Patient Order ID Doctor Status Updated
${orders
.map(
(order) => `
${escapeHtml(order.patient || "Unknown patient")} ${escapeHtml(order.mode || "-")}
${escapeHtml(order.id)}
${escapeHtml(order.doctor || "-")}
${statusBadge(order.status)}
${escapeHtml(order.updated || "-")}
`,
)
.join("")}
`
: emptyState("No orders returned", "The order endpoint did not return any rows for this filter.")
}
${panelHeader("Selected order", "Context stays visible while you review the table.", selected ? `Open detail ` : "")}
${
selected
? `
${escapeHtml(selected.patient || "Unknown patient")}
${escapeHtml(selected.id || "-")} · ${escapeHtml(selected.mode || "-")}
${statusBadge(selected.status)}${escapeHtml(selected.age || "-")} years ${escapeHtml(selected.gender || "-")}
Diagnosis ${escapeHtml(selected.diagnosis || "-")}
Requested tests ${escapeHtml((selected.tests || []).join(", ") || "-")}
Special note ${escapeHtml(selected.message || "-")}
`
: emptyState("No selected order", "The API returned no rows for this search.")
}
`;
}
function fragmentResultsTable(search = "", resultsData = null) {
const results = resultsData || [];
const selected = results[0] || null;
return `
${panelHeader("Result history", "Desktop shows table detail. Mobile collapses into stacked cards.")}
Released ${results.filter((item) => item.status === "Released").length} items
Pending ${results.filter((item) => item.status === "Pending").length} items
Reviewed ${results.filter((item) => item.status === "Review").length} items
${
results.length
? `
Patient Result ID Test Status Date
${results
.map(
(result) => `
${escapeHtml(result.patient || "Unknown patient")} ${escapeHtml(result.summary || "-")}
${escapeHtml(result.id)}
${escapeHtml(result.test || "-")}
${statusBadge(result.status)}
${escapeHtml(result.date || "-")}
`,
)
.join("")}
`
: emptyState("No results returned", "The result endpoint did not return any rows for this search.")
}
`;
}
function fragmentFpp(groups = [], group = "All") {
return renderFpp(groups, group);
}
async function fragmentOrderStep(session, step) {
const fppTests = await loadFppCatalog(session);
return renderOrderForm({}, step, fppTests, session?.mouId || "");
}
function fragmentPesanKhusus(orderId) {
const order = orderId
? { id: orderId, patient: "", status: "Processing", message: "", apiSaran: "" }
: { id: "", patient: "", status: "Processing", message: "", apiSaran: "" };
return `
${panelHeader("Pesan khusus", "Desktop opens this as a modal, mobile can still use it as a full sheet.", `Close `)}
${escapeHtml(order.patient || "Order detail")}
${escapeHtml(order.id)}
${statusBadge(order.status)}
${escapeHtml(order.message || "-")}
Special message
${escapeHtml(order.message || "")}
Save message
Cancel
`;
}
async function fragmentResultDetail(session, resultId) {
const result = await loadResultDetail(session, resultId);
return `
${renderResultDetail(result)}
`;
}
async function renderRoute(req, res, url) {
const path = url.pathname;
const query = Object.fromEntries(url.searchParams.entries());
const session = readSession(req);
const authed = Boolean(session);
const isGet = req.method === "GET" || req.method === "HEAD";
if (path === "/styles.css") {
const css = await readFile(new URL("./styles.css", import.meta.url), "utf8");
res.writeHead(200, { "Content-Type": "text/css; charset=utf-8" });
res.end(css);
return;
}
if (path === "/login" && isGet) {
html(res, 200, loginPage(), { "Cache-Control": "no-store" });
return;
}
if (path === "/splash" && isGet) {
html(res, 200, splashPage(), { "Cache-Control": "no-store" });
return;
}
if (path === "/problem-login" && isGet) {
html(res, 200, problemLoginPage(), { "Cache-Control": "no-store" });
return;
}
if (path === "/login" && req.method === "POST") {
const body = await readBody(req);
try {
const payload = await apiPost("/auth/login", body);
const normalized = normalizeSession(payload);
const upstreamFailed = String(payload?.status || payload?.result_status || "").toUpperCase() === "ERR" || !normalized.token;
if (upstreamFailed) {
throw new Error(payload?.message || "Invalid login response");
}
const sessionValue = JSON.stringify({
token: normalized.token,
username: normalized.username || body.username || "",
doctorId: normalized.doctorId || body.doctor_id || "",
doctorCode: normalized.doctorCode || body.doctor_id || "",
mouId: normalized.mouId || body.M_MouID || "",
userId: normalized.userId || "",
raw: payload,
});
redirect(res, "/", {
"Set-Cookie": setCookie(sessionKey, sessionValue, { maxAge: 60 * 60 * 12 }),
});
} catch (error) {
html(res, 200, loginPage({ error: `Login failed: ${error.message}. Check credentials and network.` }));
}
return;
}
if (path === "/logout" && req.method === "POST") {
const sessionData = readSession(req);
if (sessionData?.token) {
try {
await apiPost(
"/auth/logout",
{
M_UserID: sessionData.userId || "",
M_UserUsername: sessionData.username || sampleLogin.username,
},
sessionData.token,
);
} catch {
// Ignore upstream logout failures and still clear local session.
}
}
redirect(res, "/login", { "Set-Cookie": deleteCookie(sessionKey) });
return;
}
if (path === "/settings/change-password" && req.method === "POST") {
const sessionData = requireAuth(req, res);
if (!sessionData) return;
const body = await readBody(req);
try {
await apiPost(
"/auth/change_password",
{
token: sessionData.token,
M_UserID: sessionData.userId || "",
username: sessionData.username || sampleLogin.username,
doctor_id: sessionData.doctorCode || sessionData.doctorId || sampleLogin.doctorId,
new_password: body.new_password,
confirm_password: body.confirm_password,
},
sessionData.token,
);
redirect(res, "/settings");
} catch (error) {
html(
res,
200,
layout(
"Change Password",
`${panelHeader("Change password", "Inline validation and a straightforward submit path.")}Upstream change password call failed. Check API availability.
`,
{ authenticated: true, activePath: "/settings/change-password" },
),
);
}
return;
}
if (path === "/orders/new" && isGet) {
if (!requireAuth(req, res)) return;
html(res, 200, await orderNewPage(session, path));
return;
}
if (path.startsWith("/orders/new/") && isGet) {
if (!requireAuth(req, res)) return;
html(res, 200, await orderNewPage(session, path));
return;
}
if (path === "/orders" && isGet) {
if (!requireAuth(req, res)) return;
const orders = await loadOrders(session, query.search || "", query.status || "All");
html(res, 200, ordersPage({ query, orders, selectedOrderId: orders[0]?.id || "" }));
return;
}
if (path === "/results" && isGet) {
if (!requireAuth(req, res)) return;
const results = await loadResults(session, query.search || "");
html(res, 200, resultsPage({ query, results, selectedResultId: results[0]?.id || "" }));
return;
}
if (path === "/results/historical" && isGet) {
if (!requireAuth(req, res)) return;
const results = await loadResults(session, "");
html(res, 200, layout("Historical results", `${panelHeader("Historical results", "A compact historic view that still fits the responsive shell.")}${results.length ? `` : emptyState("No results returned", "The API returned no historical result rows.")} `, { authenticated: true, activePath: "/results" }));
return;
}
if (path === "/results/pending" && isGet) {
if (!requireAuth(req, res)) return;
const results = (await loadResults(session, "")).filter((item) => item.status !== "Released");
html(res, 200, layout("Pending results", `${panelHeader("Pending results", "Items that need attention before release.")}${results.length ? `${results.map((item) => `
${escapeHtml(item.patient || "Unknown patient")} ${statusBadge(item.status)}
${escapeHtml(item.test || "-")} · ${escapeHtml(item.summary || "-")}
Open detail `).join("")}
` : emptyState("No pending results", "The API returned no unreleased rows.")} `, { authenticated: true, activePath: "/results" }));
return;
}
if (path === "/fpp" && isGet) {
if (!requireAuth(req, res)) return;
const groups = await loadFppPosterGroups(session);
html(res, 200, fppPage({ group: query.group || "All", groups }));
return;
}
if (path === "/patients" && isGet) {
if (!requireAuth(req, res)) return;
html(res, 200, await patientsPage(session));
return;
}
if ((path === "/settings" || path === "/settings/account") && isGet) {
if (!requireAuth(req, res)) return;
html(res, 200, settingsPage(session));
return;
}
if (path === "/settings/change-password" && isGet) {
if (!requireAuth(req, res)) return;
html(res, 200, changePasswordPage(session));
return;
}
if (path === "/" && isGet) {
if (!requireAuth(req, res)) return;
html(res, 200, layout("Dashboard", await dashboardPage(session), { authenticated: true, activePath: "/" }));
return;
}
if (path.startsWith("/orders/") && path.endsWith("/pesan-khusus") && isGet) {
if (!requireAuth(req, res)) return;
const orderId = path.split("/")[2];
const order = await loadOrderDetail(session, orderId);
if (!order) {
html(res, 200, layout("Pesan Khusus", `${emptyState("No order found", "The API returned no matching order for this ID.")} `, { authenticated: true, activePath: "/orders" }));
return;
}
html(res, 200, specialMessagePage(order));
return;
}
if (path.startsWith("/orders/") && isGet) {
if (!requireAuth(req, res)) return;
const orderId = path.split("/")[2];
const order = await loadOrderDetail(session, orderId);
if (!order) {
html(res, 200, layout("Order Detail", `${emptyState("No order found", "The API returned no matching order for this ID.")} `, { authenticated: true, activePath: "/orders" }));
return;
}
html(res, 200, orderDetailPage(order));
return;
}
if (path.startsWith("/results/") && isGet) {
if (!requireAuth(req, res)) return;
const resultId = path.split("/")[2];
const result = await loadResultDetail(session, resultId);
if (!result) {
html(res, 200, layout("Result Detail", `${emptyState("No result found", "The API returned no matching result for this ID.")} `, { authenticated: true, activePath: "/results" }));
return;
}
html(res, 200, resultDetailPage(result));
return;
}
if (path === "/fragments/orders/table" && isGet) {
if (!requireAuth(req, res)) return;
const orders = await loadOrders(session, query.search || "", query.status || "All");
html(res, 200, fragmentOrdersTable(query.search || "", query.status || "All", orders), { "HX-Trigger": JSON.stringify({ "doclink:orders-updated": true }) });
return;
}
if (path === "/fragments/results/table" && isGet) {
if (!requireAuth(req, res)) return;
const results = await loadResults(session, query.search || "");
html(res, 200, fragmentResultsTable(query.search || "", results));
return;
}
if (path === "/fragments/fpp/list" && isGet) {
if (!requireAuth(req, res)) return;
const groups = await loadFppPosterGroups(session);
html(res, 200, fragmentFpp(groups, query.group || "All"));
return;
}
if (path.startsWith("/fragments/forms/order-step/") && isGet) {
if (!requireAuth(req, res)) return;
const step = path.split("/")[4] || "demografi";
html(res, 200, await fragmentOrderStep(session, step));
return;
}
if (path === "/fragments/modals/pesan-khusus" && isGet) {
if (!requireAuth(req, res)) return;
html(res, 200, fragmentPesanKhusus(query.order_id || ""));
return;
}
if (path.startsWith("/fragments/results/detail/") && isGet) {
if (!requireAuth(req, res)) return;
const resultId = path.split("/")[4] || "";
html(res, 200, await fragmentResultDetail(session, resultId));
return;
}
if (path.startsWith("/api/") && isGet) {
json(res, 200, { ok: true });
return;
}
if (path === "/orders" && req.method === "POST") {
const sessionData = requireAuth(req, res);
if (!sessionData) return;
const body = await readBody(req);
try {
await apiPost(
"/order/order_patient",
{
token: sessionData.token,
M_MouID: body.M_MouID || sessionData.mouId || sampleLogin.mouId || "",
patient_name: body.patient_name || "",
patient_diagnosa: body.patient_diagnosa || "",
patient_address: body.patient_address || "",
patient_nik: body.patient_nik || "",
patient_hp: body.patient_hp || "",
patient_dob: body.patient_dob || "",
patient_note: body.patient_note || "",
details: body.details || [],
},
sessionData.token,
);
redirect(res, "/orders");
} catch {
html(
res,
200,
layout(
"Create Order",
`${panelHeader("Create new order", "The new project collapses the old step-heavy flow into a cleaner shell while keeping the same workflow.")}Upstream order create call failed. Check API availability.
`,
{ authenticated: true, activePath: "/orders" },
),
);
}
return;
}
if (path.startsWith("/orders/") && path.endsWith("/pesan-khusus") && req.method === "POST") {
const sessionData = requireAuth(req, res);
if (!sessionData) return;
const orderId = path.split("/")[2];
const body = await readBody(req);
try {
await apiPost(
"/Pesankhusus/add_pesan_khusus",
{
token: sessionData.token,
order_id: orderId,
pesan_khusus: body.pesan_khusus,
},
sessionData.token,
);
redirect(res, `/orders/${orderId}`);
} catch {
html(
res,
200,
layout(
"Pesan Khusus",
`${panelHeader("Pesan khusus", "Desktop can treat this as a modal-style panel; mobile reads it as a dedicated page.")}Upstream save failed. Check API availability.
`,
{ authenticated: true, activePath: "/orders" },
),
);
}
return;
}
html(res, 404, emptyRoute(path));
}
const server = http.createServer(async (req, res) => {
try {
const url = new URL(req.url, `http://${req.headers.host}`);
await renderRoute(req, res, url);
} catch (error) {
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
res.end(`Internal error: ${error.message}`);
}
});
server.listen(PORT, () => {
console.log(`DocLink Web running on http://localhost:${PORT}`);
});