const ADMIN_TOKEN_KEY = "admin_token"; function formatMoney(value) { const number = Number(value || 0); if (!Number.isFinite(number)) { return "Rp 0"; } return new Intl.NumberFormat("id-ID", { style: "currency", currency: "IDR", maximumFractionDigits: 0 }).format(number); } function formatDateTime(value) { if (!value) { return "-"; } const date = new Date(value); if (Number.isNaN(date.getTime())) { return value; } return new Intl.DateTimeFormat("en-GB", { dateStyle: "medium", timeStyle: "short" }).format(date); } function buildQuery(query) { const search = new URLSearchParams(); Object.entries(query || {}).forEach(([key, val]) => { if (val !== undefined && val !== null && val !== "") { search.append(key, String(val)); } }); return search.toString(); } async function adminFetch(path, options = {}) { const token = localStorage.getItem(ADMIN_TOKEN_KEY); const { method = "GET", query, body, headers: extraHeaders = {}, auth = true } = options; const suffix = buildQuery(query || {}); const url = suffix ? `${path}?${suffix}` : path; const headers = { ...extraHeaders }; if (auth) { if (!token) { throw new Error("ADMIN_AUTH_MISSING"); } headers.Authorization = `Bearer ${token}`; } if (method !== "GET" && body !== undefined) { headers["Content-Type"] = "application/json"; } const response = await fetch(url, { method, headers, body: method === "GET" || body === undefined ? undefined : JSON.stringify(body) }); const raw = await response.text(); let payload; try { payload = raw ? JSON.parse(raw) : {}; } catch (error) { payload = {}; } if (!response.ok) { const message = payload?.message || payload?.error || `Request failed with status ${response.status}`; throw new Error(message); } return payload?.data !== undefined ? payload.data : payload; } window.AdminUIAPI = { ADMIN_TOKEN_KEY, setToken: (token) => localStorage.setItem(ADMIN_TOKEN_KEY, token), getToken: () => localStorage.getItem(ADMIN_TOKEN_KEY), clearToken: () => localStorage.removeItem(ADMIN_TOKEN_KEY), requireToken: () => { const token = localStorage.getItem(ADMIN_TOKEN_KEY); if (!token) { window.location.href = "/ui/admin-login"; throw new Error("ADMIN_AUTH_MISSING"); } return token; }, login: async ({ username, password }) => { const data = await adminFetch("/admin/login", { method: "POST", auth: false, body: { username, password } }); if (data?.token) { window.AdminUIAPI.setToken(data.token); } return data; }, listMerchants: () => adminFetch("/admin/merchants"), listOutlets: (query) => adminFetch("/admin/outlets", { query }), getOutlet: (id) => adminFetch(`/admin/outlets/${id}`), getMerchant: (id) => adminFetch(`/admin/merchants/${id}`), patchMerchant: (id, payload) => adminFetch(`/admin/merchants/${id}`, { method: "PATCH", body: payload }), approveMerchant: (id) => adminFetch(`/admin/merchants/${id}/approve`, { method: "POST" }), rejectMerchant: (id, payload) => adminFetch(`/admin/merchants/${id}/reject`, { method: "POST", body: payload || {} }), listTerminals: (query) => adminFetch("/admin/terminals", { query }), getTerminal: (id) => adminFetch(`/admin/terminals/${id}`), listDevices: (query) => adminFetch("/admin/devices", { query }), getDevice: (id) => adminFetch(`/admin/devices/${id}`), getDeviceHeartbeats: (id, query) => adminFetch(`/admin/devices/${id}/heartbeats`, { query }), getDeviceConfig: (id) => adminFetch(`/admin/devices/${id}/config`), getDeviceConfigStatus: (id) => adminFetch(`/admin/devices/${id}/config/status`), retryDeviceConfigPush: (id, payload) => adminFetch(`/admin/devices/${id}/config/retry-push`, { method: "POST", body: payload || {} }), listTransactions: (query) => adminFetch("/admin/transactions", { query }), getDashboardSummary: () => adminFetch("/admin/dashboard/summary"), formatMoney, formatDateTime };