Files
Qris-Soundbox/ui/shared/admin-api.js
2026-05-25 08:22:12 +07:00

143 lines
3.8 KiB
JavaScript

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 }),
listTransactions: (query) => adminFetch("/admin/transactions", { query }),
getDashboardSummary: () => adminFetch("/admin/dashboard/summary"),
formatMoney,
formatDateTime
};