document.addEventListener('DOMContentLoaded', function() {
const grandTotalDisplay = document.getElementById('grand-total-timbangan');
const grandTotalOrganikDisplay = document.getElementById('grand-total-organik');
const grandTotalAnorganikDisplay = document.getElementById('grand-total-anorganik');
const grandTotalResiduDisplay = document.getElementById('grand-total-residu');
const tpsContentContainer = document.getElementById('tps-content');
let activeTpsIndex = 0;
let tpsData = [];
let nomorSpj = 'SPJ/07-2025/PKM/000476';
const OCR_AREAS = [
{ id: 'A', x: 0.34, y: 0.35, w: 0.40, h: 0.11, color: 'border-lime-400 bg-lime-500/15' },
{ id: 'B', x: 0.31, y: 0.33, w: 0.45, h: 0.14, color: 'border-amber-300 bg-amber-400/10' },
{ id: 'C', x: 0.29, y: 0.31, w: 0.49, h: 0.17, color: 'border-cyan-300 bg-cyan-400/10' }
];
const JENIS_SAMPAH = ['Organik', 'Anorganik', 'Residu'];
const DEFAULT_JENIS = 'Residu';
const DETAIL_DATA_URL = '/driver/json/detail-penjemputan-non-tps.json';
const DEFAULT_TPS_NAME = 'Lokasi Pengangkutan 1';
function initializeLocation() {
tpsData = [{
name: DEFAULT_TPS_NAME,
index: 0,
lokasiAngkutId: '',
spjDetailId: '',
latitude: '',
longitude: '',
alamatJalan: '',
waktuKedatangan: '',
fotoKedatangan: [],
fotoKedatanganUploaded: false,
timbangan: [],
totalOrganik: 0,
totalAnorganik: 0,
totalResidu: 0,
totalTimbangan: 0,
fotoPetugas: [],
fotoPetugasUploaded: false,
namaPetugas: '',
submitted: false
}];
renderTpsForm();
}
async function loadDetailData() {
try {
const response = await fetch(DETAIL_DATA_URL, { cache: 'no-store' });
if (!response.ok) {
return;
}
const payload = await response.json();
const detail = payload.detailPenjemputan || payload;
const namaTps = detail.namaTps || detail.tpsName || detail.name || DEFAULT_TPS_NAME;
const namaPerusahaan = detail.namaPerusahaan || detail.companyName || '';
if (tpsData[0]) {
tpsData[0].name = namaTps;
tpsData[0].lokasiAngkutId = detail.lokasiAngkutId || detail.LokasiAngkutID || tpsData[0].lokasiAngkutId;
tpsData[0].spjDetailId = detail.spjDetailId || detail.SpjDetailID || tpsData[0].spjDetailId;
}
nomorSpj = detail.nomorSpj || nomorSpj;
applyDetailDataToView(detail, namaTps, namaPerusahaan);
renderTpsForm();
} catch (error) {
console.warn('Gagal memuat detail penjemputan non-TPS:', error);
}
}
function applyDetailDataToView(detail, namaTps, namaPerusahaan) {
const titleEl = document.getElementById('detail-page-title');
const badgeEl = document.getElementById('detail-tps-badge');
const formBadgeEl = document.getElementById('detail-form-badge');
const companyEl = document.getElementById('detail-company-name');
const spjEl = document.getElementById('detail-spj-number');
const addressEl = document.getElementById('detail-address');
const platEl = document.getElementById('plat-nomor');
const doorEl = document.getElementById('detail-nomor-pintu');
if (titleEl) titleEl.textContent = namaTps;
if (badgeEl) badgeEl.textContent = namaTps;
if (formBadgeEl) formBadgeEl.textContent = namaTps;
if (companyEl && namaPerusahaan) companyEl.textContent = namaPerusahaan;
if (spjEl && detail.nomorSpj) spjEl.textContent = detail.nomorSpj;
if (addressEl && detail.alamat) addressEl.textContent = detail.alamat;
if (platEl && detail.platNomor) platEl.textContent = detail.platNomor;
if (doorEl && detail.nomorPintu) doorEl.textContent = detail.nomorPintu;
document.title = `Detail Penjemputan - ${namaTps}`;
}
function renderTpsForm() {
const tps = tpsData[activeTpsIndex];
const submitState = getSubmitState(tps);
tpsContentContainer.innerHTML = `
`;
attachTpsFormListeners();
restoreTpsTimbanganItems();
restorePhotoPreview();
}
function restorePhotoPreview() {
const tps = tpsData[activeTpsIndex];
const form = tpsContentContainer.querySelector('form');
if (!form) return;
const previewKedatangan = form.querySelector('.tps-preview-kedatangan');
if (previewKedatangan && tps.fotoKedatangan.length > 0) {
renderStoredPhotos(tps.fotoKedatangan, previewKedatangan);
}
const previewPetugas = form.querySelector('.tps-preview-petugas');
if (previewPetugas && tps.fotoPetugas.length > 0) {
renderStoredPhotos(tps.fotoPetugas, previewPetugas);
}
}
function renderStoredPhotos(files, container) {
container.innerHTML = '';
container.className = 'space-y-2';
files.forEach((file, index) => {
const item = document.createElement('div');
item.className = 'rounded-xl border border-gray-200 overflow-hidden bg-black';
const imageUrl = URL.createObjectURL(file);
const safeName = file.name.replace(/"/g, '"');
item.innerHTML = `
${index + 1}. ${safeName}
${formatFileSize(file.size)}
`;
const img = item.querySelector('.preview-multi-image');
if (img) {
img.onload = function() {
URL.revokeObjectURL(imageUrl);
};
}
container.appendChild(item);
});
}
function attachTpsFormListeners() {
const form = tpsContentContainer.querySelector('form');
const tps = tpsData[activeTpsIndex];
const fotoKedatanganInput = form.querySelector('.tps-foto-kedatangan');
const fotoPetugasInput = form.querySelector('.tps-foto-petugas');
const namaPetugasInput = form.querySelector('.tps-nama-petugas');
const btnAddTimbangan = form.querySelector('.tps-btn-add-timbangan');
fotoKedatanganInput.addEventListener('change', function() {
tps.fotoKedatangan = Array.from(this.files);
tps.fotoKedatanganUploaded = false;
updateWaktuKedatangan();
updateMultiPreview(this, form.querySelector('.tps-preview-kedatangan'));
refreshKedatanganUploadState(form);
});
fotoPetugasInput.addEventListener('change', function() {
tps.fotoPetugas = Array.from(this.files);
tps.fotoPetugasUploaded = false;
updateMultiPreview(this, form.querySelector('.tps-preview-petugas'));
refreshPetugasUploadState(form);
});
namaPetugasInput.addEventListener('input', function() {
tps.namaPetugas = this.value;
refreshPetugasUploadState(form);
});
btnAddTimbangan.addEventListener('click', function() {
createTimbanganItem(form.querySelector('.tps-timbangan-repeater'));
syncTimbanganToTpsData();
refreshSubmitButtonState(form);
});
const btnUploadKedatangan = form.querySelector('.tps-btn-upload-kedatangan');
if (btnUploadKedatangan) {
btnUploadKedatangan.addEventListener('click', uploadFotoKedatangan);
}
const btnUploadPetugas = form.querySelector('.tps-btn-upload-petugas');
if (btnUploadPetugas) {
btnUploadPetugas.addEventListener('click', uploadFotoPetugas);
}
form.addEventListener('submit', function(e) {
e.preventDefault();
submitTpsData();
});
}
function restoreTpsTimbanganItems() {
const tps = tpsData[activeTpsIndex];
const form = tpsContentContainer.querySelector('form');
const repeater = form.querySelector('.tps-timbangan-repeater');
if (tps.timbangan.length === 0) {
createTimbanganItem(repeater);
} else {
tps.timbangan.forEach(timb => createTimbanganItem(repeater, timb));
}
}
function updateWaktuKedatangan() {
const tps = tpsData[activeTpsIndex];
const now = new Date();
const formatted = now.toLocaleString('id-ID', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
tps.waktuKedatangan = formatted;
const form = tpsContentContainer.querySelector('form');
const displayWaktu = form.querySelector('.tps-waktu-kedatangan');
if (displayWaktu) displayWaktu.value = formatted;
getLocationUpdate();
}
function reverseGeocode(lat, lng) {
const tps = tpsData[activeTpsIndex];
fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}`)
.then(res => res.json())
.then(data => {
const address = data.display_name || `${lat}, ${lng}`;
tps.latitude = lat;
tps.longitude = lng;
tps.alamatJalan = address;
const form = tpsContentContainer.querySelector('form');
if (form) {
const latInput = form.querySelector('.tps-display-latitude');
const lngInput = form.querySelector('.tps-display-longitude');
if (latInput) latInput.value = lat;
if (lngInput) lngInput.value = lng;
}
})
.catch(() => {
tps.latitude = lat;
tps.longitude = lng;
tps.alamatJalan = `${lat}, ${lng}`;
const form = tpsContentContainer.querySelector('form');
if (form) {
const latInput = form.querySelector('.tps-display-latitude');
const lngInput = form.querySelector('.tps-display-longitude');
if (latInput) latInput.value = lat;
if (lngInput) lngInput.value = lng;
}
});
}
function getLocationUpdate() {
if (!('geolocation' in navigator)) return;
navigator.geolocation.getCurrentPosition(
function(position) {
const lat = position.coords.latitude.toFixed(6);
const lng = position.coords.longitude.toFixed(6);
reverseGeocode(lat, lng);
},
function() {
console.log('Lokasi tidak diizinkan');
}
);
}
function formatFileSize(bytes) {
const mb = bytes / (1024 * 1024);
return `${mb.toFixed(2)} MB`;
}
function updateMultiPreview(input, previewContainer) {
if (!input || !previewContainer) return;
previewContainer.innerHTML = '';
previewContainer.className = 'space-y-2';
if (!input.files || input.files.length === 0) return;
Array.from(input.files).forEach((file, index) => {
const item = document.createElement('div');
item.className = 'rounded-xl border border-gray-200 overflow-hidden bg-black';
const imageUrl = URL.createObjectURL(file);
const safeName = file.name.replace(/"/g, '"');
item.innerHTML = `
${index + 1}. ${safeName}
${formatFileSize(file.size)}
`;
const img = item.querySelector('.preview-multi-image');
if (img) {
img.onload = function() {
URL.revokeObjectURL(imageUrl);
};
}
previewContainer.appendChild(item);
});
}
function formatWeightDisplay(value) {
if (isNaN(value)) return '0,00';
return value.toFixed(2).replace('.', ',');
}
function parseWeightInput(value) {
if (!value) return 0;
const cleaned = value.toString().trim().replace(/\s/g, '').replace(',', '.');
const parsed = parseFloat(cleaned);
return isNaN(parsed) ? 0 : parsed;
}
async function applyWatermark(file, photoNumber) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function(e) {
const img = new Image();
img.onload = function() {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const now = new Date();
const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'];
const timestamp = `${days[now.getDay()]}, ${now.getDate().toString().padStart(2, '0')} ${months[now.getMonth()]} ${now.getFullYear()} • ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
const baseFontSize = Math.max(16, Math.min(img.width, img.height) * 0.025);
const fontFamily = "'Montserrat', 'Segoe UI', 'Roboto', sans-serif";
const lines = [
{ text: `FOTO TIMBANG #${photoNumber}`, size: baseFontSize * 1.1, weight: '900', color: '#FFD700' },
{ text: `${nomorSpj}`, size: baseFontSize * 0.9, weight: '700', color: '#FFFFFF' },
{ text: timestamp, size: baseFontSize * 0.75, weight: '500', color: '#E2E8F0' }
];
const paddingX = baseFontSize * 1.2;
const paddingY = baseFontSize * 1.0;
const lineGap = baseFontSize * 0.4;
let maxWidth = 0;
let totalHeight = 0;
lines.forEach(line => {
ctx.font = `${line.weight} ${line.size}px ${fontFamily}`;
const metrics = ctx.measureText(line.text);
maxWidth = Math.max(maxWidth, metrics.width);
totalHeight += line.size + lineGap;
});
totalHeight -= lineGap;
const margin = baseFontSize * 1.5;
const boxWidth = maxWidth + (paddingX * 2);
const boxHeight = totalHeight + (paddingY * 2);
const boxX = img.width - boxWidth - margin;
const boxY = img.height - boxHeight - margin;
ctx.beginPath();
if (ctx.roundRect) {
ctx.roundRect(boxX, boxY, boxWidth, boxHeight, baseFontSize * 0.8);
} else {
ctx.rect(boxX, boxY, boxWidth, boxHeight);
}
ctx.fillStyle = 'rgba(15, 23, 42, 0.85)';
ctx.fill();
const accentWidth = baseFontSize * 0.3;
ctx.beginPath();
if (ctx.roundRect) {
ctx.roundRect(boxX + boxWidth - accentWidth, boxY, accentWidth, boxHeight, [0, baseFontSize * 0.8, baseFontSize * 0.8, 0]);
} else {
ctx.rect(boxX + boxWidth - accentWidth, boxY, accentWidth, boxHeight);
}
ctx.fillStyle = '#FFD700';
ctx.fill();
ctx.textAlign = 'right';
ctx.textBaseline = 'top';
let currentY = boxY + paddingY;
const textRightLimit = boxX + boxWidth - paddingX - accentWidth;
lines.forEach(line => {
ctx.font = `${line.weight} ${line.size}px ${fontFamily}`;
ctx.fillStyle = line.color;
ctx.fillText(line.text, textRightLimit, currentY);
currentY += line.size + lineGap;
});
canvas.toBlob(function(blob) {
const watermarkedFile = new File([blob], file.name, {
type: 'image/jpeg',
lastModified: Date.now()
});
resolve(watermarkedFile);
}, 'image/jpeg', 0.95);
};
img.onerror = reject;
img.src = e.target.result;
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
function readFileAsImage(file) {
return new Promise(function(resolve, reject) {
const objectUrl = URL.createObjectURL(file);
const img = new Image();
img.onload = function() {
URL.revokeObjectURL(objectUrl);
resolve(img);
};
img.onerror = function() {
URL.revokeObjectURL(objectUrl);
reject(new Error('Gagal membaca gambar.'));
};
img.src = objectUrl;
});
}
function createCropCanvas(img, area) {
const sx = Math.max(0, Math.floor(img.width * area.x));
const sy = Math.max(0, Math.floor(img.height * area.y));
const sw = Math.max(1, Math.floor(img.width * area.w));
const sh = Math.max(1, Math.floor(img.height * area.h));
const canvas = document.createElement('canvas');
const scale = 2.2;
canvas.width = Math.max(1, Math.floor(sw * scale));
canvas.height = Math.max(1, Math.floor(sh * scale));
const ctx = canvas.getContext('2d');
if (!ctx) return canvas;
ctx.imageSmoothingEnabled = true;
ctx.drawImage(img, sx, sy, sw, sh, 0, 0, canvas.width, canvas.height);
return canvas;
}
function canvasToJpegFile(canvas, fileName) {
return new Promise(function(resolve, reject) {
canvas.toBlob(function(blob) {
if (!blob) {
reject(new Error('Gagal membuat crop image.'));
return;
}
resolve(new File([blob], fileName, { type: 'image/jpeg' }));
}, 'image/jpeg', 0.92);
});
}
async function requestOpenRouterWeight(imageFile) {
const formData = new FormData();
formData.append('Foto', imageFile);
const response = await fetch('/upst/detail-penjemputan/ocr-timbangan', { method: 'POST', body: formData });
const result = await response.json();
if (!response.ok) throw new Error(result.message || 'Request OCR gagal.');
return result;
}
async function autoFillWeight(file, weightInput, ocrInfoEl) {
let guessedWeight = 0;
weightInput.placeholder = 'Membaca angka dari foto...';
if (ocrInfoEl) ocrInfoEl.textContent = 'AI: memproses gambar...';
try {
const img = await readFileAsImage(file);
let bestRawText = '';
let isSuccess = false;
for (const area of OCR_AREAS) {
const cropCanvas = createCropCanvas(img, area);
const cropFile = await canvasToJpegFile(cropCanvas, `crop-${area.id}.jpg`);
const aiResult = await requestOpenRouterWeight(cropFile);
if (aiResult && aiResult.success && aiResult.weight) {
guessedWeight = parseWeightInput(aiResult.weight);
bestRawText = aiResult.raw || aiResult.weight;
isSuccess = guessedWeight > 0;
if (isSuccess) break;
}
if (aiResult && aiResult.raw) bestRawText = aiResult.raw;
}
if (ocrInfoEl) {
const cleaned = (bestRawText || '').replace(/\s+/g, ' ').trim();
ocrInfoEl.textContent = isSuccess
? `AI terbaca: ${cleaned}`
: (cleaned ? `AI tidak valid: ${cleaned}` : 'AI tidak menemukan angka valid.');
}
} catch (_) {
guessedWeight = 0;
if (ocrInfoEl) ocrInfoEl.textContent = 'AI gagal diproses.';
}
if (guessedWeight > 0) {
weightInput.value = formatWeightDisplay(guessedWeight);
weightInput.placeholder = 'Berat terdeteksi otomatis';
} else {
weightInput.placeholder = 'Tidak terbaca otomatis, isi manual';
}
updateTpsTotalTimbangan();
}
function updateTpsTotalTimbangan() {
const tps = tpsData[activeTpsIndex];
const form = tpsContentContainer.querySelector('form');
if (!form) return;
let totalOrganik = 0.0;
let totalAnorganik = 0.0;
let totalResidu = 0.0;
const repeater = form.querySelector('.tps-timbangan-repeater');
const items = repeater.querySelectorAll('.timbangan-item');
items.forEach(function(item) {
const weightInput = item.querySelector('.input-berat-timbangan-value');
const jenisSampahSelect = item.querySelector('.input-jenis-sampah');
if (weightInput && jenisSampahSelect) {
const value = parseWeightInput(weightInput.value || '0');
const jenis = jenisSampahSelect.value;
if (jenis === 'Organik') totalOrganik += value;
else if (jenis === 'Anorganik') totalAnorganik += value;
else totalResidu += value;
}
});
tps.totalOrganik = totalOrganik;
tps.totalAnorganik = totalAnorganik;
tps.totalResidu = totalResidu;
tps.totalTimbangan = totalOrganik + totalAnorganik + totalResidu;
const displayTotal = form.querySelector('.tps-display-total');
if (displayTotal) displayTotal.textContent = formatWeightDisplay(tps.totalTimbangan);
if (grandTotalDisplay) grandTotalDisplay.textContent = formatWeightDisplay(tps.totalTimbangan);
if (grandTotalOrganikDisplay) grandTotalOrganikDisplay.textContent = formatWeightDisplay(totalOrganik);
if (grandTotalAnorganikDisplay) grandTotalAnorganikDisplay.textContent = formatWeightDisplay(totalAnorganik);
if (grandTotalResiduDisplay) grandTotalResiduDisplay.textContent = formatWeightDisplay(totalResidu);
}
function getTimbanganUploadStateMarkup(hasFile, isUploaded, hasValidWeight) {
if (!hasFile) {
return 'Pilih foto timbangan terlebih dahulu
';
}
if (isUploaded) {
return `
✓ Foto timbangan sudah diupload
Jika ingin revisi, pilih file baru diatas. Status upload akan tereset otomatis.
`;
}
if (!hasValidWeight) {
return `
Isi berat manual dulu sebelum upload jika berat tidak terbaca.
`;
}
return `
Foto siap diupload.
`;
}
function getKedatanganUploadStateMarkup(tps) {
if (tps.fotoKedatanganUploaded) {
return '✓ Foto kedatangan sudah diupload
';
}
if (!tps.fotoKedatangan.length) {
return '';
}
return ``;
}
function refreshKedatanganUploadState(form) {
const stateContainer = form.querySelector('.kedatangan-upload-state');
if (!stateContainer) return;
const tps = tpsData[activeTpsIndex];
stateContainer.innerHTML = getKedatanganUploadStateMarkup(tps);
const btnUploadKedatangan = stateContainer.querySelector('.tps-btn-upload-kedatangan');
if (btnUploadKedatangan) {
btnUploadKedatangan.addEventListener('click', uploadFotoKedatangan);
}
refreshSubmitButtonState(form);
}
function getPetugasUploadStateMarkup(tps) {
if (tps.fotoPetugasUploaded) {
return '✓ Foto petugas sudah diupload
';
}
if (!tps.fotoPetugas.length) {
return '';
}
if (!tps.namaPetugas.trim()) {
return `
Isi nama petugas terlebih dahulu
`;
}
return ``;
}
function refreshPetugasUploadState(form) {
const stateContainer = form.querySelector('.petugas-upload-state');
if (!stateContainer) return;
const tps = tpsData[activeTpsIndex];
stateContainer.innerHTML = getPetugasUploadStateMarkup(tps);
const btnUploadPetugas = stateContainer.querySelector('.tps-btn-upload-petugas:not([disabled])');
if (btnUploadPetugas) {
btnUploadPetugas.addEventListener('click', uploadFotoPetugas);
}
refreshSubmitButtonState(form);
}
function isTimbanganItemReady(timbanganItem) {
const weight = timbanganItem?.berat && timbanganItem.berat.length > 0 ? timbanganItem.berat[0] : 0;
return Boolean(timbanganItem?.file) && Boolean(timbanganItem?.uploaded) && weight > 0;
}
function getSubmitState(tps) {
if (!tps.fotoKedatangan.length || !tps.fotoKedatanganUploaded) {
return { canSubmit: false, message: 'Silakan upload foto kedatangan terlebih dahulu.' };
}
if (!tps.timbangan.length) {
return { canSubmit: false, message: 'Tambahkan minimal 1 data timbangan sebelum submit.' };
}
if (tps.timbangan.some(item => !isTimbanganItemReady(item))) {
return { canSubmit: false, message: 'Pastikan semua foto timbangan sudah diupload dan beratnya valid.' };
}
if (!tps.fotoPetugas.length || !tps.fotoPetugasUploaded) {
return { canSubmit: false, message: 'Silakan upload foto petugas terlebih dahulu' };
}
if (!tps.namaPetugas.trim()) {
return { canSubmit: false, message: 'Isi nama petugas dulu sebelum submit.' };
}
return { canSubmit: true, message: '' };
}
function refreshSubmitButtonState(form) {
const submitButton = form.querySelector('button[type="submit"]');
if (!submitButton) return;
const helperText = form.querySelector('.submit-state-message');
const tps = tpsData[activeTpsIndex];
const submitState = getSubmitState(tps);
submitButton.disabled = !submitState.canSubmit;
submitButton.className = `w-2/3 py-3 rounded-xl font-bold text-sm transition ${submitState.canSubmit ? 'bg-upst text-white hover:brightness-110' : 'bg-gray-300 text-gray-500 cursor-not-allowed'}`;
let messageEl = helperText;
if (!messageEl) {
messageEl = document.createElement('p');
messageEl.className = 'submit-state-message text-[11px] text-center text-red-500 font-medium';
submitButton.closest('.flex.gap-3')?.insertAdjacentElement('afterend', messageEl);
}
if (submitState.canSubmit) {
messageEl.textContent = '';
messageEl.classList.add('hidden');
} else {
messageEl.textContent = submitState.message;
messageEl.classList.remove('hidden');
}
}
function refreshTimbanganUploadState(item) {
const stateContainer = item.querySelector('.timbangan-upload-state');
if (!stateContainer) return;
const repeater = item.parentElement;
const itemIndex = repeater ? Array.from(repeater.children).indexOf(item) : -1;
const tps = tpsData[activeTpsIndex];
const currentData = itemIndex >= 0 ? tps.timbangan[itemIndex] : null;
const fileInput = item.querySelector('.input-foto-timbangan');
const hasFile = Boolean(currentData?.file || fileInput?.files?.[0]);
const isUploaded = Boolean(currentData?.uploaded);
const weightInputValue = item.querySelector('.input-berat-timbangan-value');
const currentWeight = currentData?.berat && currentData.berat.length > 0
? currentData.berat[0]
: parseWeightInput(weightInputValue?.value || '0');
const hasValidWeight = currentWeight > 0;
stateContainer.innerHTML = getTimbanganUploadStateMarkup(hasFile, isUploaded, hasValidWeight);
const uploadBtn = stateContainer.querySelector('.btn-upload-timbangan');
if (uploadBtn) {
uploadBtn.addEventListener('click', function() {
const latestIndex = repeater ? Array.from(repeater.children).indexOf(item) : -1;
uploadSingleFotoTimbangan(latestIndex, item);
});
}
const form = tpsContentContainer.querySelector('form');
if (form) {
refreshSubmitButtonState(form);
}
}
function renumberTimbanganItems(repeater) {
const items = repeater.querySelectorAll('.timbangan-item');
items.forEach((item, index) => {
const newNumber = index + 1;
item.dataset.photoNumber = newNumber;
const label = item.querySelector('.text-xs.font-bold.text-gray-600');
if (label) {
label.textContent = `Item Timbangan #${newNumber}`;
}
});
}
function createTimbanganItem(repeater, existingData = null) {
const photoNumber = repeater.children.length + 1;
const item = document.createElement('div');
item.className = 'timbangan-item rounded-2xl border border-gray-200 p-3 space-y-2 bg-gray-50';
item.dataset.photoNumber = photoNumber;
const weight = existingData ? (existingData.berat && existingData.berat.length > 0 ? existingData.berat[0] : 0) : 0;
const jenisSampah = existingData ? (existingData.jenisSampah && existingData.jenisSampah.length > 0 ? existingData.jenisSampah[0] : DEFAULT_JENIS) : DEFAULT_JENIS;
const hasFile = existingData && existingData.file;
const isUploaded = existingData && existingData.uploaded;
const ocrInfoText = existingData && existingData.ocrInfo ? existingData.ocrInfo : (hasFile ? 'OCR: diproses.' : 'OCR: belum diproses.');
item.innerHTML = `
Item Timbangan #${photoNumber}
${ocrInfoText}
${getTimbanganUploadStateMarkup(hasFile, isUploaded, weight > 0)}
`;
const fileInput = item.querySelector('.input-foto-timbangan');
const previewWrap = item.querySelector('.input-preview-wrap');
const previewImage = item.querySelector('.input-preview-image');
const ocrInfoEl = item.querySelector('.input-ocr-info');
const weightInputDisplay = item.querySelector('.input-berat-timbangan-display');
const weightInputValue = item.querySelector('.input-berat-timbangan-value');
const jenisSampahSelect = item.querySelector('.input-jenis-sampah');
const removeBtn = item.querySelector('.btn-remove-timbangan');
if (existingData && existingData.file) {
const localUrl = URL.createObjectURL(existingData.file);
previewImage.src = localUrl;
previewImage.onload = function() { URL.revokeObjectURL(localUrl); };
}
fileInput.addEventListener('change', async function() {
if (fileInput.files && fileInput.files[0]) {
const originalFile = fileInput.files[0];
const photoNumber = Number(item.dataset.photoNumber || (Array.from(repeater.children).indexOf(item) + 1));
const watermarkedFile = await applyWatermark(originalFile, photoNumber);
const dataTransfer = new DataTransfer();
dataTransfer.items.add(watermarkedFile);
fileInput.files = dataTransfer.files;
const localUrl = URL.createObjectURL(watermarkedFile);
previewImage.src = localUrl;
previewWrap.classList.remove('hidden');
previewImage.onload = function() { URL.revokeObjectURL(localUrl); };
await autoFillWeight(watermarkedFile, weightInputDisplay, ocrInfoEl);
const parsed = parseWeightInput(weightInputDisplay.value);
weightInputValue.value = parsed.toFixed(2);
updateTpsTotalTimbangan();
syncTimbanganToTpsData();
const tps = tpsData[activeTpsIndex];
const itemIndex = Array.from(repeater.children).indexOf(item);
if (itemIndex >= 0 && tps.timbangan[itemIndex]) {
tps.timbangan[itemIndex].uploaded = false;
refreshTimbanganUploadState(item);
}
}
});
weightInputDisplay.addEventListener('input', function() {
const cleaned = this.value.replace(/[^0-9.,]/g, '');
this.value = cleaned;
const parsed = parseWeightInput(cleaned);
weightInputValue.value = parsed.toFixed(2);
updateTpsTotalTimbangan();
syncTimbanganToTpsData();
refreshTimbanganUploadState(item);
const form = tpsContentContainer.querySelector('form');
if (form) refreshSubmitButtonState(form);
});
weightInputDisplay.addEventListener('blur', function() {
const parsed = parseWeightInput(this.value);
if (parsed > 0) {
this.value = formatWeightDisplay(parsed);
weightInputValue.value = parsed.toFixed(2);
} else {
this.value = '';
weightInputValue.value = '0.00';
}
updateTpsTotalTimbangan();
syncTimbanganToTpsData();
refreshTimbanganUploadState(item);
const form = tpsContentContainer.querySelector('form');
if (form) refreshSubmitButtonState(form);
});
jenisSampahSelect.addEventListener('change', function() {
updateTpsTotalTimbangan();
syncTimbanganToTpsData();
const form = tpsContentContainer.querySelector('form');
if (form) refreshSubmitButtonState(form);
});
removeBtn.addEventListener('click', function() {
item.remove();
const form = tpsContentContainer.querySelector('form');
const rep = form ? form.querySelector('.tps-timbangan-repeater') : null;
if (rep) {
renumberTimbanganItems(rep);
if (rep.children.length === 0) createTimbanganItem(rep);
}
updateTpsTotalTimbangan();
syncTimbanganToTpsData();
if (form) refreshSubmitButtonState(form);
});
repeater.appendChild(item);
refreshTimbanganUploadState(item);
return item;
}
function syncTimbanganToTpsData() {
const tps = tpsData[activeTpsIndex];
const form = tpsContentContainer.querySelector('form');
if (!form) return;
const repeater = form.querySelector('.tps-timbangan-repeater');
const items = repeater.querySelectorAll('.timbangan-item');
const previousTimbangan = [...tps.timbangan];
tps.timbangan = [];
items.forEach((item, index) => {
const fileInput = item.querySelector('.input-foto-timbangan');
const weightValue = item.querySelector('.input-berat-timbangan-value');
const weight = parseWeightInput(weightValue.value);
const jenisSampah = item.querySelector('.input-jenis-sampah').value;
const ocrInfo = item.querySelector('.input-ocr-info')?.textContent || 'OCR: belum diproses.';
tps.timbangan.push({
file: fileInput.files[0] || previousTimbangan[index]?.file || null,
berat: [weight],
jenisSampah: [jenisSampah],
lokasiAngkut: [],
uploaded: previousTimbangan[index]?.uploaded ?? false,
ocrInfo
});
});
}
function buildSubmitFormData(tps) {
const formData = new FormData();
formData.append('LokasiAngkutID', tps.lokasiAngkutId || '');
formData.append('SpjDetailID', tps.spjDetailId || '');
formData.append('Latitude', tps.latitude);
formData.append('Longitude', tps.longitude);
formData.append('AlamatJalan', tps.alamatJalan);
formData.append('WaktuKedatangan', tps.waktuKedatangan);
formData.append('TotalTimbangan', tps.totalTimbangan);
formData.append('TotalOrganik', tps.totalOrganik);
formData.append('TotalAnorganik', tps.totalAnorganik);
formData.append('TotalResidu', tps.totalResidu);
formData.append('NamaPetugas', tps.namaPetugas);
tps.fotoKedatangan.forEach((file) => formData.append('FotoKedatangan', file));
tps.timbangan.forEach((timb) => {
if (timb.file) formData.append('FotoTimbangan', timb.file);
const weight = timb.berat && timb.berat.length > 0 ? timb.berat[0] : 0;
const jenis = timb.jenisSampah && timb.jenisSampah.length > 0 ? timb.jenisSampah[0] : DEFAULT_JENIS;
formData.append('BeratTimbangan', weight);
formData.append('JenisSampahList', jenis);
});
tps.fotoPetugas.forEach((file) => formData.append('FotoPetugas', file));
const antiForgeryTokenEl = document.querySelector('#upst-antiforgery input[name="__RequestVerificationToken"]');
if (antiForgeryTokenEl && antiForgeryTokenEl.value) {
formData.append('__RequestVerificationToken', antiForgeryTokenEl.value);
}
return formData;
}
function uploadSingleFotoTimbangan(itemIndex, targetItem = null) {
const tps = tpsData[activeTpsIndex];
if (!tps.timbangan[itemIndex] || !tps.timbangan[itemIndex].file) {
alert('Belum ada foto timbangan yang dipilih!');
return;
}
const weight = tps.timbangan[itemIndex].berat && tps.timbangan[itemIndex].berat.length > 0
? tps.timbangan[itemIndex].berat[0]
: 0;
if (weight <= 0) {
alert('Berat belum valid. Isi manual dulu sebelum upload foto timbangan.');
return;
}
const _ext = (tps.timbangan[itemIndex].file.name.split('.').pop() || 'jpg').toLowerCase();
const _jenis = (tps.timbangan[itemIndex].jenisSampah[0] || 'residu').toLowerCase();
const _beratStr = parseFloat(weight.toFixed(2)).toString().replace('.', '_');
const _newName = `timbangan${itemIndex + 1}-${_jenis}-${_beratStr}.${_ext}`;
tps.timbangan[itemIndex].file = new File([tps.timbangan[itemIndex].file], _newName, { type: tps.timbangan[itemIndex].file.type, lastModified: tps.timbangan[itemIndex].file.lastModified });
alert(`Upload foto timbangan #${itemIndex + 1}\nBerat: ${formatWeightDisplay(weight)} kg\n(Implementasi upload ke server)`);
tps.timbangan[itemIndex].uploaded = true;
if (!targetItem) {
const form = tpsContentContainer.querySelector('form');
const repeater = form ? form.querySelector('.tps-timbangan-repeater') : null;
const items = repeater ? repeater.querySelectorAll('.timbangan-item') : [];
targetItem = items[itemIndex] || null;
}
if (targetItem) {
refreshTimbanganUploadState(targetItem);
}
syncTimbanganToTpsData();
const form = tpsContentContainer.querySelector('form');
if (form) refreshSubmitButtonState(form);
}
function uploadFotoKedatangan() {
const tps = tpsData[activeTpsIndex];
if (tps.fotoKedatangan.length === 0) {
alert('Belum ada foto kedatangan yang dipilih!');
return;
}
alert(`Upload ${tps.fotoKedatangan.length} foto kedatangan\n(Implementasi upload ke server)`);
tps.fotoKedatanganUploaded = true;
const form = tpsContentContainer.querySelector('form');
if (form) refreshKedatanganUploadState(form);
}
function uploadFotoPetugas() {
const tps = tpsData[activeTpsIndex];
if (tps.fotoPetugas.length === 0) {
alert('Belum ada foto petugas yang dipilih!');
return;
}
if (!tps.namaPetugas.trim()) {
alert('Nama petugas wajib diisi sebelum upload foto petugas!');
return;
}
alert(`Upload ${tps.fotoPetugas.length} foto petugas untuk ${tps.name}\n(Implementasi upload ke server)`);
tps.fotoPetugasUploaded = true;
const form = tpsContentContainer.querySelector('form');
if (form) refreshPetugasUploadState(form);
}
function submitTpsData() {
const tps = tpsData[activeTpsIndex];
const submitState = getSubmitState(tps);
if (!submitState.canSubmit) return alert(submitState.message);
alert(`Validasi OK (${tps.name}).\n- Organik: ${formatWeightDisplay(tps.totalOrganik)} kg\n- Anorganik: ${formatWeightDisplay(tps.totalAnorganik)} kg\n- Residu: ${formatWeightDisplay(tps.totalResidu)} kg\n- Total: ${formatWeightDisplay(tps.totalTimbangan)} kg\n- Petugas: ${tps.namaPetugas}`);
tps.submitted = true;
/*
const formData = buildSubmitFormData(tps);
fetch('/upst/detail-penjemputan', { method: 'POST', body: formData });
*/
}
const nomorSpjEl = document.querySelector('.text-gray-600.font-mono');
if (nomorSpjEl && nomorSpjEl.textContent) {
nomorSpj = nomorSpjEl.textContent.trim();
}
initializeLocation();
loadDetailData();
});