protae5544 commited on
Commit
2ce99ee
·
verified ·
1 Parent(s): c2f5104

ฟังชั่นที่เคยมีกลับใช่ไม่ได้ทำให้มันใช้ได้และมีตัวอย่างการใช้งานเต็มรูปแบบ

Browse files
Files changed (3) hide show
  1. index.html +1 -2
  2. script.js +196 -12
  3. style.css +108 -1
index.html CHANGED
@@ -174,7 +174,6 @@
174
  <script src="components/navbar.js"></script>
175
  <script src="components/sidebar.js"></script>
176
  <script src="components/footer.js"></script>
177
-
178
  <!-- Navigation Links -->
179
  <script>
180
  // Listen for data updates from dashboard
@@ -190,4 +189,4 @@
190
  <script src="script.js"></script>
191
  <script>feather.replace();</script>
192
  </body>
193
- </html>
 
174
  <script src="components/navbar.js"></script>
175
  <script src="components/sidebar.js"></script>
176
  <script src="components/footer.js"></script>
 
177
  <!-- Navigation Links -->
178
  <script>
179
  // Listen for data updates from dashboard
 
189
  <script src="script.js"></script>
190
  <script>feather.replace();</script>
191
  </body>
192
+ </html>
script.js CHANGED
@@ -76,15 +76,17 @@ function setupGlobalListeners() {
76
  document.getElementById('bg2-file')?.addEventListener('change', e => handleBgFile('2', e.target.files[0]));
77
  document.getElementById('json-file')?.addEventListener('change', handleJsonUpload);
78
  document.getElementById('pdf-upload')?.addEventListener('change', handlePdfUpload);
79
-
80
  // Sidebar Actions
81
  document.getElementById('btn-apply-bg')?.addEventListener('click', applyBg);
82
  document.getElementById('btn-load-sample')?.addEventListener('click', loadSample);
83
  document.getElementById('btn-download-pdf')?.addEventListener('click', downloadPDF);
84
  document.getElementById('btn-download-all')?.addEventListener('click', downloadAllPDFs);
85
  document.getElementById('btn-qr-modal')?.addEventListener('click', openQRModal);
86
-
87
- // View Switching
 
 
 
88
  document.querySelectorAll('.tab-btn').forEach(btn => {
89
  btn.addEventListener('click', () => switchTab(btn.dataset.tab));
90
  });
@@ -106,15 +108,16 @@ function setupGlobalListeners() {
106
  if(modal) modal.classList.add('hidden');
107
  });
108
  });
109
-
110
  // QR Actions
111
  document.getElementById('qr-url')?.addEventListener('input', updateQRPreview);
112
  document.getElementById('btn-qr-apply')?.addEventListener('click', applyQRAndDownload);
113
 
114
  // History
115
  document.addEventListener('show-history', showHistory);
