KDS: per-station line item filtering with expand toggle

Backend returns all line items for every order (removes station
filter from sub-query). Frontend filters by station, showing only
relevant items by default. An expand toggle reveals other stations'
items dimmed at 35% opacity for full order context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-03-02 15:30:59 -08:00
parent c65cd8242b
commit 94b5bbbce1
3 changed files with 77 additions and 56 deletions

View file

@ -110,57 +110,28 @@
<cfset orders = []>
<cfloop query="qOrders">
<!--- Get line items for this order --->
<!--- If filtering by station, only show items for that station (plus modifiers) --->
<cfif StationID GT 0>
<cfset qLineItems = queryTimed("
SELECT
oli.ID,
oli.ParentOrderLineItemID,
oli.ItemID,
oli.Price,
oli.Quantity,
oli.Remark,
oli.IsDeleted,
i.Name,
i.ParentItemID,
i.IsCheckedByDefault,
i.StationID,
parent.Name AS ItemParentName
FROM OrderLineItems oli
INNER JOIN Items i ON i.ID = oli.ItemID
LEFT JOIN Items parent ON parent.ID = i.ParentItemID
WHERE oli.OrderID = ?
AND oli.IsDeleted = b'0'
AND (i.StationID = ? OR i.StationID = 0 OR i.StationID IS NULL OR oli.ParentOrderLineItemID > 0)
ORDER BY oli.ID
", [
{ value = qOrders.ID, cfsqltype = "cf_sql_integer" },
{ value = StationID, cfsqltype = "cf_sql_integer" }
], { datasource = "payfrit" })>
<cfelse>
<cfset qLineItems = queryTimed("
SELECT
oli.ID,
oli.ParentOrderLineItemID,
oli.ItemID,
oli.Price,
oli.Quantity,
oli.Remark,
oli.IsDeleted,
i.Name,
i.ParentItemID,
i.IsCheckedByDefault,
i.StationID,
parent.Name AS ItemParentName
FROM OrderLineItems oli
INNER JOIN Items i ON i.ID = oli.ItemID
LEFT JOIN Items parent ON parent.ID = i.ParentItemID
WHERE oli.OrderID = ?
AND oli.IsDeleted = b'0'
ORDER BY oli.ID
", [ { value = qOrders.ID, cfsqltype = "cf_sql_integer" } ], { datasource = "payfrit" })>
</cfif>
<!--- Get all line items for this order (frontend handles station filtering) --->
<cfset qLineItems = queryTimed("
SELECT
oli.ID,
oli.ParentOrderLineItemID,
oli.ItemID,
oli.Price,
oli.Quantity,
oli.Remark,
oli.IsDeleted,
i.Name,
i.ParentItemID,
i.IsCheckedByDefault,
i.StationID,
parent.Name AS ItemParentName
FROM OrderLineItems oli
INNER JOIN Items i ON i.ID = oli.ItemID
LEFT JOIN Items parent ON parent.ID = i.ParentItemID
WHERE oli.OrderID = ?
AND oli.IsDeleted = b'0'
ORDER BY oli.ID
", [ { value = qOrders.ID, cfsqltype = "cf_sql_integer" } ], { datasource = "payfrit" })>
<cfset lineItems = []>
<cfloop query="qLineItems">

View file

@ -79,6 +79,13 @@
.station-btn.all-stations:hover { border-color: #3b82f6; background: #001a2a; }
.station-btn svg { width: 24px; height: 24px; opacity: 0.5; }
.station-name-display { font-size: 11px; color: #555; margin-left: 8px; text-transform: uppercase; letter-spacing: 1px; }
/* Expand toggle */
.expand-toggle { background: none; border: 1px solid #333; color: #555; padding: 4px 10px; border-radius: 4px; font-size: 11px; cursor: pointer; margin-bottom: 10px; width: 100%; transition: all 0.2s; }
.expand-toggle:hover { border-color: #555; color: #888; }
/* Other-station items (dimmed) */
.line-item.other-station { opacity: 0.35; }
</style>
</head>
<body>
@ -109,6 +116,6 @@
</div>
</div>
<script src="kds.js?v=5"></script>
<script src="kds.js?v=6"></script>
</body>
</html>

View file

@ -12,6 +12,7 @@ let config = {
let orders = [];
let stations = [];
let refreshTimer = null;
let expandedOrders = new Set();
// Status ID mapping
const STATUS = { NEW: 1, PREPARING: 2, READY: 3, COMPLETED: 4 };
@ -283,12 +284,52 @@ function renderOrders() {
grid.innerHTML = orders.map(order => renderOrder(order)).join('');
}
// Check if a root item belongs to the current station
function isStationItem(item) {
if (!config.stationId || config.stationId === 0) return true;
const sid = parseInt(item.StationID) || 0;
return sid === config.stationId || sid === 0;
}
// Toggle expand/collapse for an order
function toggleExpand(orderId) {
if (expandedOrders.has(orderId)) {
expandedOrders.delete(orderId);
} else {
expandedOrders.add(orderId);
}
renderOrders();
}
// Render single order card
function renderOrder(order) {
const statusClass = getStatusClass(order.StatusID);
const elapsedTime = getElapsedTime(order.SubmittedOn);
const timeClass = getTimeClass(elapsedTime);
const rootItems = order.LineItems.filter(item => item.ParentOrderLineItemID === 0);
const allRootItems = order.LineItems.filter(item => item.ParentOrderLineItemID === 0);
const isFiltering = config.stationId && config.stationId > 0;
const isExpanded = expandedOrders.has(order.OrderID);
// Split into station items and other-station items
const stationItems = isFiltering ? allRootItems.filter(i => isStationItem(i)) : allRootItems;
const otherItems = isFiltering ? allRootItems.filter(i => !isStationItem(i)) : [];
const hasOtherItems = otherItems.length > 0;
// Build expand toggle button
let expandToggle = '';
if (isFiltering && hasOtherItems) {
if (isExpanded) {
expandToggle = `<button class="expand-toggle" onclick="toggleExpand(${order.OrderID})">Hide ${otherItems.length} other item${otherItems.length > 1 ? 's' : ''}</button>`;
} else {
expandToggle = `<button class="expand-toggle" onclick="toggleExpand(${order.OrderID})">Show ${otherItems.length} other item${otherItems.length > 1 ? 's' : ''}</button>`;
}
}
// Render line items: station items always, other items only when expanded
let lineItemsHtml = stationItems.map(item => renderLineItem(item, order.LineItems, false)).join('');
if (isExpanded && hasOtherItems) {
lineItemsHtml += otherItems.map(item => renderLineItem(item, order.LineItems, true)).join('');
}
return `
<div class="order-card ${statusClass}">
@ -309,8 +350,9 @@ function renderOrder(order) {
${order.Remarks ? `<div class="order-remarks">Note: ${escapeHtml(order.Remarks)}</div>` : ''}
<div class="line-items">
${rootItems.map(item => renderLineItem(item, order.LineItems)).join('')}
${lineItemsHtml}
</div>
${expandToggle}
<div class="action-buttons">
${renderActionButtons(order)}
@ -320,11 +362,12 @@ function renderOrder(order) {
}
// Render line item with modifiers
function renderLineItem(item, allItems) {
function renderLineItem(item, allItems, isOtherStation = false) {
const modifiers = allItems.filter(mod => mod.ParentOrderLineItemID === item.OrderLineItemID);
console.log(`Item: ${item.Name} (ID: ${item.OrderLineItemID}) has ${modifiers.length} direct modifiers:`, modifiers.map(m => m.Name));
const otherClass = isOtherStation ? ' other-station' : '';
return `
<div class="line-item">
<div class="line-item${otherClass}">
<div class="line-item-main">
<div class="item-name">${escapeHtml(item.Name)}</div>
<div class="item-qty">x${item.Quantity}</div>