feat: add bulk operations to vendor pages

Add selection and bulk actions to products, orders, and inventory:
- Products: bulk activate/deactivate, feature/unfeature, delete
- Orders: bulk status update, CSV export
- Inventory: bulk stock adjustment, CSV export

All pages include select-all checkbox, row selection highlighting,
and action bars with operation buttons.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-01 16:58:27 +01:00
parent 646d789af7
commit abeacbe25a
7 changed files with 760 additions and 17 deletions

View File

@@ -64,8 +64,13 @@ function vendorOrders() {
// Modal states
showDetailModal: false,
showStatusModal: false,
showBulkStatusModal: false,
selectedOrder: null,
newStatus: '',
bulkStatus: '',
// Bulk selection
selectedOrders: [],
// Debounce timer
searchTimeout: null,
@@ -111,6 +116,16 @@ function vendorOrders() {
return pages;
},
// Computed: Check if all visible orders are selected
get allSelected() {
return this.orders.length > 0 && this.selectedOrders.length === this.orders.length;
},
// Computed: Check if some but not all orders are selected
get someSelected() {
return this.selectedOrders.length > 0 && this.selectedOrders.length < this.orders.length;
},
async init() {
vendorOrdersLog.info('Orders init() called');
@@ -349,6 +364,120 @@ function vendorOrders() {
this.pagination.page = pageNum;
this.loadOrders();
}
},
// ============================================================================
// BULK OPERATIONS
// ============================================================================
/**
* Toggle select all orders on current page
*/
toggleSelectAll() {
if (this.allSelected) {
this.selectedOrders = [];
} else {
this.selectedOrders = this.orders.map(o => o.id);
}
},
/**
* Toggle selection of a single order
*/
toggleSelect(orderId) {
const index = this.selectedOrders.indexOf(orderId);
if (index === -1) {
this.selectedOrders.push(orderId);
} else {
this.selectedOrders.splice(index, 1);
}
},
/**
* Check if order is selected
*/
isSelected(orderId) {
return this.selectedOrders.includes(orderId);
},
/**
* Clear all selections
*/
clearSelection() {
this.selectedOrders = [];
},
/**
* Open bulk status change modal
*/
openBulkStatusModal() {
if (this.selectedOrders.length === 0) return;
this.bulkStatus = '';
this.showBulkStatusModal = true;
},
/**
* Execute bulk status update
*/
async bulkUpdateStatus() {
if (this.selectedOrders.length === 0 || !this.bulkStatus) return;
this.saving = true;
try {
let successCount = 0;
for (const orderId of this.selectedOrders) {
try {
await apiClient.put(`/vendor/${this.vendorCode}/orders/${orderId}/status`, {
status: this.bulkStatus
});
successCount++;
} catch (error) {
vendorOrdersLog.warn(`Failed to update order ${orderId}:`, error);
}
}
Utils.showToast(`${successCount} order(s) updated to ${this.getStatusLabel(this.bulkStatus)}`, 'success');
this.showBulkStatusModal = false;
this.clearSelection();
await this.loadOrders();
} catch (error) {
vendorOrdersLog.error('Bulk status update failed:', error);
Utils.showToast(error.message || 'Failed to update orders', 'error');
} finally {
this.saving = false;
}
},
/**
* Export selected orders as CSV
*/
exportSelectedOrders() {
if (this.selectedOrders.length === 0) return;
const selectedOrderData = this.orders.filter(o => this.selectedOrders.includes(o.id));
// Build CSV content
const headers = ['Order ID', 'Date', 'Customer', 'Status', 'Total'];
const rows = selectedOrderData.map(o => [
o.order_number || o.id,
this.formatDate(o.created_at),
o.customer_name || o.customer_email || '-',
this.getStatusLabel(o.status),
this.formatPrice(o.total)
]);
const csvContent = [
headers.join(','),
...rows.map(row => row.map(cell => `"${cell}"`).join(','))
].join('\n');
// Download
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = `orders_export_${new Date().toISOString().split('T')[0]}.csv`;
link.click();
Utils.showToast(`Exported ${selectedOrderData.length} order(s)`, 'success');
}
};
}