This commit is contained in:
Utkarsh Khedkar
2025-12-03 16:17:36 +05:30
20 changed files with 2459 additions and 672 deletions

View File

@@ -91,8 +91,8 @@ class AdminOrderController extends Controller
'status' => 'pending',
]);
// If you want to auto-create an invoice at order creation, uncomment:
// $this->createInvoice($order);
//If you want to auto-create an invoice at order creation, uncomment:
$this->createInvoice($order);
return redirect()->route('admin.orders.show', $order->id)
->with('success', 'Order created successfully.');
@@ -500,84 +500,193 @@ public function addTempItem(Request $request)
session()->push('temp_order_items', $item);
return redirect()->to(route('admin.orders.index') . '#createOrderForm')
->with('success', 'Item added.');
->with('success', 'Item added.');
}
public function finishOrder(Request $request)
// -------------------------------------------------------------------------
// STEP 3 : FINISH ORDER
// -------------------------------------------------------------------------
public function finishOrder(Request $request)
{
$request->validate([
'mark_no' => 'required',
'origin' => 'required',
'destination' => 'required',
]);
$items = session('temp_order_items', []);
if (empty($items)) {
return redirect()->to(route('admin.orders.index') . '#createOrderForm')
->with('error', 'Add at least one item before finishing.');
}
// =======================
// GENERATE ORDER ID
// =======================
$year = date('y');
$prefix = "KNT-$year-";
$lastOrder = Order::latest('id')->first();
$nextNumber = $lastOrder ? intval(substr($lastOrder->order_id, -8)) + 1 : 1;
$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'));
$total_amount = array_sum(array_column($items, 'ttl_amount'));
$total_cbm = array_sum(array_column($items, 'cbm'));
$total_ttl_cbm = array_sum(array_column($items, 'ttl_cbm'));
$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,
'origin' => $request->origin,
'destination' => $request->destination,
'ctn' => $total_ctn,
'qty' => $total_qty,
'ttl_qty' => $total_ttl_qty,
'ttl_amount' => $total_amount,
'cbm' => $total_cbm,
'ttl_cbm' => $total_ttl_cbm,
'kg' => $total_kg,
'ttl_kg' => $total_ttl_kg,
'status' => 'pending',
]);
// SAVE ORDER ITEMS
foreach ($items as $item) {
OrderItem::create([
'order_id' => $order->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'],
]);
}
// =======================
// 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 + Invoice created successfully.');
}
// ---------------------------
// ORDER CRUD: update / destroy
// ---------------------------
public function updateItem(Request $request, $id)
{
$request->validate([
'mark_no' => 'required',
'origin' => 'required',
'destination' => 'required',
$item = OrderItem::findOrFail($id);
$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,
]);
$items = session('temp_order_items', []);
if (empty($items)) {
return redirect()->to(route('admin.orders.index') . '#createOrderForm')
->with('error', 'Add at least one item before finishing.');
}
// Generate Order ID
$year = date('y');
$prefix = "KNT-$year-";
$lastOrder = Order::latest('id')->first();
$nextNumber = $lastOrder ? intval(substr($lastOrder->order_id, -8)) + 1 : 1;
$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'));
$total_amount = array_sum(array_column($items, 'ttl_amount'));
$total_cbm = array_sum(array_column($items, 'cbm'));
$total_ttl_cbm = array_sum(array_column($items, 'ttl_cbm'));
$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,
'origin' => $request->origin,
'destination' => $request->destination,
'ctn' => $total_ctn,
'qty' => $total_qty,
'ttl_qty' => $total_ttl_qty,
'ttl_amount' => $total_amount,
'cbm' => $total_cbm,
'ttl_cbm' => $total_ttl_cbm,
'kg' => $total_kg,
'ttl_kg' => $total_ttl_kg,
'status' => 'pending',
]);
// SAVE ALL SUB-ITEMS
foreach ($items as $item) {
OrderItem::create([
'order_id' => $order->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'],
]);
}
// CLEAR TEMP DATA
session()->forget(['temp_order_items', 'mark_no', 'origin', 'destination']);
return redirect()->route('admin.orders.index')
->with('success', 'Order saved successfully.');
return back()->with('success', 'Item updated successfully!');
}
}

View File

@@ -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,24 +117,112 @@ 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);
return response()->json([
// 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
'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' => $addedOrders
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Failed to add orders: ' . $e->getMessage()
], 500);
}
}
/**
* Update Shipment status from action button
*/
@@ -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
]);
}
}

View File

