enhancing printing
This commit is contained in:
parent
9f56d9b0a2
commit
c451a24982
@ -74,8 +74,8 @@ include 'includes/header.php';
|
|||||||
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h2 class="fw-bold mb-0">Outlets</h2>
|
<h2 class="fw-bold mb-0">Outlets</h2>
|
||||||
<?php if (has_permission('outlets_add')): ?>
|
<?php if (has_permission('outlets_add')):
|
||||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#outletModal" onclick="prepareAddForm()">
|
?><button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#outletModal" onclick="prepareAddForm()">
|
||||||
<i class="bi bi-plus-lg"></i> Add Outlet
|
<i class="bi bi-plus-lg"></i> Add Outlet
|
||||||
</button>
|
</button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@ -101,8 +101,8 @@ include 'includes/header.php';
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php foreach ($outlets as $outlet): ?>
|
<?php foreach ($outlets as $outlet):
|
||||||
<tr>
|
?><tr>
|
||||||
<td class="ps-4 fw-medium">#<?= $outlet['id'] ?></td>
|
<td class="ps-4 fw-medium">#<?= $outlet['id'] ?></td>
|
||||||
<td>
|
<td>
|
||||||
<div class="fw-bold"><?= htmlspecialchars($outlet['name']) ?></div>
|
<div class="fw-bold"><?= htmlspecialchars($outlet['name']) ?></div>
|
||||||
@ -118,20 +118,20 @@ include 'includes/header.php';
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end pe-4">
|
<td class="text-end pe-4">
|
||||||
<?php if (has_permission('outlets_add')): ?>
|
<?php if (has_permission('outlets_add')):
|
||||||
<button type="button" class="btn btn-sm btn-outline-primary me-1"
|
?><button type="button" class="btn btn-sm btn-outline-primary me-1"
|
||||||
data-bs-toggle="modal" data-bs-target="#outletModal"
|
data-bs-toggle="modal" data-bs-target="#outletModal"
|
||||||
onclick='prepareEditForm(<?= htmlspecialchars(json_encode($outlet), ENT_QUOTES, "UTF-8") ?>)'><i class="bi bi-pencil"></i></button>
|
onclick='prepareEditForm(<?= htmlspecialchars(json_encode($outlet), ENT_QUOTES, "UTF-8") ?>)'><i class="bi bi-pencil"></i></button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (has_permission('outlets_del')): ?>
|
<?php if (has_permission('outlets_del')):
|
||||||
<a href="?delete=<?= $outlet['id'] ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('<?= t('are_you_sure') ?>')"><i class="bi bi-trash"></i></a>
|
?><a href="?delete=<?= $outlet['id'] ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('<?= t('are_you_sure') ?>')"><i class="bi bi-trash"></i></a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php if (empty($outlets)): ?>
|
<?php if (empty($outlets)):
|
||||||
<tr>
|
?><tr>
|
||||||
<td colspan="5" class="text-center py-4 text-muted">No outlets found.</td>
|
<td colspan="5" class="text-center py-4 text-muted">No outlets found.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@ -146,8 +146,8 @@ include 'includes/header.php';
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Outlet Modal -->
|
<!-- Outlet Modal -->
|
||||||
<?php if (has_permission('outlets_add')): ?>
|
<?php if (has_permission('outlets_add')):
|
||||||
<div class="modal fade" id="outletModal" tabindex="-1" aria-hidden="true">
|
?><div class="modal fade" id="outletModal" tabindex="-1" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header bg-primary text-white">
|
<div class="modal-header bg-primary text-white">
|
||||||
@ -176,14 +176,28 @@ include 'includes/header.php';
|
|||||||
<textarea name="address" id="outletAddress" class="form-control" rows="3"></textarea>
|
<textarea name="address" id="outletAddress" class="form-control" rows="3"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
<div class="alert alert-info small py-2">
|
||||||
|
<i class="bi bi-info-circle me-1"></i>
|
||||||
|
Note: Direct IP printing only works if the printer is reachable from the server. For local printers, use browser printing instead.
|
||||||
|
</div>
|
||||||
<h6 class="fw-bold mb-3">IP/TCP Printers (ESC/POS)</h6>
|
<h6 class="fw-bold mb-3">IP/TCP Printers (ESC/POS)</h6>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label small">Cashier Printer IP</label>
|
<label class="form-label small">Cashier Printer IP</label>
|
||||||
<input type="text" name="cashier_printer_ip" id="outletCashierIp" class="form-control" placeholder="e.g. 192.168.1.100">
|
<div class="input-group">
|
||||||
|
<input type="text" name="cashier_printer_ip" id="outletCashierIp" class="form-control" placeholder="e.g. 192.168.1.100">
|
||||||
|
<button class="btn btn-outline-secondary" type="button" onclick="testPrinter('outletCashierIp')">
|
||||||
|
Test
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label small">Kitchen Printer IP</label>
|
<label class="form-label small">Kitchen Printer IP</label>
|
||||||
<input type="text" name="kitchen_printer_ip" id="outletKitchenIp" class="form-control" placeholder="e.g. 192.168.1.101">
|
<div class="input-group">
|
||||||
|
<input type="text" name="kitchen_printer_ip" id="outletKitchenIp" class="form-control" placeholder="e.g. 192.168.1.101">
|
||||||
|
<button class="btn btn-outline-secondary" type="button" onclick="testPrinter('outletKitchenIp')">
|
||||||
|
Test
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@ -215,6 +229,46 @@ function prepareEditForm(outlet) {
|
|||||||
document.getElementById('outletKitchenIp').value = outlet.kitchen_printer_ip || '';
|
document.getElementById('outletKitchenIp').value = outlet.kitchen_printer_ip || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testPrinter(inputId) {
|
||||||
|
const ip = document.getElementById(inputId).value;
|
||||||
|
if (!ip) {
|
||||||
|
alert('Please enter an IP address first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const btn = event.currentTarget;
|
||||||
|
const originalText = btn.innerText;
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.innerText = '...';
|
||||||
|
|
||||||
|
fetch('../api/print.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
action: 'test',
|
||||||
|
ip: ip
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
alert('Printer test command sent successfully!');
|
||||||
|
} else {
|
||||||
|
alert('Printer connection failed: ' + (data.error || 'Unknown error') + '\n\nNote: Local IPs (like 192.168.x.x) are not reachable from the cloud server.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
alert('An error occurred during the test.');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.innerText = originalText;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('btnTranslate').addEventListener('click', function() {
|
document.getElementById('btnTranslate').addEventListener('click', function() {
|
||||||
const text = document.getElementById('outletName').value;
|
const text = document.getElementById('outletName').value;
|
||||||
if (!text) {
|
if (!text) {
|
||||||
@ -257,4 +311,4 @@ document.getElementById('btnTranslate').addEventListener('click', function() {
|
|||||||
</script>
|
</script>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php include 'includes/footer.php'; ?>
|
<?php include 'includes/footer.php'; ?>
|
||||||
|
|||||||
@ -7,6 +7,28 @@ require_once __DIR__ . '/../includes/PrinterService.php';
|
|||||||
$pdo = db();
|
$pdo = db();
|
||||||
$input = json_decode(file_get_contents('php://input'), true);
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
$action = $input['action'] ?? 'print';
|
||||||
|
|
||||||
|
if ($action === 'test') {
|
||||||
|
$ip = $input['ip'] ?? null;
|
||||||
|
if (!$ip) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'IP is required']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a simple test line
|
||||||
|
$testData = "\x1b" . "@"; // Initialize printer
|
||||||
|
$testData .= "Printer Test Successful\n";
|
||||||
|
$testData .= "IP: $ip\n";
|
||||||
|
$testData .= "Date: " . date('Y-m-d H:i:s') . "\n";
|
||||||
|
$testData .= "\n\n\n\n\n";
|
||||||
|
$testData .= "\x1b" . "m"; // Cut
|
||||||
|
|
||||||
|
$result = PrinterService::sendToNetworkPrinter($ip, $testData);
|
||||||
|
echo json_encode($result);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
$order_id = $input['order_id'] ?? null;
|
$order_id = $input['order_id'] ?? null;
|
||||||
$type = $input['type'] ?? 'cashier'; // 'cashier' or 'kitchen'
|
$type = $input['type'] ?? 'cashier'; // 'cashier' or 'kitchen'
|
||||||
|
|
||||||
@ -17,12 +39,12 @@ if (!$order_id) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch order details with outlet info
|
// Fetch order details with outlet info
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare(
|
||||||
SELECT o.*, out.cashier_printer_ip, out.kitchen_printer_ip
|
"SELECT o.*, out.cashier_printer_ip, out.kitchen_printer_ip "
|
||||||
FROM orders o
|
. "FROM orders o "
|
||||||
JOIN outlets out ON o.outlet_id = out.id
|
. "JOIN outlets out ON o.outlet_id = out.id "
|
||||||
WHERE o.id = ?
|
. "WHERE o.id = ?"
|
||||||
");
|
);
|
||||||
$stmt->execute([$order_id]);
|
$stmt->execute([$order_id]);
|
||||||
$order = $stmt->fetch(PDO::FETCH_ASSOC);
|
$order = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@ -57,4 +79,4 @@ try {
|
|||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||||
}
|
}
|
||||||
@ -589,7 +589,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (cartVatAmount) cartVatAmount.innerText = formatCurrency(0);
|
if (cartVatAmount) cartVatAmount.innerText = formatCurrency(0);
|
||||||
// Always show VAT row even if 0
|
// Always show VAT row even if 0
|
||||||
if (cartVatRow) cartVatRow.classList.remove('d-none');
|
if (cartVatRow) cartVatRow.classList.remove('d-none');
|
||||||
if (cartVatInput) cartVatInput.value = totalVat.toFixed(3);
|
if (cartVatInput) cartVatInput.value = '0';
|
||||||
if (cartTotalPrice) cartTotalPrice.innerText = formatCurrency(0);
|
if (cartTotalPrice) cartTotalPrice.innerText = formatCurrency(0);
|
||||||
if (quickOrderBtn) { quickOrderBtn.disabled = true; quickOrderBtn.innerText = _t('quick_pay'); }
|
if (quickOrderBtn) { quickOrderBtn.disabled = true; quickOrderBtn.innerText = _t('quick_pay'); }
|
||||||
if (placeOrderBtn) { placeOrderBtn.disabled = true; placeOrderBtn.innerText = _t('save_bill'); }
|
if (placeOrderBtn) { placeOrderBtn.disabled = true; placeOrderBtn.innerText = _t('save_bill'); }
|
||||||
@ -762,8 +762,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
console.error(`Network Print Error (${type}):`, data.error);
|
// Skip toast for local IPs as we know they are unreachable from cloud
|
||||||
// Optionally show a toast for errors, but might be too noisy
|
const isLocalIp = /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)/.test(printerIp);
|
||||||
|
if (!isLocalIp) {
|
||||||
|
showToast(`Printer Error (${type}): ${data.error}`, 'warning');
|
||||||
|
} else {
|
||||||
|
console.warn(`Printer Error (${type}) for local IP ${printerIp}: ${data.error}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err => console.error(`Network Print Fetch Error (${type}):`, err));
|
.catch(err => console.error(`Network Print Fetch Error (${type}):`, err));
|
||||||
@ -917,4 +922,4 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const modal = new bootstrap.Modal(document.getElementById('qrRatingModal'));
|
const modal = new bootstrap.Modal(document.getElementById('qrRatingModal'));
|
||||||
modal.show();
|
modal.show();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
95
kitchen.php
95
kitchen.php
@ -36,6 +36,8 @@ if (!has_permission('all')) {
|
|||||||
$current_outlet_id = (int)$outlets[0]['id'];
|
$current_outlet_id = (int)$outlets[0]['id'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$settings = get_company_settings();
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@ -43,7 +45,6 @@ if (!has_permission('all')) {
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Kitchen Display System</title>
|
<title>Kitchen Display System</title>
|
||||||
<!-- Re-using existing CSS if any, or adding Bootstrap/Custom -->
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
||||||
<link href="assets/css/custom.css" rel="stylesheet">
|
<link href="assets/css/custom.css" rel="stylesheet">
|
||||||
@ -56,6 +57,8 @@ if (!has_permission('all')) {
|
|||||||
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
||||||
transition: transform 0.2s;
|
transition: transform 0.2s;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.order-card:hover { transform: translateY(-2px); }
|
.order-card:hover { transform: translateY(-2px); }
|
||||||
.card-header {
|
.card-header {
|
||||||
@ -84,6 +87,7 @@ if (!has_permission('all')) {
|
|||||||
.item-qty { font-weight: bold; margin-right: 10px; color: #333; }
|
.item-qty { font-weight: bold; margin-right: 10px; color: #333; }
|
||||||
|
|
||||||
#kitchen-grid { padding: 20px; }
|
#kitchen-grid { padding: 20px; }
|
||||||
|
.card-footer { border-top: none; background: white; padding: 1rem; border-radius: 0 0 12px 12px; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -139,12 +143,17 @@ if (!has_permission('all')) {
|
|||||||
<script>
|
<script>
|
||||||
const OUTLET_ID = <?= $current_outlet_id ?>;
|
const OUTLET_ID = <?= $current_outlet_id ?>;
|
||||||
const CAN_EDIT = <?= has_permission('kitchen_add') ? 'true' : 'false' ?>;
|
const CAN_EDIT = <?= has_permission('kitchen_add') ? 'true' : 'false' ?>;
|
||||||
|
const COMPANY_SETTINGS = <?= json_encode($settings) ?>;
|
||||||
|
const BASE_URL = '<?= get_base_url() ?>';
|
||||||
|
|
||||||
|
let activeOrders = [];
|
||||||
|
|
||||||
async function fetchOrders() {
|
async function fetchOrders() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('api/kitchen.php?outlet_id=' + OUTLET_ID);
|
const response = await fetch('api/kitchen.php?outlet_id=' + OUTLET_ID);
|
||||||
if (!response.ok) throw new Error('Network response was not ok');
|
if (!response.ok) throw new Error('Network response was not ok');
|
||||||
const orders = await response.json();
|
const orders = await response.json();
|
||||||
|
activeOrders = orders;
|
||||||
renderOrders(orders);
|
renderOrders(orders);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching orders:', error);
|
console.error('Error fetching orders:', error);
|
||||||
@ -167,15 +176,15 @@ function renderOrders(orders) {
|
|||||||
if (order.status === 'pending') {
|
if (order.status === 'pending') {
|
||||||
statusBadge = '<span class="badge badge-pending">Pending</span>';
|
statusBadge = '<span class="badge badge-pending">Pending</span>';
|
||||||
cardClass += 'status-pending';
|
cardClass += 'status-pending';
|
||||||
if (CAN_EDIT) actionBtn = `<button class="btn btn-info btn-sm w-100 text-white" onclick="updateStatus(${order.id}, 'preparing')">Start Preparing</button>`;
|
if (CAN_EDIT) actionBtn = `<button class="btn btn-info btn-sm w-100 text-white mb-2" onclick="updateStatus(${order.id}, 'preparing')">Start Preparing</button>`;
|
||||||
} else if (order.status === 'preparing') {
|
} else if (order.status === 'preparing') {
|
||||||
statusBadge = '<span class="badge badge-preparing">Preparing</span>';
|
statusBadge = '<span class="badge badge-preparing">Preparing</span>';
|
||||||
cardClass += 'status-preparing';
|
cardClass += 'status-preparing';
|
||||||
if (CAN_EDIT) actionBtn = `<button class="btn btn-success btn-sm w-100" onclick="updateStatus(${order.id}, 'ready')">Mark Ready</button>`;
|
if (CAN_EDIT) actionBtn = `<button class="btn btn-success btn-sm w-100 mb-2" onclick="updateStatus(${order.id}, 'ready')">Mark Ready</button>`;
|
||||||
} else if (order.status === 'ready') {
|
} else if (order.status === 'ready') {
|
||||||
statusBadge = '<span class="badge badge-ready">Ready</span>';
|
statusBadge = '<span class="badge badge-ready">Ready</span>';
|
||||||
cardClass += 'status-ready';
|
cardClass += 'status-ready';
|
||||||
if (CAN_EDIT) actionBtn = `<button class="btn btn-secondary btn-sm w-100" onclick="updateStatus(${order.id}, 'completed')">Serve / Complete</button>`;
|
if (CAN_EDIT) actionBtn = `<button class="btn btn-secondary btn-sm w-100 mb-2" onclick="updateStatus(${order.id}, 'completed')">Serve / Complete</button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemsHtml = order.items.map(item => `
|
const itemsHtml = order.items.map(item => `
|
||||||
@ -184,7 +193,6 @@ function renderOrders(orders) {
|
|||||||
</li>
|
</li>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
// Calculate time elapsed
|
|
||||||
const created = new Date(order.created_at);
|
const created = new Date(order.created_at);
|
||||||
const timeString = created.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
|
const timeString = created.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
|
||||||
|
|
||||||
@ -204,8 +212,11 @@ function renderOrders(orders) {
|
|||||||
${itemsHtml}
|
${itemsHtml}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer bg-white border-top-0 pb-3">
|
<div class="card-footer">
|
||||||
${actionBtn || '<div class="text-center small text-muted">View Only</div>'}
|
${actionBtn}
|
||||||
|
<button class="btn btn-outline-primary btn-sm w-100" onclick="printKitchenTicket(${order.id})">
|
||||||
|
<i class="bi bi-printer"></i> Print Ticket
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -215,6 +226,69 @@ function renderOrders(orders) {
|
|||||||
grid.innerHTML = newHtml;
|
grid.innerHTML = newHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function printKitchenTicket(orderId) {
|
||||||
|
const order = activeOrders.find(o => o.id == orderId);
|
||||||
|
if (!order) return;
|
||||||
|
|
||||||
|
const width = 450;
|
||||||
|
const height = 600;
|
||||||
|
const left = (screen.width - width) / 2;
|
||||||
|
const top = (screen.height - height) / 2;
|
||||||
|
|
||||||
|
const win = window.open('', '_blank', `width=${width},height=${height},top=${top},left=${left}`);
|
||||||
|
if (!win) {
|
||||||
|
alert('Please allow popups for printing.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemsHtml = order.items.map(item => `
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 10px 0; border-bottom: 1px solid #000;">
|
||||||
|
<div style="font-size: 20px; font-weight: bold;">${item.quantity} x ${item.product_name}</div>
|
||||||
|
${item.variant_name ? `<div style="font-size: 16px;">Variant: ${item.variant_name}</div>` : ''}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
const html = `
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Kitchen Ticket #${order.id}</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Courier New', monospace; padding: 20px; color: #000; }
|
||||||
|
.header { text-align: center; border-bottom: 2px dashed #000; padding-bottom: 10px; margin-bottom: 10px; }
|
||||||
|
.info { font-size: 18px; margin-bottom: 10px; }
|
||||||
|
.info div { margin-bottom: 5px; }
|
||||||
|
table { width: 100%; border-collapse: collapse; }
|
||||||
|
@media print { body { width: 80mm; padding: 5mm; } @page { size: 80mm auto; margin: 0; } }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<h1 style="margin:0;">KITCHEN TICKET</h1>
|
||||||
|
<div style="font-size: 24px; font-weight: bold;">#${order.id}</div>
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<div><strong>Type:</strong> ${order.order_type.toUpperCase()}</div>
|
||||||
|
${order.order_type === 'dine-in' ? `<div><strong>Table:</strong> ${order.table_number}</div>` : ''}
|
||||||
|
<div><strong>Time:</strong> ${new Date(order.created_at).toLocaleString()}</div>
|
||||||
|
${order.customer_name ? `<div><strong>Cust:</strong> ${order.customer_name}</div>` : ''}
|
||||||
|
</div>
|
||||||
|
<div style="border-bottom: 2px solid #000; margin-bottom: 10px;"></div>
|
||||||
|
<table>
|
||||||
|
<tbody>${itemsHtml}</tbody>
|
||||||
|
</table>
|
||||||
|
<div style="border-top: 2px dashed #000; margin-top: 20px; padding-top: 10px; text-align: center;">
|
||||||
|
<strong>*** END OF TICKET ***</strong>
|
||||||
|
</div>
|
||||||
|
<script>window.onload = function() { window.print(); setTimeout(function() { window.close(); }, 500); }</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
win.document.write(html);
|
||||||
|
win.document.close();
|
||||||
|
}
|
||||||
|
|
||||||
function updateStatus(orderId, newStatus) {
|
function updateStatus(orderId, newStatus) {
|
||||||
if (!CAN_EDIT) return;
|
if (!CAN_EDIT) return;
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
@ -241,7 +315,7 @@ async function performUpdate(orderId, newStatus) {
|
|||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
fetchOrders(); // Refresh immediately
|
fetchOrders();
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: 'success',
|
icon: 'success',
|
||||||
title: 'Updated!',
|
title: 'Updated!',
|
||||||
@ -291,7 +365,6 @@ async function serveAll() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Outlet Selector Logic
|
|
||||||
const outletSelector = document.getElementById('outlet-selector');
|
const outletSelector = document.getElementById('outlet-selector');
|
||||||
if (outletSelector) {
|
if (outletSelector) {
|
||||||
outletSelector.addEventListener('change', function() {
|
outletSelector.addEventListener('change', function() {
|
||||||
@ -299,15 +372,13 @@ if (outletSelector) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clock
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
const clock = document.getElementById('clock');
|
const clock = document.getElementById('clock');
|
||||||
if (clock) clock.textContent = new Date().toLocaleTimeString();
|
if (clock) clock.textContent = new Date().toLocaleTimeString();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
// Poll orders
|
|
||||||
fetchOrders();
|
fetchOrders();
|
||||||
setInterval(fetchOrders, 10000); // Poll every 10s
|
setInterval(fetchOrders, 10000);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user