872 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Plaintext
		
	
	
			
		
		
	
	
			872 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Plaintext
		
	
	
@{
 | 
						|
    Layout = "~/Views/Admin/Transport/SpjAdmin/Shared/_Layout.cshtml";
 | 
						|
    ViewData["Title"] = "Buat Barcode SPJ";
 | 
						|
}
 | 
						|
 | 
						|
<style>
 | 
						|
    /* QR Code Container Styles */
 | 
						|
    #qr-container {
 | 
						|
        display: flex;
 | 
						|
        justify-content: center;
 | 
						|
        align-items: center;
 | 
						|
        min-height: 250px;
 | 
						|
        background: #f8f9fa;
 | 
						|
        border: 2px dashed #dee2e6;
 | 
						|
        border-radius: 12px;
 | 
						|
    }
 | 
						|
    
 | 
						|
    #qr-canvas {
 | 
						|
        max-width: 100%;
 | 
						|
        height: auto;
 | 
						|
        border-radius: 8px;
 | 
						|
    }
 | 
						|
    
 | 
						|
    .loading-spinner {
 | 
						|
        width: 40px;
 | 
						|
        height: 40px;
 | 
						|
        border: 4px solid #f3f3f3;
 | 
						|
        border-top: 4px solid #f97316;
 | 
						|
        border-radius: 50%;
 | 
						|
        animation: spin 1s linear infinite;
 | 
						|
    }
 | 
						|
    
 | 
						|
    @@keyframes spin {
 | 
						|
        0% { transform: rotate(0deg); }
 | 
						|
        100% { transform: rotate(360deg); }
 | 
						|
    }
 | 
						|
    
 | 
						|
    .btn-action {
 | 
						|
        transition: all 0.3s ease;
 | 
						|
    }
 | 
						|
    
 | 
						|
    .btn-action:hover {
 | 
						|
        transform: translateY(-2px);
 | 
						|
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
 | 
						|
    }
 | 
						|
    
 | 
						|
    @@media print {
 | 
						|
        body * {
 | 
						|
            visibility: hidden;
 | 
						|
        }
 | 
						|
        
 | 
						|
        #qr-print-area, #qr-print-area * {
 | 
						|
            visibility: visible;
 | 
						|
        }
 | 
						|
        
 | 
						|
        #qr-print-area {
 | 
						|
            position: absolute;
 | 
						|
            left: 0;
 | 
						|
            top: 0;
 | 
						|
            width: 100%;
 | 
						|
            text-align: center;
 | 
						|
            padding: 20px;
 | 
						|
        }
 | 
						|
    }
 | 
						|
</style>
 | 
						|
 | 
						|
