465 lines
15 KiB
JavaScript
465 lines
15 KiB
JavaScript
// API base URL
|
|
const API_BASE_URL = '/api';
|
|
|
|
// Selected orders for actions
|
|
let selectedOrders = new Set();
|
|
|
|
// Show toast notification
|
|
function showToast(message, type = 'success') {
|
|
const toast = document.createElement('div');
|
|
toast.className = `fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg text-white ${
|
|
type === 'success' ? 'bg-green-600' : 'bg-red-600'
|
|
} transform translate-y-0 opacity-100 transition-all duration-300`;
|
|
toast.textContent = message;
|
|
document.body.appendChild(toast);
|
|
|
|
setTimeout(() => {
|
|
toast.style.transform = 'translateY(100%)';
|
|
toast.style.opacity = '0';
|
|
setTimeout(() => toast.remove(), 300);
|
|
}, 3000);
|
|
}
|
|
|
|
// Show loading state
|
|
function setLoading(isLoading) {
|
|
const buttons = document.querySelectorAll('button');
|
|
buttons.forEach(button => {
|
|
if (isLoading) {
|
|
button.disabled = true;
|
|
button.classList.add('opacity-50', 'cursor-not-allowed');
|
|
} else {
|
|
button.disabled = false;
|
|
button.classList.remove('opacity-50', 'cursor-not-allowed');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Fetch orders from the API
|
|
async function fetchOrders() {
|
|
try {
|
|
setLoading(true);
|
|
const searchRange = document.getElementById('searchRange').value;
|
|
const openOnly = document.getElementById('openOnly').checked;
|
|
|
|
const response = await fetch(`${API_BASE_URL}/orders?search_range=${searchRange}&open_only=${openOnly}`);
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch orders');
|
|
}
|
|
|
|
const orders = await response.json();
|
|
displayOrders(orders);
|
|
showToast('Orders loaded successfully');
|
|
} catch (error) {
|
|
showToast('Error fetching orders: ' + error.message, 'error');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
// Display orders in the UI
|
|
function displayOrders(orders) {
|
|
const ordersList = document.getElementById('ordersList');
|
|
ordersList.innerHTML = '';
|
|
|
|
if (!orders || orders.length === 0) {
|
|
ordersList.innerHTML = '<div class="col-span-full text-center text-gray-400 py-4">No orders found</div>';
|
|
return;
|
|
}
|
|
|
|
orders.forEach(order => {
|
|
const orderCard = document.createElement('div');
|
|
orderCard.className = `bg-gray-700 rounded-lg shadow-sm p-4 border border-gray-600 hover:shadow-md transition-shadow cursor-pointer ${
|
|
selectedOrders.has(order.orderNumber) ? 'ring-2 ring-blue-500' : ''
|
|
}`;
|
|
orderCard.dataset.orderId = order.orderNumber;
|
|
orderCard.innerHTML = `
|
|
<div class="flex flex-col h-full">
|
|
<div class="flex justify-between items-start mb-3">
|
|
<div class="flex-1 min-w-0">
|
|
<h3 class="text-lg font-bold text-blue-400 truncate">#${order.orderNumber || 'N/A'}</h3>
|
|
<p class="text-sm text-gray-400">${order.buyerName || 'N/A'}</p>
|
|
</div>
|
|
<span class="px-2 py-1 text-xs rounded-full ${
|
|
order.orderStatus === 'Open' ? 'bg-green-900/50 text-green-300' : 'bg-gray-600/50 text-gray-300'
|
|
}">${order.orderStatus || 'Unknown'}</span>
|
|
</div>
|
|
<div class="mt-auto">
|
|
<div class="flex justify-between items-center">
|
|
<p class="text-sm text-gray-400">${order.orderDate ? new Date(order.orderDate).toLocaleString() : 'N/A'}</p>
|
|
<p class="text-lg font-bold text-white">$${order.totalAmount ? order.totalAmount.toFixed(2) : '0.00'}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
ordersList.appendChild(orderCard);
|
|
|
|
// Add click event listener to the order card
|
|
orderCard.addEventListener('click', () => {
|
|
const orderId = orderCard.dataset.orderId;
|
|
if (selectedOrders.has(orderId)) {
|
|
selectedOrders.delete(orderId);
|
|
orderCard.classList.remove('ring-2', 'ring-blue-500');
|
|
} else {
|
|
selectedOrders.add(orderId);
|
|
orderCard.classList.add('ring-2', 'ring-blue-500');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// Select all orders on the page
|
|
function selectAllOrders() {
|
|
const orderCards = document.querySelectorAll('[data-order-id]');
|
|
const allSelected = orderCards.length > 0 && Array.from(orderCards).every(card => selectedOrders.has(card.dataset.orderId));
|
|
|
|
orderCards.forEach(card => {
|
|
const orderId = card.dataset.orderId;
|
|
if (allSelected) {
|
|
selectedOrders.delete(orderId);
|
|
card.classList.remove('ring-2', 'ring-blue-500');
|
|
} else {
|
|
selectedOrders.add(orderId);
|
|
card.classList.add('ring-2', 'ring-blue-500');
|
|
}
|
|
});
|
|
|
|
showToast(allSelected ? 'All orders deselected' : 'All orders selected');
|
|
}
|
|
|
|
// Generate pull sheets
|
|
async function generatePullSheets() {
|
|
try {
|
|
const orderIds = Array.from(selectedOrders);
|
|
if (orderIds.length === 0) {
|
|
showToast('Please select at least one order', 'error');
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
const response = await fetch(`${API_BASE_URL}/orders/generate-pull-sheets`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
order_ids: orderIds
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.detail || 'Failed to generate pull sheets');
|
|
}
|
|
|
|
showToast('Pull sheets generated successfully');
|
|
} catch (error) {
|
|
showToast('Error generating pull sheets: ' + error.message, 'error');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
// Generate packing slips
|
|
async function generatePackingSlips() {
|
|
try {
|
|
const orderIds = Array.from(selectedOrders);
|
|
if (orderIds.length === 0) {
|
|
showToast('Please select at least one order', 'error');
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
const response = await fetch(`${API_BASE_URL}/orders/generate-packing-slips`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
order_ids: orderIds
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.detail || 'Failed to generate packing slips');
|
|
}
|
|
|
|
showToast('Packing slips generated successfully');
|
|
} catch (error) {
|
|
showToast('Error generating packing slips: ' + error.message, 'error');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
// Generate address labels
|
|
async function generateAddressLabels() {
|
|
try {
|
|
const orderIds = Array.from(selectedOrders);
|
|
if (orderIds.length === 0) {
|
|
showToast('Please select at least one order', 'error');
|
|
return;
|
|
}
|
|
|
|
const labelType = document.getElementById('labelType').value;
|
|
|
|
setLoading(true);
|
|
const response = await fetch(`${API_BASE_URL}/orders/generate-address-labels`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
order_ids: orderIds,
|
|
label_type: labelType
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.detail || 'Failed to generate address labels');
|
|
}
|
|
|
|
showToast('Address labels generated successfully');
|
|
} catch (error) {
|
|
showToast('Error generating address labels: ' + error.message, 'error');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
// Show return labels modal
|
|
function showReturnLabelsModal() {
|
|
const modal = document.getElementById('returnLabelsModal');
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('flex');
|
|
}
|
|
|
|
// Close return labels modal
|
|
function closeReturnLabelsModal() {
|
|
const modal = document.getElementById('returnLabelsModal');
|
|
modal.classList.remove('flex');
|
|
modal.classList.add('hidden');
|
|
}
|
|
|
|
// Submit return labels request
|
|
async function submitReturnLabels() {
|
|
try {
|
|
const numberOfLabels = document.getElementById('numberOfLabels').value;
|
|
if (!numberOfLabels || numberOfLabels < 1) {
|
|
showToast('Please enter a valid number of labels', 'error');
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
const response = await fetch(`${API_BASE_URL}/orders/generate-return-labels`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
number_of_labels: parseInt(numberOfLabels)
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.detail || 'Failed to generate return labels');
|
|
}
|
|
|
|
showToast('Return labels generated successfully');
|
|
closeReturnLabelsModal();
|
|
} catch (error) {
|
|
showToast('Error generating return labels: ' + error.message, 'error');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
// Generate return labels (opens modal)
|
|
function generateReturnLabels() {
|
|
showReturnLabelsModal();
|
|
}
|
|
|
|
// Show Pirate Ship label modal
|
|
function showPirateShipModal() {
|
|
const modal = document.getElementById('pirateShipModal');
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('flex');
|
|
}
|
|
|
|
// Close Pirate Ship label modal
|
|
function closePirateShipModal() {
|
|
const modal = document.getElementById('pirateShipModal');
|
|
modal.classList.remove('flex');
|
|
modal.classList.add('hidden');
|
|
// Reset file input
|
|
document.getElementById('pirateShipFile').value = '';
|
|
}
|
|
|
|
// Submit Pirate Ship label
|
|
async function submitPirateShipLabel() {
|
|
try {
|
|
const fileInput = document.getElementById('pirateShipFile');
|
|
const file = fileInput.files[0];
|
|
|
|
if (!file) {
|
|
showToast('Please select a PDF file', 'error');
|
|
return;
|
|
}
|
|
|
|
if (file.type !== 'application/pdf') {
|
|
showToast('Please select a valid PDF file', 'error');
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
|
|
const response = await fetch(`${API_BASE_URL}/orders/print-pirate-ship-label`, {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.detail || 'Failed to print Pirate Ship label');
|
|
}
|
|
|
|
showToast('Pirate Ship label printed successfully');
|
|
closePirateShipModal();
|
|
} catch (error) {
|
|
showToast('Error printing Pirate Ship label: ' + error.message, 'error');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
// Show set labels modal
|
|
function showSetLabelsModal() {
|
|
const modal = document.getElementById('setLabelsModal');
|
|
modal.classList.remove('hidden');
|
|
modal.classList.add('flex');
|
|
fetchAvailableSets();
|
|
|
|
// Add event listener for search input
|
|
const searchInput = document.getElementById('setSearch');
|
|
searchInput.addEventListener('input', filterSets);
|
|
}
|
|
|
|
// Close set labels modal
|
|
function closeSetLabelsModal() {
|
|
const modal = document.getElementById('setLabelsModal');
|
|
modal.classList.remove('flex');
|
|
modal.classList.add('hidden');
|
|
|
|
// Clear search input
|
|
document.getElementById('setSearch').value = '';
|
|
}
|
|
|
|
// Filter sets based on search input
|
|
function filterSets() {
|
|
const searchTerm = document.getElementById('setSearch').value.toLowerCase();
|
|
const setItems = document.querySelectorAll('#setLabelsList > div');
|
|
|
|
setItems.forEach(item => {
|
|
const label = item.querySelector('label');
|
|
const text = label.textContent.toLowerCase();
|
|
if (text.includes(searchTerm)) {
|
|
item.style.display = 'flex';
|
|
} else {
|
|
item.style.display = 'none';
|
|
}
|
|
});
|
|
}
|
|
|
|
// Fetch available sets from the API
|
|
async function fetchAvailableSets() {
|
|
try {
|
|
setLoading(true);
|
|
const response = await fetch(`${API_BASE_URL}/set-labels/available-sets`);
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch available sets');
|
|
}
|
|
|
|
const sets = await response.json();
|
|
displayAvailableSets(sets);
|
|
} catch (error) {
|
|
showToast('Error fetching available sets: ' + error.message, 'error');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
// Display available sets in the modal
|
|
function displayAvailableSets(sets) {
|
|
const setList = document.getElementById('setLabelsList');
|
|
setList.innerHTML = '';
|
|
|
|
if (!sets || sets.length === 0) {
|
|
setList.innerHTML = '<div class="text-center text-gray-400 py-4">No sets available</div>';
|
|
return;
|
|
}
|
|
|
|
// Sort sets alphabetically by name
|
|
sets.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
sets.forEach(set => {
|
|
const setItem = document.createElement('div');
|
|
setItem.className = 'flex items-center p-2 hover:bg-gray-600 rounded-lg cursor-pointer';
|
|
setItem.innerHTML = `
|
|
<input type="checkbox" id="set-${set.code}" class="rounded border-gray-600 bg-gray-800 text-teal-600 focus:ring-teal-500">
|
|
<label for="set-${set.code}" class="ml-2 text-gray-300">${set.name} (${set.code})</label>
|
|
`;
|
|
setList.appendChild(setItem);
|
|
});
|
|
|
|
// Trigger initial filter in case there's text in the search box
|
|
filterSets();
|
|
}
|
|
|
|
// Submit set labels request
|
|
async function submitSetLabels() {
|
|
try {
|
|
const selectedSets = Array.from(document.querySelectorAll('#setLabelsList input[type="checkbox"]:checked'))
|
|
.map(checkbox => checkbox.id.replace('set-', ''));
|
|
|
|
if (selectedSets.length === 0) {
|
|
showToast('Please select at least one set', 'error');
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
const response = await fetch(`${API_BASE_URL}/set-labels/generate`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
sets: selectedSets
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.detail || 'Failed to generate set labels');
|
|
}
|
|
|
|
showToast('Set labels generated successfully');
|
|
closeSetLabelsModal();
|
|
} catch (error) {
|
|
showToast('Error generating set labels: ' + error.message, 'error');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
// Load orders when page loads
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
fetchOrders();
|
|
|
|
// Add event listeners for search range and open only checkbox
|
|
document.getElementById('searchRange').addEventListener('change', fetchOrders);
|
|
document.getElementById('openOnly').addEventListener('change', fetchOrders);
|
|
});
|