823 lines
20 KiB
HTML
823 lines
20 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Batch Management</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||
min-height: 100vh;
|
||
padding: 20px;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1600px;
|
||
margin: 0 auto;
|
||
background: white;
|
||
border-radius: 16px;
|
||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.header {
|
||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||
color: white;
|
||
padding: 30px;
|
||
text-align: center;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 28px;
|
||
font-weight: 600;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.controls {
|
||
padding: 25px 30px;
|
||
background: #f8f9fa;
|
||
display: flex;
|
||
gap: 15px;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
border-bottom: 1px solid #e9ecef;
|
||
}
|
||
|
||
.search-box {
|
||
flex: 1;
|
||
min-width: 250px;
|
||
position: relative;
|
||
}
|
||
|
||
.search-box input {
|
||
width: 100%;
|
||
padding: 12px 16px;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.search-box input:focus {
|
||
outline: none;
|
||
border-color: #2c3e50;
|
||
box-shadow: 0 0 0 3px rgba(44, 62, 80, 0.1);
|
||
}
|
||
|
||
.btn {
|
||
padding: 12px 24px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(44, 62, 80, 0.4);
|
||
}
|
||
|
||
.btn-success {
|
||
background: linear-gradient(135deg, #27ae60 0%, #229954 100%);
|
||
color: white;
|
||
}
|
||
|
||
.btn-success:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(39, 174, 96, 0.4);
|
||
}
|
||
|
||
.table-container {
|
||
overflow-x: auto;
|
||
padding: 0 30px 30px;
|
||
}
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
thead {
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
th {
|
||
padding: 16px;
|
||
text-align: left;
|
||
font-weight: 600;
|
||
color: #495057;
|
||
font-size: 13px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
position: relative;
|
||
}
|
||
|
||
th:hover {
|
||
background: #e9ecef;
|
||
}
|
||
|
||
th.sorted::after {
|
||
content: ' ▼';
|
||
font-size: 10px;
|
||
}
|
||
|
||
th.sorted.asc::after {
|
||
content: ' ▲';
|
||
}
|
||
|
||
td {
|
||
padding: 16px;
|
||
border-bottom: 1px solid #f1f3f5;
|
||
color: #495057;
|
||
font-size: 14px;
|
||
}
|
||
|
||
tbody tr {
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
tbody tr:hover {
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.batch-row {
|
||
cursor: pointer;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.batch-row:hover {
|
||
background: #e3f2fd !important;
|
||
}
|
||
|
||
.batch-row.expanded {
|
||
background: #e8f5e9 !important;
|
||
}
|
||
|
||
.items-row {
|
||
display: none;
|
||
background: #fafafa;
|
||
}
|
||
|
||
.items-row.show {
|
||
display: table-row;
|
||
}
|
||
|
||
.items-cell {
|
||
padding: 20px !important;
|
||
border-left: 4px solid #27ae60;
|
||
}
|
||
|
||
.items-container {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||
gap: 15px;
|
||
}
|
||
|
||
.items-section {
|
||
background: white;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.items-section h4 {
|
||
color: #2c3e50;
|
||
margin-bottom: 10px;
|
||
font-size: 14px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.items-section.mix h4 {
|
||
color: #e74c3c;
|
||
}
|
||
|
||
.item-card {
|
||
background: #f8f9fa;
|
||
padding: 10px;
|
||
border-radius: 6px;
|
||
margin-bottom: 8px;
|
||
border-left: 3px solid #27ae60;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.items-section.mix .item-card {
|
||
border-left-color: #e74c3c;
|
||
}
|
||
|
||
.item-card .label {
|
||
color: #6c757d;
|
||
font-weight: 500;
|
||
display: inline-block;
|
||
width: 40px;
|
||
}
|
||
|
||
.item-card .value {
|
||
color: #495057;
|
||
}
|
||
|
||
.badge {
|
||
display: inline-block;
|
||
padding: 4px 12px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.badge-success {
|
||
background: #d4edda;
|
||
color: #155724;
|
||
}
|
||
|
||
.badge-warning {
|
||
background: #fff3cd;
|
||
color: #856404;
|
||
}
|
||
|
||
.delete-btn {
|
||
background: #dc3545;
|
||
color: white;
|
||
border: none;
|
||
padding: 6px 12px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.delete-btn:hover {
|
||
background: #c82333;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.expand-icon {
|
||
display: inline-block;
|
||
transition: transform 0.3s;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.expand-icon.expanded {
|
||
transform: rotate(90deg);
|
||
}
|
||
|
||
.pagination {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 30px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.page-btn {
|
||
padding: 8px 16px;
|
||
border: 2px solid #e9ecef;
|
||
background: white;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.page-btn:hover:not(:disabled) {
|
||
border-color: #2c3e50;
|
||
color: #2c3e50;
|
||
}
|
||
|
||
.page-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.page-btn.active {
|
||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||
color: white;
|
||
border-color: transparent;
|
||
}
|
||
|
||
.page-info {
|
||
color: #6c757d;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.loading {
|
||
text-align: center;
|
||
padding: 40px;
|
||
color: #6c757d;
|
||
}
|
||
|
||
.empty {
|
||
text-align: center;
|
||
padding: 60px;
|
||
color: #adb5bd;
|
||
}
|
||
|
||
.empty h3 {
|
||
margin-bottom: 10px;
|
||
color: #6c757d;
|
||
}
|
||
|
||
.modal {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0,0,0,0.5);
|
||
z-index: 1000;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.modal.active {
|
||
display: flex;
|
||
}
|
||
|
||
.modal-content {
|
||
background: white;
|
||
padding: 30px;
|
||
border-radius: 12px;
|
||
width: 90%;
|
||
max-width: 800px;
|
||
max-height: 80vh;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.modal h2 {
|
||
margin-bottom: 20px;
|
||
color: #495057;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
color: #495057;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-group input, .form-group textarea {
|
||
width: 100%;
|
||
padding: 10px;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
font-family: inherit;
|
||
}
|
||
|
||
.form-group textarea {
|
||
min-height: 150px;
|
||
resize: vertical;
|
||
}
|
||
|
||
.modal-buttons {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: #6c757d;
|
||
color: white;
|
||
flex: 1;
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
background: #5a6268;
|
||
}
|
||
|
||
.help-text {
|
||
font-size: 12px;
|
||
color: #6c757d;
|
||
margin-top: 5px;
|
||
}
|
||
|
||
.json-example {
|
||
background: #f8f9fa;
|
||
padding: 15px;
|
||
border-radius: 6px;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 12px;
|
||
overflow-x: auto;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.items-container {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.controls {
|
||
padding: 20px;
|
||
}
|
||
|
||
.search-box {
|
||
min-width: 100%;
|
||
}
|
||
|
||
.table-container {
|
||
padding: 0 20px 20px;
|
||
}
|
||
|
||
table {
|
||
font-size: 12px;
|
||
}
|
||
|
||
th, td {
|
||
padding: 10px 8px;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 22px;
|
||
}
|
||
|
||
.pagination {
|
||
padding: 20px;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1>📦 Batch Management System</h1>
|
||
<p>Manage batches with items and mixed items</p>
|
||
</div>
|
||
|
||
<div class="controls">
|
||
<div class="search-box">
|
||
<input type="text" id="searchInput" placeholder="Search by batch name or ID...">
|
||
</div>
|
||
<button class="btn btn-success" onclick="openCreateModal()">
|
||
➕ Create Batch
|
||
</button>
|
||
</div>
|
||
|
||
<div class="table-container">
|
||
<div id="loading" class="loading" style="display: none;">
|
||
Loading batches...
|
||
</div>
|
||
|
||
<div id="empty" class="empty" style="display: none;">
|
||
<h3>No batches found</h3>
|
||
<p>Create your first batch to get started!</p>
|
||
</div>
|
||
|
||
<table id="batchTable" style="display: none;">
|
||
<thead>
|
||
<tr>
|
||
<th onclick="sortTable('id')" style="width: 80px;">ID</th>
|
||
<th onclick="sortTable('batch_name')">Batch Name</th>
|
||
<th style="width: 120px;">Items</th>
|
||
<th style="width: 120px;">Mixed Items</th>
|
||
<th onclick="sortTable('createdAt')" style="width: 180px;">Created At</th>
|
||
<th style="width: 100px;">Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="batchBody"></tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="pagination" id="pagination"></div>
|
||
</div>
|
||
|
||
<!-- Create Batch Modal -->
|
||
<div id="createModal" class="modal">
|
||
<div class="modal-content">
|
||
<h2>Create New Batch</h2>
|
||
|
||
<div class="form-group">
|
||
<label>Batch Name *</label>
|
||
<input type="text" id="batchNameInput" placeholder="e.g., BATCH_20250210141530">
|
||
<div class="help-text">Format: BATCH_yyyymmddhhmmss or any unique name</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Items (Valid - Same MPN) *</label>
|
||
<textarea id="itemsInput" placeholder='Enter JSON array of items...'></textarea>
|
||
<div class="json-example">
|
||
Example:
|
||
[
|
||
{"mpn": "CN_M378A5143DB0-CPB_1620", "sn": "U03A00062074544398"},
|
||
{"mpn": "CN_M378A5143DB0-CPB_1620", "sn": "U03A00062074544399"}
|
||
]</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Mixed Items (Different MPN) - Optional</label>
|
||
<textarea id="itemsMixInput" placeholder='Enter JSON array of mixed items...'></textarea>
|
||
<div class="json-example">
|
||
Example:
|
||
[
|
||
{"mpn": "CN_M378A5143DB0-CPB_1621", "sn": "U03A00062074544403"}
|
||
]</div>
|
||
</div>
|
||
|
||
<div class="modal-buttons">
|
||
<button class="btn btn-secondary" onclick="closeCreateModal()">Cancel</button>
|
||
<button class="btn btn-success" onclick="createBatch()">Create Batch</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
let currentPage = 1;
|
||
let currentSort = 'id';
|
||
let currentOrder = 'DESC';
|
||
let searchTerm = '';
|
||
let URL = window.location.hostname === 'localhost'
|
||
? 'http://localhost:4444/api/'
|
||
: 'https://logs1.danielvu.com/mobile-api/';
|
||
|
||
let expandedBatches = new Set();
|
||
|
||
async function loadBatches() {
|
||
document.getElementById('loading').style.display = 'block';
|
||
document.getElementById('batchTable').style.display = 'none';
|
||
document.getElementById('empty').style.display = 'none';
|
||
|
||
try {
|
||
const response = await fetch(`${URL}batch/get-all?page=${currentPage}&sortBy=${currentSort}&sortOrder=${currentOrder}&search=${encodeURIComponent(searchTerm)}`);
|
||
const data = await response.json();
|
||
|
||
document.getElementById('loading').style.display = 'none';
|
||
|
||
if (data.batches.length === 0) {
|
||
document.getElementById('empty').style.display = 'block';
|
||
} else {
|
||
document.getElementById('batchTable').style.display = 'table';
|
||
renderBatches(data.batches);
|
||
renderPagination(data);
|
||
}
|
||
|
||
updateSortIndicators();
|
||
} catch (error) {
|
||
console.error('Error loading batches:', error);
|
||
document.getElementById('loading').style.display = 'none';
|
||
alert('Error loading batches. Please check if the server is running.');
|
||
}
|
||
}
|
||
|
||
function renderBatches(batches) {
|
||
const tbody = document.getElementById('batchBody');
|
||
tbody.innerHTML = batches.map(batch => {
|
||
const date = new Date(batch.createdAt + "Z");
|
||
const formattedDate = date.toLocaleString('en-US', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit',
|
||
hour12: false
|
||
});
|
||
|
||
const isExpanded = expandedBatches.has(batch.id);
|
||
|
||
return `
|
||
<tr class="batch-row ${isExpanded ? 'expanded' : ''}" onclick="toggleBatch(${batch.id})">
|
||
<td>
|
||
<span class="expand-icon ${isExpanded ? 'expanded' : ''}">▶</span>
|
||
${batch.id}
|
||
</td>
|
||
<td><strong>${batch.batch_name}</strong></td>
|
||
<td><span class="badge badge-success">${batch.items.length}</span></td>
|
||
<td><span class="badge badge-warning">${batch.items_mix.length}</span></td>
|
||
<td>${formattedDate}</td>
|
||
<td>
|
||
<button class="delete-btn" onclick="event.stopPropagation(); deleteBatch(${batch.id})">Delete</button>
|
||
</td>
|
||
</tr>
|
||
<tr class="items-row ${isExpanded ? 'show' : ''}" id="items-${batch.id}">
|
||
<td colspan="6" class="items-cell">
|
||
<div class="items-container">
|
||
<div class="items-section">
|
||
<h4>✅ Valid Items (${batch.items.length})</h4>
|
||
${batch.items.length > 0 ? batch.items.map(item => `
|
||
<div class="item-card">
|
||
<div><span class="label">MPN:</span> <span class="value">${item.mpn}</span></div>
|
||
<div><span class="label">SN:</span> <span class="value">${item.sn}</span></div>
|
||
</div>
|
||
`).join('') : '<p style="color: #999;">No items</p>'}
|
||
</div>
|
||
<div class="items-section mix">
|
||
<h4>⚠️ Mixed Items (${batch.items_mix.length})</h4>
|
||
${batch.items_mix.length > 0 ? batch.items_mix.map(item => `
|
||
<div class="item-card">
|
||
<div><span class="label">MPN:</span> <span class="value">${item.mpn}</span></div>
|
||
<div><span class="label">SN:</span> <span class="value">${item.sn}</span></div>
|
||
</div>
|
||
`).join('') : '<p style="color: #999;">No mixed items</p>'}
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
`;
|
||
}).join('');
|
||
}
|
||
|
||
function toggleBatch(batchId) {
|
||
if (expandedBatches.has(batchId)) {
|
||
expandedBatches.delete(batchId);
|
||
} else {
|
||
expandedBatches.add(batchId);
|
||
}
|
||
loadBatches();
|
||
}
|
||
|
||
function renderPagination(data) {
|
||
const pagination = document.getElementById('pagination');
|
||
let html = '';
|
||
|
||
html += `<button class="page-btn" ${currentPage === 1 ? 'disabled' : ''} onclick="changePage(${currentPage - 1})">Previous</button>`;
|
||
|
||
const startPage = Math.max(1, currentPage - 2);
|
||
const endPage = Math.min(data.totalPages, currentPage + 2);
|
||
|
||
for (let i = startPage; i <= endPage; i++) {
|
||
html += `<button class="page-btn ${i === currentPage ? 'active' : ''}" onclick="changePage(${i})">${i}</button>`;
|
||
}
|
||
|
||
html += `<button class="page-btn" ${currentPage === data.totalPages ? 'disabled' : ''} onclick="changePage(${currentPage + 1})">Next</button>`;
|
||
html += `<span class="page-info">Total: ${data.total} batches</span>`;
|
||
|
||
pagination.innerHTML = html;
|
||
}
|
||
|
||
function changePage(page) {
|
||
currentPage = page;
|
||
loadBatches();
|
||
}
|
||
|
||
function sortTable(column) {
|
||
if (currentSort === column) {
|
||
currentOrder = currentOrder === 'DESC' ? 'ASC' : 'DESC';
|
||
} else {
|
||
currentSort = column;
|
||
currentOrder = 'DESC';
|
||
}
|
||
currentPage = 1;
|
||
loadBatches();
|
||
}
|
||
|
||
function updateSortIndicators() {
|
||
document.querySelectorAll('th').forEach(th => {
|
||
th.classList.remove('sorted', 'asc');
|
||
});
|
||
|
||
const columns = ['id', 'batch_name', 'createdAt'];
|
||
const index = columns.indexOf(currentSort);
|
||
if (index !== -1) {
|
||
const th = document.querySelectorAll('th')[index];
|
||
th.classList.add('sorted');
|
||
if (currentOrder === 'ASC') {
|
||
th.classList.add('asc');
|
||
}
|
||
}
|
||
}
|
||
|
||
let searchTimeout;
|
||
document.getElementById('searchInput').addEventListener('input', (e) => {
|
||
clearTimeout(searchTimeout);
|
||
searchTimeout = setTimeout(() => {
|
||
searchTerm = e.target.value;
|
||
currentPage = 1;
|
||
expandedBatches.clear();
|
||
loadBatches();
|
||
}, 300);
|
||
});
|
||
|
||
async function deleteBatch(id) {
|
||
if (!confirm('Are you sure you want to delete this batch and all its items?')) return;
|
||
|
||
try {
|
||
const response = await fetch(`${URL}batch/delete/${id}`, {
|
||
method: 'DELETE'
|
||
});
|
||
|
||
if (response.ok) {
|
||
expandedBatches.delete(id);
|
||
loadBatches();
|
||
} else {
|
||
alert('Error deleting batch');
|
||
}
|
||
} catch (error) {
|
||
console.error('Error deleting batch:', error);
|
||
alert('Error deleting batch');
|
||
}
|
||
}
|
||
|
||
function openCreateModal() {
|
||
document.getElementById('createModal').classList.add('active');
|
||
// Auto-generate batch name with timestamp
|
||
const now = new Date();
|
||
const timestamp = now.getFullYear() +
|
||
String(now.getMonth() + 1).padStart(2, '0') +
|
||
String(now.getDate()).padStart(2, '0') +
|
||
String(now.getHours()).padStart(2, '0') +
|
||
String(now.getMinutes()).padStart(2, '0') +
|
||
String(now.getSeconds()).padStart(2, '0');
|
||
document.getElementById('batchNameInput').value = `BATCH_${timestamp}`;
|
||
}
|
||
|
||
function closeCreateModal() {
|
||
document.getElementById('createModal').classList.remove('active');
|
||
document.getElementById('batchNameInput').value = '';
|
||
document.getElementById('itemsInput').value = '';
|
||
document.getElementById('itemsMixInput').value = '';
|
||
}
|
||
|
||
async function createBatch() {
|
||
const batch_name = document.getElementById('batchNameInput').value.trim();
|
||
const itemsText = document.getElementById('itemsInput').value.trim();
|
||
const itemsMixText = document.getElementById('itemsMixInput').value.trim();
|
||
|
||
if (!batch_name) {
|
||
alert('Please enter a batch name');
|
||
return;
|
||
}
|
||
|
||
if (!itemsText) {
|
||
alert('Please enter at least one item');
|
||
return;
|
||
}
|
||
|
||
let items, items_mix;
|
||
|
||
try {
|
||
items = JSON.parse(itemsText);
|
||
if (!Array.isArray(items) || items.length === 0) {
|
||
alert('Items must be a non-empty array');
|
||
return;
|
||
}
|
||
} catch (e) {
|
||
alert('Invalid JSON format for items: ' + e.message);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
items_mix = itemsMixText ? JSON.parse(itemsMixText) : [];
|
||
if (!Array.isArray(items_mix)) {
|
||
alert('Mixed items must be an array');
|
||
return;
|
||
}
|
||
} catch (e) {
|
||
alert('Invalid JSON format for mixed items: ' + e.message);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(URL + 'batch/save', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({ batch_name, items, items_mix })
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (response.ok) {
|
||
closeCreateModal();
|
||
expandedBatches.clear();
|
||
loadBatches();
|
||
alert(`Batch created successfully!\nItems: ${result.inserted_items}\nMixed Items: ${result.inserted_mix_items}`);
|
||
} else {
|
||
alert('Error creating batch: ' + result.error);
|
||
}
|
||
} catch (error) {
|
||
console.error('Error creating batch:', error);
|
||
alert('Error creating batch: ' + error.message);
|
||
}
|
||
}
|
||
|
||
// Load batches on page load
|
||
loadBatches();
|
||
</script>
|
||
</body>
|
||
</html> |