conflict resolve
This commit is contained in:
98
app/Exports/OrdersExport.php
Normal file
98
app/Exports/OrdersExport.php
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use App\Models\Order;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
|
||||||
|
class OrdersExport implements FromCollection, WithHeadings
|
||||||
|
{
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
public function __construct(Request $request)
|
||||||
|
{
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildQuery()
|
||||||
|
{
|
||||||
|
$query = Order::with(['markList', 'invoice', 'shipments']);
|
||||||
|
|
||||||
|
if ($this->request->filled('search')) {
|
||||||
|
$search = $this->request->search;
|
||||||
|
$query->where(function($q) use ($search) {
|
||||||
|
$q->where('order_id', 'like', "%{$search}%")
|
||||||
|
->orWhereHas('markList', function($q2) use ($search) {
|
||||||
|
$q2->where('company_name', 'like', "%{$search}%")
|
||||||
|
->orWhere('customer_id', 'like', "%{$search}%");
|
||||||
|
})
|
||||||
|
->orWhereHas('invoice', function($q3) use ($search) {
|
||||||
|
$q3->where('invoice_number', 'like', "%{$search}%");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->request->filled('status')) {
|
||||||
|
$query->whereHas('invoice', function($q) {
|
||||||
|
$q->where('status', $this->request->status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->request->filled('shipment')) {
|
||||||
|
$query->whereHas('shipments', function($q) {
|
||||||
|
$q->where('status', $this->request->shipment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->latest('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function collection()
|
||||||
|
{
|
||||||
|
$orders = $this->buildQuery()->get();
|
||||||
|
|
||||||
|
// Map to simple array rows suitable for Excel
|
||||||
|
return $orders->map(function($order) {
|
||||||
|
$mark = $order->markList;
|
||||||
|
$invoice = $order->invoice;
|
||||||
|
$shipment = $order->shipments->first() ?? null;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'Order ID' => $order->order_id,
|
||||||
|
'Shipment ID' => $shipment->shipment_id ?? '-',
|
||||||
|
'Customer ID' => $mark->customer_id ?? '-',
|
||||||
|
'Company' => $mark->company_name ?? '-',
|
||||||
|
'Origin' => $mark->origin ?? $order->origin ?? '-',
|
||||||
|
'Destination' => $mark->destination ?? $order->destination ?? '-',
|
||||||
|
'Order Date' => $order->created_at ? $order->created_at->format('d-m-Y') : '-',
|
||||||
|
'Invoice No' => $invoice->invoice_number ?? '-',
|
||||||
|
'Invoice Date' => $invoice?->invoice_date ? \Carbon\Carbon::parse($invoice->invoice_date)->format('d-m-Y') : '-',
|
||||||
|
'Amount' => $invoice?->final_amount ? number_format($invoice->final_amount, 2) : '-',
|
||||||
|
'Amount + GST' => $invoice?->final_amount_with_gst ? number_format($invoice->final_amount_with_gst, 2) : '-',
|
||||||
|
'Invoice Status' => $invoice->status ? ucfirst($invoice->status) : 'Pending',
|
||||||
|
'Shipment Status' => $shipment?->status ? ucfirst(str_replace('_', ' ', $shipment->status)) : 'Pending',
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Order ID',
|
||||||
|
'Shipment ID',
|
||||||
|
'Customer ID',
|
||||||
|
'Company',
|
||||||
|
'Origin',
|
||||||
|
'Destination',
|
||||||
|
'Order Date',
|
||||||
|
'Invoice No',
|
||||||
|
'Invoice Date',
|
||||||
|
'Amount',
|
||||||
|
'Amount + GST',
|
||||||
|
'Invoice Status',
|
||||||
|
'Shipment Status',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,10 @@ use App\Models\MarkList;
|
|||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\InvoiceItem;
|
use App\Models\InvoiceItem;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use PDF; // barryvdh/laravel-dompdf facade
|
||||||
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
|
use App\Exports\OrdersExport;
|
||||||
|
|
||||||
|
|
||||||
class AdminOrderController extends Controller
|
class AdminOrderController extends Controller
|
||||||
{
|
{
|
||||||
@@ -87,8 +91,8 @@ class AdminOrderController extends Controller
|
|||||||
'status' => 'pending',
|
'status' => 'pending',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// If you want to auto-create an invoice at order creation, uncomment:
|
//If you want to auto-create an invoice at order creation, uncomment:
|
||||||
// $this->createInvoice($order);
|
$this->createInvoice($order);
|
||||||
|
|
||||||
return redirect()->route('admin.orders.show', $order->id)
|
return redirect()->route('admin.orders.show', $order->id)
|
||||||
->with('success', 'Order created successfully.');
|
->with('success', 'Order created successfully.');
|
||||||
@@ -164,30 +168,46 @@ class AdminOrderController extends Controller
|
|||||||
return redirect()->back()->with('success', 'Item deleted and totals updated.');
|
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
|
// ORDER CRUD: update / destroy
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
public function updateItem(Request $request, $id)
|
public function update(Request $request, $id)
|
||||||
{
|
{
|
||||||
$item = OrderItem::findOrFail($id);
|
$order = Order::findOrFail($id);
|
||||||
|
|
||||||
$item->update([
|
$data = $request->validate([
|
||||||
'description' => $request->description,
|
'mark_no' => 'required|string',
|
||||||
'ctn' => $request->ctn,
|
'origin' => 'nullable|string',
|
||||||
'qty' => $request->qty,
|
'destination' => 'nullable|string',
|
||||||
'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,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return back()->with('success', 'Item updated successfully!');
|
$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.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -325,10 +345,6 @@ class AdminOrderController extends Controller
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Popup function
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
public function popup($id)
|
public function popup($id)
|
||||||
{
|
{
|
||||||
// Load order with items + markList
|
// Load order with items + markList
|
||||||
@@ -342,27 +358,8 @@ class AdminOrderController extends Controller
|
|||||||
|
|
||||||
return view('admin.popup', compact('order', 'user'));
|
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()
|
public function resetTemp()
|
||||||
@@ -373,11 +370,6 @@ class AdminOrderController extends Controller
|
|||||||
->with('success', 'Order reset successfully.');
|
->with('success', 'Order reset successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// ORDER SHOW PAGE
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
public function orderShow()
|
public function orderShow()
|
||||||
{
|
{
|
||||||
$orders = Order::with([
|
$orders = Order::with([
|
||||||
@@ -391,55 +383,79 @@ class AdminOrderController extends Controller
|
|||||||
return view('admin.orders', compact('orders'));
|
return view('admin.orders', compact('orders'));
|
||||||
}
|
}
|
||||||
|
|
||||||
//====================================================
|
// inside AdminOrderController
|
||||||
// Download Pdf
|
|
||||||
//=================================================
|
private function buildOrdersQueryFromRequest(Request $request)
|
||||||
public function downloadPdf(Request $request)
|
{
|
||||||
{
|
|
||||||
$query = Order::with(['markList', 'invoice', 'shipments']);
|
$query = Order::with(['markList', 'invoice', 'shipments']);
|
||||||
|
|
||||||
// Apply filters
|
// Search across order_id, markList.company_name, markList.customer_id, invoice.invoice_number
|
||||||
if ($request->has('search') && $request->search) {
|
if ($request->filled('search')) {
|
||||||
$search = $request->search;
|
$search = $request->search;
|
||||||
$query->where(function($q) use ($search) {
|
$query->where(function($q) use ($search) {
|
||||||
$q->where('order_id', 'like', "%{$search}%")
|
$q->where('order_id', 'like', "%{$search}%")
|
||||||
->orWhereHas('markList', function($q) use ($search) {
|
->orWhereHas('markList', function($q2) use ($search) {
|
||||||
$q->where('company_name', 'like', "%{$search}%");
|
$q2->where('company_name', 'like', "%{$search}%")
|
||||||
|
->orWhere('customer_id', 'like', "%{$search}%");
|
||||||
})
|
})
|
||||||
->orWhereHas('invoice', function($q) use ($search) {
|
->orWhereHas('invoice', function($q3) use ($search) {
|
||||||
$q->where('invoice_number', 'like', "%{$search}%");
|
$q3->where('invoice_number', 'like', "%{$search}%");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->has('status') && $request->status) {
|
// Invoice status filter
|
||||||
|
if ($request->filled('status')) {
|
||||||
$query->whereHas('invoice', function($q) use ($request) {
|
$query->whereHas('invoice', function($q) use ($request) {
|
||||||
$q->where('status', $request->status);
|
$q->where('status', $request->status);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->has('shipment') && $request->shipment) {
|
// Shipment status filter
|
||||||
|
if ($request->filled('shipment')) {
|
||||||
$query->whereHas('shipments', function($q) use ($request) {
|
$query->whereHas('shipments', function($q) use ($request) {
|
||||||
$q->where('status', $request->shipment);
|
$q->where('status', $request->shipment);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optional ordering
|
||||||
|
$query->latest('id');
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadPdf(Request $request)
|
||||||
|
{
|
||||||
|
// Build same filtered query used for table
|
||||||
|
$query = $this->buildOrdersQueryFromRequest($request);
|
||||||
|
|
||||||
$orders = $query->get();
|
$orders = $query->get();
|
||||||
|
|
||||||
$pdf = PDF::loadView('admin.orders.pdf', compact('orders'));
|
// optional: pass filters to view for header
|
||||||
return $pdf->download('orders-report-' . date('Y-m-d') . '.pdf');
|
$filters = [
|
||||||
}
|
'search' => $request->search ?? null,
|
||||||
|
'status' => $request->status ?? null,
|
||||||
|
'shipment' => $request->shipment ?? null,
|
||||||
|
];
|
||||||
|
|
||||||
public function downloadExcel(Request $request)
|
$pdf = PDF::loadView('admin.orders.pdf', compact('orders', 'filters'))
|
||||||
{
|
->setPaper('a4', 'landscape'); // adjust if needed
|
||||||
|
|
||||||
|
$fileName = 'orders-report'
|
||||||
|
. ($filters['status'] ? "-{$filters['status']}" : '')
|
||||||
|
. '-' . date('Y-m-d') . '.pdf';
|
||||||
|
|
||||||
|
return $pdf->download($fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadExcel(Request $request)
|
||||||
|
{
|
||||||
|
// pass request to OrdersExport which will build Filtered query internally
|
||||||
return Excel::download(new OrdersExport($request), 'orders-report-' . date('Y-m-d') . '.xlsx');
|
return Excel::download(new OrdersExport($request), 'orders-report-' . date('Y-m-d') . '.xlsx');
|
||||||
}
|
}
|
||||||
|
|
||||||
//====================================================
|
|
||||||
// add Temp Item
|
|
||||||
//=================================================
|
|
||||||
|
|
||||||
public function addTempItem(Request $request)
|
public function addTempItem(Request $request)
|
||||||
{
|
{
|
||||||
// Validate item fields
|
// Validate item fields
|
||||||
$item = $request->validate([
|
$item = $request->validate([
|
||||||
@@ -484,9 +500,13 @@ class AdminOrderController extends Controller
|
|||||||
session()->push('temp_order_items', $item);
|
session()->push('temp_order_items', $item);
|
||||||
|
|
||||||
return redirect()->to(route('admin.orders.index') . '#createOrderForm')
|
return redirect()->to(route('admin.orders.index') . '#createOrderForm')
|
||||||
|
|
||||||
->with('success', 'Item added.');
|
->with('success', 'Item added.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// STEP 3 : FINISH ORDER
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
public function finishOrder(Request $request)
|
public function finishOrder(Request $request)
|
||||||
{
|
{
|
||||||
@@ -644,5 +664,29 @@ class AdminOrderController extends Controller
|
|||||||
return redirect()->route('admin.orders.index')
|
return redirect()->route('admin.orders.index')
|
||||||
->with('success', 'Order + Invoice created successfully.');
|
->with('success', 'Order + Invoice created successfully.');
|
||||||
}
|
}
|
||||||
|
// ---------------------------
|
||||||
|
// ORDER CRUD: update / destroy
|
||||||
|
// ---------------------------
|
||||||
|
public function updateItem(Request $request, $id)
|
||||||
|
{
|
||||||
|
$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,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return back()->with('success', 'Item updated successfully!');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -65,4 +65,52 @@ class UserRequestController extends Controller
|
|||||||
|
|
||||||
return redirect()->back()->with('info', 'Request rejected successfully.');
|
return redirect()->back()->with('info', 'Request rejected successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function profileUpdateRequests()
|
||||||
|
{
|
||||||
|
$requests = \App\Models\UpdateRequest::where('status', 'pending')
|
||||||
|
->orderBy('id', 'desc')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('admin.profile_update_requests', compact('requests'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function approveProfileUpdate($id)
|
||||||
|
{
|
||||||
|
$req = \App\Models\UpdateRequest::findOrFail($id);
|
||||||
|
$user = \App\Models\User::findOrFail($req->user_id);
|
||||||
|
|
||||||
|
// FIX: Ensure data is array
|
||||||
|
$newData = is_array($req->data) ? $req->data : json_decode($req->data, true);
|
||||||
|
|
||||||
|
foreach ($newData as $key => $value) {
|
||||||
|
if ($value !== null && $value !== "") {
|
||||||
|
if (in_array($key, ['customer_name','company_name','designation','email','mobile_no','address','pincode'])) {
|
||||||
|
$user->$key = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$req->status = 'approved';
|
||||||
|
$req->admin_note = 'Approved by admin on ' . now();
|
||||||
|
$req->save();
|
||||||
|
|
||||||
|
return back()->with('success', 'Profile updated successfully.');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function rejectProfileUpdate($id)
|
||||||
|
{
|
||||||
|
$req = \App\Models\UpdateRequest::findOrFail($id);
|
||||||
|
$req->status = 'rejected';
|
||||||
|
$req->admin_note = 'Rejected by admin on ' . now();
|
||||||
|
$req->save();
|
||||||
|
|
||||||
|
return back()->with('info', 'Profile update request rejected.');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,19 +12,68 @@ class UserAuthController extends Controller
|
|||||||
|
|
||||||
public function refreshToken()
|
public function refreshToken()
|
||||||
{
|
{
|
||||||
|
\Log::info('🔄 refreshToken() called');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$newToken = JWTAuth::refresh(JWTAuth::getToken());
|
// Get current token
|
||||||
|
$currentToken = JWTAuth::getToken();
|
||||||
|
|
||||||
|
if (!$currentToken) {
|
||||||
|
\Log::warning('⚠ No token provided in refreshToken()');
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Token not provided',
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Log::info('📥 Current Token:', ['token' => (string) $currentToken]);
|
||||||
|
|
||||||
|
// Try refreshing token
|
||||||
|
$newToken = JWTAuth::refresh($currentToken);
|
||||||
|
|
||||||
|
\Log::info('✅ Token refreshed successfully', ['new_token' => $newToken]);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'token' => $newToken,
|
'token' => $newToken,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
|
||||||
|
\Log::error('❌ TokenExpiredException in refreshToken()', [
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Token expired, cannot refresh.',
|
||||||
|
], 401);
|
||||||
|
|
||||||
|
} catch (\Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
|
||||||
|
\Log::error('❌ TokenInvalidException in refreshToken()', [
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Invalid token.',
|
||||||
|
], 401);
|
||||||
|
|
||||||
|
} catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
|
||||||
|
\Log::error('❌ JWTException in refreshToken()', [
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
]);
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => 'Could not refresh token.',
|
'message' => 'Could not refresh token.',
|
||||||
], 401);
|
], 401);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\Log::error('❌ General Exception in refreshToken()', [
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
]);
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Unexpected error while refreshing token.',
|
||||||
|
], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,16 @@ namespace App\Http\Controllers\User;
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\UpdateRequest;
|
||||||
use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth;
|
use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth;
|
||||||
|
|
||||||
class UserProfileController extends Controller
|
class UserProfileController extends Controller
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Get user profile
|
||||||
|
*/
|
||||||
public function profile()
|
public function profile()
|
||||||
{
|
{
|
||||||
// Get logged-in user using JWT
|
|
||||||
try {
|
try {
|
||||||
$user = JWTAuth::parseToken()->authenticate();
|
$user = JWTAuth::parseToken()->authenticate();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
@@ -20,7 +23,6 @@ class UserProfileController extends Controller
|
|||||||
], 401);
|
], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (! $user) {
|
if (! $user) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
@@ -28,7 +30,6 @@ class UserProfileController extends Controller
|
|||||||
], 401);
|
], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format response
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'data' => [
|
'data' => [
|
||||||
@@ -49,8 +50,13 @@ class UserProfileController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateProfile(Request $request)
|
|
||||||
{
|
|
||||||
|
/**
|
||||||
|
* Update profile IMAGE only (no admin approval)
|
||||||
|
*/
|
||||||
|
public function updateProfileImage(Request $request)
|
||||||
|
{
|
||||||
$user = JWTAuth::parseToken()->authenticate();
|
$user = JWTAuth::parseToken()->authenticate();
|
||||||
|
|
||||||
if (! $user) {
|
if (! $user) {
|
||||||
@@ -60,34 +66,22 @@ class UserProfileController extends Controller
|
|||||||
], 401);
|
], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate ONLY profile image
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'profile_image' => 'required|image|mimes:jpg,jpeg,png|max:2048'
|
'profile_image' => 'required|image|mimes:jpg,jpeg,png|max:2048'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// If new image uploaded
|
|
||||||
if ($request->hasFile('profile_image')) {
|
|
||||||
|
|
||||||
// DELETE OLD IMAGE
|
// DELETE OLD IMAGE
|
||||||
if ($user->profile_image && file_exists(public_path($user->profile_image))) {
|
if ($user->profile_image && file_exists(public_path($user->profile_image))) {
|
||||||
@unlink(public_path($user->profile_image));
|
@unlink(public_path($user->profile_image));
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW FILE
|
// SAVE NEW IMAGE
|
||||||
$file = $request->file('profile_image');
|
$file = $request->file('profile_image');
|
||||||
$filename = 'profile_' . time() . '.' . $file->getClientOriginalExtension();
|
$filename = 'profile_' . time() . '.' . $file->getClientOriginalExtension();
|
||||||
|
|
||||||
// Correct folder name (from your message)
|
|
||||||
$folder = 'profile_upload/';
|
$folder = 'profile_upload/';
|
||||||
$fullPath = $folder . $filename;
|
|
||||||
|
|
||||||
// Move file
|
|
||||||
$file->move(public_path($folder), $filename);
|
$file->move(public_path($folder), $filename);
|
||||||
|
|
||||||
// Save in DB (same pattern you said)
|
$user->profile_image = $folder . $filename;
|
||||||
$user->profile_image = $fullPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@@ -104,9 +98,51 @@ class UserProfileController extends Controller
|
|||||||
'pincode' => $user->pincode,
|
'pincode' => $user->pincode,
|
||||||
'status' => $user->status,
|
'status' => $user->status,
|
||||||
'customer_type' => $user->customer_type,
|
'customer_type' => $user->customer_type,
|
||||||
'profile_image' => $user->profile_image ? url($user->profile_image) : null,
|
'profile_image' => url($user->profile_image),
|
||||||
'date' => $user->date,
|
'date' => $user->date,
|
||||||
]
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit profile update request (requires admin approval)
|
||||||
|
*/
|
||||||
|
public function updateProfileRequest(Request $request)
|
||||||
|
{
|
||||||
|
$user = JWTAuth::parseToken()->authenticate();
|
||||||
|
|
||||||
|
if (! $user) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Unauthorized'
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
$request->validate([
|
||||||
|
'customer_name' => 'nullable|string|max:255',
|
||||||
|
'company_name' => 'nullable|string|max:255',
|
||||||
|
'designation' => 'nullable|string|max:255',
|
||||||
|
'email' => 'nullable|email',
|
||||||
|
'mobile_no' => 'nullable|string|max:15',
|
||||||
|
'address' => 'nullable|string',
|
||||||
|
'pincode' => 'nullable|string|max:10'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// SAVE AS ARRAY (NOT JSON STRING!)
|
||||||
|
$updateReq = \App\Models\UpdateRequest::create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'data' => $request->all(), // <---- FIXED
|
||||||
|
'status' => 'pending',
|
||||||
|
]);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Profile update request submitted. Waiting for admin approval.',
|
||||||
|
'request_id' => $updateReq->id
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
app/Models/UpdateRequest.php
Normal file
30
app/Models/UpdateRequest.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class UpdateRequest extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'update_requests';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'user_id',
|
||||||
|
'data',
|
||||||
|
'status',
|
||||||
|
'admin_note',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'data' => 'array', // converts JSON to array automatically
|
||||||
|
];
|
||||||
|
|
||||||
|
// Relationship: request belongs to a user
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"barryvdh/laravel-dompdf": "^3.1",
|
"barryvdh/laravel-dompdf": "^3.1",
|
||||||
"laravel/framework": "^12.0",
|
"laravel/framework": "^12.0",
|
||||||
"laravel/tinker": "^2.10.1",
|
"laravel/tinker": "^2.10.1",
|
||||||
"maatwebsite/excel": "^1.1",
|
"maatwebsite/excel": "^3.1",
|
||||||
"mpdf/mpdf": "^8.2",
|
"mpdf/mpdf": "^8.2",
|
||||||
"php-open-source-saver/jwt-auth": "2.8"
|
"php-open-source-saver/jwt-auth": "2.8"
|
||||||
},
|
},
|
||||||
|
|||||||
1249
composer.lock
generated
1249
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateUpdateRequestsTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('update_requests', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
// The user who is requesting profile update
|
||||||
|
$table->unsignedBigInteger('user_id');
|
||||||
|
|
||||||
|
// JSON data of the requested profile changes
|
||||||
|
$table->json('data')->nullable();
|
||||||
|
|
||||||
|
// pending / approved / rejected
|
||||||
|
$table->enum('status', ['pending', 'approved', 'rejected'])->default('pending');
|
||||||
|
|
||||||
|
// Optional message (admin notes)
|
||||||
|
$table->text('admin_note')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
// Foreign key constraint
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('update_requests');
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000033.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000033.pdf
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 65 KiB |
BIN
public/profile_upload/profile_1764645094.jpg
Normal file
BIN
public/profile_upload/profile_1764645094.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
BIN
public/profile_upload/profile_1764743106.jpg
Normal file
BIN
public/profile_upload/profile_1764743106.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 209 KiB |
@@ -1723,4 +1723,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener("hidden.bs.modal", function () {
|
||||||
|
// Remove Bootstrap backdrops
|
||||||
|
document.querySelectorAll(".modal-backdrop").forEach(el => el.remove());
|
||||||
|
|
||||||
|
// Fix page scroll locking
|
||||||
|
document.body.classList.remove("modal-open");
|
||||||
|
document.body.style.overflow = "";
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
@@ -212,6 +212,13 @@
|
|||||||
<i class="bi bi-bag"></i> Orders
|
<i class="bi bi-bag"></i> Orders
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ route('admin.requests') }}" class="{{ request()->routeIs('admin.requests') ? 'active' : '' }}"><i class="bi bi-envelope"></i> Requests</a>
|
<a href="{{ route('admin.requests') }}" class="{{ request()->routeIs('admin.requests') ? 'active' : '' }}"><i class="bi bi-envelope"></i> Requests</a>
|
||||||
|
<li>
|
||||||
|
<a href="{{ route('admin.profile.requests') }}">
|
||||||
|
<i class="bi bi-person-lines-fill"></i>
|
||||||
|
Profile Update Requests
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<a href="{{ route('admin.staff') }}" class="{{ request()->routeIs('admin.staff') ? 'active' : '' }}"><i class="bi bi-person-badge"></i> Staff</a>
|
<a href="{{ route('admin.staff') }}" class="{{ request()->routeIs('admin.staff') ? 'active' : '' }}"><i class="bi bi-person-badge"></i> Staff</a>
|
||||||
<a href="{{ route('admin.account') }}" class="{{ request()->routeIs('admin.account') ? 'active' : '' }}"><i class="bi bi-gear"></i> Account</a>
|
<a href="{{ route('admin.account') }}" class="{{ request()->routeIs('admin.account') ? 'active' : '' }}"><i class="bi bi-gear"></i> Account</a>
|
||||||
<a href="{{ route('admin.marklist.index') }}" class="{{ request()->routeIs('admin.marklist.index') ? 'active' : '' }}"><i class="bi bi-list-check"></i> Mark List</a>
|
<a href="{{ route('admin.marklist.index') }}" class="{{ request()->routeIs('admin.marklist.index') ? 'active' : '' }}"><i class="bi bi-list-check"></i> Mark List</a>
|
||||||
|
|||||||
70
resources/views/admin/orders/pdf.blade.php
Normal file
70
resources/views/admin/orders/pdf.blade.php
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Orders Report</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: DejaVu Sans, sans-serif; font-size:12px; }
|
||||||
|
table { width:100%; border-collapse: collapse; }
|
||||||
|
th, td { border: 1px solid #ddd; padding: 6px; text-align: left; }
|
||||||
|
th { background: #f2f2f2; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h3>Orders Report</h3>
|
||||||
|
|
||||||
|
@if(!empty($filters))
|
||||||
|
<p>
|
||||||
|
@if($filters['search']) Search: <strong>{{ $filters['search'] }}</strong> @endif
|
||||||
|
@if($filters['status']) | Status: <strong>{{ ucfirst($filters['status']) }}</strong> @endif
|
||||||
|
@if($filters['shipment']) | Shipment: <strong>{{ ucfirst($filters['shipment']) }}</strong> @endif
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Order ID</th>
|
||||||
|
<th>Shipment ID</th>
|
||||||
|
<th>Customer ID</th>
|
||||||
|
<th>Company</th>
|
||||||
|
<th>Origin</th>
|
||||||
|
<th>Destination</th>
|
||||||
|
<th>Order Date</th>
|
||||||
|
<th>Invoice No</th>
|
||||||
|
<th>Invoice Date</th>
|
||||||
|
<th>Amount</th>
|
||||||
|
<th>Amount + GST</th>
|
||||||
|
<th>Invoice Status</th>
|
||||||
|
<th>Shipment Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse($orders as $order)
|
||||||
|
@php
|
||||||
|
$mark = $order->markList ?? null;
|
||||||
|
$invoice = $order->invoice ?? null;
|
||||||
|
$shipment = $order->shipments->first() ?? null;
|
||||||
|
@endphp
|
||||||
|
<tr>
|
||||||
|
<td>{{ $order->order_id }}</td>
|
||||||
|
<td>{{ $shipment->shipment_id ?? '-' }}</td>
|
||||||
|
<td>{{ $mark->customer_id ?? '-' }}</td>
|
||||||
|
<td>{{ $mark->company_name ?? '-' }}</td>
|
||||||
|
<td>{{ $mark->origin ?? $order->origin ?? '-' }}</td>
|
||||||
|
<td>{{ $mark->destination ?? $order->destination ?? '-' }}</td>
|
||||||
|
<td>{{ $order->created_at ? $order->created_at->format('d-m-Y') : '-' }}</td>
|
||||||
|
<td>{{ $invoice->invoice_number ?? '-' }}</td>
|
||||||
|
<td>{{ $invoice?->invoice_date ? \Carbon\Carbon::parse($invoice->invoice_date)->format('d-m-Y') : '-' }}</td>
|
||||||
|
<td>{{ $invoice?->final_amount ? number_format($invoice->final_amount, 2) : '-' }}</td>
|
||||||
|
<td>{{ $invoice?->final_amount_with_gst ? number_format($invoice->final_amount_with_gst, 2) : '-' }}</td>
|
||||||
|
<td>{{ $invoice->status ? ucfirst($invoice->status) : 'Pending' }}</td>
|
||||||
|
<td>{{ $shipment?->status ? ucfirst(str_replace('_',' ',$shipment->status)) : 'Pending' }}</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr><td colspan="13" style="text-align:center">No orders found</td></tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
111
resources/views/admin/profile_update_requests.blade.php
Normal file
111
resources/views/admin/profile_update_requests.blade.php
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
@extends('admin.layouts.app')
|
||||||
|
|
||||||
|
@section('page-title', 'Profile Update Requests')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container-fluid px-0">
|
||||||
|
|
||||||
|
@php
|
||||||
|
$perPage = 5;
|
||||||
|
$currentPage = request()->get('page', 1);
|
||||||
|
$currentPage = max(1, (int)$currentPage);
|
||||||
|
$total = $requests->count();
|
||||||
|
$totalPages = ceil($total / $perPage);
|
||||||
|
$currentItems = $requests->slice(($currentPage - 1) * $perPage, $perPage);
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.old-value { color: #6b7280; font-weight: 600; }
|
||||||
|
.new-value { color: #111827; font-weight: 700; }
|
||||||
|
.changed { background: #fef3c7; padding: 6px; border-radius: 6px; }
|
||||||
|
.box { padding: 10px 14px; border-radius: 8px; background: #f8fafc; margin-bottom: 10px; }
|
||||||
|
.diff-label { font-size: 13px; font-weight: 700; }
|
||||||
|
.actions { display: flex; gap: 10px; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<h4 class="fw-bold my-3">Profile Update Requests ({{ $total }})</h4>
|
||||||
|
|
||||||
|
<div class="card mb-4 shadow-sm">
|
||||||
|
<div class="card-body pb-1">
|
||||||
|
|
||||||
|
<div class="table-responsive custom-table-wrapper">
|
||||||
|
<table class="table align-middle mb-0 custom-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>User</th>
|
||||||
|
<th>Requested Changes</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Requested At</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
@foreach($currentItems as $index => $req)
|
||||||
|
@php
|
||||||
|
$user = $req->user;
|
||||||
|
// FIX: Convert string to array
|
||||||
|
$newData = is_array($req->data) ? $req->data : json_decode($req->data, true);
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><strong>{{ ($currentPage - 1) * $perPage + $index + 1 }}</strong></td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<strong>{{ $user->customer_name }}</strong><br>
|
||||||
|
<small>{{ $user->email }}</small><br>
|
||||||
|
<small>ID: {{ $user->customer_id }}</small>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
@foreach($newData as $key => $newValue)
|
||||||
|
@php
|
||||||
|
$oldValue = $user->$key ?? '—';
|
||||||
|
$changed = $oldValue != $newValue;
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="box {{ $changed ? 'changed' : '' }}">
|
||||||
|
<span class="diff-label">{{ ucfirst(str_replace('_',' ', $key)) }}:</span><br>
|
||||||
|
<span class="old-value">Old: {{ $oldValue }}</span><br>
|
||||||
|
<span class="new-value">New: {{ $newValue ?? '—' }}</span>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
@if($req->status == 'pending')
|
||||||
|
<span class="badge badge-pending">Pending</span>
|
||||||
|
@elseif($req->status == 'approved')
|
||||||
|
<span class="badge badge-approved">Approved</span>
|
||||||
|
@else
|
||||||
|
<span class="badge badge-rejected">Rejected</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>{{ $req->created_at->format('d M Y, h:i A') }}</td>
|
||||||
|
|
||||||
|
<td class="actions">
|
||||||
|
@if($req->status == 'pending')
|
||||||
|
<a href="{{ route('admin.profile.approve', $req->id) }}" class="btn btn-success btn-sm">
|
||||||
|
<i class="bi bi-check-circle"></i> Approve
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="{{ route('admin.profile.reject', $req->id) }}" class="btn btn-danger btn-sm">
|
||||||
|
<i class="bi bi-x-circle"></i> Reject
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<span class="text-muted">Completed</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -7,18 +7,15 @@
|
|||||||
/*Remove horizontal scroll bar*/
|
/*Remove horizontal scroll bar*/
|
||||||
html, body {
|
html, body {
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table thead th,
|
.table thead th,
|
||||||
.table tbody td {
|
.table tbody td {
|
||||||
white-space: nowrap !important;
|
white-space: nowrap !important;
|
||||||
padding: 14px 8px !important;
|
padding: 8px 4px !important;
|
||||||
font-size: 14px;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table th:nth-child(10),
|
.table th:nth-child(10), /* if Date is column 10 */
|
||||||
.table td:nth-child(10) {
|
.table td:nth-child(10) {
|
||||||
max-width: 110px !important;
|
max-width: 110px !important;
|
||||||
width: 110px !important;
|
width: 110px !important;
|
||||||
@@ -28,7 +25,6 @@
|
|||||||
|
|
||||||
.table {
|
.table {
|
||||||
table-layout: fixed !important;
|
table-layout: fixed !important;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -36,7 +32,6 @@
|
|||||||
min-width: unset !important;
|
min-width: unset !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
table-layout: auto !important;
|
table-layout: auto !important;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table thead th,
|
.table thead th,
|
||||||
@@ -47,8 +42,6 @@
|
|||||||
.shipment-details-table td {
|
.shipment-details-table td {
|
||||||
white-space: normal !important;
|
white-space: normal !important;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-responsive, .modal .table-responsive {
|
.table-responsive, .modal .table-responsive {
|
||||||
@@ -76,27 +69,25 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background: white;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
box-shadow: var(--card-shadow);
|
box-shadow: var(--card-shadow);
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
color: #333;
|
color: white;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
border: 1px solid #e2e8f0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input-container {
|
.search-shipment-bar::before {
|
||||||
display: flex;
|
content: '';
|
||||||
flex: 1;
|
position: absolute;
|
||||||
min-width: 300px;
|
top: 0;
|
||||||
background: white;
|
left: 0;
|
||||||
border-radius: 10px;
|
right: 0;
|
||||||
overflow: hidden;
|
bottom: 0;
|
||||||
border: 1px solid #d1d5db;
|
background: rgba(255,255,255,0.1);
|
||||||
transition: all 0.3s ease;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input-container:focus-within {
|
.search-input-container:focus-within {
|
||||||
@@ -164,31 +155,31 @@
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-shipment-bar input,
|
||||||
.search-shipment-bar select {
|
.search-shipment-bar select {
|
||||||
padding: 20px 16px;
|
padding: 12px 16px;
|
||||||
border: 1px solid #d1d5db;
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
flex: 1;
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
background: white;
|
background: rgba(255,255,255,0.9);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-shipment-bar input:focus,
|
||||||
.search-shipment-bar select:focus {
|
.search-shipment-bar select:focus {
|
||||||
background: white;
|
background: white;
|
||||||
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.1);
|
box-shadow: 0 0 0 3px rgba(255,255,255,0.3);
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #4361ee;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-add-shipment {
|
.btn-add-shipment {
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: rgba(255,255,255,0.2);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: 1px solid rgba(255,255,255,0.3);
|
||||||
padding: 17px 24px;
|
padding: 12px 24px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -197,13 +188,17 @@
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-add-shipment:hover {
|
.btn-add-shipment:hover {
|
||||||
|
background: rgba(255,255,255,0.3);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
|
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));
|
||||||
}
|
}
|
||||||
|
|
||||||
.truck-icon {
|
.truck-icon {
|
||||||
@@ -215,7 +210,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
.search-input-container,
|
.search-shipment-bar input,
|
||||||
.search-shipment-bar select {
|
.search-shipment-bar select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@@ -248,7 +243,6 @@
|
|||||||
box-shadow: var(--card-shadow);
|
box-shadow: var(--card-shadow);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover {
|
.card:hover {
|
||||||
@@ -262,7 +256,6 @@
|
|||||||
border: none;
|
border: none;
|
||||||
padding: 20px 25px;
|
padding: 20px 25px;
|
||||||
border-radius: 16px 16px 0 0 !important;
|
border-radius: 16px 16px 0 0 !important;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header h5 {
|
.card-header h5 {
|
||||||
@@ -271,8 +264,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Table Styles */
|
/* Table Styles */
|
||||||
@@ -288,7 +279,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 1200px;
|
min-width: 1200px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table thead th {
|
.table thead th {
|
||||||
@@ -302,8 +292,6 @@
|
|||||||
border-bottom: 2px solid var(--border);
|
border-bottom: 2px solid var(--border);
|
||||||
position: relative;
|
position: relative;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table thead th:first-child {
|
.table thead th:first-child {
|
||||||
@@ -332,8 +320,6 @@
|
|||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table tbody tr:last-child td {
|
.table tbody tr:last-child td {
|
||||||
@@ -349,13 +335,8 @@
|
|||||||
border: 2px solid transparent !important;
|
border: 2px solid transparent !important;
|
||||||
min-width: 40px !important;
|
min-width: 40px !important;
|
||||||
text-align: center !important;
|
text-align: center !important;
|
||||||
display: inline-flex !important;
|
display: inline-block !important;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
line-height: 1.2 !important;
|
line-height: 1.2 !important;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
gap: 6px;
|
|
||||||
width: 110px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Status icons */
|
/* Status icons */
|
||||||
@@ -371,6 +352,7 @@
|
|||||||
background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
|
background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
|
||||||
color: #d97706 !important;
|
color: #d97706 !important;
|
||||||
border-color: #f59e0b !important;
|
border-color: #f59e0b !important;
|
||||||
|
width: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In Transit Status - SAME SIZE WITH TRUCK ICON */
|
/* In Transit Status - SAME SIZE WITH TRUCK ICON */
|
||||||
@@ -378,6 +360,7 @@
|
|||||||
background: linear-gradient(135deg, #dbeafe, #93c5fd) !important;
|
background: linear-gradient(135deg, #dbeafe, #93c5fd) !important;
|
||||||
color: #1e40af !important;
|
color: #1e40af !important;
|
||||||
border-color: #3b82f6 !important;
|
border-color: #3b82f6 !important;
|
||||||
|
width: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dispatched Status - SAME SIZE WITH BOX ICON */
|
/* Dispatched Status - SAME SIZE WITH BOX ICON */
|
||||||
@@ -385,6 +368,7 @@
|
|||||||
background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important;
|
background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important;
|
||||||
color: #6b21a8 !important;
|
color: #6b21a8 !important;
|
||||||
border-color: #8b5cf6 !important;
|
border-color: #8b5cf6 !important;
|
||||||
|
width: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delivered Status - SAME SIZE WITH CHECK ICON */
|
/* Delivered Status - SAME SIZE WITH CHECK ICON */
|
||||||
@@ -392,6 +376,7 @@
|
|||||||
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
|
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
|
||||||
color: #065f46 !important;
|
color: #065f46 !important;
|
||||||
border-color: #10b981 !important;
|
border-color: #10b981 !important;
|
||||||
|
width: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default badge styles - SAME SIZE */
|
/* Default badge styles - SAME SIZE */
|
||||||
@@ -422,9 +407,6 @@
|
|||||||
border: 1px solid #dee2e6 !important;
|
border: 1px solid #dee2e6 !important;
|
||||||
min-width: 80px !important;
|
min-width: 80px !important;
|
||||||
padding: 6px 12px !important;
|
padding: 6px 12px !important;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 13px;
|
|
||||||
width: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NEW: Action Button Styles */
|
/* NEW: Action Button Styles */
|
||||||
@@ -446,7 +428,6 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-edit-status:hover {
|
.btn-edit-status:hover {
|
||||||
@@ -470,7 +451,6 @@
|
|||||||
margin-top: -50px;
|
margin-top: -50px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
z-index: 1050;
|
z-index: 1050;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-dropdown.show {
|
.status-dropdown.show {
|
||||||
@@ -491,7 +471,6 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-option:hover {
|
.status-option:hover {
|
||||||
@@ -623,7 +602,6 @@
|
|||||||
border: none;
|
border: none;
|
||||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
@@ -633,7 +611,6 @@
|
|||||||
padding: 25px 30px 15px;
|
padding: 25px 30px 15px;
|
||||||
border-radius: 20px 20px 0 0;
|
border-radius: 20px 20px 0 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header::after {
|
.modal-header::after {
|
||||||
@@ -649,7 +626,6 @@
|
|||||||
.modal-title {
|
.modal-title {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-close {
|
.btn-close {
|
||||||
@@ -663,7 +639,6 @@
|
|||||||
|
|
||||||
.modal-body {
|
.modal-body {
|
||||||
padding: 25px 30px;
|
padding: 25px 30px;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Form Styles */
|
/* Form Styles */
|
||||||
@@ -672,18 +647,16 @@
|
|||||||
color: #5a6c7d;
|
color: #5a6c7d;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid #e2e8f0;
|
border: 1px solid #e2e8f0;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
background: #fafbfc;
|
background: #fafbfc;
|
||||||
color: #4a5568;
|
color: #4a5568;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control:focus {
|
.form-control:focus {
|
||||||
@@ -738,8 +711,6 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
padding: 12px 30px;
|
padding: 12px 30px;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-cancel:hover {
|
.btn-cancel:hover {
|
||||||
@@ -753,12 +724,11 @@
|
|||||||
background: linear-gradient(135deg, #48bb78, #38a169);
|
background: linear-gradient(135deg, #48bb78, #38a169);
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 14px;
|
font-size: 16px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 12px 35px;
|
padding: 12px 35px;
|
||||||
border: none;
|
border: none;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-create:hover {
|
.btn-create:hover {
|
||||||
@@ -774,7 +744,6 @@
|
|||||||
border-spacing: 0 8px;
|
border-spacing: 0 8px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-width: 1300px;
|
min-width: 1300px;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-table-modal thead th {
|
.custom-table-modal thead th {
|
||||||
@@ -785,10 +754,9 @@
|
|||||||
border: 1px solid #e2e8f0;
|
border: 1px solid #e2e8f0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 14px;
|
font-size: 0.9rem;
|
||||||
letter-spacing: 0.3px;
|
letter-spacing: 0.3px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-table-modal thead th:first-child {
|
.custom-table-modal thead th:first-child {
|
||||||
@@ -825,8 +793,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
color: #4a5568;
|
color: #4a5568;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-table-modal tbody tr td:first-child {
|
.custom-table-modal tbody tr td:first-child {
|
||||||
@@ -881,12 +847,10 @@
|
|||||||
border: none;
|
border: none;
|
||||||
padding: 25px 30px 15px;
|
padding: 25px 30px 15px;
|
||||||
border-radius: 20px 20px 0 0;
|
border-radius: 20px 20px 0 0;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shipment-details-body {
|
.shipment-details-body {
|
||||||
padding: 40px 45px;
|
padding: 40px 45px;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shipment-info-row {
|
.shipment-info-row {
|
||||||
@@ -897,7 +861,6 @@
|
|||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shipment-info-item {
|
.shipment-info-item {
|
||||||
@@ -911,14 +874,12 @@
|
|||||||
color: #64748b;
|
color: #64748b;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shipment-info-value {
|
.shipment-info-value {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: var(--dark);
|
color: var(--dark);
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shipment-details-table {
|
.shipment-details-table {
|
||||||
@@ -927,7 +888,6 @@
|
|||||||
border-spacing: 0 8px;
|
border-spacing: 0 8px;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
min-width: 1400px;
|
min-width: 1400px;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shipment-details-table th {
|
.shipment-details-table th {
|
||||||
@@ -939,8 +899,6 @@
|
|||||||
border: none;
|
border: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shipment-details-table th:first-child {
|
.shipment-details-table th:first-child {
|
||||||
@@ -960,8 +918,6 @@
|
|||||||
background: white;
|
background: white;
|
||||||
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shipment-details-table tr td:first-child {
|
.shipment-details-table tr td:first-child {
|
||||||
@@ -982,7 +938,6 @@
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
||||||
border-left: 4px solid #4361ee;
|
border-left: 4px solid #4361ee;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shipment-totals-row {
|
.shipment-totals-row {
|
||||||
@@ -1009,7 +964,6 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shipment-total-value {
|
.shipment-total-value {
|
||||||
@@ -1017,7 +971,6 @@
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: #1e293b;
|
color: #1e293b;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-amount {
|
.total-amount {
|
||||||
@@ -1090,7 +1043,6 @@
|
|||||||
/* Status Filter Styles */
|
/* Status Filter Styles */
|
||||||
.status-filter-container {
|
.status-filter-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-left:350px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-filter-select {
|
.status-filter-select {
|
||||||
@@ -1126,14 +1078,12 @@
|
|||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
padding: 12px 25px;
|
padding: 12px 25px;
|
||||||
border-top: 1px solid #eef3fb;
|
border-top: 1px solid #eef3fb;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-info {
|
.pagination-info {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #9ba5bb;
|
color: #9ba5bb;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-controls {
|
.pagination-controls {
|
||||||
@@ -1157,7 +1107,6 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-btn:hover:not(:disabled) {
|
.pagination-btn:hover:not(:disabled) {
|
||||||
@@ -1186,7 +1135,6 @@
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
min-width: 36px;
|
min-width: 36px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-page-btn:hover {
|
.pagination-page-btn:hover {
|
||||||
@@ -1211,7 +1159,6 @@
|
|||||||
color: #9ba5bb;
|
color: #9ba5bb;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Image-based pagination buttons */
|
/* Image-based pagination buttons */
|
||||||
@@ -1540,7 +1487,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
@empty
|
@empty
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="12" class="text-center py-5 text-muted">
|
<td colspan="11" class="text-center py-5 text-muted">
|
||||||
<i class="bi bi-inbox display-4 d-block mb-3"></i>
|
<i class="bi bi-inbox display-4 d-block mb-3"></i>
|
||||||
No shipments found
|
No shipments found
|
||||||
</td>
|
</td>
|
||||||
@@ -1754,7 +1701,7 @@ function renderTable() {
|
|||||||
if (filteredShipments.length === 0) {
|
if (filteredShipments.length === 0) {
|
||||||
tbody.innerHTML = `
|
tbody.innerHTML = `
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="12" class="text-center py-5 text-muted">
|
<td colspan="11" class="text-center py-5 text-muted">
|
||||||
<i class="bi bi-search display-4 d-block mb-3"></i>
|
<i class="bi bi-search display-4 d-block mb-3"></i>
|
||||||
No shipments found matching your criteria
|
No shipments found matching your criteria
|
||||||
</td>
|
</td>
|
||||||
@@ -1789,9 +1736,9 @@ function renderTable() {
|
|||||||
</td>
|
</td>
|
||||||
<td>${shipment.origin}</td>
|
<td>${shipment.origin}</td>
|
||||||
<td>${shipment.destination}</td>
|
<td>${shipment.destination}</td>
|
||||||
<td>${shipment.total_qty}</td>
|
<td><span class="badge bg-light text-dark">${shipment.total_qty}</span></td>
|
||||||
<td>${shipment.total_kg} kg</td>
|
<td><span class="badge bg-light text-dark">${shipment.total_kg} kg</span></td>
|
||||||
<td>${shipment.total_cbm} CBM</td>
|
<td><span class="badge bg-light text-dark">${shipment.total_cbm} CBM</span></td>
|
||||||
<td class="fw-bold text-success">₹${parseFloat(shipment.total_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
<td class="fw-bold text-success">₹${parseFloat(shipment.total_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge badge-${shipment.status}">
|
<span class="badge badge-${shipment.status}">
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ Route::post('/signup-request', [RequestController::class, 'usersignup']);
|
|||||||
Route::post('/user/login', [UserAuthController::class, 'login']);
|
Route::post('/user/login', [UserAuthController::class, 'login']);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Route::middleware(['auth:api'])->group(function () {
|
Route::middleware(['auth:api'])->group(function () {
|
||||||
|
//Route::post('/user/refresh', [UserAuthController::class, 'refreshToken']);
|
||||||
|
|
||||||
Route::post('/user/logout', [UserAuthController::class, 'logout']);
|
Route::post('/user/logout', [UserAuthController::class, 'logout']);
|
||||||
|
|
||||||
@@ -37,6 +39,11 @@ Route::middleware(['auth:api'])->group(function () {
|
|||||||
Route::get('/user/invoice/{invoice_id}/installments', [UserOrderController::class, 'invoiceInstallmentsById']);
|
Route::get('/user/invoice/{invoice_id}/installments', [UserOrderController::class, 'invoiceInstallmentsById']);
|
||||||
|
|
||||||
// Profile
|
// Profile
|
||||||
|
|
||||||
|
|
||||||
Route::get('/user/profile', [UserProfileController::class, 'profile']);
|
Route::get('/user/profile', [UserProfileController::class, 'profile']);
|
||||||
Route::post('/user/profile/update', [UserProfileController::class, 'updateProfile']);
|
Route::post('/user/profile-image', [UserProfileController::class, 'updateProfileImage']);
|
||||||
|
Route::post('/user/profile-update-request', [UserProfileController::class, 'updateProfileRequest']);
|
||||||
|
|
||||||
|
// Route::post('/user/profile/update', [UserProfileController::class, 'updateProfile']);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -75,6 +75,20 @@ Route::prefix('admin')
|
|||||||
Route::get('/requests/reject/{id}', [UserRequestController::class, 'reject'])
|
Route::get('/requests/reject/{id}', [UserRequestController::class, 'reject'])
|
||||||
->name('admin.requests.reject');
|
->name('admin.requests.reject');
|
||||||
|
|
||||||
|
// PROFILE UPDATE REQUESTS
|
||||||
|
Route::get('/profile-update-requests',
|
||||||
|
[UserRequestController::class, 'profileUpdateRequests']
|
||||||
|
)->name('admin.profile.requests');
|
||||||
|
|
||||||
|
Route::get('/profile-update/approve/{id}',
|
||||||
|
[UserRequestController::class, 'approveProfileUpdate']
|
||||||
|
)->name('admin.profile.approve');
|
||||||
|
|
||||||
|
Route::get('/profile-update/reject/{id}',
|
||||||
|
[UserRequestController::class, 'rejectProfileUpdate']
|
||||||
|
)->name('admin.profile.reject');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// MARK LIST
|
// MARK LIST
|
||||||
@@ -174,6 +188,7 @@ Route::prefix('admin')
|
|||||||
|
|
||||||
Route::post('/shipments/{id}/add-orders', [ShipmentController::class, 'addOrders'])
|
Route::post('/shipments/{id}/add-orders', [ShipmentController::class, 'addOrders'])
|
||||||
->name('admin.shipments.addOrders');
|
->name('admin.shipments.addOrders');
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// INVOICES
|
// INVOICES
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@@ -195,6 +210,8 @@ Route::prefix('admin')
|
|||||||
Route::post('/invoices/{id}/installment', [AdminInvoiceController::class, 'storeInstallment'])
|
Route::post('/invoices/{id}/installment', [AdminInvoiceController::class, 'storeInstallment'])
|
||||||
->name('admin.invoice.installment.store');
|
->name('admin.invoice.installment.store');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Route::delete('/installment/{id}', [AdminInvoiceController::class, 'deleteInstallment'])
|
Route::delete('/installment/{id}', [AdminInvoiceController::class, 'deleteInstallment'])
|
||||||
->name('admin.invoice.installment.delete');
|
->name('admin.invoice.installment.delete');
|
||||||
|
|
||||||
@@ -278,8 +295,11 @@ Route::prefix('admin')
|
|||||||
// ---------------------------
|
// ---------------------------
|
||||||
// REPORTS DOWNLOAD ROUTES
|
// REPORTS DOWNLOAD ROUTES
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
Route::get('/admin/orders/download/pdf', [OrderController::class, 'downloadPdf'])->name('admin.orders.download.pdf');
|
Route::get('/admin/orders/download/pdf', [AdminOrderController::class, 'downloadPdf'])
|
||||||
Route::get('/admin/orders/download/excel', [OrderController::class, 'downloadExcel'])->name('admin.orders.download.excel');
|
->name('admin.orders.download.pdf');
|
||||||
|
|
||||||
|
Route::get('/admin/orders/download/excel', [AdminOrderController::class, 'downloadExcel'])
|
||||||
|
->name('admin.orders.download.excel');
|
||||||
|
|
||||||
//---------------------------
|
//---------------------------
|
||||||
//Edit Button Route
|
//Edit Button Route
|
||||||
|
|||||||
Reference in New Issue
Block a user