Spaces:
Runtime error
Runtime error
| <html xmlns="http://www.w3.org/1999/xhtml" lang="th" xml:lang="th"> | |
| <head> | |
| <title>journey</title> | |
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script> | |
| <style type="text/css"> | |
| @font-face { | |
| font-family: 'THSarabunNew'; | |
| src: url('fonts/THSarabunNew.ttf') format('truetype'); | |
| font-weight: normal; | |
| font-style: normal; | |
| unicode-range: U+0E00-0E7F, U+0020-007F, U+00A0-00FF; | |
| font-display: swap; | |
| } | |
| @font-face { | |
| font-family: 'THSarabunNew'; | |
| src: url('fonts/THSarabunNew Bold.ttf') format('truetype'); | |
| font-weight: bold; | |
| font-style: normal; | |
| unicode-range: U+0E00-0E7F, U+0020-007F, U+00A0-00FF; | |
| font-display: swap; | |
| } | |
| * { | |
| font-family: 'THSarabunNew', sans-serif ; | |
| } | |
| .ft00 { font-size: 19px; color: #000000; } | |
| .ft01 { font-size: 25px; color: #000000; } | |
| .ft02 { font-size: 22px; color: #000000; } | |
| .ft03 { font-size: 22px; color: #000000; } | |
| .ft06 { font-size: 15px; line-height: 18px; color: #000000; } | |
| /* สำหรับการพิมพ์เป็น PDF */ | |
| @media print { | |
| body { | |
| filter: grayscale(100%); | |
| } | |
| .controls-container { display: none ; } | |
| body { margin: 0 ; padding: 0 ; } | |
| .page-div { width: 100% ; | |
| height: auto ; | |
| margin: 0 ; | |
| padding: 0 ; | |
| page-break-after: always; } | |
| .page-div:last-child { page-break-after: avoid; } | |
| .overlay-frame { display: none ; } | |
| } | |
| .page-div { | |
| position: relative; | |
| width: 892px; | |
| height: 1261px; | |
| background: none; | |
| margin: 10px auto; | |
| } | |
| .page-div:last-child { | |
| margin-bottom: 0; | |
| } | |
| .controls-container { | |
| position: fixed; | |
| bottom: 16px; | |
| left: 16px; | |
| background: rgba(255, 255, 255, 0.95); | |
| padding: 16px; | |
| border-radius: 12px; | |
| width: 340px; | |
| max-width: calc(100vw - 32px); | |
| z-index: 1000; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.3); | |
| } | |
| /* Custom Dropdown Styles */ | |
| .custom-dropdown { | |
| position: relative; | |
| width: 100%; | |
| } | |
| .dropdown-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| padding: 10px 12px; | |
| background: white; | |
| border: 2px solid #e2e8f0; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| min-height: 48px; | |
| } | |
| .dropdown-header:hover { | |
| border-color: #3b82f6; | |
| } | |
| .dropdown-header:focus-within { | |
| border-color: #3b82f6; | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2); | |
| } | |
| .dropdown-header img { | |
| width: 32px; | |
| height: 32px; | |
| border-radius: 50%; | |
| object-fit: cover; | |
| flex-shrink: 0; | |
| } | |
| .dropdown-header-text { | |
| flex: 1; | |
| font-size: 15px; | |
| color: #1e293b; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .dropdown-arrow { | |
| width: 20px; | |
| height: 20px; | |
| transition: transform 0.3s ease; | |
| flex-shrink: 0; | |
| } | |
| .dropdown-arrow.open { | |
| transform: rotate(180deg); | |
| } | |
| .dropdown-menu { | |
| position: absolute; | |
| bottom: calc(100% + 8px); | |
| left: 0; | |
| right: 0; | |
| background: white; | |
| border-radius: 12px; | |
| box-shadow: 0 12px 40px rgba(0, 0, 0, 0.2); | |
| opacity: 0; | |
| visibility: hidden; | |
| transform: translateY(10px); | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| z-index: 1001; | |
| overflow: hidden; | |
| max-height: 400px; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .dropdown-menu.open { | |
| opacity: 1; | |
| visibility: visible; | |
| transform: translateY(0); | |
| } | |
| .search-container { | |
| padding: 12px; | |
| border-bottom: 1px solid #e2e8f0; | |
| background: #f8fafc; | |
| position: sticky; | |
| top: 0; | |
| z-index: 10; | |
| } | |
| .search-input { | |
| width: 100%; | |
| padding: 10px 40px 10px 12px; | |
| border: 2px solid #e2e8f0; | |
| border-radius: 8px; | |
| font-size: 15px; | |
| outline: none; | |
| transition: all 0.2s ease; | |
| background: white; | |
| } | |
| .search-input:focus { | |
| border-color: #3b82f6; | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15); | |
| } | |
| .search-input::placeholder { | |
| color: #94a3b8; | |
| } | |
| .search-wrapper { | |
| position: relative; | |
| } | |
| .search-icon { | |
| position: absolute; | |
| right: 12px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| width: 20px; | |
| height: 20px; | |
| color: #94a3b8; | |
| pointer-events: none; | |
| } | |
| .clear-search { | |
| position: absolute; | |
| right: 12px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| width: 24px; | |
| height: 24px; | |
| background: #ef4444; | |
| color: white; | |
| border: none; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| display: none; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 14px; | |
| font-weight: bold; | |
| transition: background 0.2s; | |
| } | |
| .clear-search:hover { | |
| background: #dc2626; | |
| } | |
| .clear-search.visible { | |
| display: flex; | |
| } | |
| .dropdown-list { | |
| overflow-y: auto; | |
| max-height: 280px; | |
| overscroll-behavior: contain; | |
| } | |
| .dropdown-list::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| .dropdown-list::-webkit-scrollbar-track { | |
| background: #f1f5f9; | |
| } | |
| .dropdown-list::-webkit-scrollbar-thumb { | |
| background: #cbd5e1; | |
| border-radius: 4px; | |
| } | |
| .dropdown-list::-webkit-scrollbar-thumb:hover { | |
| background: #94a3b8; | |
| } | |
| .dropdown-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 12px 16px; | |
| cursor: pointer; | |
| transition: all 0.15s ease; | |
| border-bottom: 1px solid #f1f5f9; | |
| } | |
| .dropdown-item:last-child { | |
| border-bottom: none; | |
| } | |
| .dropdown-item:hover { | |
| background: #f0f9ff; | |
| } | |
| .dropdown-item.selected { | |
| background: #eff6ff; | |
| } | |
| .dropdown-item.active { | |
| background: #dbeafe; | |
| } | |
| .dropdown-item-image { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| object-fit: cover; | |
| flex-shrink: 0; | |
| border: 2px solid #e2e8f0; | |
| } | |
| .dropdown-item-content { | |
| flex: 1; | |
| min-width: 0; | |
| } | |
| .dropdown-item-title { | |
| font-size: 14px; | |
| font-weight: 600; | |
| color: #1e293b; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .dropdown-item-subtitle { | |
| font-size: 12px; | |
| color: #64748b; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .dropdown-item-checkbox { | |
| flex-shrink: 0; | |
| } | |
| .custom-checkbox { | |
| width: 22px; | |
| height: 22px; | |
| border: 2px solid #cbd5e1; | |
| border-radius: 6px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| background: white; | |
| } | |
| .custom-checkbox:hover { | |
| border-color: #3b82f6; | |
| } | |
| .custom-checkbox.checked { | |
| background: #3b82f6; | |
| border-color: #3b82f6; | |
| } | |
| .custom-checkbox svg { | |
| width: 14px; | |
| height: 14px; | |
| color: white; | |
| opacity: 0; | |
| transform: scale(0.5); | |
| transition: all 0.2s ease; | |
| } | |
| .custom-checkbox.checked svg { | |
| opacity: 1; | |
| transform: scale(1); | |
| } | |
| .dropdown-actions { | |
| padding: 12px; | |
| border-top: 1px solid #e2e8f0; | |
| background: #f8fafc; | |
| display: flex; | |
| gap: 8px; | |
| flex-wrap: wrap; | |
| } | |
| .action-btn { | |
| flex: 1; | |
| min-width: 100px; | |
| padding: 10px 16px; | |
| border: none; | |
| border-radius: 8px; | |
| font-size: 14px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 6px; | |
| } | |
| .action-btn:active { | |
| transform: scale(0.98); | |
| } | |
| .btn-select-all { | |
| background: #e2e8f0; | |
| color: #475569; | |
| } | |
| .btn-select-all:hover { | |
| background: #cbd5e1; | |
| } | |
| .btn-clear-selection { | |
| background: #fee2e2; | |
| color: #dc2626; | |
| } | |
| .btn-clear-selection:hover { | |
| background: #fecaca; | |
| } | |
| .selected-count { | |
| padding: 8px 12px; | |
| background: #dbeafe; | |
| color: #1d4ed8; | |
| border-radius: 8px; | |
| font-size: 13px; | |
| font-weight: 600; | |
| text-align: center; | |
| } | |
| .no-results { | |
| padding: 40px 20px; | |
| text-align: center; | |
| color: #94a3b8; | |
| } | |
| .no-results svg { | |
| width: 48px; | |
| height: 48px; | |
| margin: 0 auto 12px; | |
| opacity: 0.5; | |
| } | |
| /* Control Buttons */ | |
| .control-btn { | |
| padding: 12px 16px; | |
| border-radius: 8px; | |
| font-size: 15px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| border: none; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| min-height: 48px; | |
| } | |
| .control-btn:active { | |
| transform: scale(0.98); | |
| } | |
| .control-btn:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| } | |
| .btn-grayscale { | |
| background: #6b7280; | |
| color: white; | |
| } | |
| .btn-grayscale:hover:not(:disabled) { | |
| background: #4b5563; | |
| } | |
| .btn-download-current { | |
| background: white; | |
| color: #1e293b; | |
| border: 2px solid #e2e8f0; | |
| } | |
| .btn-download-current:hover:not(:disabled) { | |
| background: #f8fafc; | |
| border-color: #cbd5e1; | |
| } | |
| .btn-download-selected { | |
| background: #3b82f6; | |
| color: white; | |
| } | |
| .btn-download-selected:hover:not(:disabled) { | |
| background: #2563eb; | |
| } | |
| .btn-download-all { | |
| background: #10b981; | |
| color: white; | |
| } | |
| .btn-download-all:hover:not(:disabled) { | |
| background: #059669; | |
| } | |
| .btn-randomize { | |
| background: #8b5cf6; | |
| color: white; | |
| } | |
| .btn-randomize:hover:not(:disabled) { | |
| background: #7c3aed; | |
| } | |
| .overlay-frame { | |
| width: 300px; | |
| height: 320px; | |
| border: none; | |
| position: absolute; | |
| overflow: hidden; | |
| z-index: 50; | |
| background: transparent; | |
| } | |
| .overlay-image-container { | |
| width: 260px; | |
| height: 150px; | |
| background: transparent; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| position: absolute; | |
| transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1); | |
| transform-origin: center; | |
| } | |
| .overlay-image-container img { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| } | |
| #page1-overlay { | |
| top: 700px; | |
| right: 320px; | |
| } | |
| #page2-overlay { | |
| top: 1000px; | |
| left: 500px; | |
| } | |
| /* Responsive Design */ | |
| @media (max-width: 480px) { | |
| .controls-container { | |
| width: calc(100% - 32px); | |
| left: 16px; | |
| bottom: 16px; | |
| padding: 12px; | |
| } | |
| .dropdown-menu { | |
| max-height: 350px; | |
| } | |
| .dropdown-list { | |
| max-height: 200px; | |
| } | |
| .overlay-frame { | |
| width: 250px; | |
| height: 260px; | |
| } | |
| .overlay-image-container { | |
| width: 200px; | |
| height: 120px; | |
| } | |
| .stamp-text { | |
| font-size: 12px; | |
| } | |
| #page1-overlay { | |
| top: 700px; | |
| right: 320px; | |
| } | |
| #page2-overlay { | |
| top: 1000px; | |
| left: 500px; | |
| } | |
| .page-div { | |
| width: 100%; | |
| height: auto; | |
| margin: 10px 0; | |
| } | |
| .control-btn { | |
| font-size: 14px; | |
| padding: 10px 12px; | |
| } | |
| } | |
| /* Loading Spinner */ | |
| .loading-spinner { | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid transparent; | |
| border-top-color: currentColor; | |
| border-radius: 50%; | |
| animation: spin 0.8s linear infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| /* Progress Bar */ | |
| .progress-container { | |
| display: none; | |
| padding: 12px; | |
| background: #f0fdf4; | |
| border-radius: 8px; | |
| border: 1px solid #bbf7d0; | |
| } | |
| .progress-container.visible { | |
| display: block; | |
| } | |
| .progress-bar { | |
| height: 8px; | |
| background: #e2e8f0; | |
| border-radius: 4px; | |
| overflow: hidden; | |
| margin-bottom: 8px; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background: linear-gradient(90deg, #10b981, #34d399); | |
| border-radius: 4px; | |
| transition: width 0.3s ease; | |
| } | |
| .progress-text { | |
| font-size: 13px; | |
| color: #166534; | |
| text-align: center; | |
| } | |
| /* Highlight matched text */ | |
| .highlight { | |
| background: #fef08a; | |
| padding: 0 2px; | |
| border-radius: 2px; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100"> | |
| <div class="controls-container" role="region" aria-label="Controls"> | |
| <!-- Custom Dropdown --> | |
| <div class="custom-dropdown" id="customDropdown"> | |
| <div class="dropdown-header" | |
| id="dropdownHeader" | |
| tabindex="0" | |
| role="combobox" | |
| aria-expanded="false" | |
| aria-haspopup="listbox" | |
| aria-label="เลือกพนักงาน"> | |
| <img id="dropdownProfilePic" src="https://via.placeholder.com/32?text=..." alt="Profile"/> | |
| <span class="dropdown-header-text" id="selectedText">กรุณาเลือกพนักงาน</span> | |
| <svg class="dropdown-arrow" id="dropdownArrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <polyline points="6,9 12,15 18,9"></polyline> | |
| </svg> | |
| </div> | |
| <div class="dropdown-menu" id="dropdownMenu" role="listbox" aria-label="รายชื่อพนักงาน"> | |
| <div class="search-container"> | |
| <div class="search-wrapper"> | |
| <input type="text" | |
| class="search-input" | |
| id="searchInput" | |
| placeholder="ค้นหา ชื่อ, เลขที่, หรือ ID..." | |
| aria-label="ค้นหาพนักงาน" | |
| autocomplete="off"/> | |
| <svg class="search-icon" id="searchIcon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <circle cx="11" cy="11" r="8"></circle> | |
| <line x1="21" y1="21" x2="16.65" y2="16.65"></line> | |
| </svg> | |
| <button class="clear-search" id="clearSearch" aria-label="ล้างการค้นหา">×</button> | |
| </div> | |
| </div> | |
| <div class="dropdown-list" id="dropdownList"> | |
| <!-- Items will be populated here --> | |
| </div> | |
| <div class="dropdown-actions"> | |
| <div class="selected-count" id="selectedCount">เลือกแล้ว 0 รายการ</div> | |
| <button class="action-btn btn-select-all" id="selectAllBtn" aria-label="เลือกทั้งหมด"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <polyline points="9,11 12,14 22,4"></polyline> | |
| <path d="M21,12v7a2,2,0,0,1-2,2H5a2,2,0,0,1-2-2V5A2,2,0,0,1,5,3h11"></path> | |
| </svg> | |
| เลือกทั้งหมด | |
| </button> | |
| <button class="action-btn btn-clear-selection" id="clearSelectionBtn" aria-label="ล้างการเลือก"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <line x1="18" y1="6" x2="6" y2="18"></line> | |
| <line x1="6" y1="6" x2="18" y2="18"></line> | |
| </svg> | |
| ล้าง | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Progress Bar --> | |
| <div class="progress-container" id="progressContainer"> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" id="progressFill" style="width: 0%"></div> | |
| </div> | |
| <div class="progress-text" id="progressText">กำลังดาวน์โหลด...</div> | |
| </div> | |
| <!-- Control Buttons --> | |
| <button type="button" id="grayscaleBtn" class="control-btn btn-grayscale"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <circle cx="12" cy="12" r="10"></circle> | |
| <path d="M12 2a10 10 0 0 1 0 20"></path> | |
| </svg> | |
| Grayscale: ปิด | |
| </button> | |
| <button id="downloadCurrentBtn" class="control-btn btn-download-current"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> | |
| <polyline points="7,10 12,15 17,10"></polyline> | |
| <line x1="12" y1="15" x2="12" y2="3"></line> | |
| </svg> | |
| ดาวน์โหลด PDF ปัจจุบัน | |
| </button> | |
| <button id="downloadSelectedBtn" class="control-btn btn-download-selected" disabled> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> | |
| <polyline points="7,10 12,15 17,10"></polyline> | |
| <line x1="12" y1="15" x2="12" y2="3"></line> | |
| </svg> | |
| ดาวน์โหลด PDF ที่เลือก (0) | |
| </button> | |
| <button id="downloadAllBtn" class="control-btn btn-download-all"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> | |
| <polyline points="7,10 12,15 17,10"></polyline> | |
| <line x1="12" y1="15" x2="12" y2="3"></line> | |
| </svg> | |
| ดาวน์โหลด PDF ทั้งหมด | |
| </button> | |
| <button id="randomizeBtn" class="control-btn btn-randomize"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <polyline points="16,3 21,3 21,8"></polyline> | |
| <line x1="4" y1="20" x2="21" y2="3"></line> | |
| <polyline points="21,16 21,21 16,21"></polyline> | |
| <line x1="15" y1="15" x2="21" y2="21"></line> | |
| <line x1="4" y1="4" x2="9" y2="9"></line> | |
| </svg> | |
| สุ่มตำแหน่งภาพ | |
| </button> | |
| </div> | |
| <!-- Page 1: Work Permit --> | |
| <div id="page1-div" class="page-div"> | |
| <img width="892" height="1261" src="bg3.svg" alt="Background"/> | |
| <div id="overlayContainer1" class="overlay-frame" style="width: 300px; height: 200px; position: absolute; top: 700px; right: 320px;"></div> | |
| <img class="absolute top-[46px] left-[725px] w-[110px] h-[138px] object-cover z-[100]" id="profilePic" src="https://via.placeholder.com/110x138?text=Loading" alt="Profile Picture"/> | |
| <img class="absolute top-[977px] left-[762.5px] w-[69px] h-[69px] object-cover" id="qrCode" src="https://via.placeholder.com/69?text=Loading" alt="QR Code"/> | |
| <p class="absolute top-[32px] left-[134px] whitespace-nowrap text-[21px] font-bold">ทะเบียนใบอนุญาตทำงานของคนต่างด้าวตามมติคณะรัฐมนตรี เมื่อวันที่ 24 กันยายน 2567</p> | |
| <p class="absolute top-[57px] left-[134px] whitespace-nowrap text-[21px] font-bold">เอกสารฉบับนี้ใช้แทนใบอนุญาตทำงาน</p> | |
| <p class="absolute top-[87px] left-[158px] whitespace-nowrap text-[15px]">เลขรับที่ (No.)</p> | |
| <p class="absolute top-[87px] left-[224px] whitespace-nowrap text-[14px]">:</p> | |
| <p class="absolute top-[87px] left-[238px] whitespace-nowrap text-[14px] font-bold" id="requestNumber">xxxxxxx</p> | |
| <p class="absolute top-[87px] left-[417px] whitespace-nowrap text-[15px]">วันที่อนุมัติ (Date)</p> | |
| <p class="absolute top-[87px] left-[500px] whitespace-nowrap text-[14px]">:</p> | |
| <p class="absolute top-[87px] left-[514px] whitespace-nowrap text-[15px] font-bold">06 มีนาคม 2568</p> | |
| <p class="absolute top-[114px] left-[62px] whitespace-nowrap text-[15px]">ชื่อคนต่างด้าว (Name of Applicant)</p> | |
| <p class="absolute top-[114px] left-[224px] whitespace-nowrap text-[14px]">:</p> | |
| <p class="absolute top-[114px] left-[238px] whitespace-nowrap text-[14px] font-bold" id="englishName">xxxxxxxxxxxxx</p> | |
| <p class="absolute top-[140px] left-[94px] whitespace-nowrap text-[15px]">เจ้าหน้าที่ (Name of Officer)</p> | |
| <p class="absolute top-[140px] left-[224px] whitespace-nowrap text-[14px]">:</p> | |
| <p class="absolute top-[140px] left-[238px] whitespace-nowrap text-[15px] font-bold">นายสมมาตร อนันต์ธราทรัพย์</p> | |
| <p class="absolute top-[140px] left-[440px] whitespace-nowrap text-[15px]">นายทะเบียน</p> | |
| <p class="absolute top-[140px] left-[500px] whitespace-nowrap text-[14px]">:</p> | |
| <p class="absolute top-[167px] left-[238px] whitespace-nowrap text-[15px]">จัดหางานจังหวัดระยอง</p> | |
| <p class="absolute top-[163px] left-[449px] whitespace-nowrap text-[15px]">(Registrar)</p> | |
| <p class="absolute top-[163px] left-[546px] whitespace-nowrap text-[15px]">นายสมชาย มรกตศรีวรรณ</p> | |
| <p class="absolute top-[182px] left-[555px] whitespace-nowrap text-[15px]">อธิบดีกรมการจัดหางาน</p> | |
| <p class="absolute top-[201px] left-[577px] whitespace-nowrap text-[15px]">นายทะเบียน</p> | |
| <p class="absolute top-[227px] left-[171px] whitespace-nowrap text-[16px] font-bold">ลงเลขรับและชำระค่ายื่นแบบคำขอ (REGISTERING APPLICATION FORM AND PAYING APPLICATION FEE)</p> | |
| <p class="absolute top-[254px] left-[55px] whitespace-nowrap text-[15px] font-bold leading-[21px]">ข้อมูลคนต่างด้าว</p> | |
| <p class="absolute top-[278px] left-[55px] whitespace-nowrap text-[14px]">สถานะใบอนุญาต</p> | |
| <p class="absolute top-[278px] left-[199px] whitespace-nowrap text-[14px]">: อนุมัติ (รอพิมพ์บัตร)</p> | |
| <p class="absolute top-[278px] left-[440px] whitespace-nowrap text-[14px]">ออกให้ ณ จังหวัด</p> | |
| <p class="absolute top-[278px] left-[586px] whitespace-nowrap text-[14px]">: สำนักงานจัดหางานจังหวัดระยอง</p> | |
| <p class="absolute top-[300px] left-[55px] whitespace-nowrap text-[14px]">เลขประจำตัวคนต่างด้าว</p> | |
| <p class="absolute top-[300px] left-[199px] whitespace-nowrap text-[14px]" id="personalID">: 6685490000472</p> | |
| <p class="absolute top-[300px] left-[440px] whitespace-nowrap text-[14px]">ใบอนุญาตทำงานเลขที่</p> | |
| <p class="absolute top-[300px] left-[586px] whitespace-nowrap text-[14px]" id="workPermitNumber">: 5400689000472</p> | |
| <p class="absolute top-[321px] left-[55px] whitespace-nowrap text-[14px]">ชื่อภาษาไทย</p> | |
| <p class="absolute top-[321px] left-[199px] whitespace-nowrap text-[14px]" id="thaiName">: นาง เมย์ เท็ท โช</p> | |
| <p class="absolute top-[321px] left-[440px] whitespace-nowrap text-[14px]">ชื่อภาษาอังกฤษ</p> | |
| <p class="absolute top-[321px] left-[586px] whitespace-nowrap text-[14px]">:</p> | |
| <p class="absolute top-[321px] left-[592px] whitespace-nowrap text-[14px]" id="englishNameDuplicate">MRS. MAY THET CHO</p> | |
| <p class="absolute top-[343px] left-[55px] whitespace-nowrap text-[14px]">วัน/เดือน/ปี (พ.ศ.) เกิด</p> | |
| <p class="absolute top-[343px] left-[199px] whitespace-nowrap text-[14px]" id="birthDate">: xx/xx/xx</p> | |
| <p class="absolute top-[343px] left-[440px] whitespace-nowrap text-[14px]">อายุ (ปี)</p> | |
| <p class="absolute top-[343px] left-[586px] whitespace-nowrap text-[14px]" id="age">: xx</p> | |
| <p class="absolute top-[365px] left-[55px] whitespace-nowrap text-[14px]">สัญชาติ</p> | |
| <p class="absolute top-[365px] left-[199px] whitespace-nowrap text-[14px]" id="nationality">: เมียนมา</p> | |
| <p class="absolute top-[365px] left-[440px] whitespace-nowrap text-[14px]">สถานภาพ</p> | |
| <p class="absolute top-[365px] left-[586px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[386px] left-[55px] whitespace-nowrap text-[14px]">ชื่อ-สกุล บิดา</p> | |
| <p class="absolute top-[386px] left-[199px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[386px] left-[440px] whitespace-nowrap text-[14px]">ชื่อ-สกุล มารดา</p> | |
| <p class="absolute top-[386px] left-[586px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[408px] left-[55px] whitespace-nowrap text-[14px]">เลขอ้างอิงคนต่างด้าว</p> | |
| <p class="absolute top-[408px] left-[199px] whitespace-nowrap text-[14px]" id="alienReferenceNumber">: xxxxxxxxxxxxx</p> | |
| <p class="absolute top-[429px] left-[55px] whitespace-nowrap text-[14px]">ที่อยู่อาศัย</p> | |
| <p class="absolute top-[429px] left-[199px] whitespace-nowrap text-[14px]">: 36/6 หมู่ที่ 8 ตำบลมาบข่า อำเภอนิคมพัฒนา จังหวัดระยอง 21180</p> | |
| <p class="absolute top-[449px] left-[55px] whitespace-nowrap text-[15px] font-bold leading-[21px]">ข้อมูลหนังสือเดินทาง และข้อมูลการตรวจลงตรา</p> | |
| <p class="absolute top-[469px] left-[55px] whitespace-nowrap text-[14px]">เลขที่หนังสือเดินทาง</p> | |
| <p class="absolute top-[472px] left-[199px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[472px] left-[440px] whitespace-nowrap text-[14px]">ประเภทหนังสือเดินทาง</p> | |
| <p class="absolute top-[472px] left-[586px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[494px] left-[55px] whitespace-nowrap text-[14px]">สถานที่ออกหนังสือเดินทาง</p> | |
| <p class="absolute top-[494px] left-[199px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[494px] left-[440px] whitespace-nowrap text-[14px]">ประเทศที่ออกหนังสือเดินทาง</p> | |
| <p class="absolute top-[494px] left-[586px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[516px] left-[55px] whitespace-nowrap text-[14px]">วันที่ออกหนังสือเดินทาง</p> | |
| <p class="absolute top-[516px] left-[199px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[516px] left-[440px] whitespace-nowrap text-[14px]">วันหมดอายุ</p> | |
| <p class="absolute top-[516px] left-[586px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[537px] left-[55px] whitespace-nowrap text-[14px]">เลขที่ตรวจลงตรา</p> | |
| <p class="absolute top-[537px] left-[199px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[559px] left-[55px] whitespace-nowrap text-[14px]">ออกให้วันที่</p> | |
| <p class="absolute top-[559px] left-[199px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[559px] left-[440px] whitespace-nowrap text-[14px]">ใช้ได้ถึงวันที่</p> | |
| <p class="absolute top-[559px] left-[586px] whitespace-nowrap text-[14px]">: -</p> | |
| <p class="absolute top-[578px] left-[55px] whitespace-nowrap text-[15px] font-bold leading-[21px]">ข้อมูลนายจ้าง/สถานประกอบการ</p> | |
| <p class="absolute top-[598px] left-[55px] whitespace-nowrap text-[14px]">เลขประจำตัวนายจ้าง</p> | |
| <p class="absolute top-[602px] left-[199px] whitespace-nowrap text-[14px]">: 0415567000061</p> | |
| <p class="absolute top-[602px] left-[440px] whitespace-nowrap text-[14px]">ชื่อนายจ้าง/สถานประกอบการ</p> | |
| <p class="absolute top-[602px] left-[586px] whitespace-nowrap text-[14px]">: บริษัท บาน กง เอ็นจิเนียริ่ง จำกัด</p> | |
| <p class="absolute top-[623px] left-[55px] whitespace-nowrap text-[14px]">ประเภทกิจการ</p> | |
| <p class="absolute top-[623px] left-[199px] whitespace-nowrap text-[14px]">: BT04 - กิจการก่อสร้าง</p> | |
| <p class="absolute top-[645px] left-[55px] whitespace-nowrap text-[14px]">ที่ตั้งสำนักงาน</p> | |
| <p class="absolute top-[645px] left-[199px] whitespace-nowrap text-[14px]">: 102 หมู่ที่ 8 ถนนอุดรธานี-ขอนแก่น ตำบลโนนสูง อำเภอเมืองอุดรธานี จังหวัดอุดรธานี 41000</p> | |
| <p class="absolute top-[666px] left-[55px] whitespace-nowrap text-[15px] font-bold leading-[21px]">ข้อมูลการทำงาน</p> | |
| <p class="absolute top-[684px] left-[55px] whitespace-nowrap text-[14px]">ทำงานในตำแหน่ง</p> | |
| <p class="absolute top-[688px] left-[199px] whitespace-nowrap text-[14px]">: กรรมกร</p> | |
| <p class="absolute top-[688px] left-[440px] whitespace-nowrap text-[14px]">ลักษณะงาน</p> | |
| <p class="absolute top-[688px] left-[586px] whitespace-nowrap text-[14px]">: กรรมกร (กิจการก่อสร้าง)</p> | |
| <p class="absolute top-[710px] left-[55px] whitespace-nowrap text-[14px]">สถานที่ทำงาน</p> | |
| <p class="absolute top-[710px] left-[199px] whitespace-nowrap text-[14px]">: 36/6 หมู่ที่ 8 ตำบลมาบข่า อำเภอนิคมพัฒนา จังหวัดระยอง 21180</p> | |
| <p class="absolute top-[731px] left-[55px] whitespace-nowrap text-[14px]">อนุญาตให้ทำงานถึงวันที่</p> | |
| <p class="absolute top-[731px] left-[199px] whitespace-nowrap text-[14px]">: 31 มีนาคม 2569</p> | |
| <p class="absolute top-[755px] left-[55px] whitespace-nowrap text-[15px] font-bold leading-[21px]">ข้อมูลสิทธิการรักษาพยาบาล</p> | |
| <p class="absolute top-[775px] left-[55px] whitespace-nowrap text-[15px] leading-[21px]">ประกันสังคม</p> | |
| <p class="absolute top-[796px] left-[55px] whitespace-nowrap text-[15px] leading-[21px]">ประกันสุขภาพ สิ้นสุดวันที่ 30/09/2025</p> | |
| <p class="absolute top-[825px] left-[55px] whitespace-nowrap text-[14px] font-bold leading-[19px]">เงื่อนไข</p> | |
| <p class="absolute top-[841px] left-[85px] whitespace-nowrap text-[14px] leading-[19px]">คนต่างด้าวจะต้องทำประกันสุขภาพตลอดระยะเวลาการอนุญาตให้ทำงาน หากปรากฎว่าระยะเวลาการทำประกันสุขภาพสิ้นสุดลง ก่อนระยะเวลาการอนุญาตให้ทำงาน</p> | |
| <p class="absolute top-[857px] left-[55px] whitespace-nowrap text-[14px] leading-[19px]">นายทะเบียนจะเพิกถอนใบอนุญาตทำงาน ซึ่งมีผลให้การอนุญาตให้อยู่ในราชอาณาจักรสิ้นสุดลง</p> | |
| <p class="absolute top-[885px] left-[55px] whitespace-nowrap text-[14px] font-bold leading-[19px]">คำเตือน</p> | |
| <p class="absolute top-[905px] left-[85px] whitespace-nowrap text-[14px] leading-[19px]">เมื่อได้รับอนุญาตให้ทำงานแล้วคนต่างด้าวต้องดำเนินการดังต่อไปนี้ มิเช่นนั้น การอนุญาตให้ทำงานและการอนุญาตให้อยู่ในราชอาณาจักรของคนต่างด้าวจะสิ้นสุดลง</p> | |
| <p class="absolute top-[922px] left-[55px] whitespace-nowrap text-[14px] leading-[19px]">1. จัดเก็บข้อมูลอัตลักษณ์บุคคล ภายในวันที่ 28 มิถุนายน 2568</p> | |
| <p class="absolute top-[939px] left-[55px] whitespace-nowrap text-[14px] leading-[19px]">2. จัดทำหรือปรับปรุงทะเบียนประวัติตามกฎหมายว่าด้วยการทะเบียนราษฎร ภายในวันที่ 31 มีนาคม 2569</p> | |
| <p class="absolute top-[1000px] left-[55px] text-[14px] leading-[19px]" id="timestamp">Loading...</p> | |
| </div> | |
| <!-- Page 2: Receipt --> | |
| <div id="page2-div" class="page-div"> | |
| <img width="892" height="1261" src="bg2.svg" alt="Receipt Background"/> | |
| <div id="overlayContainer2" class="overlay-frame" style="position: absolute; top: 1000px; left: 500px;"></div> | |
| <img class="absolute top-[925px] left-[120px] w-[90px] h-[90px] object-cover" id="receiptQrCode" src="https://via.placeholder.com/90?text=Loading" alt="Receipt QR Code"/> | |
| <p style="position:absolute;top:147px;left:86px;white-space:nowrap" class="ft00">กรมการจัดหางาน</p> | |
| <p style="position:absolute;top:170px;left:88px;white-space:nowrap" class="ft00">กระทรวงแรงงาน</p> | |
| <p style="position:absolute;top:90px;left:397px;white-space:nowrap" class="ft01"><b>ใบเสร็จรับเงิน</b></p> | |
| <p style="position:absolute;top:120px;left:418px;white-space:nowrap" class="ft01"><b>ต้นฉบับ</b></p> | |
| <p style="position:absolute;top:60px;left:598px;white-space:nowrap" class="ft00">เลขที่</p> | |
| <p style="position:absolute;top:60px;left:640px;white-space:nowrap" class="ft00" id="receiptNumberReceipt">xxxxxxx</p> | |
| <p style="position:absolute;top:149px;left:582px;white-space:nowrap" class="ft00">ที่ทำการ   สำนักบริหารแรงงานต่างด้าว</p> | |
| <p style="position:absolute;top:188px;left:602px;white-space:nowrap" class="ft00">วันที่   19 มีนาคม 2568</p> | |
| <p style="position:absolute;top:227px;left:540px;white-space:nowrap" class="ft00">เลขที่ใบชำระเงิน  </p> | |
| <p style="position:absolute;top:227px;left:640px;white-space:nowrap" class="ft00" id="paymentNumberReceipt">IV680329/002308</p> | |
| <p style="position:absolute;top:271px;left:60px;white-space:nowrap" class="ft00">เลขรับคำขอที่</p> | |
| <p style="position:absolute;top:271px;left:180px;white-space:nowrap" class="ft00" id="requestNumberReceipt">xxxxxxx</p> | |
| <p style="position:absolute;top:310px;left:60px;white-space:nowrap" class="ft00">ชื่อผู้ชำระเงิน</p> | |
| <p style="position:absolute;top:310px;left:180px;white-space:nowrap" class="ft00" id="payerNameReceipt">xxxxxxxxxxxxx</p> | |
| <p style="position:absolute;top:310px;left:471px;white-space:nowrap" class="ft00">สัญชาติ</p> | |
| <p style="position:absolute;top:310px;left:520px;white-space:nowrap" class="ft00" id="nationalityReceipt">เมียนมา</p> | |
| <p style="position:absolute;top:354px;left:60px;white-space:nowrap" class="ft00">เลขอ้างอิงคนต่างด้าว</p> | |
| <p style="position:absolute;top:354px;left:180px;white-space:nowrap" class="ft00" id="alienReferenceReceipt">xxxxxxxxxxxxx</p> | |
| <p style="position:absolute;top:354px;left:432px;white-space:nowrap" class="ft00">หมายเลขประจำตัวคนต่างด้าว</p> | |
| <p style="position:absolute;top:354px;left:640px;white-space:nowrap" class="ft00" id="personalIDReceipt">xxxxxxxxxxxxx</p> | |
| <p style="position:absolute;top:399px;left:60px;white-space:nowrap" class="ft00">ชื่อนายจ้าง / สถานประกอบการ   บริษัท บาน กง เอ็นจิเนียริ่ง จำกัด</p> | |
| <p style="position:absolute;top:438px;left:60px;white-space:nowrap" class="ft00">เลขประจำตัวนายจ้าง</p> | |
| <p style="position:absolute;top:437px;left:233px;white-space:nowrap" class="ft00">  0415567000061</p> | |
| <p style="position:absolute;top:526px;left:345px;white-space:nowrap" class="ft02"><b>รายการ</b></p> | |
| <p style="position:absolute;top:526px;left:688px;white-space:nowrap" class="ft02"><b>จำนวนเงิน</b></p> | |
| <p style="position:absolute;top:572px;left:118px;white-space:nowrap" class="ft03">1. ค่าธรรมเนียมในการยื่นคำขอ ฉบับละ 100 บาท</p> | |
| <p style="position:absolute;top:572px;left:736px;white-space:nowrap" class="ft03">100.00</p> | |
| <p style="position:absolute;top:616px;left:118px;white-space:nowrap" class="ft03">2. ค่าธรรมเนียมใบอนุญาตทำงาน</p> | |
| <p style="position:absolute;top:616px;left:736px;white-space:nowrap" class="ft03">900.00</p> | |
| <p style="position:absolute;top:694px;left:97px;white-space:nowrap" class="ft03"> </p> | |
| <p style="position:absolute;top:694px;left:648px;white-space:nowrap" class="ft03"> </p> | |
| <p style="position:absolute;top:772px;left:174px;white-space:nowrap" class="ft02"><b>รวมเป็นเงินทั้งสิ้น (บาท)</b></p> | |
| <p style="position:absolute;top:799px;left:188px;white-space:nowrap" class="ft02"><b>( หนึ่งพันบาทถ้วน )</b></p> | |
| <p style="position:absolute;top:786px;left:385px;white-space:nowrap" class="ft03"> </p> | |
| <p style="position:absolute;top:774px;left:722px;white-space:nowrap" class="ft02"><b>1,000.00</b></p> | |
| <p style="position:absolute;top:894px;left:94px;white-space:nowrap" class="ft00">ได้รับเงินไว้เป็นการถูกต้องแล้ว</p> | |
| <p style="position:absolute;top:977px;left:481px;white-space:nowrap" class="ft00">(ลงชื่อ)</p> | |
| <p style="position:absolute;top:977px;left:564px;white-space:nowrap" class="ft00">นางสาวอารีวรรณ โพธิ์นิ่มแดง</p> | |
| <p style="position:absolute;top:977px;left:762px;white-space:nowrap" class="ft00">(ผู้รับเงิน)</p> | |
| <p style="position:absolute;top:1017px;left:473px;white-space:nowrap" class="ft00">ตำแหน่ง</p> | |
| <p style="position:absolute;top:1016px;left:562px;white-space:nowrap" class="ft00">นักวิชาการแรงงานชำนาญการ</p> | |
| <p style="position:absolute;top:1133px;left:55px;white-space:nowrap" class="ft06" id="receiptTimestamp">Loading...</p> | |
| </div> | |
| <script type="text/javascript"> | |
| let allWorkerData = []; | |
| let currentIndex = 0; | |
| let isGrayscaleEnabled = false; | |
| let selectedWorkers = new Set(); | |
| let isDropdownOpen = false; | |
| let filteredData = []; | |
| // DOM Elements | |
| const dropdownHeader = document.getElementById('dropdownHeader'); | |
| const dropdownMenu = document.getElementById('dropdownMenu'); | |
| const dropdownArrow = document.getElementById('dropdownArrow'); | |
| const dropdownList = document.getElementById('dropdownList'); | |
| const searchInput = document.getElementById('searchInput'); | |
| const searchIcon = document.getElementById('searchIcon'); | |
| const clearSearch = document.getElementById('clearSearch'); | |
| const selectedText = document.getElementById('selectedText'); | |
| const selectedCountEl = document.getElementById('selectedCount'); | |
| const selectAllBtn = document.getElementById('selectAllBtn'); | |
| const clearSelectionBtn = document.getElementById('clearSelectionBtn'); | |
| const downloadSelectedBtn = document.getElementById('downloadSelectedBtn'); | |
| const downloadCurrentBtn = document.getElementById('downloadCurrentBtn'); | |
| const downloadAllBtn = document.getElementById('downloadAllBtn'); | |
| const grayscaleBtn = document.getElementById('grayscaleBtn'); | |
| const randomizeBtn = document.getElementById('randomizeBtn'); | |
| const progressContainer = document.getElementById('progressContainer'); | |
| const progressFill = document.getElementById('progressFill'); | |
| const progressText = document.getElementById('progressText'); | |
| // Normalize text for search (handles Thai, English, numbers) | |
| function normalizeText(text) { | |
| if (!text) return ''; | |
| return text.toString().toLowerCase().trim() | |
| .replace(/\s+/g, ' '); | |
| } | |
| // Search function supporting Thai, English, and numbers | |
| function searchWorkers(query) { | |
| const normalizedQuery = normalizeText(query); | |
| if (!normalizedQuery) { | |
| filteredData = [...allWorkerData]; | |
| return filteredData; | |
| } | |
| filteredData = allWorkerData.filter(worker => { | |
| const searchFields = [ | |
| worker.requestNumber, | |
| worker.englishName, | |
| worker.thaiName, | |
| worker.personalID, | |
| worker.workPermitNumber, | |
| worker.alienReferenceNumber, | |
| worker.nationality | |
| ]; | |
| return searchFields.some(field => { | |
| const normalizedField = normalizeText(field); | |
| return normalizedField.includes(normalizedQuery); | |
| }); | |
| }); | |
| return filteredData; | |
| } | |
| // Highlight matched text | |
| function highlightText(text, query) { | |
| if (!text || !query) return text || ''; | |
| const normalizedQuery = normalizeText(query); | |
| if (!normalizedQuery) return text; | |
| const regex = new RegExp(`(${escapeRegex(query)})`, 'gi'); | |
| return text.replace(regex, '<span class="highlight">$1</span>'); | |
| } | |
| function escapeRegex(string) { | |
| return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | |
| } | |
| // Toggle dropdown | |
| function toggleDropdown(forceClose = false) { | |
| if (forceClose || isDropdownOpen) { | |
| isDropdownOpen = false; | |
| dropdownMenu.classList.remove('open'); | |
| dropdownArrow.classList.remove('open'); | |
| dropdownHeader.setAttribute('aria-expanded', 'false'); | |
| } else { | |
| isDropdownOpen = true; | |
| dropdownMenu.classList.add('open'); | |
| dropdownArrow.classList.add('open'); | |
| dropdownHeader.setAttribute('aria-expanded', 'true'); | |
| searchInput.focus(); | |
| renderDropdownList(); | |
| } | |
| } | |
| // Render dropdown list | |
| function renderDropdownList(query = '') { | |
| const workers = searchWorkers(query); | |
| if (workers.length === 0) { | |
| dropdownList.innerHTML = ` | |
| <div class="no-results"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <circle cx="11" cy="11" r="8"></circle> | |
| <line x1="21" y1="21" x2="16.65" y2="16.65"></line> | |
| </svg> | |
| <p>ไม่พบผลลัพธ์สำหรับ "${query}"</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| dropdownList.innerHTML = workers.map((worker, idx) => { | |
| const originalIndex = allWorkerData.findIndex(w => w.requestNumber === worker.requestNumber); | |
| const isChecked = selectedWorkers.has(originalIndex); | |
| const isActive = originalIndex === currentIndex; | |
| const displayName = query ? highlightText(worker.englishName || worker.thaiName || 'N/A', query) : (worker.englishName || worker.thaiName || 'N/A'); | |
| const displayId = query ? highlightText(worker.requestNumber || 'N/A', query) : (worker.requestNumber || 'N/A'); | |
| return ` | |
| <div class="dropdown-item ${isChecked ? 'selected' : ''} ${isActive ? 'active' : ''}" | |
| data-index="${originalIndex}" | |
| role="option" | |
| aria-selected="${isActive}" | |
| tabindex="0"> | |
| <img class="dropdown-item-image" | |
| src="${worker.profileImage || 'https://via.placeholder.com/40?text=N/A'}" | |
| alt="${worker.englishName || 'Worker'}" | |
| onerror="this.src='https://via.placeholder.com/40?text=N/A'"/> | |
| <div class="dropdown-item-content"> | |
| <div class="dropdown-item-title">${displayName}</div> | |
| <div class="dropdown-item-subtitle">เลขที่: ${displayId}</div> | |
| </div> | |
| <div class="dropdown-item-checkbox" onclick="event.stopPropagation(); toggleWorkerSelection(${originalIndex})"> | |
| <div class="custom-checkbox ${isChecked ? 'checked' : ''}" role="checkbox" aria-checked="${isChecked}"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"> | |
| <polyline points="20,6 9,17 4,12"></polyline> | |
| </svg> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| }).join(''); | |
| // Add click listeners | |
| dropdownList.querySelectorAll('.dropdown-item').forEach(item => { | |
| item.addEventListener('click', (e) => { | |
| if (!e.target.closest('.dropdown-item-checkbox')) { | |
| const index = parseInt(item.dataset.index); | |
| selectWorker(index); | |
| } | |
| }); | |
| // Keyboard navigation | |
| item.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter' || e.key === ' ') { | |
| e.preventDefault(); | |
| const index = parseInt(item.dataset.index); | |
| if (e.shiftKey) { | |
| toggleWorkerSelection(index); | |
| } else { | |
| selectWorker(index); | |
| } | |
| } | |
| }); | |
| }); | |
| } | |
| // Select a worker to view | |
| function selectWorker(index) { | |
| currentIndex = index; | |
| displayWorkerData(index); | |
| updateDropdownHeader(); | |
| randomizeBothOverlays(); | |
| } | |
| // Toggle worker selection for PDF download | |
| function toggleWorkerSelection(index) { | |
| if (selectedWorkers.has(index)) { | |
| selectedWorkers.delete(index); | |
| } else { | |
| selectedWorkers.add(index); | |
| } | |
| updateSelectionUI(); | |
| renderDropdownList(searchInput.value); | |
| } | |
| // Update selection UI | |
| function updateSelectionUI() { | |
| const count = selectedWorkers.size; | |
| selectedCountEl.textContent = `เลือกแล้ว ${count} รายการ`; | |
| downloadSelectedBtn.disabled = count === 0; | |
| downloadSelectedBtn.innerHTML = ` | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> | |
| <polyline points="7,10 12,15 17,10"></polyline> | |
| <line x1="12" y1="15" x2="12" y2="3"></line> | |
| </svg> | |
| ดาวน์โหลด PDF ที่เลือก (${count}) | |
| `; | |
| } | |
| // Update dropdown header | |
| function updateDropdownHeader() { | |
| if (currentIndex >= 0 && currentIndex < allWorkerData.length) { | |
| const worker = allWorkerData[currentIndex]; | |
| selectedText.textContent = `${worker.requestNumber || 'N/A'} - ${worker.englishName || worker.thaiName || 'N/A'}`; | |
| setImageSrc('dropdownProfilePic', worker.profileImage, 'https://via.placeholder.com/32?text=N/A', worker.englishName, 'dropdown profile'); | |
| } else { | |
| selectedText.textContent = 'กรุณาเลือกพนักงาน'; | |
| document.getElementById('dropdownProfilePic').src = 'https://via.placeholder.com/32?text=...'; | |
| } | |
| } | |
| // Select all workers | |
| function selectAllWorkers() { | |
| const currentFiltered = searchWorkers(searchInput.value); | |
| currentFiltered.forEach(worker => { | |
| const idx = allWorkerData.findIndex(w => w.requestNumber === worker.requestNumber); | |
| if (idx !== -1) selectedWorkers.add(idx); | |
| }); | |
| updateSelectionUI(); | |
| renderDropdownList(searchInput.value); | |
| } | |
| // Clear all selections | |
| function clearAllSelections() { | |
| selectedWorkers.clear(); | |
| updateSelectionUI(); | |
| renderDropdownList(searchInput.value); | |
| } | |
| // Event Listeners | |
| dropdownHeader.addEventListener('click', () => toggleDropdown()); | |
| dropdownHeader.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter' || e.key === ' ') { | |
| e.preventDefault(); | |
| toggleDropdown(); | |
| } | |
| }); | |
| searchInput.addEventListener('input', (e) => { | |
| const query = e.target.value; | |
| renderDropdownList(query); | |
| if (query) { | |
| searchIcon.style.display = 'none'; | |
| clearSearch.classList.add('visible'); | |
| } else { | |
| searchIcon.style.display = 'block'; | |
| clearSearch.classList.remove('visible'); | |
| } | |
| }); | |
| clearSearch.addEventListener('click', () => { | |
| searchInput.value = ''; | |
| searchIcon.style.display = 'block'; | |
| clearSearch.classList.remove('visible'); | |
| renderDropdownList(''); | |
| searchInput.focus(); | |
| }); | |
| selectAllBtn.addEventListener('click', selectAllWorkers); | |
| clearSelectionBtn.addEventListener('click', clearAllSelections); | |
| // Close dropdown when clicking outside | |
| document.addEventListener('click', (e) => { | |
| if (!e.target.closest('.custom-dropdown')) { | |
| toggleDropdown(true); | |
| } | |
| }); | |
| // Button event listeners | |
| grayscaleBtn.addEventListener('click', toggleGrayscale); | |
| downloadCurrentBtn.addEventListener('click', downloadCurrentPDF); | |
| downloadSelectedBtn.addEventListener('click', downloadSelectedPDFs); | |
| downloadAllBtn.addEventListener('click', downloadAllPDFs); | |
| randomizeBtn.addEventListener('click', randomizeBothOverlays); | |
| function setImageSrc(imgElementId, src, defaultSrc, workerName, type) { | |
| const imgElement = document.getElementById(imgElementId); | |
| if (!imgElement) { | |
| console.error(`Image element with ID "${imgElementId}" not found.`); | |
| return; | |
| } | |
| imgElement.src = src || defaultSrc; | |
| imgElement.alt = workerName || type; | |
| imgElement.crossOrigin = "anonymous"; | |
| imgElement.onerror = function() { | |
| imgElement.src = defaultSrc; | |
| imgElement.alt = 'No Image'; | |
| }; | |
| } | |
| function getCurrentTimestamp() { | |
| const now = new Date(); | |
| return now.toLocaleString('th-TH', { | |
| hour: '2-digit', | |
| minute: '2-digit', | |
| hour12: false | |
| }).replace(',', ' น.') + ' น.'; | |
| } | |
| function toggleGrayscale() { | |
| isGrayscaleEnabled = !isGrayscaleEnabled; | |
| document.body.style.filter = isGrayscaleEnabled ? 'grayscale(100%)' : 'grayscale(0%)'; | |
| grayscaleBtn.innerHTML = ` | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <circle cx="12" cy="12" r="10"></circle> | |
| <path d="M12 2a10 10 0 0 1 0 20"></path> | |
| </svg> | |
| Grayscale: ${isGrayscaleEnabled ? 'เปิด' : 'ปิด'} | |
| `; | |
| } | |
| function jitter(base, maxDelta = 5) { | |
| return base + (Math.random() * 2 - 1) * maxDelta; | |
| } | |
| const borImages = ['bor01.png', 'bor02.png', 'bor03.png', 'bor04.png']; | |
| let borCounter = 0; | |
| function randomizeOverlayPosition(containerId, startIdx, useSetA = false, borIndex = 0, opacity = 1, refPos = null) { | |
| const container = document.getElementById(containerId); | |
| if (!container) return []; | |
| const setA = ['a1.png','a2.png','a3.png','a4.png','a5.png','a6.png']; | |
| const setB = ['b1.png','b2.png','b3.png','b4.png','b5.png','b6.png']; | |
| const imageFiles = useSetA ? setA : setB; | |
| const borSrc = borImages[borCounter % borImages.length]; | |
| borCounter++; | |
| container.innerHTML = ''; | |
| const imgs = []; | |
| for (let i = 0; i < 3; i++) { | |
| const img = document.createElement('img'); | |
| img.src = imageFiles[(startIdx + i) % 6]; | |
| img.style.zIndex = [1,2,3][i]; | |
| img.style.position = 'absolute'; | |
| container.appendChild(img); | |
| imgs.push(img); | |
| } | |
| const borImg = document.createElement('img'); | |
| borImg.src = borSrc; | |
| borImg.style.opacity = opacity; | |
| borImg.style.zIndex = 0; | |
| borImg.style.position = 'absolute'; | |
| container.insertBefore(borImg, container.firstChild); | |
| Promise.all([ | |
| ...imgs.map(i => new Promise(r => { i.onload = r; if (i.complete) r(); })), | |
| new Promise(r => { borImg.onload = r; if (borImg.complete) r(); }) | |
| ]).then(() => { | |
| const w = container.offsetWidth; | |
| const h = container.offsetHeight; | |
| const positions = imgs.map((img, i) => { | |
| const imgW = img.offsetWidth; | |
| const imgH = img.offsetHeight; | |
| const maxX = w - imgW; | |
| const maxY = h - imgH; | |
| const baseX = refPos && refPos[i] ? refPos[i].x : Math.random() * maxX; | |
| const baseY = refPos && refPos[i] ? refPos[i].y : Math.random() * maxY; | |
| const x = jitter(baseX); | |
| const y = jitter(baseY); | |
| const rotation = (Math.random() - 0.5) * 10 + (startIdx + i) * 0.3; | |
| let transform = `rotate(${rotation}deg)`; | |
| if (parseInt(img.style.zIndex) === 3) { | |
| const scale = 1 + (Math.random() - 0.5) * 0.3; | |
| transform += ` scale(${scale})`; | |
| } | |
| img.style.left = `${Math.max(0, Math.min(x, maxX))}px`; | |
| img.style.top = `${Math.max(0, Math.min(y, maxY))}px`; | |
| img.style.transform = transform; | |
| if (i === 0) { | |
| borImg.style.left = img.style.left; | |
| borImg.style.top = img.style.top; | |
| borImg.style.transform = transform; | |
| } | |
| return { x: parseFloat(img.style.left), y: parseFloat(img.style.top) }; | |
| }); | |
| container._lastPositions = positions; | |
| }); | |
| return container._lastPositions || []; | |
| } | |
| function randomizeBothOverlays() { | |
| const useSetA = Math.random() < 0.5; | |
| const useReverse = Math.random() < 0.5; | |
| const start1 = useReverse ? 3 : 0; | |
| const start2 = useReverse ? 0 : 3; | |
| const borIndex1 = borCounter % 4; | |
| const borIndex2 = (borCounter + 1) % 4; | |
| borCounter++; | |
| const baseOpacity = 0.75 + Math.random() * 0.15; | |
| const opacity1 = Math.min(1, baseOpacity + 0.05); | |
| const opacity2 = Math.max(0.6, baseOpacity - 0.05); | |
| const ref = randomizeOverlayPosition('overlayContainer1', start1, useSetA, borIndex1, opacity1); | |
| randomizeOverlayPosition('overlayContainer2', start2, useSetA, borIndex2, opacity2, ref); | |
| } | |
| function loadWorkerData() { | |
| const urlParams = new URLSearchParams(window.location.search); | |
| const requestNumberFromUrl = urlParams.get('id'); | |
| // Set loading states | |
| document.getElementById('requestNumber').innerHTML = '<b>Loading data...</b>'; | |
| document.getElementById('englishName').innerHTML = '<b>Loading data...</b>'; | |
| document.getElementById('englishNameDuplicate').innerHTML = 'Loading data...'; | |
| document.getElementById('thaiName').innerHTML = ': Loading data...'; | |
| document.getElementById('personalID').innerHTML = ': Loading data...'; | |
| document.getElementById('workPermitNumber').innerHTML = ': Loading data...'; | |
| document.getElementById('alienReferenceNumber').innerHTML = ': Loading data...'; | |
| document.getElementById('nationality').innerHTML = ': Loading data...'; | |
| document.getElementById('age').innerHTML = ': Loading data...'; | |
| document.getElementById('birthDate').innerHTML = ': Loading data...'; | |
| document.getElementById('timestamp').innerHTML = 'Loading data...'; | |
| document.getElementById('requestNumberReceipt').innerHTML = 'Loading data...'; | |
| document.getElementById('receiptNumberReceipt').innerHTML = 'Loading data...'; | |
| document.getElementById('paymentNumberReceipt').innerHTML = 'Loading data...'; | |
| document.getElementById('payerNameReceipt').innerHTML = 'Loading data...'; | |
| document.getElementById('nationalityReceipt').innerHTML = 'Loading data...'; | |
| document.getElementById('alienReferenceReceipt').innerHTML = 'Loading data...'; | |
| document.getElementById('personalIDReceipt').innerHTML = 'Loading data...'; | |
| document.getElementById('receiptTimestamp').innerHTML = 'Loading data...'; | |
| fetch('combined-data.json') | |
| .then(response => { | |
| if (!response.ok) throw new Error('Failed to load combined-data.json'); | |
| return response.json(); | |
| }) | |
| .then(data => { | |
| if (!Array.isArray(data) || data.length === 0) throw new Error('Data is empty or not a valid array.'); | |
| allWorkerData = data; | |
| filteredData = [...data]; | |
| currentIndex = requestNumberFromUrl | |
| ? allWorkerData.findIndex(w => w.requestNumber && w.requestNumber.toString() === requestNumberFromUrl.toString()) | |
| : 0; | |
| if (currentIndex === -1) currentIndex = 0; | |
| displayWorkerData(currentIndex); | |
| updateDropdownHeader(); | |
| renderDropdownList(); | |
| updateSelectionUI(); | |
| }) | |
| .catch(error => { | |
| console.error('Error loading JSON:', error); | |
| showError(error.message); | |
| }); | |
| } | |
| function showError(message) { | |
| const errorDiv = document.createElement('div'); | |
| errorDiv.className = 'text-red-500 text-center mt-5 p-2 bg-red-100 border border-red-500 rounded'; | |
| errorDiv.innerHTML = `<p>Error loading: ${message}</p>`; | |
| document.body.appendChild(errorDiv); | |
| clearWorkerData(); | |
| setImageSrc('profilePic', null, 'https://via.placeholder.com/110x138?text=Error', 'N/A', 'profile picture'); | |
| setImageSrc('qrCode', null, 'https://via.placeholder.com/69?text=Error', 'N/A', 'work permit QR code'); | |
| setImageSrc('receiptQrCode', null, 'https://via.placeholder.com/90?text=Error', 'N/A', 'receipt QR code'); | |
| setImageSrc('dropdownProfilePic', null, 'https://via.placeholder.com/32?text=Error', 'N/A', 'dropdown profile picture'); | |
| const timestamp = getCurrentTimestamp(); | |
| const workPermitTimestamp = `เอกสารอิเล็กทรอนิกส์ฉบับนี้ถูกสร้างจากระบบอนุญาตทำงานคนต่างด้าวที่มีสถานะการทำงานไม่ถูกต้องตามกฎหมาย ตามมติคณะรัฐมนตรีเมื่อวันที่ 24 กันยายน 2567<br>โดยกรมการจัดหางาน กระทรวงแรงงาน<br>พิมพ์เอกสาร วันที่ 10/04/2568 ${timestamp} `; | |
| document.getElementById('timestamp').innerHTML = workPermitTimestamp; | |
| document.getElementById('receiptTimestamp').innerHTML = workPermitTimestamp; | |
| } | |
| function displayWorkerData(index) { | |
| const defaultProfileSrc = 'https://via.placeholder.com/110x138?text=No+Image'; | |
| const defaultQrCodeSrc = 'https://via.placeholder.com/69?text=No+QR'; | |
| const defaultReceiptQrCodeSrc = 'https://via.placeholder.com/90?text=No+QR'; | |
| if (!allWorkerData || !Array.isArray(allWorkerData) || index < 0 || index >= allWorkerData.length) { | |
| clearWorkerData(); | |
| setImageSrc('profilePic', null, defaultProfileSrc, 'N/A', 'profile picture'); | |
| setImageSrc('qrCode', null, defaultQrCodeSrc, 'N/A', 'work permit QR code'); | |
| setImageSrc('receiptQrCode', null, defaultReceiptQrCodeSrc, 'N/A', 'receipt QR code'); | |
| const timestamp = getCurrentTimestamp(); | |
| const workPermitTimestamp = `เอกสารอิเล็กทรอนิกส์ฉบับนี้ถูกสร้างจากระบบอนุญาตทำงานคนต่างด้าวที่มีสถานะการทำงานไม่ถูกต้องตามกฎหมาย ตามมติคณะรัฐมนตรีเมื่อวันที่ 24 กันยายน 2567<br>โดยกรมการจัดหางาน กระทรวงแรงงาน<br>พิมพ์เอกสาร วันที่ 10/04/2568 ${timestamp} `; | |
| document.getElementById('timestamp').innerHTML = workPermitTimestamp; | |
| document.getElementById('receiptTimestamp').innerHTML = workPermitTimestamp; | |
| return; | |
| } | |
| const worker = allWorkerData[index]; | |
| document.getElementById('requestNumber').innerHTML = `<b>${worker.requestNumber || 'N/A'}</b>`; | |
| document.getElementById('englishName').innerHTML = `<b>${worker.englishName || 'N/A'}</b>`; | |
| document.getElementById('englishNameDuplicate').innerHTML = worker.englishName || 'N/A'; | |
| document.getElementById('thaiName').innerHTML = `: ${worker.thaiName || 'N/A'}`; | |
| document.getElementById('personalID').innerHTML = `: ${worker.personalID || 'N/A'}`; | |
| document.getElementById('workPermitNumber').innerHTML = `: ${worker.workPermitNumber || 'N/A'}`; | |
| document.getElementById('alienReferenceNumber').innerHTML = `: ${worker.alienReferenceNumber || 'N/A'}`; | |
| document.getElementById('nationality').innerHTML = `: ${worker.nationality || 'N/A'}`; | |
| document.getElementById('age').innerHTML = `: ${worker.age || 'N/A'}`; | |
| document.getElementById('birthDate').innerHTML = `: ${worker.birthDate || 'N/A'}`; | |
| document.getElementById('requestNumberReceipt').innerHTML = worker.requestNumber || 'N/A'; | |
| document.getElementById('receiptNumberReceipt').innerHTML = worker.receiptNumber || 'N/A'; | |
| document.getElementById('paymentNumberReceipt').innerHTML = worker.paymentNumber || 'N/A'; | |
| document.getElementById('payerNameReceipt').innerHTML = worker.englishName || worker.thaiName || 'N/A'; | |
| document.getElementById('nationalityReceipt').innerHTML = worker.nationality || 'N/A'; | |
| document.getElementById('alienReferenceReceipt').innerHTML = worker.alienReferenceNumber || 'N/A'; | |
| document.getElementById('personalIDReceipt').innerHTML = worker.personalID || 'N/A'; | |
| setImageSrc('profilePic', worker.profileImage, defaultProfileSrc, worker.englishName, 'profile picture'); | |
| const currentDomain = window.location.origin; | |
| const workerPageUrl = `${currentDomain}/worker.html?id=${encodeURIComponent(worker.requestNumber || '')}`; | |
| const receiptUrl = `${currentDomain}/pdf.html?id=${encodeURIComponent(worker.requestNumber || '')}`; | |
| setImageSrc('qrCode', `https://api.qrserver.com/v1/create-qr-code/?size=300x300&ecc=H&data=${encodeURIComponent(workerPageUrl)}`, defaultQrCodeSrc, worker.englishName, 'work permit QR code'); | |
| setImageSrc('receiptQrCode', `https://api.qrserver.com/v1/create-qr-code/?size=300x300&ecc=H&data=${encodeURIComponent(receiptUrl)}`, defaultReceiptQrCodeSrc, worker.englishName, 'receipt QR code'); | |
| const timestamp = getCurrentTimestamp(); | |
| const workPermitTimestamp = `เอกสารอิเล็กทรอนิกส์ฉบับนี้ถูกสร้างจากระบบอนุญาตทำงานคนต่างด้าวที่มีสถานะการทำงานไม่ถูกต้องตามกฎหมาย ตามมติคณะรัฐมนตรีเมื่อวันที่ 24 กันยายน 2567<br>โดยกรมการจัดหางาน กระทรวงแรงงาน<br>พิมพ์เอกสาร วันที่ 10/04/2568 ${timestamp} `; | |
| document.getElementById('timestamp').innerHTML = workPermitTimestamp; | |
| document.getElementById('receiptTimestamp').innerHTML = workPermitTimestamp; | |
| } | |
| function clearWorkerData() { | |
| document.getElementById('requestNumber').innerHTML = '<b>N/A</b>'; | |
| document.getElementById('englishName').innerHTML = '<b>N/A</b>'; | |
| document.getElementById('englishNameDuplicate').innerHTML = 'N/A'; | |
| document.getElementById('thaiName').innerHTML = ': N/A'; | |
| document.getElementById('personalID').innerHTML = ': N/A'; | |
| document.getElementById('workPermitNumber').innerHTML = ': N/A'; | |
| document.getElementById('alienReferenceNumber').innerHTML = ': N/A'; | |
| document.getElementById('nationality').innerHTML = ': N/A'; | |
| document.getElementById('age').innerHTML = ': N/A'; | |
| document.getElementById('birthDate').innerHTML = ': N/A'; | |
| document.getElementById('requestNumberReceipt').innerHTML = 'N/A'; | |
| document.getElementById('receiptNumberReceipt').innerHTML = 'N/A'; | |
| document.getElementById('paymentNumberReceipt').innerHTML = 'N/A'; | |
| document.getElementById('payerNameReceipt').innerHTML = 'N/A'; | |
| document.getElementById('nationalityReceipt').innerHTML = 'N/A'; | |
| document.getElementById('alienReferenceReceipt').innerHTML = 'N/A'; | |
| document.getElementById('personalIDReceipt').innerHTML = 'N/A'; | |
| } | |
| // Show/hide progress bar | |
| function showProgress(show, current = 0, total = 0) { | |
| if (show) { | |
| progressContainer.classList.add('visible'); | |
| const percent = total > 0 ? Math.round((current / total) * 100) : 0; | |
| progressFill.style.width = `${percent}%`; | |
| progressText.textContent = `กำลังดาวน์โหลด ${current}/${total} (${percent}%)`; | |
| } else { | |
| progressContainer.classList.remove('visible'); | |
| } | |
| } | |
| // Disable/enable all buttons | |
| function setButtonsDisabled(disabled) { | |
| downloadCurrentBtn.disabled = disabled; | |
| downloadSelectedBtn.disabled = disabled || selectedWorkers.size === 0; | |
| downloadAllBtn.disabled = disabled; | |
| randomizeBtn.disabled = disabled; | |
| grayscaleBtn.disabled = disabled; | |
| } | |
| async function downloadPDF(workerItem, filenameSuffix = '') { | |
| try { | |
| randomizeBothOverlays(); | |
| const elements = [document.getElementById('page1-div'), document.getElementById('page2-div')]; | |
| const reqNum = workerItem.requestNumber || 'worker'; | |
| const english = (workerItem.englishName || '').replace(/\s+/g, ''); // ตัดช่องว่างออก | |
| const last4 = reqNum.slice(-4); // เลขท้าย 4 หลัก | |
| // ถ้าอยากใช้เลขท้าย 4: | |
| const filename = `${last4}_${english || 'noname'}.pdf`; | |
| const controls = document.querySelector('.controls-container'); | |
| if (controls) controls.style.display = 'none'; | |
| if (allWorkerData[currentIndex].requestNumber !== workerItem.requestNumber) { | |
| const tempIndex = allWorkerData.findIndex(w => w.requestNumber === workerItem.requestNumber); | |
| if (tempIndex !== -1) displayWorkerData(tempIndex); | |
| } | |
| const images = document.querySelectorAll('img'); | |
| await Promise.all(Array.from(images).map(img => { | |
| return new Promise(resolve => { | |
| if (img.complete && img.naturalWidth > 0) resolve(); | |
| else { | |
| img.onload = resolve; | |
| img.onerror = () => { | |
| console.error(`Image failed to load: ${img.src}`); | |
| resolve(); | |
| }; | |
| } | |
| }); | |
| })); | |
| const container = document.createElement('div'); | |
| container.style.width = '892px'; | |
| container.style.height = '2522px'; | |
| container.className = 'pdf-export-container'; | |
| const page1Clone = elements[0].cloneNode(true); | |
| const page2Clone = elements[1].cloneNode(true); | |
| page1Clone.style.marginBottom = '0'; | |
| page2Clone.style.marginTop = '0'; | |
| container.appendChild(page1Clone); | |
| container.appendChild(page2Clone); | |
| document.body.appendChild(container); | |
| const allImages = container.querySelectorAll('img'); | |
| allImages.forEach(img => { | |
| img.style.filter = 'grayscale(100%)'; | |
| img.style.webkitFilter = 'grayscale(100%)'; | |
| }); | |
| await new Promise(resolve => setTimeout(resolve, 100)); | |
| const opt = { | |
| margin: [0, 0, 0, 0], | |
| filename: filename, | |
| image: { type: 'jpeg', quality: 0.98 }, | |
| html2canvas: { | |
| scale: 3, | |
| useCORS: true, | |
| width: 892, | |
| height: 2522, | |
| logging: false, | |
| allowTaint: true, | |
| backgroundColor: '#ffffff' | |
| }, | |
| jsPDF: { | |
| unit: 'px', | |
| format: [892, 1261], | |
| orientation: 'portrait', | |
| compress: true | |
| } | |
| }; | |
| await html2pdf().set(opt).from(container).save(); | |
| document.body.removeChild(container); | |
| if (controls) controls.style.display = 'block'; | |
| if (allWorkerData[currentIndex].requestNumber !== workerItem.requestNumber) { | |
| displayWorkerData(currentIndex); | |
| } | |
| } catch (error) { | |
| console.error('Error generating PDF:', error); | |
| alert('เกิดข้อผิดพลาดในการสร้าง PDF กรุณาลองใหม่อีกครั้ง'); | |
| const controls = document.querySelector('.controls-container'); | |
| if (controls) controls.style.display = 'block'; | |
| } | |
| } | |
| async function downloadCurrentPDF() { | |
| if (currentIndex >= 0 && currentIndex < allWorkerData.length) { | |
| setButtonsDisabled(true); | |
| try { | |
| await downloadPDF(allWorkerData[currentIndex]); | |
| } catch (error) { | |
| alert('เกิดข้อผิดพลาดในการสร้าง PDF กรุณาลองใหม่อีกครั้ง'); | |
| } | |
| setButtonsDisabled(false); | |
| } else { | |
| alert('ไม่มีข้อมูลพนักงานที่เลือก'); | |
| } | |
| } | |
| async function downloadSelectedPDFs() { | |
| if (selectedWorkers.size === 0) { | |
| alert('กรุณาเลือกพนักงานอย่างน้อย 1 รายการ'); | |
| return; | |
| } | |
| const selectedArray = Array.from(selectedWorkers).sort((a, b) => a - b); | |
| const confirmDownload = confirm(`คุณต้องการดาวน์โหลดใบอนุญาตทำงานสำหรับพนักงาน ${selectedArray.length} รายการใช่หรือไม่?`); | |
| if (!confirmDownload) return; | |
| setButtonsDisabled(true); | |
| showProgress(true, 0, selectedArray.length); | |
| for (let i = 0; i < selectedArray.length; i++) { | |
| const workerIndex = selectedArray[i]; | |
| try { | |
| showProgress(true, i + 1, selectedArray.length); | |
| displayWorkerData(workerIndex); | |
| await downloadPDF(allWorkerData[workerIndex], `work_permit_${i + 1}`); | |
| await new Promise(resolve => setTimeout(resolve, 800)); | |
| } catch (error) { | |
| console.error(`Error downloading PDF for worker at index ${workerIndex}:`, error); | |
| continue; | |
| } | |
| } | |
| showProgress(false); | |
| setButtonsDisabled(false); | |
| alert(`ดาวน์โหลดใบอนุญาตทำงานเรียบร้อยแล้ว ${selectedArray.length} รายการ`); | |
| if (currentIndex >= 0 && currentIndex < allWorkerData.length) { | |
| displayWorkerData(currentIndex); | |
| } | |
| } | |
| async function downloadAllPDFs() { | |
| if (!allWorkerData || allWorkerData.length === 0) { | |
| alert('ไม่มีข้อมูลพนักงาน'); | |
| return; | |
| } | |
| const confirmDownload = confirm(`คุณต้องการดาวน์โหลดใบอนุญาตทำงานสำหรับพนักงานทั้งหมด ${allWorkerData.length} รายการใช่หรือไม่?`); | |
| if (!confirmDownload) return; | |
| setButtonsDisabled(true); | |
| showProgress(true, 0, allWorkerData.length); | |
| for (let i = 0; i < allWorkerData.length; i++) { | |
| try { | |
| showProgress(true, i + 1, allWorkerData.length); | |
| displayWorkerData(i); | |
| await downloadPDF(allWorkerData[i], `work_permit_${i + 1}`); | |
| await new Promise(resolve => setTimeout(resolve, 800)); | |
| } catch (error) { | |
| console.error(`Error downloading PDF for worker at index ${i}:`, error); | |
| continue; | |
| } | |
| } | |
| showProgress(false); | |
| setButtonsDisabled(false); | |
| alert(`ดาวน์โหลดใบอนุญาตทำงานเรียบร้อยแล้ว ${allWorkerData.length} รายการ`); | |
| if (currentIndex >= 0 && currentIndex < allWorkerData.length) { | |
| displayWorkerData(currentIndex); | |
| } else if (allWorkerData.length > 0) { | |
| displayWorkerData(0); | |
| } | |
| } | |
| window.onload = async () => { | |
| try { | |
| await loadWorkerData(); | |
| randomizeBothOverlays(); | |
| } catch (e) { | |
| console.error('Error initializing application:', e); | |
| } | |
| }; | |
| </script> | |
| </body> | |
| </html> |