Account and Shipment Changes

This commit is contained in:
Utkarsh Khedkar
2025-12-04 11:21:46 +05:30
parent e7fef314fc
commit 4dab96b8d1
5 changed files with 190 additions and 490 deletions

View File

@@ -2,7 +2,6 @@
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\Shipment; use App\Models\Shipment;
@@ -107,9 +106,6 @@ class ShipmentController extends Controller
'order_qty' => $order->qty, 'order_qty' => $order->qty,
'order_ttl_qty' => $order->ttl_qty, 'order_ttl_qty' => $order->ttl_qty,
'order_ttl_amount' => $order->ttl_amount, 'order_ttl_amount' => $order->ttl_amount,
'order_cbm' => $order->cbm,
'order_ttl_cbm' => $order->ttl_cbm,
'order_kg' => $order->kg,
'order_ttl_kg' => $order->ttl_kg, 'order_ttl_kg' => $order->ttl_kg,
]); ]);
} }
@@ -117,110 +113,22 @@ class ShipmentController extends Controller
return redirect()->back()->with('success', "Shipment $newShipmentId created successfully!"); return redirect()->back()->with('success', "Shipment $newShipmentId created successfully!");
} }
public function edit($id) /**
{ * Show shipment details (for modal popup)
$shipment = Shipment::with('orders')->findOrFail($id); */
return view('admin.shipments.show', [
'shipment' => $shipment,
'orders' => $shipment->orders,
'isViewMode' => false // This is the edit mode
]);
}
public function show($id) public function show($id)
{ {
$mode = request()->get('mode', 'view');
$shipment = Shipment::with(['items.order'])->findOrFail($id);
// Get orders from shipment items
$orders = collect();
foreach ($shipment->items as $item) {
if ($item->order) {
$orders->push($item->order);
}
}
// Get orders not assigned to any shipment (available orders)
$usedOrderIds = ShipmentItem::pluck('order_id')->toArray();
$availableOrders = Order::whereNotIn('id', $usedOrderIds)->get();
return view('admin.view-shipment', [
'shipment' => $shipment,
'orders' => $orders,
'mode' => $mode,
'availableOrders' => $availableOrders
]);
}
public function addOrders(Request $request, $id)
{
$request->validate([
'order_ids' => 'required|array|min:1'
]);
$shipment = Shipment::findOrFail($id); $shipment = Shipment::findOrFail($id);
$orderIds = $request->order_ids;
DB::beginTransaction(); // Load full order data from orders table
$orders = Order::whereIn('id',
ShipmentItem::where('shipment_id', $id)->pluck('order_id')
)->get();
try { return response()->json([
$orders = Order::whereIn('id', $orderIds)->get(); 'shipment' => $shipment,
$addedOrders = []; 'orders' => $orders
]);
foreach ($orders as $order) {
// Prevent duplicates
if (ShipmentItem::where('shipment_id', $shipment->id)->where('order_id', $order->id)->exists()) {
continue;
}
ShipmentItem::create([
'shipment_id' => $shipment->id,
'order_id' => $order->id,
'order_ctn' => $order->ctn,
'order_qty' => $order->qty,
'order_ttl_qty' => $order->ttl_qty,
'order_ttl_amount' => $order->ttl_amount,
'order_cbm' => $order->cbm,
'order_ttl_cbm' => $order->ttl_cbm,
'order_kg' => $order->kg,
'order_ttl_kg' => $order->ttl_kg,
]);
$addedOrders[] = $order;
}
// Recalculate totals
$this->recalculateShipmentTotals($shipment->id);
DB::commit();
$shipment->refresh();
$shipment->load('items.order');
// Get updated orders list
$updatedOrders = collect();
foreach ($shipment->items as $item) {
if ($item->order) {
$updatedOrders->push($item->order);
}
}
return response()->json([
'success' => true,
'message' => 'Orders added to shipment successfully.',
'shipment' => $shipment,
'orders' => $addedOrders
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Failed to add orders: ' . $e->getMessage()
], 500);
}
} }
/** /**
@@ -301,77 +209,4 @@ class ShipmentController extends Controller
return redirect()->route('admin.shipments') return redirect()->route('admin.shipments')
->with('success', 'Shipment deleted successfully.'); ->with('success', 'Shipment deleted successfully.');
} }
public function removeOrder(Request $request)
{
$request->validate([
'shipment_id' => 'required|exists:shipments,id',
'order_id' => 'required|exists:orders,id'
]);
$shipmentId = $request->shipment_id;
$orderId = $request->order_id;
// Get order data before deletion
$order = Order::findOrFail($orderId);
// Delete pivot entry
ShipmentItem::where('shipment_id', $shipmentId)
->where('order_id', $orderId)
->delete();
// Recalculate totals
$shipment = $this->recalculateShipmentTotals($shipmentId);
if ($request->ajax() || $request->wantsJson()) {
return response()->json([
'success' => true,
'message' => 'Order removed successfully.',
'order' => $order,
'shipment' => $shipment
]);
}
return back()->with('success', 'Order removed successfully.');
}
private function recalculateShipmentTotals($shipmentId)
{
$shipment = Shipment::with('items')->findOrFail($shipmentId);
// Use the shipment items to calculate totals
$shipment->total_ctn = $shipment->items->sum('order_ctn');
$shipment->total_qty = $shipment->items->sum('order_qty');
$shipment->total_ttl_qty = $shipment->items->sum('order_ttl_qty');
$shipment->total_amount = $shipment->items->sum('order_ttl_amount');
$shipment->total_cbm = $shipment->items->sum('order_cbm');
$shipment->total_ttl_cbm = $shipment->items->sum('order_ttl_cbm');
$shipment->total_kg = $shipment->items->sum('order_kg');
$shipment->total_ttl_kg = $shipment->items->sum('order_ttl_kg');
$shipment->save();
$shipment->refresh(); // Refresh to get updated data
return $shipment;
}
// Helper method to get available orders for a shipment
public function getAvailableOrders($shipmentId)
{
$shipment = Shipment::findOrFail($shipmentId);
// Get all used order IDs
$usedOrderIds = ShipmentItem::pluck('order_id')->toArray();
// Remove orders that are already in this shipment
$shipmentOrderIds = $shipment->items->pluck('order_id')->toArray();
$availableOrderIds = array_diff($usedOrderIds, $shipmentOrderIds);
$availableOrders = Order::whereNotIn('id', $availableOrderIds)->get();
return response()->json([
'success' => true,
'availableOrders' => $availableOrders
]);
}
} }

View File

@@ -1413,31 +1413,39 @@ tr:hover td{ background:#fbfdff; }
<input type="text" id="edit_description" name="description" required class="input" placeholder="Entry description"> <input type="text" id="edit_description" name="description" required class="input" placeholder="Entry description">
</div> </div>
<div> <div>
<label>Order Quantity</label> <label>
<input Order Quantity
type="number" <span style="font-size:12px; color:#6b7280; margin-left:4px;">
id="edit_order_quantity" (Auto Update)
name="order_quantity" </span>
min="0" </label>
class="input"
placeholder="Order quantity" <input
readonly type="number"
style="cursor:pointer;" id="edit_order_quantity"
onclick="openEntryOrdersModal(document.getElementById('edit_entry_no').value)" name="order_quantity"
> min="0"
<small class="helper-note"> class="input"
Click on quantity to view associated orders. placeholder="Order quantity"
</small> readonly
style="cursor:pointer;"
onclick="openEntryOrdersModal(document.getElementById('edit_entry_no').value)"
>
<small class="helper-note">
Click on quantity to view associated orders.
</small>
</div> </div>
<div>
<!-- <div>
<label>Payment Status</label> <label>Payment Status</label>
<select id="edit_payment_status" name="payment_status" class="input"> <select id="edit_payment_status" name="payment_status" class="input">
<option value="unpaid">Unpaid</option> <option value="unpaid">Unpaid</option>
<option value="paid">Paid</option> <option value="paid">Paid</option>
<option value="pending">Pending</option> <option value="pending">Pending</option>
</select> </select>
</div> </div>-->
<div> <div>
<label>Region</label> <label>Region</label>
<select id="edit_region" name="region" class="input" required> <select id="edit_region" name="region" class="input" required>
@@ -2182,10 +2190,23 @@ function renderPaymentTable(list){
body.appendChild(tr); body.appendChild(tr);
const btn = tr.querySelector('.toggle-switch-btn'); const btn = tr.querySelector('.toggle-switch-btn');
setToggleVisual(btn, Number(entry.toggle_pos)); btn.dataset.entry = entry.entry_no; // entry_no from API
btn.addEventListener('click', () => cycleToggle(btn)); btn.dataset.pos = entry.toggle_pos ?? 0;
setToggleVisual(btn, Number(btn.dataset.pos));
btn.addEventListener('click', () => cycleToggle(btn));
const actions = tr.querySelector('.action-btns');
if (actions) {
if (entry.payment_status.toLowerCase() === 'paid') {
actions.style.display = 'none';
} else {
actions.style.display = 'flex';
}
}
if (canActions) { if (canActions) {
const editBtn = tr.querySelector('.edit-btn'); const editBtn = tr.querySelector('.edit-btn');
@@ -2196,7 +2217,70 @@ function renderPaymentTable(list){
} }
}); });
} }
function cycleToggle(btn) {
// वर्तमान position घेऊन पुढचा स्टेट कॅल्क्युलेट करा
let pos = parseInt(btn.dataset.pos || '0', 10); // 0 = unpaid, 1 = pending, 2 = paid
pos = (pos + 1) % 3;
btn.dataset.pos = pos;
setToggleVisual(btn, pos); // रंग वगैरे अपडेट
const entryNo = btn.dataset.entry;
jsonFetch('/admin/account/toggle-payment', {
method: 'POST',
body: {
entry_no: entryNo, // controller मध्ये जी नावे आहेत तीच
toggle_pos: pos
}
}).then(res => {
if (!res.success) {
alert(res.message || 'Failed to update payment status');
// error असेल तर UI परत जुन्या स्टेटला ने
pos = (pos + 2) % 3;
btn.dataset.pos = pos;
setToggleVisual(btn, pos);
return;
}
// 1) global entries array update करा (dashboard data)
if (Array.isArray(entries)) {
const idx = entries.findIndex(e => e.entry_no === entryNo);
if (idx !== -1) {
entries[idx].toggle_pos = res.entry.toggle_pos;
entries[idx].payment_status = res.entry.payment_status;
}
}
// 2) current row मधला status badge आणि actions अपडेट करा
const row = btn.closest('tr');
if (row) {
// status badge
const statusCell = row.querySelector('.status-badge');
if (statusCell) {
statusCell.textContent = res.entry.payment_status;
statusCell.className = 'status-badge ' + statusClass(res.entry.payment_status);
}
// paid झाल्यावर edit/delete लपवा
const actions = row.querySelector('.action-btns');
if (actions) {
if (res.entry.payment_status.toLowerCase() === 'paid') {
actions.style.display = 'none';
} else {
actions.style.display = 'flex'; // किंवा तुझा default layout
}
}
}
}).catch(() => {
alert('Network error while updating payment status');
// network error UI मागे घे
pos = (pos + 2) % 3;
btn.dataset.pos = pos;
setToggleVisual(btn, pos);
});
}
function renderOrderTable(list){ function renderOrderTable(list){
@@ -2336,7 +2420,7 @@ function openEditModal(entry) {
document.getElementById('edit_entry_no').value = entry.entry_no; document.getElementById('edit_entry_no').value = entry.entry_no;
document.getElementById('edit_description').value = entry.description || ''; document.getElementById('edit_description').value = entry.description || '';
document.getElementById('edit_order_quantity').value = entry.order_quantity || ''; document.getElementById('edit_order_quantity').value = entry.order_quantity || '';
document.getElementById('edit_payment_status').value = entry.payment_status || 'unpaid'; // document.getElementById('edit_payment_status').value = entry.payment_status || 'unpaid';
document.getElementById('edit_region').value = entry.region || 'China'; document.getElementById('edit_region').value = entry.region || 'China';
document.getElementById('edit_amount').value = entry.amount || ''; document.getElementById('edit_amount').value = entry.amount || '';
@@ -2470,7 +2554,7 @@ async function submitEditEntry(e) {
entry_no: form.entry_no.value, entry_no: form.entry_no.value,
description: form.description.value.trim(), description: form.description.value.trim(),
order_quantity: Number(form.order_quantity.value) || 0, order_quantity: Number(form.order_quantity.value) || 0,
payment_status: form.payment_status.value, // payment_status: form.payment_status.value,
region: form.region.value, region: form.region.value,
amount: Number(form.amount.value) || 0 amount: Number(form.amount.value) || 0
}; };

View File

@@ -63,7 +63,6 @@
--hover-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); --hover-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
} }
/* UPDATED: Search Bar Styles - White Background */
.search-shipment-bar { .search-shipment-bar {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -90,69 +89,9 @@
z-index: 0; z-index: 0;
} }
.search-input-container:focus-within { .search-shipment-bar > * {
border-color: #4361ee; position: relative;
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.1); z-index: 1;
}
.search-shipment-bar input {
padding: 12px 16px;
border: none;
flex: 1;
background: transparent;
font-weight: 500;
transition: all 0.3s ease;
font-family: 'Inter', sans-serif;
font-size: 14px;
outline: none;
color: #333;
}
.search-shipment-bar input::placeholder {
color: #6b7280;
}
/* UPDATED: Search Button - White with Blue Icon by default, Gradient on hover */
.search-button {
background: white;
color: #4361ee;
border: none;
padding: 12px 20px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 600;
font-family: 'Inter', sans-serif;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
border-radius: 0 10px 10px 0;
min-width: 100px;
justify-content: center;
border-left: 1px solid #d1d5db;
}
.search-input-group {
border: 1px solid #d1d5db !important;
border-radius: 8px !important;
padding: 4px !important;
background: #ffffff !important;
}
.search-button:hover {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.search-icon {
font-size: 16px;
color: #4361ee;
transition: all 0.3s ease;
}
.search-button:hover .search-icon {
color: white;
} }
.search-shipment-bar input, .search-shipment-bar input,
@@ -215,26 +154,6 @@
width: 100%; width: 100%;
} }
} }
/* VIEW BUTTON STYLING */
.btn-view {
background: linear-gradient(135deg, #4cc9f0, #4361ee) !important;
border: none !important;
border-radius: 10px !important;
padding: 8px 14px !important;
color: white !important;
box-shadow: 0 4px 12px rgba(67, 97, 238, 0.35) !important;
transition: all 0.3s ease !important;
}
.btn-view:hover {
transform: translateY(-2px) scale(1.05) !important;
background: linear-gradient(135deg, #3a56d4, #4cc9f0) !important;
box-shadow: 0 8px 20px rgba(67, 97, 238, 0.5) !important;
}
.btn-view i {
font-size: 16px !important;
}
/* Card Styles */ /* Card Styles */
.card { .card {
@@ -326,7 +245,7 @@
border-bottom: none; border-bottom: none;
} }
/* UPDATED: Status Badge Styles - ALL SAME SIZE WITH ICONS */ /* UPDATED: Status Badge Styles - ALL SAME SIZE */
.badge { .badge {
padding: 7px 17px !important; padding: 7px 17px !important;
border-radius: 20px !important; border-radius: 20px !important;
@@ -339,15 +258,7 @@
line-height: 1.2 !important; line-height: 1.2 !important;
} }
/* Status icons */ /* Pending Status - SAME SIZE */
.status-icon {
font-size: 13px;
display: flex;
align-items: center;
justify-content: center;
}
/* Pending Status - SAME SIZE WITH CLOCK ICON */
.badge-pending { .badge-pending {
background: linear-gradient(135deg, #fef3c7, #fde68a) !important; background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
color: #d97706 !important; color: #d97706 !important;
@@ -355,7 +266,7 @@
width: 110px; width: 110px;
} }
/* In Transit Status - SAME SIZE WITH TRUCK ICON */ /* In Transit Status - SAME SIZE */
.badge-in_transit { .badge-in_transit {
background: linear-gradient(135deg, #dbeafe, #93c5fd) !important; background: linear-gradient(135deg, #dbeafe, #93c5fd) !important;
color: #1e40af !important; color: #1e40af !important;
@@ -363,7 +274,7 @@
width: 110px; width: 110px;
} }
/* Dispatched Status - SAME SIZE WITH BOX ICON */ /* Dispatched Status - SAME SIZE */
.badge-dispatched { .badge-dispatched {
background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important; background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important;
color: #6b21a8 !important; color: #6b21a8 !important;
@@ -371,7 +282,7 @@
width: 110px; width: 110px;
} }
/* Delivered Status - SAME SIZE WITH CHECK ICON */ /* Delivered Status - SAME SIZE */
.badge-delivered { .badge-delivered {
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important; background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
color: #065f46 !important; color: #065f46 !important;
@@ -536,66 +447,6 @@
background: #10b981; background: #10b981;
} }
/* NEW: View Button Styles - Icon Only */
.btn-view {
background: #4361ee;
color: white;
border: none;
border-radius: 8px;
padding: 8px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
font-weight: 600;
font-size: 16px;
font-family: 'Inter', sans-serif;
position: relative;
overflow: hidden;
}
.btn-view::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
transition: all 0.3s ease;
z-index: 1;
}
.btn-view:hover::before {
left: 0;
}
.btn-view i {
position: relative;
z-index: 2;
transition: all 0.3s ease;
}
.btn-view:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.btn-view:hover i {
transform: scale(1.1);
}
/* Action buttons container */
.action-buttons {
display: flex;
gap: 8px;
align-items: center;
justify-content: center;
}
/* Modal Styles */ /* Modal Styles */
.modal-content { .modal-content {
border-radius: 20px; border-radius: 20px;
@@ -830,9 +681,6 @@
transition: all 0.3s ease; transition: all 0.3s ease;
position: relative; position: relative;
color: #4361ee !important; color: #4361ee !important;
font-family: 'Inter', sans-serif;
font-size: 14px;
cursor: pointer;
} }
a.text-primary:hover { a.text-primary:hover {
@@ -840,7 +688,7 @@
text-decoration: underline; text-decoration: underline;
} }
/* Shipment Details Modal - UPDATED TO MATCH SECOND CODE */ /* Shipment Details Modal */
.shipment-details-header { .shipment-details-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; color: white;
@@ -930,7 +778,7 @@
border-bottom-right-radius: 10px; border-bottom-right-radius: 10px;
} }
/* Shipment Totals Section - UPDATED */ /* Shipment Totals Section */
.shipment-totals { .shipment-totals {
margin-top: 25px; margin-top: 25px;
padding: 25px; padding: 25px;
@@ -1214,48 +1062,8 @@
justify-content: center; justify-content: center;
} }
} }
/* Edit Form Styles */
.edit-shipment-form {
background: #f8fafc;
padding: 25px;
border-radius: 12px;
margin-bottom: 20px;
border-left: 4px solid #4361ee;
}
.btn-save {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
font-weight: 600;
border-radius: 8px;
padding: 10px 20px;
border: none;
transition: all 0.3s ease;
}
.btn-save:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(72, 187, 120, 0.3);
}
.btn-cancel-edit {
background: #f7fafc;
color: #718096;
border: 1px solid #cbd5e0;
border-radius: 8px;
font-weight: 600;
padding: 10px 20px;
transition: all 0.3s ease;
}
.btn-cancel-edit:hover {
background: #edf2f7;
color: #4a5568;
}
</style> </style>
<div class="container-fluid py-4"> <div class="container-fluid py-4">
{{-- SUCCESS / ERROR MESSAGES --}} {{-- SUCCESS / ERROR MESSAGES --}}
@@ -1420,7 +1228,6 @@
<th>Status</th> <th>Status</th>
<th>Date</th> <th>Date</th>
<th>Action</th> <th>Action</th>
<th>View</th>
</tr> </tr>
</thead> </thead>
@@ -1433,15 +1240,16 @@
{{-- REVERSE INDEX: सर्वात वरच्या shipment ला सर्वात मोठा क्रमांक --}} {{-- REVERSE INDEX: सर्वात वरच्या shipment ला सर्वात मोठा क्रमांक --}}
<td class="fw-bold">{{ $totalShipments - $loop->index }}</td> <td class="fw-bold">{{ $totalShipments - $loop->index }}</td>
<td> <td>
<a href="#" class="text-primary fw-bold" onclick="openShipmentDetails({{ $ship->id }})"> <a href="#" class="text-primary fw-bold"
onclick="openShipmentDetails({{ $ship->id }})">
{{ $ship->shipment_id }} {{ $ship->shipment_id }}
</a> </a>
</td> </td>
<td>{{ $ship->origin }}</td> <td>{{ $ship->origin }}</td>
<td>{{ $ship->destination }}</td> <td>{{ $ship->destination }}</td>
<td>{{ $ship->total_qty }}</td> <td><span class="badge bg-light text-dark">{{ $ship->total_qty }}</span></td>
<td>{{ $ship->total_kg }} kg</td> <td><span class="badge bg-light text-dark">{{ $ship->total_kg }} kg</span></td>
<td>{{ $ship->total_cbm }} CBM</td> <td><span class="badge bg-light text-dark">{{ $ship->total_cbm }} CBM</span></td>
<td class="fw-bold text-success">{{ number_format($ship->total_amount, 2) }}</td> <td class="fw-bold text-success">{{ number_format($ship->total_amount, 2) }}</td>
<td> <td>
<span class="badge badge-{{ $ship->status }}"> <span class="badge badge-{{ $ship->status }}">
@@ -1478,12 +1286,6 @@
</div> </div>
</div> </div>
</td> </td>
<td>
<a href="{{ route('admin.shipments.view', ['id' => $ship->id, 'mode' => 'edit']) }}"
class="btn btn-view">
<i class="bi bi-eye"></i>
</a>
</td>
</tr> </tr>
@empty @empty
<tr> <tr>
@@ -1521,29 +1323,29 @@
</div> </div>
</div> </div>
</div> <!-- ============================= -->
<!-- SHIPMENT DETAILS MODAL -->
<!-- ============================= -->
<div class="modal fade" id="shipmentDetailsModal" tabindex="-1">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header shipment-details-header">
<h5 class="modal-title fw-bold"><i class="bi bi-box-seam me-2"></i>Consolidated Shipment Details</h5>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<!-- ============================= --> <div class="modal-body shipment-details-body" id="shipmentDetailsContent">
<!-- SHIPMENT DETAILS MODAL --> <div class="text-center py-4 loading">
<!-- ============================= --> <div class="spinner-border text-primary" role="status">
<div class="modal fade" id="shipmentDetailsModal" tabindex="-1"> <span class="visually-hidden">Loading...</span>
<div class="modal-dialog modal-xl modal-dialog-scrollable"> </div>
<div class="modal-content"> <p class="mt-2 text-muted">Loading shipment details...</p>
<div class="modal-header shipment-details-header">
<h5 class="modal-title fw-bold"><i class="bi bi-box-seam me-2"></i>Consolidated Shipment Details</h5>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body shipment-details-body" id="shipmentDetailsContent">
<div class="text-center py-4 loading">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div> </div>
<p class="mt-2 text-muted">Loading shipment details...</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- ========================= --> <!-- ========================= -->
@@ -1775,19 +1577,11 @@ function renderTable() {
</div> </div>
</div> </div>
</td> </td>
<td>
<a href="/admin/shipments/view/${shipment.id}?mode=edit"
class="btn btn-view"
title="Edit Shipment">
<i class="bi bi-eye"></i>
</a>
</td>
`; `;
tbody.appendChild(row); tbody.appendChild(row);
}); });
} }
// Function to open shipment details modal
function openShipmentDetails(id) { function openShipmentDetails(id) {
let modal = new bootstrap.Modal(document.getElementById('shipmentDetailsModal')); let modal = new bootstrap.Modal(document.getElementById('shipmentDetailsModal'));
let content = document.getElementById('shipmentDetailsContent'); let content = document.getElementById('shipmentDetailsContent');
@@ -1958,15 +1752,11 @@ function toggleStatusDropdown(button, shipmentId) {
// Close dropdown when clicking outside // Close dropdown when clicking outside
document.addEventListener('click', function closeDropdown(e) { document.addEventListener('click', function closeDropdown(e) {
// allow clicking links normally if (!button.contains(e.target) && !dropdown.contains(e.target)) {
if (e.target.closest('a')) return; dropdown.classList.remove('show');
document.removeEventListener('click', closeDropdown);
if (!button.contains(e.target) && !dropdown.contains(e.target)) { }
dropdown.classList.remove('show'); });
document.removeEventListener('click', closeDropdown);
}
}, { once: true });
} }
// Auto-close dropdown after form submission // Auto-close dropdown after form submission

