// public/dashboard/js/api.js // Centralized REST API client for Btekno Retribusi Admin Dashboard import { API_CONFIG } from './config.js'; // Export API_CONFIG untuk digunakan di file lain export { API_CONFIG }; const API_BASE_URL = API_CONFIG.BASE_URL; function getToken() { return localStorage.getItem('token') || ''; } async function apiRequest(path, options = {}) { const url = path.startsWith('http') ? path : `${API_BASE_URL}${path}`; const headers = { 'Content-Type': 'application/json', // X-API-KEY dari konfigurasi backend (RETRIBUSI_API_KEY) 'X-API-KEY': API_CONFIG.API_KEY, ...(options.headers || {}) }; const token = getToken(); if (token) { headers['Authorization'] = `Bearer ${token}`; } const config = { method: options.method || 'GET', headers, body: options.body ? JSON.stringify(options.body) : null }; try { const res = await fetch(url, config); if (res.status === 401) { // Unauthorized → clear token & redirect to login localStorage.removeItem('token'); localStorage.removeItem('user'); // Cek apakah sudah di login page untuk menghindari redirect loop const currentPath = window.location.pathname; const isLoginPage = currentPath.includes('index.php') || currentPath === '/' || currentPath.endsWith('/'); if (!isLoginPage) { window.location.href = '../index.php'; } throw new Error('Unauthorized'); } const text = await res.text(); let json; try { json = text ? JSON.parse(text) : {}; } catch (e) { json = { raw: text }; } if (!res.ok) { const msg = json.message || json.error || `HTTP ${res.status}`; throw new Error(msg); } // Some endpoints might wrap data as { success, data, ... } if (json && Object.prototype.hasOwnProperty.call(json, 'success') && Object.prototype.hasOwnProperty.call(json, 'data')) { return json.data; } return json; } catch (err) { console.error('API error', { url, error: err }); throw err; } } // Helper untuk build query string dari object params function buildQuery(params = {}) { const search = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== null && value !== '') { search.append(key, value); } }); const qs = search.toString(); return qs ? `?${qs}` : ''; } // Typed helpers export async function apiLogin(username, password) { return apiRequest('/auth/v1/login', { method: 'POST', body: { username, password } }); } export async function apiGetLocations(params = {}) { // Handle pagination: { page, limit } const qs = buildQuery(params); return apiRequest(`/retribusi/v1/frontend/locations${qs}`); } export async function apiGetGates(locationCode, params = {}) { // Handle pagination: { page, limit, location_code } const queryParams = { ...params }; if (locationCode) queryParams.location_code = locationCode; const qs = buildQuery(queryParams); return apiRequest(`/retribusi/v1/frontend/gates${qs}`); } export async function apiGetSummary({ date, locationCode, gateCode }) { const qs = buildQuery({ date, location_code: locationCode, gate_code: gateCode }); return apiRequest(`/retribusi/v1/dashboard/summary${qs}`); } export async function apiGetDaily({ startDate, endDate, locationCode }) { const qs = buildQuery({ start_date: startDate, end_date: endDate, location_code: locationCode }); return apiRequest(`/retribusi/v1/dashboard/daily${qs}`); } export async function apiGetByCategory({ date, locationCode, gateCode }) { const qs = buildQuery({ date, location_code: locationCode, gate_code: gateCode }); return apiRequest(`/retribusi/v1/dashboard/by-category${qs}`); } // Ringkasan global harian (daily_summary) export async function apiGetSummaryDaily(params = {}) { const qs = buildQuery(params); return apiRequest(`/retribusi/v1/summary/daily${qs}`); } // Ringkasan per jam (hourly_summary) export async function apiGetSummaryHourly(params = {}) { const qs = buildQuery(params); return apiRequest(`/retribusi/v1/summary/hourly${qs}`); } // Snapshot realtime (untuk panel live / TV wall) export async function apiGetRealtimeSnapshot(params = {}) { const qs = buildQuery(params); return apiRequest(`/retribusi/v1/realtime/snapshot${qs}`); } // Entry events list (raw events dari mesin YOLO) // GET /retribusi/v1/frontend/entry-events // Parameters: page, limit, location_code, gate_code, category, start_date, end_date export async function apiGetEntryEvents(params = {}) { const qs = buildQuery(params); return apiRequest(`/retribusi/v1/frontend/entry-events${qs}`); } // Realtime events list (history untuk SSE) // GET /retribusi/v1/realtime/events // Parameters: page, limit, location_code, gate_code, category, start_date, end_date export async function apiGetRealtimeEvents(params = {}) { const qs = buildQuery(params); return apiRequest(`/retribusi/v1/realtime/events${qs}`); } // Alias untuk backward compatibility export async function apiGetEvents(params = {}) { return apiGetEntryEvents(params); } // Catatan: realtime SSE /retribusi/v1/realtime/stream akan diakses langsung via EventSource, // bukan lewat fetch/apiRequest karena menggunakan Server-Sent Events (SSE).