zebra-day 1.0.2__py3-none-any.whl → 2.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. zebra_day/__init__.py +7 -2
  2. zebra_day/_version.py +1 -0
  3. zebra_day/cli/__init__.py +80 -30
  4. zebra_day/cli/cognito.py +15 -9
  5. zebra_day/cli/gui.py +101 -13
  6. zebra_day/cli/printer.py +34 -27
  7. zebra_day/cli/template.py +19 -15
  8. zebra_day/cmd_mgr.py +3 -6
  9. zebra_day/docs/gx420d-gx430d-ug-en.pdf +0 -0
  10. zebra_day/docs/hardware_config_guide.md +149 -0
  11. zebra_day/docs/programatic_guide.md +181 -0
  12. zebra_day/docs/qln420_zebra_manual.pdf +0 -0
  13. zebra_day/docs/uid_screed_light.md +38 -0
  14. zebra_day/docs/zd620-zd420-ug-en.pdf +0 -0
  15. zebra_day/docs/zebra_day_ui_guide.md +194 -0
  16. zebra_day/etc/printer_config.json +5 -11
  17. zebra_day/etc/printer_config.template.json +5 -11
  18. zebra_day/etc/tmp_printers120.json +10 -0
  19. zebra_day/etc/tmp_printers139.json +10 -0
  20. zebra_day/etc/tmp_printers145.json +10 -0
  21. zebra_day/etc/tmp_printers147.json +10 -0
  22. zebra_day/etc/tmp_printers207.json +10 -0
  23. zebra_day/etc/tmp_printers34.json +10 -0
  24. zebra_day/etc/tmp_printers389.json +10 -0
  25. zebra_day/etc/tmp_printers398.json +10 -0
  26. zebra_day/etc/tmp_printers437.json +10 -0
  27. zebra_day/etc/tmp_printers439.json +10 -0
  28. zebra_day/etc/tmp_printers440.json +10 -0
  29. zebra_day/etc/tmp_printers469.json +10 -0
  30. zebra_day/etc/tmp_printers485.json +10 -0
  31. zebra_day/etc/tmp_printers508.json +10 -0
  32. zebra_day/etc/tmp_printers531.json +10 -0
  33. zebra_day/etc/tmp_printers540.json +10 -0
  34. zebra_day/etc/tmp_printers542.json +10 -0
  35. zebra_day/etc/tmp_printers543.json +10 -0
  36. zebra_day/etc/tmp_printers552.json +10 -0
  37. zebra_day/etc/tmp_printers715.json +10 -0
  38. zebra_day/etc/tmp_printers835.json +10 -0
  39. zebra_day/etc/tmp_printers842.json +10 -0
  40. zebra_day/etc/tmp_printers931.json +10 -0
  41. zebra_day/etc/tmp_printers969.json +10 -0
  42. zebra_day/etc/tmp_printers972.json +10 -0
  43. zebra_day/exceptions.py +1 -1
  44. zebra_day/files/blank_preview.png +0 -0
  45. zebra_day/files/corners_20cmX30cm_preview.png +0 -0
  46. zebra_day/files/corners_smallTube_preview.png +0 -0
  47. zebra_day/files/generic_2inX1in_preview.png +0 -0
  48. zebra_day/files/test_png_12020.png +0 -0
  49. zebra_day/files/test_png_12352.png +0 -0
  50. zebra_day/files/test_png_15472.png +0 -0
  51. zebra_day/files/test_png_24493.png +0 -0
  52. zebra_day/files/test_png_2897.png +0 -0
  53. zebra_day/files/test_png_30069.png +0 -0
  54. zebra_day/files/test_png_31690.png +0 -0
  55. zebra_day/files/test_png_33804.png +0 -0
  56. zebra_day/files/test_png_34737.png +0 -0
  57. zebra_day/files/test_png_4161.png +0 -0
  58. zebra_day/files/test_png_44748.png +0 -0
  59. zebra_day/files/test_png_4635.png +0 -0
  60. zebra_day/files/test_png_47791.png +0 -0
  61. zebra_day/files/test_png_47799.png +0 -0
  62. zebra_day/files/test_png_55588.png +0 -0
  63. zebra_day/files/test_png_56349.png +0 -0
  64. zebra_day/files/test_png_58809.png +0 -0
  65. zebra_day/files/test_png_5936.png +0 -0
  66. zebra_day/files/test_png_64110.png +0 -0
  67. zebra_day/files/test_png_64891.png +0 -0
  68. zebra_day/files/test_png_67242.png +0 -0
  69. zebra_day/files/test_png_69002.png +0 -0
  70. zebra_day/files/test_png_70065.png +0 -0
  71. zebra_day/files/test_png_72366.png +0 -0
  72. zebra_day/files/test_png_77793.png +0 -0
  73. zebra_day/files/test_png_89893.png +0 -0
  74. zebra_day/files/test_png_9572.png +0 -0
  75. zebra_day/files/tube_20mmX30mmA_preview.png +0 -0
  76. zebra_day/imgs/.hold +0 -0
  77. zebra_day/imgs/bar_ltpurp.png +0 -0
  78. zebra_day/imgs/bar_purp.png +0 -0
  79. zebra_day/imgs/bar_purp3.png +0 -0
  80. zebra_day/imgs/bar_red.png +0 -0
  81. zebra_day/imgs/legacy/UBC_gantt_chart.png +0 -0
  82. zebra_day/imgs/legacy/gx420d_network_config.png +0 -0
  83. zebra_day/imgs/legacy/gx420d_printer_config.png +0 -0
  84. zebra_day/imgs/legacy/ngrok.png +0 -0
  85. zebra_day/imgs/legacy/printer_details.png +0 -0
  86. zebra_day/imgs/legacy/quick_start_test_label.png +0 -0
  87. zebra_day/imgs/legacy/quick_start_test_label2.png +0 -0
  88. zebra_day/imgs/legacy/zd620_network_config.png +0 -0
  89. zebra_day/imgs/legacy/zd620_printer_config.png +0 -0
  90. zebra_day/imgs/legacy/zday_quick_gui.png +0 -0
  91. zebra_day/imgs/legacy/zebra_day_alt_css_dog.png +0 -0
  92. zebra_day/imgs/legacy/zebra_day_alt_css_flower.png +0 -0
  93. zebra_day/imgs/legacy/zebra_day_alt_css_main.png +0 -0
  94. zebra_day/imgs/legacy/zebra_day_available_zpl_templates.png +0 -0
  95. zebra_day/imgs/legacy/zebra_day_bkup_pconfig.png +0 -0
  96. zebra_day/imgs/legacy/zebra_day_home.png +0 -0
  97. zebra_day/imgs/legacy/zebra_day_manual_print.png +0 -0
  98. zebra_day/imgs/legacy/zebra_day_printer_fleet_json.png +0 -0
  99. zebra_day/imgs/legacy/zebra_day_quick_ex.png +0 -0
  100. zebra_day/imgs/legacy/zebra_day_zpl_template_IRLa.png +0 -0
  101. zebra_day/imgs/legacy/zebra_day_zpl_template_IRLb.png +0 -0
  102. zebra_day/imgs/ui_api_docs.png +0 -0
  103. zebra_day/imgs/ui_config.png +0 -0
  104. zebra_day/imgs/ui_dashboard.png +0 -0
  105. zebra_day/imgs/ui_print_request.png +0 -0
  106. zebra_day/imgs/ui_printers.png +0 -0
  107. zebra_day/imgs/ui_templates.png +0 -0
  108. zebra_day/logging_config.py +4 -9
  109. zebra_day/mkcert.py +157 -0
  110. zebra_day/paths.py +1 -2
  111. zebra_day/print_mgr.py +261 -185
  112. zebra_day/templates/modern/config.html +7 -0
  113. zebra_day/templates/modern/config_backups.html +59 -0
  114. zebra_day/templates/modern/config_editor.html +95 -0
  115. zebra_day/templates/modern/config_new.html +93 -0
  116. zebra_day/templates/modern/print_request.html +70 -8
  117. zebra_day/templates/modern/printer_detail.html +161 -34
  118. zebra_day/templates/modern/printers.html +17 -6
  119. zebra_day/templates/modern/template_editor.html +7 -4
  120. zebra_day/web/__init__.py +1 -1
  121. zebra_day/web/app.py +99 -17
  122. zebra_day/web/auth.py +17 -15
  123. zebra_day/web/middleware.py +8 -5
  124. zebra_day/web/routers/__init__.py +0 -1
  125. zebra_day/web/routers/api.py +330 -31
  126. zebra_day/web/routers/ui.py +174 -591
  127. zebra_day/zpl_renderer.py +45 -34
  128. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/METADATA +144 -74
  129. zebra_day-2.1.4.dist-info/RECORD +240 -0
  130. zebra_day/bin/fetch_zebra_config.py +0 -15
  131. zebra_day/bin/generate_coord_grid_zpl.py +0 -50
  132. zebra_day/bin/print_zpl_from_file.py +0 -21
  133. zebra_day/bin/probe_new_label_dimensions.py +0 -75
  134. zebra_day/bin/scan_for_networed_zebra_printers.py +0 -23
  135. zebra_day/bin/scan_for_networed_zebra_printers_arp_scan.sh +0 -1
  136. zebra_day/bin/scan_for_networed_zebra_printers_curl.sh +0 -30
  137. zebra_day/bin/zserve.py +0 -1062
  138. zebra_day/templates/base.html +0 -36
  139. zebra_day/templates/bpr.html +0 -72
  140. zebra_day/templates/build_new_config.html +0 -36
  141. zebra_day/templates/build_print_request.html +0 -32
  142. zebra_day/templates/chg_ui_style.html +0 -19
  143. zebra_day/templates/edit_template.html +0 -128
  144. zebra_day/templates/edit_zpl.html +0 -37
  145. zebra_day/templates/index.html +0 -82
  146. zebra_day/templates/legacy/base.html +0 -37
  147. zebra_day/templates/legacy/bpr.html +0 -72
  148. zebra_day/templates/legacy/build_new_config.html +0 -36
  149. zebra_day/templates/legacy/build_print_request.html +0 -32
  150. zebra_day/templates/legacy/chg_ui_style.html +0 -19
  151. zebra_day/templates/legacy/edit_template.html +0 -128
  152. zebra_day/templates/legacy/edit_zpl.html +0 -37
  153. zebra_day/templates/legacy/index.html +0 -82
  154. zebra_day/templates/legacy/list_prior_configs.html +0 -24
  155. zebra_day/templates/legacy/print_result.html +0 -30
  156. zebra_day/templates/legacy/printer_details.html +0 -25
  157. zebra_day/templates/legacy/printer_status.html +0 -70
  158. zebra_day/templates/legacy/save_result.html +0 -17
  159. zebra_day/templates/legacy/send_print_request.html +0 -34
  160. zebra_day/templates/legacy/simple_print.html +0 -94
  161. zebra_day/templates/legacy/view_pstation_json.html +0 -29
  162. zebra_day/templates/list_prior_configs.html +0 -24
  163. zebra_day/templates/print_result.html +0 -30
  164. zebra_day/templates/printer_details.html +0 -25
  165. zebra_day/templates/printer_status.html +0 -70
  166. zebra_day/templates/save_result.html +0 -17
  167. zebra_day/templates/send_print_request.html +0 -34
  168. zebra_day/templates/simple_print.html +0 -94
  169. zebra_day/templates/view_pstation_json.html +0 -29
  170. zebra_day-1.0.2.dist-info/RECORD +0 -179
  171. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/WHEEL +0 -0
  172. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/entry_points.txt +0 -0
  173. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/licenses/LICENSE +0 -0
  174. {zebra_day-1.0.2.dist-info → zebra_day-2.1.4.dist-info}/top_level.txt +0 -0
