protae5544 commited on
Commit
83be24b
·
verified ·
1 Parent(s): b4dbf8a

Upload 24 files

Browse files
Files changed (25) hide show
  1. .gitattributes +16 -0
  2. a1.png +3 -0
  3. a2.png +3 -0
  4. a3.png +3 -0
  5. a4.png +3 -0
  6. a5.png +3 -0
  7. a6.png +3 -0
  8. b1.png +3 -0
  9. b2.png +3 -0
  10. b3.png +3 -0
  11. b4.png +3 -0
  12. b5.png +3 -0
  13. b6.png +3 -0
  14. bg1.png +0 -0
  15. bg1.svg +0 -0
  16. bg2.svg +1427 -0
  17. bg3.svg +0 -0
  18. bor01.png +3 -0
  19. bor02.png +3 -0
  20. bor03.png +3 -0
  21. bor04.png +3 -0
  22. combined-data.json +16 -0
  23. index.html +1673 -0
  24. pdf.html +264 -0
  25. worker.html +366 -0
.gitattributes CHANGED
@@ -33,3 +33,19 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ a1.png filter=lfs diff=lfs merge=lfs -text
37
+ a2.png filter=lfs diff=lfs merge=lfs -text
38
+ a3.png filter=lfs diff=lfs merge=lfs -text
39
+ a4.png filter=lfs diff=lfs merge=lfs -text
40
+ a5.png filter=lfs diff=lfs merge=lfs -text
41
+ a6.png filter=lfs diff=lfs merge=lfs -text
42
+ b1.png filter=lfs diff=lfs merge=lfs -text
43
+ b2.png filter=lfs diff=lfs merge=lfs -text
44
+ b3.png filter=lfs diff=lfs merge=lfs -text
45
+ b4.png filter=lfs diff=lfs merge=lfs -text
46
+ b5.png filter=lfs diff=lfs merge=lfs -text
47
+ b6.png filter=lfs diff=lfs merge=lfs -text
48
+ bor01.png filter=lfs diff=lfs merge=lfs -text
49
+ bor02.png filter=lfs diff=lfs merge=lfs -text
50
+ bor03.png filter=lfs diff=lfs merge=lfs -text
51
+ bor04.png filter=lfs diff=lfs merge=lfs -text
a1.png ADDED

Git LFS Details

  • SHA256: d43f2d988052a9a27d1d05cb8c472fc34817472d0a0ec6d2a3f516cb0baf6031
  • Pointer size: 132 Bytes
  • Size of remote file: 2.13 MB
a2.png ADDED

Git LFS Details

  • SHA256: 93c7e06a8c6dd3b846c51b88299d50e7ead20c47943664f6ce3f7ff3515fd102
  • Pointer size: 131 Bytes
  • Size of remote file: 187 kB
a3.png ADDED

Git LFS Details

  • SHA256: 44d95784f49f1c387d36f34dc6d67f3a1cc9f10b63589ca5bbc02cade4c55b2e
  • Pointer size: 131 Bytes
  • Size of remote file: 116 kB
a4.png ADDED

Git LFS Details

  • SHA256: afd185893e0876bcc8e456932dca20c1eef4993325cefd8bce1542aa0fdf6e2c
  • Pointer size: 132 Bytes
  • Size of remote file: 1.41 MB
a5.png ADDED

Git LFS Details

  • SHA256: eaf1c529ec637622d0eefa6182175c9265776f73209d051ca6516990932ed48a
  • Pointer size: 131 Bytes
  • Size of remote file: 184 kB
a6.png ADDED

Git LFS Details

  • SHA256: 4cb373822aac8e404805d3c6266ee4fb158f4402d6053eccdc583d3ae5e8223c
  • Pointer size: 131 Bytes
  • Size of remote file: 171 kB
b1.png ADDED

Git LFS Details

  • SHA256: 3062f39685119e134d6a14f13811d9e85de1ddc3d00e07eb22d0b758a5cbbe71
  • Pointer size: 132 Bytes
  • Size of remote file: 2.09 MB
b2.png ADDED

Git LFS Details

  • SHA256: 713a1ee2b0498502ad076e207366b45726ba9833131b5de53ea55d1dbf61e110
  • Pointer size: 131 Bytes
  • Size of remote file: 191 kB
b3.png ADDED

Git LFS Details

  • SHA256: 390144201857f7bfb204d3b1e7e5c0cefd48783db543dfce48eec0d0858236c6
  • Pointer size: 131 Bytes
  • Size of remote file: 218 kB
b4.png ADDED

Git LFS Details

  • SHA256: 727112ee64542cba19650349a966380f82aec43d26cd446d2032adc3bbf4acd6
  • Pointer size: 132 Bytes
  • Size of remote file: 1.46 MB
b5.png ADDED

Git LFS Details

  • SHA256: 4fa2d425b678c2963fbc8e8200e0ebcd783fcfe52cfcb6dc1f34b1d2a1801317
  • Pointer size: 131 Bytes
  • Size of remote file: 187 kB
b6.png ADDED

Git LFS Details

  • SHA256: d3bea865c08787cfcd76f4f2aa07bee1e1f8f2c0b267974c482370196f7dab00
  • Pointer size: 131 Bytes
  • Size of remote file: 226 kB
bg1.png ADDED
bg1.svg ADDED
bg2.svg ADDED
bg3.svg ADDED
bor01.png ADDED

Git LFS Details

  • SHA256: d829494f82076f8cf675024cbe637c10265b479a32da7eeb5e93c942817ea177
  • Pointer size: 131 Bytes
  • Size of remote file: 545 kB
bor02.png ADDED

Git LFS Details

  • SHA256: 59c540043d80e254dce2e9d76054c7ba007854b6c0e1368e0071cce6042200a2
  • Pointer size: 131 Bytes
  • Size of remote file: 351 kB
bor03.png ADDED

Git LFS Details

  • SHA256: 91d1d5f68eed3529499840272c623fc1e8dbf8059c0d83ac91e5e95a73726b94
  • Pointer size: 131 Bytes
  • Size of remote file: 351 kB
bor04.png ADDED

Git LFS Details

  • SHA256: dde0e1caaa4be60be3fa259ed527892c04c47ab73a8cc611c6cd35e16ffe0d07
  • Pointer size: 131 Bytes
  • Size of remote file: 424 kB