@@ -84,11 +84,25 @@ class UserProfileController extends Controller
$user->profile_image = $folder . $filename;
$user->save();
return response()->json([
'success' => true,
'message' => 'Profile image updated successfully',
'profile_image' => url($user->profile_image),
]);
return response()->json([
'success' => true,
'message' => 'Profile image updated successfully',
'data' => [
'customer_id' => $user->customer_id,
'customer_name' => $user->customer_name,
'company_name' => $user->company_name,
'designation' => $user->designation,
'email' => $user->email,
'mobile' => $user->mobile_no,
'address' => $user->address,
'pincode' => $user->pincode,
'status' => $user->status,
'customer_type' => $user->customer_type,
'profile_image' => url($user->profile_image),
'date' => $user->date,
]
]);
}

View File

@@ -11,12 +11,27 @@ class ShipmentItem extends Model
protected $fillable = [
'shipment_id',
'order_id',
'order_ctn',
'order_qty',
'order_ttl_qty',
'order_ttl_amount',
'order_ttl_kg',
'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;
}
}

689
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

View File

@@ -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,23 +188,157 @@
<td>{{ $item->ttl_kg }}</td>
<td>{{ $item->shop_no }}</td>
<td>
<form action="{{ route('admin.orders.deleteItem', $item->id) }}"
method="POST"
onsubmit="return confirm('Delete this item?')">
@csrf
@method('DELETE')
<button class="btn btn-sm btn-delete-item">
<i class="fas fa-trash"></i>
</button>
</form>
</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?')">
@csrf
@method('DELETE')
<button class="btn btn-sm btn-delete-item">
<i class="fas fa-trash"></i>
</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>
{{-- USE THIS ITEM --}}
<button type="button"
class="btn btn-sm btn-use-item"
onclick="fillFormFromDeleted({{ json_encode($deleted) }})">
Use
</button>
<div class="d-flex gap-2">
{{-- Auto-fill button --}}
<button type="button" class="btn btn-sm btn-use-item"
onclick="fillFormFromDeleted({{ json_encode($deleted) }})">
Use This
{{-- RESTORE ITEM --}}
<form action="{{ route('admin.orders.restoreItem', $deleted->id) }}" method="POST">
@csrf
<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>
{{-- Restore --}}
<form action="{{ route('admin.orders.restoreItem', $deleted->id) }}" method="POST">
@csrf
<button class="btn btn-sm btn-restore">Restore</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="14" class="text-muted">No deleted items.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</li>
@empty
<li class="list-group-item text-muted">No deleted items.</li>
@endforelse
</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 {

View File

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

View 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

View File

@@ -66,14 +66,14 @@ Route::prefix('admin')
// ---------------------------
// USER REQUESTS
// ---------------------------
Route::get('/requests', [UserRequestController::class, 'index'])
->name('admin.requests');
Route::get('/requests', [UserRequestController::class, 'index'])
->name('admin.requests');
Route::get('/requests/approve/{id}', [UserRequestController::class, 'approve'])
->name('admin.requests.approve');
Route::get('/requests/approve/{id}', [UserRequestController::class, 'approve'])
->name('admin.requests.approve');
Route::get('/requests/reject/{id}', [UserRequestController::class, 'reject'])
->name('admin.requests.reject');
Route::get('/requests/reject/{id}', [UserRequestController::class, 'reject'])
->name('admin.requests.reject');
// PROFILE UPDATE REQUESTS
Route::get('/profile-update-requests',
@@ -93,61 +93,56 @@ Route::prefix('admin')
// ---------------------------
// MARK LIST
// ---------------------------
Route::get('/mark-list', [AdminMarkListController::class, 'index'])
->name('admin.marklist.index');
Route::get('/mark-list', [AdminMarkListController::class, 'index'])
->name('admin.marklist.index');
Route::get('/mark-list/status/{id}', [AdminMarkListController::class, 'toggleStatus'])
->name('admin.marklist.toggle');
Route::get('/mark-list/status/{id}', [AdminMarkListController::class, 'toggleStatus'])
->name('admin.marklist.toggle');
// ---------------------------
// ORDERS
// ---------------------------
Route::get('/orders', [AdminOrderController::class, 'orderShow'])
->name('admin.orders');
Route::get('/orders', [AdminOrderController::class, 'orderShow'])
->name('admin.orders');
Route::get('/orders/list', [AdminOrderController::class, 'index'])
->name('admin.orders.index');
Route::get('/orders/list', [AdminOrderController::class, 'index'])
->name('admin.orders.index');
Route::get('/orders/{id}', [AdminOrderController::class, 'show'])
->name('admin.orders.show');
Route::get('/orders/{id}', [AdminOrderController::class, 'show'])
->name('admin.orders.show');
Route::post('/orders/temp/add', [AdminOrderController::class, 'addTempItem'])
->name('admin.orders.temp.add');
Route::post('/orders/temp/add', [AdminOrderController::class, 'addTempItem'])
->name('admin.orders.temp.add');
Route::post('/orders/temp/delete', [AdminOrderController::class, 'deleteTempItem'])
->name('admin.orders.temp.delete');
Route::post('/orders/temp/delete', [AdminOrderController::class, 'deleteTempItem'])
->name('admin.orders.temp.delete');
Route::post('/orders/temp/reset', [AdminOrderController::class, 'resetTemp'])
->name('admin.orders.temp.reset');
Route::post('/orders/temp/reset', [AdminOrderController::class, 'resetTemp'])
->name('admin.orders.temp.reset');
Route::post('/orders/finish', [AdminOrderController::class, 'finishOrder'])
->name('admin.orders.finish');
Route::post('/orders/finish', [AdminOrderController::class, 'finishOrder'])
->name('admin.orders.finish');
Route::get('/orders/view/{id}', [AdminOrderController::class, 'popup'])
->name('admin.orders.popup');
Route::get('/orders/view/{id}', [AdminOrderController::class, 'popup'])
->name('admin.orders.popup');
// ---------------------------
// ORDERS (FIXED ROUTES)
// ---------------------------
// 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');
@@ -155,48 +150,64 @@ Route::prefix('admin')
// ---------------------------
// SHIPMENTS (FIXED ROUTES)
// ---------------------------
Route::get('/shipments', [ShipmentController::class, 'index'])
->name('admin.shipments');
Route::post('/shipments', [ShipmentController::class, 'store'])
->name('admin.shipments.store');
// View shipment MUST be before /shipments/{id}
Route::get('/shipments/view/{id}', [ShipmentController::class, 'show'])
->name('admin.shipments.view');
Route::post('/shipments/update-status', [ShipmentController::class, 'updateStatus'])
->name('admin.shipments.updateStatus');
// List shipments
Route::get('/shipments', [ShipmentController::class, 'index'])
->name('admin.shipments');
// Get shipment orders for modal (AJAX)
Route::get('/shipments/{id}/orders', [ShipmentController::class, 'getShipmentOrders'])
->name('admin.shipments.orders');
// Create shipment
Route::post('/shipments', [ShipmentController::class, 'store'])
->name('admin.shipments.store');
// Get shipment details for edit (AJAX)
Route::get('/shipments/{id}', [ShipmentController::class, 'show'])
->name('admin.shipments.show');
// Update status
Route::post('/shipments/update-status', [ShipmentController::class, 'updateStatus'])
->name('admin.shipments.updateStatus');
// Shipment Update
Route::put('/shipments/{id}', [ShipmentController::class, 'update'])
->name('admin.shipments.update');
// Shipment orders (AJAX)
Route::get('/shipments/{id}/orders', [ShipmentController::class, 'getShipmentOrders'])
->name('admin.shipments.orders');
// Shipment Delete
Route::delete('/shipments/{id}', [ShipmentController::class, 'destroy'])
->name('admin.shipments.destroy');
// Shipment update
Route::put('/shipments/{id}', [ShipmentController::class, 'update'])
->name('admin.shipments.update');
// 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
// ---------------------------
Route::get('/invoices', [AdminInvoiceController::class, 'index'])
->name('admin.invoices.index');
Route::get('/invoices', [AdminInvoiceController::class, 'index'])
->name('admin.invoices.index');
Route::get('/invoices/{id}/popup', [AdminInvoiceController::class, 'popup'])
->name('admin.invoices.popup');
Route::get('/invoices/{id}/popup', [AdminInvoiceController::class, 'popup'])
->name('admin.invoices.popup');
Route::get('/invoices/{id}/edit', [AdminInvoiceController::class, 'edit'])
->name('admin.invoices.edit');
Route::get('/invoices/{id}/edit', [AdminInvoiceController::class, 'edit'])
->name('admin.invoices.edit');
Route::post('/invoices/{id}/update', [AdminInvoiceController::class, 'update'])
->name('admin.invoices.update');
Route::post('/invoices/{id}/update', [AdminInvoiceController::class, 'update'])
->name('admin.invoices.update');
Route::post('/invoices/{invoice}/installments', [AdminInvoiceController::class, 'storeInstallment'])
Route::post('/invoices/{invoice}/installments', [AdminInvoiceController::class, 'storeInstallment'])
->name('admin.invoice.installment.store');
Route::post('/invoices/{id}/installment', [AdminInvoiceController::class, 'storeInstallment'])
->name('admin.invoice.installment.store');
@@ -205,81 +216,78 @@ Route::prefix('admin')
->name('admin.invoice.installment.delete');
//Add New Invoice
Route::get('/admin/invoices/create', [InvoiceController::class, 'create'])->name('admin.invoices.create');
//Add New Invoice
Route::get('/admin/invoices/create', [InvoiceController::class, 'create'])->name('admin.invoices.create');
// ---------------------------
// CUSTOMERS
// ---------------------------
Route::get('/customers', [AdminCustomerController::class, 'index'])
->name('admin.customers.index');
Route::get('/customers', [AdminCustomerController::class, 'index'])
->name('admin.customers.index');
Route::get('/customers/add', [AdminCustomerController::class, 'create'])
->name('admin.customers.add');
Route::get('/customers/add', [AdminCustomerController::class, 'create'])
->name('admin.customers.add');
Route::post('/customers/store', [AdminCustomerController::class, 'store'])
->name('admin.customers.store');
Route::post('/customers/store', [AdminCustomerController::class, 'store'])
->name('admin.customers.store');
Route::get('/customers/{id}/view', [AdminCustomerController::class, 'view'])
->name('admin.customers.view');
Route::get('/customers/{id}/view', [AdminCustomerController::class, 'view'])
->name('admin.customers.view');
Route::post('/customers/{id}/status', [AdminCustomerController::class, 'toggleStatus'])
->name('admin.customers.status');
});
Route::post('/customers/{id}/status', [AdminCustomerController::class, 'toggleStatus'])
->name('admin.customers.status');
});
// ==========================================
// ADMIN ACCOUNT (AJAX) ROUTES
// ==========================================
Route::prefix('admin/account')
->middleware('auth:admin')
->name('admin.account.')
->group(function () {
Route::prefix('admin/account')
->middleware('auth:admin')
->name('admin.account.')
->group(function () {
Route::get('/dashboard', [AdminAccountController::class, 'getDashboardData'])
->name('dashboard');
Route::get('/dashboard', [AdminAccountController::class, 'getDashboardData'])
->name('dashboard');
Route::get('/available-orders', [AdminAccountController::class, 'getAvailableOrders'])
->name('orders.available');
Route::get('/available-orders', [AdminAccountController::class, 'getAvailableOrders'])
->name('orders.available');
Route::post('/create-order', [AdminAccountController::class, 'accountCreateOrder'])
->name('create');
Route::post('/create-order', [AdminAccountController::class, 'accountCreateOrder'])
->name('create');
Route::post('/toggle-payment', [AdminAccountController::class, 'togglePayment'])
->name('toggle');
Route::post('/toggle-payment', [AdminAccountController::class, 'togglePayment'])
->name('toggle');
Route::post('/installment/create', [AdminAccountController::class, 'addInstallment'])
->name('installment.create');
Route::post('/installment/create', [AdminAccountController::class, 'addInstallment'])
->name('installment.create');
Route::post('/installment/update-status', [AdminAccountController::class, 'updateInstallmentStatus'])
->name('installment.update');
Route::post('/installment/update-status', [AdminAccountController::class, 'updateInstallmentStatus'])
->name('installment.update');
Route::get('/entry/{entry_no}', [AdminAccountController::class, 'getEntryDetails'])
->name('entry.details');
Route::get('/entry/{entry_no}', [AdminAccountController::class, 'getEntryDetails'])
->name('entry.details');
// ⬇⬇ NEW ROUTES FOR EDIT + DELETE ⬇⬇
Route::post('/update-entry', [AdminAccountController::class, 'updateEntry'])
->name('update.entry');
// ⬇⬇ NEW ROUTES FOR EDIT + DELETE ⬇⬇
Route::post('/update-entry', [AdminAccountController::class, 'updateEntry'])
->name('update.entry');
Route::post('/delete-entry', [AdminAccountController::class, 'deleteEntry'])
->name('delete.entry');
Route::post('/delete-entry', [AdminAccountController::class, 'deleteEntry'])
->name('delete.entry');
// ===== Associated Orders Routes (EDIT MODAL साठी) =====
Route::post('/add-orders-to-entry', [AdminAccountController::class, 'addOrdersToEntry'])
->name('add.orders.to.entry');
// ===== Associated Orders Routes (EDIT MODAL साठी) =====
Route::post('/add-orders-to-entry', [AdminAccountController::class, 'addOrdersToEntry'])
->name('add.orders.to.entry');
Route::get('/entry-orders/{entry_no}', [AdminAccountController::class, 'getEntryOrders'])
->name('entry.orders');
Route::get('/entry-orders/{entry_no}', [AdminAccountController::class, 'getEntryOrders'])
->name('entry.orders');
Route::post('/remove-order-from-entry', [AdminAccountController::class, 'removeOrderFromEntry'])
->name('remove.order.from.entry');
Route::post('/remove-order-from-entry', [AdminAccountController::class, 'removeOrderFromEntry'])
->name('remove.order.from.entry');
});
});
@@ -297,4 +305,3 @@ Route::prefix('admin')
//Edit Button Route
//---------------------------