View File

@@ -147,47 +147,33 @@ Route::prefix('admin')
Route::delete('/orders/{id}/delete', [AdminOrderController::class, 'destroy']) Route::delete('/orders/{id}/delete', [AdminOrderController::class, 'destroy'])
->name('admin.orders.destroy'); ->name('admin.orders.destroy');
// --------------------------- // ---------------------------
// SHIPMENTS (FIXED ROUTES) // SHIPMENTS (FIXED ROUTES)
// --------------------------- // ---------------------------
Route::get('/shipments', [ShipmentController::class, 'index'])
->name('admin.shipments');
// View shipment MUST be before /shipments/{id} Route::post('/shipments', [ShipmentController::class, 'store'])
Route::get('/shipments/view/{id}', [ShipmentController::class, 'show']) ->name('admin.shipments.store');
->name('admin.shipments.view');
// List shipments Route::post('/shipments/update-status', [ShipmentController::class, 'updateStatus'])
Route::get('/shipments', [ShipmentController::class, 'index']) ->name('admin.shipments.updateStatus');
->name('admin.shipments');
// Create shipment // Get shipment orders for modal (AJAX)
Route::post('/shipments', [ShipmentController::class, 'store']) Route::get('/shipments/{id}/orders', [ShipmentController::class, 'getShipmentOrders'])
->name('admin.shipments.store'); ->name('admin.shipments.orders');
// Update status // Get shipment details for edit (AJAX)
Route::post('/shipments/update-status', [ShipmentController::class, 'updateStatus']) Route::get('/shipments/{id}', [ShipmentController::class, 'show'])
->name('admin.shipments.updateStatus'); ->name('admin.shipments.show');
// Shipment orders (AJAX) // Shipment Update
Route::get('/shipments/{id}/orders', [ShipmentController::class, 'getShipmentOrders']) Route::put('/shipments/{id}', [ShipmentController::class, 'update'])
->name('admin.shipments.orders'); ->name('admin.shipments.update');
// Shipment update // Shipment Delete
Route::put('/shipments/{id}', [ShipmentController::class, 'update']) Route::delete('/shipments/{id}', [ShipmentController::class, 'destroy'])
->name('admin.shipments.update'); ->name('admin.shipments.destroy');
// Shipment delete
Route::delete('/shipments/{id}', [ShipmentController::class, 'destroy'])
->name('admin.shipments.destroy');
// Remove order
Route::post('/shipments/remove-order', [ShipmentController::class, 'removeOrder'])
->name('admin.shipments.removeOrder');
Route::post('/shipments/add-order', [ShipmentController::class, 'addOrder'])
->name('admin.shipments.addOrder');
Route::post('/shipments/{id}/add-orders', [ShipmentController::class, 'addOrders'])
->name('admin.shipments.addOrders');
// --------------------------- // ---------------------------
// INVOICES // INVOICES
@@ -301,6 +287,11 @@ Route::prefix('admin')
Route::get('/admin/orders/download/excel', [AdminOrderController::class, 'downloadExcel']) Route::get('/admin/orders/download/excel', [AdminOrderController::class, 'downloadExcel'])
->name('admin.orders.download.excel'); ->name('admin.orders.download.excel');
Route::prefix('admin/account')->middleware('auth:admin')->name('admin.account.')->group(function () {
Route::post('/toggle-payment', [AdminAccountController::class, 'togglePayment'])->name('toggle');
});
//--------------------------- //---------------------------
//Edit Button Route //Edit Button Route
//--------------------------- //---------------------------