Fix: Data inconsistency pada transisi tahun/bulan dan setup API lokal untuk testing

- Fix deteksi transisi bulan untuk semua bulan (tidak hanya 1 Januari)
- Perbaikan validasi data dengan threshold 20% untuk transisi bulan/tahun
- Auto-reset chart jika data hourly tidak valid
- Setup force local mode untuk testing API lokal
- Perbaikan normalisasi dan validasi tanggal
- Enhanced logging untuk debugging transisi
This commit is contained in:
BTekno Dev
2026-01-01 23:38:42 +07:00
parent fccda40d72
commit 5e330e931b
6 changed files with 655 additions and 48 deletions

View File

@@ -67,6 +67,19 @@ async function apiRequest(path, options = {}) {
json = { raw: text };
}
// Log response body untuk debug (khusus untuk summary endpoint)
if (url.includes('/dashboard/summary') || url.includes('/summary/hourly') || url.includes('/by-category')) {
console.log('[API] Response body for', url, ':', {
textLength: text.length,
jsonKeys: Object.keys(json || {}),
jsonPreview: JSON.stringify(json).substring(0, 500),
hasSuccess: json && 'success' in json,
hasData: json && 'data' in json,
totalCount: json?.total_count ?? json?.data?.total_count,
totalAmount: json?.total_amount ?? json?.data?.total_amount
});
}
if (!res.ok) {
const msg = json.message || json.error || `HTTP ${res.status}`;
throw new Error(msg);
@@ -103,7 +116,48 @@ function buildQuery(params = {}) {
const search = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
search.append(key, value);
// Pastikan value adalah string dan sudah di-encode dengan benar
let stringValue = String(value).trim();
// Validasi khusus untuk parameter date: harus format YYYY-MM-DD
if (key === 'date' || key === 'start_date' || key === 'end_date') {
// Validasi format tanggal
if (!/^\d{4}-\d{2}-\d{2}$/.test(stringValue)) {
console.error('[API] buildQuery - Invalid date format:', stringValue);
// Coba normalisasi jika mungkin
const dateObj = new Date(stringValue);
if (!isNaN(dateObj.getTime())) {
const year = dateObj.getFullYear();
const month = String(dateObj.getMonth() + 1).padStart(2, '0');
const day = String(dateObj.getDate()).padStart(2, '0');
stringValue = `${year}-${month}-${day}`;
console.warn('[API] buildQuery - Date normalized to:', stringValue);
} else {
console.error('[API] buildQuery - Cannot normalize date, skipping:', stringValue);
return; // Skip parameter ini jika tidak valid
}
}
// Validasi bahwa tanggal valid (tidak ada 2025-13-45)
const [year, month, day] = stringValue.split('-').map(Number);
const dateObj = new Date(year, month - 1, day);
if (dateObj.getFullYear() !== year || dateObj.getMonth() !== month - 1 || dateObj.getDate() !== day) {
console.error('[API] buildQuery - Invalid date values:', { year, month, day });
return; // Skip parameter ini jika tidak valid
}
console.log('[API] buildQuery - Date param validated:', {
key,
originalValue: value,
stringValue: stringValue,
encoded: stringValue, // URLSearchParams akan encode otomatis
year: year,
month: month,
day: day
});
}
search.append(key, stringValue);
}
});
const qs = search.toString();
@@ -139,7 +193,9 @@ export async function apiGetSummary({ date, locationCode, gateCode }) {
location_code: locationCode,
gate_code: gateCode
});
return apiRequest(`/retribusi/v1/dashboard/summary${qs}`);
const url = `/retribusi/v1/dashboard/summary${qs}`;
console.log('[API] apiGetSummary - URL:', url, 'Params:', { date, locationCode, gateCode });
return apiRequest(url);
}
export async function apiGetDaily({ startDate, endDate, locationCode }) {
@@ -157,7 +213,9 @@ export async function apiGetByCategory({ date, locationCode, gateCode }) {
location_code: locationCode,
gate_code: gateCode
});
return apiRequest(`/retribusi/v1/dashboard/by-category${qs}`);
const url = `/retribusi/v1/dashboard/by-category${qs}`;
console.log('[API] apiGetByCategory - URL:', url, 'Params:', { date, locationCode, gateCode });
return apiRequest(url);
}
// Ringkasan global harian (daily_summary)
@@ -169,7 +227,9 @@ export async function apiGetSummaryDaily(params = {}) {
// Ringkasan per jam (hourly_summary)
export async function apiGetSummaryHourly(params = {}) {
const qs = buildQuery(params);
return apiRequest(`/retribusi/v1/summary/hourly${qs}`);
const url = `/retribusi/v1/summary/hourly${qs}`;
console.log('[API] apiGetSummaryHourly - URL:', url, 'Params:', params);
return apiRequest(url);
}
// Snapshot realtime (untuk panel live / TV wall)
@@ -202,3 +262,55 @@ export async function apiGetEvents(params = {}) {
// Catatan: realtime SSE /retribusi/v1/realtime/stream akan diakses langsung via EventSource,
// bukan lewat fetch/apiRequest karena menggunakan Server-Sent Events (SSE).
// Test function untuk debug - bandingkan URL yang dihasilkan untuk tanggal berbeda
// Bisa dipanggil dari browser console: window.testDateUrls('2025-12-31', '2026-01-01')
if (typeof window !== 'undefined') {
window.testDateUrls = function(date1, date2) {
console.log('=== TEST: Membandingkan URL untuk tanggal berbeda ===');
const testParams = {
date: date1 || '2025-12-31',
locationCode: '',
gateCode: ''
};
const testParams2 = {
date: date2 || '2026-01-01',
locationCode: '',
gateCode: ''
};
console.log('\n--- Tanggal 1:', testParams.date, '---');
const qs1 = buildQuery(testParams);
console.log('Query string:', qs1);
console.log('Full URL:', `${API_CONFIG.BASE_URL}/retribusi/v1/dashboard/summary${qs1}`);
console.log('\n--- Tanggal 2:', testParams2.date, '---');
const qs2 = buildQuery(testParams2);
console.log('Query string:', qs2);
console.log('Full URL:', `${API_CONFIG.BASE_URL}/retribusi/v1/dashboard/summary${qs2}`);
console.log('\n--- Perbandingan ---');
console.log('Query string sama?', qs1 === qs2);
console.log('Date param sama?', testParams.date === testParams2.date);
console.log('Date length sama?', testParams.date.length === testParams2.date.length);
// Test URLSearchParams parsing
const params1 = new URLSearchParams(qs1.substring(1));
const params2 = new URLSearchParams(qs2.substring(1));
console.log('Parsed date 1:', params1.get('date'));
console.log('Parsed date 2:', params2.get('date'));
console.log('Parsed dates sama?', params1.get('date') === params2.get('date'));
return {
date1: testParams.date,
date2: testParams2.date,
url1: `${API_CONFIG.BASE_URL}/retribusi/v1/dashboard/summary${qs1}`,
url2: `${API_CONFIG.BASE_URL}/retribusi/v1/dashboard/summary${qs2}`,
qs1,
qs2,
areEqual: qs1 === qs2
};
};
}