@@ -81,6 +81,13 @@
81
81
  <i class="fas fa-external-link-alt"></i> View Full
82
82
  </a>
83
83
  </div>
84
+
85
+ <!-- Config File Path -->
86
+ <div class="mb-md" style="padding: 0.75rem 1rem; background: var(--color-gray-700); border-radius: var(--radius-sm); font-family: 'JetBrains Mono', monospace; font-size: 0.875rem;">
87
+ <span class="text-muted"><i class="fas fa-file-alt"></i> Loaded from:</span>
88
+ <code style="color: var(--color-highlight); margin-left: 0.5rem;">{{ config_file_path | default('Unknown') }}</code>
89
+ </div>
90
+
84
91
  {% if config_summary %}
85
92
  <div class="grid grid-4 mb-lg">
86
93
  <div class="stat-card">
@@ -0,0 +1,59 @@
1
+ {% extends "modern/base.html" %}
2
+
3
+ {% block title %}Config Backups - Zebra Day{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="page-header">
7
+ <h1 class="page-title">Configuration Backups</h1>
8
+ <div class="header-actions">
9
+ <a href="/config" class="btn btn-outline"><i class="fas fa-arrow-left"></i> Back</a>
10
+ </div>
11
+ </div>
12
+
13
+ <div class="card">
14
+ <div class="card-header">
15
+ <h3 class="card-title"><i class="fas fa-history"></i> Saved Configuration Files</h3>
16
+ <span class="badge">{{ backup_files | length }} backups</span>
17
+ </div>
18
+
19
+ <p class="text-muted mb-md" style="padding: 0 1rem;">
20
+ To restore a backup, download the file, open in a text editor, copy the contents,
21
+ then paste into the <a href="/config/edit">config editor</a> and save.
22
+ </p>
23
+
24
+ {% if backup_files %}
25
+ <div class="table-container">
26
+ <table class="table">
27
+ <thead>
28
+ <tr>
29
+ <th>Filename</th>
30
+ <th>Actions</th>
31
+ </tr>
32
+ </thead>
33
+ <tbody>
34
+ {% for file in backup_files %}
35
+ <tr>
36
+ <td>
37
+ <i class="fas fa-file-code text-muted"></i>
38
+ {{ file }}
39
+ </td>
40
+ <td>
41
+ <a href="/etc/old_printer_config/{{ file }}" class="btn btn-sm btn-outline" download>
42
+ <i class="fas fa-download"></i> Download
43
+ </a>
44
+ </td>
45
+ </tr>
46
+ {% endfor %}
47
+ </tbody>
48
+ </table>
49
+ </div>
50
+ {% else %}
51
+ <div class="empty-state">
52
+ <i class="fas fa-history"></i>
53
+ <p>No backup files found</p>
54
+ <small class="text-muted">Backups are created automatically when you save configuration changes</small>
55
+ </div>
56
+ {% endif %}
57
+ </div>
58
+ {% endblock %}
59
+
@@ -0,0 +1,95 @@
1
+ {% extends "modern/base.html" %}
2
+
3
+ {% block title %}{{ title }} - Zebra Day{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="page-header">
7
+ <h1 class="page-title">{{ title }}</h1>
8
+ <div class="header-actions">
9
+ <a href="/config" class="btn btn-outline"><i class="fas fa-arrow-left"></i> Back</a>
10
+ {% if mode == 'edit' %}
11
+ <button type="button" class="btn btn-outline" onclick="resetConfig()"><i class="fas fa-undo"></i> Reset</button>
12
+ <button type="button" class="btn btn-outline" onclick="clearConfig()"><i class="fas fa-trash"></i> Clear</button>
13
+ {% endif %}
14
+ </div>
15
+ </div>
16
+
17
+ {% if error_msg %}
18
+ <div class="alert alert-error mb-lg">
19
+ <i class="fas fa-exclamation-circle"></i> {{ error_msg }}
20
+ </div>
21
+ {% endif %}
22
+
23
+ <div class="card">
24
+ <div class="card-header">
25
+ <h3 class="card-title"><i class="fas fa-file-code"></i> Printer Configuration JSON</h3>
26
+ {% if mode == 'edit' %}
27
+ <span class="badge badge-warning">Editing</span>
28
+ {% else %}
29
+ <span class="badge badge-info">Read Only</span>
30
+ {% endif %}
31
+ </div>
32
+ <form method="post" action="/config/save">
33
+ <div class="form-group">
34
+ <textarea name="json_data" id="json-editor" class="form-control code-editor" rows="30" {% if mode != 'edit' %}readonly{% endif %}>{{ config_json }}</textarea>
35
+ </div>
36
+ {% if mode == 'edit' %}
37
+ <div class="form-actions">
38
+ <button type="submit" class="btn btn-primary" onclick="showLoading('Saving configuration...')">
39
+ <i class="fas fa-save"></i> Save Configuration
40
+ </button>
41
+ </div>
42
+ {% endif %}
43
+ </form>
44
+ </div>
45
+
46
+ <style>
47
+ .code-editor {
48
+ font-family: 'JetBrains Mono', 'Consolas', monospace;
49
+ font-size: 0.875rem;
50
+ line-height: 1.5;
51
+ background: var(--color-gray-700);
52
+ border: 1px solid var(--color-gray-600);
53
+ padding: 1rem;
54
+ resize: vertical;
55
+ min-height: 400px;
56
+ }
57
+ .code-editor:read-only {
58
+ background: var(--color-gray-800);
59
+ cursor: default;
60
+ }
61
+ .form-actions {
62
+ display: flex;
63
+ gap: 1rem;
64
+ padding: 1rem;
65
+ border-top: 1px solid var(--color-gray-600);
66
+ }
67
+ </style>
68
+
69
+ <script>
70
+ async function resetConfig() {
71
+ if (confirm('Reset configuration to template defaults? This will backup the current config.')) {
72
+ showLoading('Resetting configuration...');
73
+ window.location.href = '/config/reset';
74
+ }
75
+ }
76
+
77
+ async function clearConfig() {
78
+ if (confirm('Clear all printer configuration? This will backup the current config.')) {
79
+ showLoading('Clearing configuration...');
80
+ window.location.href = '/config/clear';
81
+ }
82
+ }
83
+
84
+ // Auto-format JSON on blur
85
+ document.getElementById('json-editor')?.addEventListener('blur', function() {
86
+ try {
87
+ const parsed = JSON.parse(this.value);
88
+ this.value = JSON.stringify(parsed, null, 4);
89
+ } catch (e) {
90
+ // Invalid JSON, leave as-is
91
+ }
92
+ });
93
+ </script>
94
+ {% endblock %}
95
+
@@ -0,0 +1,93 @@
1
+ {% extends "modern/base.html" %}
2
+
3
+ {% block title %}New Configuration - Zebra Day{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="page-header">
7
+ <h1 class="page-title">Build New Configuration</h1>
8
+ <div class="header-actions">
9
+ <a href="/config" class="btn btn-outline"><i class="fas fa-arrow-left"></i> Back</a>
10
+ </div>
11
+ </div>
12
+
13
+ <div class="grid grid-2">
14
+ <!-- Network Scan -->
15
+ <div class="card">
16
+ <div class="card-header">
17
+ <h3 class="card-title"><i class="fas fa-wifi"></i> Scan Network for Printers</h3>
18
+ </div>
19
+ <form action="/config/scan" method="get">
20
+ <div class="form-group">
21
+ <label class="form-label">IP Range Prefix</label>
22
+ <input type="text" name="ip_stub" class="form-control" value="{{ ip_root }}" placeholder="192.168.1">
23
+ <small class="text-muted">Enter the first three octets of your network</small>
24
+ </div>
25
+ <div class="form-group">
26
+ <label class="form-label">Lab Name</label>
27
+ <input type="text" name="lab" class="form-control" value="scan-results" placeholder="Enter lab name">
28
+ <small class="text-muted">Printers found will be added to this lab</small>
29
+ </div>
30
+ <div class="form-group">
31
+ <label class="form-label">Scan Wait Time</label>
32
+ <select name="scan_wait" class="form-control">
33
+ <option value="0.15">Fast (0.15s)</option>
34
+ <option value="0.25" selected>Normal (0.25s)</option>
35
+ <option value="0.5">Slow (0.5s)</option>
36
+ <option value="1.0">Very Slow (1.0s)</option>
37
+ </select>
38
+ <small class="text-muted">Longer wait times find more printers on slow networks</small>
39
+ </div>
40
+ <button type="submit" class="btn btn-primary" onclick="showLoading('Scanning network for Zebra printers...')">
41
+ <i class="fas fa-search"></i> Start Network Scan
42
+ </button>
43
+ </form>
44
+ </div>
45
+
46
+ <!-- Quick Actions -->
47
+ <div class="card">
48
+ <div class="card-header">
49
+ <h3 class="card-title"><i class="fas fa-bolt"></i> Quick Actions</h3>
50
+ </div>
51
+ <div class="quick-actions">
52
+ <a href="/config/reset" class="action-card" onclick="return confirm('Reset config to template defaults?')">
53
+ <i class="fas fa-undo"></i>
54
+ <div>
55
+ <strong>Reset to Template</strong>
56
+ <small class="text-muted d-block">Restore default configuration</small>
57
+ </div>
58
+ </a>
59
+ <a href="/config/clear" class="action-card" onclick="return confirm('Clear all printer configuration?')">
60
+ <i class="fas fa-trash"></i>
61
+ <div>
62
+ <strong>Clear Configuration</strong>
63
+ <small class="text-muted d-block">Remove all printers</small>
64
+ </div>
65
+ </a>
66
+ <a href="/config/edit" class="action-card">
67
+ <i class="fas fa-edit"></i>
68
+ <div>
69
+ <strong>Manual Edit</strong>
70
+ <small class="text-muted d-block">Edit JSON directly</small>
71
+ </div>
72
+ </a>
73
+ </div>
74
+
75
+ <hr class="my-md">
76
+
77
+ <h4 class="mb-md">Existing Labs</h4>
78
+ {% if labs %}
79
+ <div class="grid grid-3">
80
+ {% for lab in labs %}
81
+ <a href="/printers/{{ lab }}" class="action-card">
82
+ <i class="fas fa-building"></i>
83
+ <strong>{{ lab }}</strong>
84
+ </a>
85
+ {% endfor %}
86
+ </div>
87
+ {% else %}
88
+ <p class="text-muted">No labs configured yet. Run a network scan to discover printers.</p>
89
+ {% endif %}
90
+ </div>
91
+ </div>
92
+ {% endblock %}
93
+
@@ -96,9 +96,14 @@
96
96
  <a href="/" class="btn btn-outline">
97
97
  <i class="fas fa-arrow-left"></i> Cancel
98
98
  </a>
99
- <button type="submit" class="btn btn-primary btn-lg" onclick="showLoading('Sending print request...')">
100
- <i class="fas fa-print"></i> Print Label
101
- </button>
99
+ <div class="d-flex" style="gap: var(--spacing-md);">
100
+ <button type="button" class="btn btn-secondary btn-lg" onclick="downloadPNG()">
101
+ <i class="fas fa-download"></i> Download PNG
102
+ </button>
103
+ <button type="submit" class="btn btn-primary btn-lg" onclick="showLoading('Sending print request...')">
104
+ <i class="fas fa-print"></i> Print Label
105
+ </button>
106
+ </div>
102
107
  </div>
103
108
  </div>
104
109
  </form>
@@ -116,12 +121,16 @@
116
121
 
117
122
  printerSelect.innerHTML = '<option value="">Select a printer...</option>';
118
123
 
119
- if (lab && labsDict[lab]) {
120
- for (var printer in labsDict[lab]) {
124
+ if (lab && labsDict[lab] && labsDict[lab].printers) {
125
+ // v2 schema: printers are nested under 'printers' key
126
+ var printers = labsDict[lab].printers;
127
+ for (var printerId in printers) {
128
+ var printerInfo = printers[printerId];
121
129
  var option = document.createElement('option');
122
- option.value = printer;
123
- option.text = printer;
124
- if (printer === selectedPrinter) {
130
+ option.value = printerId;
131
+ // Display friendly name if available, otherwise use ID
132
+ option.text = printerInfo.printer_name || printerId;
133
+ if (printerId === selectedPrinter) {
125
134
  option.selected = true;
126
135
  }
127
136
  printerSelect.appendChild(option);
@@ -136,6 +145,59 @@
136
145
  updatePrinters();
137
146
  }
138
147
  });
148
+
149
+ async function downloadPNG() {
150
+ var form = document.getElementById('print-form');
151
+ var template = form.querySelector('select[name="label_zpl_style"]').value;
152
+
153
+ if (!template) {
154
+ showToast('error', 'Error', 'Please select a label template');
155
+ return;
156
+ }
157
+
158
+ showLoading('Rendering PNG...');
159
+
160
+ try {
161
+ var response = await fetch('/api/v1/render/png', {
162
+ method: 'POST',
163
+ headers: {'Content-Type': 'application/json'},
164
+ body: JSON.stringify({
165
+ template: template,
166
+ uid_barcode: form.querySelector('input[name="uid_barcode"]').value || '',
167
+ alt_a: form.querySelector('input[name="alt_a"]').value || '',
168
+ alt_b: form.querySelector('input[name="alt_b"]').value || '',
169
+ alt_c: form.querySelector('input[name="alt_c"]').value || '',
170
+ alt_d: form.querySelector('input[name="alt_d"]').value || '',
171
+ alt_e: form.querySelector('input[name="alt_e"]').value || '',
172
+ alt_f: form.querySelector('input[name="alt_f"]').value || ''
173
+ })
174
+ });
175
+
176
+ hideLoading();
177
+
178
+ if (!response.ok) {
179
+ var error = await response.json();
180
+ showToast('error', 'Render Failed', error.detail || 'Unknown error');
181
+ return;
182
+ }
183
+
184
+ // Download the PNG file
185
+ var blob = await response.blob();
186
+ var url = window.URL.createObjectURL(blob);
187
+ var a = document.createElement('a');
188
+ a.href = url;
189
+ a.download = template + '_label.png';
190
+ document.body.appendChild(a);
191
+ a.click();
192
+ window.URL.revokeObjectURL(url);
193
+ a.remove();
194
+
195
+ showToast('success', 'Success', 'PNG downloaded');
196
+ } catch (e) {
197
+ hideLoading();
198
+ showToast('error', 'Error', 'Failed to render PNG: ' + e.message);
199
+ }
200
+ }
139
201
  </script>
140
202
  {% endblock %}
141
203
 
@@ -5,7 +5,7 @@
5
5
  {% block content %}
6
6
  <div class="page-header">
7
7
  <h1 class="page-title">{{ printer_name }}</h1>
8
- <p class="page-subtitle">Printer details for {{ lab }}</p>
8
+ <p class="page-subtitle">Printer details for {{ lab_name | default(lab) }}</p>
9
9
  </div>
10
10
 
11
11
  <!-- Breadcrumb -->
@@ -14,7 +14,7 @@
14
14
  <span class="text-muted"> / </span>
15
15
  <a href="/printers">Printers</a>
16
16
  <span class="text-muted"> / </span>
17
- <a href="/printers/{{ lab }}">{{ lab }}</a>
17
+ <a href="/printers/{{ lab }}">{{ lab_name | default(lab) }}</a>
18
18
  <span class="text-muted"> / </span>
19
19
  <span>{{ printer_name }}</span>
20
20
  </div>
@@ -32,53 +32,149 @@
32
32
  </div>
33
33
  <table class="table" style="background: transparent; box-shadow: none;">
34
34
  <tbody>
35
- {% for key, value in printer_info.items() %}
36
35
  <tr>
37
- <td style="width: 40%;"><strong>{{ key }}</strong></td>
36
+ <td style="width: 40%;"><strong>Printer ID</strong></td>
37
+ <td>{{ printer_id }}</td>
38
+ </tr>
39
+ <tr>
40
+ <td><strong>Display Name</strong></td>
41
+ <td>{{ printer_info.printer_name | default('Not set', true) }}</td>
42
+ </tr>
43
+ <tr>
44
+ <td><strong>Location</strong></td>
45
+ <td>{{ printer_info.lab_location | default('Not set', true) }}</td>
46
+ </tr>
47
+ <tr>
48
+ <td><strong>Manufacturer</strong></td>
49
+ <td>{{ printer_info.manufacturer | default('zebra') | title }}</td>
50
+ </tr>
51
+ <tr>
52
+ <td><strong>Model</strong></td>
53
+ <td>{{ printer_info.model | default('Unknown') }}</td>
54
+ </tr>
55
+ <tr>
56
+ <td><strong>Serial</strong></td>
57
+ <td>{{ printer_info.serial | default('Unknown') }}</td>
58
+ </tr>
59
+ <tr>
60
+ <td><strong>IP Address</strong></td>
38
61
  <td>
39
- {% if key == 'ip_address' and value != 'dl_png' %}
40
- <a href="http://{{ value }}" target="_blank">{{ value }}</a>
41
- {% elif key == 'status' %}
42
- {% if value == 'online' %}
43
- <span class="badge badge-success">Online</span>
62
+ {% if printer_info.ip_address != 'dl_png' %}
63
+ <a href="http://{{ printer_info.ip_address }}" target="_blank">{{ printer_info.ip_address }}</a>
44
64
  {% else %}
45
- <span class="badge badge-error">Offline</span>
65
+ {{ printer_info.ip_address }}
46
66
  {% endif %}
47
- {% elif key == 'label_zpl_styles' %}
48
- {% for style in value %}
67
+ </td>
68
+ </tr>
69
+ <tr>
70
+ <td><strong>Print Method</strong></td>
71
+ <td>{{ printer_info.print_method | default('socket') }}</td>
72
+ </tr>
73
+ <tr>
74
+ <td><strong>Label Styles</strong></td>
75
+ <td>
76
+ {% for style in printer_info.label_zpl_styles %}
49
77
  <span class="badge badge-primary">{{ style }}</span>
50
78
  {% endfor %}
79
+ </td>
80
+ </tr>
81
+ <tr>
82
+ <td><strong>Default Label Style</strong></td>
83
+ <td>
84
+ {% if printer_info.default_label_style %}
85
+ <span class="badge badge-success">{{ printer_info.default_label_style }}</span>
51
86
  {% else %}
52
- {{ value }}
87
+ <span class="text-muted">{{ printer_info.label_zpl_styles[0] if printer_info.label_zpl_styles else 'None' }} (first in list)</span>
53
88
  {% endif %}
54
89
  </td>
55
90
  </tr>
56
- {% endfor %}
91
+ {% if printer_info.notes %}
92
+ <tr>
93
+ <td><strong>Notes</strong></td>
94
+ <td>{{ printer_info.notes }}</td>
95
+ </tr>
96
+ {% endif %}
57
97
  </tbody>
58
98
  </table>
59
99
  </div>
60
-
61
- <!-- Quick Actions -->
100
+
101
+ <!-- Edit Printer Settings -->
62
102
  <div class="card">
63
103
  <div class="card-header">
64
- <h3 class="card-title">Quick Actions</h3>
65
- </div>
66
- <div class="quick-actions">
67
- <a href="/print?lab={{ lab }}&printer={{ printer_name }}" class="action-card">
68
- <i class="fas fa-print"></i>
69
- <div>
70
- <strong>Print Label</strong>
71
- <small class="text-muted d-block">Send a print request</small>
72
- </div>
73
- </a>
74
- <a href="/templates" class="action-card">
75
- <i class="fas fa-file-code"></i>
76
- <div>
77
- <strong>View Templates</strong>
78
- <small class="text-muted d-block">Available label styles</small>
79
- </div>
80
- </a>
104
+ <h3 class="card-title">Edit Settings</h3>
81
105
  </div>
106
+ <form id="printer-edit-form" class="form-group">
107
+ <div class="form-group">
108
+ <label class="form-label">Display Name</label>
109
+ <input type="text" name="printer_name" class="form-control"
110
+ value="{{ printer_info.printer_name | default('', true) }}"
111
+ placeholder="e.g., Lab 3 - Bench A">
112
+ <small class="text-muted">User-friendly name for this printer</small>
113
+ </div>
114
+ <div class="form-group">
115
+ <label class="form-label">Location</label>
116
+ {% if available_locations %}
117
+ <select name="lab_location" class="form-control">
118
+ <option value="">-- Select Location --</option>
119
+ {% for loc in available_locations %}
120
+ <option value="{{ loc }}" {% if printer_info.lab_location == loc %}selected{% endif %}>{{ loc }}</option>
121
+ {% endfor %}
122
+ </select>
123
+ {% else %}
124
+ <input type="text" name="lab_location" class="form-control"
125
+ value="{{ printer_info.lab_location | default('', true) }}"
126
+ placeholder="No locations defined for this lab">
127
+ <small class="text-muted">Define locations at the lab level first</small>
128
+ {% endif %}
129
+ </div>
130
+ <div class="form-group">
131
+ <label class="form-label">Default Label Style</label>
132
+ <select name="default_label_style" class="form-control">
133
+ <option value="">-- Use first in list --</option>
134
+ {% for style in printer_info.label_zpl_styles %}
135
+ <option value="{{ style }}" {% if printer_info.default_label_style == style %}selected{% endif %}>{{ style }}</option>
136
+ {% endfor %}
137
+ </select>
138
+ <small class="text-muted">Style used when printing without specifying a template</small>
139
+ </div>
140
+ <div class="form-group">
141
+ <label class="form-label">Notes</label>
142
+ <textarea name="notes" class="form-control" rows="2" placeholder="Optional notes">{{ printer_info.notes | default('', true) }}</textarea>
143
+ </div>
144
+ <button type="submit" class="btn btn-primary">
145
+ <i class="fas fa-save"></i> Save Changes
146
+ </button>
147
+ </form>
148
+ </div>
149
+ </div>
150
+
151
+ <!-- Quick Actions -->
152
+ <div class="card mt-lg">
153
+ <div class="card-header">
154
+ <h3 class="card-title">Quick Actions</h3>
155
+ </div>
156
+ <div class="grid grid-3">
157
+ <a href="/print?lab={{ lab }}&printer={{ printer_id }}" class="action-card">
158
+ <i class="fas fa-print"></i>
159
+ <div>
160
+ <strong>Print Label</strong>
161
+ <small class="text-muted d-block">Send a print request</small>
162
+ </div>
163
+ </a>
164
+ <a href="/templates" class="action-card">
165
+ <i class="fas fa-file-code"></i>
166
+ <div>
167
+ <strong>View Templates</strong>
168
+ <small class="text-muted d-block">Available label styles</small>
169
+ </div>
170
+ </a>
171
+ <a href="/printers/{{ lab }}" class="action-card">
172
+ <i class="fas fa-arrow-left"></i>
173
+ <div>
174
+ <strong>Back to Lab</strong>
175
+ <small class="text-muted d-block">View all printers</small>
176
+ </div>
177
+ </a>
82
178
  </div>
83
179
  </div>
84
180
 
@@ -103,7 +199,7 @@
103
199
  <p class="text-muted mb-lg">Send a test label to verify the printer is working correctly.</p>
104
200
  <div class="grid grid-3">
105
201
  {% for style in printer_info.label_zpl_styles[:6] %}
106
- <a href="/print?lab={{ lab }}&printer={{ printer_name }}&template={{ style }}&test=1" class="action-card">
202
+ <a href="/print?lab={{ lab }}&printer={{ printer_id }}&template={{ style }}&test=1" class="action-card">
107
203
  <i class="fas fa-tag"></i>
108
204
  <div>
109
205
  <strong>{{ style }}</strong>
@@ -113,5 +209,36 @@
113
209
  {% endfor %}
114
210
  </div>
115
211
  </div>
212
+
213
+ <script>
214
+ document.getElementById('printer-edit-form').addEventListener('submit', async (e) => {
215
+ e.preventDefault();
216
+ const form = e.target;
217
+ const data = {
218
+ printer_name: form.printer_name.value || null,
219
+ lab_location: form.lab_location.value || null,
220
+ default_label_style: form.default_label_style.value || null,
221
+ notes: form.notes.value || ''
222
+ };
223
+
224
+ try {
225
+ const response = await fetch('/api/v1/labs/{{ lab }}/printers/{{ printer_id }}', {
226
+ method: 'PATCH',
227
+ headers: { 'Content-Type': 'application/json' },
228
+ body: JSON.stringify(data)
229
+ });
230
+
231
+ if (response.ok) {
232
+ showToast('success', 'Saved', 'Printer settings updated successfully');
233
+ setTimeout(() => window.location.reload(), 1000);
234
+ } else {
235
+ const err = await response.json();
236
+ showToast('error', 'Error', err.detail || 'Failed to save settings');
237
+ }
238
+ } catch (err) {
239
+ showToast('error', 'Error', 'Network error: ' + err.message);
240
+ }
241
+ });
242
+ </script>
116
243
  {% endblock %}
117
244