diff --git a/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Index.cshtml b/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Index.cshtml
index a4fce0a..c6f2fab 100644
--- a/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Index.cshtml
+++ b/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Index.cshtml
@@ -127,5 +127,5 @@
@section Scripts {
-
+
}
\ No newline at end of file
diff --git a/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/TanpaTps.cshtml b/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/TanpaTps.cshtml
index fd794b5..3e3efd0 100644
--- a/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/TanpaTps.cshtml
+++ b/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/TanpaTps.cshtml
@@ -113,1005 +113,6 @@
-
-
-
+@section Scripts {
+
+}
diff --git a/wwwroot/driver/js/detail-penjemputan-non-tps.js b/wwwroot/driver/js/detail-penjemputan-non-tps.js
new file mode 100644
index 0000000..caabdac
--- /dev/null
+++ b/wwwroot/driver/js/detail-penjemputan-non-tps.js
@@ -0,0 +1,854 @@
+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';
+
+ function initializeLocation() {
+ tpsData = [{
+ 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();
+ }
+
+ function renderTpsForm() {
+ const tps = tpsData[activeTpsIndex];
+
+ 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'));
+ renderTpsForm();
+ });
+
+ fotoPetugasInput.addEventListener('change', function() {
+ tps.fotoPetugas = Array.from(this.files);
+ tps.fotoPetugasUploaded = false;
+ updateMultiPreview(this, form.querySelector('.tps-preview-petugas'));
+ renderTpsForm();
+ });
+
+ namaPetugasInput.addEventListener('input', function() {
+ tps.namaPetugas = this.value;
+ });
+
+ btnAddTimbangan.addEventListener('click', function() {
+ createTimbanganItem(form.querySelector('.tps-timbangan-repeater'));
+ });
+
+ 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 OCR: 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 OCR terbaca: ${cleaned}`
+ : (cleaned ? `AI OCR tidak valid: ${cleaned}` : 'AI OCR tidak menemukan angka valid.');
+ }
+ } catch (_) {
+ guessedWeight = 0;
+ if (ocrInfoEl) ocrInfoEl.textContent = 'AI OCR 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 createTimbanganItem(repeater, existingData = null) {
+ const item = document.createElement('div');
+ item.className = 'timbangan-item rounded-2xl border border-gray-200 p-3 space-y-2 bg-gray-50';
+
+ const weight = existingData ? existingData.weight : 0;
+ const jenisSampah = existingData ? (existingData.jenisSampah || DEFAULT_JENIS) : DEFAULT_JENIS;
+ const hasFile = existingData && existingData.file;
+ const isUploaded = existingData && existingData.uploaded;
+
+ item.innerHTML = `
+
+
Item Timbangan
+
+
+
+
+ ${hasFile ? 'OCR: diproses.' : 'OCR: belum diproses.'}
+ ${hasFile && !isUploaded ? `
+
+ ` : isUploaded ? `✓ Foto timbangan sudah diupload
` : ''}
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ 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 = 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;
+ const existingUploadBtn = item.querySelector('.btn-upload-timbangan');
+ if (!existingUploadBtn) {
+ const ocrInfo = item.querySelector('.input-ocr-info');
+ const uploadBtn = document.createElement('button');
+ uploadBtn.type = 'button';
+ uploadBtn.className = 'btn-upload-timbangan w-full bg-blue-500 text-white py-2 rounded-xl font-bold text-xs hover:brightness-110';
+ uploadBtn.textContent = 'Upload Foto Timbangan Ini';
+ uploadBtn.addEventListener('click', function() { uploadSingleFotoTimbangan(itemIndex); });
+ ocrInfo.parentNode.insertBefore(uploadBtn, ocrInfo.nextSibling);
+ }
+ }
+ }
+ });
+
+ 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();
+ });
+
+ 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();
+ });
+
+ jenisSampahSelect.addEventListener('change', function() {
+ updateTpsTotalTimbangan();
+ syncTimbanganToTpsData();
+ });
+
+ removeBtn.addEventListener('click', function() {
+ item.remove();
+ const form = tpsContentContainer.querySelector('form');
+ const rep = form ? form.querySelector('.tps-timbangan-repeater') : null;
+ if (rep && rep.children.length === 0) createTimbanganItem(rep);
+ updateTpsTotalTimbangan();
+ syncTimbanganToTpsData();
+ });
+
+ const btnUploadTimbangan = item.querySelector('.btn-upload-timbangan');
+ if (btnUploadTimbangan) {
+ btnUploadTimbangan.addEventListener('click', function() {
+ const itemIndex = Array.from(repeater.children).indexOf(item);
+ uploadSingleFotoTimbangan(itemIndex);
+ });
+ }
+
+ repeater.appendChild(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');
+
+ tps.timbangan = [];
+ items.forEach(item => {
+ const fileInput = item.querySelector('.input-foto-timbangan');
+ const weightValue = item.querySelector('.input-berat-timbangan-value');
+ tps.timbangan.push({
+ file: fileInput.files[0] || null,
+ weight: parseWeightInput(weightValue.value),
+ jenisSampah: item.querySelector('.input-jenis-sampah').value,
+ uploaded: false
+ });
+ });
+ }
+
+ 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);
+ formData.append('BeratTimbangan', timb.weight);
+ formData.append('JenisSampahList', timb.jenisSampah || DEFAULT_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) {
+ const tps = tpsData[activeTpsIndex];
+ if (!tps.timbangan[itemIndex] || !tps.timbangan[itemIndex].file) {
+ alert('Belum ada foto timbangan yang dipilih!');
+ return;
+ }
+ alert(`Upload foto timbangan #${itemIndex + 1}\nBerat: ${tps.timbangan[itemIndex].weight} kg\n(Implementasi upload ke server)`);
+ tps.timbangan[itemIndex].uploaded = true;
+ }
+
+ 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;
+ renderTpsForm();
+ }
+
+ function uploadFotoPetugas() {
+ const tps = tpsData[activeTpsIndex];
+ if (tps.fotoPetugas.length === 0) {
+ alert('Belum ada foto petugas yang dipilih!');
+ return;
+ }
+ alert(`Upload ${tps.fotoPetugas.length} foto petugas\n(Implementasi upload ke server)`);
+ tps.fotoPetugasUploaded = true;
+ renderTpsForm();
+ }
+
+ function submitTpsData() {
+ const tps = tpsData[activeTpsIndex];
+ if (!tps.fotoKedatangan.length) return alert('Foto kedatangan belum diupload!');
+ if (!tps.timbangan.length) return alert('Belum ada data timbangan!');
+ if (!tps.fotoPetugas.length) return alert('Foto petugas belum diupload!');
+ if (!tps.namaPetugas.trim()) return alert('Nama petugas belum diisi!');
+
+ alert(`Validasi OK (Tanpa TPS).\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();
+});
diff --git a/wwwroot/driver/js/detail-penjemputan.js b/wwwroot/driver/js/detail-penjemputan-tps.js
similarity index 98%
rename from wwwroot/driver/js/detail-penjemputan.js
rename to wwwroot/driver/js/detail-penjemputan-tps.js
index 2ede325..9fed49c 100644
--- a/wwwroot/driver/js/detail-penjemputan.js
+++ b/wwwroot/driver/js/detail-penjemputan-tps.js
@@ -82,9 +82,11 @@ const DetailPenjemputan = (function() {
}
function initializeTpsData(tpsNames) {
- state.tpsData = tpsNames.map((name, index) => ({
- name: name,
+ state.tpsData = tpsNames.map((tpsItem, index) => ({
+ name: typeof tpsItem === 'string' ? tpsItem : (tpsItem?.name || tpsItem?.Name || `TPS ${index + 1}`),
index: index,
+ lokasiAngkutId: typeof tpsItem === 'string' ? '' : (tpsItem?.lokasiAngkutId || tpsItem?.LokasiAngkutID || ''),
+ spjDetailId: typeof tpsItem === 'string' ? '' : (tpsItem?.spjDetailId || tpsItem?.SpjDetailID || ''),
latitude: '',
longitude: '',
alamatJalan: '',
@@ -205,6 +207,8 @@ const DetailPenjemputan = (function() {
elements.tpsContentContainer.innerHTML = `