Fix dualpath propagation, getShopfloorPCs filtering, USB management, and printer features
- Fix dualpath PC propagation direction (Equipment->PC) in api.asp and db_helpers.asp - Fix early exit in CreatePCMachineRelationship preventing propagation - Fix getShopfloorPCs to filter machinetypeid IN (33,34,35) instead of >= 33 - Fix getShopfloorPCs to show equipment numbers via GROUP_CONCAT subquery - Add detailed PropagateDP logging for dualpath debugging - Default "Show on Shopfloor Dashboard" checkbox to checked in addnotification.asp - Add USB label batch printing, single USB labels, and USB history pages - Add printer supplies tracking and toner report enhancements - Add uptime map visualization page - Add dashboard/lobby display SQL migration - Update CLAUDE.md with IIS 401 workaround documentation - Update TODO.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
329
usblabelbatch.asp
Normal file
329
usblabelbatch.asp
Normal file
@@ -0,0 +1,329 @@
|
||||
<%@ Language=VBScript %>
|
||||
<%
|
||||
Option Explicit
|
||||
Dim objConn, rs
|
||||
%>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--#include file="./includes/sql.asp"-->
|
||||
<title>Batch Print USB Barcode Labels</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
|
||||
<style>
|
||||
/* ULINE S-20135: 8.5x11 sheet, 3x3 labels, 2 cols x 3 rows */
|
||||
/* Each 3x3 label holds 3x4 grid of 1x0.75 mini-labels (12 per cell) */
|
||||
@page { size: letter; margin: 0; }
|
||||
body { font-family: Arial, sans-serif; background: #f0f0f0; margin: 0; padding: 20px; }
|
||||
|
||||
.no-print { margin-bottom: 20px; }
|
||||
.controls { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
|
||||
.controls h3 { margin-top: 0; }
|
||||
.print-btn { padding: 10px 30px; font-size: 16px; cursor: pointer; background: #667eea; color: white; border: none; border-radius: 5px; margin-right: 10px; }
|
||||
.print-btn:disabled { background: #ccc; cursor: not-allowed; }
|
||||
.clear-btn { padding: 10px 20px; font-size: 14px; cursor: pointer; background: #dc3545; color: white; border: none; border-radius: 5px; margin-right: 10px; }
|
||||
.select-all-btn { padding: 10px 20px; font-size: 14px; cursor: pointer; background: #28a745; color: white; border: none; border-radius: 5px; }
|
||||
.back-btn { padding: 10px 20px; font-size: 14px; cursor: pointer; background: #6c757d; color: white; border: none; border-radius: 5px; text-decoration: none; margin-left: 10px; }
|
||||
|
||||
.usb-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; max-height: 300px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background: #fafafa; }
|
||||
.usb-item { display: flex; align-items: center; padding: 8px; background: white; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; }
|
||||
.usb-item:hover { background: #f0f0f0; }
|
||||
.usb-item.selected { background: #e7f1ff; border-color: #667eea; }
|
||||
.usb-item input { margin-right: 10px; }
|
||||
.usb-item label { cursor: pointer; flex: 1; }
|
||||
.usb-item .alias { font-size: 11px; color: #666; }
|
||||
|
||||
.selected-count { font-weight: bold; margin: 10px 0; }
|
||||
.selected-count .count { color: #667eea; }
|
||||
.selected-count .pages { color: #28a745; }
|
||||
|
||||
.sheets-container { display: flex; flex-direction: column; gap: 20px; }
|
||||
|
||||
.print-sheet { width: 8.5in; height: 11in; background: white; margin: 0 auto; position: relative; border: 1px solid #ccc; page-break-after: always; }
|
||||
.print-sheet:last-child { page-break-after: auto; }
|
||||
|
||||
.sheet-label { position: absolute; top: -25px; left: 0; font-size: 12px; color: #666; }
|
||||
|
||||
.label-cell {
|
||||
width: 3in;
|
||||
height: 3in;
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
border: 1px dashed #ccc;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.label-cell.has-content { border: 1px solid #667eea; }
|
||||
.label-cell.empty { background: #fafafa; }
|
||||
|
||||
/* Label cell positions */
|
||||
.cell-1 { top: 0.875in; left: 1.1875in; }
|
||||
.cell-2 { top: 0.875in; left: 4.3125in; }
|
||||
.cell-3 { top: 4in; left: 1.1875in; }
|
||||
.cell-4 { top: 4in; left: 4.3125in; }
|
||||
.cell-5 { top: 7.125in; left: 1.1875in; }
|
||||
.cell-6 { top: 7.125in; left: 4.3125in; }
|
||||
|
||||
.mini-grid { display: grid; grid-template-columns: repeat(3, 1in); grid-template-rows: repeat(4, 0.75in); width: 3in; height: 3in; }
|
||||
|
||||
.mini-label { width: 1in; height: 0.75in; display: flex; flex-direction: column; align-items: center; justify-content: center; box-sizing: border-box; padding: 0.02in; border: 1px dotted #ddd; overflow: hidden; }
|
||||
.mini-label.filled { border: 1px solid #999; }
|
||||
.mini-label.empty { background: #f8f8f8; border: 1px dotted #eee; }
|
||||
|
||||
.barcode-container { text-align: center; line-height: 0; }
|
||||
.barcode-container svg { max-width: 0.9in; height: 24px; }
|
||||
.serial-text { font-size: 6pt; font-weight: bold; font-family: monospace; text-align: center; margin-top: 1px; letter-spacing: 0.3px; }
|
||||
|
||||
.empty-cell-text { color: #ccc; font-size: 12px; display: flex; align-items: center; justify-content: center; height: 100%; }
|
||||
|
||||
@media print {
|
||||
body { padding: 0; margin: 0; background: white; }
|
||||
.no-print { display: none; }
|
||||
.sheets-container { gap: 0; }
|
||||
.print-sheet { border: none; margin: 0; width: 8.5in; height: 11in; overflow: hidden; }
|
||||
.sheet-label { display: none; }
|
||||
.label-cell { border: none !important; }
|
||||
.label-cell.empty { visibility: hidden; }
|
||||
.mini-label { border: 1px dotted #ccc !important; }
|
||||
.mini-label.empty { visibility: hidden; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<%
|
||||
Dim strSQL
|
||||
strSQL = "SELECT m.machineid, m.serialnumber, m.alias FROM machines m WHERE m.machinetypeid = 44 AND m.isactive = 1 ORDER BY m.serialnumber ASC"
|
||||
Set rs = objConn.Execute(strSQL)
|
||||
%>
|
||||
<div class="no-print">
|
||||
<div class="controls">
|
||||
<h3>Batch Print USB Barcode Labels</h3>
|
||||
<p>Select USB devices to print (72 labels per page - 6 ULINE labels x 12 mini-labels each, cut after printing):</p>
|
||||
|
||||
<div class="usb-grid">
|
||||
<%
|
||||
Dim displayName
|
||||
Do While Not rs.EOF
|
||||
displayName = rs("serialnumber") & ""
|
||||
%>
|
||||
<div class="usb-item" onclick="toggleUSB(this, <%=rs("machineid")%>, '<%=Server.HTMLEncode(Replace(rs("serialnumber") & "", "'", "\'"))%>', '<%=Server.HTMLEncode(Replace(rs("alias") & "", "'", "\'"))%>')">
|
||||
<input type="checkbox" id="usb-<%=rs("machineid")%>">
|
||||
<label>
|
||||
<strong><code><%=Server.HTMLEncode(displayName)%></code></strong>
|
||||
<div class="alias"><%=Server.HTMLEncode(rs("alias") & "")%></div>
|
||||
</label>
|
||||
</div>
|
||||
<%
|
||||
rs.MoveNext
|
||||
Loop
|
||||
rs.Close
|
||||
Set rs = Nothing
|
||||
objConn.Close
|
||||
%>
|
||||
</div>
|
||||
|
||||
<div class="selected-count">
|
||||
Selected: <span class="count" id="selectedCount">0</span> USB devices
|
||||
(<span class="pages" id="pageCount">0</span> pages)
|
||||
<label style="margin-left: 20px;">Start at cell:
|
||||
<select id="startCell" onchange="updateSheets()" style="padding: 5px; font-size: 14px;">
|
||||
<option value="1">1 - Top Left</option>
|
||||
<option value="2">2 - Top Right</option>
|
||||
<option value="3">3 - Middle Left</option>
|
||||
<option value="4">4 - Middle Right</option>
|
||||
<option value="5">5 - Bottom Left</option>
|
||||
<option value="6">6 - Bottom Right</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button class="print-btn" id="printBtn" onclick="window.print()" disabled>Print Labels</button>
|
||||
<button class="clear-btn" onclick="clearSelection()">Clear All</button>
|
||||
<button class="select-all-btn" onclick="selectAll()">Select All</button>
|
||||
<a href="./displayusb.asp" class="back-btn">Back to USB Devices</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sheets-container" id="sheetsContainer">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var selectedUSBs = [];
|
||||
var allUSBs = [];
|
||||
var MINI_LABELS_PER_CELL = 12;
|
||||
var CELLS_PER_PAGE = 6;
|
||||
|
||||
document.querySelectorAll('.usb-item').forEach(function(el) {
|
||||
var onclick = el.getAttribute('onclick');
|
||||
var match = onclick.match(/toggleUSB\(this, (\d+), '([^']*)', '([^']*)'\)/);
|
||||
if (match) {
|
||||
allUSBs.push({
|
||||
id: parseInt(match[1]),
|
||||
serial: match[2].replace(/\\'/g, "'"),
|
||||
alias: match[3].replace(/\\'/g, "'"),
|
||||
element: el
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function toggleUSB(el, id, serial, alias) {
|
||||
var checkbox = el.querySelector('input[type="checkbox"]');
|
||||
var index = selectedUSBs.findIndex(function(u) { return u.id === id; });
|
||||
|
||||
if (index > -1) {
|
||||
selectedUSBs.splice(index, 1);
|
||||
checkbox.checked = false;
|
||||
el.classList.remove('selected');
|
||||
} else {
|
||||
selectedUSBs.push({ id: id, serial: serial, alias: alias });
|
||||
checkbox.checked = true;
|
||||
el.classList.add('selected');
|
||||
}
|
||||
|
||||
updateSheets();
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
selectedUSBs = [];
|
||||
document.querySelectorAll('.usb-item').forEach(function(el) {
|
||||
el.classList.remove('selected');
|
||||
el.querySelector('input[type="checkbox"]').checked = false;
|
||||
});
|
||||
updateSheets();
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
selectedUSBs = [];
|
||||
allUSBs.forEach(function(u) {
|
||||
selectedUSBs.push({ id: u.id, serial: u.serial, alias: u.alias });
|
||||
u.element.classList.add('selected');
|
||||
u.element.querySelector('input[type="checkbox"]').checked = true;
|
||||
});
|
||||
updateSheets();
|
||||
}
|
||||
|
||||
function updateSheets() {
|
||||
var numUSBs = selectedUSBs.length;
|
||||
var startCell = parseInt(document.getElementById('startCell').value) || 1;
|
||||
|
||||
// Calculate cells needed, accounting for skipped cells on first page
|
||||
var skippedCells = startCell - 1;
|
||||
var numCells = Math.ceil(numUSBs / MINI_LABELS_PER_CELL);
|
||||
var totalCellsNeeded = numCells + skippedCells;
|
||||
var numPages = Math.ceil(totalCellsNeeded / CELLS_PER_PAGE) || 0;
|
||||
if (numUSBs === 0) numPages = 0;
|
||||
|
||||
document.getElementById('selectedCount').textContent = numUSBs;
|
||||
document.getElementById('pageCount').textContent = numPages;
|
||||
document.getElementById('printBtn').disabled = numUSBs === 0;
|
||||
|
||||
var container = document.getElementById('sheetsContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
if (numUSBs === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var usbIndex = 0;
|
||||
|
||||
for (var page = 0; page < numPages; page++) {
|
||||
var sheet = document.createElement('div');
|
||||
sheet.className = 'print-sheet';
|
||||
sheet.style.position = 'relative';
|
||||
|
||||
var sheetLabel = document.createElement('div');
|
||||
sheetLabel.className = 'sheet-label';
|
||||
sheetLabel.textContent = 'Page ' + (page + 1) + ' of ' + numPages;
|
||||
sheet.appendChild(sheetLabel);
|
||||
|
||||
for (var cellNum = 1; cellNum <= CELLS_PER_PAGE; cellNum++) {
|
||||
var cell = document.createElement('div');
|
||||
|
||||
// On first page, skip cells before startCell
|
||||
var skipThisCell = (page === 0 && cellNum < startCell);
|
||||
var cellHasContent = !skipThisCell && usbIndex < numUSBs;
|
||||
|
||||
cell.className = 'label-cell cell-' + cellNum + (cellHasContent ? ' has-content' : ' empty');
|
||||
|
||||
if (cellHasContent) {
|
||||
var miniGrid = document.createElement('div');
|
||||
miniGrid.className = 'mini-grid';
|
||||
|
||||
for (var miniPos = 0; miniPos < MINI_LABELS_PER_CELL; miniPos++) {
|
||||
var usb = selectedUSBs[usbIndex];
|
||||
var miniLabel = document.createElement('div');
|
||||
miniLabel.className = 'mini-label' + (usb ? ' filled' : ' empty');
|
||||
|
||||
if (usb) {
|
||||
var barcodeId = 'bc-' + page + '-' + cellNum + '-' + miniPos;
|
||||
miniLabel.innerHTML = '<div class="barcode-container"><svg id="' + barcodeId + '"></svg></div><div class="serial-text">' + escapeHtml(usb.serial) + '</div>';
|
||||
usbIndex++;
|
||||
}
|
||||
|
||||
miniGrid.appendChild(miniLabel);
|
||||
}
|
||||
|
||||
cell.appendChild(miniGrid);
|
||||
} else {
|
||||
cell.innerHTML = '<div class="empty-cell-text">Empty</div>';
|
||||
}
|
||||
|
||||
sheet.appendChild(cell);
|
||||
}
|
||||
|
||||
container.appendChild(sheet);
|
||||
}
|
||||
|
||||
setTimeout(generateAllBarcodes, 50);
|
||||
}
|
||||
|
||||
function generateAllBarcodes() {
|
||||
var usbIndex = 0;
|
||||
var numUSBs = selectedUSBs.length;
|
||||
var startCell = parseInt(document.getElementById('startCell').value) || 1;
|
||||
|
||||
var skippedCells = startCell - 1;
|
||||
var numCells = Math.ceil(numUSBs / MINI_LABELS_PER_CELL);
|
||||
var totalCellsNeeded = numCells + skippedCells;
|
||||
var numPages = Math.ceil(totalCellsNeeded / CELLS_PER_PAGE) || 0;
|
||||
|
||||
usbIndex = 0;
|
||||
for (var page = 0; page < numPages; page++) {
|
||||
for (var cellNum = 1; cellNum <= CELLS_PER_PAGE; cellNum++) {
|
||||
// Skip cells before startCell on first page
|
||||
if (page === 0 && cellNum < startCell) continue;
|
||||
|
||||
for (var miniPos = 0; miniPos < MINI_LABELS_PER_CELL; miniPos++) {
|
||||
if (usbIndex < numUSBs) {
|
||||
var usb = selectedUSBs[usbIndex];
|
||||
var barcodeId = 'bc-' + page + '-' + cellNum + '-' + miniPos;
|
||||
var el = document.getElementById(barcodeId);
|
||||
if (el) {
|
||||
try {
|
||||
JsBarcode('#' + barcodeId, usb.serial, {
|
||||
format: "CODE128",
|
||||
width: 1,
|
||||
height: 22,
|
||||
displayValue: false,
|
||||
margin: 0,
|
||||
background: "transparent"
|
||||
});
|
||||
} catch(e) {
|
||||
console.error('Barcode error:', usb.serial, e);
|
||||
}
|
||||
}
|
||||
usbIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
if (!text) return '';
|
||||
var div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user