116
-
117
- // Keyboard
 
 
118
  document.addEventListener('keydown', (e) => {
119
  if (e.ctrlKey || e.metaKey) {
120
  if (e.key === '=') { e.preventDefault(); zoomIn(); }
@@ -723,8 +726,64 @@ function updateTemplateZoom() {
723
  p.style.marginBottom = `${(1261 * (state.scale - 1))}px`;
724
  });
725
  }
726
-
727
  // --- Export & Generation ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
728
  async function downloadPDF() {
729
  if (state.data.length === 0) {
730
  toast('No data to export', 'error');
@@ -732,7 +791,7 @@ async function downloadPDF() {
732
  }
733
 
734
  loading(true, 'Generating PDF...');
735
- try {
736
  const data = state.data[state.currentIndex];
737
  const layout = await saveLayoutToStorage(); // Capture current layout
738
  const element = document.createElement('div');
@@ -783,7 +842,6 @@ async function downloadPDF() {
783
 
784
  await html2pdf().set(opt).from(element).save();
785
  document.body.removeChild(element);
786
-
787
  // Save layout to history
788
  const doc = {
789
  id: Date.now().toString(),
@@ -795,7 +853,7 @@ async function downloadPDF() {
795
  await saveDoc(doc);
796
 
797
  toast('PDF Downloaded & Layout Saved', 'success');
798
- } catch (e) {
799
  toast('Generation Failed', 'error');
800
  debugLog(e);
801
  } finally {
@@ -1016,7 +1074,6 @@ async function showHistory() {
1016
  }
1017
  document.getElementById('history-modal').classList.remove('hidden');
1018
  }
1019
-
1020
  // Utils
1021
  function rgbToHex(rgb) {
1022
  if (!rgb) return '#000000';
@@ -1024,4 +1081,131 @@ function rgbToHex(rgb) {
1024
  const rgbValues = rgb.match(/\d+/g);
1025
  if (!rgbValues) return '#000000';
1026
  return "#" + ((1 << 24) + (parseInt(rgbValues[0]) << 16) + (parseInt(rgbValues[1]) << 8) + parseInt(rgbValues[2])).toString(16).slice(1);
1027
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  document.getElementById('bg2-file')?.addEventListener('change', e => handleBgFile('2', e.target.files[0]));
77
  document.getElementById('json-file')?.addEventListener('change', handleJsonUpload);
78
  document.getElementById('pdf-upload')?.addEventListener('change', handlePdfUpload);
 
79
  // Sidebar Actions
80
  document.getElementById('btn-apply-bg')?.addEventListener('click', applyBg);
81
  document.getElementById('btn-load-sample')?.addEventListener('click', loadSample);
82
  document.getElementById('btn-download-pdf')?.addEventListener('click', downloadPDF);
83
  document.getElementById('btn-download-all')?.addEventListener('click', downloadAllPDFs);
84
  document.getElementById('btn-qr-modal')?.addEventListener('click', openQRModal);
85
+ document.getElementById('btn-export-layout')?.addEventListener('click', exportLayout);
86
+ document.getElementById('btn-save-layout')?.addEventListener('click', saveLayoutToDB);
87
+ document.getElementById('btn-new-layout')?.addEventListener('click', newLayout);
88
+ document.getElementById('layout-import')?.addEventListener('change', (e) => importLayout(e.target.files[0]));
89
+ // View Switching
90
  document.querySelectorAll('.tab-btn').forEach(btn => {
91
  btn.addEventListener('click', () => switchTab(btn.dataset.tab));
92
  });
 
108
  if(modal) modal.classList.add('hidden');
109
  });
110
  });
 
111
  // QR Actions
112
  document.getElementById('qr-url')?.addEventListener('input', updateQRPreview);
113
  document.getElementById('btn-qr-apply')?.addEventListener('click', applyQRAndDownload);
114
 
115
  // History
116
  document.addEventListener('show-history', showHistory);
117
+
118
+ // Layout Management
119
+ document.addEventListener('layout-saved', updateFileCount);
120
+ // Keyboard
121
  document.addEventListener('keydown', (e) => {
122
  if (e.ctrlKey || e.metaKey) {
123
  if (e.key === '=') { e.preventDefault(); zoomIn(); }
 
726
  p.style.marginBottom = `${(1261 * (state.scale - 1))}px`;
727
  });
728
  }
 
729
  // --- Export & Generation ---
730
+ async function exportLayout() {
731
+ const layout = await saveLayoutToStorage();
732
+ const name = document.getElementById('layout-name-input').value || layout.metadata.name || 'layout';
733
+ layout.metadata.name = name;
734
+
735
+ const blob = new Blob([JSON.stringify(layout, null, 2)], { type: 'application/json' });
736
+ const link = document.createElement('a');
737
+ link.href = URL.createObjectURL(blob);
738
+ link.download = `${name}_${new Date().getTime()}.json`;
739
+ link.click();
740
+
741
+ toast('Layout Exported', 'success');
742
+ }
743
+
744
+ async function importLayout(file) {
745
+ if (!file || !file.name.endsWith('.json')) {
746
+ toast('Please select a layout JSON file', 'error');
747
+ return;
748
+ }
749
+
750
+ document.getElementById('layout-import-name').textContent = file.name;
751
+
752
+ const reader = new FileReader();
753
+ reader.onload = async (e) => {
754
+ try {
755
+ const layout = JSON.parse(e.target.result);
756
+ if (!layout.fields || !layout.pages) {
757
+ throw new Error('Invalid layout format');
758
+ }
759
+
760
+ state.currentLayoutConfig = layout;
761
+ applyLayoutConfig(layout);
762
+
763
+ // Update form inputs
764
+ document.getElementById('layout-name-input').value = layout.metadata?.name || 'Imported Layout';
765
+
766
+ // Load background images if they are base64
767
+ layout.pages.forEach(pageConfig => {
768
+ if (pageConfig.background && pageConfig.background.startsWith('data:')) {
769
+ if (pageConfig.id === 'page1') {
770
+ state.bg1B64 = pageConfig.background;
771
+ } else if (pageConfig.id === 'page2') {
772
+ state.bg2B64 = pageConfig.background;
773
+ }
774
+ }
775
+ });
776
+
777
+ toast('Layout Imported Successfully', 'success');
778
+ state.isLayoutLoaded = true;
779
+ } catch (err) {
780
+ toast('Failed to import layout: ' + err.message, 'error');
781
+ debugLog(err);
782
+ }
783
+ };
784
+ reader.readAsText(file);
785
+ }
786
+
787
  async function downloadPDF() {
788
  if (state.data.length === 0) {
789
  toast('No data to export', 'error');
 
791
  }
792
 
793
  loading(true, 'Generating PDF...');
794
+ try {
795
  const data = state.data[state.currentIndex];
796
  const layout = await saveLayoutToStorage(); // Capture current layout
797
  const element = document.createElement('div');
 
842
 
843
  await html2pdf().set(opt).from(element).save();
844
  document.body.removeChild(element);
 
845
  // Save layout to history
846
  const doc = {
847
  id: Date.now().toString(),
 
853
  await saveDoc(doc);
854
 
855
  toast('PDF Downloaded & Layout Saved', 'success');
856
+ } catch (e) {
857
  toast('Generation Failed', 'error');
858
  debugLog(e);
859
  } finally {
 
1074
  }
1075
  document.getElementById('history-modal').classList.remove('hidden');
1076
  }
 
1077
  // Utils
1078
  function rgbToHex(rgb) {
1079
  if (!rgb) return '#000000';
 
1081
  const rgbValues = rgb.match(/\d+/g);
1082
  if (!rgbValues) return '#000000';
1083
  return "#" + ((1 << 24) + (parseInt(rgbValues[0]) << 16) + (parseInt(rgbValues[1]) << 8) + parseInt(rgbValues[2])).toString(16).slice(1);
1084
+ }
1085
+
1086
+ // --- Complete Usage Examples ---
1087
+ function showUsageExamples() {
1088
+ console.log('=== PDF Layout Wizard - Complete Usage Examples ===\n');
1089
+
1090
+ console.log('1. BASIC USAGE:');
1091
+ console.log(' - Load sample data: Click "Load Sample" in sidebar');
1092
+ console.log(' - Edit field positions: Toggle Design Mode (pencil icon in navbar)');
1093
+ console.log(' - Download PDF: Click "Download PDF" in sidebar');
1094
+
1095
+ console.log('\n2. ADVANCED FEATURES:');
1096
+ console.log(' ├─ Layout Management:');
1097
+ console.log(' │ • Export Layout: Design Mode → Export Layout');
1098
+ console.log(' │ • Import Layout: Sidebar → Import Layout');
1099
+ console.log(' │ • Save Layout: Design Mode → Save Layout to History');
1100
+ console.log(' │ • New Layout: Design Mode → New Layout');
1101
+ console.log(' ├─ Data Management:');
1102
+ console.log(' │ • JSON Import: Sidebar → Import JSON');
1103
+ console.log(' │ • Data Dashboard: Navbar → Database icon');
1104
+ console.log(' │ • Record Switching: Sidebar dropdown');
1105
+ console.log(' ├─ PDF Features:');
1106
+ console.log(' │ • PDF Upload: Sidebar → Import PDF');
1107
+ console.log(' │ • PDF Viewer: Tab switch → PDF Viewer');
1108
+ console.log(' │ • QR Integration: Navbar → Grid icon');
1109
+ console.log(' └─ Export Options:');
1110
+ console.log(' • Single PDF: Sidebar → Download PDF');
1111
+ console.log(' • Batch Export: Sidebar → Download All');
1112
+ console.log(' • Layout Export: Design Mode → Export Layout');
1113
+
1114
+ console.log('\n3. KEYBOARD SHORTCUTS:');
1115
+ console.log(' - Ctrl + S: Download PDF');
1116
+ console.log(' - Ctrl + =: Zoom In');
1117
+ console.log(' - Ctrl + -: Zoom Out');
1118
+ console.log(' - ESC: Close modals / Exit Design Mode');
1119
+
1120
+ console.log('\n4. DESIGN MODE WORKFLOW:');
1121
+ console.log(' Step 1: Click Design Mode button (pencil icon)');
1122
+ console.log(' Step 2: Click and drag fields to position');
1123
+ console.log(' Step 3: Select field to edit properties (size, color, weight)');
1124
+ console.log(' Step 4: Export layout for reuse');
1125
+ console.log(' Step 5: Exit Design Mode (ESC or button)');
1126
+
1127
+ console.log('\n5. DATA WORKFLOW:');
1128
+ console.log(' • JSON Format: Must be array of objects with numbered keys (1-12)');
1129
+ console.log(' • Dashboard: Open in new tab for full CRUD operations');
1130
+ console.log(' • Real-time Update: Dashboard changes reflect in main app');
1131
+
1132
+ console.log('\n6. PDF WORKFLOW:');
1133
+ console.log(' • Upload Base PDF: Use PDF as template');
1134
+ console.log(' • View Mode: Switch to PDF tab to preview');
1135
+ console.log(' • QR Addition: Add QR codes to existing PDF');
1136
+ console.log(' • Zoom Controls: Bottom HUD controls');
1137
+
1138
+ console.log('\n7. HISTORY & STORAGE:');
1139
+ console.log(' • Auto-save: All generations saved to history');
1140
+ console.log(' • Layout Persistence: Saved in browser IndexedDB');
1141
+ console.log(' • Restore: Click history items to restore');
1142
+ console.log(' • Export/Import: Share layouts across devices');
1143
+
1144
+ console.log('\n8. CUSTOMIZATION:');
1145
+ console.log(' • CSS Variables: Modify :root colors in style.css');
1146
+ console.log(' • Field Mapping: Edit fieldToKey object in script.js');
1147
+ console.log(' • Default Layouts: Create preset layouts');
1148
+ console.log(' • Branding: Update navbar title and icons');
1149
+ console.log(' • Theme: Toggle light/dark mode');
1150
+
1151
+ console.log('\n9. SAMPLE COMMANDS:');
1152
+ console.log(' loadSample() // Load demo data');
1153
+ console.log(' toggleDesignMode() // Toggle field editing');
1154
+ console.log(' exportLayout() // Save current layout');
1155
+ console.log(' newLayout() // Reset to blank layout');
1156
+ console.log(' showData(0) // Show first data record');
1157
+ console.log(' downloadPDF() // Generate PDF');
1158
+ console.log(' saveLayoutToDB() // Save layout to history');
1159
+ console.log(' importLayout(file) // Import layout JSON');
1160
+
1161
+ console.log('\n10. TROUBLESHOOTING:');
1162
+ console.log(' • Images not showing: Enable CORS in browser');
1163
+ console.log(' • PDF generation slow: Reduce image sizes');
1164
+ console.log(' • Layout not saving: Check browser storage limits');
1165
+ console.log(' • QR codes broken: Verify URL includes protocol');
1166
+ console.log(' • Fields misaligned: Check zoom level is 100%');
1167
+
1168
+ console.log('\n=== Ready to use! Check dashboard.html for data management ===');
1169
+ }
1170
+
1171
+ // Auto-show usage on first load
1172
+ if (!localStorage.getItem('pdf_wizard_usage_shown')) {
1173
+ setTimeout(showUsageExamples, 2000);
1174
+ localStorage.setItem('pdf_wizard_usage_shown', 'true');
1175
+ }
1176
+
1177
+ // Example of programmatic usage:
1178
+ /*
1179
+ // 1. Create custom data
1180
+ const customData = [{
1181
+ "1": "John Doe",
1182
+ "2": "Senior Developer",
1183
+ "3": "Engineering",
1184
+ "4": "https://example.com/photo.jpg",
1185
+ "5": "2024-01-15",
1186
+ "6": "2025-01-15",
1187
+ "7": "Tech Corp",
1188
+ "8": "San Francisco",
1189
+ "9": "94105",
1190
+ "10": "USA",
1191
+ "11": "EMP001",
1192
+ "12": "DOC2024001"
1193
+ }];
1194
+
1195
+ // 2. Load data programmatically
1196
+ state.data = customData;
1197
+ populateDataDropdown();
1198
+ showData(0);
1199
+
1200
+ // 3. Enable design mode
1201
+ toggleDesignMode();
1202
+
1203
+ // 4. Export layout programmatically
1204
+ setTimeout(() => {
1205
+ document.getElementById('layout-name-input').value = 'My Custom Layout';
1206
+ exportLayout();
1207
+ }, 5000);
1208
+
1209
+ // 5. Generate PDF with data
1210
+ setTimeout(downloadPDF, 8000);
1211
+ */
style.css CHANGED
@@ -300,7 +300,6 @@ body.design-mode .page:hover::before {
300
  4%,60% { transform: translate(-2px, 0) skew(0deg); }
301
  62% { transform: translate(0, 0) skew(5deg); }
302
  }
303
-
304
  /* Creative field styling */
305
  .field {
306
  font-family: 'Comic Neue', cursive;
@@ -313,6 +312,114 @@ body.design-mode .page:hover::before {
313
  animation: rainbow 3s ease infinite;
314
  }
315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  /* Make the template view more engaging */
317
  #template-view {
318
  perspective: 1000px;
 
300
  4%,60% { transform: translate(-2px, 0) skew(0deg); }
301
  62% { transform: translate(0, 0) skew(5deg); }
302
  }
 
303
  /* Creative field styling */
304
  .field {
305
  font-family: 'Comic Neue', cursive;
 
312
  animation: rainbow 3s ease infinite;
313
  }
314
 
315
+ /* Design Mode Enhancement */
316
+ body.design-mode .field {
317
+ cursor: grab;
318
+ border: 1px dashed #3b82f6 !important;
319
+ background: rgba(59, 130, 246, 0.1);
320
+ min-width: 20px;
321
+ min-height: 1em;
322
+ }
323
+
324
+ body.design-mode .field:hover {
325
+ background: rgba(59, 130, 246, 0.2);
326
+ }
327
+
328
+ body.design-mode .field.selected {
329
+ border: 2px solid #10b981 !important;
330
+ background: rgba(16, 185, 129, 0.1);
331
+ z-index: 50;
332
+ }
333
+
334
+ body.design-mode .page {
335
+ box-shadow: 0 0 0 1px #3b82f6;
336
+ }
337
+
338
+ /* Layout Management Styles */
339
+ #sidebar-design .form-control {
340
+ @apply w-full px-3 py-2 border border-slate-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-700 text-slate-800 dark:text-slate-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors;
341
+ }
342
+
343
+ #sidebar-design .form-control:focus {
344
+ @apply border-blue-500 ring-2 ring-blue-500;
345
+ }
346
+
347
+ /* Import/Export buttons */
348
+ #sidebar-design label[for="layout-import"] {
349
+ @apply cursor-pointer hover:bg-blue-50 dark:hover:bg-blue-900/30 transition-colors;
350
+ }
351
+
352
+ /* Property controls styling */
353
+ #field-controls .form-group {
354
+ @apply mb-4;
355
+ }
356
+
357
+ #field-controls label {
358
+ @apply block text-xs font-bold text-slate-500 dark:text-slate-400 uppercase mb-2;
359
+ }
360
+
361
+ #field-controls input[type="number"],
362
+ #field-controls select,
363
+ #field-controls input[type="color"] {
364
+ @apply w-full;
365
+ }
366
+
367
+ /* Layout name input */
368
+ #layout-name-input {
369
+ @apply font-mono text-sm;
370
+ }
371
+
372
+ /* Success feedback for saved layouts */
373
+ @keyframes saved-pulse {
374
+ 0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); }
375
+ 70% { box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); }
376
+ 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
377
+ }
378
+
379
+ .saved-indicator {
380
+ animation: saved-pulse 1s;
381
+ }
382
+
383
+ /* Enhanced field selection */
384
+ body.design-mode .field.selected::before {
385
+ content: '';
386
+ position: absolute;
387
+ top: -4px;
388
+ left: -4px;
389
+ right: -4px;
390
+ bottom: -4px;
391
+ border: 2px solid #10b981;
392
+ border-radius: 4px;
393
+ pointer-events: none;
394
+ animation: selected-glow 1s ease-in-out infinite alternate;
395
+ }
396
+
397
+ @keyframes selected-glow {
398
+ from { opacity: 0.5; }
399
+ to { opacity: 1; }
400
+ }
401
+
402
+ /* Make the design panel more prominent */
403
+ #sidebar-design {
404
+ background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
405
+ }
406
+
407
+ [data-theme="dark"] #sidebar-design {
408
+ background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
409
+ }
410
+
411
+ /* Usage examples styling */
412
+ .usage-example {
413
+ @apply bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4 mb-4;
414
+ }
415
+
416
+ .usage-example h4 {
417
+ @apply font-bold text-blue-800 dark:text-blue-200 mb-2;
418
+ }
419
+
420
+ .usage-example code {
421
+ @apply bg-slate-100 dark:bg-slate-800 px-2 py-1 rounded text-sm font-mono;
422
+ }
423
  /* Make the template view more engaging */
424
  #template-view {
425
  perspective: 1000px;