- Created admin.html for BusinessID=17 to manually create test orders - Service point selector with all tables - Dynamic menu item rows with quantities - Three-step order creation: cart → line items → submit - Modern gradient UI with purple theme - Success/error notifications - Form auto-resets after successful order creation Technical details: - Fixed case sensitivity: API returns uppercase keys (ORDER, ORDERLINEITEMS) - Fixed parameter naming: BusinessID (not OrderBusinessID) - Added links to KDS display and debug view 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
427 lines
13 KiB
HTML
427 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>KDS Admin - Manual Task Creation</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
padding: 20px;
|
|
}
|
|
|
|
.container {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
border-radius: 12px;
|
|
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
|
padding: 30px;
|
|
}
|
|
|
|
h1 {
|
|
color: #333;
|
|
margin-bottom: 10px;
|
|
font-size: 28px;
|
|
}
|
|
|
|
.subtitle {
|
|
color: #666;
|
|
margin-bottom: 30px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
color: #333;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
}
|
|
|
|
select, input, textarea {
|
|
width: 100%;
|
|
padding: 12px;
|
|
border: 2px solid #e1e8ed;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
transition: border-color 0.3s;
|
|
}
|
|
|
|
select:focus, input:focus, textarea:focus {
|
|
outline: none;
|
|
border-color: #667eea;
|
|
}
|
|
|
|
textarea {
|
|
resize: vertical;
|
|
min-height: 80px;
|
|
}
|
|
|
|
.items-section {
|
|
background: #f8f9fa;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.item-row {
|
|
background: white;
|
|
padding: 15px;
|
|
border-radius: 6px;
|
|
margin-bottom: 10px;
|
|
display: grid;
|
|
grid-template-columns: 2fr 1fr auto;
|
|
gap: 10px;
|
|
align-items: center;
|
|
}
|
|
|
|
.item-row select {
|
|
width: 100%;
|
|
}
|
|
|
|
.item-row input {
|
|
width: 100%;
|
|
}
|
|
|
|
.btn {
|
|
padding: 12px 24px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #6c757d;
|
|
color: white;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #5a6268;
|
|
}
|
|
|
|
.btn-success {
|
|
background: #28a745;
|
|
color: white;
|
|
width: 100%;
|
|
padding: 15px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.btn-success:hover {
|
|
background: #218838;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 20px rgba(40, 167, 69, 0.4);
|
|
}
|
|
|
|
.btn-remove {
|
|
background: #dc3545;
|
|
color: white;
|
|
padding: 8px 16px;
|
|
}
|
|
|
|
.btn-remove:hover {
|
|
background: #c82333;
|
|
}
|
|
|
|
.alert {
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.alert-success {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
border: 1px solid #c3e6cb;
|
|
}
|
|
|
|
.alert-error {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
border: 1px solid #f5c6cb;
|
|
}
|
|
|
|
.loading {
|
|
text-align: center;
|
|
padding: 20px;
|
|
color: #666;
|
|
}
|
|
|
|
.actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-top: 30px;
|
|
}
|
|
|
|
.quick-actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>KDS Admin - Manual Task Creation</h1>
|
|
<p class="subtitle">Create orders manually for testing and demonstration (BusinessID: 17)</p>
|
|
|
|
<div id="alert"></div>
|
|
|
|
<div class="form-group">
|
|
<label>Service Point (Table/Location)</label>
|
|
<select id="servicePointSelect">
|
|
<option value="">Loading service points...</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="items-section">
|
|
<label>Order Items</label>
|
|
<div id="itemsContainer"></div>
|
|
<button class="btn btn-secondary" onclick="addItemRow()">+ Add Item</button>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Special Instructions / Remarks</label>
|
|
<textarea id="remarks" placeholder="e.g., Extra napkins, no ice, etc."></textarea>
|
|
</div>
|
|
|
|
<div class="actions">
|
|
<button class="btn btn-success" onclick="createOrder()">Create & Submit Order</button>
|
|
</div>
|
|
|
|
<div class="quick-actions" style="margin-top: 30px;">
|
|
<button class="btn btn-secondary" onclick="window.location.href='index.html'">View KDS Display</button>
|
|
<button class="btn btn-secondary" onclick="window.location.href='debug.html'">Debug View</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const BUSINESS_ID = 17;
|
|
const API_BASE = '../api';
|
|
|
|
let menuItems = [];
|
|
let servicePoints = [];
|
|
|
|
async function init() {
|
|
await loadServicePoints();
|
|
await loadMenuItems();
|
|
addItemRow(); // Add first item row
|
|
}
|
|
|
|
async function loadServicePoints() {
|
|
try {
|
|
const response = await fetch(`${API_BASE}/servicepoints/list.cfm`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ BusinessID: BUSINESS_ID })
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.OK && data.ServicePoints) {
|
|
servicePoints = data.ServicePoints;
|
|
|
|
const select = document.getElementById('servicePointSelect');
|
|
select.innerHTML = '<option value="">Select a service point...</option>';
|
|
|
|
servicePoints.forEach(sp => {
|
|
const option = document.createElement('option');
|
|
option.value = sp.ServicePointID;
|
|
option.textContent = `${sp.ServicePointName} (ID: ${sp.ServicePointID})`;
|
|
select.appendChild(option);
|
|
});
|
|
}
|
|
} catch (error) {
|
|
showAlert('Error loading service points: ' + error.message, 'error');
|
|
}
|
|
}
|
|
|
|
async function loadMenuItems() {
|
|
try {
|
|
const response = await fetch(`${API_BASE}/menu/items.cfm`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ BusinessID: BUSINESS_ID })
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.OK && data.Items) {
|
|
// Filter only root items (no parent)
|
|
menuItems = data.Items.filter(item => !item.ItemParentItemID || item.ItemParentItemID === 0);
|
|
}
|
|
} catch (error) {
|
|
showAlert('Error loading menu items: ' + error.message, 'error');
|
|
}
|
|
}
|
|
|
|
function addItemRow() {
|
|
const container = document.getElementById('itemsContainer');
|
|
const row = document.createElement('div');
|
|
row.className = 'item-row';
|
|
|
|
const itemSelect = document.createElement('select');
|
|
itemSelect.innerHTML = '<option value="">Select item...</option>';
|
|
menuItems.forEach(item => {
|
|
const option = document.createElement('option');
|
|
option.value = item.ItemID;
|
|
option.textContent = `${item.ItemName} ($${parseFloat(item.ItemPrice).toFixed(2)})`;
|
|
option.dataset.price = item.ItemPrice;
|
|
itemSelect.appendChild(option);
|
|
});
|
|
|
|
const qtyInput = document.createElement('input');
|
|
qtyInput.type = 'number';
|
|
qtyInput.value = '1';
|
|
qtyInput.min = '1';
|
|
qtyInput.placeholder = 'Qty';
|
|
|
|
const removeBtn = document.createElement('button');
|
|
removeBtn.className = 'btn btn-remove';
|
|
removeBtn.textContent = 'Remove';
|
|
removeBtn.onclick = () => row.remove();
|
|
|
|
row.appendChild(itemSelect);
|
|
row.appendChild(qtyInput);
|
|
row.appendChild(removeBtn);
|
|
container.appendChild(row);
|
|
}
|
|
|
|
async function createOrder() {
|
|
const servicePointId = document.getElementById('servicePointSelect').value;
|
|
const remarks = document.getElementById('remarks').value;
|
|
const itemRows = document.querySelectorAll('.item-row');
|
|
|
|
if (!servicePointId) {
|
|
showAlert('Please select a service point', 'error');
|
|
return;
|
|
}
|
|
|
|
const items = [];
|
|
for (const row of itemRows) {
|
|
const select = row.querySelector('select');
|
|
const qtyInput = row.querySelector('input');
|
|
|
|
if (select.value) {
|
|
items.push({
|
|
itemId: parseInt(select.value),
|
|
quantity: parseInt(qtyInput.value) || 1
|
|
});
|
|
}
|
|
}
|
|
|
|
if (items.length === 0) {
|
|
showAlert('Please add at least one item', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Step 1: Create cart order (using UserID=1 for admin)
|
|
const cartResponse = await fetch(`${API_BASE}/orders/getOrCreateCart.cfm`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
BusinessID: BUSINESS_ID,
|
|
OrderServicePointID: parseInt(servicePointId),
|
|
OrderTypeID: 1, // Dine-in
|
|
OrderUserID: 1 // Admin user
|
|
})
|
|
});
|
|
|
|
const cartData = await cartResponse.json();
|
|
|
|
if (!cartData.OK) {
|
|
throw new Error(cartData.MESSAGE || 'Failed to create cart');
|
|
}
|
|
|
|
const orderId = cartData.ORDER.OrderID;
|
|
|
|
// Step 2: Add items to order
|
|
for (const item of items) {
|
|
const itemResponse = await fetch(`${API_BASE}/orders/setLineItem.cfm`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
OrderID: orderId,
|
|
ParentOrderLineItemID: 0,
|
|
ItemID: item.itemId,
|
|
IsSelected: true,
|
|
Quantity: item.quantity,
|
|
Remark: ''
|
|
})
|
|
});
|
|
|
|
const itemData = await itemResponse.json();
|
|
if (!itemData.OK) {
|
|
throw new Error('Failed to add item: ' + itemData.MESSAGE);
|
|
}
|
|
}
|
|
|
|
// Step 3: Submit the order
|
|
const submitResponse = await fetch(`${API_BASE}/orders/submit.cfm`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ OrderID: orderId })
|
|
});
|
|
|
|
const submitData = await submitResponse.json();
|
|
|
|
if (!submitData.OK) {
|
|
throw new Error(submitData.MESSAGE || 'Failed to submit order');
|
|
}
|
|
|
|
showAlert(`Order #${orderId} created and submitted successfully!`, 'success');
|
|
|
|
// Reset form
|
|
document.getElementById('remarks').value = '';
|
|
document.getElementById('itemsContainer').innerHTML = '';
|
|
addItemRow();
|
|
|
|
} catch (error) {
|
|
showAlert('Error creating order: ' + error.message, 'error');
|
|
console.error(error);
|
|
}
|
|
}
|
|
|
|
function showAlert(message, type) {
|
|
const alertDiv = document.getElementById('alert');
|
|
alertDiv.className = `alert alert-${type}`;
|
|
alertDiv.textContent = message;
|
|
alertDiv.style.display = 'block';
|
|
|
|
setTimeout(() => {
|
|
alertDiv.style.display = 'none';
|
|
}, 5000);
|
|
}
|
|
|
|
// Initialize on page load
|
|
init();
|
|
</script>
|
|
</body>
|
|
</html>
|