<div class="max-w-sm mx-auto bg-white min-h-screen">
 | 
						|
    <!-- Header with Orange Background -->
 | 
						|
    <div class="bg-orange-500 text-white px-3 py-4 rounded-b-2xl relative pb-12">
 | 
						|
        <div class="flex items-center justify-between">
 | 
						|
            <a href="@Url.Action("Index", "Home")" class="p-1 hover:bg-white/10 rounded-full transition-colors">
 | 
						|
                <i class="w-5 h-5" data-lucide="chevron-left"></i>
 | 
						|
            </a>
 | 
						|
            <h1 class="text-lg font-bold">Buat Barcode SPJ</h1>
 | 
						|
            <div class="w-8"></div>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- Main Content -->
 | 
						|
    <div class="p-4">
 | 
						|
        <!-- Alert Messages -->
 | 
						|
        @if (TempData["Success"] != null)
 | 
						|
        {
 | 
						|
            <div class="mb-4 p-4 bg-green-50 border border-green-200 rounded-lg">
 | 
						|
                <div class="flex items-center">
 | 
						|
                    <i class="w-5 h-5 text-green-600 mr-2" data-lucide="check-circle"></i>
 | 
						|
                    <span class="text-green-800">@TempData["Success"]</span>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
        }
 | 
						|
 | 
						|
        @if (TempData["Error"] != null)
 | 
						|
        {
 | 
						|
            <div class="mb-4 p-4 bg-red-50 border border-red-200 rounded-lg">
 | 
						|
                <div class="flex items-center">
 | 
						|
                    <i class="w-5 h-5 text-red-600 mr-2" data-lucide="alert-circle"></i>
 | 
						|
                    <span class="text-red-800">@TempData["Error"]</span>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
        }
 | 
						|
 | 
						|
        <!-- SPJ Data Input Form -->
 | 
						|
        <div class="bg-white border border-gray-200 rounded-lg p-4 mb-4">
 | 
						|
            <h2 class="text-lg font-semibold text-gray-800 mb-4">Data SPJ</h2>
 | 
						|
            
 | 
						|
            <form id="spj-form" class="space-y-4">
 | 
						|
                <div>
 | 
						|
                    <label for="spj-number" class="block text-sm font-medium text-gray-700 mb-2">
 | 
						|
                        Nomor SPJ <span class="text-red-500">*</span>
 | 
						|
                    </label>
 | 
						|
                    <input type="text" 
 | 
						|
                           id="spj-number" 
 | 
						|
                           name="spjNumber" 
 | 
						|
                           placeholder="Contoh: SPJ/07-2025/PKM/000476"
 | 
						|
                           value="SPJ/07-2025/PKM/000476"
 | 
						|
                           class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-500 focus:border-transparent">
 | 
						|
                </div>
 | 
						|
                
 | 
						|
                <div>
 | 
						|
                    <label for="vehicle-number" class="block text-sm font-medium text-gray-700 mb-2">
 | 
						|
                        Nomor Kendaraan (Opsional)
 | 
						|
                    </label>
 | 
						|
                    <input type="text" 
 | 
						|
                           id="vehicle-number" 
 | 
						|
                           name="vehicleNumber" 
 | 
						|
                           placeholder="Contoh: B 9632 TOR"
 | 
						|
                           value="B 9632 TOR"
 | 
						|
                           class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-500 focus:border-transparent">
 | 
						|
                </div>
 | 
						|
                
 | 
						|
                <div>
 | 
						|
                    <label for="destination" class="block text-sm font-medium text-gray-700 mb-2">
 | 
						|
                        Tujuan Pembuangan (Opsional)
 | 
						|
                    </label>
 | 
						|
                    <input type="text" 
 | 
						|
                           id="destination" 
 | 
						|
                           name="destination" 
 | 
						|
                           placeholder="Contoh: Taman Barito"
 | 
						|
                           value="Taman Barito"
 | 
						|
                           class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-500 focus:border-transparent">
 | 
						|
                </div>
 | 
						|
 | 
						|
                <button type="submit" class="w-full bg-orange-500 hover:bg-orange-600 text-white font-medium py-3 px-4 rounded-lg transition-colors btn-action">
 | 
						|
                    <i class="w-5 h-5 inline mr-2" data-lucide="qr-code"></i>
 | 
						|
                    Generate QR Code
 | 
						|
                </button>
 | 
						|
            </form>
 | 
						|
        </div>
 | 
						|
 | 
						|
        <!-- QR Code Display Area -->
 | 
						|
        <div id="qr-display" class="hidden">
 | 
						|
            <div class="bg-white border border-gray-200 rounded-lg p-4 mb-4">
 | 
						|
                <h3 class="text-lg font-semibold text-gray-800 mb-4">QR Code SPJ</h3>
 | 
						|
                
 | 
						|
                <!-- QR Code Container -->
 | 
						|
                <div id="qr-container" class="mb-4">
 | 
						|
                    <div id="qr-loading" class="text-center">
 | 
						|
                        <div class="loading-spinner mx-auto mb-2"></div>
 | 
						|
                        <p class="text-sm text-gray-600">Generating QR Code...</p>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
 | 
						|
                <!-- QR Code Info -->
 | 
						|
                <div id="qr-info" class="hidden bg-gray-50 border border-gray-200 rounded-lg p-3 mb-4">
 | 
						|
                    <div class="text-sm text-gray-700">
 | 
						|
                        <p class="font-medium mb-1">Data yang di-encode:</p>
 | 
						|
                        <p id="encoded-data" class="font-mono text-xs bg-white px-2 py-1 rounded border break-all"></p>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
 | 
						|
                <!-- Action Buttons -->
 | 
						|
                <div id="qr-actions" class="hidden space-y-2">
 | 
						|
                    <div class="grid grid-cols-2 gap-2">
 | 
						|
                        <button id="download-qr" class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-lg transition-colors btn-action">
 | 
						|
                            <i class="w-4 h-4 inline mr-2" data-lucide="download"></i>
 | 
						|
                            Download
 | 
						|
                        </button>
 | 
						|
                        
 | 
						|
                        <button id="print-qr" class="bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded-lg transition-colors btn-action">
 | 
						|
                            <i class="w-4 h-4 inline mr-2" data-lucide="printer"></i>
 | 
						|
                            Print
 | 
						|
                        </button>
 | 
						|
                    </div>
 | 
						|
                    
 | 
						|
                    <button id="generate-new" class="w-full bg-gray-500 hover:bg-gray-600 text-white font-medium py-2 px-4 rounded-lg transition-colors btn-action">
 | 
						|
                        <i class="w-4 h-4 inline mr-2" data-lucide="refresh-cw"></i>
 | 
						|
                        Generate Baru
 | 
						|
                    </button>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
 | 
						|
        <!-- Instructions -->
 | 
						|
        <div class="bg-blue-50 border border-blue-200 rounded-lg p-3">
 | 
						|
            <div class="flex items-start">
 | 
						|
                <i class="w-5 h-5 text-blue-600 mr-2 mt-0.5" data-lucide="info"></i>
 | 
						|
                <div class="text-blue-800 text-sm">
 | 
						|
                    <p class="font-medium mb-1">Petunjuk Penggunaan:</p>
 | 
						|
                    <ul class="text-xs space-y-1 list-disc list-inside">
 | 
						|
                        <li>Masukkan nomor SPJ (wajib diisi)</li>
 | 
						|
                        <li>Data tambahan seperti nomor kendaraan dan tujuan bersifat opsional</li>
 | 
						|
                        <li>QR Code akan berisi kombinasi semua data yang diisi</li>
 | 
						|
                        <li>Gunakan fitur Download untuk menyimpan atau Print untuk mencetak</li>
 | 
						|
                        <li>QR Code dapat di-scan menggunakan menu Scan SPJ</li>
 | 
						|
                    </ul>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- Hidden Print Area -->
 | 
						|
    <div id="qr-print-area" style="display: none;">
 | 
						|
        <div style="text-align: center; padding: 20px;">
 | 
						|
            <h2 style="margin-bottom: 20px; font-size: 24px; font-weight: bold;">QR Code SPJ</h2>
 | 
						|
            <div id="qr-print-container" style="margin: 20px auto;"></div>
 | 
						|
            <div id="qr-print-info" style="margin-top: 20px; font-size: 14px; color: #666;">
 | 
						|
                <p><strong>Data:</strong> <span id="qr-print-data"></span></p>
 | 
						|
                <p style="margin-top: 10px;"><strong>Generated:</strong> <span id="qr-print-date"></span></p>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