combined-data.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "requestNumber": "WP-68-1012028",
4
+ "englishName": "MR. VL SAL",
5
+ "thaiName": "นาย วล ซาน",
6
+ "profileImage": "https://i.postimg.cc/NjHdwcmr/processed-132-MR-VL-SAL.jpg",
7
+ "age": "18",
8
+ "alienReferenceNumber": "2492102076170",
9
+ "personalID": "6682190040909",
10
+ "nationality": "กัมพูชา",
11
+ "workPermitNumber": "2100689040330",
12
+ "birthDate": "13/3/2007",
13
+ "receiptNumber": "2100680035321",
14
+ "paymentNumber": "IV680329/002439"
15
+ }
16
+ ]
index.html ADDED
@@ -0,0 +1,1673 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="th" xml:lang="th">
3
+ <head>
4
+ <title>journey</title>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
9
+ <style type="text/css">
10
+ @font-face {
11
+ font-family: 'THSarabunNew';
12
+ src: url('fonts/THSarabunNew.ttf') format('truetype');
13
+ font-weight: normal;
14
+ font-style: normal;
15
+ unicode-range: U+0E00-0E7F, U+0020-007F, U+00A0-00FF;
16
+ font-display: swap;
17
+ }
18
+
19
+ @font-face {
20
+ font-family: 'THSarabunNew';
21
+ src: url('fonts/THSarabunNew Bold.ttf') format('truetype');
22
+ font-weight: bold;
23
+ font-style: normal;
24
+ unicode-range: U+0E00-0E7F, U+0020-007F, U+00A0-00FF;
25
+ font-display: swap;
26
+ }
27
+
28
+ * {
29
+ font-family: 'THSarabunNew', sans-serif !important;
30
+ }
31
+
32
+ .ft00 { font-size: 19px; color: #000000; }
33
+ .ft01 { font-size: 25px; color: #000000; }
34
+ .ft02 { font-size: 22px; color: #000000; }
35
+ .ft03 { font-size: 22px; color: #000000; }
36
+ .ft06 { font-size: 15px; line-height: 18px; color: #000000; }
37
+
38
+ /* สำหรับการพิมพ์เป็น PDF */
39
+ @media print {
40
+ body {
41
+ filter: grayscale(100%);
42
+ }
43
+ .controls-container { display: none !important; }
44
+ body { margin: 0 !important; padding: 0 !important; }
45
+ .page-div { width: 100% !important;
46
+ height: auto !important;
47
+ margin: 0 !important;
48
+ padding: 0 !important;
49
+ page-break-after: always; }
50
+ .page-div:last-child { page-break-after: avoid; }
51
+ .overlay-frame { display: none !important; }
52
+ }
53
+
54
+ .page-div {
55
+ position: relative;
56
+ width: 892px;
57
+ height: 1261px;
58
+ background: none;
59
+ margin: 10px auto;
60
+ }
61
+ .page-div:last-child {
62
+ margin-bottom: 0;
63
+ }
64
+
65
+ .controls-container {
66
+ position: fixed;
67
+ bottom: 16px;
68
+ left: 16px;
69
+ background: rgba(255, 255, 255, 0.95);
70
+ padding: 16px;
71
+ border-radius: 12px;
72
+ width: 340px;
73
+ max-width: calc(100vw - 32px);
74
+ z-index: 1000;
75
+ display: flex;
76
+ flex-direction: column;
77
+ gap: 10px;
78
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
79
+ backdrop-filter: blur(10px);
80
+ border: 1px solid rgba(255, 255, 255, 0.3);
81
+ }
82
+
83
+ /* Custom Dropdown Styles */
84
+ .custom-dropdown {
85
+ position: relative;
86
+ width: 100%;
87
+ }
88
+
89
+ .dropdown-header {
90
+ display: flex;
91
+ align-items: center;
92
+ gap: 10px;
93
+ padding: 10px 12px;
94
+ background: white;
95
+ border: 2px solid #e2e8f0;
96
+ border-radius: 8px;
97
+ cursor: pointer;
98
+ transition: all 0.2s ease;
99
+ min-height: 48px;
100
+ }
101
+
102
+ .dropdown-header:hover {
103
+ border-color: #3b82f6;
104
+ }
105
+
106
+ .dropdown-header:focus-within {
107
+ border-color: #3b82f6;
108
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
109
+ }
110
+
111
+ .dropdown-header img {
112
+ width: 32px;
113
+ height: 32px;
114
+ border-radius: 50%;
115
+ object-fit: cover;
116
+ flex-shrink: 0;
117
+ }
118
+
119
+ .dropdown-header-text {
120
+ flex: 1;
121
+ font-size: 15px;
122
+ color: #1e293b;
123
+ white-space: nowrap;
124
+ overflow: hidden;
125
+ text-overflow: ellipsis;
126
+ }
127
+
128
+ .dropdown-arrow {
129
+ width: 20px;
130
+ height: 20px;
131
+ transition: transform 0.3s ease;
132
+ flex-shrink: 0;
133
+ }
134
+
135
+ .dropdown-arrow.open {
136
+ transform: rotate(180deg);
137
+ }
138
+
139
+ .dropdown-menu {
140
+ position: absolute;
141
+ bottom: calc(100% + 8px);
142
+ left: 0;
143
+ right: 0;
144
+ background: white;
145
+ border-radius: 12px;
146
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.2);
147
+ opacity: 0;
148
+ visibility: hidden;
149
+ transform: translateY(10px);
150
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
151
+ z-index: 1001;
152
+ overflow: hidden;
153
+ max-height: 400px;
154
+ display: flex;
155
+ flex-direction: column;
156
+ }
157
+
158
+ .dropdown-menu.open {
159
+ opacity: 1;
160
+ visibility: visible;
161
+ transform: translateY(0);
162
+ }
163
+
164
+ .search-container {
165
+ padding: 12px;
166
+ border-bottom: 1px solid #e2e8f0;
167
+ background: #f8fafc;
168
+ position: sticky;
169
+ top: 0;
170
+ z-index: 10;
171
+ }
172
+
173
+ .search-input {
174
+ width: 100%;
175
+ padding: 10px 40px 10px 12px;
176
+ border: 2px solid #e2e8f0;
177
+ border-radius: 8px;
178
+ font-size: 15px;
179
+ outline: none;
180
+ transition: all 0.2s ease;
181
+ background: white;
182
+ }
183
+
184
+ .search-input:focus {
185
+ border-color: #3b82f6;
186
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
187
+ }
188
+
189
+ .search-input::placeholder {
190
+ color: #94a3b8;
191
+ }
192
+
193
+ .search-wrapper {
194
+ position: relative;
195
+ }
196
+
197
+ .search-icon {
198
+ position: absolute;
199
+ right: 12px;
200
+ top: 50%;
201
+ transform: translateY(-50%);
202
+ width: 20px;
203
+ height: 20px;
204
+ color: #94a3b8;
205
+ pointer-events: none;
206
+ }
207
+
208
+ .clear-search {
209
+ position: absolute;
210
+ right: 12px;
211
+ top: 50%;
212
+ transform: translateY(-50%);
213
+ width: 24px;
214
+ height: 24px;
215
+ background: #ef4444;
216
+ color: white;
217
+ border: none;
218
+ border-radius: 50%;
219
+ cursor: pointer;
220
+ display: none;
221
+ align-items: center;
222
+ justify-content: center;
223
+ font-size: 14px;
224
+ font-weight: bold;
225
+ transition: background 0.2s;
226
+ }
227
+
228
+ .clear-search:hover {
229
+ background: #dc2626;
230
+ }
231
+
232
+ .clear-search.visible {
233
+ display: flex;
234
+ }
235
+
236
+ .dropdown-list {
237
+ overflow-y: auto;
238
+ max-height: 280px;
239
+ overscroll-behavior: contain;
240
+ }
241
+
242
+ .dropdown-list::-webkit-scrollbar {
243
+ width: 8px;
244
+ }
245
+
246
+ .dropdown-list::-webkit-scrollbar-track {
247
+ background: #f1f5f9;
248
+ }
249
+
250
+ .dropdown-list::-webkit-scrollbar-thumb {
251
+ background: #cbd5e1;
252
+ border-radius: 4px;
253
+ }
254
+
255
+ .dropdown-list::-webkit-scrollbar-thumb:hover {
256
+ background: #94a3b8;
257
+ }
258
+
259
+ .dropdown-item {
260
+ display: flex;
261
+ align-items: center;
262
+ gap: 12px;
263
+ padding: 12px 16px;
264
+ cursor: pointer;
265
+ transition: all 0.15s ease;
266
+ border-bottom: 1px solid #f1f5f9;
267
+ }
268
+
269
+ .dropdown-item:last-child {
270
+ border-bottom: none;
271
+ }
272
+
273
+ .dropdown-item:hover {
274
+ background: #f0f9ff;
275
+ }
276
+
277
+ .dropdown-item.selected {
278
+ background: #eff6ff;
279
+ }
280
+
281
+ .dropdown-item.active {
282
+ background: #dbeafe;
283
+ }
284
+
285
+ .dropdown-item-image {
286
+ width: 40px;
287
+ height: 40px;
288
+ border-radius: 50%;
289
+ object-fit: cover;
290
+ flex-shrink: 0;
291
+ border: 2px solid #e2e8f0;
292
+ }
293
+
294
+ .dropdown-item-content {
295
+ flex: 1;
296
+ min-width: 0;
297
+ }
298
+
299
+ .dropdown-item-title {
300
+ font-size: 14px;
301
+ font-weight: 600;
302
+ color: #1e293b;
303
+ white-space: nowrap;
304
+ overflow: hidden;
305
+ text-overflow: ellipsis;
306
+ }
307
+
308
+ .dropdown-item-subtitle {
309
+ font-size: 12px;
310
+ color: #64748b;
311
+ white-space: nowrap;
312
+ overflow: hidden;
313
+ text-overflow: ellipsis;
314
+ }
315
+
316
+ .dropdown-item-checkbox {
317
+ flex-shrink: 0;
318
+ }
319
+
320
+ .custom-checkbox {
321
+ width: 22px;
322
+ height: 22px;
323
+ border: 2px solid #cbd5e1;
324
+ border-radius: 6px;
325
+ display: flex;
326
+ align-items: center;
327
+ justify-content: center;
328
+ cursor: pointer;
329
+ transition: all 0.2s ease;
330
+ background: white;
331
+ }
332
+
333
+ .custom-checkbox:hover {
334
+ border-color: #3b82f6;
335
+ }
336
+
337
+ .custom-checkbox.checked {
338
+ background: #3b82f6;
339
+ border-color: #3b82f6;
340
+ }
341
+
342
+ .custom-checkbox svg {
343
+ width: 14px;
344
+ height: 14px;
345
+ color: white;
346
+ opacity: 0;
347
+ transform: scale(0.5);
348
+ transition: all 0.2s ease;
349
+ }
350
+
351
+ .custom-checkbox.checked svg {
352
+ opacity: 1;
353
+ transform: scale(1);
354
+ }
355
+
356
+ .dropdown-actions {
357
+ padding: 12px;
358
+ border-top: 1px solid #e2e8f0;
359
+ background: #f8fafc;
360
+ display: flex;
361
+ gap: 8px;
362
+ flex-wrap: wrap;
363
+ }
364
+
365
+ .action-btn {
366
+ flex: 1;
367
+ min-width: 100px;
368
+ padding: 10px 16px;
369
+ border: none;
370
+ border-radius: 8px;
371
+ font-size: 14px;
372
+ font-weight: 600;
373
+ cursor: pointer;
374
+ transition: all 0.2s ease;
375
+ display: flex;
376
+ align-items: center;
377
+ justify-content: center;
378
+ gap: 6px;
379
+ }
380
+
381
+ .action-btn:active {
382
+ transform: scale(0.98);
383
+ }
384
+
385
+ .btn-select-all {
386
+ background: #e2e8f0;
387
+ color: #475569;
388
+ }
389
+
390
+ .btn-select-all:hover {
391
+ background: #cbd5e1;
392
+ }
393
+
394
+ .btn-clear-selection {
395
+ background: #fee2e2;
396
+ color: #dc2626;
397
+ }
398
+
399
+ .btn-clear-selection:hover {
400
+ background: #fecaca;
401
+ }
402
+
403
+ .selected-count {
404
+ padding: 8px 12px;
405
+ background: #dbeafe;
406
+ color: #1d4ed8;
407
+ border-radius: 8px;
408
+ font-size: 13px;
409
+ font-weight: 600;
410
+ text-align: center;
411
+ }
412
+
413
+ .no-results {
414
+ padding: 40px 20px;
415
+ text-align: center;
416
+ color: #94a3b8;
417
+ }
418
+
419
+ .no-results svg {
420
+ width: 48px;
421
+ height: 48px;
422
+ margin: 0 auto 12px;
423
+ opacity: 0.5;
424
+ }
425
+
426
+ /* Control Buttons */
427
+ .control-btn {
428
+ padding: 12px 16px;
429
+ border-radius: 8px;
430
+ font-size: 15px;
431
+ font-weight: 600;
432
+ cursor: pointer;
433
+ transition: all 0.2s ease;
434
+ border: none;
435
+ display: flex;
436
+ align-items: center;
437
+ justify-content: center;
438
+ gap: 8px;
439
+ min-height: 48px;
440
+ }
441
+
442
+ .control-btn:active {
443
+ transform: scale(0.98);
444
+ }
445
+
446
+ .control-btn:disabled {
447
+ opacity: 0.5;
448
+ cursor: not-allowed;
449
+ }
450
+
451
+ .btn-grayscale {
452
+ background: #6b7280;
453
+ color: white;
454
+ }
455
+
456
+ .btn-grayscale:hover:not(:disabled) {
457
+ background: #4b5563;
458
+ }
459
+
460
+ .btn-download-current {
461
+ background: white;
462
+ color: #1e293b;
463
+ border: 2px solid #e2e8f0;
464
+ }
465
+
466
+ .btn-download-current:hover:not(:disabled) {
467
+ background: #f8fafc;
468
+ border-color: #cbd5e1;
469
+ }
470
+
471
+ .btn-download-selected {
472
+ background: #3b82f6;
473
+ color: white;
474
+ }
475
+
476
+ .btn-download-selected:hover:not(:disabled) {
477
+ background: #2563eb;
478
+ }
479
+
480
+ .btn-download-all {
481
+ background: #10b981;
482
+ color: white;
483
+ }
484
+
485
+ .btn-download-all:hover:not(:disabled) {
486
+ background: #059669;
487
+ }
488
+
489
+ .btn-randomize {
490
+ background: #8b5cf6;
491
+ color: white;
492
+ }
493
+
494
+ .btn-randomize:hover:not(:disabled) {
495
+ background: #7c3aed;
496
+ }
497
+
498
+ .overlay-frame {
499
+ width: 300px;
500
+ height: 320px;
501
+ border: none;
502
+ position: absolute;
503
+ overflow: hidden;
504
+ z-index: 50;
505
+ background: transparent;
506
+ }
507
+
508
+ .overlay-image-container {
509
+ width: 260px;
510
+ height: 150px;
511
+ background: transparent;
512
+ display: flex;
513
+ align-items: center;
514
+ justify-content: center;
515
+ position: absolute;
516
+ transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
517
+ transform-origin: center;
518
+ }
519
+
520
+ .overlay-image-container img {
521
+ position: absolute;
522
+ top: 0;
523
+ left: 0;
524
+ width: 100%;
525
+ height: 100%;
526
+ object-fit: cover;
527
+ }
528
+
529
+ #page1-overlay {
530
+ top: 700px;
531
+ right: 320px;
532
+ }
533
+ #page2-overlay {
534
+ top: 1000px;
535
+ left: 500px;
536
+ }
537
+
538
+ /* Responsive Design */
539
+ @media (max-width: 480px) {
540
+ .controls-container {
541
+ width: calc(100% - 32px);
542
+ left: 16px;
543
+ bottom: 16px;
544
+ padding: 12px;
545
+ }
546
+
547
+ .dropdown-menu {
548
+ max-height: 350px;
549
+ }
550
+
551
+ .dropdown-list {
552
+ max-height: 200px;
553
+ }
554
+
555
+ .overlay-frame {
556
+ width: 250px;
557
+ height: 260px;
558
+ }
559
+ .overlay-image-container {
560
+ width: 200px;
561
+ height: 120px;
562
+ }
563
+ .stamp-text {
564
+ font-size: 12px;
565
+ }
566
+ #page1-overlay {
567
+ top: 700px;
568
+ right: 320px;
569
+ }
570
+ #page2-overlay {
571
+ top: 1000px;
572
+ left: 500px;
573
+ }
574
+ .page-div {
575
+ width: 100%;
576
+ height: auto;
577
+ margin: 10px 0;
578
+ }
579
+
580
+ .control-btn {
581
+ font-size: 14px;
582
+ padding: 10px 12px;
583
+ }
584
+ }
585
+
586
+ /* Loading Spinner */
587
+ .loading-spinner {
588
+ width: 20px;
589
+ height: 20px;
590
+ border: 2px solid transparent;
591
+ border-top-color: currentColor;
592
+ border-radius: 50%;
593
+ animation: spin 0.8s linear infinite;
594
+ }
595
+
596
+ @keyframes spin {
597
+ to { transform: rotate(360deg); }
598
+ }
599
+
600
+ /* Progress Bar */
601
+ .progress-container {
602
+ display: none;
603
+ padding: 12px;
604
+ background: #f0fdf4;
605
+ border-radius: 8px;
606
+ border: 1px solid #bbf7d0;
607
+ }
608
+
609
+ .progress-container.visible {
610
+ display: block;
611
+ }
612
+
613
+ .progress-bar {
614
+ height: 8px;
615
+ background: #e2e8f0;
616
+ border-radius: 4px;
617
+ overflow: hidden;
618
+ margin-bottom: 8px;
619
+ }
620
+
621
+ .progress-fill {
622
+ height: 100%;
623
+ background: linear-gradient(90deg, #10b981, #34d399);
624
+ border-radius: 4px;
625
+ transition: width 0.3s ease;
626
+ }
627
+
628
+ .progress-text {
629
+ font-size: 13px;
630
+ color: #166534;
631
+ text-align: center;
632
+ }
633
+
634
+ /* Highlight matched text */
635
+ .highlight {
636
+ background: #fef08a;
637
+ padding: 0 2px;
638
+ border-radius: 2px;
639
+ }
640
+ </style>
641
+ </head>
642
+ <body class="bg-gray-100">
643
+ <div class="controls-container" role="region" aria-label="Controls">
644
+ <!-- Custom Dropdown -->
645
+ <div class="custom-dropdown" id="customDropdown">
646
+ <div class="dropdown-header"
647
+ id="dropdownHeader"
648
+ tabindex="0"
649
+ role="combobox"
650
+ aria-expanded="false"
651
+ aria-haspopup="listbox"
652
+ aria-label="เลือกพนักงาน">
653
+ <img id="dropdownProfilePic" src="https://via.placeholder.com/32?text=..." alt="Profile"/>
654
+ <span class="dropdown-header-text" id="selectedText">กรุณาเลือกพนักงาน</span>
655
+ <svg class="dropdown-arrow" id="dropdownArrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
656
+ <polyline points="6,9 12,15 18,9"></polyline>
657
+ </svg>
658
+ </div>
659
+ <div class="dropdown-menu" id="dropdownMenu" role="listbox" aria-label="รายชื่อพนักงาน">
660
+ <div class="search-container">
661
+ <div class="search-wrapper">
662
+ <input type="text"
663
+ class="search-input"
664
+ id="searchInput"
665
+ placeholder="ค้นหา ชื่อ, เลขที่, หรือ ID..."
666
+ aria-label="ค้นหาพนักงาน"
667
+ autocomplete="off"/>
668
+ <svg class="search-icon" id="searchIcon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
669
+ <circle cx="11" cy="11" r="8"></circle>
670
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
671
+ </svg>
672
+ <button class="clear-search" id="clearSearch" aria-label="ล้างการค้นหา">&times;</button>
673
+ </div>
674
+ </div>
675
+ <div class="dropdown-list" id="dropdownList">
676
+ <!-- Items will be populated here -->
677
+ </div>
678
+ <div class="dropdown-actions">
679
+ <div class="selected-count" id="selectedCount">เลือกแล้ว 0 รายการ</div>
680
+ <button class="action-btn btn-select-all" id="selectAllBtn" aria-label="เลือกทั้งหมด">
681
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
682
+ <polyline points="9,11 12,14 22,4"></polyline>
683
+ <path d="M21,12v7a2,2,0,0,1-2,2H5a2,2,0,0,1-2-2V5A2,2,0,0,1,5,3h11"></path>
684
+ </svg>
685
+ เลือกทั้งหมด
686
+ </button>
687
+ <button class="action-btn btn-clear-selection" id="clearSelectionBtn" aria-label="ล้างการเลือก">
688
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
689
+ <line x1="18" y1="6" x2="6" y2="18"></line>
690
+ <line x1="6" y1="6" x2="18" y2="18"></line>
691
+ </svg>
692
+ ล้าง
693
+ </button>
694
+ </div>
695
+ </div>
696
+ </div>
697
+
698
+ <!-- Progress Bar -->
699
+ <div class="progress-container" id="progressContainer">
700
+ <div class="progress-bar">
701
+ <div class="progress-fill" id="progressFill" style="width: 0%"></div>
702
+ </div>
703
+ <div class="progress-text" id="progressText">กำลังดาวน์โหลด...</div>
704
+ </div>
705
+
706
+ <!-- Control Buttons -->
707
+ <button type="button" id="grayscaleBtn" class="control-btn btn-grayscale">
708
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
709
+ <circle cx="12" cy="12" r="10"></circle>
710
+ <path d="M12 2a10 10 0 0 1 0 20"></path>
711
+ </svg>
712
+ Grayscale: ปิด
713
+ </button>
714
+
715
+ <button id="downloadCurrentBtn" class="control-btn btn-download-current">
716
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
717
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
718
+ <polyline points="7,10 12,15 17,10"></polyline>
719
+ <line x1="12" y1="15" x2="12" y2="3"></line>
720
+ </svg>
721
+ ดาวน์โหลด PDF ปัจจุบัน
722
+ </button>
723
+
724
+ <button id="downloadSelectedBtn" class="control-btn btn-download-selected" disabled>
725
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
726
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
727
+ <polyline points="7,10 12,15 17,10"></polyline>
728
+ <line x1="12" y1="15" x2="12" y2="3"></line>
729
+ </svg>
730
+ ดาวน์โหลด PDF ที่เลือก (0)
731
+ </button>
732
+
733
+ <button id="downloadAllBtn" class="control-btn btn-download-all">
734
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
735
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
736
+ <polyline points="7,10 12,15 17,10"></polyline>
737
+ <line x1="12" y1="15" x2="12" y2="3"></line>
738
+ </svg>
739
+ ดาวน์โหลด PDF ทั้งหมด
740
+ </button>
741
+
742
+ <button id="randomizeBtn" class="control-btn btn-randomize">
743
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
744
+ <polyline points="16,3 21,3 21,8"></polyline>
745
+ <line x1="4" y1="20" x2="21" y2="3"></line>
746
+ <polyline points="21,16 21,21 16,21"></polyline>
747
+ <line x1="15" y1="15" x2="21" y2="21"></line>
748
+ <line x1="4" y1="4" x2="9" y2="9"></line>
749
+ </svg>
750
+ สุ่มตำแหน่งภาพ
751
+ </button>
752
+ </div>
753
+
754
+ <!-- Page 1: Work Permit -->
755
+ <div id="page1-div" class="page-div">
756
+ <img width="892" height="1261" src="bg3.svg" alt="Background"/>
757
+ <div id="overlayContainer1" class="overlay-frame" style="width: 300px; height: 200px; position: absolute; top: 700px; right: 320px;"></div>
758
+ <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"/>
759
+ <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"/>
760
+ <p class="absolute top-[32px] left-[134px] whitespace-nowrap text-[21px] font-bold">ทะเบียนใบอนุญาตทำงานของคนต่างด้าวตามมติ���ณะรัฐมนตรี เมื่อวันที่ 24 กันยายน 2567</p>
761
+ <p class="absolute top-[57px] left-[134px] whitespace-nowrap text-[21px] font-bold">เอกสารฉบับนี้ใช้แทนใบอนุญาตทำงาน</p>
762
+ <p class="absolute top-[87px] left-[158px] whitespace-nowrap text-[15px]">เลขรับที่ (No.)</p>
763
+ <p class="absolute top-[87px] left-[224px] whitespace-nowrap text-[14px]">:</p>
764
+ <p class="absolute top-[87px] left-[238px] whitespace-nowrap text-[14px] font-bold" id="requestNumber">xxxxxxx</p>
765
+ <p class="absolute top-[87px] left-[417px] whitespace-nowrap text-[15px]">วันที่อนุมัติ (Date)</p>
766
+ <p class="absolute top-[87px] left-[500px] whitespace-nowrap text-[14px]">:</p>
767
+ <p class="absolute top-[87px] left-[514px] whitespace-nowrap text-[15px] font-bold">06 มีนาคม 2568</p>
768
+ <p class="absolute top-[114px] left-[62px] whitespace-nowrap text-[15px]">ชื่อคนต่างด้าว (Name of Applicant)</p>
769
+ <p class="absolute top-[114px] left-[224px] whitespace-nowrap text-[14px]">:</p>
770
+ <p class="absolute top-[114px] left-[238px] whitespace-nowrap text-[14px] font-bold" id="englishName">xxxxxxxxxxxxx</p>
771
+ <p class="absolute top-[140px] left-[94px] whitespace-nowrap text-[15px]">เจ้าหน้าที่ (Name of Officer)</p>
772
+ <p class="absolute top-[140px] left-[224px] whitespace-nowrap text-[14px]">:</p>
773
+ <p class="absolute top-[140px] left-[238px] whitespace-nowrap text-[15px] font-bold">นายสมมาตร อนันต์ธราทรัพย์</p>
774
+ <p class="absolute top-[140px] left-[440px] whitespace-nowrap text-[15px]">นายทะเบียน</p>
775
+ <p class="absolute top-[140px] left-[500px] whitespace-nowrap text-[14px]">:</p>
776
+ <p class="absolute top-[167px] left-[238px] whitespace-nowrap text-[15px]">จัดหางานจังหวัดระยอง</p>
777
+ <p class="absolute top-[163px] left-[449px] whitespace-nowrap text-[15px]">(Registrar)</p>
778
+ <p class="absolute top-[163px] left-[546px] whitespace-nowrap text-[15px]">นายสมชาย มรกตศรีวรรณ</p>
779
+ <p class="absolute top-[182px] left-[555px] whitespace-nowrap text-[15px]">อธิบดีกรมการจัดหางาน</p>
780
+ <p class="absolute top-[201px] left-[577px] whitespace-nowrap text-[15px]">นายทะเบียน</p>
781
+ <p class="absolute top-[227px] left-[171px] whitespace-nowrap text-[16px] font-bold">ลงเลขรับและชำระค่ายื่นแบบคำขอ (REGISTERING APPLICATION FORM AND PAYING APPLICATION FEE)</p>
782
+ <p class="absolute top-[254px] left-[55px] whitespace-nowrap text-[15px] font-bold leading-[21px]">ข้อมูลคนต่างด้าว</p>
783
+ <p class="absolute top-[278px] left-[55px] whitespace-nowrap text-[14px]">สถานะใบอนุญาต</p>
784
+ <p class="absolute top-[278px] left-[199px] whitespace-nowrap text-[14px]">: อนุมัติ (รอพิมพ์บัตร)</p>
785
+ <p class="absolute top-[278px] left-[440px] whitespace-nowrap text-[14px]">ออกให้ ณ จังหวัด</p>
786
+ <p class="absolute top-[278px] left-[586px] whitespace-nowrap text-[14px]">: สำนักงานจัดหางานจังหวัดระยอง</p>
787
+ <p class="absolute top-[300px] left-[55px] whitespace-nowrap text-[14px]">เลขประจำตัวคนต่างด้าว</p>
788
+ <p class="absolute top-[300px] left-[199px] whitespace-nowrap text-[14px]" id="personalID">: 6685490000472</p>
789
+ <p class="absolute top-[300px] left-[440px] whitespace-nowrap text-[14px]">ใบอนุญาตทำงานเลขที่</p>
790
+ <p class="absolute top-[300px] left-[586px] whitespace-nowrap text-[14px]" id="workPermitNumber">: 5400689000472</p>
791
+ <p class="absolute top-[321px] left-[55px] whitespace-nowrap text-[14px]">ชื่อภาษาไทย</p>
792
+ <p class="absolute top-[321px] left-[199px] whitespace-nowrap text-[14px]" id="thaiName">: นาง เมย์ เท็ท โช</p>
793
+ <p class="absolute top-[321px] left-[440px] whitespace-nowrap text-[14px]">ชื่อภาษาอังกฤษ</p>
794
+ <p class="absolute top-[321px] left-[586px] whitespace-nowrap text-[14px]">:</p>
795
+ <p class="absolute top-[321px] left-[592px] whitespace-nowrap text-[14px]" id="englishNameDuplicate">MRS. MAY THET CHO</p>
796
+ <p class="absolute top-[343px] left-[55px] whitespace-nowrap text-[14px]">วัน/เดือน/ปี (พ.ศ.) เกิด</p>
797
+ <p class="absolute top-[343px] left-[199px] whitespace-nowrap text-[14px]" id="birthDate">: xx/xx/xx</p>
798
+ <p class="absolute top-[343px] left-[440px] whitespace-nowrap text-[14px]">อายุ (ปี)</p>
799
+ <p class="absolute top-[343px] left-[586px] whitespace-nowrap text-[14px]" id="age">: xx</p>
800
+ <p class="absolute top-[365px] left-[55px] whitespace-nowrap text-[14px]">สัญชาติ</p>
801
+ <p class="absolute top-[365px] left-[199px] whitespace-nowrap text-[14px]" id="nationality">: เมียนมา</p>
802
+ <p class="absolute top-[365px] left-[440px] whitespace-nowrap text-[14px]">สถานภาพ</p>
803
+ <p class="absolute top-[365px] left-[586px] whitespace-nowrap text-[14px]">: -</p>
804
+ <p class="absolute top-[386px] left-[55px] whitespace-nowrap text-[14px]">ชื่อ-สกุล บิดา</p>
805
+ <p class="absolute top-[386px] left-[199px] whitespace-nowrap text-[14px]">: -</p>
806
+ <p class="absolute top-[386px] left-[440px] whitespace-nowrap text-[14px]">ชื่อ-สกุล มารดา</p>
807
+ <p class="absolute top-[386px] left-[586px] whitespace-nowrap text-[14px]">: -</p>
808
+ <p class="absolute top-[408px] left-[55px] whitespace-nowrap text-[14px]">เลขอ้างอิงคนต่างด้าว</p>
809
+ <p class="absolute top-[408px] left-[199px] whitespace-nowrap text-[14px]" id="alienReferenceNumber">: xxxxxxxxxxxxx</p>
810
+ <p class="absolute top-[429px] left-[55px] whitespace-nowrap text-[14px]">ที่อยู่อาศัย</p>
811
+ <p class="absolute top-[429px] left-[199px] whitespace-nowrap text-[14px]">: 36/6 หมู่ที่ 8 ตำบลมาบข่า อำเภอนิคมพัฒนา จังหวัดระยอง 21180</p>
812
+ <p class="absolute top-[449px] left-[55px] whitespace-nowrap text-[15px] font-bold leading-[21px]">ข้อมูลหนังสือเดินทาง และข้อมูลการตรวจลงตรา</p>
813
+ <p class="absolute top-[469px] left-[55px] whitespace-nowrap text-[14px]">เลขที่หนังสือเดินทาง</p>
814
+ <p class="absolute top-[472px] left-[199px] whitespace-nowrap text-[14px]">: -</p>
815
+ <p class="absolute top-[472px] left-[440px] whitespace-nowrap text-[14px]">ประเภทหนังสือเดินทาง</p>
816
+ <p class="absolute top-[472px] left-[586px] whitespace-nowrap text-[14px]">: -</p>
817
+ <p class="absolute top-[494px] left-[55px] whitespace-nowrap text-[14px]">สถานที่ออกหนังสือเดินทาง</p>
818
+ <p class="absolute top-[494px] left-[199px] whitespace-nowrap text-[14px]">: -</p>
819
+ <p class="absolute top-[494px] left-[440px] whitespace-nowrap text-[14px]">ประเทศที่ออกหนังสือเดินทาง</p>
820
+ <p class="absolute top-[494px] left-[586px] whitespace-nowrap text-[14px]">: -</p>
821
+ <p class="absolute top-[516px] left-[55px] whitespace-nowrap text-[14px]">วันที่ออกหนังสือเดินทาง</p>
822
+ <p class="absolute top-[516px] left-[199px] whitespace-nowrap text-[14px]">: -</p>
823
+ <p class="absolute top-[516px] left-[440px] whitespace-nowrap text-[14px]">วันหมดอายุ</p>
824
+ <p class="absolute top-[516px] left-[586px] whitespace-nowrap text-[14px]">: -</p>
825
+ <p class="absolute top-[537px] left-[55px] whitespace-nowrap text-[14px]">เลขที่ตรวจลงตรา</p>
826
+ <p class="absolute top-[537px] left-[199px] whitespace-nowrap text-[14px]">: -</p>
827
+ <p class="absolute top-[559px] left-[55px] whitespace-nowrap text-[14px]">ออกให้วันที่</p>
828
+ <p class="absolute top-[559px] left-[199px] whitespace-nowrap text-[14px]">: -</p>
829
+ <p class="absolute top-[559px] left-[440px] whitespace-nowrap text-[14px]">ใช้ได้ถึงวันที่</p>
830
+ <p class="absolute top-[559px] left-[586px] whitespace-nowrap text-[14px]">: -</p>
831
+ <p class="absolute top-[578px] left-[55px] whitespace-nowrap text-[15px] font-bold leading-[21px]">ข้อมูลนายจ้าง/สถานประกอบการ</p>
832
+ <p class="absolute top-[598px] left-[55px] whitespace-nowrap text-[14px]">เลขประจำตัวนายจ้าง</p>
833
+ <p class="absolute top-[602px] left-[199px] whitespace-nowrap text-[14px]">: 0415567000061</p>
834
+ <p class="absolute top-[602px] left-[440px] whitespace-nowrap text-[14px]">ชื่อนายจ้าง/สถานประกอบการ</p>
835
+ <p class="absolute top-[602px] left-[586px] whitespace-nowrap text-[14px]">: บริษัท บาน กง เอ็นจิเนียริ่ง จำกัด</p>
836
+ <p class="absolute top-[623px] left-[55px] whitespace-nowrap text-[14px]">ประเภทกิจการ</p>
837
+ <p class="absolute top-[623px] left-[199px] whitespace-nowrap text-[14px]">: BT04 - กิจการก่อสร้าง</p>
838
+ <p class="absolute top-[645px] left-[55px] whitespace-nowrap text-[14px]">ที่ตั้งสำนักงาน</p>
839
+ <p class="absolute top-[645px] left-[199px] whitespace-nowrap text-[14px]">: 102 หมู่ที่ 8 ถนนอุดรธานี-ขอนแก่น ตำบลโนนสูง อำเภอเมืองอุดรธานี จังหวัดอุดรธานี 41000</p>
840
+ <p class="absolute top-[666px] left-[55px] whitespace-nowrap text-[15px] font-bold leading-[21px]">ข้อมูลการทำงาน</p>
841
+ <p class="absolute top-[684px] left-[55px] whitespace-nowrap text-[14px]">ทำงานในตำแหน่ง</p>
842
+ <p class="absolute top-[688px] left-[199px] whitespace-nowrap text-[14px]">: กรรมกร</p>
843
+ <p class="absolute top-[688px] left-[440px] whitespace-nowrap text-[14px]">ลักษณะงาน</p>
844
+ <p class="absolute top-[688px] left-[586px] whitespace-nowrap text-[14px]">: กรรมกร (กิจการก่อสร้าง)</p>
845
+ <p class="absolute top-[710px] left-[55px] whitespace-nowrap text-[14px]">สถานที่ทำงาน</p>
846
+ <p class="absolute top-[710px] left-[199px] whitespace-nowrap text-[14px]">: 36/6 หมู่ที่ 8 ตำบลมาบข่า อำเภอนิคมพัฒนา จังหวัดระยอง 21180</p>
847
+ <p class="absolute top-[731px] left-[55px] whitespace-nowrap text-[14px]">อนุญาตให้ทำงานถึงวันที่</p>
848
+ <p class="absolute top-[731px] left-[199px] whitespace-nowrap text-[14px]">: 31 มีนาคม 2569</p>
849
+ <p class="absolute top-[755px] left-[55px] whitespace-nowrap text-[15px] font-bold leading-[21px]">ข้อมูลสิทธิการรักษาพยาบาล</p>
850
+ <p class="absolute top-[775px] left-[55px] whitespace-nowrap text-[15px] leading-[21px]">ประกันสังคม</p>
851
+ <p class="absolute top-[796px] left-[55px] whitespace-nowrap text-[15px] leading-[21px]">ประกันสุขภาพ สิ้นสุดวันที่ 30/09/2025</p>
852
+ <p class="absolute top-[825px] left-[55px] whitespace-nowrap text-[14px] font-bold leading-[19px]">เงื่อนไข</p>
853
+ <p class="absolute top-[841px] left-[85px] whitespace-nowrap text-[14px] leading-[19px]">คนต่างด้าวจะต้องทำประกันสุขภาพตลอดระยะเวลาการอนุญาตให้ทำงาน หากปรากฎว่าระยะเวลาการทำประกันสุขภาพสิ้นสุดลง ก่อนระยะเวลาการอนุญาตให้ทำงาน</p>
854
+ <p class="absolute top-[857px] left-[55px] whitespace-nowrap text-[14px] leading-[19px]">นายทะเบียนจะเพิกถอนใบอนุญาตทำงาน ซึ่งมีผลให้การอนุญาตให้อยู่ในราชอาณาจักรสิ้นสุดลง</p>
855
+ <p class="absolute top-[885px] left-[55px] whitespace-nowrap text-[14px] font-bold leading-[19px]">คำเตือน</p>
856
+ <p class="absolute top-[905px] left-[85px] whitespace-nowrap text-[14px] leading-[19px]">เมื่อได้รับอนุญาตให้ทำงานแล้วคนต่างด้าวต้องดำเนินการดังต่อไปนี้ มิเช่นนั้น การอนุญาตให้ทำงานและการอนุญาตให้อยู่ในราชอาณาจักรของคนต่างด้าวจะสิ้นสุดลง</p>
857
+ <p class="absolute top-[922px] left-[55px] whitespace-nowrap text-[14px] leading-[19px]">1. จัดเก็บข้อมูลอัตลักษณ์บุคคล ภายในวันที่ 28 มิถุนายน 2568</p>
858
+ <p class="absolute top-[939px] left-[55px] whitespace-nowrap text-[14px] leading-[19px]">2. จัดทำหรือปรับปรุงทะเบียนประวัติตามกฎหมายว่าด้วยการทะเบียนราษฎร ภายในวันที่ 31 มีนาคม 2569</p>
859
+ <p class="absolute top-[1000px] left-[55px] text-[14px] leading-[19px]" id="timestamp">Loading...</p>
860
+ </div>
861
+
862
+ <!-- Page 2: Receipt -->
863
+ <div id="page2-div" class="page-div">
864
+ <img width="892" height="1261" src="bg2.svg" alt="Receipt Background"/>
865
+ <div id="overlayContainer2" class="overlay-frame" style="position: absolute; top: 1000px; left: 500px;"></div>
866
+ <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"/>
867
+ <p style="position:absolute;top:147px;left:86px;white-space:nowrap" class="ft00">กรมการจัดหางาน</p>
868
+ <p style="position:absolute;top:170px;left:88px;white-space:nowrap" class="ft00">กระทรวงแรงงาน</p>
869
+ <p style="position:absolute;top:90px;left:397px;white-space:nowrap" class="ft01"><b>ใบเสร็จรับเงิน</b></p>
870
+ <p style="position:absolute;top:120px;left:418px;white-space:nowrap" class="ft01"><b>ต้นฉบับ</b></p>
871
+ <p style="position:absolute;top:60px;left:598px;white-space:nowrap" class="ft00">เลขที่</p>
872
+ <p style="position:absolute;top:60px;left:640px;white-space:nowrap" class="ft00" id="receiptNumberReceipt">xxxxxxx</p>
873
+ <p style="position:absolute;top:149px;left:582px;white-space:nowrap" class="ft00">ที่ทำการ&#160;&#160; สำนักบริหารแรงงานต่างด้าว</p>
874
+ <p style="position:absolute;top:188px;left:602px;white-space:nowrap" class="ft00">วันที่&#160;&#160; 19 มีนาคม 2568</p>
875
+ <p style="position:absolute;top:227px;left:540px;white-space:nowrap" class="ft00">เลขที่ใบชำระเงิน&#160;&#160;</p>
876
+ <p style="position:absolute;top:227px;left:640px;white-space:nowrap" class="ft00" id="paymentNumberReceipt">IV680329/002308</p>
877
+ <p style="position:absolute;top:271px;left:60px;white-space:nowrap" class="ft00">เลขรับคำขอที่</p>
878
+ <p style="position:absolute;top:271px;left:180px;white-space:nowrap" class="ft00" id="requestNumberReceipt">xxxxxxx</p>
879
+ <p style="position:absolute;top:310px;left:60px;white-space:nowrap" class="ft00">ชื่อผู้ชำระเงิน</p>
880
+ <p style="position:absolute;top:310px;left:180px;white-space:nowrap" class="ft00" id="payerNameReceipt">xxxxxxxxxxxxx</p>
881
+ <p style="position:absolute;top:310px;left:471px;white-space:nowrap" class="ft00">สัญชาติ</p>
882
+ <p style="position:absolute;top:310px;left:520px;white-space:nowrap" class="ft00" id="nationalityReceipt">เมียนมา</p>
883
+ <p style="position:absolute;top:354px;left:60px;white-space:nowrap" class="ft00">เลขอ้างอิงคนต่างด้าว</p>
884
+ <p style="position:absolute;top:354px;left:180px;white-space:nowrap" class="ft00" id="alienReferenceReceipt">xxxxxxxxxxxxx</p>
885
+ <p style="position:absolute;top:354px;left:432px;white-space:nowrap" class="ft00">หมายเลขประจำตัวคนต่างด้าว</p>
886
+ <p style="position:absolute;top:354px;left:640px;white-space:nowrap" class="ft00" id="personalIDReceipt">xxxxxxxxxxxxx</p>
887
+ <p style="position:absolute;top:399px;left:60px;white-space:nowrap" class="ft00">ชื่อนายจ้าง / สถานประกอบการ&#160;&#160; บริษัท บาน กง เอ็นจิเนียริ่ง จำกัด</p>
888
+ <p style="position:absolute;top:438px;left:60px;white-space:nowrap" class="ft00">เลขประจำตัวนายจ้าง</p>
889
+ <p style="position:absolute;top:437px;left:233px;white-space:nowrap" class="ft00">&#160; 0415567000061</p>
890
+ <p style="position:absolute;top:526px;left:345px;white-space:nowrap" class="ft02"><b>รายการ</b></p>
891
+ <p style="position:absolute;top:526px;left:688px;white-space:nowrap" class="ft02"><b>จำนวนเงิน</b></p>
892
+ <p style="position:absolute;top:572px;left:118px;white-space:nowrap" class="ft03">1. ค่าธรรมเนียมในการยื่นคำขอ ฉบับละ 100 บาท</p>
893
+ <p style="position:absolute;top:572px;left:736px;white-space:nowrap" class="ft03">100.00</p>
894
+ <p style="position:absolute;top:616px;left:118px;white-space:nowrap" class="ft03">2. ค่าธรรมเนียมใบอนุญาตทำงาน</p>
895
+ <p style="position:absolute;top:616px;left:736px;white-space:nowrap" class="ft03">900.00</p>
896
+ <p style="position:absolute;top:694px;left:97px;white-space:nowrap" class="ft03">&#160;</p>
897
+ <p style="position:absolute;top:694px;left:648px;white-space:nowrap" class="ft03">&#160;</p>
898
+ <p style="position:absolute;top:772px;left:174px;white-space:nowrap" class="ft02"><b>รวมเป็นเงินทั้งสิ้น (บาท)</b></p>
899
+ <p style="position:absolute;top:799px;left:188px;white-space:nowrap" class="ft02"><b>( หนึ่งพันบาทถ้วน )</b></p>
900
+ <p style="position:absolute;top:786px;left:385px;white-space:nowrap" class="ft03">&#160;</p>
901
+ <p style="position:absolute;top:774px;left:722px;white-space:nowrap" class="ft02"><b>1,000.00</b></p>
902
+ <p style="position:absolute;top:894px;left:94px;white-space:nowrap" class="ft00">ได้รับเงินไว้เป็นการถูกต้องแล้ว</p>
903
+ <p style="position:absolute;top:977px;left:481px;white-space:nowrap" class="ft00">(ลงชื่อ)</p>
904
+ <p style="position:absolute;top:977px;left:564px;white-space:nowrap" class="ft00">นางสาวอารีวรรณ โพธิ์นิ่มแดง</p>
905
+ <p style="position:absolute;top:977px;left:762px;white-space:nowrap" class="ft00">(ผู้รับเงิน)</p>
906
+ <p style="position:absolute;top:1017px;left:473px;white-space:nowrap" class="ft00">ตำแหน่ง</p>
907
+ <p style="position:absolute;top:1016px;left:562px;white-space:nowrap" class="ft00">นักวิชาการ���รงงานชำนาญการ</p>
908
+ <p style="position:absolute;top:1133px;left:55px;white-space:nowrap" class="ft06" id="receiptTimestamp">Loading...</p>
909
+ </div>
910
+
911
+ <script type="text/javascript">
912
+ let allWorkerData = [];
913
+ let currentIndex = 0;
914
+ let isGrayscaleEnabled = false;
915
+ let selectedWorkers = new Set();
916
+ let isDropdownOpen = false;
917
+ let filteredData = [];
918
+
919
+ // DOM Elements
920
+ const dropdownHeader = document.getElementById('dropdownHeader');
921
+ const dropdownMenu = document.getElementById('dropdownMenu');
922
+ const dropdownArrow = document.getElementById('dropdownArrow');
923
+ const dropdownList = document.getElementById('dropdownList');
924
+ const searchInput = document.getElementById('searchInput');
925
+ const searchIcon = document.getElementById('searchIcon');
926
+ const clearSearch = document.getElementById('clearSearch');
927
+ const selectedText = document.getElementById('selectedText');
928
+ const selectedCountEl = document.getElementById('selectedCount');
929
+ const selectAllBtn = document.getElementById('selectAllBtn');
930
+ const clearSelectionBtn = document.getElementById('clearSelectionBtn');
931
+ const downloadSelectedBtn = document.getElementById('downloadSelectedBtn');
932
+ const downloadCurrentBtn = document.getElementById('downloadCurrentBtn');
933
+ const downloadAllBtn = document.getElementById('downloadAllBtn');
934
+ const grayscaleBtn = document.getElementById('grayscaleBtn');
935
+ const randomizeBtn = document.getElementById('randomizeBtn');
936
+ const progressContainer = document.getElementById('progressContainer');
937
+ const progressFill = document.getElementById('progressFill');
938
+ const progressText = document.getElementById('progressText');
939
+
940
+ // Normalize text for search (handles Thai, English, numbers)
941
+ function normalizeText(text) {
942
+ if (!text) return '';
943
+ return text.toString().toLowerCase().trim()
944
+ .replace(/\s+/g, ' ');
945
+ }
946
+
947
+ // Search function supporting Thai, English, and numbers
948
+ function searchWorkers(query) {
949
+ const normalizedQuery = normalizeText(query);
950
+ if (!normalizedQuery) {
951
+ filteredData = [...allWorkerData];
952
+ return filteredData;
953
+ }
954
+
955
+ filteredData = allWorkerData.filter(worker => {
956
+ const searchFields = [
957
+ worker.requestNumber,
958
+ worker.englishName,
959
+ worker.thaiName,
960
+ worker.personalID,
961
+ worker.workPermitNumber,
962
+ worker.alienReferenceNumber,
963
+ worker.nationality
964
+ ];
965
+
966
+ return searchFields.some(field => {
967
+ const normalizedField = normalizeText(field);
968
+ return normalizedField.includes(normalizedQuery);
969
+ });
970
+ });
971
+
972
+ return filteredData;
973
+ }
974
+
975
+ // Highlight matched text
976
+ function highlightText(text, query) {
977
+ if (!text || !query) return text || '';
978
+ const normalizedQuery = normalizeText(query);
979
+ if (!normalizedQuery) return text;
980
+
981
+ const regex = new RegExp(`(${escapeRegex(query)})`, 'gi');
982
+ return text.replace(regex, '<span class="highlight">$1</span>');
983
+ }
984
+
985
+ function escapeRegex(string) {
986
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
987
+ }
988
+
989
+ // Toggle dropdown
990
+ function toggleDropdown(forceClose = false) {
991
+ if (forceClose || isDropdownOpen) {
992
+ isDropdownOpen = false;
993
+ dropdownMenu.classList.remove('open');
994
+ dropdownArrow.classList.remove('open');
995
+ dropdownHeader.setAttribute('aria-expanded', 'false');
996
+ } else {
997
+ isDropdownOpen = true;
998
+ dropdownMenu.classList.add('open');
999
+ dropdownArrow.classList.add('open');
1000
+ dropdownHeader.setAttribute('aria-expanded', 'true');
1001
+ searchInput.focus();
1002
+ renderDropdownList();
1003
+ }
1004
+ }
1005
+
1006
+ // Render dropdown list
1007
+ function renderDropdownList(query = '') {
1008
+ const workers = searchWorkers(query);
1009
+
1010
+ if (workers.length === 0) {
1011
+ dropdownList.innerHTML = `
1012
+ <div class="no-results">
1013
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1014
+ <circle cx="11" cy="11" r="8"></circle>
1015
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
1016
+ </svg>
1017
+ <p>ไม่พบผลลัพธ์สำหรับ "${query}"</p>
1018
+ </div>
1019
+ `;
1020
+ return;
1021
+ }
1022
+
1023
+ dropdownList.innerHTML = workers.map((worker, idx) => {
1024
+ const originalIndex = allWorkerData.findIndex(w => w.requestNumber === worker.requestNumber);
1025
+ const isChecked = selectedWorkers.has(originalIndex);
1026
+ const isActive = originalIndex === currentIndex;
1027
+
1028
+ const displayName = query ? highlightText(worker.englishName || worker.thaiName || 'N/A', query) : (worker.englishName || worker.thaiName || 'N/A');
1029
+ const displayId = query ? highlightText(worker.requestNumber || 'N/A', query) : (worker.requestNumber || 'N/A');
1030
+
1031
+ return `
1032
+ <div class="dropdown-item ${isChecked ? 'selected' : ''} ${isActive ? 'active' : ''}"
1033
+ data-index="${originalIndex}"
1034
+ role="option"
1035
+ aria-selected="${isActive}"
1036
+ tabindex="0">
1037
+ <img class="dropdown-item-image"
1038
+ src="${worker.profileImage || 'https://via.placeholder.com/40?text=N/A'}"
1039
+ alt="${worker.englishName || 'Worker'}"
1040
+ onerror="this.src='https://via.placeholder.com/40?text=N/A'"/>
1041
+ <div class="dropdown-item-content">
1042
+ <div class="dropdown-item-title">${displayName}</div>
1043
+ <div class="dropdown-item-subtitle">เลขที่: ${displayId}</div>
1044
+ </div>
1045
+ <div class="dropdown-item-checkbox" onclick="event.stopPropagation(); toggleWorkerSelection(${originalIndex})">
1046
+ <div class="custom-checkbox ${isChecked ? 'checked' : ''}" role="checkbox" aria-checked="${isChecked}">
1047
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
1048
+ <polyline points="20,6 9,17 4,12"></polyline>
1049
+ </svg>
1050
+ </div>
1051
+ </div>
1052
+ </div>
1053
+ `;
1054
+ }).join('');
1055
+
1056
+ // Add click listeners
1057
+ dropdownList.querySelectorAll('.dropdown-item').forEach(item => {
1058
+ item.addEventListener('click', (e) => {
1059
+ if (!e.target.closest('.dropdown-item-checkbox')) {
1060
+ const index = parseInt(item.dataset.index);
1061
+ selectWorker(index);
1062
+ }
1063
+ });
1064
+
1065
+ // Keyboard navigation
1066
+ item.addEventListener('keydown', (e) => {
1067
+ if (e.key === 'Enter' || e.key === ' ') {
1068
+ e.preventDefault();
1069
+ const index = parseInt(item.dataset.index);
1070
+ if (e.shiftKey) {
1071
+ toggleWorkerSelection(index);
1072
+ } else {
1073
+ selectWorker(index);
1074
+ }
1075
+ }
1076
+ });
1077
+ });
1078
+ }
1079
+
1080
+ // Select a worker to view
1081
+ function selectWorker(index) {
1082
+ currentIndex = index;
1083
+ displayWorkerData(index);
1084
+ updateDropdownHeader();
1085
+ randomizeBothOverlays();
1086
+ }
1087
+
1088
+ // Toggle worker selection for PDF download
1089
+ function toggleWorkerSelection(index) {
1090
+ if (selectedWorkers.has(index)) {
1091
+ selectedWorkers.delete(index);
1092
+ } else {
1093
+ selectedWorkers.add(index);
1094
+ }
1095
+ updateSelectionUI();
1096
+ renderDropdownList(searchInput.value);
1097
+ }
1098
+
1099
+ // Update selection UI
1100
+ function updateSelectionUI() {
1101
+ const count = selectedWorkers.size;
1102
+ selectedCountEl.textContent = `เลือกแล้ว ${count} รายการ`;
1103
+ downloadSelectedBtn.disabled = count === 0;
1104
+ downloadSelectedBtn.innerHTML = `
1105
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1106
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
1107
+ <polyline points="7,10 12,15 17,10"></polyline>
1108
+ <line x1="12" y1="15" x2="12" y2="3"></line>
1109
+ </svg>
1110
+ ดาวน์โหลด PDF ที่เลือก (${count})
1111
+ `;
1112
+ }
1113
+
1114
+ // Update dropdown header
1115
+ function updateDropdownHeader() {
1116
+ if (currentIndex >= 0 && currentIndex < allWorkerData.length) {
1117
+ const worker = allWorkerData[currentIndex];
1118
+ selectedText.textContent = `${worker.requestNumber || 'N/A'} - ${worker.englishName || worker.thaiName || 'N/A'}`;
1119
+ setImageSrc('dropdownProfilePic', worker.profileImage, 'https://via.placeholder.com/32?text=N/A', worker.englishName, 'dropdown profile');
1120
+ } else {
1121
+ selectedText.textContent = 'กรุณาเลือกพนักงาน';
1122
+ document.getElementById('dropdownProfilePic').src = 'https://via.placeholder.com/32?text=...';
1123
+ }
1124
+ }
1125
+
1126
+ // Select all workers
1127
+ function selectAllWorkers() {
1128
+ const currentFiltered = searchWorkers(searchInput.value);
1129
+ currentFiltered.forEach(worker => {
1130
+ const idx = allWorkerData.findIndex(w => w.requestNumber === worker.requestNumber);
1131
+ if (idx !== -1) selectedWorkers.add(idx);
1132
+ });
1133
+ updateSelectionUI();
1134
+ renderDropdownList(searchInput.value);
1135
+ }
1136
+
1137
+ // Clear all selections
1138
+ function clearAllSelections() {
1139
+ selectedWorkers.clear();
1140
+ updateSelectionUI();
1141
+ renderDropdownList(searchInput.value);
1142
+ }
1143
+
1144
+ // Event Listeners
1145
+ dropdownHeader.addEventListener('click', () => toggleDropdown());
1146
+ dropdownHeader.addEventListener('keydown', (e) => {
1147
+ if (e.key === 'Enter' || e.key === ' ') {
1148
+ e.preventDefault();
1149
+ toggleDropdown();
1150
+ }
1151
+ });
1152
+
1153
+ searchInput.addEventListener('input', (e) => {
1154
+ const query = e.target.value;
1155
+ renderDropdownList(query);
1156
+
1157
+ if (query) {
1158
+ searchIcon.style.display = 'none';
1159
+ clearSearch.classList.add('visible');
1160
+ } else {
1161
+ searchIcon.style.display = 'block';
1162
+ clearSearch.classList.remove('visible');
1163
+ }
1164
+ });
1165
+
1166
+ clearSearch.addEventListener('click', () => {
1167
+ searchInput.value = '';
1168
+ searchIcon.style.display = 'block';
1169
+ clearSearch.classList.remove('visible');
1170
+ renderDropdownList('');
1171
+ searchInput.focus();
1172
+ });
1173
+
1174
+ selectAllBtn.addEventListener('click', selectAllWorkers);
1175
+ clearSelectionBtn.addEventListener('click', clearAllSelections);
1176
+
1177
+ // Close dropdown when clicking outside
1178
+ document.addEventListener('click', (e) => {
1179
+ if (!e.target.closest('.custom-dropdown')) {
1180
+ toggleDropdown(true);
1181
+ }
1182
+ });
1183
+
1184
+ // Button event listeners
1185
+ grayscaleBtn.addEventListener('click', toggleGrayscale);
1186
+ downloadCurrentBtn.addEventListener('click', downloadCurrentPDF);
1187
+ downloadSelectedBtn.addEventListener('click', downloadSelectedPDFs);
1188
+ downloadAllBtn.addEventListener('click', downloadAllPDFs);
1189
+ randomizeBtn.addEventListener('click', randomizeBothOverlays);
1190
+
1191
+ function setImageSrc(imgElementId, src, defaultSrc, workerName, type) {
1192
+ const imgElement = document.getElementById(imgElementId);
1193
+ if (!imgElement) {
1194
+ console.error(`Image element with ID "${imgElementId}" not found.`);
1195
+ return;
1196
+ }
1197
+ imgElement.src = src || defaultSrc;
1198
+ imgElement.alt = workerName || type;
1199
+ imgElement.crossOrigin = "anonymous";
1200
+ imgElement.onerror = function() {
1201
+ imgElement.src = defaultSrc;
1202
+ imgElement.alt = 'No Image';
1203
+ };
1204
+ }
1205
+
1206
+ function getCurrentTimestamp() {
1207
+ const now = new Date();
1208
+ return now.toLocaleString('th-TH', {
1209
+ hour: '2-digit',
1210
+ minute: '2-digit',
1211
+ hour12: false
1212
+ }).replace(',', ' น.') + ' น.';
1213
+ }
1214
+
1215
+ function toggleGrayscale() {
1216
+ isGrayscaleEnabled = !isGrayscaleEnabled;
1217
+ document.body.style.filter = isGrayscaleEnabled ? 'grayscale(100%)' : 'grayscale(0%)';
1218
+ grayscaleBtn.innerHTML = `
1219
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1220
+ <circle cx="12" cy="12" r="10"></circle>
1221
+ <path d="M12 2a10 10 0 0 1 0 20"></path>
1222
+ </svg>
1223
+ Grayscale: ${isGrayscaleEnabled ? 'เปิด' : 'ปิด'}
1224
+ `;
1225
+ }
1226
+
1227
+ function jitter(base, maxDelta = 5) {
1228
+ return base + (Math.random() * 2 - 1) * maxDelta;
1229
+ }
1230
+
1231
+ const borImages = ['bor01.png', 'bor02.png', 'bor03.png', 'bor04.png'];
1232
+ let borCounter = 0;
1233
+
1234
+ function randomizeOverlayPosition(containerId, startIdx, useSetA = false, borIndex = 0, opacity = 1, refPos = null) {
1235
+ const container = document.getElementById(containerId);
1236
+ if (!container) return [];
1237
+
1238
+ const setA = ['a1.png','a2.png','a3.png','a4.png','a5.png','a6.png'];
1239
+ const setB = ['b1.png','b2.png','b3.png','b4.png','b5.png','b6.png'];
1240
+ const imageFiles = useSetA ? setA : setB;
1241
+
1242
+ const borSrc = borImages[borCounter % borImages.length];
1243
+ borCounter++;
1244
+
1245
+ container.innerHTML = '';
1246
+
1247
+ const imgs = [];
1248
+ for (let i = 0; i < 3; i++) {
1249
+ const img = document.createElement('img');
1250
+ img.src = imageFiles[(startIdx + i) % 6];
1251
+ img.style.zIndex = [1,2,3][i];
1252
+ img.style.position = 'absolute';
1253
+ container.appendChild(img);
1254
+ imgs.push(img);
1255
+ }
1256
+
1257
+ const borImg = document.createElement('img');
1258
+ borImg.src = borSrc;
1259
+ borImg.style.opacity = opacity;
1260
+ borImg.style.zIndex = 0;
1261
+ borImg.style.position = 'absolute';
1262
+ container.insertBefore(borImg, container.firstChild);
1263
+
1264
+ Promise.all([
1265
+ ...imgs.map(i => new Promise(r => { i.onload = r; if (i.complete) r(); })),
1266
+ new Promise(r => { borImg.onload = r; if (borImg.complete) r(); })
1267
+ ]).then(() => {
1268
+ const w = container.offsetWidth;
1269
+ const h = container.offsetHeight;
1270
+
1271
+ const positions = imgs.map((img, i) => {
1272
+ const imgW = img.offsetWidth;
1273
+ const imgH = img.offsetHeight;
1274
+ const maxX = w - imgW;
1275
+ const maxY = h - imgH;
1276
+
1277
+ const baseX = refPos && refPos[i] ? refPos[i].x : Math.random() * maxX;
1278
+ const baseY = refPos && refPos[i] ? refPos[i].y : Math.random() * maxY;
1279
+ const x = jitter(baseX);
1280
+ const y = jitter(baseY);
1281
+
1282
+ const rotation = (Math.random() - 0.5) * 10 + (startIdx + i) * 0.3;
1283
+ let transform = `rotate(${rotation}deg)`;
1284
+
1285
+ if (parseInt(img.style.zIndex) === 3) {
1286
+ const scale = 1 + (Math.random() - 0.5) * 0.3;
1287
+ transform += ` scale(${scale})`;
1288
+ }
1289
+
1290
+ img.style.left = `${Math.max(0, Math.min(x, maxX))}px`;
1291
+ img.style.top = `${Math.max(0, Math.min(y, maxY))}px`;
1292
+ img.style.transform = transform;
1293
+
1294
+ if (i === 0) {
1295
+ borImg.style.left = img.style.left;
1296
+ borImg.style.top = img.style.top;
1297
+ borImg.style.transform = transform;
1298
+ }
1299
+
1300
+ return { x: parseFloat(img.style.left), y: parseFloat(img.style.top) };
1301
+ });
1302
+
1303
+ container._lastPositions = positions;
1304
+ });
1305
+
1306
+ return container._lastPositions || [];
1307
+ }
1308
+
1309
+ function randomizeBothOverlays() {
1310
+ const useSetA = Math.random() < 0.5;
1311
+ const useReverse = Math.random() < 0.5;
1312
+ const start1 = useReverse ? 3 : 0;
1313
+ const start2 = useReverse ? 0 : 3;
1314
+
1315
+ const borIndex1 = borCounter % 4;
1316
+ const borIndex2 = (borCounter + 1) % 4;
1317
+ borCounter++;
1318
+
1319
+ const baseOpacity = 0.75 + Math.random() * 0.15;
1320
+ const opacity1 = Math.min(1, baseOpacity + 0.05);
1321
+ const opacity2 = Math.max(0.6, baseOpacity - 0.05);
1322
+
1323
+ const ref = randomizeOverlayPosition('overlayContainer1', start1, useSetA, borIndex1, opacity1);
1324
+ randomizeOverlayPosition('overlayContainer2', start2, useSetA, borIndex2, opacity2, ref);
1325
+ }
1326
+
1327
+ function loadWorkerData() {
1328
+ const urlParams = new URLSearchParams(window.location.search);
1329
+ const requestNumberFromUrl = urlParams.get('id');
1330
+
1331
+ // Set loading states
1332
+ document.getElementById('requestNumber').innerHTML = '<b>Loading data...</b>';
1333
+ document.getElementById('englishName').innerHTML = '<b>Loading data...</b>';
1334
+ document.getElementById('englishNameDuplicate').innerHTML = 'Loading data...';
1335
+ document.getElementById('thaiName').innerHTML = ': Loading data...';
1336
+ document.getElementById('personalID').innerHTML = ': Loading data...';
1337
+ document.getElementById('workPermitNumber').innerHTML = ': Loading data...';
1338
+ document.getElementById('alienReferenceNumber').innerHTML = ': Loading data...';
1339
+ document.getElementById('nationality').innerHTML = ': Loading data...';
1340
+ document.getElementById('age').innerHTML = ': Loading data...';
1341
+ document.getElementById('birthDate').innerHTML = ': Loading data...';
1342
+ document.getElementById('timestamp').innerHTML = 'Loading data...';
1343
+ document.getElementById('requestNumberReceipt').innerHTML = 'Loading data...';
1344
+ document.getElementById('receiptNumberReceipt').innerHTML = 'Loading data...';
1345
+ document.getElementById('paymentNumberReceipt').innerHTML = 'Loading data...';
1346
+ document.getElementById('payerNameReceipt').innerHTML = 'Loading data...';
1347
+ document.getElementById('nationalityReceipt').innerHTML = 'Loading data...';
1348
+ document.getElementById('alienReferenceReceipt').innerHTML = 'Loading data...';
1349
+ document.getElementById('personalIDReceipt').innerHTML = 'Loading data...';
1350
+ document.getElementById('receiptTimestamp').innerHTML = 'Loading data...';
1351
+
1352
+ fetch('combined-data.json')
1353
+ .then(response => {
1354
+ if (!response.ok) throw new Error('Failed to load combined-data.json');
1355
+ return response.json();
1356
+ })
1357
+ .then(data => {
1358
+ if (!Array.isArray(data) || data.length === 0) throw new Error('Data is empty or not a valid array.');
1359
+ allWorkerData = data;
1360
+ filteredData = [...data];
1361
+
1362
+ currentIndex = requestNumberFromUrl
1363
+ ? allWorkerData.findIndex(w => w.requestNumber && w.requestNumber.toString() === requestNumberFromUrl.toString())
1364
+ : 0;
1365
+ if (currentIndex === -1) currentIndex = 0;
1366
+
1367
+ displayWorkerData(currentIndex);
1368
+ updateDropdownHeader();
1369
+ renderDropdownList();
1370
+ updateSelectionUI();
1371
+ })
1372
+ .catch(error => {
1373
+ console.error('Error loading JSON:', error);
1374
+ showError(error.message);
1375
+ });
1376
+ }
1377
+
1378
+ function showError(message) {
1379
+ const errorDiv = document.createElement('div');
1380
+ errorDiv.className = 'text-red-500 text-center mt-5 p-2 bg-red-100 border border-red-500 rounded';
1381
+ errorDiv.innerHTML = `<p>Error loading: ${message}</p>`;
1382
+ document.body.appendChild(errorDiv);
1383
+ clearWorkerData();
1384
+ setImageSrc('profilePic', null, 'https://via.placeholder.com/110x138?text=Error', 'N/A', 'profile picture');
1385
+ setImageSrc('qrCode', null, 'https://via.placeholder.com/69?text=Error', 'N/A', 'work permit QR code');
1386
+ setImageSrc('receiptQrCode', null, 'https://via.placeholder.com/90?text=Error', 'N/A', 'receipt QR code');
1387
+ setImageSrc('dropdownProfilePic', null, 'https://via.placeholder.com/32?text=Error', 'N/A', 'dropdown profile picture');
1388
+ const timestamp = getCurrentTimestamp();
1389
+ const workPermitTimestamp = `เอกสารอิเล็กทรอนิกส์ฉบับนี้ถูกสร้างจากระบบอนุญาตทำงานคนต่างด้าวที่มีสถานะการทำงานไม่ถูกต้องตามกฎหมาย ตามมติคณะรัฐมนตรีเมื่อวันที่ 24 กันยายน 2567<br>โดยกรมการจัดหางาน กระทรวงแรงงาน<br>พิมพ์เอกสาร วันที่ 10/04/2568 ${timestamp} `;
1390
+ document.getElementById('timestamp').innerHTML = workPermitTimestamp;
1391
+ document.getElementById('receiptTimestamp').innerHTML = workPermitTimestamp;
1392
+ }
1393
+
1394
+ function displayWorkerData(index) {
1395
+ const defaultProfileSrc = 'https://via.placeholder.com/110x138?text=No+Image';
1396
+ const defaultQrCodeSrc = 'https://via.placeholder.com/69?text=No+QR';
1397
+ const defaultReceiptQrCodeSrc = 'https://via.placeholder.com/90?text=No+QR';
1398
+
1399
+ if (!allWorkerData || !Array.isArray(allWorkerData) || index < 0 || index >= allWorkerData.length) {
1400
+ clearWorkerData();
1401
+ setImageSrc('profilePic', null, defaultProfileSrc, 'N/A', 'profile picture');
1402
+ setImageSrc('qrCode', null, defaultQrCodeSrc, 'N/A', 'work permit QR code');
1403
+ setImageSrc('receiptQrCode', null, defaultReceiptQrCodeSrc, 'N/A', 'receipt QR code');
1404
+ const timestamp = getCurrentTimestamp();
1405
+ const workPermitTimestamp = `เอกสารอิเล็กทรอนิกส์ฉบับนี้ถูกสร้างจากระบบอนุญาตทำงานคนต่างด้าวที่มีสถานะการทำงานไม่ถูกต้องตามกฎหมาย ตามมติคณะรัฐมนตรีเมื่อวันที่ 24 กันยายน 2567<br>โดยกรมการจัดหางาน กระทรวงแรงงาน<br>พิมพ์เอกสาร วันที่ 10/04/2568 ${timestamp} `;
1406
+ document.getElementById('timestamp').innerHTML = workPermitTimestamp;
1407
+ document.getElementById('receiptTimestamp').innerHTML = workPermitTimestamp;
1408
+ return;
1409
+ }
1410
+
1411
+ const worker = allWorkerData[index];
1412
+ document.getElementById('requestNumber').innerHTML = `<b>${worker.requestNumber || 'N/A'}</b>`;
1413
+ document.getElementById('englishName').innerHTML = `<b>${worker.englishName || 'N/A'}</b>`;
1414
+ document.getElementById('englishNameDuplicate').innerHTML = worker.englishName || 'N/A';
1415
+ document.getElementById('thaiName').innerHTML = `: ${worker.thaiName || 'N/A'}`;
1416
+ document.getElementById('personalID').innerHTML = `: ${worker.personalID || 'N/A'}`;
1417
+ document.getElementById('workPermitNumber').innerHTML = `: ${worker.workPermitNumber || 'N/A'}`;
1418
+ document.getElementById('alienReferenceNumber').innerHTML = `: ${worker.alienReferenceNumber || 'N/A'}`;
1419
+ document.getElementById('nationality').innerHTML = `: ${worker.nationality || 'N/A'}`;
1420
+ document.getElementById('age').innerHTML = `: ${worker.age || 'N/A'}`;
1421
+ document.getElementById('birthDate').innerHTML = `: ${worker.birthDate || 'N/A'}`;
1422
+ document.getElementById('requestNumberReceipt').innerHTML = worker.requestNumber || 'N/A';
1423
+ document.getElementById('receiptNumberReceipt').innerHTML = worker.receiptNumber || 'N/A';
1424
+ document.getElementById('paymentNumberReceipt').innerHTML = worker.paymentNumber || 'N/A';
1425
+ document.getElementById('payerNameReceipt').innerHTML = worker.englishName || worker.thaiName || 'N/A';
1426
+ document.getElementById('nationalityReceipt').innerHTML = worker.nationality || 'N/A';
1427
+ document.getElementById('alienReferenceReceipt').innerHTML = worker.alienReferenceNumber || 'N/A';
1428
+ document.getElementById('personalIDReceipt').innerHTML = worker.personalID || 'N/A';
1429
+ setImageSrc('profilePic', worker.profileImage, defaultProfileSrc, worker.englishName, 'profile picture');
1430
+
1431
+ const currentDomain = window.location.origin;
1432
+
1433
+ const workerPageUrl = `${currentDomain}/worker.html?id=${encodeURIComponent(worker.requestNumber || '')}`;
1434
+ const receiptUrl = `${currentDomain}/pdf.html?id=${encodeURIComponent(worker.requestNumber || '')}`;
1435
+
1436
+ setImageSrc('qrCode', `https://api.qrserver.com/v1/create-qr-code/?size=300x300&ecc=H&data=${encodeURIComponent(workerPageUrl)}`, defaultQrCodeSrc, worker.englishName, 'work permit QR code');
1437
+ setImageSrc('receiptQrCode', `https://api.qrserver.com/v1/create-qr-code/?size=300x300&ecc=H&data=${encodeURIComponent(receiptUrl)}`, defaultReceiptQrCodeSrc, worker.englishName, 'receipt QR code');
1438
+
1439
+ const timestamp = getCurrentTimestamp();
1440
+ const workPermitTimestamp = `เอกสารอิเล็กทรอนิกส์ฉบับนี้ถูกสร้างจากระบบอนุญาตทำงานคนต่างด้าวที่มีสถานะการทำงานไม่ถูกต้องตามกฎหมาย ตามมติคณะรัฐมนตรีเมื่อวันที่ 24 กันยายน 2567<br>โดยกรมการจัดหางาน กระทรวงแรงงาน<br>พิมพ์เอกสาร วันที่ 10/04/2568 ${timestamp} `;
1441
+ document.getElementById('timestamp').innerHTML = workPermitTimestamp;
1442
+ document.getElementById('receiptTimestamp').innerHTML = workPermitTimestamp;
1443
+ }
1444
+
1445
+ function clearWorkerData() {
1446
+ document.getElementById('requestNumber').innerHTML = '<b>N/A</b>';
1447
+ document.getElementById('englishName').innerHTML = '<b>N/A</b>';
1448
+ document.getElementById('englishNameDuplicate').innerHTML = 'N/A';
1449
+ document.getElementById('thaiName').innerHTML = ': N/A';
1450
+ document.getElementById('personalID').innerHTML = ': N/A';
1451
+ document.getElementById('workPermitNumber').innerHTML = ': N/A';
1452
+ document.getElementById('alienReferenceNumber').innerHTML = ': N/A';
1453
+ document.getElementById('nationality').innerHTML = ': N/A';
1454
+ document.getElementById('age').innerHTML = ': N/A';
1455
+ document.getElementById('birthDate').innerHTML = ': N/A';
1456
+ document.getElementById('requestNumberReceipt').innerHTML = 'N/A';
1457
+ document.getElementById('receiptNumberReceipt').innerHTML = 'N/A';
1458
+ document.getElementById('paymentNumberReceipt').innerHTML = 'N/A';
1459
+ document.getElementById('payerNameReceipt').innerHTML = 'N/A';
1460
+ document.getElementById('nationalityReceipt').innerHTML = 'N/A';
1461
+ document.getElementById('alienReferenceReceipt').innerHTML = 'N/A';
1462
+ document.getElementById('personalIDReceipt').innerHTML = 'N/A';
1463
+ }
1464
+
1465
+ // Show/hide progress bar
1466
+ function showProgress(show, current = 0, total = 0) {
1467
+ if (show) {
1468
+ progressContainer.classList.add('visible');
1469
+ const percent = total > 0 ? Math.round((current / total) * 100) : 0;
1470
+ progressFill.style.width = `${percent}%`;
1471
+ progressText.textContent = `กำลังดาวน์โหลด ${current}/${total} (${percent}%)`;
1472
+ } else {
1473
+ progressContainer.classList.remove('visible');
1474
+ }
1475
+ }
1476
+
1477
+ // Disable/enable all buttons
1478
+ function setButtonsDisabled(disabled) {
1479
+ downloadCurrentBtn.disabled = disabled;
1480
+ downloadSelectedBtn.disabled = disabled || selectedWorkers.size === 0;
1481
+ downloadAllBtn.disabled = disabled;
1482
+ randomizeBtn.disabled = disabled;
1483
+ grayscaleBtn.disabled = disabled;
1484
+ }
1485
+
1486
+ async function downloadPDF(workerItem, filenameSuffix = '') {
1487
+ try {
1488
+ randomizeBothOverlays();
1489
+ const elements = [document.getElementById('page1-div'), document.getElementById('page2-div')];
1490
+ const reqNum = workerItem.requestNumber || 'worker';
1491
+ const english = (workerItem.englishName || '').replace(/\s+/g, ''); // ตัดช่องว่างออก
1492
+ const last4 = reqNum.slice(-4); // เลขท้าย 4 หลัก
1493
+
1494
+ // ถ้าอยากใช้เลขท้าย 4:
1495
+ const filename = `${last4}_${english || 'noname'}.pdf`;
1496
+
1497
+ const controls = document.querySelector('.controls-container');
1498
+ if (controls) controls.style.display = 'none';
1499
+
1500
+ if (allWorkerData[currentIndex].requestNumber !== workerItem.requestNumber) {
1501
+ const tempIndex = allWorkerData.findIndex(w => w.requestNumber === workerItem.requestNumber);
1502
+ if (tempIndex !== -1) displayWorkerData(tempIndex);
1503
+ }
1504
+
1505
+ const images = document.querySelectorAll('img');
1506
+ await Promise.all(Array.from(images).map(img => {
1507
+ return new Promise(resolve => {
1508
+ if (img.complete && img.naturalWidth > 0) resolve();
1509
+ else {
1510
+ img.onload = resolve;
1511
+ img.onerror = () => {
1512
+ console.error(`Image failed to load: ${img.src}`);
1513
+ resolve();
1514
+ };
1515
+ }
1516
+ });
1517
+ }));
1518
+
1519
+ const container = document.createElement('div');
1520
+ container.style.width = '892px';
1521
+ container.style.height = '2522px';
1522
+ container.className = 'pdf-export-container';
1523
+
1524
+ const page1Clone = elements[0].cloneNode(true);
1525
+ const page2Clone = elements[1].cloneNode(true);
1526
+ page1Clone.style.marginBottom = '0';
1527
+ page2Clone.style.marginTop = '0';
1528
+
1529
+ container.appendChild(page1Clone);
1530
+ container.appendChild(page2Clone);
1531
+ document.body.appendChild(container);
1532
+
1533
+ const allImages = container.querySelectorAll('img');
1534
+ allImages.forEach(img => {
1535
+ img.style.filter = 'grayscale(100%)';
1536
+ img.style.webkitFilter = 'grayscale(100%)';
1537
+ });
1538
+
1539
+
1540
+ await new Promise(resolve => setTimeout(resolve, 100));
1541
+
1542
+ const opt = {
1543
+ margin: [0, 0, 0, 0],
1544
+ filename: filename,
1545
+ image: { type: 'jpeg', quality: 0.98 },
1546
+ html2canvas: {
1547
+ scale: 3,
1548
+ useCORS: true,
1549
+ width: 892,
1550
+ height: 2522,
1551
+ logging: false,
1552
+ allowTaint: true,
1553
+ backgroundColor: '#ffffff'
1554
+ },
1555
+ jsPDF: {
1556
+ unit: 'px',
1557
+ format: [892, 1261],
1558
+ orientation: 'portrait',
1559
+ compress: true
1560
+ }
1561
+ };
1562
+
1563
+ await html2pdf().set(opt).from(container).save();
1564
+
1565
+ document.body.removeChild(container);
1566
+ if (controls) controls.style.display = 'block';
1567
+
1568
+ if (allWorkerData[currentIndex].requestNumber !== workerItem.requestNumber) {
1569
+ displayWorkerData(currentIndex);
1570
+ }
1571
+ } catch (error) {
1572
+ console.error('Error generating PDF:', error);
1573
+ alert('เกิดข้อผิดพลาดในการสร้าง PDF กรุณาลองใหม่อีก���รั้ง');
1574
+ const controls = document.querySelector('.controls-container');
1575
+ if (controls) controls.style.display = 'block';
1576
+ }
1577
+ }
1578
+
1579
+ async function downloadCurrentPDF() {
1580
+ if (currentIndex >= 0 && currentIndex < allWorkerData.length) {
1581
+ setButtonsDisabled(true);
1582
+ try {
1583
+ await downloadPDF(allWorkerData[currentIndex]);
1584
+ } catch (error) {
1585
+ alert('เกิดข้อผิดพลาดในการสร้าง PDF กรุณาลองใหม่อีกครั้ง');
1586
+ }
1587
+ setButtonsDisabled(false);
1588
+ } else {
1589
+ alert('ไม่มีข้อมูลพนักงานที่เลือก');
1590
+ }
1591
+ }
1592
+
1593
+ async function downloadSelectedPDFs() {
1594
+ if (selectedWorkers.size === 0) {
1595
+ alert('กรุณาเลือกพนักงานอย่างน้อย 1 รายการ');
1596
+ return;
1597
+ }
1598
+
1599
+ const selectedArray = Array.from(selectedWorkers).sort((a, b) => a - b);
1600
+ const confirmDownload = confirm(`คุณต้องการดาวน์โหลดใบอนุญาตทำงานสำหรับพนักงาน ${selectedArray.length} รายการใช่หรือไม่?`);
1601
+ if (!confirmDownload) return;
1602
+
1603
+ setButtonsDisabled(true);
1604
+ showProgress(true, 0, selectedArray.length);
1605
+
1606
+ for (let i = 0; i < selectedArray.length; i++) {
1607
+ const workerIndex = selectedArray[i];
1608
+ try {
1609
+ showProgress(true, i + 1, selectedArray.length);
1610
+ displayWorkerData(workerIndex);
1611
+ await downloadPDF(allWorkerData[workerIndex], `work_permit_${i + 1}`);
1612
+ await new Promise(resolve => setTimeout(resolve, 800));
1613
+ } catch (error) {
1614
+ console.error(`Error downloading PDF for worker at index ${workerIndex}:`, error);
1615
+ continue;
1616
+ }
1617
+ }
1618
+
1619
+ showProgress(false);
1620
+ setButtonsDisabled(false);
1621
+ alert(`ดาวน์โหลดใบอนุญาตทำงานเรียบร้อยแล้ว ${selectedArray.length} รายการ`);
1622
+
1623
+ if (currentIndex >= 0 && currentIndex < allWorkerData.length) {
1624
+ displayWorkerData(currentIndex);
1625
+ }
1626
+ }
1627
+
1628
+ async function downloadAllPDFs() {
1629
+ if (!allWorkerData || allWorkerData.length === 0) {
1630
+ alert('ไม่มีข้อมูลพนักงาน');
1631
+ return;
1632
+ }
1633
+
1634
+ const confirmDownload = confirm(`คุณต้องการดาวน์โหลดใบอนุญาตทำงานสำหรับพนักงานทั้งหมด ${allWorkerData.length} รายการใช่หรือไม่?`);
1635
+ if (!confirmDownload) return;
1636
+
1637
+ setButtonsDisabled(true);
1638
+ showProgress(true, 0, allWorkerData.length);
1639
+
1640
+ for (let i = 0; i < allWorkerData.length; i++) {
1641
+ try {
1642
+ showProgress(true, i + 1, allWorkerData.length);
1643
+ displayWorkerData(i);
1644
+ await downloadPDF(allWorkerData[i], `work_permit_${i + 1}`);
1645
+ await new Promise(resolve => setTimeout(resolve, 800));
1646
+ } catch (error) {
1647
+ console.error(`Error downloading PDF for worker at index ${i}:`, error);
1648
+ continue;
1649
+ }
1650
+ }
1651
+
1652
+ showProgress(false);
1653
+ setButtonsDisabled(false);
1654
+ alert(`ดาวน์โหลดใบอนุญาตทำงานเรียบร้อยแล้ว ${allWorkerData.length} รายการ`);
1655
+
1656
+ if (currentIndex >= 0 && currentIndex < allWorkerData.length) {
1657
+ displayWorkerData(currentIndex);
1658
+ } else if (allWorkerData.length > 0) {
1659
+ displayWorkerData(0);
1660
+ }
1661
+ }
1662
+
1663
+ window.onload = async () => {
1664
+ try {
1665
+ await loadWorkerData();
1666
+ randomizeBothOverlays();
1667
+ } catch (e) {
1668
+ console.error('Error initializing application:', e);
1669
+ }
1670
+ };
1671
+ </script>
1672
+ </body>
1673
+ </html>
pdf.html ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="th" xml:lang="th">
3
+ <head>
4
+ <title>PDF Download</title>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
6
+ <script src="https://cdn.tailwindcss.com"></script>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
8
+ <style type="text/css">
9
+ @font-face {
10
+ font-family: 'THSarabunNew';
11
+ src: url('fonts/THSarabunNew.ttf') format('truetype');
12
+ font-weight: normal;
13
+ font-style: normal;
14
+ unicode-range: U+0E00-0E7F, U+0020-007F, U+00A0-00FF;
15
+ font-display: swap;
16
+ }
17
+
18
+ @font-face {
19
+ font-family: 'THSarabunNew';
20
+ src: url('fonts/THSarabunNew Bold.ttf') format('truetype');
21
+ font-weight: bold;
22
+ font-style: normal;
23
+ unicode-range: U+0E00-0E7F, U+0020-007F, U+00A0-00FF;
24
+ font-display: swap;
25
+ }
26
+
27
+ * {
28
+ font-family: 'THSarabunNew', sans-serif !important;
29
+ }
30
+
31
+ .ft00 { font-size: 19px; color: #000000; }
32
+ .ft01 { font-size: 25px; color: #000000; }
33
+ .ft02 { font-size: 22px; color: #000000; }
34
+ .ft03 { font-size: 22px; color: #000000; }
35
+ .ft06 { font-size: 15px; line-height: 18px; color: #000000; }
36
+
37
+
38
+ .loading-overlay {
39
+ position: fixed; top: 0; left: 0; width: 100%; height: 100%;
40
+ background: rgba(0, 0, 0, 0.8); display: flex; flex-direction: column;
41
+ align-items: center; justify-content: center; z-index: 9999;
42
+ color: white; font-family: 'THSarabunPsk', sans-serif;
43
+ }
44
+ .spinner {
45
+ border: 4px solid #f3f3f3; border-top: 4px solid #3498db;
46
+ border-radius: 50%; width: 50px; height: 50px;
47
+ animation: spin 1s linear infinite; margin-bottom: 20px;
48
+ }
49
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
50
+ </style>
51
+ <script>
52
+ let allWorkerData = [];
53
+
54
+ function setImageSrc(imgElementId, src, defaultSrc, workerName, type) {
55
+ const imgElement = document.getElementById(imgElementId);
56
+ if (!imgElement) return;
57
+ if (src && src.trim() !== '') {
58
+ imgElement.crossOrigin = "anonymous";
59
+ const testImage = new Image();
60
+ testImage.crossOrigin = "anonymous";
61
+ testImage.onload = () => imgElement.src = src;
62
+ testImage.onerror = () => imgElement.src = defaultSrc;
63
+ testImage.src = src;
64
+ } else {
65
+ imgElement.src = defaultSrc;
66
+ }
67
+ }
68
+
69
+ function getCurrentTimestamp() {
70
+ const now = new Date();
71
+ return now.toLocaleString('th-TH', {
72
+ hour: '2-digit', minute: '2-digit', hour12: false
73
+ }).replace(',', ' น.');
74
+ }
75
+
76
+ function displayWorkerData(worker) {
77
+ // Receipt Data
78
+ document.getElementById('requestNumberReceipt').innerHTML = worker.requestNumber || 'N/A';
79
+ document.getElementById('receiptNumberReceipt').innerHTML = worker.receiptNumber || 'N/A';
80
+ document.getElementById('paymentNumberReceipt').innerHTML = worker.paymentNumber || 'N/A';
81
+ document.getElementById('payerNameReceipt').innerHTML = worker.englishName || worker.thaiName || 'N/A';
82
+ document.getElementById('nationalityReceipt').innerHTML = worker.nationality || 'N/A';
83
+ document.getElementById('alienReferenceReceipt').innerHTML = worker.alienReferenceNumber || 'N/A';
84
+ document.getElementById('personalIDReceipt').innerHTML = worker.personalID || 'N/A';
85
+
86
+ // QR Code for receipt
87
+ const currentUrl = window.location.href;
88
+ const baseUrl = currentUrl.substring(0, currentUrl.lastIndexOf('/') + 1);
89
+ const currentDomain = window.location.origin;
90
+ const receiptUrl = `${currentDomain}/pdf.html?id=${encodeURIComponent(worker.requestNumber || '')}`;
91
+ const receiptQrCodeApiUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&ecc=H&data=${encodeURIComponent(receiptUrl)}`;
92
+ const defaultQrCodeSrc = 'https://via.placeholder.com/90?text=No+QR';
93
+ setImageSrc('receiptQrCode', receiptQrCodeApiUrl, defaultQrCodeSrc, worker.englishName, 'receipt QR code');
94
+
95
+ // Timestamp
96
+ const timestamp = getCurrentTimestamp();
97
+ const receiptTimestamp = `เอกสารอิเล็กทรอนิกส์ฉบับนี้ถูกสร้างจากระบบอนุญาตทำงานคนต่างด้าวที่มีสถานะการทำงานไม่ถูกต้องตามกฎหมาย ตามมติคณะรัฐมนตรีเมื่อวันที่ 24 กันยายน 2567<br/>โดยกรมการจัดหางาน กระทรวงแรงงาน<br/>พิมพ์เอกสาร วันที่ 10/04/2568 ${timestamp} น.`;
98
+ document.getElementById('receiptTimestamp').innerHTML = receiptTimestamp ;
99
+ }
100
+
101
+ async function downloadPage2AsPDF(worker) {
102
+ try {
103
+ const page2Element = document.getElementById('page2-div');
104
+
105
+ // Wait for images to load
106
+ const images = document.querySelectorAll('img');
107
+ await Promise.all(Array.from(images).map(img => {
108
+ return new Promise(resolve => {
109
+ if (img.complete) resolve();
110
+ else {
111
+ img.onload = resolve;
112
+ img.onerror = resolve;
113
+ setTimeout(resolve, 2000);
114
+ }
115
+ });
116
+ }));
117
+
118
+ const opt = {
119
+ margin: [0, 0, 0, 0],
120
+ filename: 'mpdf.pdf', // Fixed filename for QR code access
121
+ image: { type: 'jpeg', quality: 0.98 },
122
+ html2canvas: { scale: 3, useCORS: true, width: 892, height: 1261, scrollX: 0, scrollY: 0 },
123
+ jsPDF: { unit: 'px', format: [892, 1261], orientation: 'portrait', compress: true }
124
+ };
125
+
126
+ // Clone page 2 only
127
+ const page2Clone = page2Element.cloneNode(true);
128
+ const container = document.createElement('div');
129
+ container.style.width = '892px';
130
+ container.style.height = '1261px';
131
+ container.appendChild(page2Clone);
132
+ document.body.appendChild(container);
133
+
134
+ await html2pdf().set(opt).from(container).save();
135
+ document.body.removeChild(container);
136
+
137
+ const loadingOverlay = document.getElementById('loadingOverlay');
138
+ if (loadingOverlay) loadingOverlay.style.display = 'none';
139
+
140
+ } catch (error) {
141
+ console.error('Error generating PDF:', error);
142
+ showError('เกิดข้อผิดพลาดในการสร้าง PDF กรุณาลองใหม่อีกครั้ง');
143
+ }
144
+ }
145
+
146
+ function showError(message) {
147
+ const loadingOverlay = document.getElementById('loadingOverlay');
148
+ if (loadingOverlay) {
149
+ loadingOverlay.innerHTML = `
150
+ <div style="text-align: center;">
151
+ <h2 style="color: #ff6b6b; font-size: 24px; margin-bottom: 20px;">เกิดข้อผิดพลาด</h2>
152
+ <p style="font-size: 18px; margin-bottom: 20px;">${message}</p>
153
+ <button onclick="window.close()" style="padding: 10px 20px; background: #3498db; color: white; border: none; border-radius: 5px; cursor: pointer;">ปิดหน้าต่าง</button>
154
+ </div>
155
+ `;
156
+ }
157
+ }
158
+
159
+ function loadWorkerData() {
160
+ const urlParams = new URLSearchParams(window.location.search);
161
+ const requestNumberFromUrl = urlParams.get('id');
162
+
163
+ fetch('combined-data.json')
164
+ .then(response => {
165
+ if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
166
+ return response.json();
167
+ })
168
+ .then(data => {
169
+ allWorkerData = data;
170
+
171
+ if (requestNumberFromUrl) {
172
+ const worker = allWorkerData.find(w => w.requestNumber === requestNumberFromUrl);
173
+
174
+ if (worker) {
175
+ displayWorkerData(worker);
176
+ setTimeout(() => { downloadPage2AsPDF(worker); }, 2000);
177
+ } else {
178
+ showError(`ไม่พบข้อมูลสำหรับเลขที่คำขอ: ${requestNumberFromUrl}`);
179
+ }
180
+ } else {
181
+ showError('ไม่พบเลขที่คำขอในลิงก์');
182
+ }
183
+ })
184
+ .catch(error => {
185
+ console.error('Error loading worker data:', error);
186
+ showError(`เกิดข้อผิดพลาดในการโหลดข้อมูล: ${error.message}`);
187
+ });
188
+ }
189
+
190
+ window.onload = function() { loadWorkerData(); };
191
+ </script>
192
+ </head>
193
+ <body class="bg-gray-500">
194
+ <div id="loadingOverlay" class="loading-overlay">
195
+ <div class="spinner"></div>
196
+ <h2 style="font-size: 24px; margin-bottom: 10px;">กำลังเตรียม PDF...</h2>
197
+ <p style="font-size: 18px;">กรุณารอสักครู่ ระบบกำลังสร้างเอกสารให้คุณ</p>
198
+ </div>
199
+
200
+ <!-- Page 2: Receipt (เหมือนต้นฉบับ 100%) -->
201
+ <div id="page2-div" class="page-div relative w-[892px] h-[1261px] font-['THSarabunPSK'] text-black mx-auto bg-white">
202
+ <img width="892" height="1261" src="bg2.svg" alt="receipt background image"/>
203
+ <!-- Receipt QR Code -->
204
+ <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"/>
205
+ <!-- Department Info -->
206
+ <p style="position:absolute;top:147px;left:86px;white-space:nowrap" class="ft00">กรมการจัดหางาน</p>
207
+ <p style="position:absolute;top:170px;left:88px;white-space:nowrap" class="ft00">กระทรวงแรงงาน</p>
208
+ <!-- Receipt Header -->
209
+ <p style="position:absolute;top:90px;left:397px;white-space:nowrap" class="ft01"><b>ใบเสร็จรับเงิน</b></p>
210
+ <p style="position:absolute;top:120px;left:418px;white-space:nowrap" class="ft01"><b>ต้นฉบับ</b></p>
211
+ <!-- Receipt Number -->
212
+ <p style="position:absolute;top:60px;left:598px;white-space:nowrap" class="ft00">เลขที่</p>
213
+ <p style="position:absolute;top:60px;left:640px;white-space:nowrap" class="ft00" id="receiptNumberReceipt">xxxxxxx</p>
214
+ <!-- Office and Date -->
215
+ <p style="position:absolute;top:149px;left:582px;white-space:nowrap" class="ft00">ที่ทำการ&#160;&#160; สำนักบริหารแรงงานต่างด้าว</p>
216
+ <p style="position:absolute;top:188px;left:602px;white-space:nowrap" class="ft00">วันที่&#160;&#160; 19 มีนาคม 2568</p>
217
+ <!-- Payment Number -->
218
+ <p style="position:absolute;top:227px;left:540px;white-space:nowrap" class="ft00">เลขที่ใบชำระเงิน&#160;&#160;</p>
219
+ <p style="position:absolute;top:227px;left:640px;white-space:nowrap" class="ft00" id="paymentNumberReceipt">IV680329/002308</p>
220
+ <!-- Payer Information -->
221
+ <p style="position:absolute;top:271px;left:60px;white-space:nowrap" class="ft00">เลขรับคำขอที่</p>
222
+ <p style="position:absolute;top:271px;left:180px;white-space:nowrap" class="ft00" id="requestNumberReceipt">xxxxxxx</p>
223
+ <p style="position:absolute;top:310px;left:60px;white-space:nowrap" class="ft00">ชื่อผู้ชำระเงิน</p>
224
+ <p style="position:absolute;top:310px;left:180px;white-space:nowrap" class="ft00" id="payerNameReceipt">xxxxxxxxxxxxx</p>
225
+ <p style="position:absolute;top:310px;left:471px;white-space:nowrap" class="ft00">สัญชาติ</p>
226
+ <p style="position:absolute;top:310px;left:520px;white-space:nowrap" class="ft00" id="nationalityReceipt">เมียนมา</p>
227
+ <p style="position:absolute;top:354px;left:60px;white-space:nowrap" class="ft00">เลขอ้างอิงคนต่างด้าว</p>
228
+ <p style="position:absolute;top:354px;left:180px;white-space:nowrap" class="ft00" id="alienReferenceReceipt">xxxxxxxxxxxxx</p>
229
+ <p style="position:absolute;top:354px;left:432px;white-space:nowrap" class="ft00">หมายเลขประจำตัวคนต่างด้าว</p>
230
+ <p style="position:absolute;top:354px;left:640px;white-space:nowrap" class="ft00" id="personalIDReceipt">xxxxxxxxxxxxx</p>
231
+ <!-- Employer Information -->
232
+ <p style="position:absolute;top:399px;left:60px;white-space:nowrap" class="ft00">ชื่อนายจ้าง / สถานประกอบการ&#160;&#160; บริษัท บาน กง เอ็นจิเนียริ่ง จำกัด</p>
233
+ <p style="position:absolute;top:438px;left:60px;white-space:nowrap" class="ft00">เลขประจำตัวนายจ้าง</p>
234
+ <p style="position:absolute;top:437px;left:233px;white-space:nowrap" class="ft00">&#160; 0415567000061</p>
235
+ <!-- Items Header -->
236
+ <p style="position:absolute;top:526px;left:345px;white-space:nowrap" class="ft02"><b>รายการ</b></p>
237
+ <p style="position:absolute;top:526px;left:688px;white-space:nowrap" class="ft02"><b>จำนวนเงิน</b></p>
238
+ <!-- Fee Items -->
239
+ <p style="position:absolute;top:572px;left:118px;white-space:nowrap" class="ft03">1. ค่าธรรมเนียมในการยื่นคำขอ ฉบับละ 100 บาท</p>
240
+ <p style="position:absolute;top:572px;left:736px;white-space:nowrap" class="ft03">100.00</p>
241
+ <p style="position:absolute;top:616px;left:118px;white-space:nowrap" class="ft03">2. ค่าธรรมเนียมใบอนุญาตทำงาน</p>
242
+ <p style="position:absolute;top:616px;left:736px;white-space:nowrap" class="ft03">900.00</p>
243
+ <!-- Empty rows -->
244
+ <p style="position:absolute;top:694px;left:97px;white-space:nowrap" class="ft03">&#160;</p>
245
+ <p style="position:absolute;top:694px;left:648px;white-space:nowrap" class="ft03">&#160;</p>
246
+ <!-- Total -->
247
+ <p style="position:absolute;top:772px;left:174px;white-space:nowrap" class="ft02"><b>รวมเป็นเงินทั้งสิ้น (บาท)</b></p>
248
+ <p style="position:absolute;top:799px;left:188px;white-space:nowrap" class="ft02"><b>( หนึ่งพันบาทถ้วน )</b></p>
249
+ <p style="position:absolute;top:786px;left:385px;white-space:nowrap" class="ft03">&#160;</p>
250
+ <p style="position:absolute;top:774px;left:722px;white-space:nowrap" class="ft02"><b>1,000.00</b></p>
251
+ <!-- Receipt Confirmation -->
252
+ <p style="position:absolute;top:894px;left:94px;white-space:nowrap" class="ft00">ได้รับเงินไว้เป็นการถูกต้องแล้ว</p>
253
+ <!-- Signature -->
254
+ <p style="position:absolute;top:977px;left:481px;white-space:nowrap" class="ft00">(ลงชื่อ)</p>
255
+ <p style="position:absolute;top:977px;left:564px;white-space:nowrap" class="ft00">นางสาวอารีวรรณ โพธิ์นิ่มแดง</p>
256
+ <p style="position:absolute;top:977px;left:762px;white-space:nowrap" class="ft00">(ผู้รับเงิน)</p>
257
+ <!-- Position -->
258
+ <p style="position:absolute;top:1017px;left:473px;white-space:nowrap" class="ft00">ตำแหน่ง</p>
259
+ <p style="position:absolute;top:1016px;left:562px;white-space:nowrap" class="ft00">นักวิชาการแรงงานชำนาญการ</p>
260
+ <!-- Receipt Timestamp -->
261
+ <p style="position:absolute;top:1133px;left:55px;white-space:nowrap" class="ft06" id="receiptTimestamp">เอกสารอิเล็กทรอนิกส์ฉบับนี้ถูกสร้างจากระบบอนุญาตทำงานคนต่างด้าวที่มีสถานะการทำงานไม่ถูกต้องตามกฎหมาย ตามมติคณะรัฐมนตรีเมื่อวันที่ 24 กันยายน 2567<br/>โดยกรมการจัดหางาน กระทรวงแรงงาน</p>
262
+ </div>
263
+ </body>
264
+ </html>
worker.html ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <!DOCTYPE html>
3
+ <html lang="th">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title></title>
8
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;600;700&display=swap" rel="stylesheet">
9
+ <style>
10
+ body {
11
+ font-family: 'Roboto', sans-serif;
12
+ margin: 0;
13
+ padding: 0;
14
+ background-color: white;
15
+ line-height: 1.5;
16
+ font-size: 13px;
17
+ position: relative;
18
+ }
19
+ .container {
20
+ max-width: 892px;
21
+ margin: 0 auto;
22
+ }
23
+ .header {
24
+ display: flex;
25
+ background-color: white;
26
+ padding: 0;
27
+ justify-content: flex-start;
28
+ }
29
+ .header-image {
30
+ width: 50%;
31
+ max-width: 350px;
32
+ height: auto;
33
+ padding: 5px 0;
34
+ }
35
+ .blue-bar {
36
+ background-color: #55a2cc;
37
+ height: 25px;
38
+ width: 100%;
39
+ }
40
+ .title {
41
+ text-align: center;
42
+ padding: 10px 15px;
43
+ font-size: 13px;
44
+ font-weight: bold;
45
+ color: #284f7c;
46
+ line-height: 1.5;
47
+ }
48
+ .profile-image {
49
+ display: block;
50
+ width: 128px;
51
+ height: 160px;
52
+ object-fit: cover;
53
+ margin: 0 auto 15px;
54
+ border: 1px solid #ddd;
55
+ background-color: #f9f9f9;
56
+ }
57
+ .info-table {
58
+ width: 100%;
59
+ border-collapse: collapse;
60
+ table-layout: fixed;
61
+ }
62
+ .info-table td {
63
+ padding: 6px 5px;
64
+ vertical-align: top;
65
+ border-bottom: 1px solid #f0f0f0;
66
+ word-wrap: break-word;
67
+ font-size: 12px;
68
+ }
69
+ .info-table td:first-child {
70
+ width: 250px;
71
+ color: #284f7c;
72
+ font-weight: bold;
73
+ }
74
+ .info-table td:last-child {
75
+ color: #000000;
76
+ }
77
+ .section-header {
78
+ font-weight: bold;
79
+ color: #000000;
80
+ margin-top: 20px;
81
+ margin-bottom: 8px;
82
+ font-size: 12px;
83
+ }
84
+ .footer {
85
+ background-color: #2f3133;
86
+ color: white;
87
+ text-align: center;
88
+ padding: 12px 0;
89
+ font-size: 7px;
90
+ margin-top: 24px;
91
+ line-height: 1.6;
92
+ }
93
+ .navigation {
94
+ position: fixed;
95
+ top: 95%;
96
+ left: 80%;
97
+ transform: translate(-50%, -50%);
98
+ display: flex;
99
+ align-items: center;
100
+ gap: 20px;
101
+ z-index: 1000;
102
+ pointer-events: none;
103
+ }
104
+ .nav-button {
105
+ background: rgba(0, 0, 0, 0);
106
+ border: none;
107
+ width: 60px;
108
+ height: 60px;
109
+ border-radius: 50%;
110
+ cursor: pointer;
111
+ font-size: 24px;
112
+ color: rgba(0, 0, 0, 0);
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: center;
116
+ transition: all 0.3s ease;
117
+ pointer-events: auto;
118
+ }
119
+ .nav-button:hover {
120
+ background: rgba(0, 0, 0, 0);
121
+ color: rgba(0, 0, 0, 0);
122
+ }
123
+ .nav-button:disabled {
124
+ opacity: 0.3;
125
+ cursor: not-allowed;
126
+ }
127
+ .position-indicator {
128
+ background: rgba(0, 0, 0, 0);
129
+ padding: 8px 16px;
130
+ border-radius: 20px;
131
+ font-size: 12px;
132
+ color: rgba(0, 0, 0, 0);
133
+ pointer-events: auto;
134
+ white-space: nowrap;
135
+ }
136
+ </style>
137
+ </head>
138
+ <body>
139
+ <div class="container" id="profile-container">
140
+ <div class="header">
141
+ <img src="https://i.postimg.cc/kGk7nBX9/1756976041773.png" alt="logo" class="header-image">
142
+ </div>
143
+
144
+ <div class="blue-bar"></div>
145
+
146
+ <div class="title">
147
+ ทะเบียนใบอนุญาตทำงานของคนต่างด้าว<br>
148
+ ตามมติคณะรัฐมนตรี เมื่อวันที่ 24 กันยายน 2567 (บต.50 อ.6)
149
+ </div>
150
+
151
+ <img class="profile-image" id="worker-image" src="" alt="">
152
+
153
+ <table class="info-table">
154
+ <tr>
155
+ <td>เลขคำขอ</td>
156
+ <td id="worker-id"></td>
157
+ </tr>
158
+ <tr>
159
+ <td>Version no</td>
160
+ <td>01</td>
161
+ </tr>
162
+ <tr>
163
+ <td>แบบคำขอ</td>
164
+ <td>บต.50 อ.6</td>
165
+ </tr>
166
+ </table>
167
+
168
+ <div class="section-header">รายละเอียดแรงงานต่างด้าวรายบุคคล</div>
169
+
170
+ <table class="info-table">
171
+ <tr>
172
+ <td>เลขอ้างอิงคนต่างด้าว</td>
173
+ <td id="worker-alien-ref"></td>
174
+ </tr>
175
+ <tr>
176
+ <td>เลขประจำตัวคนต่างด้าว</td>
177
+ <td id="worker-person-id"></td>
178
+ </tr>
179
+ <tr>
180
+ <td>ใบอนุญาตเลขที่</td>
181
+ <td id="worker-license"></td>
182
+ </tr>
183
+ <tr>
184
+ <td>ออกให้ ณ จังหวัด</td>
185
+ <td>สำนักงานจัดหางานจังหวัดระยอง</td>
186
+ </tr>
187
+ <tr>
188
+ <td>ชื่อภาษาไทย</td>
189
+ <td id="worker-name-th"></td>
190
+ </tr>
191
+ <tr>
192
+ <td>ชื่อภาษาอังกฤษ</td>
193
+ <td id="worker-name-en"></td>
194
+ </tr>
195
+ <tr>
196
+ <td>อายุ (ปี)</td>
197
+ <td id="worker-age"></td>
198
+ </tr>
199
+ <tr>
200
+ <td>สัญชาติ</td>
201
+ <td id="worker-nationality"></td>
202
+ </tr>
203
+ <tr>
204
+ <td>เลขที่หนังสือเดินทาง</td>
205
+ <td>-</td>
206
+ </tr>
207
+ <tr>
208
+ <td>สถานที่ทำงาน</td>
209
+ <td>36/6 หมู่ที่ 8 ตำบลมาบข่า อำเภอนิคมพัฒนา จังหวัดระยอง 21180</td>
210
+ </tr>
211
+ <tr>
212
+ <td>ชื่อนายจ้าง หรือ สθานที่ทำงาน</td>
213
+ <td>บริษัท บาน กง เอ็นจิเนียริ่ง จำกัด</td>
214
+ </tr>
215
+ <tr>
216
+ <td>ที่ตั้งสำนักงาน</td>
217
+ <td>102 หมู่ที่ 8 ถนนอุดรธานี-ขอนแก่น ตำบลโนนสูง อำเภอเมืองอุดรธานี จังหวัดอุดรธานี 41000</td>
218
+ </tr>
219
+ <tr>
220
+ <td>ลั���ษณะงาน</td>
221
+ <td>กรรมกร (กิจการก่อสร้าง)</td>
222
+ </tr>
223
+ <tr>
224
+ <td>อนุญาตให้ทำงานถึงวันที่</td>
225
+ <td>31/03/2026</td>
226
+ </tr>
227
+ </table>
228
+
229
+ <div class="footer">
230
+ สำนักบริหารแรงงานต่างด้าว กรมการจัดหางาน ถนนมิตรไมตรี แขวงดินแดง เขตดินแดง กรุงเทพมหานคร 10400<br>
231
+ ช่องทางการติดต่อกรมการจัดหางาน หมายเลข 1506 กด 2<br>
232
+ Copyright © Department of Employment. All rights reserved.<br>
233
+ displayed in 0.061 seconds, used 16.34/19.7 MB (server load 10.46%)
234
+ </div>
235
+ </div>
236
+
237
+ <div class="navigation">
238
+ <button class="nav-button" id="prev-btn" onclick="showPreviousWorker()">‹</button>
239
+ <div class="position-indicator" id="position-indicator">1 / 1</div>
240
+ <button class="nav-button" id="next-btn" onclick="showNextWorker()">›</button>
241
+ </div>
242
+
243
+ <script>
244
+ let allWorkerData = []; // Changed variable name
245
+ let currentIndex = 0;
246
+
247
+ // Sample data as fallback
248
+ const sampleData = [
249
+ {
250
+ "requestNumber": "WP-68-1011897",
251
+ "englishName": "MRS.AYE SANDRA HTWE",
252
+ "profileImage": "bg1.svg",
253
+ "thaiName": "นาง เอ ซานดรา ตเว",
254
+ "age": "47",
255
+ "alienReferenceNumber": "2492102076039",
256
+ "personalID": "6682190040778",
257
+ "nationality": "เมียนมา",
258
+ "workPermitNumber": "2100689040199",
259
+ "birthDate": "25/02/1978",
260
+ "receiptNumber": "2100680035190",
261
+ "paymentNumber": "IV680329/002308"
262
+ }
263
+ ];
264
+
265
+ async function loadWorkersData() {
266
+ try {
267
+ const response = await fetch('combined-data.json'); // Load from the combined file
268
+ if (response.ok) {
269
+ allWorkerData = await response.json();
270
+ } else {
271
+ throw new Error('Failed to fetch combined-data.json');
272
+ }
273
+ } catch (error) {
274
+ console.warn('Using sample data as fallback due to error:', error.message);
275
+ allWorkerData = sampleData;
276
+ }
277
+
278
+ if (!Array.isArray(allWorkerData) || allWorkerData.length === 0) {
279
+ console.warn('Loaded data is not an array or is empty, using sample data.');
280
+ allWorkerData = sampleData;
281
+ }
282
+
283
+ const urlParams = new URLSearchParams(window.location.search);
284
+ const workerIdFromUrl = urlParams.get('id');
285
+
286
+ if (workerIdFromUrl) {
287
+ const workerIndex = allWorkerData.findIndex(w => w.requestNumber === workerIdFromUrl);
288
+ if (workerIndex !== -1) {
289
+ currentIndex = workerIndex;
290
+ } else {
291
+ console.warn(`Worker with ID "${workerIdFromUrl}" not found in combined data. Displaying first worker.`);
292
+ // Optionally, inform the user that the specific ID was not found.
293
+ }
294
+ }
295
+
296
+ displayCurrentWorker();
297
+ updateNavigationButtons();
298
+ }
299
+
300
+ function displayCurrentWorker() {
301
+ if (allWorkerData.length === 0) {
302
+ console.error("No worker data available to display.");
303
+ // Display a message to the user in the UI
304
+ document.getElementById('profile-container').innerHTML = '<p style="text-align:center; padding: 20px;">No worker data found.</p>';
305
+ return;
306
+ }
307
+
308
+ const worker = allWorkerData[currentIndex];
309
+
310
+ document.getElementById('worker-image').src = worker.profileImage || "https://via.placeholder.com/128x160?text=No+Image";
311
+ document.getElementById('worker-image').alt = worker.thaiName || worker.englishName || "Worker Image";
312
+ document.getElementById('worker-id').textContent = worker.requestNumber || "N/A";
313
+ document.getElementById('worker-alien-ref').textContent = worker.alienReferenceNumber || "N/A";
314
+ document.getElementById('worker-person-id').textContent = worker.personalID || "N/A";
315
+ document.getElementById('worker-license').textContent = worker.workPermitNumber || "N/A";
316
+ document.getElementById('worker-name-th').textContent = worker.thaiName || "N/A";
317
+ document.getElementById('worker-name-en').textContent = worker.englishName || "N/A";
318
+ document.getElementById('worker-age').textContent = worker.age || "N/A";
319
+ document.getElementById('worker-nationality').textContent = worker.nationality || "N/A";
320
+ // Note: 'birthDate' is in combined-data.json but not displayed in current worker.html layout.
321
+
322
+ document.getElementById('position-indicator').textContent = `${currentIndex + 1} / ${allWorkerData.length}`;
323
+
324
+ const newUrl = new URL(window.location.href);
325
+ newUrl.searchParams.set('id', worker.requestNumber || '');
326
+ window.history.replaceState({}, '', newUrl);
327
+ }
328
+
329
+ function updateNavigationButtons() {
330
+ const prevBtn = document.getElementById('prev-btn');
331
+ const nextBtn = document.getElementById('next-btn');
332
+
333
+ if (!prevBtn || !nextBtn) return;
334
+
335
+ prevBtn.disabled = currentIndex === 0;
336
+ nextBtn.disabled = currentIndex >= allWorkerData.length - 1;
337
+ }
338
+
339
+ function showPreviousWorker() {
340
+ if (currentIndex > 0) {
341
+ currentIndex--;
342
+ displayCurrentWorker();
343
+ updateNavigationButtons();
344
+ }
345
+ }
346
+
347
+ function showNextWorker() {
348
+ if (currentIndex < allWorkerData.length - 1) {
349
+ currentIndex++;
350
+ displayCurrentWorker();
351
+ updateNavigationButtons();
352
+ }
353
+ }
354
+
355
+ document.addEventListener('keydown', function(e) {
356
+ if (e.key === 'ArrowLeft') {
357
+ showPreviousWorker();
358
+ } else if (e.key === 'ArrowRight') {
359
+ showNextWorker();
360
+ }
361
+ });
362
+
363
+ document.addEventListener('DOMContentLoaded', loadWorkersData);
364
+ </script>
365
+ </body>
366
+ </html>