// ============================================ // APLIKASI DOKUMENTASI NASABAH LPD GERANA // ============================================ // Global Variables let stream = null; let capturedImage = null; let db = null; let currentDetailId = null; // DOM Elements const elements = { // Webcam webcam: document.getElementById('webcam'), canvas: document.getElementById('canvas'), preview: document.getElementById('preview'), previewImage: document.getElementById('previewImage'), // Buttons startBtn: document.getElementById('startBtn'), captureBtn: document.getElementById('captureBtn'), retakeBtn: document.getElementById('retakeBtn'), saveBtn: document.getElementById('saveBtn'), printBtn: document.getElementById('printBtn'), deleteBtn: document.getElementById('deleteBtn'), exportBtn: document.getElementById('exportBtn'), // Form form: document.getElementById('nasabahForm'), nama: document.getElementById('nama'), noAnggota: document.getElementById('noAnggota'), jenisPerjanjian: document.getElementById('jenisPerjanjian'), tanggal: document.getElementById('tanggal'), catatan: document.getElementById('catatan'), // Gallery gallery: document.getElementById('gallery'), emptyState: document.getElementById('emptyState'), searchInput: document.getElementById('searchInput'), galleryStats: document.getElementById('galleryStats'), // Modal modal: document.getElementById('detailModal'), modalBody: document.getElementById('modalBody'), closeModal: document.querySelector('.close-modal'), // Status webcamStatus: document.getElementById('webcamStatus'), toast: document.getElementById('toast') }; // ============================================ // API CONFIGURATION // ============================================ const API_BASE_URL = 'http://localhost:3002/api'; async function checkApiConnection() { try { const response = await fetch(`${API_BASE_URL}/health`); if (!response.ok) { throw new Error('API not responding'); } console.log('✅ API connection successful'); return true; } catch (error) { console.error('❌ API connection failed:', error); showToast('⚠️ Tidak dapat terhubung ke server. Pastikan server berjalan.', 'error'); return false; } } // ============================================ // DATABASE OPERATIONS // ============================================ async function saveToDatabase(data) { try { const response = await fetch(`${API_BASE_URL}/dokumentasi`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Failed to save'); } const result = await response.json(); console.log('Data berhasil disimpan dengan ID:', result.id); return result.id; } catch (error) { console.error('Error saving to database:', error); throw error; } } async function getAllData() { try { const response = await fetch(`${API_BASE_URL}/dokumentasi`); if (!response.ok) { throw new Error('Failed to fetch data'); } const result = await response.json(); return result.data || []; } catch (error) { console.error('Error fetching data:', error); throw error; } } async function getDataById(id) { try { const response = await fetch(`${API_BASE_URL}/dokumentasi/${id}`); if (!response.ok) { throw new Error('Data not found'); } const result = await response.json(); return result.data; } catch (error) { console.error('Error fetching data by ID:', error); throw error; } } async function deleteData(id) { try { const response = await fetch(`${API_BASE_URL}/dokumentasi/${id}`, { method: 'DELETE' }); if (!response.ok) { throw new Error('Failed to delete'); } return true; } catch (error) { console.error('Error deleting data:', error); throw error; } } // ============================================ // WEBCAM FUNCTIONS // ============================================ async function startWebcam() { try { showStatus('Meminta akses webcam...', 'info'); stream = await navigator.mediaDevices.getUserMedia({ video: { width: { ideal: 1280 }, height: { ideal: 720 }, facingMode: 'user' }, audio: false }); elements.webcam.srcObject = stream; elements.startBtn.style.display = 'none'; elements.captureBtn.style.display = 'inline-flex'; showStatus('Webcam aktif. Posisikan nasabah dan klik "Ambil Foto"', 'success'); showToast('✅ Webcam berhasil diaktifkan', 'success'); } catch (error) { console.error('Error accessing webcam:', error); showStatus('Gagal mengakses webcam. Pastikan webcam terhubung dan izin diberikan.', 'error'); showToast('❌ Gagal mengakses webcam', 'error'); } } function capturePhoto() { const canvas = elements.canvas; const context = canvas.getContext('2d'); // Set canvas size to match video canvas.width = elements.webcam.videoWidth; canvas.height = elements.webcam.videoHeight; // Draw video frame to canvas context.drawImage(elements.webcam, 0, 0, canvas.width, canvas.height); // Get image data capturedImage = canvas.toDataURL('image/jpeg', 0.9); // Show preview elements.previewImage.src = capturedImage; elements.webcam.style.display = 'none'; elements.preview.style.display = 'block'; // Update buttons elements.captureBtn.style.display = 'none'; elements.retakeBtn.style.display = 'inline-flex'; elements.saveBtn.style.display = 'inline-flex'; showStatus('Foto berhasil diambil. Periksa hasilnya dan klik "Simpan" jika sudah sesuai.', 'success'); showToast('📸 Foto berhasil diambil', 'success'); } function retakePhoto() { capturedImage = null; elements.webcam.style.display = 'block'; elements.preview.style.display = 'none'; elements.retakeBtn.style.display = 'none'; elements.saveBtn.style.display = 'none'; elements.captureBtn.style.display = 'inline-flex'; showStatus('Webcam aktif. Ambil foto ulang.', 'info'); } function stopWebcam() { if (stream) { stream.getTracks().forEach(track => track.stop()); stream = null; } elements.webcam.srcObject = null; elements.webcam.style.display = 'block'; elements.preview.style.display = 'none'; elements.startBtn.style.display = 'inline-flex'; elements.captureBtn.style.display = 'none'; elements.retakeBtn.style.display = 'none'; elements.saveBtn.style.display = 'none'; capturedImage = null; } // ============================================ // FORM FUNCTIONS // ============================================ async function saveDocumentation() { // Validate form if (!elements.form.checkValidity()) { elements.form.reportValidity(); showToast('⚠️ Lengkapi semua data yang wajib diisi', 'error'); return; } // Validate photo if (!capturedImage) { showToast('⚠️ Ambil foto terlebih dahulu', 'error'); return; } // Prepare data const data = { nama: elements.nama.value.trim(), noAnggota: elements.noAnggota.value.trim(), jenisPerjanjian: elements.jenisPerjanjian.value, tanggal: elements.tanggal.value, catatan: elements.catatan.value.trim(), foto: capturedImage, timestamp: new Date().toISOString() }; try { await saveToDatabase(data); showToast('✅ Dokumentasi berhasil disimpan', 'success'); // Reset form and webcam elements.form.reset(); setDefaultDate(); stopWebcam(); showStatus('', ''); // Reload gallery await loadGallery(); } catch (error) { console.error('Error saving documentation:', error); showToast('❌ Gagal menyimpan dokumentasi', 'error'); } } function setDefaultDate() { const today = new Date().toISOString().split('T')[0]; elements.tanggal.value = today; } // ============================================ // GALLERY FUNCTIONS // ============================================ async function loadGallery(searchTerm = '') { try { const allData = await getAllData(); // Filter by search term let filteredData = allData; if (searchTerm) { const term = searchTerm.toLowerCase(); filteredData = allData.filter(item => item.nama.toLowerCase().includes(term) || item.noAnggota.toLowerCase().includes(term) ); } // Sort by timestamp (newest first) filteredData.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)); // Update stats updateGalleryStats(filteredData.length, allData.length); // Show empty state if no data if (filteredData.length === 0) { elements.gallery.innerHTML = ''; elements.emptyState.style.display = 'block'; return; } elements.emptyState.style.display = 'none'; // Render gallery items elements.gallery.innerHTML = filteredData.map(item => `
No. Anggota: ${escapeHtml(item.noAnggota)}
Perjanjian: ${escapeHtml(item.jenisPerjanjian)}
📅 ${formatDate(item.tanggal)} • 🕐 ${formatTime(item.timestamp)}