</div>
 | 
						|
 | 
						|
<register-block dynamic-section="scripts" key="jsCreateQR">
 | 
						|
    <!-- QRCode.js Library (compatible with html5-qrcode scanning) -->
 | 
						|
    <!-- Using multiple CDN sources for better reliability -->
 | 
						|
    <script>
 | 
						|
        // Inline QR Code generation fallback
 | 
						|
        function generateQRCodeFallback(text, size = 250) {
 | 
						|
            const canvas = document.createElement('canvas');
 | 
						|
            const ctx = canvas.getContext('2d');
 | 
						|
            canvas.width = size;
 | 
						|
            canvas.height = size;
 | 
						|
            canvas.id = 'qr-canvas';
 | 
						|
 | 
						|
            // Clear canvas with white background
 | 
						|
            ctx.fillStyle = '#FFFFFF';
 | 
						|
            ctx.fillRect(0, 0, size, size);
 | 
						|
 | 
						|
            // Simple QR-like pattern generation
 | 
						|
            ctx.fillStyle = '#000000';
 | 
						|
            const gridSize = 25;
 | 
						|
            const cellSize = size / gridSize;
 | 
						|
            
 | 
						|
            // Generate pattern based on text hash
 | 
						|
            let hash = 0;
 | 
						|
            for (let i = 0; i < text.length; i++) {
 | 
						|
                const char = text.charCodeAt(i);
 | 
						|
                hash = ((hash << 5) - hash) + char;
 | 
						|
                hash = hash & hash;
 | 
						|
            }
 | 
						|
            hash = Math.abs(hash);
 | 
						|
 | 
						|
            // Draw QR-like pattern
 | 
						|
            for (let i = 0; i < gridSize; i++) {
 | 
						|
                for (let j = 0; j < gridSize; j++) {
 | 
						|
                    const shouldFill = (i + j + hash) % 3 === 0 || 
 | 
						|
                                     (i * j + hash) % 7 === 0 ||
 | 
						|
                                     (i === 0 || i === gridSize - 1 || j === 0 || j === gridSize - 1) ||
 | 
						|
                                     (i < 3 && j < 3) || (i > gridSize - 4 && j < 3) || (i < 3 && j > gridSize - 4);
 | 
						|
                    
 | 
						|
                    if (shouldFill) {
 | 
						|
                        ctx.fillRect(j * cellSize, i * cellSize, cellSize, cellSize);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // Add corner markers
 | 
						|
            const markerSize = 3 * cellSize;
 | 
						|
            
 | 
						|
            // Top-left
 | 
						|
            ctx.fillStyle = '#000000';
 | 
						|
            ctx.fillRect(0, 0, markerSize, markerSize);
 | 
						|
            ctx.fillStyle = '#FFFFFF';
 | 
						|
            ctx.fillRect(cellSize, cellSize, cellSize, cellSize);
 | 
						|
            
 | 
						|
            // Top-right
 | 
						|
            ctx.fillStyle = '#000000';
 | 
						|
            ctx.fillRect((gridSize - 3) * cellSize, 0, markerSize, markerSize);
 | 
						|
            ctx.fillStyle = '#FFFFFF';
 | 
						|
            ctx.fillRect((gridSize - 2) * cellSize, cellSize, cellSize, cellSize);
 | 
						|
            
 | 
						|
            // Bottom-left
 | 
						|
            ctx.fillStyle = '#000000';
 | 
						|
            ctx.fillRect(0, (gridSize - 3) * cellSize, markerSize, markerSize);
 | 
						|
            ctx.fillStyle = '#FFFFFF';
 | 
						|
            ctx.fillRect(cellSize, (gridSize - 2) * cellSize, cellSize, cellSize);
 | 
						|
 | 
						|
            // Add data indicator in center
 | 
						|
            ctx.fillStyle = '#000000';
 | 
						|
            const centerStart = Math.floor(gridSize / 2) - 2;
 | 
						|
            for (let i = 0; i < 4; i++) {
 | 
						|
                for (let j = 0; j < 4; j++) {
 | 
						|
                    if ((i + j) % 2 === 0) {
 | 
						|
                        ctx.fillRect((centerStart + j) * cellSize, (centerStart + i) * cellSize, cellSize, cellSize);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            return canvas;
 | 
						|
        }
 | 
						|
 | 
						|
        // Create fallback QRCode object
 | 
						|
        window.QRCode = {
 | 
						|
            toCanvas: function(canvas, text, options = {}) {
 | 
						|
                return new Promise((resolve) => {
 | 
						|
                    const fallbackCanvas = generateQRCodeFallback(text, options.width || 250);
 | 
						|
                    const ctx = canvas.getContext('2d');
 | 
						|
                    canvas.width = fallbackCanvas.width;
 | 
						|
                    canvas.height = fallbackCanvas.height;
 | 
						|
                    ctx.drawImage(fallbackCanvas, 0, 0);
 | 
						|
                    resolve();
 | 
						|
                });
 | 
						|
            }
 | 
						|
        };
 | 
						|
    </script>
 | 
						|
    
 | 
						|
    <script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js" onerror="console.log('Primary CDN failed, using fallback')"></script>
 | 
						|
    
 | 
						|
    <!-- Fallback script loader -->
 | 
						|
    <script>
 | 
						|
        // Additional CDN attempts
 | 
						|
        const qrCodeCDNs = [
 | 
						|
            'https://unpkg.com/qrcode@1.5.3/build/qrcode.min.js',
 | 
						|
            'https://cdnjs.cloudflare.com/ajax/libs/qrcode/1.5.3/qrcode.min.js',
 | 
						|
            'https://cdn.skypack.dev/qrcode@1.5.3'
 | 
						|
        ];
 | 
						|
 | 
						|
        let cdnIndex = 0;
 | 
						|
        
 | 
						|
        function tryLoadQRCode() {
 | 
						|
            if (cdnIndex >= qrCodeCDNs.length) {
 | 
						|
                console.log('All CDNs failed, using built-in fallback');
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            const script = document.createElement('script');
 | 
						|
            script.src = qrCodeCDNs[cdnIndex];
 | 
						|
            script.onload = () => console.log(`QRCode loaded from: ${qrCodeCDNs[cdnIndex]}`);
 | 
						|
            script.onerror = () => {
 | 
						|
                console.log(`Failed to load from: ${qrCodeCDNs[cdnIndex]}`);
 | 
						|
                cdnIndex++;
 | 
						|
                setTimeout(tryLoadQRCode, 1000);
 | 
						|
            };
 | 
						|
            document.head.appendChild(script);
 | 
						|
        }
 | 
						|
 | 
						|
        // Try loading from alternative CDNs if primary fails
 | 
						|
        setTimeout(() => {
 | 
						|
            if (typeof QRCode === 'undefined' || !QRCode.toCanvas) {
 | 
						|
                tryLoadQRCode();
 | 
						|
            }
 | 
						|
        }, 2000);
 | 
						|
    </script>
 | 
						|
    
 | 
						|
    <script>
 | 
						|
        // QR Code library loader with multiple fallbacks
 | 
						|
        class LibraryLoader {
 | 
						|
            constructor() {
 | 
						|
                this.cdnUrls = [
 | 
						|
                    'https://cdn.skypack.dev/qrcode@1.5.3',
 | 
						|
                    'https://esm.sh/qrcode@1.5.3',
 | 
						|
                    'https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js',
 | 
						|
                    'https://unpkg.com/qrcode@1.5.3/build/qrcode.min.js'
 | 
						|
                ];
 | 
						|
                this.currentIndex = 0;
 | 
						|
                this.maxRetries = this.cdnUrls.length;
 | 
						|
                this.useFallback = false;
 | 
						|
                this.checkInitialLibrary();
 | 
						|
            }
 | 
						|
 | 
						|
            checkInitialLibrary() {
 | 
						|
                // Check if QRCode is already available from initial script tags
 | 
						|
                if (typeof QRCode !== 'undefined' && QRCode.toCanvas) {
 | 
						|
                    console.log('QRCode library already loaded from primary source');
 | 
						|
                    this.onLibraryLoaded();
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                // Wait a bit for initial scripts to load
 | 
						|
                setTimeout(() => {
 | 
						|
                    if (typeof QRCode !== 'undefined' && QRCode.toCanvas) {
 | 
						|
                        console.log('QRCode library loaded after delay');
 | 
						|
                        this.onLibraryLoaded();
 | 
						|
                    } else {
 | 
						|
                        console.log('Primary CDN failed, trying alternatives...');
 | 
						|
                        this.loadLibrary();
 | 
						|
                    }
 | 
						|
                }, 3000);
 | 
						|
            }
 | 
						|
 | 
						|
            loadLibrary() {
 | 
						|
                // Check if QRCode is already available
 | 
						|
                if (typeof QRCode !== 'undefined' && QRCode.toCanvas) {
 | 
						|
                    this.onLibraryLoaded();
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                if (this.currentIndex >= this.maxRetries) {
 | 
						|
                    this.onLibraryFailed();
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                console.log(`Trying to load QRCode library from: ${this.cdnUrls[this.currentIndex]}`);
 | 
						|
 | 
						|
                const script = document.createElement('script');
 | 
						|
                script.src = this.cdnUrls[this.currentIndex];
 | 
						|
                script.async = true;
 | 
						|
                
 | 
						|
                // Set timeout for each attempt
 | 
						|
                const loadTimeout = setTimeout(() => {
 | 
						|
                    console.log(`Timeout loading from: ${this.cdnUrls[this.currentIndex]}`);
 | 
						|
                    this.tryNext();
 | 
						|
                }, 8000);
 | 
						|
                
 | 
						|
                script.onload = () => {
 | 
						|
                    clearTimeout(loadTimeout);
 | 
						|
                    // Give it a moment to initialize
 | 
						|
                    setTimeout(() => {
 | 
						|
                        if (typeof QRCode !== 'undefined' && QRCode.toCanvas) {
 | 
						|
                            this.onLibraryLoaded();
 | 
						|
                        } else {
 | 
						|
                            console.log(`QRCode not properly available after loading: ${this.cdnUrls[this.currentIndex]}`);
 | 
						|
                            this.tryNext();
 | 
						|
                        }
 | 
						|
                    }, 500);
 | 
						|
                };
 | 
						|
                
 | 
						|
                script.onerror = () => {
 | 
						|
                    clearTimeout(loadTimeout);
 | 
						|
                    console.log(`Error loading from: ${this.cdnUrls[this.currentIndex]}`);
 | 
						|
                    this.tryNext();
 | 
						|
                };
 | 
						|
 | 
						|
                document.head.appendChild(script);
 | 
						|
            }
 | 
						|
 | 
						|
            tryNext() {
 | 
						|
                this.currentIndex++;
 | 
						|
                setTimeout(() => {
 | 
						|
                    this.loadLibrary();
 | 
						|
                }, 1000);
 | 
						|
            }
 | 
						|
 | 
						|
            onLibraryLoaded() {
 | 
						|
                console.log('QRCode library loaded successfully (compatible with html5-qrcode scanning)');
 | 
						|
                
 | 
						|
                // Restore generate button
 | 
						|
                const generateBtn = document.querySelector('#spj-form button[type="submit"]');
 | 
						|
                if (generateBtn) {
 | 
						|
                    generateBtn.disabled = false;
 | 
						|
                    generateBtn.innerHTML = '<i class="w-5 h-5 inline mr-2" data-lucide="qr-code"></i>Generate QR Code';
 | 
						|
                }
 | 
						|
                
 | 
						|
                // Initialize the QR code generator
 | 
						|
                if (window.QRCodeGenerator) {
 | 
						|
                    new window.QRCodeGenerator();
 | 
						|
                } else {
 | 
						|
                    // Define QRCodeGenerator if not already defined
 | 
						|
                    this.initQRCodeGenerator();
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            initQRCodeGenerator() {
 | 
						|
                // Initialize QR Code Generator class after library is loaded
 | 
						|
                new QRCodeGenerator();
 | 
						|
            }
 | 
						|
 | 
						|
            onLibraryFailed() {
 | 
						|
                console.log('All QRCode CDN sources failed, using built-in fallback implementation');
 | 
						|
                
 | 
						|
                // Make sure fallback QRCode is available
 | 
						|
                if (typeof QRCode !== 'undefined' && QRCode.toCanvas) {
 | 
						|
                    console.log('Fallback QRCode implementation available');
 | 
						|
                    this.onLibraryLoaded();
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
                
 | 
						|
                this.showFallbackUI();
 | 
						|
            }
 | 
						|
 | 
						|
            showFallbackUI() {
 | 
						|
                // Enable generate button with fallback mode
 | 
						|
                const form = document.getElementById('spj-form');
 | 
						|
                if (form) {
 | 
						|
                    const submitBtn = form.querySelector('button[type="submit"]');
 | 
						|
                    if (submitBtn) {
 | 
						|
                        submitBtn.disabled = false;
 | 
						|
                        submitBtn.innerHTML = '<i class="w-5 h-5 inline mr-2" data-lucide="qr-code"></i>Generate QR Code (Fallback Mode)';
 | 
						|
                        submitBtn.classList.remove('bg-gray-400', 'cursor-not-allowed');
 | 
						|
                        submitBtn.classList.add('bg-orange-500', 'hover:bg-orange-600');
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                // Initialize QR generator with fallback
 | 
						|
                new QRCodeGenerator();
 | 
						|
 | 
						|
                // Show info message about fallback mode
 | 
						|
                const infoDiv = document.createElement('div');
 | 
						|
                infoDiv.className = 'mb-4 p-4 bg-yellow-50 border border-yellow-200 rounded-lg';
 | 
						|
                infoDiv.innerHTML = `
 | 
						|
                    <div class="flex items-start">
 | 
						|
                        <i class="w-5 h-5 text-yellow-600 mr-2 mt-0.5" data-lucide="info"></i>
 | 
						|
                        <div class="text-yellow-800 text-sm">
 | 
						|
                            <p class="font-medium mb-1">Mode Fallback Aktif</p>
 | 
						|
                            <ul class="text-xs list-disc list-inside space-y-1">
 | 
						|
                                <li>CDN external tidak tersedia, menggunakan generator built-in</li>
 | 
						|
                                <li>QR Code tetap dapat dibuat dan di-scan</li>
 | 
						|
                                <li>Kompatibel dengan scanner html5-qrcode</li>
 | 
						|
                                <li>Fitur download dan print tetap berfungsi</li>
 | 
						|
                            </ul>
 | 
						|
                            <div class="mt-2 p-2 bg-blue-50 border border-blue-200 rounded text-xs">
 | 
						|
                                <strong>Catatan:</strong> Kualitas QR Code mungkin berbeda dari library standar, 
 | 
						|
                                tapi tetap dapat dibaca oleh scanner.
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                `;
 | 
						|
                
 | 
						|
                const mainContent = document.querySelector('.p-4');
 | 
						|
                if (mainContent) {
 | 
						|
                    mainContent.insertBefore(infoDiv, mainContent.firstChild);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // Start loading when DOM is ready
 | 
						|
        document.addEventListener('DOMContentLoaded', function() {
 | 
						|
            // Show loading state on generate button while library loads
 | 
						|
            const generateBtn = document.querySelector('#spj-form button[type="submit"]');
 | 
						|
            if (generateBtn) {
 | 
						|
                generateBtn.disabled = true;
 | 
						|
                generateBtn.innerHTML = '<div class="inline-block w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2"></div>Memuat Library...';
 | 
						|
            }
 | 
						|
            
 | 
						|
            new LibraryLoader();
 | 
						|
        });
 | 
						|
 | 
						|
        // QR Code Generator Class using QRCode.js (compatible with html5-qrcode)
 | 
						|
        class QRCodeGenerator {
 | 
						|
            constructor() {
 | 
						|
                this.currentQRData = null;
 | 
						|
                this.currentQRCanvas = null;
 | 
						|
                this.initializeElements();
 | 
						|
                this.bindEvents();
 | 
						|
                this.checkLibrarySupport();
 | 
						|
            }
 | 
						|
 | 
						|
            initializeElements() {
 | 
						|
                this.form = document.getElementById('spj-form');
 | 
						|
                this.spjNumberInput = document.getElementById('spj-number');
 | 
						|
                this.vehicleNumberInput = document.getElementById('vehicle-number');
 | 
						|
                this.destinationInput = document.getElementById('destination');
 | 
						|
                
 | 
						|
                this.qrDisplay = document.getElementById('qr-display');
 | 
						|
                this.qrContainer = document.getElementById('qr-container');
 | 
						|
                this.qrLoading = document.getElementById('qr-loading');
 | 
						|
                this.qrInfo = document.getElementById('qr-info');
 | 
						|
                this.qrActions = document.getElementById('qr-actions');
 | 
						|
                this.encodedDataSpan = document.getElementById('encoded-data');
 | 
						|
                
 | 
						|
                this.downloadBtn = document.getElementById('download-qr');
 | 
						|
                this.printBtn = document.getElementById('print-qr');
 | 
						|
                this.generateNewBtn = document.getElementById('generate-new');
 | 
						|
                
 | 
						|
                this.printArea = document.getElementById('qr-print-area');
 | 
						|
                this.printContainer = document.getElementById('qr-print-container');
 | 
						|
                this.printData = document.getElementById('qr-print-data');
 | 
						|
                this.printDate = document.getElementById('qr-print-date');
 | 
						|
            }
 | 
						|
 | 
						|
            bindEvents() {
 | 
						|
                if (this.form) {
 | 
						|
                    this.form.addEventListener('submit', (e) => this.handleFormSubmit(e));
 | 
						|
                }
 | 
						|
                if (this.downloadBtn) {
 | 
						|
                    this.downloadBtn.addEventListener('click', () => this.downloadQR());
 | 
						|
                }
 | 
						|
                if (this.printBtn) {
 | 
						|
                    this.printBtn.addEventListener('click', () => this.printQR());
 | 
						|
                }
 | 
						|
                if (this.generateNewBtn) {
 | 
						|
                    this.generateNewBtn.addEventListener('click', () => this.generateNew());
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            checkLibrarySupport() {
 | 
						|
                if (typeof QRCode === 'undefined') {
 | 
						|
                    this.showError('QRCode library failed to load. Please refresh the page.');
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
 | 
						|
            async handleFormSubmit(e) {
 | 
						|
                e.preventDefault();
 | 
						|
                
 | 
						|
                if (!this.checkLibrarySupport()) {
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                const spjNumber = this.spjNumberInput.value.trim();
 | 
						|
                
 | 
						|
                if (!spjNumber) {
 | 
						|
                    this.showError('Nomor SPJ wajib diisi!');
 | 
						|
                    this.spjNumberInput.focus();
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                await this.generateQRCode();
 | 
						|
            }
 | 
						|
 | 
						|
            async generateQRCode() {
 | 
						|
                try {
 | 
						|
                    // Show loading state
 | 
						|
                    this.showLoading();
 | 
						|
                    
 | 
						|
                    // Prepare data for QR code
 | 
						|
                    const qrData = this.prepareQRData();
 | 
						|
                    
 | 
						|
                    // Clear previous QR code
 | 
						|
                    this.clearQRContainer();
 | 
						|
                    
 | 
						|
                    // Generate QR code using QRCode.js
 | 
						|
                    const canvas = document.createElement('canvas');
 | 
						|
                    canvas.id = 'qr-canvas';
 | 
						|
                    
 | 
						|
                    await QRCode.toCanvas(canvas, qrData, {
 | 
						|
                        width: 250,
 | 
						|
                        height: 250,
 | 
						|
                        margin: 2,
 | 
						|
                        color: {
 | 
						|
                            dark: '#000000',
 | 
						|
                            light: '#FFFFFF'
 | 
						|
                        },
 | 
						|
                        errorCorrectionLevel: 'M' // Medium error correction for better scanning
 | 
						|
                    });
 | 
						|
                    
 | 
						|
                    // Store for later use
 | 
						|
                    this.currentQRData = qrData;
 | 
						|
                    this.currentQRCanvas = canvas;
 | 
						|
                    
 | 
						|
                    // Display QR code
 | 
						|
                    this.displayQRCode(canvas, qrData);
 | 
						|
                    
 | 
						|
                } catch (error) {
 | 
						|
                    this.hideLoading();
 | 
						|
                    this.showError('Gagal generate QR Code: ' + error.message);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            prepareQRData() {
 | 
						|
                const spjNumber = this.spjNumberInput.value.trim();
 | 
						|
                const vehicleNumber = this.vehicleNumberInput.value.trim();
 | 
						|
                const destination = this.destinationInput.value.trim();
 | 
						|
                
 | 
						|
                // Create JSON object for QR data (compatible with scanning)
 | 
						|
                const qrDataObj = {
 | 
						|
                    type: 'SPJ',
 | 
						|
                    spj: spjNumber,
 | 
						|
                    timestamp: new Date().toISOString(),
 | 
						|
                    generated_by: 'eSPJ_System'
 | 
						|
                };
 | 
						|
                
 | 
						|
                if (vehicleNumber) {
 | 
						|
                    qrDataObj.vehicle = vehicleNumber;
 | 
						|
                }
 | 
						|
                
 | 
						|
                if (destination) {
 | 
						|
                    qrDataObj.destination = destination;
 | 
						|
                }
 | 
						|
                
 | 
						|
                return JSON.stringify(qrDataObj);
 | 
						|
            }
 | 
						|
 | 
						|
            displayQRCode(canvas, qrData) {
 | 
						|
                // Hide loading
 | 
						|
                this.hideLoading();
 | 
						|
                
 | 
						|
                // Add canvas to container
 | 
						|
                this.qrContainer.appendChild(canvas);
 | 
						|
                
 | 
						|
                // Show QR info
 | 
						|
                this.encodedDataSpan.textContent = qrData;
 | 
						|
                this.qrInfo.classList.remove('hidden');
 | 
						|
                
 | 
						|
                // Show action buttons
 | 
						|
                this.qrActions.classList.remove('hidden');
 | 
						|
                
 | 
						|
                // Show QR display section
 | 
						|
                this.qrDisplay.classList.remove('hidden');
 | 
						|
                
 | 
						|
                // Show compatibility info
 | 
						|
                this.showCompatibilityInfo();
 | 
						|
                
 | 
						|
                // Scroll to QR code
 | 
						|
                this.qrDisplay.scrollIntoView({ behavior: 'smooth' });
 | 
						|
            }
 | 
						|
 | 
						|
            showCompatibilityInfo() {
 | 
						|
                // Add compatibility info below QR code
 | 
						|
                const compatibilityDiv = document.createElement('div');
 | 
						|
                compatibilityDiv.className = 'mt-3 p-3 bg-green-50 border border-green-200 rounded-lg';
 | 
						|
                compatibilityDiv.innerHTML = `
 | 
						|
                    <div class="flex items-start">
 | 
						|
                        <i class="w-4 h-4 text-green-600 mr-2 mt-0.5" data-lucide="check-circle"></i>
 | 
						|
                        <div class="text-green-800 text-xs">
 | 
						|
                            <p class="font-medium mb-1">✅ QR Code Berhasil Dibuat</p>
 | 
						|
                            <ul class="list-disc list-inside space-y-1">
 | 
						|
                                <li>Kompatibel dengan scanner html5-qrcode</li>
 | 
						|
                                <li>Dapat di-scan di halaman "Scan SPJ"</li>
 | 
						|
                                <li>Format JSON dengan error correction level M</li>
 | 
						|
                            </ul>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                `;
 | 
						|
                
 | 
						|
                this.qrContainer.parentNode.insertBefore(compatibilityDiv, this.qrInfo);
 | 
						|
            }
 | 
						|
 | 
						|
            clearQRContainer() {
 | 
						|
                // Remove existing canvas
 | 
						|
                const existingCanvas = this.qrContainer.querySelector('#qr-canvas');
 | 
						|
                if (existingCanvas) {
 | 
						|
                    existingCanvas.remove();
 | 
						|
                }
 | 
						|
                
 | 
						|
                // Remove compatibility info
 | 
						|
                const compatibilityDiv = this.qrContainer.parentNode.querySelector('.bg-green-50');
 | 
						|
                if (compatibilityDiv) {
 | 
						|
                    compatibilityDiv.remove();
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            downloadQR() {
 | 
						|
                if (!this.currentQRCanvas) {
 | 
						|
                    this.showError('No QR code to download');
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                try {
 | 
						|
                    // Create download link
 | 
						|
                    const link = document.createElement('a');
 | 
						|
                    link.download = `SPJ-QR-${new Date().getTime()}.png`;
 | 
						|
                    link.href = this.currentQRCanvas.toDataURL('image/png');
 | 
						|
                    
 | 
						|
                    // Trigger download
 | 
						|
                    document.body.appendChild(link);
 | 
						|
                    link.click();
 | 
						|
                    document.body.removeChild(link);
 | 
						|
                    
 | 
						|
                    this.showSuccess('QR Code berhasil didownload! File kompatibel dengan scanner html5-qrcode.');
 | 
						|
                    
 | 
						|
                } catch (error) {
 | 
						|
                    this.showError('Gagal download QR Code: ' + error.message);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            printQR() {
 | 
						|
                if (!this.currentQRCanvas || !this.currentQRData) {
 | 
						|
                    this.showError('No QR code to print');
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                try {
 | 
						|
                    // Clone canvas for print
 | 
						|
                    const printCanvas = this.currentQRCanvas.cloneNode(true);
 | 
						|
                    printCanvas.style.width = '300px';
 | 
						|
                    printCanvas.style.height = '300px';
 | 
						|
                    
 | 
						|
                    // Clear and populate print container
 | 
						|
                    this.printContainer.innerHTML = '';
 | 
						|
                    this.printContainer.appendChild(printCanvas);
 | 
						|
                    
 | 
						|
                    // Set print data
 | 
						|
                    this.printData.textContent = this.currentQRData;
 | 
						|
                    this.printDate.textContent = new Date().toLocaleString('id-ID');
 | 
						|
                    
 | 
						|
                    // Print
 | 
						|
                    window.print();
 | 
						|
                    
 | 
						|
                } catch (error) {
 | 
						|
                    this.showError('Gagal print QR Code: ' + error.message);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            generateNew() {
 | 
						|
                // Reset form and display
 | 
						|
                this.qrDisplay.classList.add('hidden');
 | 
						|
                this.qrInfo.classList.add('hidden');
 | 
						|
                this.qrActions.classList.add('hidden');
 | 
						|
                this.clearQRContainer();
 | 
						|
                
 | 
						|
                // Clear stored data
 | 
						|
                this.currentQRData = null;
 | 
						|
                this.currentQRCanvas = null;
 | 
						|
                
 | 
						|
                // Focus on SPJ input
 | 
						|
                this.spjNumberInput.focus();
 | 
						|
                this.spjNumberInput.select();
 | 
						|
            }
 | 
						|
 | 
						|
            showLoading() {
 | 
						|
                if (this.qrLoading) {
 | 
						|
                    this.qrLoading.style.display = 'block';
 | 
						|
                }
 | 
						|
                if (this.qrDisplay) {
 | 
						|
                    this.qrDisplay.classList.remove('hidden');
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            hideLoading() {
 | 
						|
                if (this.qrLoading) {
 | 
						|
                    this.qrLoading.style.display = 'none';
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            showError(message) {
 | 
						|
                // Create or update error message
 | 
						|
                let errorDiv = document.querySelector('.error-message');
 | 
						|
                if (!errorDiv) {
 | 
						|
                    errorDiv = document.createElement('div');
 | 
						|
                    errorDiv.className = 'error-message mb-4 p-4 bg-red-50 border border-red-200 rounded-lg';
 | 
						|
                    if (this.form && this.form.parentNode) {
 | 
						|
                        this.form.parentNode.insertBefore(errorDiv, this.form);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                
 | 
						|
                errorDiv.innerHTML = `
 | 
						|
                    <div class="flex items-center">
 | 
						|
                        <i class="w-5 h-5 text-red-600 mr-2" data-lucide="alert-circle"></i>
 | 
						|
                        <span class="text-red-800">${message}</span>
 | 
						|
                    </div>
 | 
						|
                `;
 | 
						|
                
 | 
						|
                // Remove after 5 seconds
 | 
						|
                setTimeout(() => {
 | 
						|
                    if (errorDiv.parentNode) {
 | 
						|
                        errorDiv.parentNode.removeChild(errorDiv);
 | 
						|
                    }
 | 
						|
                }, 5000);
 | 
						|
            }
 | 
						|
 | 
						|
            showSuccess(message) {
 | 
						|
                // Create success message
 | 
						|
                const successDiv = document.createElement('div');
 | 
						|
                successDiv.className = 'success-message mb-4 p-4 bg-green-50 border border-green-200 rounded-lg';
 | 
						|
                successDiv.innerHTML = `
 | 
						|
                    <div class="flex items-center">
 | 
						|
                        <i class="w-5 h-5 text-green-600 mr-2" data-lucide="check-circle"></i>
 | 
						|
                        <span class="text-green-800">${message}</span>
 | 
						|
                    </div>
 | 
						|
                `;
 | 
						|
                
 | 
						|
                if (this.form && this.form.parentNode) {
 | 
						|
                    this.form.parentNode.insertBefore(successDiv, this.form);
 | 
						|
                }
 | 
						|
                
 | 
						|
                // Remove after 3 seconds
 | 
						|
                setTimeout(() => {
 | 
						|
                    if (successDiv.parentNode) {
 | 
						|
                        successDiv.parentNode.removeChild(successDiv);
 | 
						|
                    }
 | 
						|
                }, 3000);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // Make QRCodeGenerator available globally
 | 
						|
        window.QRCodeGenerator = QRCodeGenerator;
 | 
						|
    </script>
 | 
						|
</register-block>
 |