Szatuna/dashboard/labelmaker.php
2026-02-26 14:35:27 +01:00

759 lines
18 KiB
PHP

<?php
include '../managers/menu.php';
$warehouses = [];
$foil_warehouse = null;
$query = "SELECT * FROM warehouse_structure ORDER BY warehouse_id ASC";
if ($result = $conn->query($query)) {
while ($row = $result->fetch_assoc()) {
$warehouses[] = $row;
}
}
// Fóliás helyeket lekérése
$foil_places = [];
$foil_query = "SELECT foil_product_place FROM pr_warehouse_parameters WHERE foil_product_place IS NOT NULL AND foil_product_place != ''";
if ($result_foil = $conn->query($foil_query)) {
while ($row = $result_foil->fetch_assoc()) {
// Vesszővel elválasztott helyeket fel bontjuk
$places = array_map('trim', explode(',', $row['foil_product_place']));
$foil_places = array_merge($foil_places, $places);
}
}
// Duplikátumok eltávolítása, rendezés
$foil_places = array_unique($foil_places);
$foil_places = array_values($foil_places);
function mixed_compare($a, $b) {
preg_match_all('/(\D+|\d+)/', $a, $a_parts);
preg_match_all('/(\D+|\d+)/', $b, $b_parts);
$a_parts = $a_parts[0];
$b_parts = $b_parts[0];
$i = 0;
while (isset($a_parts[$i]) && isset($b_parts[$i])) {
if (ctype_digit($a_parts[$i]) && ctype_digit($b_parts[$i])) {
$diff = intval($a_parts[$i]) - intval($b_parts[$i]);
if ($diff !== 0) {
return $diff;
}
} else {
$diff = strcmp($a_parts[$i], $b_parts[$i]);
if ($diff !== 0) {
return $diff;
}
}
$i++;
}
return count($a_parts) - count($b_parts);
}
// Használat usort-tal
usort($foil_places, 'mixed_compare');
?>
<!DOCTYPE html>
<html lang="hu">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Raktár Címke Generátor</title>
<script src="../js/qrcode.js" type="text/javascript"></script>
<style>
:root {
--label-width: 8.6cm;
--label-height: 4.2cm;
--qr-size: 140;
}
:root {
--color-primary: #208091;
--color-bg: #fcfcf9;
--color-surface: #fffffe;
--color-text: #134252;
--color-border: #5e5240;
}
* {
box-sizing: border-box;
}
body {
font-family: "Source Sans Pro", sans-serif;
margin: 0;
padding: 20px;
background: var(--color-bg);
color: #333333;
}
.size-settings {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 20px;
}
.size-input-group {
display: flex;
gap: 12px;
align-items: flex-end;
}
.size-input-group input {
width: 100px;
padding: 8px 12px;
border: 1px solid rgba(94, 82, 64, 0.2);
border-radius: 8px;
font-size: 14px;
}
.size-input-group label {
margin-bottom: 0;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h1 {
font-size: 28px;
margin: 0 0 24px 0;
font-weight: 600;
}
.selector-section {
background: var(--color-surface);
border: 1px solid rgba(94, 82, 64, 0.2);
border-radius: 8px;
padding: 20px;
margin-bottom: 24px;
}
.selector-row {
display: flex;
gap: 16px;
align-items: flex-end;
margin-bottom: 16px;
}
.selector-row:last-child {
margin-bottom: 0;
}
.form-group {
flex: 1;
min-width: 200px;
}
label {
display: block;
font-size: 12px;
font-weight: 500;
margin-bottom: 8px;
color: var(--color-text);
}
select {
width: 100%;
padding: 8px 12px;
border: 1px solid rgba(94, 82, 64, 0.2);
border-radius: 8px;
font-size: 14px;
background: var(--color-surface);
color: var(--color-text);
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
background-size: 16px;
padding-right: 32px;
cursor: pointer;
}
select:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
button {
padding: 8px 20px;
background: var(--color-primary);
color: #fcfcf9;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background 150ms ease;
}
button:hover {
background: #1a7480;
}
button:active {
background: #1a6873;
}
button:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
.print-section {
display: none;
}
.print-section.active {
display: block;
}
.print-controls {
margin-bottom: 20px;
text-align: right;
}
.print-controls button {
margin-left: 12px;
}
.labels-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--label-width), 1fr));
gap: 20px;
margin-bottom: 20px;
}
.label-card {
background: var(--color-surface);
border: 1px solid rgba(94, 82, 64, 0.2);
border-radius: 8px;
padding: 16px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: var(--label-width);
height: var(--label-height);
box-sizing: border-box;
overflow: hidden;
gap: 8px;
}
.label-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.label-code {
font-size: 55px;
font-weight: 600;
color: var(--color-text);
white-space: nowrap;
line-height: 1.2;
margin: 0 0 8px 0;
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.qrcode-container {
flex-shrink: 0;
align-self: center;
margin-left: 12px;
width: var(--qr-size);
height: var(--qr-size);
min-width: var(--qr-size);
min-height: var(--qr-size);
}
.qrcode-container img {
width: var(--qr-size) !important;
height: var(--qr-size) !important;
max-width: var(--qr-size) !important;
max-height: var(--qr-size) !important;
display: block;
}
@media print {
body {
background: white;
padding: 0;
}
.selector-section,
.print-controls,
.size-settings,
h1 {
display: none;
}
.labels-grid {
grid-template-columns: repeat(auto-fill, minmax(var(--label-width), 1fr));
gap: 16px;
margin: 0;
}
.label-card {
page-break-inside: avoid;
width: var(--label-width);
height: var(--label-height);
box-sizing: border-box;
overflow: hidden;
}
.qrcode-container {
width: var(--qr-size);
height: var(--qr-size);
min-width: var(--qr-size);
min-height: var(--qr-size);
}
}
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.loading-overlay.hidden {
display: none;
}
.loading-spinner {
text-align: center;
background: var(--color-surface);
padding: 40px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
}
.spinner {
width: 50px;
height: 50px;
margin: 0 auto 20px;
border: 4px solid rgba(94, 82, 64, 0.2);
border-top: 4px solid var(--color-primary);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-spinner p {
color: var(--color-text);
margin: 0;
font-size: 16px;
font-weight: 500;
}
.loading-counter {
color: var(--color-text-secondary);
margin: 12px 0 0 0;
font-size: 16px;
}
</style>
</head>
<body>
<div id="loading-overlay" class="loading-overlay hidden">
<div class="loading-spinner">
<div class="spinner"></div>
<p class="loading-counter"><span id="qr-total">0</span> címke generálása...</p>
</div>
</div>
<div class="container">
<h1>Raktárhely Címke Generátor</h1>
<div class="selector-section">
<div class="selector-row">
<div class="form-group" style="flex: 2;">
<label for="warehouse-select">Raktár kiválasztása:</label>
<select id="warehouse-select">
<option value="">-- Válassz raktárt --</option>
</select>
</div>
<button onclick="generateLabels()">Címkék generálása</button>
<button onclick="generateAllLabels()" style="background: #1a7480;">Összes generálása</button>
</div>
<div class="size-settings">
<div class="size-input-group">
<label for="label-width">Szélesség (cm):</label>
<input type="number" id="label-width" value="9" min="1" max="30" step="0.1">
</div>
<div class="size-input-group">
<label for="label-height">Magasság (cm):</label>
<input type="number" id="label-height" value="3" min="1" max="30" step="0.1">
</div>
<div class="size-input-group">
<label for="qr-size">QR méret (px):</label>
<input type="number" id="qr-size" value="80" min="40" max="300" step="5">
</div>
</div>
</div>
<div class="print-section" id="print-section">
<div class="print-controls">
<button onclick="window.print()">🖨 Nyomtatás</button>
<button onclick="resetView()">← Újra</button>
</div>
<div class="labels-grid" id="labels-grid"></div>
</div>
</div>
<script>
function NumberToABC(n) {
const abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
if (typeof n === 'string') {
const num = parseInt(n, 10);
if (isNaN(num) || num < 1 || num > 26) return null;
return abc.charAt(num - 1);
}
if (typeof n !== 'number' || n < 1 || n > 26) return null;
return abc.charAt(n - 1);
}
function padWithZero(str) {
if (str.length === 2) return str;
return str.toString().padStart(2, '0');
}
// Raktárak adatai PHP-ből
const warehouses = <?php echo json_encode($warehouses); ?>;
const foilPlaces = <?php echo json_encode($foil_places); ?>;
// Dropdown feltöltése
function initializeSelect() {
const select = document.getElementById('warehouse-select');
// Normál raktárak
warehouses.forEach(warehouse => {
const option = document.createElement('option');
option.value = JSON.stringify(warehouse);
option.textContent = warehouse.name;
select.appendChild(option);
});
// Fóliás raktár hozzáadása
if (foilPlaces && foilPlaces.length > 0) {
const option = document.createElement('option');
const foilData = {
is_foil: true,
foil_places: foilPlaces
};
option.value = JSON.stringify(foilData);
option.textContent = 'Fóliás raktár';
select.appendChild(option);
}
}
// Méret beállítások frissítése
function updateSizeSettings() {
const width = document.getElementById('label-width').value;
const height = document.getElementById('label-height').value;
const qrSize = document.getElementById('qr-size').value;
document.documentElement.style.setProperty('--label-width', width + 'cm');
document.documentElement.style.setProperty('--label-height', height + 'cm');
document.documentElement.style.setProperty('--qr-size', qrSize);
}
// Címkék generálása
function generateLabels() {
const select = document.getElementById('warehouse-select');
if (!select.value) {
alert('Kérjük válassz raktárt!');
return;
}
// Loading overlay megjelenítése
document.getElementById('loading-overlay').classList.remove('hidden');
updateSizeSettings();
const warehouse = JSON.parse(select.value);
const grid = document.getElementById('labels-grid');
grid.innerHTML = '';
let labelIndex = 0;
const qrSize = parseInt(document.getElementById('qr-size').value);
let qrGeneratedCount = 0;
let totalQRCodes = 0;
if (warehouse.is_foil) {
totalQRCodes = warehouse.foil_places.filter(place => place.length <= 7).length;
const invalidPlaces = [];
document.getElementById('qr-total').textContent = totalQRCodes;
warehouse.foil_places.forEach((place, index) => {
if (place.length > 7) {
invalidPlaces.push(place);
return;
}
const labelCode = place;
const qrcodeId = `qrcode-${labelIndex}`;
const card = document.createElement('div');
card.className = 'label-card';
card.innerHTML = `
<div class="label-content">
<p class="label-code">${labelCode}</p>
</div>
<div class="qrcode-container" id="${qrcodeId}"></div>
`;
grid.appendChild(card);
setTimeout(() => {
new QRCode(document.getElementById(qrcodeId), {
text: labelCode,
width: qrSize,
height: qrSize,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H,
callback: function() {
qrGeneratedCount++;
if (qrGeneratedCount === totalQRCodes) {
document.getElementById('loading-overlay').classList.add('hidden');
}
}
});
}, 0);
labelIndex++;
});
if (invalidPlaces.length > 0) {
alert('Az alábbi helyek 7 karakternél hosszabbak, ezért nem voltak generálva:\n\n' + invalidPlaces.join('\n'));
}
} else {
totalQRCodes = warehouse.row * warehouse.columns;
document.getElementById('qr-total').textContent = totalQRCodes;
for (let row = 1; row <= warehouse.row; row++) {
for (let col = 1; col <= warehouse.columns; col++) {
const labelCode = warehouse.code + NumberToABC(row) + padWithZero(col);
const qrcodeId = `qrcode-${labelIndex}`;
const card = document.createElement('div');
card.className = 'label-card';
card.innerHTML = `
<div class="label-content">
<p class="label-code">${labelCode}</p>
</div>
<div class="qrcode-container" id="${qrcodeId}"></div>
`;
grid.appendChild(card);
setTimeout(() => {
new QRCode(document.getElementById(qrcodeId), {
text: labelCode,
width: qrSize,
height: qrSize,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
});
}, 0);
labelIndex++;
}
}
}
document.getElementById('print-section').classList.add('active');
monitorQRCodeLoading(totalQRCodes);
}
// Összes raktár címkéinek generálása
function generateAllLabels() {
// Loading overlay megjelenítése
document.getElementById('loading-overlay').classList.remove('hidden');
updateSizeSettings();
const grid = document.getElementById('labels-grid');
grid.innerHTML = '';
let labelIndex = 0;
const qrSize = parseInt(document.getElementById('qr-size').value);
let qrGeneratedCount = 0;
let totalQRCodes = 0;
const foilWarehouseObject = foilPlaces && foilPlaces.length > 0 ? {
is_foil: true,
foil_places: foilPlaces
} : null;
const allWarehouses = foilWarehouseObject ? [...warehouses, foilWarehouseObject] : warehouses;
// Először számold meg az összes QR kódot
allWarehouses.forEach(warehouse => {
if (warehouse.is_foil) {
totalQRCodes += warehouse.foil_places.filter(place => place.length <= 7).length;
} else {
totalQRCodes += warehouse.row * warehouse.columns;
}
});
document.getElementById('qr-total').textContent = totalQRCodes;
allWarehouses.forEach(warehouse => {
if (warehouse.is_foil) {
const invalidPlaces = [];
warehouse.foil_places.forEach((place, index) => {
if (place.length > 7) {
invalidPlaces.push(place);
return;
}
const labelCode = place;
const qrcodeId = `qrcode-${labelIndex}`;
const card = document.createElement('div');
card.className = 'label-card';
card.innerHTML = `
<div class="label-content">
<p class="label-code">${labelCode}</p>
</div>
<div class="qrcode-container" id="${qrcodeId}"></div>
`;
grid.appendChild(card);
setTimeout(() => {
new QRCode(document.getElementById(qrcodeId), {
text: labelCode,
width: qrSize,
height: qrSize,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
});
}, 0);
labelIndex++;
});
if (invalidPlaces.length > 0) {
alert('Az alábbi helyek 7 karakternél hosszabbak, ezért nem voltak generálva:\n\n' + invalidPlaces.join('\n'));
}
} else {
for (let row = 1; row <= warehouse.row; row++) {
for (let col = 1; col <= warehouse.columns; col++) {
const labelCode = warehouse.code + NumberToABC(row) + padWithZero(col);
const qrcodeId = `qrcode-${labelIndex}`;
const card = document.createElement('div');
card.className = 'label-card';
card.innerHTML = `
<div class="label-content">
<p class="label-code">${labelCode}</p>
</div>
<div class="qrcode-container" id="${qrcodeId}"></div>
`;
grid.appendChild(card);
setTimeout(() => {
new QRCode(document.getElementById(qrcodeId), {
text: labelCode,
width: qrSize,
height: qrSize,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H,
callback: function() {
qrGeneratedCount++;
if (qrGeneratedCount === totalQRCodes) {
document.getElementById('loading-overlay').classList.add('hidden');
}
}
});
}, 0);
labelIndex++;
}
}
}
});
document.getElementById('print-section').classList.add('active');
monitorQRCodeLoading(totalQRCodes);
}
function monitorQRCodeLoading(total) {
document.getElementById('qr-total').textContent = total;
const checkInterval = setInterval(() => {
const grid = document.getElementById('labels-grid');
const imgElements = grid.querySelectorAll('img').length;
if (imgElements === total) {
clearInterval(checkInterval);
setTimeout(() => {
document.getElementById('loading-overlay').classList.add('hidden');
}, 300);
}
}, 10);
}
// Vissza az alapértelmezett nézetre
function resetView() {
document.getElementById('warehouse-select').value = '';
document.getElementById('print-section').classList.remove('active');
document.getElementById('labels-grid').innerHTML = '';
}
// Méret inputok figyelése
document.getElementById('label-width').addEventListener('change', updateSizeSettings);
document.getElementById('label-height').addEventListener('change', updateSizeSettings);
document.getElementById('qr-size').addEventListener('change', updateSizeSettings);
// Inicializálás
initializeSelect();
updateSizeSettings();
</script>
</body>
</html>