Shipment dashboard changes
This commit is contained in:
@@ -164,46 +164,30 @@ class AdminOrderController extends Controller
|
||||
return redirect()->back()->with('success', 'Item deleted and totals updated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore soft-deleted item and recalc totals
|
||||
*/
|
||||
public function restoreItem($id)
|
||||
{
|
||||
$item = OrderItem::withTrashed()->findOrFail($id);
|
||||
$order = Order::findOrFail($item->order_id);
|
||||
|
||||
$item->restore();
|
||||
|
||||
// recalc totals
|
||||
$this->recalcTotals($order);
|
||||
|
||||
return redirect()->back()->with('success', 'Item restored and totals updated.');
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// ORDER CRUD: update / destroy
|
||||
// ---------------------------
|
||||
public function update(Request $request, $id)
|
||||
public function updateItem(Request $request, $id)
|
||||
{
|
||||
$order = Order::findOrFail($id);
|
||||
$item = OrderItem::findOrFail($id);
|
||||
|
||||
$data = $request->validate([
|
||||
'mark_no' => 'required|string',
|
||||
'origin' => 'nullable|string',
|
||||
'destination' => 'nullable|string',
|
||||
$item->update([
|
||||
'description' => $request->description,
|
||||
'ctn' => $request->ctn,
|
||||
'qty' => $request->qty,
|
||||
'ttl_qty' => $request->ttl_qty,
|
||||
'unit' => $request->unit,
|
||||
'price' => $request->price,
|
||||
'ttl_amount' => $request->ttl_amount,
|
||||
'cbm' => $request->cbm,
|
||||
'ttl_cbm' => $request->ttl_cbm,
|
||||
'kg' => $request->kg,
|
||||
'ttl_kg' => $request->ttl_kg,
|
||||
'shop_no' => $request->shop_no,
|
||||
]);
|
||||
|
||||
$order->update([
|
||||
'mark_no' => $data['mark_no'],
|
||||
'origin' => $data['origin'] ?? null,
|
||||
'destination' => $data['destination'] ?? null,
|
||||
]);
|
||||
|
||||
// optionally recalc totals (not necessary unless you change item-level fields here)
|
||||
$this->recalcTotals($order);
|
||||
|
||||
return redirect()->route('admin.orders.show', $order->id)
|
||||
->with('success', 'Order updated successfully.');
|
||||
return back()->with('success', 'Item updated successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,6 +325,10 @@ class AdminOrderController extends Controller
|
||||
return null;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Popup function
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function popup($id)
|
||||
{
|
||||
// Load order with items + markList
|
||||
@@ -354,8 +342,27 @@ class AdminOrderController extends Controller
|
||||
|
||||
return view('admin.popup', compact('order', 'user'));
|
||||
}
|
||||
// -------------------------------------------------------------------------
|
||||
// Restore Item
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function restoreItem($id)
|
||||
{
|
||||
$item = OrderItem::onlyTrashed()->findOrFail($id);
|
||||
$item->restore();
|
||||
|
||||
if (request()->ajax()) {
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
'message' => 'Item restored.'
|
||||
]);
|
||||
}
|
||||
|
||||
return back()->belongsTo(Order::class)->with('success', 'Item restored successfully.');
|
||||
}
|
||||
// -------------------------------------------------------------------------
|
||||
// Reset temp
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
public function resetTemp()
|
||||
@@ -366,6 +373,11 @@ class AdminOrderController extends Controller
|
||||
->with('success', 'Order reset successfully.');
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ORDER SHOW PAGE
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
public function orderShow()
|
||||
{
|
||||
$orders = Order::with([
|
||||
@@ -379,8 +391,11 @@ class AdminOrderController extends Controller
|
||||
return view('admin.orders', compact('orders'));
|
||||
}
|
||||
|
||||
public function downloadPdf(Request $request)
|
||||
{
|
||||
//====================================================
|
||||
// Download Pdf
|
||||
//=================================================
|
||||
public function downloadPdf(Request $request)
|
||||
{
|
||||
$query = Order::with(['markList', 'invoice', 'shipments']);
|
||||
|
||||
// Apply filters
|
||||
@@ -413,14 +428,18 @@ public function downloadPdf(Request $request)
|
||||
|
||||
$pdf = PDF::loadView('admin.orders.pdf', compact('orders'));
|
||||
return $pdf->download('orders-report-' . date('Y-m-d') . '.pdf');
|
||||
}
|
||||
}
|
||||
|
||||
public function downloadExcel(Request $request)
|
||||
{
|
||||
public function downloadExcel(Request $request)
|
||||
{
|
||||
return Excel::download(new OrdersExport($request), 'orders-report-' . date('Y-m-d') . '.xlsx');
|
||||
}
|
||||
}
|
||||
|
||||
public function addTempItem(Request $request)
|
||||
//====================================================
|
||||
// add Temp Item
|
||||
//=================================================
|
||||
|
||||
public function addTempItem(Request $request)
|
||||
{
|
||||
// Validate item fields
|
||||
$item = $request->validate([
|
||||
@@ -468,8 +487,9 @@ public function addTempItem(Request $request)
|
||||
->with('success', 'Item added.');
|
||||
}
|
||||
|
||||
public function finishOrder(Request $request)
|
||||
{
|
||||
|
||||
public function finishOrder(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'mark_no' => 'required',
|
||||
'origin' => 'required',
|
||||
@@ -483,7 +503,9 @@ public function finishOrder(Request $request)
|
||||
->with('error', 'Add at least one item before finishing.');
|
||||
}
|
||||
|
||||
// Generate Order ID
|
||||
// =======================
|
||||
// GENERATE ORDER ID
|
||||
// =======================
|
||||
$year = date('y');
|
||||
$prefix = "KNT-$year-";
|
||||
|
||||
@@ -492,7 +514,9 @@ public function finishOrder(Request $request)
|
||||
|
||||
$orderId = $prefix . str_pad($nextNumber, 8, '0', STR_PAD_LEFT);
|
||||
|
||||
// =======================
|
||||
// TOTAL SUMS
|
||||
// =======================
|
||||
$total_ctn = array_sum(array_column($items, 'ctn'));
|
||||
$total_qty = array_sum(array_column($items, 'qty'));
|
||||
$total_ttl_qty = array_sum(array_column($items, 'ttl_qty'));
|
||||
@@ -502,7 +526,9 @@ public function finishOrder(Request $request)
|
||||
$total_kg = array_sum(array_column($items, 'kg'));
|
||||
$total_ttl_kg = array_sum(array_column($items, 'ttl_kg'));
|
||||
|
||||
// =======================
|
||||
// CREATE ORDER
|
||||
// =======================
|
||||
$order = Order::create([
|
||||
'order_id' => $orderId,
|
||||
'mark_no' => $request->mark_no,
|
||||
@@ -519,7 +545,7 @@ public function finishOrder(Request $request)
|
||||
'status' => 'pending',
|
||||
]);
|
||||
|
||||
// SAVE ALL SUB-ITEMS
|
||||
// SAVE ORDER ITEMS
|
||||
foreach ($items as $item) {
|
||||
OrderItem::create([
|
||||
'order_id' => $order->id,
|
||||
@@ -538,11 +564,85 @@ public function finishOrder(Request $request)
|
||||
]);
|
||||
}
|
||||
|
||||
// =======================
|
||||
// INVOICE CREATION START
|
||||
// =======================
|
||||
|
||||
// 1. Auto-generate invoice number
|
||||
$lastInvoice = \App\Models\Invoice::latest()->first();
|
||||
$nextInvoice = $lastInvoice ? $lastInvoice->id + 1 : 1;
|
||||
$invoiceNumber = 'INV-' . date('Y') . '-' . str_pad($nextInvoice, 6, '0', STR_PAD_LEFT);
|
||||
|
||||
// 2. Fetch customer (using mark list → customer_id)
|
||||
$markList = MarkList::where('mark_no', $order->mark_no)->first();
|
||||
$customer = null;
|
||||
|
||||
if ($markList && $markList->customer_id) {
|
||||
$customer = \App\Models\User::where('customer_id', $markList->customer_id)->first();
|
||||
}
|
||||
|
||||
// 3. Create Invoice Record
|
||||
$invoice = \App\Models\Invoice::create([
|
||||
'order_id' => $order->id,
|
||||
'customer_id' => $customer->id ?? null,
|
||||
'mark_no' => $order->mark_no,
|
||||
|
||||
'invoice_number' => $invoiceNumber,
|
||||
'invoice_date' => now(),
|
||||
'due_date' => now()->addDays(10),
|
||||
|
||||
'payment_method' => null,
|
||||
'reference_no' => null,
|
||||
'status' => 'pending',
|
||||
|
||||
'final_amount' => $total_amount,
|
||||
'gst_percent' => 0,
|
||||
'gst_amount' => 0,
|
||||
'final_amount_with_gst' => $total_amount,
|
||||
|
||||
// snapshot customer fields
|
||||
'customer_name' => $customer->customer_name ?? null,
|
||||
'company_name' => $customer->company_name ?? null,
|
||||
'customer_email' => $customer->email ?? null,
|
||||
'customer_mobile' => $customer->mobile_no ?? null,
|
||||
'customer_address' => $customer->address ?? null,
|
||||
'pincode' => $customer->pincode ?? null,
|
||||
|
||||
'notes' => null,
|
||||
]);
|
||||
|
||||
// 4. Clone order items into invoice_items
|
||||
foreach ($order->items as $item) {
|
||||
\App\Models\InvoiceItem::create([
|
||||
'invoice_id' => $invoice->id,
|
||||
'description' => $item->description,
|
||||
'ctn' => $item->ctn,
|
||||
'qty' => $item->qty,
|
||||
'ttl_qty' => $item->ttl_qty,
|
||||
'unit' => $item->unit,
|
||||
'price' => $item->price,
|
||||
'ttl_amount' => $item->ttl_amount,
|
||||
'cbm' => $item->cbm,
|
||||
'ttl_cbm' => $item->ttl_cbm,
|
||||
'kg' => $item->kg,
|
||||
'ttl_kg' => $item->ttl_kg,
|
||||
'shop_no' => $item->shop_no,
|
||||
]);
|
||||
}
|
||||
|
||||
// 5. TODO: PDF generation (I will add this later)
|
||||
$invoice->pdf_path = null; // placeholder for now
|
||||
$invoice->save();
|
||||
|
||||
// =======================
|
||||
// END INVOICE CREATION
|
||||
// =======================
|
||||
|
||||
// CLEAR TEMP DATA
|
||||
session()->forget(['temp_order_items', 'mark_no', 'origin', 'destination']);
|
||||
|
||||
return redirect()->route('admin.orders.index')
|
||||
->with('success', 'Order saved successfully.');
|
||||
}
|
||||
->with('success', 'Order + Invoice created successfully.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Shipment;
|
||||
@@ -106,6 +107,9 @@ class ShipmentController extends Controller
|
||||
'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,
|
||||
]);
|
||||
}
|
||||
@@ -113,22 +117,110 @@ class ShipmentController extends Controller
|
||||
return redirect()->back()->with('success', "Shipment $newShipmentId created successfully!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Show shipment details (for modal popup)
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$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)
|
||||
{
|
||||
$shipment = Shipment::findOrFail($id);
|
||||
$mode = request()->get('mode', 'view');
|
||||
|
||||
// Load full order data from orders table
|
||||
$orders = Order::whereIn('id',
|
||||
ShipmentItem::where('shipment_id', $id)->pluck('order_id')
|
||||
)->get();
|
||||
$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);
|
||||
$orderIds = $request->order_ids;
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$orders = Order::whereIn('id', $orderIds)->get();
|
||||
$addedOrders = [];
|
||||
|
||||
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' => $orders
|
||||
'orders' => $addedOrders
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Failed to add orders: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,4 +301,77 @@ class ShipmentController extends Controller
|
||||
return redirect()->route('admin.shipments')
|
||||
->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
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,26 @@ class ShipmentItem extends Model
|
||||
protected $fillable = [
|
||||
'shipment_id',
|
||||
'order_id',
|
||||
|
||||
// OLD fields (keep them if old data exists)
|
||||
'order_ctn',
|
||||
'order_qty',
|
||||
'order_ttl_qty',
|
||||
'order_ttl_amount',
|
||||
'order_ttl_kg',
|
||||
|
||||
// NEW fields (added for correct shipments)
|
||||
'ctn',
|
||||
'qty',
|
||||
'ttl_qty',
|
||||
|
||||
'cbm',
|
||||
'ttl_cbm',
|
||||
|
||||
'kg',
|
||||
'ttl_kg',
|
||||
|
||||
'ttl_amount',
|
||||
];
|
||||
|
||||
// ---------------------------
|
||||
@@ -36,11 +51,11 @@ class ShipmentItem extends Model
|
||||
// Helper: return order data with fallback to snapshot
|
||||
public function getDisplayQty()
|
||||
{
|
||||
return $this->order->qty ?? $this->order_qty;
|
||||
return $this->qty;
|
||||
}
|
||||
|
||||
public function getDisplayAmount()
|
||||
{
|
||||
return $this->order->ttl_amount ?? $this->order_ttl_amount;
|
||||
return $this->ttl_amount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"barryvdh/laravel-dompdf": "^3.1",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"maatwebsite/excel": "^1.1",
|
||||
"mpdf/mpdf": "^8.2",
|
||||
"php-open-source-saver/jwt-auth": "2.8"
|
||||
},
|
||||
|
||||
488
composer.lock
generated
488
composer.lock
generated
@@ -4,8 +4,85 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "fa680e1e8b3550d710849bbd820e3626",
|
||||
"content-hash": "6a2ec43d7e96d38cacfc2f9e1ae5cb9e",
|
||||
"packages": [
|
||||
{
|
||||
"name": "barryvdh/laravel-dompdf",
|
||||
"version": "v3.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-dompdf.git",
|
||||
"reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d",
|
||||
"reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"dompdf/dompdf": "^3.0",
|
||||
"illuminate/support": "^9|^10|^11|^12",
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"larastan/larastan": "^2.7|^3.0",
|
||||
"orchestra/testbench": "^7|^8|^9|^10",
|
||||
"phpro/grumphp": "^2.5",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"PDF": "Barryvdh\\DomPDF\\Facade\\Pdf",
|
||||
"Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf"
|
||||
},
|
||||
"providers": [
|
||||
"Barryvdh\\DomPDF\\ServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Barryvdh\\DomPDF\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A DOMPDF Wrapper for Laravel",
|
||||
"keywords": [
|
||||
"dompdf",
|
||||
"laravel",
|
||||
"pdf"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-dompdf/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://fruitcake.nl",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/barryvdh",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-13T15:07:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.14.0",
|
||||
@@ -377,6 +454,161 @@
|
||||
],
|
||||
"time": "2024-02-05T11:56:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dompdf/dompdf",
|
||||
"version": "v3.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dompdf/dompdf.git",
|
||||
"reference": "db712c90c5b9868df3600e64e68da62e78a34623"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/db712c90c5b9868df3600e64e68da62e78a34623",
|
||||
"reference": "db712c90c5b9868df3600e64e68da62e78a34623",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"dompdf/php-font-lib": "^1.0.0",
|
||||
"dompdf/php-svg-lib": "^1.0.0",
|
||||
"ext-dom": "*",
|
||||
"ext-mbstring": "*",
|
||||
"masterminds/html5": "^2.0",
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-gd": "*",
|
||||
"ext-json": "*",
|
||||
"ext-zip": "*",
|
||||
"mockery/mockery": "^1.3",
|
||||
"phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "Needed to process images",
|
||||
"ext-gmagick": "Improves image processing performance",
|
||||
"ext-imagick": "Improves image processing performance",
|
||||
"ext-zlib": "Needed for pdf stream compression"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Dompdf\\": "src/"
|
||||
},
|
||||
"classmap": [
|
||||
"lib/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "The Dompdf Community",
|
||||
"homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
|
||||
}
|
||||
],
|
||||
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
|
||||
"homepage": "https://github.com/dompdf/dompdf",
|
||||
"support": {
|
||||
"issues": "https://github.com/dompdf/dompdf/issues",
|
||||
"source": "https://github.com/dompdf/dompdf/tree/v3.1.4"
|
||||
},
|
||||
"time": "2025-10-29T12:43:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dompdf/php-font-lib",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dompdf/php-font-lib.git",
|
||||
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
|
||||
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FontLib\\": "src/FontLib"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "The FontLib Community",
|
||||
"homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md"
|
||||
}
|
||||
],
|
||||
"description": "A library to read, parse, export and make subsets of different types of font files.",
|
||||
"homepage": "https://github.com/dompdf/php-font-lib",
|
||||
"support": {
|
||||
"issues": "https://github.com/dompdf/php-font-lib/issues",
|
||||
"source": "https://github.com/dompdf/php-font-lib/tree/1.0.1"
|
||||
},
|
||||
"time": "2024-12-02T14:37:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dompdf/php-svg-lib",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dompdf/php-svg-lib.git",
|
||||
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
|
||||
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": "^7.1 || ^8.0",
|
||||
"sabberworm/php-css-parser": "^8.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Svg\\": "src/Svg"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-3.0-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "The SvgLib Community",
|
||||
"homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md"
|
||||
}
|
||||
],
|
||||
"description": "A library to read, parse and export to PDF SVG files.",
|
||||
"homepage": "https://github.com/dompdf/php-svg-lib",
|
||||
"support": {
|
||||
"issues": "https://github.com/dompdf/php-svg-lib/issues",
|
||||
"source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0"
|
||||
},
|
||||
"time": "2024-04-29T13:26:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dragonmantank/cron-expression",
|
||||
"version": "v3.6.0",
|
||||
@@ -2081,6 +2313,132 @@
|
||||
],
|
||||
"time": "2024-12-08T08:18:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maatwebsite/excel",
|
||||
"version": "v1.1.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Maatwebsite/Laravel-Excel.git",
|
||||
"reference": "0c67aba8387726458d42461eae91a3415593bbc4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Maatwebsite/Laravel-Excel/zipball/0c67aba8387726458d42461eae91a3415593bbc4",
|
||||
"reference": "0c67aba8387726458d42461eae91a3415593bbc4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"phpoffice/phpexcel": "~1.8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~0.9",
|
||||
"orchestra/testbench": "~2.2.0@dev",
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Maatwebsite\\Excel\\": "src/"
|
||||
},
|
||||
"classmap": [
|
||||
"src/Maatwebsite/Excel",
|
||||
"tests/TestCase.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maatwebsite.nl",
|
||||
"email": "patrick@maatwebsite.nl"
|
||||
}
|
||||
],
|
||||
"description": "An eloquent way of importing and exporting Excel and CSV in Laravel 4 with the power of PHPExcel",
|
||||
"keywords": [
|
||||
"PHPExcel",
|
||||
"batch",
|
||||
"csv",
|
||||
"excel",
|
||||
"export",
|
||||
"import",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Maatwebsite/Laravel-Excel/issues",
|
||||
"source": "https://github.com/Maatwebsite/Laravel-Excel/tree/master"
|
||||
},
|
||||
"time": "2014-07-10T09:06:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "masterminds/html5",
|
||||
"version": "2.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Masterminds/html5-php.git",
|
||||
"reference": "fcf91eb64359852f00d921887b219479b4f21251"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251",
|
||||
"reference": "fcf91eb64359852f00d921887b219479b4f21251",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Masterminds\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Matt Butcher",
|
||||
"email": "technosophos@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Matt Farina",
|
||||
"email": "matt@mattfarina.com"
|
||||
},
|
||||
{
|
||||
"name": "Asmir Mustafic",
|
||||
"email": "goetas@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "An HTML5 parser and serializer.",
|
||||
"homepage": "http://masterminds.github.io/html5-php",
|
||||
"keywords": [
|
||||
"HTML5",
|
||||
"dom",
|
||||
"html",
|
||||
"parser",
|
||||
"querypath",
|
||||
"serializer",
|
||||
"xml"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Masterminds/html5-php/issues",
|
||||
"source": "https://github.com/Masterminds/html5-php/tree/2.10.0"
|
||||
},
|
||||
"time": "2025-07-25T09:04:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "3.9.0",
|
||||
@@ -3034,6 +3392,68 @@
|
||||
},
|
||||
"time": "2025-02-10T21:11:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpexcel",
|
||||
"version": "1.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPOffice/PHPExcel.git",
|
||||
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
|
||||
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-xml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"php": ">=5.2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"PHPExcel": "Classes/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maarten Balliauw",
|
||||
"homepage": "http://blog.maartenballiauw.be"
|
||||
},
|
||||
{
|
||||
"name": "Mark Baker"
|
||||
},
|
||||
{
|
||||
"name": "Franck Lefevre",
|
||||
"homepage": "http://blog.rootslabs.net"
|
||||
},
|
||||
{
|
||||
"name": "Erik Tilt"
|
||||
}
|
||||
],
|
||||
"description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
|
||||
"homepage": "http://phpexcel.codeplex.com",
|
||||
"keywords": [
|
||||
"OpenXML",
|
||||
"excel",
|
||||
"php",
|
||||
"spreadsheet",
|
||||
"xls",
|
||||
"xlsx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPOffice/PHPExcel/issues",
|
||||
"source": "https://github.com/PHPOffice/PHPExcel/tree/master"
|
||||
},
|
||||
"abandoned": "phpoffice/phpspreadsheet",
|
||||
"time": "2015-05-01T07:00:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.9.4",
|
||||
@@ -3798,6 +4218,72 @@
|
||||
},
|
||||
"time": "2025-09-04T20:59:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sabberworm/php-css-parser",
|
||||
"version": "v8.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
|
||||
"reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9",
|
||||
"reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-iconv": "*",
|
||||
"php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41",
|
||||
"rawr/cross-data-providers": "^2.0.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "for parsing UTF-8 CSS"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "9.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Sabberworm\\CSS\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Raphael Schweikert"
|
||||
},
|
||||
{
|
||||
"name": "Oliver Klee",
|
||||
"email": "github@oliverklee.de"
|
||||
},
|
||||
{
|
||||
"name": "Jake Hotson",
|
||||
"email": "jake.github@qzdesign.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "Parser for CSS Files written in PHP",
|
||||
"homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
|
||||
"keywords": [
|
||||
"css",
|
||||
"parser",
|
||||
"stylesheet"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
|
||||
"source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0"
|
||||
},
|
||||
"time": "2025-07-11T13:20:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "setasign/fpdi",
|
||||
"version": "v2.6.4",
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('shipment_items', function (Blueprint $table) {
|
||||
|
||||
if (!Schema::hasColumn('shipment_items', 'ctn')) {
|
||||
$table->integer('ctn')->default(0);
|
||||
}
|
||||
|
||||
if (!Schema::hasColumn('shipment_items', 'qty')) {
|
||||
$table->integer('qty')->default(0);
|
||||
}
|
||||
|
||||
if (!Schema::hasColumn('shipment_items', 'ttl_qty')) {
|
||||
$table->integer('ttl_qty')->default(0);
|
||||
}
|
||||
|
||||
if (!Schema::hasColumn('shipment_items', 'cbm')) {
|
||||
$table->double('cbm')->default(0);
|
||||
}
|
||||
|
||||
if (!Schema::hasColumn('shipment_items', 'ttl_cbm')) {
|
||||
$table->double('ttl_cbm')->default(0);
|
||||
}
|
||||
|
||||
if (!Schema::hasColumn('shipment_items', 'kg')) {
|
||||
$table->double('kg')->default(0);
|
||||
}
|
||||
|
||||
if (!Schema::hasColumn('shipment_items', 'ttl_kg')) {
|
||||
$table->double('ttl_kg')->default(0);
|
||||
}
|
||||
|
||||
if (!Schema::hasColumn('shipment_items', 'ttl_amount')) {
|
||||
$table->double('ttl_amount')->default(0);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('shipment_items', function (Blueprint $table) {
|
||||
// safely remove columns (optional)
|
||||
$columns = [
|
||||
'ctn', 'qty', 'ttl_qty', 'cbm',
|
||||
'ttl_cbm', 'kg', 'ttl_kg', 'ttl_amount'
|
||||
];
|
||||
|
||||
foreach ($columns as $col) {
|
||||
if (Schema::hasColumn('shipment_items', $col)) {
|
||||
$table->dropColumn($col);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000028.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000028.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000029.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000029.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000030.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000030.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000032.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000032.pdf
Normal file
Binary file not shown.
@@ -15,27 +15,30 @@
|
||||
<h4 class="fw-bold mb-0">Order Details</h4>
|
||||
<small class="text-muted">Detailed view of this shipment order</small>
|
||||
</div>
|
||||
<a href="{{ route('admin.dashboard') }}" class="btn-close"></a>
|
||||
</div>
|
||||
|
||||
{{-- ACTION BUTTONS --}}
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
|
||||
{{-- ADD ITEM --}}
|
||||
<button class="btn btn-add-item" data-bs-toggle="modal" data-bs-target="#addItemModal">
|
||||
<i class="fas fa-plus-circle me-2"></i>Add New Item
|
||||
</button>
|
||||
|
||||
{{-- EDIT ORDER --}}
|
||||
@if($order->status === 'pending')
|
||||
<a href="{{ route('admin.dashboard') }}" class="btn-close"></a>
|
||||
</div>
|
||||
|
||||
<!-- {{-- ACTION BUTTONS --}}
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
|
||||
|
||||
|
||||
{{-- EDIT ORDER --}} -->
|
||||
<!-- @if($order->status === 'pending')
|
||||
<button class="btn btn-edit-order" onclick="document.getElementById('editOrderForm').style.display='block'">
|
||||
<i class="fas fa-edit me-2"></i>Edit Order
|
||||
</button>
|
||||
@else
|
||||
<button class="btn btn-edit-order" disabled><i class="fas fa-edit me-2"></i>Edit Order</button>
|
||||
@endif
|
||||
@endif -->
|
||||
|
||||
{{-- DELETE ORDER --}}
|
||||
<!-- {{-- DELETE ORDER --}}
|
||||
@if($order->status === 'pending')
|
||||
<form action="{{ route('admin.orders.destroy', $order->id) }}"
|
||||
method="POST"
|
||||
@@ -46,9 +49,9 @@
|
||||
<i class="fas fa-trash-alt me-2"></i>Delete Order
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
@endif -->
|
||||
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
|
||||
<hr>
|
||||
|
||||
@@ -56,7 +59,7 @@
|
||||
<div id="editOrderForm" class="p-3 bg-light rounded mb-4 shadow-sm" style="display:none;">
|
||||
<h5 class="fw-bold mb-3">Edit Order</h5>
|
||||
|
||||
<form action="{{ route('admin.orders.update', $order->id) }}" method="POST">
|
||||
<form action="{{ route('admin.orders.updateItem', $order->id) }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="row">
|
||||
@@ -185,7 +188,18 @@
|
||||
<td>{{ $item->ttl_kg }}</td>
|
||||
<td>{{ $item->shop_no }}</td>
|
||||
|
||||
<td>
|
||||
<td class="d-flex justify-content-center gap-2">
|
||||
|
||||
{{-- EDIT BUTTON --}}
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-edit-item"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editItemModal{{ $item->id }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
|
||||
{{-- DELETE BUTTON --}}
|
||||
<form action="{{ route('admin.orders.deleteItem', $item->id) }}"
|
||||
method="POST"
|
||||
onsubmit="return confirm('Delete this item?')">
|
||||
@@ -196,12 +210,135 @@
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
@foreach($order->items as $item)
|
||||
<div class="modal fade" id="editItemModal{{ $item->id }}" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content custom-modal">
|
||||
|
||||
<div class="modal-header modal-gradient-header">
|
||||
<h5 class="modal-title text-white">
|
||||
<i class="fas fa-edit me-2"></i>Edit Item
|
||||
</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('admin.orders.updateItem', $item->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Description</label>
|
||||
<input type="text" name="description"
|
||||
value="{{ $item->description }}"
|
||||
class="form-control custom-input" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">CTN</label>
|
||||
<input type="number" name="ctn"
|
||||
value="{{ $item->ctn }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">QTY</label>
|
||||
<input type="number" name="qty"
|
||||
value="{{ $item->qty }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">TTL QTY</label>
|
||||
<input type="number" name="ttl_qty"
|
||||
value="{{ $item->ttl_qty }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Unit</label>
|
||||
<input type="text" name="unit"
|
||||
value="{{ $item->unit }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Price</label>
|
||||
<input type="number" name="price"
|
||||
value="{{ $item->price }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Total Amount</label>
|
||||
<input type="number" name="ttl_amount"
|
||||
value="{{ $item->ttl_amount }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">CBM</label>
|
||||
<input type="number" name="cbm"
|
||||
value="{{ $item->cbm }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">TTL CBM</label>
|
||||
<input type="number" name="ttl_cbm"
|
||||
value="{{ $item->ttl_cbm }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">KG</label>
|
||||
<input type="number" name="kg"
|
||||
value="{{ $item->kg }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">TTL KG</label>
|
||||
<input type="number" name="ttl_kg"
|
||||
value="{{ $item->ttl_kg }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Shop No</label>
|
||||
<input type="text" name="shop_no"
|
||||
value="{{ $item->shop_no }}"
|
||||
class="form-control custom-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-modal-close" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-modal-add">Add Item</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
|
||||
{{-- TOTALS --}}
|
||||
<div class="row text-center mt-4">
|
||||
<div class="col-md-3">
|
||||
@@ -239,7 +376,7 @@
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('admin.orders.addItem', $order->id) }}" method="POST">
|
||||
<form id="addItemForm" action="{{ route('admin.orders.addItem', $order->id) }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="modal-body">
|
||||
@@ -253,54 +390,75 @@
|
||||
|
||||
<h5 class="section-title">Deleted Items (Use / Restore)</h5>
|
||||
|
||||
<ul class="list-group mb-3">
|
||||
<div class="table-responsive mb-4">
|
||||
<table class="table table-bordered align-middle text-center">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Description</th>
|
||||
<th>CTN</th>
|
||||
<th>QTY</th>
|
||||
<th>TTL QTY</th>
|
||||
<th>Unit</th>
|
||||
<th>Price</th>
|
||||
<th>TTL Amount</th>
|
||||
<th>CBM</th>
|
||||
<th>TTL CBM</th>
|
||||
<th>KG</th>
|
||||
<th>TTL KG</th>
|
||||
<th>Shop No</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@forelse($deletedItems as $deleted)
|
||||
<li class="list-group-item">
|
||||
<tbody>
|
||||
@forelse($deletedItems as $key => $deleted)
|
||||
<tr>
|
||||
<td>{{ $key + 1 }}</td>
|
||||
<td>{{ $deleted->description }}</td>
|
||||
<td>{{ $deleted->ctn }}</td>
|
||||
<td>{{ $deleted->qty }}</td>
|
||||
<td>{{ $deleted->ttl_qty }}</td>
|
||||
<td>{{ $deleted->unit }}</td>
|
||||
<td>{{ number_format($deleted->price, 2) }}</td>
|
||||
<td>{{ number_format($deleted->ttl_amount, 2) }}</td>
|
||||
<td>{{ $deleted->cbm }}</td>
|
||||
<td>{{ $deleted->ttl_cbm }}</td>
|
||||
<td>{{ $deleted->kg }}</td>
|
||||
<td>{{ $deleted->ttl_kg }}</td>
|
||||
<td>{{ $deleted->shop_no }}</td>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<td class="d-flex justify-content-center gap-2">
|
||||
|
||||
<div>
|
||||
<strong>{{ $deleted->description }}</strong><br>
|
||||
<small>
|
||||
CTN: {{ $deleted->ctn }},
|
||||
QTY: {{ $deleted->qty }},
|
||||
TTL/QTY: {{ $deleted->ttl_qty }},
|
||||
Unit: {{ $deleted->unit }},
|
||||
Price: ₹{{ $deleted->price }},
|
||||
TTL Amt: ₹{{ $deleted->ttl_amount }},
|
||||
CBM: {{ $deleted->cbm }},
|
||||
TTL CBM: {{ $deleted->ttl_cbm }},
|
||||
KG: {{ $deleted->kg }},
|
||||
TTL KG: {{ $deleted->ttl_kg }},
|
||||
Shop: {{ $deleted->shop_no }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
|
||||
{{-- Auto-fill button --}}
|
||||
<button type="button" class="btn btn-sm btn-use-item"
|
||||
{{-- USE THIS ITEM --}}
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-use-item"
|
||||
onclick="fillFormFromDeleted({{ json_encode($deleted) }})">
|
||||
Use This
|
||||
Use
|
||||
</button>
|
||||
|
||||
{{-- Restore --}}
|
||||
{{-- RESTORE ITEM --}}
|
||||
<form action="{{ route('admin.orders.restoreItem', $deleted->id) }}" method="POST">
|
||||
@csrf
|
||||
<button class="btn btn-sm btn-restore">Restore</button>
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-restore js-restore-item"
|
||||
data-id="{{ $deleted->id }}"
|
||||
data-url="{{ route('admin.orders.restoreItem', $deleted->id) }}">
|
||||
Restore
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</li>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<li class="list-group-item text-muted">No deleted items.</li>
|
||||
<tr>
|
||||
<td colspan="14" class="text-muted">No deleted items.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</ul>
|
||||
|
||||
{{-- ADD FORM --}}
|
||||
<div class="row g-3">
|
||||
@@ -380,21 +538,62 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const token = '{{ csrf_token() }}';
|
||||
|
||||
document.querySelectorAll('.js-restore-item').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
if (!confirm('Restore this item?')) return;
|
||||
|
||||
const url = this.dataset.url;
|
||||
const row = this.closest('tr');
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': token,
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({})
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Restore failed');
|
||||
return response.json().catch(() => ({}));
|
||||
})
|
||||
.then(() => {
|
||||
row.remove();
|
||||
location.reload(); // 🔥 refresh UI so restored item appears
|
||||
})
|
||||
.catch(() => {
|
||||
alert('Could not restore item. Please try again.');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{{-- AUTO-FILL SCRIPT --}}
|
||||
<script>
|
||||
function fillFormFromDeleted(item) {
|
||||
document.querySelector('input[name="description"]').value = item.description;
|
||||
document.querySelector('input[name="ctn"]').value = item.ctn;
|
||||
document.querySelector('input[name="qty"]').value = item.qty;
|
||||
document.querySelector('input[name="ttl_qty"]').value = item.ttl_qty;
|
||||
document.querySelector('input[name="unit"]').value = item.unit;
|
||||
document.querySelector('input[name="price"]').value = item.price;
|
||||
document.querySelector('input[name="ttl_amount"]').value = item.ttl_amount;
|
||||
document.querySelector('input[name="cbm"]').value = item.cbm;
|
||||
document.querySelector('input[name="ttl_cbm"]').value = item.ttl_cbm;
|
||||
document.querySelector('input[name="kg"]').value = item.kg;
|
||||
document.querySelector('input[name="ttl_kg"]').value = item.ttl_kg;
|
||||
document.querySelector('input[name="shop_no"]').value = item.shop_no;
|
||||
let form = document.getElementById('addItemForm');
|
||||
|
||||
form.querySelector('input[name="description"]').value = item.description;
|
||||
form.querySelector('input[name="ctn"]').value = item.ctn;
|
||||
form.querySelector('input[name="qty"]').value = item.qty;
|
||||
form.querySelector('input[name="ttl_qty"]').value = item.ttl_qty;
|
||||
form.querySelector('input[name="unit"]').value = item.unit;
|
||||
form.querySelector('input[name="price"]').value = item.price;
|
||||
form.querySelector('input[name="ttl_amount"]').value = item.ttl_amount;
|
||||
form.querySelector('input[name="cbm"]').value = item.cbm;
|
||||
form.querySelector('input[name="ttl_cbm"]').value = item.ttl_cbm;
|
||||
form.querySelector('input[name="kg"]').value = item.kg;
|
||||
form.querySelector('input[name="ttl_kg"]').value = item.ttl_kg;
|
||||
form.querySelector('input[name="shop_no"]').value = item.shop_no;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -404,13 +603,15 @@ function fillFormFromDeleted(item) {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
padding: 6px 14px;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px 0 rgba(102, 126, 234, 0.3);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-right: -800px;
|
||||
}
|
||||
|
||||
.btn-add-item:hover {
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
--hover-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
/* Search Bar Styles */
|
||||
/* UPDATED: Search Bar Styles - White Background */
|
||||
.search-shipment-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -121,6 +121,7 @@
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
/* UPDATED: Search Button - White with Blue Icon by default, Gradient on hover */
|
||||
.search-button {
|
||||
background: white;
|
||||
color: #4361ee;
|
||||
@@ -145,8 +146,7 @@
|
||||
border-radius: 8px !important;
|
||||
padding: 4px !important;
|
||||
background: #ffffff !important;
|
||||
}
|
||||
|
||||
}
|
||||
.search-button:hover {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
@@ -220,6 +220,26 @@
|
||||
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 {
|
||||
@@ -320,7 +340,7 @@
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Status Badge Styles */
|
||||
/* UPDATED: Status Badge Styles - ALL SAME SIZE WITH ICONS */
|
||||
.badge {
|
||||
padding: 7px 17px !important;
|
||||
border-radius: 20px !important;
|
||||
@@ -346,34 +366,55 @@
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Pending Status */
|
||||
/* Pending Status - SAME SIZE WITH CLOCK ICON */
|
||||
.badge-pending {
|
||||
background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
|
||||
color: #d97706 !important;
|
||||
border-color: #f59e0b !important;
|
||||
}
|
||||
|
||||
/* In Transit Status */
|
||||
/* In Transit Status - SAME SIZE WITH TRUCK ICON */
|
||||
.badge-in_transit {
|
||||
background: linear-gradient(135deg, #dbeafe, #93c5fd) !important;
|
||||
color: #1e40af !important;
|
||||
border-color: #3b82f6 !important;
|
||||
}
|
||||
|
||||
/* Dispatched Status */
|
||||
/* Dispatched Status - SAME SIZE WITH BOX ICON */
|
||||
.badge-dispatched {
|
||||
background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important;
|
||||
color: #6b21a8 !important;
|
||||
border-color: #8b5cf6 !important;
|
||||
}
|
||||
|
||||
/* Delivered Status */
|
||||
/* Delivered Status - SAME SIZE WITH CHECK ICON */
|
||||
.badge-delivered {
|
||||
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
|
||||
color: #065f46 !important;
|
||||
border-color: #10b981 !important;
|
||||
}
|
||||
|
||||
/* Default badge styles - SAME SIZE */
|
||||
.badge.bg-info {
|
||||
background: linear-gradient(135deg, #4cc9f0, #4361ee) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.badge.bg-success {
|
||||
background: linear-gradient(135deg, #4ade80, #22c55e) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.badge.bg-warning {
|
||||
background: linear-gradient(135deg, #fbbf24, #f59e0b) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.badge.bg-danger {
|
||||
background: linear-gradient(135deg, #f87171, #ef4444) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
/* Light badges for quantity, kg, cbm */
|
||||
.badge.bg-light {
|
||||
background: #f8f9fa !important;
|
||||
@@ -386,7 +427,7 @@
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Action Button Styles */
|
||||
/* NEW: Action Button Styles */
|
||||
.action-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
@@ -516,7 +557,7 @@
|
||||
background: #10b981;
|
||||
}
|
||||
|
||||
/* View Button Styles */
|
||||
/* NEW: View Button Styles - Icon Only */
|
||||
.btn-view {
|
||||
background: #4361ee;
|
||||
color: white;
|
||||
@@ -655,6 +696,39 @@
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
/* Date Input Styling */
|
||||
input[type="date"] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input[type="date"]::-webkit-calendar-picker-indicator {
|
||||
background: transparent;
|
||||
bottom: 0;
|
||||
color: transparent;
|
||||
cursor: pointer;
|
||||
height: auto;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
input[type="date"] {
|
||||
position: relative;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
input[type="date"]:after {
|
||||
content: "📅";
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* Button Styles */
|
||||
.btn-cancel {
|
||||
background: #f7fafc;
|
||||
@@ -765,7 +839,42 @@
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
|
||||
/* Shipment Details Modal */
|
||||
/* Checkbox Styling */
|
||||
input[type="checkbox"] {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 6px;
|
||||
border: 2px solid #cbd5e0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s ease;
|
||||
background: #f7fafc;
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked {
|
||||
background: var(--primary);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* Link Styling */
|
||||
a.text-primary {
|
||||
color: var(--primary) !important;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
color: #4361ee !important;
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a.text-primary:hover {
|
||||
color: var(--primary-dark) !important;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Shipment Details Modal - UPDATED TO MATCH SECOND CODE */
|
||||
.shipment-details-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
@@ -865,32 +974,7 @@
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
|
||||
/* Delete Button for Orders */
|
||||
.btn-delete-order {
|
||||
background: linear-gradient(135deg, #f87171, #ef4444);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
font-family: 'Inter', sans-serif;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
min-width: 80px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-delete-order:hover {
|
||||
background: linear-gradient(135deg, #ef4444, #dc2626);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
|
||||
}
|
||||
|
||||
/* Shipment Totals Section */
|
||||
/* Shipment Totals Section - UPDATED */
|
||||
.shipment-totals {
|
||||
margin-top: 25px;
|
||||
padding: 25px;
|
||||
@@ -1021,7 +1105,20 @@
|
||||
padding-right: 40px !important;
|
||||
}
|
||||
|
||||
/* Pagination Styles */
|
||||
/* Shipment row styling for filtering */
|
||||
.shipment-row {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.shipment-row.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.shipment-row.visible {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
/* ---------- Pagination Styles (Same as Account Dashboard) ---------- */
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -1117,6 +1214,7 @@
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
/* Image-based pagination buttons */
|
||||
.pagination-img-btn {
|
||||
background: #fff;
|
||||
border: 1px solid #e3eaf6;
|
||||
@@ -1170,43 +1268,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete Confirmation Modal */
|
||||
.delete-confirmation-modal .modal-header {
|
||||
background: linear-gradient(135deg, #ef4444, #dc2626);
|
||||
/* Edit Form Styles */
|
||||
.edit-shipment-form {
|
||||
background: #f8fafc;
|
||||
padding: 25px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 4px solid #4361ee;
|
||||
}
|
||||
|
||||
.delete-confirmation-modal .btn-confirm-delete {
|
||||
background: linear-gradient(135deg, #ef4444, #dc2626);
|
||||
.btn-save {
|
||||
background: linear-gradient(135deg, #48bb78, #38a169);
|
||||
color: white;
|
||||
border: none;
|
||||
font-weight: 600;
|
||||
border-radius: 8px;
|
||||
padding: 10px 20px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.delete-confirmation-modal .btn-confirm-delete:hover {
|
||||
background: linear-gradient(135deg, #dc2626, #b91c1c);
|
||||
.btn-save:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(220, 38, 38, 0.3);
|
||||
box-shadow: 0 4px 12px rgba(72, 187, 120, 0.3);
|
||||
}
|
||||
|
||||
.delete-confirmation-modal .btn-cancel-delete {
|
||||
.btn-cancel-edit {
|
||||
background: #f7fafc;
|
||||
color: #718096;
|
||||
border: 1px solid #cbd5e0;
|
||||
border-radius: 8px;
|
||||
padding: 10px 20px;
|
||||
font-weight: 600;
|
||||
padding: 10px 20px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.delete-confirmation-modal .btn-cancel-delete:hover {
|
||||
.btn-cancel-edit:hover {
|
||||
background: #edf2f7;
|
||||
color: #4a5568;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
|
||||
{{-- SUCCESS / ERROR MESSAGES --}}
|
||||
@@ -1226,27 +1328,12 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- ============================= -->
|
||||
<!-- SEARCH BAR AND ADD BUTTON -->
|
||||
<!-- ============================= -->
|
||||
<div class="search-shipment-bar">
|
||||
<form method="GET" action="">
|
||||
<div class="input-group mb-2 search-input-group">
|
||||
<input
|
||||
type="text"
|
||||
name="search"
|
||||
id="searchInput"
|
||||
value="{{ request('search') }}"
|
||||
class="form-control"
|
||||
placeholder="Search Shipments..."
|
||||
>
|
||||
<button
|
||||
class="btn btn-outline-primary search-button"
|
||||
type="submit"
|
||||
>
|
||||
<i class="bi bi-search search-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<span class="search-icon">🔍</span>
|
||||
<input type="text" id="searchInput" placeholder="Search Shipments...">
|
||||
<div class="status-filter-container">
|
||||
<select id="statusFilter" class="status-filter-select">
|
||||
<option value="all">All Status</option>
|
||||
@@ -1256,13 +1343,21 @@
|
||||
<option value="delivered">Delivered</option>
|
||||
</select>
|
||||
</div>
|
||||
<select id="carrierFilter">
|
||||
<option value="all">All Carriers</option>
|
||||
<option value="fedex">FedEx</option>
|
||||
<option value="ups">UPS</option>
|
||||
<option value="dhl">DHL</option>
|
||||
</select>
|
||||
<button type="button" class="btn-add-shipment" data-bs-toggle="modal" data-bs-target="#createShipmentModal">
|
||||
<span class="truck-icon">🚚</span>
|
||||
Add Shipments
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ============================= -->
|
||||
<!-- CREATE SHIPMENT MODAL -->
|
||||
<!-- ============================= -->
|
||||
<div class="modal fade" id="createShipmentModal" tabindex="-1" aria-labelledby="createShipmentModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
@@ -1354,7 +1449,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============================= -->
|
||||
<!-- SHIPMENT LIST TABLE -->
|
||||
<!-- ============================= -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="bi bi-truck me-2"></i> Shipments List</h5>
|
||||
@@ -1375,7 +1472,7 @@
|
||||
<th>Total Amount</th>
|
||||
<th>Status</th>
|
||||
<th>Date</th>
|
||||
<th>Actions</th>
|
||||
<th>Action</th>
|
||||
<th>View</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -1386,36 +1483,26 @@
|
||||
@endphp
|
||||
@forelse($shipments as $ship)
|
||||
<tr class="shipment-row" data-status="{{ $ship->status }}" data-shipment-id="{{ $ship->shipment_id }}">
|
||||
{{-- REVERSE INDEX --}}
|
||||
{{-- REVERSE INDEX: सर्वात वरच्या shipment ला सर्वात मोठा क्रमांक --}}
|
||||
<td class="fw-bold">{{ $totalShipments - $loop->index }}</td>
|
||||
<td>
|
||||
<span class="fw-bold text-primary">
|
||||
<a href="#" class="text-primary fw-bold" onclick="openShipmentDetails({{ $ship->id }})">
|
||||
{{ $ship->shipment_id }}
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ $ship->origin }}</td>
|
||||
<td>{{ $ship->destination }}</td>
|
||||
<td><span class="badge bg-light text-dark">{{ $ship->total_qty }}</span></td>
|
||||
<td><span class="badge bg-light text-dark">{{ $ship->total_kg }} kg</span></td>
|
||||
<td><span class="badge bg-light text-dark">{{ $ship->total_cbm }} CBM</span></td>
|
||||
<td>{{ $ship->total_qty }}</td>
|
||||
<td>{{ $ship->total_kg }} kg</td>
|
||||
<td>{{ $ship->total_cbm }} CBM</td>
|
||||
<td class="fw-bold text-success">₹{{ number_format($ship->total_amount, 2) }}</td>
|
||||
<td>
|
||||
<span class="badge badge-{{ $ship->status }}">
|
||||
@if($ship->status == 'pending')
|
||||
<i class="bi bi-clock-fill status-icon"></i>
|
||||
@elseif($ship->status == 'in_transit')
|
||||
<i class="bi bi-truck status-icon"></i>
|
||||
@elseif($ship->status == 'dispatched')
|
||||
<i class="bi bi-box-seam status-icon"></i>
|
||||
@elseif($ship->status == 'delivered')
|
||||
<i class="bi bi-check-circle-fill status-icon"></i>
|
||||
@endif
|
||||
{{ ucfirst(str_replace('_', ' ', $ship->status)) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ \Carbon\Carbon::parse($ship->shipment_date)->format('d M Y') }}</td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
<div class="action-container">
|
||||
<button type="button" class="btn-edit-status" onclick="toggleStatusDropdown(this, {{ $ship->id }})" title="Edit Status">
|
||||
<i class="bi bi-pencil"></i>
|
||||
@@ -1443,12 +1530,12 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn-view" onclick="viewShipmentOrders({{ $ship->id }})" title="View Orders">
|
||||
<a href="{{ route('admin.shipments.view', ['id' => $ship->id, 'mode' => 'edit']) }}"
|
||||
class="btn btn-view">
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
@@ -1468,6 +1555,7 @@
|
||||
<div class="pagination-info" id="pageInfo">Showing 1 to {{ $shipments->count() }} of {{ $shipments->count() }} entries</div>
|
||||
<div class="pagination-controls">
|
||||
<button class="pagination-img-btn" id="prevPageBtn" title="Previous page" disabled>
|
||||
<!-- Left arrow SVG -->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
@@ -1476,6 +1564,7 @@
|
||||
<!-- Page numbers will be inserted here -->
|
||||
</div>
|
||||
<button class="pagination-img-btn" id="nextPageBtn" title="Next page" disabled>
|
||||
<!-- Right arrow SVG -->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
@@ -1485,68 +1574,42 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- VIEW SHIPMENT ORDERS MODAL -->
|
||||
<div class="modal fade" id="viewShipmentOrdersModal" tabindex="-1">
|
||||
</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 d-flex justify-content-between align-items-center">
|
||||
<h5 class="modal-title fw-bold">
|
||||
<i class="bi bi-list-ul me-2"></i>
|
||||
Shipment Details — <span id="shipmentOrdersHeader"></span>
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
<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="shipmentOrdersContent">
|
||||
|
||||
<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>
|
||||
<p class="mt-2 text-muted">Loading orders...</p>
|
||||
<p class="mt-2 text-muted">Loading shipment details...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DELETE CONFIRMATION MODAL -->
|
||||
<div class="modal fade delete-confirmation-modal" id="deleteOrderModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title fw-bold">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
Confirm Delete
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<i class="bi bi-trash-fill display-1 text-danger mb-3"></i>
|
||||
<h4 class="mb-3">Are you sure?</h4>
|
||||
<p class="text-muted mb-4">You are about to delete this order from the shipment. This action cannot be undone.</p>
|
||||
<input type="hidden" id="orderToDeleteId">
|
||||
<input type="hidden" id="shipmentToDeleteFromId">
|
||||
<div class="d-flex justify-content-center gap-3">
|
||||
<button type="button" class="btn-cancel-delete" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn-confirm-delete" onclick="confirmDeleteOrder()">
|
||||
<i class="bi bi-trash me-2"></i>Delete Order
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ========================= -->
|
||||
<!-- MODAL LOAD SCRIPT (AJAX) -->
|
||||
<!-- ========================= -->
|
||||
<script>
|
||||
// Global variables
|
||||
let currentShipmentId = null;
|
||||
// Pagination state
|
||||
let currentPage = 1;
|
||||
const itemsPerPage = 10;
|
||||
let allShipments = @json($shipments);
|
||||
let filteredShipments = [...allShipments];
|
||||
|
||||
// Initialize on page load
|
||||
// Initialize pagination on page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
renderTable();
|
||||
updatePaginationControls();
|
||||
@@ -1558,11 +1621,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// Status Filter Functionality
|
||||
const statusFilter = document.getElementById('statusFilter');
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const carrierFilter = document.getElementById('carrierFilter');
|
||||
|
||||
// Function to filter shipments
|
||||
function filterShipments() {
|
||||
const selectedStatus = statusFilter.value;
|
||||
const searchTerm = searchInput.value.toLowerCase();
|
||||
const selectedCarrier = carrierFilter.value;
|
||||
|
||||
filteredShipments = allShipments.filter(shipment => {
|
||||
let include = true;
|
||||
@@ -1575,12 +1640,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// Search filter
|
||||
if (searchTerm) {
|
||||
const matchesSearch =
|
||||
(shipment.shipment_id && shipment.shipment_id.toLowerCase().includes(searchTerm)) ||
|
||||
(shipment.origin && shipment.origin.toLowerCase().includes(searchTerm)) ||
|
||||
(shipment.destination && shipment.destination.toLowerCase().includes(searchTerm));
|
||||
shipment.shipment_id.toLowerCase().includes(searchTerm) ||
|
||||
shipment.origin.toLowerCase().includes(searchTerm) ||
|
||||
shipment.destination.toLowerCase().includes(searchTerm);
|
||||
if (!matchesSearch) include = false;
|
||||
}
|
||||
|
||||
// Carrier filter (you can add carrier data attribute if needed)
|
||||
if (selectedCarrier !== 'all') {
|
||||
// Add carrier filtering logic here if you have carrier data
|
||||
}
|
||||
|
||||
return include;
|
||||
});
|
||||
|
||||
@@ -1592,11 +1662,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// Event listeners for filters
|
||||
statusFilter.addEventListener('change', filterShipments);
|
||||
searchInput.addEventListener('input', filterShipments);
|
||||
searchInput.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
filterShipments();
|
||||
}
|
||||
});
|
||||
carrierFilter.addEventListener('change', filterShipments);
|
||||
|
||||
// Initialize filter on page load
|
||||
filterShipments();
|
||||
@@ -1717,9 +1783,9 @@ function renderTable() {
|
||||
row.innerHTML = `
|
||||
<td class="fw-bold">${displayIndex}</td>
|
||||
<td>
|
||||
<span class="fw-bold text-primary">
|
||||
<a href="#" class="text-primary fw-bold" onclick="openShipmentDetails(${shipment.id})">
|
||||
${shipment.shipment_id}
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>${shipment.origin}</td>
|
||||
<td>${shipment.destination}</td>
|
||||
@@ -1729,16 +1795,11 @@ function renderTable() {
|
||||
<td class="fw-bold text-success">₹${parseFloat(shipment.total_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
||||
<td>
|
||||
<span class="badge badge-${shipment.status}">
|
||||
${shipment.status === 'pending' ? '<i class="bi bi-clock-fill status-icon"></i>' : ''}
|
||||
${shipment.status === 'in_transit' ? '<i class="bi bi-truck status-icon"></i>' : ''}
|
||||
${shipment.status === 'dispatched' ? '<i class="bi bi-box-seam status-icon"></i>' : ''}
|
||||
${shipment.status === 'delivered' ? '<i class="bi bi-check-circle-fill status-icon"></i>' : ''}
|
||||
${shipment.status.charAt(0).toUpperCase() + shipment.status.slice(1).replace('_', ' ')}
|
||||
</span>
|
||||
</td>
|
||||
<td>${new Date(shipment.shipment_date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })}</td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
<div class="action-container">
|
||||
<button type="button" class="btn-edit-status" onclick="toggleStatusDropdown(this, ${shipment.id})" title="Edit Status">
|
||||
<i class="bi bi-pencil"></i>
|
||||
@@ -1766,42 +1827,46 @@ function renderTable() {
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn-view" onclick="viewShipmentOrders(${shipment.id})" title="View Orders">
|
||||
<a href="/admin/shipments/view/${shipment.id}?mode=edit"
|
||||
class="btn btn-view"
|
||||
title="Edit Shipment">
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
</a>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to view shipment orders
|
||||
function viewShipmentOrders(shipmentId) {
|
||||
currentShipmentId = shipmentId;
|
||||
const modal = new bootstrap.Modal(document.getElementById('viewShipmentOrdersModal'));
|
||||
const content = document.getElementById('shipmentOrdersContent');
|
||||
const header = document.getElementById('shipmentOrdersHeader');
|
||||
// Function to open shipment details modal
|
||||
function openShipmentDetails(id) {
|
||||
let modal = new bootstrap.Modal(document.getElementById('shipmentDetailsModal'));
|
||||
let content = document.getElementById('shipmentDetailsContent');
|
||||
|
||||
content.innerHTML = `
|
||||
<div class="text-center py-4 loading">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p class="mt-2 text-muted">Loading orders...</p>
|
||||
<p class="mt-2 text-muted">Loading shipment details...</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
fetch(`/admin/shipments/${shipmentId}/orders`)
|
||||
fetch(`/admin/shipments/${id}`)
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error('Network response was not ok');
|
||||
return res.json();
|
||||
})
|
||||
.then(data => {
|
||||
// Update header with shipment ID
|
||||
header.textContent = data.shipment.shipment_id;
|
||||
// Format date properly
|
||||
const shipmentDate = new Date(data.shipment.shipment_date);
|
||||
const formattedDate = shipmentDate.toLocaleDateString('en-GB', {
|
||||
day: '2-digit',
|
||||
month: 'short',
|
||||
year: 'numeric'
|
||||
});
|
||||
|
||||
let html = `
|
||||
<!-- Shipment Basic Info -->
|
||||
@@ -1816,19 +1881,15 @@ function viewShipmentOrders(shipmentId) {
|
||||
</div>
|
||||
<div class="shipment-info-item">
|
||||
<div class="shipment-info-label">Status</div>
|
||||
<div class="shipment-info-value">
|
||||
<span class="badge badge-${data.shipment.status}">
|
||||
${data.shipment.status.charAt(0).toUpperCase() + data.shipment.status.slice(1).replace('_', ' ')}
|
||||
</span>
|
||||
</div>
|
||||
<div class="shipment-info-value">${data.shipment.status}</div>
|
||||
</div>
|
||||
<div class="shipment-info-item">
|
||||
<div class="shipment-info-label">Date</div>
|
||||
<div class="shipment-info-value">${new Date(data.shipment.shipment_date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })}</div>
|
||||
<div class="shipment-info-value">${formattedDate}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mt-4 mb-3">Detailed view of all orders in this shipment</h6>
|
||||
<h6 class="fw-bold mt-4 mb-3">Detailed view of all orders in this shipment consolidation</h6>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="shipment-details-table">
|
||||
@@ -1837,6 +1898,8 @@ function viewShipmentOrders(shipmentId) {
|
||||
<th>Order ID</th>
|
||||
<th>Origin</th>
|
||||
<th>Destination</th>
|
||||
<th>Item No</th>
|
||||
<th>Description</th>
|
||||
<th>CTN</th>
|
||||
<th>QTY</th>
|
||||
<th>TTL/QTY</th>
|
||||
@@ -1845,7 +1908,6 @@ function viewShipmentOrders(shipmentId) {
|
||||
<th>KG</th>
|
||||
<th>TTL KG</th>
|
||||
<th>Amount (₹)</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -1853,10 +1915,12 @@ function viewShipmentOrders(shipmentId) {
|
||||
|
||||
data.orders.forEach(order => {
|
||||
html += `
|
||||
<tr id="order-row-${order.id}">
|
||||
<tr>
|
||||
<td class="fw-bold text-primary">${order.order_id}</td>
|
||||
<td>${order.origin || 'N/A'}</td>
|
||||
<td>${order.destination || 'N/A'}</td>
|
||||
<td>${order.mark_no || 'ITEM001'}</td>
|
||||
<td>${order.description || 'Manufacturing Equipment'}</td>
|
||||
<td>${order.ctn}</td>
|
||||
<td>${order.qty}</td>
|
||||
<td>${order.ttl_qty}</td>
|
||||
@@ -1865,11 +1929,6 @@ function viewShipmentOrders(shipmentId) {
|
||||
<td>${order.kg || '0.00'}</td>
|
||||
<td>${order.ttl_kg || '0.00'}</td>
|
||||
<td class="fw-bold text-success">₹${parseFloat(order.ttl_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
||||
<td>
|
||||
<button type="button" class="btn-delete-order" onclick="deleteOrderFromShipment(${order.id}, ${data.shipment.id})">
|
||||
<i class="bi bi-trash"></i> Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
@@ -1921,13 +1980,13 @@ function viewShipmentOrders(shipmentId) {
|
||||
content.innerHTML = html;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading shipment orders:', error);
|
||||
console.error('Error loading shipment details:', error);
|
||||
content.innerHTML = `
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-exclamation-triangle display-1 text-danger"></i>
|
||||
<h4 class="mt-3 text-danger">Error Loading Orders</h4>
|
||||
<h4 class="mt-3 text-danger">Error Loading Shipment Details</h4>
|
||||
<p class="text-muted">Please try again later</p>
|
||||
<button class="btn btn-primary mt-2" onclick="viewShipmentOrders(${shipmentId})">Retry</button>
|
||||
<button class="btn btn-primary mt-2" onclick="openShipmentDetails(${id})">Retry</button>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
@@ -1935,136 +1994,6 @@ function viewShipmentOrders(shipmentId) {
|
||||
modal.show();
|
||||
}
|
||||
|
||||
// Function to show delete confirmation modal
|
||||
function deleteOrderFromShipment(orderId, shipmentId) {
|
||||
document.getElementById('orderToDeleteId').value = orderId;
|
||||
document.getElementById('shipmentToDeleteFromId').value = shipmentId;
|
||||
|
||||
const deleteModal = new bootstrap.Modal(document.getElementById('deleteOrderModal'));
|
||||
deleteModal.show();
|
||||
}
|
||||
|
||||
// Function to confirm and delete order from shipment
|
||||
function confirmDeleteOrder() {
|
||||
const orderId = document.getElementById('orderToDeleteId').value;
|
||||
const shipmentId = document.getElementById('shipmentToDeleteFromId').value;
|
||||
|
||||
// Create form data for DELETE request
|
||||
const formData = new FormData();
|
||||
formData.append('_method', 'DELETE');
|
||||
formData.append('_token', '{{ csrf_token() }}');
|
||||
|
||||
fetch(`/admin/shipments/${shipmentId}/orders/${orderId}`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Close delete modal
|
||||
const deleteModal = bootstrap.Modal.getInstance(document.getElementById('deleteOrderModal'));
|
||||
deleteModal.hide();
|
||||
|
||||
// Remove the order row from the table
|
||||
const orderRow = document.getElementById(`order-row-${orderId}`);
|
||||
if (orderRow) {
|
||||
orderRow.remove();
|
||||
}
|
||||
|
||||
// Show success message
|
||||
showAlert('Order removed successfully from shipment', 'success');
|
||||
|
||||
// If there are no orders left, close the modal and refresh the page
|
||||
if (data.orders_remaining === 0) {
|
||||
const viewModal = bootstrap.Modal.getInstance(document.getElementById('viewShipmentOrdersModal'));
|
||||
viewModal.hide();
|
||||
location.reload();
|
||||
} else {
|
||||
// Update totals in the modal
|
||||
updateShipmentTotals(data.shipment);
|
||||
}
|
||||
} else {
|
||||
showAlert('Error removing order: ' + (data.message || 'Unknown error'), 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error deleting order:', error);
|
||||
showAlert('Error removing order. Please try again.', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
// Function to update shipment totals in the modal
|
||||
function updateShipmentTotals(shipment) {
|
||||
// Update totals section
|
||||
const totalsRow = document.querySelector('.shipment-totals-row');
|
||||
if (totalsRow) {
|
||||
totalsRow.innerHTML = `
|
||||
<div class="shipment-total-item">
|
||||
<div class="shipment-total-label">Total CTN</div>
|
||||
<div class="shipment-total-value total-ctn">${shipment.total_ctn}</div>
|
||||
</div>
|
||||
<div class="shipment-total-item">
|
||||
<div class="shipment-total-label">Total QTY</div>
|
||||
<div class="shipment-total-value total-quantity">${shipment.total_qty}</div>
|
||||
</div>
|
||||
<div class="shipment-total-item">
|
||||
<div class="shipment-total-label">Total CBM</div>
|
||||
<div class="shipment-total-value total-cbm">${shipment.total_cbm}</div>
|
||||
</div>
|
||||
<div class="shipment-total-item">
|
||||
<div class="shipment-total-label">Total KG</div>
|
||||
<div class="shipment-total-value total-weight">${shipment.total_kg}</div>
|
||||
</div>
|
||||
<div class="shipment-total-item">
|
||||
<div class="shipment-total-label">Total TTL/QTY</div>
|
||||
<div class="shipment-total-value total-quantity">${shipment.total_ttl_qty}</div>
|
||||
</div>
|
||||
<div class="shipment-total-item">
|
||||
<div class="shipment-total-label">Total TTL/CBM</div>
|
||||
<div class="shipment-total-value total-ttl-cbm">${shipment.total_ttl_cbm}</div>
|
||||
</div>
|
||||
<div class="shipment-total-item">
|
||||
<div class="shipment-total-label">Total TTL/KG</div>
|
||||
<div class="shipment-total-value total-ttl-kg">${shipment.total_ttl_kg}</div>
|
||||
</div>
|
||||
<div class="shipment-total-item">
|
||||
<div class="shipment-total-label">Total Amount</div>
|
||||
<div class="shipment-total-value total-amount">₹${parseFloat(shipment.total_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to show alert messages
|
||||
function showAlert(message, type) {
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `alert alert-${type === 'success' ? 'success' : 'danger'} alert-dismissible fade show position-fixed`;
|
||||
alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
|
||||
alertDiv.innerHTML = `
|
||||
<i class="bi ${type === 'success' ? 'bi-check-circle-fill' : 'bi-exclamation-triangle-fill'} me-2"></i>
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
`;
|
||||
|
||||
document.body.appendChild(alertDiv);
|
||||
|
||||
// Auto remove after 5 seconds
|
||||
setTimeout(() => {
|
||||
if (alertDiv.parentNode) {
|
||||
alertDiv.parentNode.removeChild(alertDiv);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Toggle status dropdown
|
||||
function toggleStatusDropdown(button, shipmentId) {
|
||||
const dropdown = document.getElementById(`statusDropdown-${shipmentId}`);
|
||||
@@ -2082,11 +2011,15 @@ function toggleStatusDropdown(button, shipmentId) {
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', function closeDropdown(e) {
|
||||
// allow clicking links normally
|
||||
if (e.target.closest('a')) return;
|
||||
|
||||
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
|
||||
|
||||
975
resources/views/admin/view-shipment.blade.php
Normal file
975
resources/views/admin/view-shipment.blade.php
Normal file
@@ -0,0 +1,975 @@
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('page-title', 'Shipment Details')
|
||||
|
||||
@section('content')
|
||||
|
||||
<style>
|
||||
/* ===== ANIMATIONS ===== */
|
||||
.fade-in {
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0px); }
|
||||
}
|
||||
|
||||
/* ===== CARD STYLES ===== */
|
||||
.card {
|
||||
border-radius: 20px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 15px 35px rgba(13, 38, 76, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-6px);
|
||||
box-shadow: 0 20px 40px rgba(103, 126, 234, 0.15);
|
||||
}
|
||||
|
||||
.card-header h5 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* ===== GLASSMORPHISM BUTTON ===== */
|
||||
.glass-btn {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(103, 126, 234, 0.25) 0%,
|
||||
rgba(118, 75, 162, 0.25) 100%
|
||||
);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.4);
|
||||
color: #ffffff !important;
|
||||
padding: 12px 24px;
|
||||
border-radius: 14px;
|
||||
font-weight: 700;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
text-decoration: none;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(103, 126, 234, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.3),
|
||||
inset 0 -1px 0 rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.glass-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(103, 126, 234, 0.4) 0%,
|
||||
rgba(118, 75, 162, 0.4) 100%
|
||||
);
|
||||
z-index: -1;
|
||||
transition: opacity 0.4s ease;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.glass-btn:hover {
|
||||
transform: translateY(-4px) scale(1.05);
|
||||
box-shadow:
|
||||
0 15px 40px rgba(103, 126, 234, 0.35),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.4),
|
||||
inset 0 -1px 0 rgba(0, 0, 0, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.6);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(103, 126, 234, 0.15) 0%,
|
||||
rgba(118, 75, 162, 0.15) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.glass-btn:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.glass-btn:active {
|
||||
transform: translateY(-1px) scale(0.98);
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
.glass-btn i {
|
||||
font-size: 18px;
|
||||
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
|
||||
}
|
||||
|
||||
/* ===== HEADER STYLES ===== */
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
padding: 22px 28px;
|
||||
border-radius: 20px 20px 0 0 !important;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(255, 255, 255, 0.1) 0%,
|
||||
rgba(255, 255, 255, 0.05) 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.header-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.header-cancel-btn {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
backdrop-filter: blur(10px);
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.3);
|
||||
padding: 10px 14px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header-cancel-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* ===== INFO BLOCKS ===== */
|
||||
.shipment-info-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 24px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.shipment-info-item {
|
||||
flex: 1;
|
||||
min-width: 220px;
|
||||
background: linear-gradient(145deg, #ffffff, #f8fafc);
|
||||
padding: 24px;
|
||||
border-radius: 16px;
|
||||
box-shadow:
|
||||
0 5px 20px rgba(0, 0, 0, 0.05),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
text-align: center;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.shipment-info-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, #667eea, #764ba2);
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s ease;
|
||||
}
|
||||
|
||||
.shipment-info-item:hover {
|
||||
transform: translateY(-6px);
|
||||
box-shadow:
|
||||
0 15px 35px rgba(103, 126, 234, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.shipment-info-item:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.shipment-info-label {
|
||||
color: #64748b;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.shipment-info-value {
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
color: #1e293b;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* ===== TOTAL BOXES ===== */
|
||||
.total-box {
|
||||
background: linear-gradient(145deg, #ffffff, #f8fafc);
|
||||
padding: 20px;
|
||||
border-radius: 16px;
|
||||
box-shadow:
|
||||
0 5px 20px rgba(0, 0, 0, 0.05),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
text-align: center;
|
||||
min-width: 160px;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.total-box::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, #667eea, #764ba2);
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
|
||||
.total-box:hover {
|
||||
transform: translateY(-5px) scale(1.03);
|
||||
box-shadow:
|
||||
0 15px 35px rgba(103, 126, 234, 0.15),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.total-box:hover::after {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.total-title {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #64748b;
|
||||
margin-bottom: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.total-value {
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
color: #1e293b;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* ===== TABLE STYLES ===== */
|
||||
.table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
background: #667eea ;
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
padding: 18px;
|
||||
white-space: nowrap;
|
||||
border: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.table thead th::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.table tbody tr {
|
||||
transition: all 0.3s ease;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background: linear-gradient(90deg, rgba(103, 126, 234, 0.05), rgba(118, 75, 162, 0.05));
|
||||
transform: translateX(4px);
|
||||
border-left: 3px solid #667eea;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.table tbody td {
|
||||
padding: 16px;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ===== GLASSMORPHISM MODAL ===== */
|
||||
#newOrderModal .modal-content {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
||||
border-radius: 24px;
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.3);
|
||||
box-shadow:
|
||||
0 25px 50px -12px rgba(0, 0, 0, 0.25),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#newOrderModal .modal-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
|
||||
padding: 24px 32px;
|
||||
border-radius: 24px 24px 0 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#newOrderModal .modal-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(255, 255, 255, 0.1) 0%,
|
||||
rgba(255, 255, 255, 0.05) 100%
|
||||
);
|
||||
}
|
||||
|
||||
#newOrderModal .modal-title {
|
||||
color: #ffffff;
|
||||
font-weight: 800;
|
||||
font-size: 22px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#newOrderModal .btn-close {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 10px;
|
||||
padding: 8px;
|
||||
opacity: 0.9;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#newOrderModal .btn-close:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
opacity: 1;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
#newOrderModal .modal-body {
|
||||
background: rgba(248, 250, 252, 0.7);
|
||||
padding: 32px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
#newOrderModal .modal-footer {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.4);
|
||||
padding: 24px 32px;
|
||||
border-radius: 0 0 24px 24px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
#newOrderModal h6 {
|
||||
color: #1e293b;
|
||||
font-weight: 700;
|
||||
margin-bottom: 16px;
|
||||
font-size: 16px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* ===== ORDER LIST ITEMS ===== */
|
||||
#availableOrdersList,
|
||||
#deletedOrdersList {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
border-radius: 16px;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.4);
|
||||
margin-bottom: 8px;
|
||||
border-radius: 12px !important;
|
||||
padding: 16px;
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.list-group-item:hover {
|
||||
background: rgba(103, 126, 234, 0.1);
|
||||
border-color: rgba(103, 126, 234, 0.3);
|
||||
transform: translateX(4px);
|
||||
box-shadow: 0 5px 15px rgba(103, 126, 234, 0.15);
|
||||
}
|
||||
|
||||
.list-group-item strong {
|
||||
color: #1e293b;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.list-group-item .small {
|
||||
color: #64748b;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.order-checkbox {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 6px;
|
||||
border: 2px solid #cbd5e1;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.order-checkbox:checked {
|
||||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(103, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
/* ===== MODAL BUTTONS ===== */
|
||||
#newOrderModal .btn-secondary {
|
||||
background: rgba(100, 116, 139, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1.5px solid rgba(100, 116, 139, 0.3);
|
||||
color: #64748b;
|
||||
padding: 12px 28px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
#newOrderModal .btn-secondary:hover {
|
||||
background: rgba(100, 116, 139, 0.3);
|
||||
border-color: rgba(100, 116, 139, 0.5);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(100, 116, 139, 0.2);
|
||||
}
|
||||
|
||||
#newOrderModal .btn-success {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(34, 197, 94, 0.25) 0%,
|
||||
rgba(21, 128, 61, 0.25) 100%
|
||||
);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1.5px solid rgba(34, 197, 94, 0.4);
|
||||
color: #166534;
|
||||
padding: 12px 28px;
|
||||
border-radius: 12px;
|
||||
font-weight: 700;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow:
|
||||
0 8px 25px rgba(34, 197, 94, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
#newOrderModal .btn-success:hover {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(34, 197, 94, 0.35) 0%,
|
||||
rgba(21, 128, 61, 0.35) 100%
|
||||
);
|
||||
border-color: rgba(34, 197, 94, 0.6);
|
||||
color: #14532d;
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 12px 30px rgba(34, 197, 94, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* ===== SCROLLBAR STYLING ===== */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(135deg, #5a6fe8, #6a42a0);
|
||||
}
|
||||
|
||||
/* ===== ACTION BUTTONS ===== */
|
||||
.btn-secondary {
|
||||
background: linear-gradient(135deg, #94a3b8, #64748b);
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 12px 28px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 6px 20px rgba(100, 116, 139, 0.2);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: linear-gradient(135deg, #8492a6, #565e6e);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 25px rgba(100, 116, 139, 0.3);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE ADJUSTMENTS ===== */
|
||||
@media (max-width: 768px) {
|
||||
.shipment-info-item,
|
||||
.total-box {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.header-controls {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.glass-btn {
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<div class="title-row">
|
||||
<h5 class="mb-0"><i class="bi bi-truck me-2"></i>Shipment Details</h5>
|
||||
<!-- <small class="text-white-50" style="margin-left:8px; font-weight:600;">{{ $shipment->shipment_id ?? '' }}</small> -->
|
||||
</div>
|
||||
|
||||
<div class="header-controls">
|
||||
<!-- Add Order button (top-right) with glass effect -->
|
||||
@if($mode === 'edit' && $shipment->status === 'pending')
|
||||
<a href="#" data-bs-toggle="modal" data-bs-target="#newOrderModal" class="glass-btn">
|
||||
<i class="bi bi-plus-circle"></i> Add Order
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<!-- Cancel/close: goes back to shipments list -->
|
||||
<a href="{{ route('admin.shipments') }}" class="btn header-cancel-btn" title="Cancel / Back">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<!-- ================================ -->
|
||||
<!-- SHIPMENT MAIN DETAILS -->
|
||||
<!-- ================================ -->
|
||||
<div class="shipment-info-box">
|
||||
|
||||
<div class="shipment-info-item">
|
||||
<div class="shipment-info-label">Shipment ID</div>
|
||||
<div class="shipment-info-value">{{ $shipment->shipment_id }}</div>
|
||||
</div>
|
||||
|
||||
<div class="shipment-info-item">
|
||||
<div class="shipment-info-label">Status</div>
|
||||
<div class="shipment-info-value text-capitalize">
|
||||
{{ str_replace('_', ' ', $shipment->status) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="shipment-info-item">
|
||||
<div class="shipment-info-label">Shipment Date</div>
|
||||
<div class="shipment-info-value">
|
||||
{{ \Carbon\Carbon::parse($shipment->shipment_date)->format('d M Y') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="shipment-info-item">
|
||||
<div class="shipment-info-label">Total Orders</div>
|
||||
<div class="shipment-info-value">{{ $orders->count() }}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ================================ -->
|
||||
<!-- SHIPMENT TOTAL BLOCKS -->
|
||||
<!-- ================================ -->
|
||||
<h5 class="fw-bold mb-3 mt-4" style="color: #1e293b;">Shipment Summary</h5>
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
|
||||
<div class="total-box">
|
||||
<div class="total-title">TOTAL CTN</div>
|
||||
<div class="total-value">{{ $shipment->total_ctn }}</div>
|
||||
</div>
|
||||
|
||||
<div class="total-box">
|
||||
<div class="total-title">TOTAL QTY</div>
|
||||
<div class="total-value">{{ $shipment->total_qty }}</div>
|
||||
</div>
|
||||
|
||||
<div class="total-box">
|
||||
<div class="total-title">TOTAL CBM</div>
|
||||
<div class="total-value">{{ $shipment->total_cbm }}</div>
|
||||
</div>
|
||||
|
||||
<div class="total-box">
|
||||
<div class="total-title">TOTAL KG</div>
|
||||
<div class="total-value">{{ $shipment->total_kg }}</div>
|
||||
</div>
|
||||
|
||||
<div class="total-box">
|
||||
<div class="total-title">TOTAL TTL QTY</div>
|
||||
<div class="total-value">{{ $shipment->total_ttl_qty }}</div>
|
||||
</div>
|
||||
|
||||
<div class="total-box">
|
||||
<div class="total-title">TOTAL TTL CBM</div>
|
||||
<div class="total-value">{{ $shipment->total_ttl_cbm }}</div>
|
||||
</div>
|
||||
|
||||
<div class="total-box">
|
||||
<div class="total-title">TOTAL TTL KG</div>
|
||||
<div class="total-value">{{ $shipment->total_ttl_kg }}</div>
|
||||
</div>
|
||||
|
||||
<div class="total-box">
|
||||
<div class="total-title">TOTAL AMOUNT</div>
|
||||
<div class="total-value text-success">
|
||||
₹{{ number_format($shipment->total_amount, 2) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ================================ -->
|
||||
<!-- ORDERS TABLE LIST -->
|
||||
<!-- ================================ -->
|
||||
<h5 class="fw-bold mb-3 mt-5" style="color: #1e293b;">Orders in This Shipment</h5>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Order ID</th>
|
||||
<th>Origin</th>
|
||||
<th>Destination</th>
|
||||
<th>CTN</th>
|
||||
<th>QTY</th>
|
||||
<th>TTL/QTY</th>
|
||||
<th>CBM</th>
|
||||
<th>TTL CBM</th>
|
||||
<th>KG</th>
|
||||
<th>TTL KG</th>
|
||||
<th>Amount (₹)</th>
|
||||
<!-- Conditionally show Delete column header -->
|
||||
@if($mode === 'edit' && $shipment->status === 'pending')
|
||||
<th>Delete</th>
|
||||
@endif
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach($orders as $order)
|
||||
<tr>
|
||||
<td class="fw-bold text-primary">{{ $order->order_id }}</td>
|
||||
<td>{{ $order->origin }}</td>
|
||||
<td>{{ $order->destination }}</td>
|
||||
<td>{{ $order->ctn }}</td>
|
||||
<td>{{ $order->qty }}</td>
|
||||
<td>{{ $order->ttl_qty }}</td>
|
||||
<td>{{ $order->cbm }}</td>
|
||||
<td>{{ $order->ttl_cbm }}</td>
|
||||
<td>{{ $order->kg }}</td>
|
||||
<td>{{ $order->ttl_kg }}</td>
|
||||
<td class="fw-bold text-success">
|
||||
₹{{ number_format($order->ttl_amount, 2) }}
|
||||
</td>
|
||||
|
||||
<!-- Conditionally show Delete button -->
|
||||
@if($mode === 'edit' && $shipment->status === 'pending')
|
||||
<td>
|
||||
<form method="POST" action="{{ route('admin.shipments.removeOrder') }}" onsubmit="return confirmDelete()">
|
||||
@csrf
|
||||
<input type="hidden" name="shipment_id" value="{{ $shipment->id }}">
|
||||
<input type="hidden" name="order_id" value="{{ $order->id }}">
|
||||
<button type="submit" class="btn btn-danger btn-sm" style="border-radius: 10px; padding: 8px 16px;">
|
||||
<i class="bi bi-trash"></i> Delete
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
@endif
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<!-- <a href="{{ route('admin.shipments') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Back to Shipments
|
||||
</a>
|
||||
-->
|
||||
@if($mode === 'edit' && $shipment->status === 'pending')
|
||||
<div>
|
||||
<!-- <a href="{{ route('admin.shipments.view', ['id' => $shipment->id, 'mode' => 'edit']) }}"
|
||||
class="btn btn-warning me-2">
|
||||
<i class="bi bi-pencil"></i> Edit Shipment
|
||||
</a> -->
|
||||
|
||||
<!-- <a href="{{ route('admin.shipments.view', ['id' => $shipment->id, 'mode' => 'view']) }}"
|
||||
class="btn btn-info">
|
||||
<i class="bi bi-eye"></i> View Mode
|
||||
</a> -->
|
||||
</div>
|
||||
@elseif($shipment->status === 'pending')
|
||||
<div>
|
||||
<!-- <a href="{{ route('admin.shipments.view', ['id' => $shipment->id, 'mode' => 'edit']) }}"
|
||||
class="btn btn-primary">
|
||||
<i class="bi bi-pencil"></i> Edit Shipment
|
||||
</a> -->
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- New Order Modal -->
|
||||
<div class="modal fade" id="newOrderModal" tabindex="-1" aria-labelledby="newOrderModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="newOrderModalLabel">Add Orders to Shipment</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<h6>Deleted Orders (recently removed)</h6>
|
||||
<div id="deletedOrdersList" class="list-group"></div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6>Available Orders</h6>
|
||||
<div id="availableOrdersList" class="list-group"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<small class="text-muted">Select orders to add back to this shipment, then click "Add Selected".</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button id="addSelectedOrdersBtn" type="button" class="btn btn-success">Add Selected</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function confirmDelete() {
|
||||
return confirm('Are you sure you want to remove this order from the shipment?');
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const csrfToken = '{{ csrf_token() }}';
|
||||
const shipmentId = {{ $shipment->id }};
|
||||
let availableOrders = @json($availableOrders ?? []);
|
||||
let deletedOrders = [];
|
||||
|
||||
function renderOrdersList(containerId, orders) {
|
||||
const container = document.getElementById(containerId);
|
||||
container.innerHTML = '';
|
||||
if (!orders.length) {
|
||||
container.innerHTML = '<div class="text-muted py-3">No orders</div>';
|
||||
return;
|
||||
}
|
||||
orders.forEach(order => {
|
||||
const item = document.createElement('label');
|
||||
item.className = 'list-group-item d-flex justify-content-between align-items-center';
|
||||
item.innerHTML = `
|
||||
<div>
|
||||
<input type="checkbox" class="me-2 order-checkbox" data-order='${JSON.stringify(order)}'>
|
||||
<strong>${order.order_id}</strong>
|
||||
<div class="small text-muted"> ${order.origin} → ${order.destination} • ${order.qty} qty • ₹${parseFloat(order.ttl_amount).toLocaleString('en-IN', {minimumFractionDigits:2})}</div>
|
||||
</div>
|
||||
<div class="small text-muted">ID: ${order.id}</div>
|
||||
`;
|
||||
container.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
renderOrdersList('availableOrdersList', availableOrders);
|
||||
renderOrdersList('deletedOrdersList', deletedOrders);
|
||||
|
||||
// Intercept remove-order forms (update selector if form action different)
|
||||
document.querySelectorAll('form[action="{{ route('admin.shipments.removeOrder') }}"]').forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
if (!confirm('Are you sure you want to remove this order from the shipment?')) return;
|
||||
|
||||
const formData = new FormData(this);
|
||||
|
||||
fetch(this.action, {
|
||||
method: 'POST',
|
||||
headers: {'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json'},
|
||||
body: formData
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (!data.success) { alert(data.message || 'Failed'); return; }
|
||||
|
||||
// Remove row from DOM
|
||||
const row = this.closest('tr');
|
||||
// Build deleted order object from row cells if present
|
||||
const orderObj = {
|
||||
id: formData.get('order_id'),
|
||||
order_id: row ? row.querySelector('td:first-child')?.innerText.trim() : ('Order-'+formData.get('order_id')),
|
||||
origin: row ? row.cells[1]?.innerText.trim() : '',
|
||||
destination: row ? row.cells[2]?.innerText.trim() : '',
|
||||
qty: row ? row.cells[4]?.innerText.trim() : '',
|
||||
ttl_amount: row ? row.cells[10]?.innerText.replace('₹','').replace(',','').trim() : 0
|
||||
};
|
||||
|
||||
if (row) row.remove();
|
||||
|
||||
deletedOrders.unshift(orderObj);
|
||||
renderOrdersList('deletedOrdersList', deletedOrders);
|
||||
|
||||
// Optional: update totals by refetching shipment summary endpoint or reload
|
||||
if (data.shipment) updateTotalsInDOM(data.shipment);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
alert('Remove failed.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Open modal from add-order button
|
||||
document.querySelectorAll('.glass-btn').forEach(btn => {
|
||||
btn.addEventListener('click', function(e) {
|
||||
renderOrdersList('availableOrdersList', availableOrders);
|
||||
renderOrdersList('deletedOrdersList', deletedOrders);
|
||||
const modal = new bootstrap.Modal(document.getElementById('newOrderModal'));
|
||||
modal.show();
|
||||
});
|
||||
});
|
||||
|
||||
// Add selected orders
|
||||
document.getElementById('addSelectedOrdersBtn').addEventListener('click', function() {
|
||||
const checked = Array.from(document.querySelectorAll('.order-checkbox:checked'));
|
||||
if (!checked.length) { alert('Please select orders'); return; }
|
||||
|
||||
const orderIds = checked.map(cb => JSON.parse(cb.getAttribute('data-order')).id);
|
||||
|
||||
fetch(`/admin/shipments/${shipmentId}/add-orders`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken, 'Accept': 'application/json'},
|
||||
body: JSON.stringify({ order_ids: orderIds })
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (!data.success) { alert(data.message || 'Failed'); return; }
|
||||
|
||||
// Update orders table — simplest approach: reload page to reflect DB
|
||||
// But we can also append rows from data.orders (if provided)
|
||||
location.reload();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
alert('Add failed.');
|
||||
});
|
||||
});
|
||||
|
||||
function updateTotalsInDOM(shipment) {
|
||||
try {
|
||||
if (document.getElementById('total_qty')) {
|
||||
document.getElementById('total_qty').innerText = shipment.total_qty;
|
||||
}
|
||||
if (document.getElementById('total_amount')) {
|
||||
document.getElementById('total_amount').innerText = '₹' + parseFloat(shipment.total_amount).toLocaleString('en-IN', {minimumFractionDigits:2});
|
||||
}
|
||||
// add other totals similarly...
|
||||
} catch(e) { console.warn(e); }
|
||||
}
|
||||
});
|
||||
document.addEventListener('hidden.bs.modal', function (event) {
|
||||
document.body.classList.remove('modal-open');
|
||||
let backdrop = document.querySelector('.modal-backdrop');
|
||||
if (backdrop) backdrop.remove();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@endsection
|
||||
@@ -117,23 +117,18 @@ Route::prefix('admin')
|
||||
// ---------------------------
|
||||
// ORDERS (FIXED ROUTES)
|
||||
// ---------------------------
|
||||
|
||||
// Add item to order
|
||||
Route::post('/orders/{order}/item', [AdminOrderController::class, 'addItem'])
|
||||
->name('admin.orders.addItem');
|
||||
|
||||
// Delete item from order
|
||||
Route::delete('/orders/item/{id}', [AdminOrderController::class, 'deleteItem'])
|
||||
->name('admin.orders.deleteItem');
|
||||
|
||||
// Restore deleted item
|
||||
Route::post('/orders/item/{id}/restore', [AdminOrderController::class, 'restoreItem'])
|
||||
->name('admin.orders.restoreItem');
|
||||
|
||||
// Update main order fields
|
||||
Route::post('/orders/{id}/update', [AdminOrderController::class, 'update'])
|
||||
->name('admin.orders.update');
|
||||
|
||||
Route::put('/admin/orders/item/update/{id}', [AdminOrderController::class, 'updateItem'])
|
||||
->name('admin.orders.updateItem');
|
||||
// Delete full order
|
||||
Route::delete('/orders/{id}/delete', [AdminOrderController::class, 'destroy'])
|
||||
->name('admin.orders.destroy');
|
||||
@@ -141,32 +136,44 @@ Route::prefix('admin')
|
||||
// ---------------------------
|
||||
// SHIPMENTS (FIXED ROUTES)
|
||||
// ---------------------------
|
||||
|
||||
// View shipment MUST be before /shipments/{id}
|
||||
Route::get('/shipments/view/{id}', [ShipmentController::class, 'show'])
|
||||
->name('admin.shipments.view');
|
||||
|
||||
// List shipments
|
||||
Route::get('/shipments', [ShipmentController::class, 'index'])
|
||||
->name('admin.shipments');
|
||||
|
||||
// Create shipment
|
||||
Route::post('/shipments', [ShipmentController::class, 'store'])
|
||||
->name('admin.shipments.store');
|
||||
|
||||
// Update status
|
||||
Route::post('/shipments/update-status', [ShipmentController::class, 'updateStatus'])
|
||||
->name('admin.shipments.updateStatus');
|
||||
|
||||
// Get shipment orders for modal (AJAX)
|
||||
// Shipment orders (AJAX)
|
||||
Route::get('/shipments/{id}/orders', [ShipmentController::class, 'getShipmentOrders'])
|
||||
->name('admin.shipments.orders');
|
||||
|
||||
// Get shipment details for edit (AJAX)
|
||||
Route::get('/shipments/{id}', [ShipmentController::class, 'show'])
|
||||
->name('admin.shipments.show');
|
||||
|
||||
// Shipment Update
|
||||
// Shipment update
|
||||
Route::put('/shipments/{id}', [ShipmentController::class, 'update'])
|
||||
->name('admin.shipments.update');
|
||||
|
||||
// Shipment Delete
|
||||
// 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
|
||||
// ---------------------------
|
||||
@@ -192,13 +199,10 @@ Route::prefix('admin')
|
||||
->name('admin.invoice.installment.delete');
|
||||
|
||||
|
||||
|
||||
|
||||
//Add New Invoice
|
||||
Route::get('/admin/invoices/create', [InvoiceController::class, 'create'])->name('admin.invoices.create');
|
||||
|
||||
|
||||
|
||||
// ---------------------------
|
||||
// CUSTOMERS
|
||||
// ---------------------------
|
||||
@@ -281,4 +285,3 @@ Route::prefix('admin')
|
||||
//Edit Button Route
|
||||
//---------------------------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user