protae5544 commited on
Commit
da07a44
·
verified ·
1 Parent(s): 861ddc9

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +1670 -19
index.html CHANGED
@@ -1,19 +1,1670 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="th" data-theme="light">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
6
+ <meta name="description" content="ระบบจัดการเอกสาร PDF พร้อมการกรอกข้อมูล JSON และ QR Code">
7
+ <title>ระบบจัดการเอกสาร PDF Professional</title>
8
+
9
+ <!-- Fonts -->
10
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sarabun:wght@300;400;500;600;700&display=swap">
11
+ <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
12
+
13
+ <!-- Libraries -->
14
+ <script src="https://cdn.tailwindcss.com"></script>
15
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
16
+ <script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script>
17
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
18
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
19
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/qrcode-generator/1.4.4/qrcode.min.js"></script>
20
+
21
+ <style>
22
+ /* Font Configuration */
23
+ @font-face {
24
+ font-family: 'THSarabunPsk';
25
+ src: url('https://oldqifkvaagtseibueaf.supabase.co/storage/v1/object/public/zzoo/ozz/ss-thsbn.woff2') format('woff2');
26
+ font-weight: normal;
27
+ unicode-range: U+0E00-0E7F, U+0020-007F;
28
+ font-display: swap;
29
+ }
30
+
31
+ @font-face {
32
+ font-family: 'THSarabunPsk';
33
+ src: url('https://oldqifkvaagtseibueaf.supabase.co/storage/v1/object/public/zzoo/ozz/ss-thsbn-bold.woff2') format('woff2');
34
+ font-weight: bold;
35
+ unicode-range: U+0E00-0E7F, U+0020-007F;
36
+ font-display: swap;
37
+ }
38
+
39
+ /* CSS Variables */
40
+ :root {
41
+ --header-height: 60px;
42
+ --footer-height: 50px;
43
+ --sidebar-width: 320px;
44
+ --bg-primary: #f8fafc;
45
+ --bg-secondary: #ffffff;
46
+ --bg-tertiary: #f1f5f9;
47
+ --text-primary: #0f172a;
48
+ --text-secondary: #475569;
49
+ --text-tertiary: #64748b;
50
+ --border-primary: #e2e8f0;
51
+ --border-secondary: #cbd5e1;
52
+ --accent-primary: #3b82f6;
53
+ --accent-secondary: #10b981;
54
+ --accent-tertiary: #8b5cf6;
55
+ --primary-color: #7c2a4a;
56
+ --canvas-bg: #1e293b;
57
+ --hud-bg: rgba(30, 41, 59, 0.95);
58
+ --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
59
+ }
60
+
61
+ [data-theme="dark"] {
62
+ --bg-primary: #0f172a;
63
+ --bg-secondary: #1e293b;
64
+ --bg-tertiary: #334155;
65
+ --text-primary: #f8fafc;
66
+ --text-secondary: #cbd5e1;
67
+ --text-tertiary: #94a3b8;
68
+ --border-primary: #334155;
69
+ --border-secondary: #475569;
70
+ --canvas-bg: #0f172a;
71
+ }
72
+
73
+ * {
74
+ margin: 0;
75
+ padding: 0;
76
+ box-sizing: border-box;
77
+ }
78
+
79
+ html, body {
80
+ height: 100%;
81
+ overflow: hidden;
82
+ font-family: 'Sarabun', 'THSarabunPsk', sans-serif;
83
+ background: var(--canvas-bg);
84
+ color: var(--text-primary);
85
+ }
86
+
87
+ /* App Layout */
88
+ .app-container {
89
+ display: flex;
90
+ flex-direction: column;
91
+ height: 100vh;
92
+ width: 100vw;
93
+ }
94
+
95
+ /* Header */
96
+ header {
97
+ height: var(--header-height);
98
+ background: var(--bg-secondary);
99
+ border-bottom: 1px solid var(--border-primary);
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: space-between;
103
+ padding: 0 1rem;
104
+ z-index: 100;
105
+ flex-shrink: 0;
106
+ }
107
+
108
+ .header-title {
109
+ font-size: 1.25rem;
110
+ font-weight: 600;
111
+ color: var(--text-primary);
112
+ display: flex;
113
+ align-items: center;
114
+ gap: 0.5rem;
115
+ }
116
+
117
+ .header-actions {
118
+ display: flex;
119
+ gap: 0.5rem;
120
+ }
121
+
122
+ /* Main Content Area */
123
+ .main-container {
124
+ flex: 1;
125
+ display: flex;
126
+ overflow: hidden;
127
+ }
128
+
129
+ /* Sidebar */
130
+ .sidebar {
131
+ width: var(--sidebar-width);
132
+ background: var(--bg-secondary);
133
+ border-right: 1px solid var(--border-primary);
134
+ display: flex;
135
+ flex-direction: column;
136
+ overflow: hidden;
137
+ flex-shrink: 0;
138
+ transition: transform 0.3s ease;
139
+ }
140
+
141
+ .sidebar-header {
142
+ padding: 1rem;
143
+ border-bottom: 1px solid var(--border-primary);
144
+ display: flex;
145
+ align-items: center;
146
+ justify-content: space-between;
147
+ }
148
+
149
+ .sidebar-content {
150
+ flex: 1;
151
+ overflow-y: auto;
152
+ padding: 1rem;
153
+ }
154
+
155
+ .sidebar-section {
156
+ margin-bottom: 1.5rem;
157
+ }
158
+
159
+ .sidebar-section h3 {
160
+ font-size: 0.875rem;
161
+ font-weight: 600;
162
+ color: var(--text-secondary);
163
+ margin-bottom: 0.75rem;
164
+ padding-left: 0.5rem;
165
+ border-left: 3px solid var(--accent-primary);
166
+ }
167
+
168
+ /* Viewer Area */
169
+ .viewer-container {
170
+ flex: 1;
171
+ display: flex;
172
+ flex-direction: column;
173
+ overflow: hidden;
174
+ background: var(--canvas-bg);
175
+ }
176
+
177
+ .viewer-tabs {
178
+ display: flex;
179
+ background: var(--bg-tertiary);
180
+ border-bottom: 1px solid var(--border-primary);
181
+ padding: 0.5rem 1rem 0;
182
+ }
183
+
184
+ .tab-btn {
185
+ padding: 0.75rem 1.5rem;
186
+ background: transparent;
187
+ border: none;
188
+ border-radius: 0.5rem 0.5rem 0 0;
189
+ cursor: pointer;
190
+ font-size: 0.875rem;
191
+ font-weight: 500;
192
+ color: var(--text-tertiary);
193
+ transition: var(--transition);
194
+ }
195
+
196
+ .tab-btn.active {
197
+ background: var(--bg-secondary);
198
+ color: var(--accent-primary);
199
+ }
200
+
201
+ .viewer-content {
202
+ flex: 1;
203
+ overflow: auto;
204
+ padding: 1rem;
205
+ }
206
+
207
+ /* Template View */
208
+ .template-viewer {
209
+ display: flex;
210
+ flex-direction: column;
211
+ align-items: center;
212
+ gap: 2rem;
213
+ padding: 2rem;
214
+ }
215
+
216
+ .page {
217
+ position: relative;
218
+ width: 892px;
219
+ height: 1261px;
220
+ background: var(--bg-secondary);
221
+ border-radius: 0.5rem;
222
+ overflow: hidden;
223
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
224
+ flex-shrink: 0;
225
+ }
226
+
227
+ .page .bg {
228
+ position: absolute;
229
+ inset: 0;
230
+ width: 100%;
231
+ height: 100%;
232
+ object-fit: cover;
233
+ z-index: 0;
234
+ }
235
+
236
+ .field {
237
+ position: absolute;
238
+ white-space: nowrap;
239
+ z-index: 10;
240
+ font-family: 'THSarabunPsk', sans-serif;
241
+ }
242
+
243
+ .profile {
244
+ position: absolute;
245
+ top: 46px;
246
+ left: 725px;
247
+ width: 110px;
248
+ height: 138px;
249
+ object-fit: cover;
250
+ border: 2px solid var(--border-primary);
251
+ border-radius: 0.375rem;
252
+ z-index: 100;
253
+ }
254
+
255
+ .qr {
256
+ position: absolute;
257
+ object-fit: cover;
258
+ z-index: 100;
259
+ border-radius: 0.25rem;
260
+ }
261
+
262
+ /* PDF Viewer */
263
+ .pdf-viewer {
264
+ display: flex;
265
+ flex-direction: column;
266
+ align-items: center;
267
+ gap: 1.5rem;
268
+ padding: 2rem;
269
+ }
270
+
271
+ .pdf-page-wrapper {
272
+ background: #fff;
273
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
274
+ }
275
+
276
+ /* Form Elements */
277
+ .form-group {
278
+ margin-bottom: 1rem;
279
+ }
280
+
281
+ .form-group label {
282
+ display: block;
283
+ font-size: 0.75rem;
284
+ font-weight: 500;
285
+ color: var(--text-secondary);
286
+ margin-bottom: 0.375rem;
287
+ }
288
+
289
+ .form-control {
290
+ width: 100%;
291
+ padding: 0.5rem 0.75rem;
292
+ font-size: 0.875rem;
293
+ background: var(--bg-tertiary);
294
+ border: 1px solid var(--border-primary);
295
+ border-radius: 0.375rem;
296
+ color: var(--text-primary);
297
+ transition: var(--transition);
298
+ }
299
+
300
+ .form-control:focus {
301
+ outline: none;
302
+ border-color: var(--accent-primary);
303
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
304
+ }
305
+
306
+ /* File Input */
307
+ .file-label {
308
+ display: block;
309
+ width: 100%;
310
+ padding: 0.625rem 1rem;
311
+ background: linear-gradient(135deg, var(--accent-primary), var(--accent-tertiary));
312
+ color: white;
313
+ font-size: 0.875rem;
314
+ font-weight: 500;
315
+ text-align: center;
316
+ border-radius: 0.375rem;
317
+ cursor: pointer;
318
+ transition: var(--transition);
319
+ }
320
+
321
+ .file-label:hover {
322
+ transform: translateY(-1px);
323
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
324
+ }
325
+
326
+ .file-name {
327
+ font-size: 0.75rem;
328
+ color: var(--text-tertiary);
329
+ margin-top: 0.25rem;
330
+ text-align: center;
331
+ white-space: nowrap;
332
+ overflow: hidden;
333
+ text-overflow: ellipsis;
334
+ }
335
+
336
+ /* Buttons */
337
+ .btn {
338
+ display: inline-flex;
339
+ align-items: center;
340
+ justify-content: center;
341
+ gap: 0.5rem;
342
+ padding: 0.625rem 1rem;
343
+ font-size: 0.875rem;
344
+ font-weight: 500;
345
+ border: none;
346
+ border-radius: 0.375rem;
347
+ cursor: pointer;
348
+ transition: var(--transition);
349
+ }
350
+
351
+ .btn:disabled {
352
+ opacity: 0.5;
353
+ cursor: not-allowed;
354
+ }
355
+
356
+ .btn-primary {
357
+ background: linear-gradient(135deg, var(--accent-secondary), #34d399);
358
+ color: white;
359
+ }
360
+
361
+ .btn-secondary {
362
+ background: var(--bg-tertiary);
363
+ color: var(--text-primary);
364
+ border: 1px solid var(--border-primary);
365
+ }
366
+
367
+ .btn-indigo {
368
+ background: linear-gradient(135deg, #6366f1, #818cf8);
369
+ color: white;
370
+ }
371
+
372
+ .btn-danger {
373
+ background: linear-gradient(135deg, #ef4444, #f87171);
374
+ color: white;
375
+ }
376
+
377
+ .btn-icon {
378
+ width: 40px;
379
+ height: 40px;
380
+ padding: 0;
381
+ border-radius: 0.5rem;
382
+ }
383
+
384
+ .btn:hover:not(:disabled) {
385
+ transform: translateY(-1px);
386
+ }
387
+
388
+ /* HUD Controls */
389
+ .hud-controls {
390
+ position: fixed;
391
+ bottom: calc(var(--footer-height) + 1rem);
392
+ left: 50%;
393
+ transform: translateX(-50%);
394
+ background: var(--hud-bg);
395
+ backdrop-filter: blur(10px);
396
+ padding: 0.75rem 1.5rem;
397
+ border-radius: 2rem;
398
+ display: flex;
399
+ align-items: center;
400
+ gap: 1rem;
401
+ color: white;
402
+ z-index: 50;
403
+ }
404
+
405
+ .hud-controls .material-icons {
406
+ cursor: pointer;
407
+ transition: var(--transition);
408
+ }
409
+
410
+ .hud-controls .material-icons:hover {
411
+ color: var(--accent-primary);
412
+ }
413
+
414
+ /* Footer */
415
+ footer {
416
+ height: var(--footer-height);
417
+ background: var(--primary-color);
418
+ color: white;
419
+ display: flex;
420
+ align-items: center;
421
+ justify-content: space-between;
422
+ padding: 0 1rem;
423
+ font-size: 0.875rem;
424
+ flex-shrink: 0;
425
+ }
426
+
427
+ /* Toast Notification */
428
+ .toast {
429
+ position: fixed;
430
+ top: calc(var(--header-height) + 1rem);
431
+ left: 50%;
432
+ transform: translateX(-50%) translateY(-100px);
433
+ background: var(--hud-bg);
434
+ backdrop-filter: blur(10px);
435
+ color: white;
436
+ padding: 0.75rem 1.5rem;
437
+ border-radius: 0.5rem;
438
+ z-index: 1000;
439
+ transition: transform 0.3s ease;
440
+ max-width: 90%;
441
+ text-align: center;
442
+ }
443
+
444
+ .toast.show {
445
+ transform: translateX(-50%) translateY(0);
446
+ }
447
+
448
+ .toast.success {
449
+ border-left: 4px solid var(--accent-secondary);
450
+ }
451
+
452
+ .toast.error {
453
+ border-left: 4px solid #ef4444;
454
+ }
455
+
456
+ /* Modal */
457
+ .modal-overlay {
458
+ position: fixed;
459
+ inset: 0;
460
+ background: rgba(0, 0, 0, 0.7);
461
+ backdrop-filter: blur(4px);
462
+ display: none;
463
+ align-items: center;
464
+ justify-content: center;
465
+ z-index: 200;
466
+ }
467
+
468
+ .modal-overlay.show {
469
+ display: flex;
470
+ }
471
+
472
+ .modal-content {
473
+ background: var(--bg-secondary);
474
+ border-radius: 1rem;
475
+ width: 90%;
476
+ max-width: 500px;
477
+ max-height: 80vh;
478
+ overflow: hidden;
479
+ display: flex;
480
+ flex-direction: column;
481
+ }
482
+
483
+ .modal-header {
484
+ padding: 1rem 1.5rem;
485
+ border-bottom: 1px solid var(--border-primary);
486
+ display: flex;
487
+ align-items: center;
488
+ justify-content: space-between;
489
+ }
490
+
491
+ .modal-header h3 {
492
+ font-size: 1.125rem;
493
+ font-weight: 600;
494
+ }
495
+
496
+ .modal-body {
497
+ padding: 1.5rem;
498
+ overflow-y: auto;
499
+ }
500
+
501
+ .modal-footer {
502
+ padding: 1rem 1.5rem;
503
+ border-top: 1px solid var(--border-primary);
504
+ display: flex;
505
+ gap: 0.75rem;
506
+ justify-content: flex-end;
507
+ }
508
+
509
+ /* Loading Overlay */
510
+ .loading-overlay {
511
+ position: fixed;
512
+ inset: 0;
513
+ background: rgba(0, 0, 0, 0.8);
514
+ display: none;
515
+ align-items: center;
516
+ justify-content: center;
517
+ flex-direction: column;
518
+ gap: 1rem;
519
+ color: white;
520
+ z-index: 300;
521
+ }
522
+
523
+ .loading-overlay.show {
524
+ display: flex;
525
+ }
526
+
527
+ .loading-spinner {
528
+ width: 50px;
529
+ height: 50px;
530
+ border: 4px solid rgba(255, 255, 255, 0.3);
531
+ border-top-color: white;
532
+ border-radius: 50%;
533
+ animation: spin 1s linear infinite;
534
+ }
535
+
536
+ @keyframes spin {
537
+ to { transform: rotate(360deg); }
538
+ }
539
+
540
+ /* QR Preview */
541
+ .qr-preview {
542
+ text-align: center;
543
+ padding: 1rem;
544
+ background: var(--bg-tertiary);
545
+ border-radius: 0.5rem;
546
+ margin: 1rem 0;
547
+ }
548
+
549
+ .qr-preview canvas {
550
+ border: 1px solid var(--border-primary);
551
+ background: white;
552
+ }
553
+
554
+ .qr-info {
555
+ background: rgba(59, 130, 246, 0.1);
556
+ border: 1px solid rgba(59, 130, 246, 0.3);
557
+ border-radius: 0.5rem;
558
+ padding: 0.75rem;
559
+ font-size: 0.875rem;
560
+ margin: 0.75rem 0;
561
+ }
562
+
563
+ /* History List */
564
+ .history-item {
565
+ padding: 0.75rem 1rem;
566
+ border-bottom: 1px solid var(--border-primary);
567
+ cursor: pointer;
568
+ transition: var(--transition);
569
+ display: flex;
570
+ align-items: center;
571
+ gap: 0.75rem;
572
+ }
573
+
574
+ .history-item:hover {
575
+ background: var(--bg-tertiary);
576
+ }
577
+
578
+ .history-item .material-icons {
579
+ color: var(--text-tertiary);
580
+ }
581
+
582
+ .history-item-info {
583
+ flex: 1;
584
+ min-width: 0;
585
+ }
586
+
587
+ .history-item-name {
588
+ font-weight: 500;
589
+ white-space: nowrap;
590
+ overflow: hidden;
591
+ text-overflow: ellipsis;
592
+ }
593
+
594
+ .history-item-date {
595
+ font-size: 0.75rem;
596
+ color: var(--text-tertiary);
597
+ }
598
+
599
+ /* Responsive */
600
+ @media (max-width: 1024px) {
601
+ .sidebar {
602
+ position: fixed;
603
+ left: 0;
604
+ top: var(--header-height);
605
+ bottom: var(--footer-height);
606
+ z-index: 90;
607
+ transform: translateX(-100%);
608
+ }
609
+
610
+ .sidebar.open {
611
+ transform: translateX(0);
612
+ }
613
+
614
+ .sidebar-overlay {
615
+ position: fixed;
616
+ inset: 0;
617
+ background: rgba(0, 0, 0, 0.5);
618
+ z-index: 80;
619
+ display: none;
620
+ }
621
+
622
+ .sidebar-overlay.show {
623
+ display: block;
624
+ }
625
+
626
+ .page {
627
+ transform: scale(0.5);
628
+ transform-origin: top center;
629
+ }
630
+ }
631
+
632
+ @media (max-width: 640px) {
633
+ :root {
634
+ --sidebar-width: 100%;
635
+ }
636
+
637
+ .page {
638
+ transform: scale(0.35);
639
+ }
640
+
641
+ .hud-controls {
642
+ padding: 0.5rem 1rem;
643
+ gap: 0.75rem;
644
+ font-size: 0.875rem;
645
+ }
646
+ }
647
+
648
+ /* Scrollbar */
649
+ ::-webkit-scrollbar {
650
+ width: 8px;
651
+ height: 8px;
652
+ }
653
+
654
+ ::-webkit-scrollbar-track {
655
+ background: var(--bg-tertiary);
656
+ }
657
+
658
+ ::-webkit-scrollbar-thumb {
659
+ background: var(--text-tertiary);
660
+ border-radius: 4px;
661
+ }
662
+
663
+ ::-webkit-scrollbar-thumb:hover {
664
+ background: var(--text-secondary);
665
+ }
666
+
667
+ /* Print Styles */
668
+ @media print {
669
+ header, footer, .sidebar, .hud-controls, .toast, .modal-overlay, .loading-overlay {
670
+ display: none !important;
671
+ }
672
+
673
+ .page {
674
+ margin: 0 !important;
675
+ box-shadow: none !important;
676
+ page-break-after: always;
677
+ }
678
+ }
679
+ </style>
680
+ </head>
681
+ <body>
682
+ <div class="app-container">
683
+ <!-- Header -->
684
+ <header>
685
+ <div class="header-title">
686
+ <button class="btn btn-icon btn-secondary" id="toggle-sidebar">
687
+ <i class="material-icons">menu</i>
688
+ </button>
689
+ <span>ระบบจัดการเอกสาร PDF Professional</span>
690
+ </div>
691
+ <div class="header-actions">
692
+ <button class="btn btn-icon btn-secondary" id="btn-theme" title="สลับธีม">
693
+ <i class="material-icons">dark_mode</i>
694
+ </button>
695
+ <button class="btn btn-icon btn-secondary" id="btn-history" title="ประวัติ">
696
+ <i class="material-icons">history</i>
697
+ </button>
698
+ </div>
699
+ </header>
700
+
701
+ <!-- Main Container -->
702
+ <div class="main-container">
703
+ <!-- Sidebar Overlay -->
704
+ <div class="sidebar-overlay" id="sidebar-overlay"></div>
705
+
706
+ <!-- Sidebar -->
707
+ <aside class="sidebar" id="sidebar">
708
+ <div class="sidebar-content">
709
+ <!-- Background Section -->
710
+ <div class="sidebar-section">
711
+ <h3>พื้นหลังเอกสาร</h3>
712
+ <div class="form-group">
713
+ <label>หน้าที่ 1</label>
714
+ <input type="file" id="bg1-file" accept="image/*,.svg" hidden>
715
+ <label for="bg1-file" class="file-label">เลือกรูปภาพ</label>
716
+ <div id="bg1-name" class="file-name">ยังไม่ได้เลือก</div>
717
+ </div>
718
+ <div class="form-group">
719
+ <label>หน้าที่ 2</label>
720
+ <input type="file" id="bg2-file" accept="image/*,.svg" hidden>
721
+ <label for="bg2-file" class="file-label">เลือกรูปภาพ</label>
722
+ <div id="bg2-name" class="file-name">ยังไม่ได้เลือก</div>
723
+ </div>
724
+ <button class="btn btn-primary" style="width:100%" onclick="applyBg()">
725
+ <i class="material-icons" style="font-size:18px">check</i>
726
+ ใช้พื้นหลัง
727
+ </button>
728
+ </div>
729
+
730
+ <!-- JSON Import Section -->
731
+ <div class="sidebar-section">
732
+ <h3>นำเข้าข้อมูล JSON</h3>
733
+ <div class="form-group">
734
+ <input type="file" id="json-file" accept=".json" hidden>
735
+ <label for="json-file" class="file-label">เลือกไฟล์ JSON</label>
736
+ <div id="json-name" class="file-name">ยังไม่ได้เลือกไฟล์</div>
737
+ </div>
738
+ <div class="form-group">
739
+ <label>เลือกรายการข้อมูล</label>
740
+ <select id="data-select" class="form-control" onchange="showData(this.value)" disabled>
741
+ <option value="">-- ไม่มีข้อมูล --</option>
742
+ </select>
743
+ </div>
744
+ <button class="btn btn-indigo" style="width:100%" onclick="loadSample()">
745
+ <i class="material-icons" style="font-size:18px">science</i>
746
+ โหลดข้อมูลตัวอย่าง
747
+ </button>
748
+ </div>
749
+
750
+ <!-- PDF Import Section -->
751
+ <div class="sidebar-section">
752
+ <h3>นำเข้า PDF</h3>
753
+ <div class="form-group">
754
+ <input type="file" id="pdf-upload" accept="application/pdf" hidden>
755
+ <label for="pdf-upload" class="file-label">
756
+ <i class="material-icons" style="font-size:18px;vertical-align:middle">upload_file</i>
757
+ อัปโหลด PDF
758
+ </label>
759
+ <div id="pdf-name" class="file-name">ยังไม่ได้เลือกไฟล์</div>
760
+ </div>
761
+ </div>
762
+
763
+ <!-- Export Section -->
764
+ <div class="sidebar-section">
765
+ <h3>ส่งออกเอกสาร</h3>
766
+ <button class="btn btn-primary" style="width:100%;margin-bottom:0.5rem" onclick="downloadPDF()">
767
+ <i class="material-icons" style="font-size:18px">picture_as_pdf</i>
768
+ ดาวน์โหลด PDF ปัจจุบัน
769
+ </button>
770
+ <button class="btn btn-secondary" style="width:100%;margin-bottom:0.5rem" onclick="downloadAllPDFs()">
771
+ <i class="material-icons" style="font-size:18px">download</i>
772
+ ดาวน์โหลดทั้งหมด
773
+ </button>
774
+ <button class="btn btn-indigo" style="width:100%" onclick="openQRModal()">
775
+ <i class="material-icons" style="font-size:18px">qr_code</i>
776
+ เพิ่ม QR Code และดาวน์โหลด
777
+ </button>
778
+ </div>
779
+ </div>
780
+ </aside>
781
+
782
+ <!-- Viewer Container -->
783
+ <div class="viewer-container">
784
+ <!-- Tabs -->
785
+ <div class="viewer-tabs">
786
+ <button class="tab-btn active" data-tab="template">
787
+ <i class="material-icons" style="font-size:18px;vertical-align:middle">description</i>
788
+ Template
789
+ </button>
790
+ <button class="tab-btn" data-tab="pdf">
791
+ <i class="material-icons" style="font-size:18px;vertical-align:middle">picture_as_pdf</i>
792
+ PDF Viewer
793
+ </button>
794
+ </div>
795
+
796
+ <!-- Viewer Content -->
797
+ <div class="viewer-content" id="viewer-content">
798
+ <!-- Template View -->
799
+ <div class="template-viewer" id="template-view">
800
+ <!-- Page 1 -->
801
+ <div id="page1" class="page">
802
+ <img id="bg1" class="bg" src="" alt="">
803
+ <img id="photo" class="profile" src="https://via.placeholder.com/110x138?text=Photo" alt="">
804
+ <img id="qr1" class="qr" style="top:977px;left:762.5px;width:69px;height:69px;" src="" alt="">
805
+ <p class="field" style="top:87px;left:238px;font-size:14px;font-weight:bold;" id="f1"><span style="color:#3b82f6">f1</span> - <span style="color:#8b5cf6">[7]</span></p>
806
+ <p class="field" style="top:116px;left:238px;font-size:14px;font-weight:bold;" id="f2"><span style="color:#3b82f6">f2</span> - <span style="color:#8b5cf6">[1]</span></p>
807
+ <p class="field" style="top:323px;left:204px;font-size:14px;font-weight:normal;" id="f3"><span style="color:#3b82f6">f3</span> - <span style="color:#8b5cf6">[2]</span></p>
808
+ <p class="field" style="top:302px;left:204px;font-size:14px;font-weight:normal;" id="f4"><span style="color:#3b82f6">f4</span> - <span style="color:#8b5cf6">[8]</span></p>
809
+ <p class="field" style="top:302px;left:592px;font-size:14px;font-weight:normal;" id="f5"><span style="color:#3b82f6">f5</span> - <span style="color:#8b5cf6">[9]</span></p>
810
+ <p class="field" style="top:323px;left:592px;font-size:14px;font-weight:normal;" id="f6"><span style="color:#3b82f6">f6</span> - <span style="color:#8b5cf6">[1]</span></p>
811
+ <p class="field" style="top:345px;left:204px;font-size:14px;font-weight:normal;" id="f7"><span style="color:#3b82f6">f7</span> - <span style="color:#8b5cf6">[5]</span></p>
812
+ <p class="field" style="top:345px;left:592px;font-size:14px;font-weight:normal;" id="f8"><span style="color:#3b82f6">f8</span> - <span style="color:#8b5cf6">[6]</span></p>
813
+ <p class="field" style="top:367px;left:204px;font-size:14px;font-weight:normal;" id="f9"><span style="color:#3b82f6">f9</span> - <span style="color:#8b5cf6">[3]</span></p>
814
+ <p class="field" style="top:410px;left:204px;font-size:14px;font-weight:normal;" id="f10"><span style="color:#3b82f6">f10</span> - <span style="color:#8b5cf6">[10]</span></p>
815
+ </div>
816
+
817
+ <!-- Page 2 -->
818
+ <div id="page2" class="page">
819
+ <img id="bg2" class="bg" src="" alt="">
820
+ <img id="qr2" class="qr" style="top:925px;left:120px;width:90px;height:90px;" src="" alt="">
821
+ <p class="field" style="top:60px;left:640px;font-size:19px;font-weight:300;" id="f12"><span style="color:#3b82f6">f12</span> - <span style="color:#8b5cf6">[11]</span></p>
822
+ <p class="field" style="top:227px;left:640px;font-size:19px;font-weight:300;" id="f13"><span style="color:#3b82f6">f13</span> - <span style="color:#8b5cf6">[12]</span></p>
823
+ <p class="field" style="top:271px;left:180px;font-size:19px;font-weight:300;" id="f14"><span style="color:#3b82f6">f14</span> - <span style="color:#8b5cf6">[7]</span></p>
824
+ <p class="field" style="top:310px;left:180px;font-size:19px;font-weight:300;" id="f15"><span style="color:#3b82f6">f15</span> - <span style="color:#8b5cf6">[1]</span></p>
825
+ <p class="field" style="top:310px;left:520px;font-size:19px;font-weight:300;" id="f16"><span style="color:#3b82f6">f16</span> - <span style="color:#8b5cf6">[3]</span></p>
826
+ <p class="field" style="top:354px;left:180px;font-size:19px;font-weight:300;" id="f17"><span style="color:#3b82f6">f17</span> - <span style="color:#8b5cf6">[10]</span></p>
827
+ <p class="field" style="top:354px;left:640px;font-size:19px;font-weight:300;" id="f18"><span style="color:#3b82f6">f18</span> - <span style="color:#8b5cf6">[8]</span></p>
828
+ </div>
829
+ </div>
830
+
831
+ <!-- PDF View -->
832
+ <div class="pdf-viewer" id="pdf-view" style="display:none">
833
+ <div id="pdf-content"></div>
834
+ </div>
835
+ </div>
836
+ </div>
837
+ </div>
838
+
839
+ <!-- HUD Controls -->
840
+ <div class="hud-controls">
841
+ <span>หน้า <input type="number" id="current-pg" value="1" min="1" style="width:50px;padding:0.25rem;border-radius:0.25rem;border:none;text-align:center"> / <span id="total-pg">2</span></span>
842
+ <i class="material-icons" id="zoom-out" title="ซูมออก">remove</i>
843
+ <span id="zoom-text">100%</span>
844
+ <i class="material-icons" id="zoom-in" title="ซูมเข้า">add</i>
845
+ </div>
846
+
847
+ <!-- Footer -->
848
+ <footer>
849
+ <span>© PDF Processing System</span>
850
+ <span><span id="file-count">0</span> ไฟล์ในประวัติ</span>
851
+ </footer>
852
+ </div>
853
+
854
+ <!-- Toast -->
855
+ <div class="toast" id="toast"></div>
856
+
857
+ <!-- History Modal -->
858
+ <div class="modal-overlay" id="history-modal">
859
+ <div class="modal-content">
860
+ <div class="modal-header">
861
+ <h3>ประวัติไฟล์</h3>
862
+ <button class="btn btn-icon btn-secondary" onclick="closeModal('history-modal')">
863
+ <i class="material-icons">close</i>
864
+ </button>
865
+ </div>
866
+ <div class="modal-body" id="history-list">
867
+ <p style="text-align:center;color:var(--text-tertiary)">ไม่มีประวัติไฟล์</p>
868
+ </div>
869
+ </div>
870
+ </div>
871
+
872
+ <!-- QR Code Modal -->
873
+ <div class="modal-overlay" id="qr-modal">
874
+ <div class="modal-content">
875
+ <div class="modal-header">
876
+ <h3>ตั้งค่า QR Code</h3>
877
+ <button class="btn btn-icon btn-secondary" onclick="closeModal('qr-modal')">
878
+ <i class="material-icons">close</i>
879
+ </button>
880
+ </div>
881
+ <div class="modal-body">
882
+ <div class="form-group">
883
+ <label>URL สำหรับ QR Code:</label>
884
+ <input type="text" id="qr-url" class="form-control" placeholder="https://...">
885
+ <small style="color:var(--text-tertiary)">หากเว้นว่างจะใช้ URL ของหน้านี้</small>
886
+ </div>
887
+
888
+ <div class="qr-info">
889
+ <strong>ขนาด QR Code: 15mm × 13.58mm</strong><br>
890
+ <small>ตำแหน่ง: X=15.15mm, Y=292.45mm (จากมุมซ้ายล่าง)</small>
891
+ </div>
892
+
893
+ <div class="qr-preview">
894
+ <h4 style="margin-bottom:0.5rem;font-size:0.875rem">ตัวอย่าง QR Code:</h4>
895
+ <canvas id="qr-preview-canvas" width="150" height="136"></canvas>
896
+ <div style="font-size:0.75rem;color:var(--text-tertiary);margin-top:0.5rem">ขนาดจริง: 15mm × 13.58mm</div>
897
+ </div>
898
+ </div>
899
+ <div class="modal-footer">
900
+ <button class="btn btn-secondary" onclick="closeModal('qr-modal')">ยกเลิก</button>
901
+ <button class="btn btn-primary" onclick="applyQRAndDownload()">
902
+ <i class="material-icons" style="font-size:18px">download</i>
903
+ เพิ่ม QR และดาวน์โหลด
904
+ </button>
905
+ </div>
906
+ </div>
907
+ </div>
908
+
909
+ <!-- Loading Overlay -->
910
+ <div class="loading-overlay" id="loading-overlay">
911
+ <div class="loading-spinner"></div>
912
+ <div id="loading-text">กำลังประมวลผล...</div>
913
+ </div>
914
+
915
+ <script>
916
+ 'use strict';
917
+
918
+ // Initialize PDF.js
919
+ const pdfjsLib = window['pdfjs-dist/build/pdf'];
920
+ pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
921
+
922
+ // Database Configuration
923
+ const DB_NAME = 'PDFProcessingDB';
924
+ const STORE = 'documents';
925
+ let db;
926
+
927
+ // State Variables
928
+ let allData = [];
929
+ let currentIndex = 0;
930
+ let pdfDoc = null;
931
+ let scale = 1;
932
+ let originalPdfBytes = null;
933
+ let currentFileName = '';
934
+ let bg1Obj = '', bg1B64 = '', bg2Obj = '', bg2B64 = '';
935
+ let currentView = 'template';
936
+
937
+ // Field Mapping
938
+ const fieldToKey = {
939
+ 'photo': '4',
940
+ 'f1': '7', 'f2': '1', 'f3': '2', 'f4': '8', 'f5': '9',
941
+ 'f6': '1', 'f7': '5', 'f8': '6', 'f9': '3', 'f10': '10',
942
+ 'f12': '11', 'f13': '12', 'f14': '7', 'f15': '1',
943
+ 'f16': '3', 'f17': '10', 'f18': '8'
944
+ };
945
+
946
+ const fieldIds = ['f1','f2','f3','f4','f5','f6','f7','f8','f9','f10','f12','f13','f14','f15','f16','f17','f18'];
947
+
948
+ const primaryFieldForKey = {};
949
+ Object.keys(fieldToKey).forEach(id => {
950
+ const key = fieldToKey[id];
951
+ if (!primaryFieldForKey[key]) primaryFieldForKey[key] = id.replace('f', '');
952
+ });
953
+
954
+ // Utility Functions
955
+ function toast(msg, type = 'info') {
956
+ const t = document.getElementById('toast');
957
+ t.textContent = msg;
958
+ t.className = 'toast show ' + type;
959
+ setTimeout(() => t.classList.remove('show'), 3000);
960
+ }
961
+
962
+ function loading(show, text) {
963
+ const o = document.getElementById('loading-overlay');
964
+ if (show) {
965
+ document.getElementById('loading-text').textContent = text || 'กำลังประมวลผล...';
966
+ o.classList.add('show');
967
+ } else {
968
+ o.classList.remove('show');
969
+ }
970
+ }
971
+
972
+ function closeModal(id) {
973
+ document.getElementById(id).classList.remove('show');
974
+ }
975
+
976
+ function sha256(buffer) {
977
+ const bufferCopy = buffer.slice(0);
978
+ const wordArray = CryptoJS.lib.WordArray.create(new Uint8Array(bufferCopy));
979
+ return CryptoJS.SHA256(wordArray).toString();
980
+ }
981
+
982
+ function mmToPoints(mm) {
983
+ return mm * 2.83465;
984
+ }
985
+
986
+ // Database Functions
987
+ function initDB() {
988
+ return new Promise((resolve, reject) => {
989
+ const request = indexedDB.open(DB_NAME, 1);
990
+ request.onupgradeneeded = (e) => {
991
+ const database = e.target.result;
992
+ if (!database.objectStoreNames.contains(STORE)) {
993
+ const store = database.createObjectStore(STORE, { keyPath: 'hash' });
994
+ store.createIndex('createdAt', 'createdAt');
995
+ }
996
+ };
997
+ request.onsuccess = () => {
998
+ db = request.result;
999
+ resolve();
1000
+ };
1001
+ request.onerror = () => reject(request.error);
1002
+ });
1003
+ }
1004
+
1005
+ function saveDoc(obj) {
1006
+ return new Promise((resolve, reject) => {
1007
+ const tx = db.transaction(STORE, 'readwrite');
1008
+ const store = tx.objectStore(STORE);
1009
+ const request = store.put(obj);
1010
+ request.onsuccess = () => resolve();
1011
+ request.onerror = () => reject(request.error);
1012
+ });
1013
+ }
1014
+
1015
+ function getAllDocs() {
1016
+ return new Promise((resolve, reject) => {
1017
+ const tx = db.transaction(STORE, 'readonly');
1018
+ const request = tx.objectStore(STORE).getAll();
1019
+ request.onsuccess = () => resolve(request.result);
1020
+ request.onerror = () => reject(request.error);
1021
+ });
1022
+ }
1023
+
1024
+ // Theme Toggle
1025
+ function toggleTheme() {
1026
+ const html = document.documentElement;
1027
+ const newTheme = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
1028
+ html.setAttribute('data-theme', newTheme);
1029
+ const icon = document.querySelector('#btn-theme .material-icons');
1030
+ icon.textContent = newTheme === 'dark' ? 'light_mode' : 'dark_mode';
1031
+ toast(`เปลี่ยนเป็นธีม${newTheme === 'dark' ? 'มืด' : 'สว่าง'}`, 'success');
1032
+ }
1033
+
1034
+ // Sidebar Toggle
1035
+ function toggleSidebar() {
1036
+ const sidebar = document.getElementById('sidebar');
1037
+ const overlay = document.getElementById('sidebar-overlay');
1038
+ sidebar.classList.toggle('open');
1039
+ overlay.classList.toggle('show');
1040
+ }
1041
+
1042
+ // Tab Switching
1043
+ function switchTab(tab) {
1044
+ document.querySelectorAll('.tab-btn').forEach(btn => {
1045
+ btn.classList.toggle('active', btn.dataset.tab === tab);
1046
+ });
1047
+
1048
+ const templateView = document.getElementById('template-view');
1049
+ const pdfView = document.getElementById('pdf-view');
1050
+
1051
+ if (tab === 'template') {
1052
+ templateView.style.display = 'flex';
1053
+ pdfView.style.display = 'none';
1054
+ document.getElementById('total-pg').textContent = '2';
1055
+ } else {
1056
+ templateView.style.display = 'none';
1057
+ pdfView.style.display = 'flex';
1058
+ if (pdfDoc) {
1059
+ document.getElementById('total-pg').textContent = pdfDoc.numPages;
1060
+ }
1061
+ }
1062
+
1063
+ currentView = tab;
1064
+ }
1065
+
1066
+ // Background Handlers
1067
+ function handleBgFile(page, file) {
1068
+ if (!file) return;
1069
+ const obj = URL.createObjectURL(file);
1070
+ document.getElementById('bg' + page).src = obj;
1071
+ document.getElementById('bg' + page + '-name').textContent = file.name;
1072
+
1073
+ if (page === '1') {
1074
+ if (bg1Obj) URL.revokeObjectURL(bg1Obj);
1075
+ bg1Obj = obj;
1076
+ } else {
1077
+ if (bg2Obj) URL.revokeObjectURL(bg2Obj);
1078
+ bg2Obj = obj;
1079
+ }
1080
+
1081
+ const reader = new FileReader();
1082
+ reader.onload = e => {
1083
+ if (page === '1') bg1B64 = e.target.result;
1084
+ else bg2B64 = e.target.result;
1085
+ };
1086
+ reader.readAsDataURL(file);
1087
+ }
1088
+
1089
+ function applyBg() {
1090
+ if (bg1Obj) document.getElementById('bg1').src = bg1Obj;
1091
+ if (bg2Obj) document.getElementById('bg2').src = bg2Obj;
1092
+ toast('ใช้พื้นหลังเรียบร้อย', 'success');
1093
+ }
1094
+
1095
+ // JSON Data Handlers
1096
+ function populateDropdown() {
1097
+ const select = document.getElementById('data-select');
1098
+ select.innerHTML = '';
1099
+
1100
+ if (allData.length === 1) {
1101
+ const opt = document.createElement('option');
1102
+ opt.value = 0;
1103
+ opt.textContent = 'รายการเดียว';
1104
+ select.appendChild(opt);
1105
+ select.disabled = true;
1106
+ } else {
1107
+ allData.forEach((item, idx) => {
1108
+ const opt = document.createElement('option');
1109
+ opt.value = idx;
1110
+ opt.textContent = item['1'] ? `รายการ ${idx + 1}: ${item['1']}` : `รายการ ${idx + 1}`;
1111
+ select.appendChild(opt);
1112
+ });
1113
+ select.disabled = false;
1114
+ }
1115
+ select.value = currentIndex;
1116
+ }
1117
+
1118
+ function showData(idx) {
1119
+ currentIndex = parseInt(idx);
1120
+ if (currentIndex < 0 || currentIndex >= allData.length) return;
1121
+
1122
+ const data = allData[currentIndex];
1123
+
1124
+ fieldIds.forEach(id => {
1125
+ const key = fieldToKey[id];
1126
+ const val = data[key] || '';
1127
+ const primaryNum = primaryFieldForKey[key];
1128
+ let label = `<span style="color:#3b82f6">${id}</span> - <span style="color:#8b5cf6">[${key}]</span>`;
1129
+
1130
+ if (primaryNum && primaryNum !== id.replace('f', '')) {
1131
+ label = `<span style="color:#3b82f6">${id}</span> - <span style="color:#8b5cf6">[${key}: ซ้ำจาก ${primaryNum}]</span>`;
1132
+ }
1133
+
1134
+ const el = document.getElementById(id);
1135
+ if (!el) return;
1136
+ el.innerHTML = val ? val : label;
1137
+ if (val) el.style.color = 'black';
1138
+ });
1139
+
1140
+ document.getElementById('photo').src = data[fieldToKey['photo']] || 'https://via.placeholder.com/110x138?text=No+Photo';
1141
+
1142
+ const qrApi = 'https://api.qrserver.com/v1/create-qr-code/?size=300x300&ecc=H&data=';
1143
+ document.getElementById('qr1').src = qrApi + encodeURIComponent(`${location.origin}/worker.html?id=${encodeURIComponent(data['12'] || '')}`);
1144
+ document.getElementById('qr2').src = qrApi + encodeURIComponent(`${location.origin}/pdf.html?page=2&page1=1&id=${encodeURIComponent(data['12'] || '')}`);
1145
+ }
1146
+
1147
+ function loadSample() {
1148
+ allData = [
1149
+ { "1": "นายทดสอบ หนึ่ง", "2": "ตำแหน่งที่ 1", "3": "แผนก A", "4": "", "5": "01/01/2025", "6": "31/12/2025", "7": "บริษัท ทดสอบ จำกัด", "8": "กรุงเทพมหานคร", "9": "10110", "10": "ประเทศไทย", "11": "REF001", "12": "DOC001" },
1150
+ { "1": "นางสาวทดสอบ สอง", "2": "ตำแหน่งที่ 2", "3": "แผนก B", "4": "", "5": "01/02/2025", "6": "28/02/2026", "7": "บริษัท ตัวอย่าง จำกัด", "8": "เชียงใหม่", "9": "50200", "10": "ประเทศไทย", "11": "REF002", "12": "DOC002" },
1151
+ { "1": "นายทดสอบ สาม", "2": "ตำแหน่งที่ 3", "3": "แผนก C", "4": "", "5": "01/03/2025", "6": "31/03/2026", "7": "องค์กร ทดสอบ", "8": "ภูเก็ต", "9": "83000", "10": "ประเทศไทย", "11": "REF003", "12": "DOC003" }
1152
+ ];
1153
+ currentIndex = 0;
1154
+ populateDropdown();
1155
+ showData(currentIndex);
1156
+ toast(`โหลดข้อมูลตัวอย่างสำเร็จ (${allData.length} รายการ)`, 'success');
1157
+ }
1158
+
1159
+ // PDF Viewer Functions
1160
+ async function renderPDF() {
1161
+ if (!pdfDoc) return;
1162
+
1163
+ const container = document.getElementById('pdf-content');
1164
+ container.innerHTML = '';
1165
+
1166
+ const viewerWidth = document.getElementById('viewer-content').clientWidth;
1167
+
1168
+ for (let i = 1; i <= pdfDoc.numPages; i++) {
1169
+ const page = await pdfDoc.getPage(i);
1170
+ const viewport = page.getViewport({ scale: 1 });
1171
+ const pageScale = (viewerWidth * 0.9 / viewport.width) * scale;
1172
+ const scaledViewport = page.getViewport({ scale: pageScale });
1173
+
1174
+ const canvas = document.createElement('canvas');
1175
+ canvas.width = scaledViewport.width;
1176
+ canvas.height = scaledViewport.height;
1177
+
1178
+ await page.render({
1179
+ canvasContext: canvas.getContext('2d'),
1180
+ viewport: scaledViewport
1181
+ }).promise;
1182
+
1183
+ const wrapper = document.createElement('div');
1184
+ wrapper.className = 'pdf-page-wrapper';
1185
+ wrapper.appendChild(canvas);
1186
+ container.appendChild(wrapper);
1187
+ }
1188
+ }
1189
+
1190
+ async function loadPDF(buffer, name) {
1191
+ loading(true, 'กำลังโหลด PDF...');
1192
+
1193
+ try {
1194
+ const pdfBuffer = buffer.slice(0);
1195
+ originalPdfBytes = buffer.slice(0);
1196
+ currentFileName = name;
1197
+
1198
+ pdfDoc = await pdfjsLib.getDocument({ data: pdfBuffer }).promise;
1199
+ document.getElementById('total-pg').textContent = pdfDoc.numPages;
1200
+ document.getElementById('pdf-name').textContent = name;
1201
+
1202
+ switchTab('pdf');
1203
+ await renderPDF();
1204
+
1205
+ toast('โหลด PDF สำเร็จ', 'success');
1206
+ } catch (error) {
1207
+ console.error('Error loading PDF:', error);
1208
+ toast('เกิดข้อผิดพลาดในการโหลด PDF', 'error');
1209
+ } finally {
1210
+ loading(false);
1211
+ }
1212
+ }
1213
+
1214
+ // QR Code Functions
1215
+ function generateQRCode15mm(text) {
1216
+ try {
1217
+ const typeNumber = 0;
1218
+ const errorCorrectionLevel = 'L';
1219
+ const qr = qrcode(typeNumber, errorCorrectionLevel);
1220
+ qr.addData(text);
1221
+ qr.make();
1222
+
1223
+ const targetWidth = 150;
1224
+ const targetHeight = 136;
1225
+ const cellCount = qr.getModuleCount();
1226
+ const cellSize = Math.min(
1227
+ Math.floor(targetWidth / cellCount),
1228
+ Math.floor(targetHeight / cellCount)
1229
+ );
1230
+
1231
+ const canvasWidth = cellCount * cellSize;
1232
+ const canvasHeight = cellCount * cellSize;
1233
+
1234
+ const canvas = document.createElement('canvas');
1235
+ const ctx = canvas.getContext('2d');
1236
+ canvas.width = canvasWidth;
1237
+ canvas.height = canvasHeight;
1238
+
1239
+ for (let row = 0; row < cellCount; row++) {
1240
+ for (let col = 0; col < cellCount; col++) {
1241
+ ctx.fillStyle = qr.isDark(row, col) ? '#000000' : '#ffffff';
1242
+ ctx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
1243
+ }
1244
+ }
1245
+
1246
+ const finalCanvas = document.createElement('canvas');
1247
+ const finalCtx = finalCanvas.getContext('2d');
1248
+ finalCanvas.width = targetWidth;
1249
+ finalCanvas.height = targetHeight;
1250
+ finalCtx.fillStyle = '#ffffff';
1251
+ finalCtx.fillRect(0, 0, targetWidth, targetHeight);
1252
+ finalCtx.drawImage(canvas, 0, 0, canvasWidth, canvasHeight, 0, 0, targetWidth, targetHeight);
1253
+
1254
+ return finalCanvas.toDataURL('image/png');
1255
+ } catch (error) {
1256
+ console.error('Error generating QR code:', error);
1257
+ return null;
1258
+ }
1259
+ }
1260
+
1261
+ function updateQRPreview() {
1262
+ const urlInput = document.getElementById('qr-url');
1263
+ const previewCanvas = document.getElementById('qr-preview-canvas');
1264
+ const ctx = previewCanvas.getContext('2d');
1265
+
1266
+ ctx.fillStyle = '#ffffff';
1267
+ ctx.fillRect(0, 0, previewCanvas.width, previewCanvas.height);
1268
+
1269
+ const qrText = urlInput.value || window.location.href;
1270
+
1271
+ try {
1272
+ const typeNumber = 0;
1273
+ const errorCorrectionLevel = 'L';
1274
+ const qr = qrcode(typeNumber, errorCorrectionLevel);
1275
+ qr.addData(qrText);
1276
+ qr.make();
1277
+
1278
+ const cellCount = qr.getModuleCount();
1279
+ const cellSize = Math.min(
1280
+ Math.floor(150 / cellCount),
1281
+ Math.floor(136 / cellCount)
1282
+ );
1283
+
1284
+ const canvasWidth = cellCount * cellSize;
1285
+ const canvasHeight = cellCount * cellSize;
1286
+
1287
+ const tempCanvas = document.createElement('canvas');
1288
+ const tempCtx = tempCanvas.getContext('2d');
1289
+ tempCanvas.width = canvasWidth;
1290
+ tempCanvas.height = canvasHeight;
1291
+
1292
+ for (let row = 0; row < cellCount; row++) {
1293
+ for (let col = 0; col < cellCount; col++) {
1294
+ tempCtx.fillStyle = qr.isDark(row, col) ? '#000000' : '#ffffff';
1295
+ tempCtx.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
1296
+ }
1297
+ }
1298
+
1299
+ ctx.drawImage(tempCanvas, 0, 0, canvasWidth, canvasHeight, 0, 0, 150, 136);
1300
+ } catch (error) {
1301
+ console.error('Error previewing QR code:', error);
1302
+ }
1303
+ }
1304
+
1305
+ function openQRModal() {
1306
+ if (!originalPdfBytes && currentView === 'pdf') {
1307
+ toast('กรุณาอัปโหลด PDF ก่อน', 'error');
1308
+ return;
1309
+ }
1310
+
1311
+ document.getElementById('qr-url').value = window.location.href;
1312
+ updateQRPreview();
1313
+ document.getElementById('qr-modal').classList.add('show');
1314
+ }
1315
+
1316
+ async function addQRCodeToPDF(pdfBytes, qrDataURL) {
1317
+ try {
1318
+ const pdfDocument = await PDFLib.PDFDocument.load(pdfBytes);
1319
+ const pages = pdfDocument.getPages();
1320
+ const qrImage = await pdfDocument.embedPng(qrDataURL);
1321
+
1322
+ const x_mm = 15.15;
1323
+ const y_mm = 292.45;
1324
+ const width_mm = 15;
1325
+ const height_mm = 13.58;
1326
+
1327
+ const x = mmToPoints(x_mm);
1328
+ const y = mmToPoints(297 - y_mm - height_mm);
1329
+ const width = mmToPoints(width_mm);
1330
+ const height = mmToPoints(height_mm);
1331
+
1332
+ if (pages.length > 0) {
1333
+ pages[0].drawImage(qrImage, { x, y, width, height });
1334
+ }
1335
+
1336
+ return await pdfDocument.save();
1337
+ } catch (error) {
1338
+ console.error('Error adding QR code to PDF:', error);
1339
+ throw error;
1340
+ }
1341
+ }
1342
+
1343
+ async function applyQRAndDownload() {
1344
+ try {
1345
+ const qrUrl = document.getElementById('qr-url').value || window.location.href;
1346
+
1347
+ if (currentView === 'pdf' && originalPdfBytes) {
1348
+ loading(true, 'กำลังเพิ่ม QR Code...');
1349
+
1350
+ const qrDataURL = generateQRCode15mm(qrUrl);
1351
+ if (!qrDataURL) throw new Error('ไม่สามารถสร้าง QR Code ได้');
1352
+
1353
+ const modifiedPdfBytes = await addQRCodeToPDF(originalPdfBytes, qrDataURL);
1354
+ const newFileName = currentFileName.replace('.pdf', '_with_qr.pdf') || 'document_with_qr.pdf';
1355
+
1356
+ const newHash = sha256(modifiedPdfBytes);
1357
+ await saveDoc({
1358
+ hash: newHash,
1359
+ fileName: newFileName,
1360
+ pdfData: modifiedPdfBytes,
1361
+ createdAt: new Date().toISOString(),
1362
+ hasQRCode: true,
1363
+ qrUrl: qrUrl
1364
+ });
1365
+
1366
+ const blob = new Blob([modifiedPdfBytes], { type: 'application/pdf' });
1367
+ const url = URL.createObjectURL(blob);
1368
+ const a = document.createElement('a');
1369
+ a.href = url;
1370
+ a.download = newFileName;
1371
+ document.body.appendChild(a);
1372
+ a.click();
1373
+ document.body.removeChild(a);
1374
+ URL.revokeObjectURL(url);
1375
+
1376
+ const docs = await getAllDocs();
1377
+ document.getElementById('file-count').textContent = docs.length;
1378
+
1379
+ closeModal('qr-modal');
1380
+ toast('ดาวน์โหลดไฟล์พร้อม QR Code สำเร็จ', 'success');
1381
+ } else {
1382
+ await downloadPDFWithQR(qrUrl);
1383
+ }
1384
+ } catch (error) {
1385
+ console.error('Error processing PDF with QR:', error);
1386
+ toast('เกิดข้อผิดพลาดในการเพิ่ม QR Code', 'error');
1387
+ } finally {
1388
+ loading(false);
1389
+ }
1390
+ }
1391
+
1392
+ // PDF Generation from Template
1393
+ async function createPDF(data, filename, addQR = false, qrUrl = '') {
1394
+ loading(true, 'กำลังสร้างไฟล์ PDF...');
1395
+
1396
+ try {
1397
+ document.querySelectorAll('.sidebar, .hud-controls, header, footer').forEach(c => c.style.display = 'none');
1398
+
1399
+ if (bg1B64) document.getElementById('bg1').src = bg1B64;
1400
+ if (bg2B64) document.getElementById('bg2').src = bg2B64;
1401
+
1402
+ fieldIds.forEach(id => {
1403
+ const key = fieldToKey[id];
1404
+ const val = data[key] || '';
1405
+ const el = document.getElementById(id);
1406
+ if (!el) return;
1407
+ if (id === 'f6') el.innerHTML = val;
1408
+ else el.innerHTML = val ? `<b>${val}</b>` : '';
1409
+ });
1410
+
1411
+ document.getElementById('photo').src = data[fieldToKey['photo']] || 'https://via.placeholder.com/110x138?text=No+Photo';
1412
+
1413
+ await new Promise(r => setTimeout(r, 600));
1414
+
1415
+ const el = document.createElement('div');
1416
+ el.style.width = '892px';
1417
+ ['page1', 'page2'].forEach(id => {
1418
+ const c = document.getElementById(id).cloneNode(true);
1419
+ c.querySelectorAll('.label').forEach(l => l.remove());
1420
+ el.appendChild(c);
1421
+ });
1422
+ document.body.appendChild(el);
1423
+
1424
+ const pdfBlob = await html2pdf().set({
1425
+ margin: 0,
1426
+ filename,
1427
+ image: { type: 'jpeg', quality: 0.98 },
1428
+ html2canvas: { scale: 6, useCORS: true, width: 892, height: 2522 },
1429
+ jsPDF: { unit: 'px', format: [892, 1261], orientation: 'portrait', compress: true }
1430
+ }).from(el).outputPdf('blob');
1431
+
1432
+ document.body.removeChild(el);
1433
+
1434
+ let finalBlob = pdfBlob;
1435
+
1436
+ if (addQR && qrUrl) {
1437
+ const pdfArrayBuffer = await pdfBlob.arrayBuffer();
1438
+ const qrDataURL = generateQRCode15mm(qrUrl);
1439
+ if (qrDataURL) {
1440
+ const modifiedPdfBytes = await addQRCodeToPDF(pdfArrayBuffer, qrDataURL);
1441
+ finalBlob = new Blob([modifiedPdfBytes], { type: 'application/pdf' });
1442
+ }
1443
+ }
1444
+
1445
+ const url = URL.createObjectURL(finalBlob);
1446
+ const a = document.createElement('a');
1447
+ a.href = url;
1448
+ a.download = filename;
1449
+ document.body.appendChild(a);
1450
+ a.click();
1451
+ document.body.removeChild(a);
1452
+ URL.revokeObjectURL(url);
1453
+
1454
+ document.querySelectorAll('.sidebar, .hud-controls, header, footer').forEach(c => c.style.display = '');
1455
+ if (bg1Obj) document.getElementById('bg1').src = bg1Obj;
1456
+ if (bg2Obj) document.getElementById('bg2').src = bg2Obj;
1457
+
1458
+ showData(currentIndex);
1459
+
1460
+ } catch (error) {
1461
+ console.error('Error creating PDF:', error);
1462
+ throw error;
1463
+ } finally {
1464
+ loading(false);
1465
+ }
1466
+ }
1467
+
1468
+ async function downloadPDF() {
1469
+ if (allData.length === 0) {
1470
+ toast('กรุณานำเข้าข้อมูลก่อน', 'error');
1471
+ return;
1472
+ }
1473
+
1474
+ try {
1475
+ await createPDF(allData[currentIndex], `${allData[currentIndex]['1'] || 'document'}.pdf`);
1476
+ toast('สร้างไฟล์ PDF สำเร็จ', 'success');
1477
+ } catch (error) {
1478
+ toast('เกิดข้อผิดพลาดในการสร้าง PDF', 'error');
1479
+ }
1480
+ }
1481
+
1482
+ async function downloadPDFWithQR(qrUrl) {
1483
+ if (allData.length === 0) {
1484
+ toast('กรุณานำเข้าข้อมูลก่อน', 'error');
1485
+ return;
1486
+ }
1487
+
1488
+ try {
1489
+ closeModal('qr-modal');
1490
+ await createPDF(allData[currentIndex], `${allData[currentIndex]['1'] || 'document'}_with_qr.pdf`, true, qrUrl);
1491
+ toast('สร้างไฟล์ PDF พร้อม QR Code สำเร็จ', 'success');
1492
+ } catch (error) {
1493
+ toast('เกิดข้อผิดพลาดในการสร้าง PDF', 'error');
1494
+ }
1495
+ }
1496
+
1497
+ async function downloadAllPDFs() {
1498
+ if (allData.length === 0) {
1499
+ toast('ไม่มีข้อมูล', 'error');
1500
+ return;
1501
+ }
1502
+
1503
+ if (!confirm(`ต้องการดาวน์โหลดทั้งหมด ${allData.length} ไฟล์?`)) return;
1504
+
1505
+ for (let i = 0; i < allData.length; i++) {
1506
+ currentIndex = i;
1507
+ document.getElementById('data-select').value = i;
1508
+ await createPDF(allData[i], `${allData[i]['1'] || 'document'}_batch_${i + 1}.pdf`);
1509
+ await new Promise(r => setTimeout(r, 800));
1510
+ }
1511
+
1512
+ toast('ดาวน์โหลดทั้งหมดสำเร็จ', 'success');
1513
+ }
1514
+
1515
+ // History Functions
1516
+ async function showHistory() {
1517
+ const list = document.getElementById('history-list');
1518
+ list.innerHTML = '';
1519
+
1520
+ try {
1521
+ const docs = await getAllDocs();
1522
+
1523
+ if (docs.length === 0) {
1524
+ list.innerHTML = '<p style="text-align:center;color:var(--text-tertiary);padding:2rem">ไม่มีประวัติไฟล์</p>';
1525
+ } else {
1526
+ docs.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
1527
+
1528
+ docs.forEach(doc => {
1529
+ const item = document.createElement('div');
1530
+ item.className = 'history-item';
1531
+ item.innerHTML = `
1532
+ <i class="material-icons">${doc.hasQRCode ? 'qr_code' : 'picture_as_pdf'}</i>
1533
+ <div class="history-item-info">
1534
+ <div class="history-item-name">${doc.fileName}</div>
1535
+ <div class="history-item-date">${new Date(doc.createdAt).toLocaleString('th-TH')}</div>
1536
+ </div>
1537
+ `;
1538
+ item.onclick = async () => {
1539
+ await loadPDF(doc.pdfData, doc.fileName);
1540
+ closeModal('history-modal');
1541
+ };
1542
+ list.appendChild(item);
1543
+ });
1544
+ }
1545
+
1546
+ document.getElementById('history-modal').classList.add('show');
1547
+ } catch (error) {
1548
+ console.error('Error loading history:', error);
1549
+ toast('เกิดข้อผิดพลาดในการโหลดประวัติ', 'error');
1550
+ }
1551
+ }
1552
+
1553
+ // Zoom Functions
1554
+ function zoomIn() {
1555
+ scale = Math.min(3, scale + 0.1);
1556
+ document.getElementById('zoom-text').textContent = Math.round(scale * 100) + '%';
1557
+ if (currentView === 'pdf' && pdfDoc) renderPDF();
1558
+ }
1559
+
1560
+ function zoomOut() {
1561
+ scale = Math.max(0.2, scale - 0.1);
1562
+ document.getElementById('zoom-text').textContent = Math.round(scale * 100) + '%';
1563
+ if (currentView === 'pdf' && pdfDoc) renderPDF();
1564
+ }
1565
+
1566
+ // Event Listeners
1567
+ document.addEventListener('DOMContentLoaded', async () => {
1568
+ try {
1569
+ await initDB();
1570
+ const docs = await getAllDocs();
1571
+ document.getElementById('file-count').textContent = docs.length;
1572
+ toast('ระบบพร้อมใช้งาน', 'success');
1573
+ } catch (error) {
1574
+ console.error('Error initializing:', error);
1575
+ toast('เกิดข้อผิดพลาดในการเริ่มต้นระบบ', 'error');
1576
+ }
1577
+ });
1578
+
1579
+ // Toggle Sidebar
1580
+ document.getElementById('toggle-sidebar').addEventListener('click', toggleSidebar);
1581
+ document.getElementById('sidebar-overlay').addEventListener('click', toggleSidebar);
1582
+
1583
+ // Theme Toggle
1584
+ document.getElementById('btn-theme').addEventListener('click', toggleTheme);
1585
+
1586
+ // History Button
1587
+ document.getElementById('btn-history').addEventListener('click', showHistory);
1588
+
1589
+ // Tab Buttons
1590
+ document.querySelectorAll('.tab-btn').forEach(btn => {
1591
+ btn.addEventListener('click', () => switchTab(btn.dataset.tab));
1592
+ });
1593
+
1594
+ // Zoom Controls
1595
+ document.getElementById('zoom-in').addEventListener('click', zoomIn);
1596
+ document.getElementById('zoom-out').addEventListener('click', zoomOut);
1597
+
1598
+ // Background File Inputs
1599
+ document.getElementById('bg1-file').addEventListener('change', e => handleBgFile('1', e.target.files[0]));
1600
+ document.getElementById('bg2-file').addEventListener('change', e => handleBgFile('2', e.target.files[0]));
1601
+
1602
+ // JSON File Input
1603
+ document.getElementById('json-file').addEventListener('change', e => {
1604
+ const file = e.target.files[0];
1605
+ if (!file) return;
1606
+
1607
+ document.getElementById('json-name').textContent = file.name;
1608
+ const reader = new FileReader();
1609
+ reader.onload = ev => {
1610
+ try {
1611
+ const raw = JSON.parse(ev.target.result);
1612
+ if (!Array.isArray(raw) || raw.length === 0) throw new Error('ข้อมูลว่างเปล่า');
1613
+ allData = raw;
1614
+ currentIndex = 0;
1615
+ populateDropdown();
1616
+ showData(currentIndex);
1617
+ toast(`นำเข้าข้อมูลสำเร็จ (${allData.length} รายการ)`, 'success');
1618
+ } catch (err) {
1619
+ toast('รูปแบบ JSON ไม่ถูกต้อง: ' + err.message, 'error');
1620
+ }
1621
+ };
1622
+ reader.readAsText(file);
1623
+ });
1624
+
1625
+ // PDF File Input
1626
+ document.getElementById('pdf-upload').addEventListener('change', e => {
1627
+ const file = e.target.files[0];
1628
+ if (!file) return;
1629
+
1630
+ const reader = new FileReader();
1631
+ reader.onload = () => loadPDF(reader.result, file.name);
1632
+ reader.readAsArrayBuffer(file);
1633
+ });
1634
+
1635
+ // QR URL Input
1636
+ document.getElementById('qr-url').addEventListener('input', updateQRPreview);
1637
+
1638
+ // Modal Close on Overlay Click
1639
+ document.querySelectorAll('.modal-overlay').forEach(modal => {
1640
+ modal.addEventListener('click', e => {
1641
+ if (e.target === modal) {
1642
+ modal.classList.remove('show');
1643
+ }
1644
+ });
1645
+ });
1646
+
1647
+ // Keyboard Shortcuts
1648
+ document.addEventListener('keydown', e => {
1649
+ if (e.ctrlKey || e.metaKey) {
1650
+ if (e.key === '=') {
1651
+ e.preventDefault();
1652
+ zoomIn();
1653
+ } else if (e.key === '-') {
1654
+ e.preventDefault();
1655
+ zoomOut();
1656
+ } else if (e.key === 's') {
1657
+ e.preventDefault();
1658
+ downloadPDF();
1659
+ }
1660
+ }
1661
+
1662
+ if (e.key === 'Escape') {
1663
+ document.querySelectorAll('.modal-overlay.show').forEach(modal => {
1664
+ modal.classList.remove('show');
1665
+ });
1666
+ }
1667
+ });
1668
+ </script>
1669
+ </body>
1670
+ </html>