From aa616fcf61f2c8f871ebe56eb8fb92f11abe2bbd4a900cd1ff0fecc2c6d15e72 Mon Sep 17 00:00:00 2001 From: divya abdar Date: Mon, 1 Dec 2025 10:38:52 +0530 Subject: [PATCH 1/4] Frontend dashboard, shipment, invoice , customer --- .../Admin/AdminOrderController.php | 550 +-- .../Controllers/Admin/ShipmentController.php | 60 +- app/Models/Order.php | 6 +- app/Models/OrderItem.php | 3 +- ...1305_create_invoice_installments_table.php | 19 +- ...82057_add_soft_deletes_to_orders_table.php | 22 + ...36_add_deleted_at_to_order_items_table.php | 26 + resources/views/admin/customers.blade.php | 540 ++- resources/views/admin/dashboard.blade.php | 290 +- resources/views/admin/invoice.blade.php | 3131 +++++++++-------- resources/views/admin/orders.blade.php | 196 +- resources/views/admin/orders_show.blade.php | 596 +++- resources/views/admin/reports.blade.php | 395 ++- resources/views/admin/shipments.blade.php | 906 +++-- routes/web.php | 49 +- 15 files changed, 4280 insertions(+), 2509 deletions(-) create mode 100644 database/migrations/2025_11_28_182057_add_soft_deletes_to_orders_table.php create mode 100644 database/migrations/2025_11_29_045236_add_deleted_at_to_order_items_table.php diff --git a/app/Http/Controllers/Admin/AdminOrderController.php b/app/Http/Controllers/Admin/AdminOrderController.php index 47a1da3..bdaef1e 100644 --- a/app/Http/Controllers/Admin/AdminOrderController.php +++ b/app/Http/Controllers/Admin/AdminOrderController.php @@ -7,28 +7,123 @@ use Illuminate\Http\Request; use App\Models\Order; use App\Models\OrderItem; use App\Models\MarkList; +use App\Models\Invoice; +use App\Models\InvoiceItem; +use App\Models\User; class AdminOrderController extends Controller { + // --------------------------- + // LIST / DASHBOARD + // --------------------------- public function index() { + // raw list for admin dashboard (simple) $orders = Order::latest()->get(); $markList = MarkList::where('status', 'active')->get(); return view('admin.dashboard', compact('orders', 'markList')); } - // ------------------------------------------------------------------------- - // STEP 1 : ADD TEMPORARY ITEM - // ------------------------------------------------------------------------- - - public function addTempItem(Request $request) + /** + * Orders list (detailed) + */ + public function orderShow() { - // Validate item fields - $item = $request->validate([ - 'mark_no' => 'required', - 'origin' => 'required', - 'destination' => 'required', + $orders = Order::with(['markList', 'shipments', 'invoice']) + ->latest('id') + ->get(); + + return view('admin.orders', compact('orders')); + } + + // --------------------------- + // CREATE NEW ORDER (simple DB flow) + // --------------------------- + /** + * Show create form (you can place create UI on separate view or dashboard) + */ + public function create() + { + // return a dedicated create view - create it at resources/views/admin/orders_create.blade.php + // If you prefer create UI on dashboard, change this to redirect route('admin.orders.index') etc. + $markList = MarkList::where('status', 'active')->get(); + return view('admin.orders_create', compact('markList')); + } + + /** + * Store a new order and optionally create initial invoice + */ + public function store(Request $request) + { + $data = $request->validate([ + 'mark_no' => 'required|string', + 'origin' => 'nullable|string', + 'destination' => 'nullable|string', + // totals optional when creating without items + 'ctn' => 'nullable|numeric', + 'qty' => 'nullable|numeric', + 'ttl_qty' => 'nullable|numeric', + 'ttl_amount' => 'nullable|numeric', + 'cbm' => 'nullable|numeric', + 'ttl_cbm' => 'nullable|numeric', + 'kg' => 'nullable|numeric', + 'ttl_kg' => 'nullable|numeric', + ]); + + $order = Order::create([ + 'order_id' => $this->generateOrderId(), + 'mark_no' => $data['mark_no'], + 'origin' => $data['origin'] ?? null, + 'destination' => $data['destination'] ?? null, + 'ctn' => $data['ctn'] ?? 0, + 'qty' => $data['qty'] ?? 0, + 'ttl_qty' => $data['ttl_qty'] ?? 0, + 'ttl_amount' => $data['ttl_amount'] ?? 0, + 'cbm' => $data['cbm'] ?? 0, + 'ttl_cbm' => $data['ttl_cbm'] ?? 0, + 'kg' => $data['kg'] ?? 0, + 'ttl_kg' => $data['ttl_kg'] ?? 0, + 'status' => 'pending', + ]); + + // 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.'); + } + + // --------------------------- + // SHOW / POPUP + // --------------------------- + public function show($id) + { + $order = Order::with('items', 'markList')->findOrFail($id); + $user = $this->getCustomerFromOrder($order); + + return view('admin.orders_show', compact('order', 'user')); + } + + public function popup($id) + { + $order = Order::with(['items', 'markList'])->findOrFail($id); + $user = $this->getCustomerFromOrder($order); + + return view('admin.popup', compact('order', 'user')); + } + + // --------------------------- + // ORDER ITEM MANAGEMENT (DB) + // --------------------------- + /** + * Add an item to an existing order + */ + public function addItem(Request $request, $orderId) + { + $order = Order::findOrFail($orderId); + + $data = $request->validate([ 'description' => 'required|string', 'ctn' => 'nullable|numeric', 'qty' => 'nullable|numeric', @@ -43,271 +138,206 @@ class AdminOrderController extends Controller 'shop_no' => 'nullable|string', ]); - // ❌ Prevent changing mark_no once first item added - if (session()->has('mark_no') && session('mark_no') != $request->mark_no) { - return redirect()->to(route('admin.orders.index') . '#createOrderForm') - ->with('error', 'You must finish or clear the current order before changing Mark No.'); - } + $data['order_id'] = $order->id; - // Save mark, origin, destination ONLY ONCE - if (!session()->has('mark_no')) { - session([ - 'mark_no' => $request->mark_no, - 'origin' => $request->origin, - 'destination' => $request->destination + OrderItem::create($data); + + // recalc totals and save to order + $this->recalcTotals($order); + + return redirect()->back()->with('success', 'Item added and totals updated.'); + } + + /** + * Soft-delete an order item and recalc totals + */ + public function deleteItem($id) + { + $item = OrderItem::findOrFail($id); + $order = $item->order; + + $item->delete(); // soft delete + + // recalc totals + $this->recalcTotals($order); + + return redirect()->back()->with('success', 'Item deleted and totals updated.'); + } + + /** + * Restore soft-deleted item and recalc totals + */ + public function restoreItem($id) + { + $item = OrderItem::withTrashed()->findOrFail($id); + $order = Order::findOrFail($item->order_id); + + $item->restore(); + + // recalc totals + $this->recalcTotals($order); + + return redirect()->back()->with('success', 'Item restored and totals updated.'); + } + + // --------------------------- + // ORDER CRUD: update / destroy + // --------------------------- + public function update(Request $request, $id) + { + $order = Order::findOrFail($id); + + $data = $request->validate([ + 'mark_no' => 'required|string', + 'origin' => 'nullable|string', + 'destination' => 'nullable|string', + ]); + + $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.'); + } + + /** + * Soft-delete whole order and its items (soft-delete items first then order) + */ + public function destroy($id) + { + $order = Order::findOrFail($id); + + // soft-delete items first (so they show up in onlyTrashed for restore) + OrderItem::where('order_id', $order->id)->delete(); + + // then soft-delete order + $order->delete(); + + return redirect()->route('admin.orders.index') + ->with('success', 'Order deleted successfully.'); + } + + // --------------------------- + // HELPERS + // --------------------------- + /** + * Recalculate totals for the order from current (non-deleted) items + */ + private function recalcTotals(Order $order) + { + // make sure we re-query live items (non-deleted) + $items = $order->items()->get(); + + $order->update([ + 'ctn' => (int) $items->sum(fn($i) => (int) ($i->ctn ?? 0)), + 'qty' => (int) $items->sum(fn($i) => (int) ($i->qty ?? 0)), + 'ttl_qty' => (int) $items->sum(fn($i) => (int) ($i->ttl_qty ?? 0)), + 'ttl_amount'=> (float) $items->sum(fn($i) => (float) ($i->ttl_amount ?? 0)), + 'cbm' => (float) $items->sum(fn($i) => (float) ($i->cbm ?? 0)), + 'ttl_cbm' => (float) $items->sum(fn($i) => (float) ($i->ttl_cbm ?? 0)), + 'kg' => (float) $items->sum(fn($i) => (float) ($i->kg ?? 0)), + 'ttl_kg' => (float) $items->sum(fn($i) => (float) ($i->ttl_kg ?? 0)), + ]); + } + + /** + * Generate order id (keeps old format) + */ + private function generateOrderId() + { + $year = date('y'); + $prefix = "KNT-$year-"; + + $lastOrder = Order::latest('id')->first(); + $nextNumber = $lastOrder ? intval(substr($lastOrder->order_id, -8)) + 1 : 1; + + return $prefix . str_pad($nextNumber, 8, '0', STR_PAD_LEFT); + } + + // --------------------------- + // INVOICE CREATION (optional helper used by store/finish) + // --------------------------- + private function createInvoice(Order $order) + { + $invoiceNumber = $this->generateInvoiceNumber(); + $customer = $this->getCustomerFromMarkList($order->mark_no); + $totalAmount = $order->ttl_amount; + + $invoice = 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' => $totalAmount, + 'gst_percent' => 0, + 'gst_amount' => 0, + 'final_amount_with_gst' => $totalAmount, + '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, + 'pdf_path' => null, + ]); + + // clone order items into invoice items + foreach ($order->items as $item) { + 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, ]); } - - // ❌ DO NOT overwrite these values again - // session(['mark_no' => $request->mark_no]); - // session(['origin' => $request->origin]); - // session(['destination' => $request->destination]); - - // Add new sub-item into session - session()->push('temp_order_items', $item); - - return redirect()->to(route('admin.orders.index') . '#createOrderForm') - ->with('success', 'Item added.'); } - // ------------------------------------------------------------------------- - // STEP 2 : DELETE TEMPORARY ITEM - // ------------------------------------------------------------------------- - - public function deleteTempItem(Request $request) + private function generateInvoiceNumber() { - $index = $request->index; + $lastInvoice = Invoice::latest()->first(); + $nextInvoice = $lastInvoice ? $lastInvoice->id + 1 : 1; - $items = session('temp_order_items', []); + return 'INV-' . date('Y') . '-' . str_pad($nextInvoice, 6, '0', STR_PAD_LEFT); + } - if (isset($items[$index])) { - unset($items[$index]); - session(['temp_order_items' => array_values($items)]); + private function getCustomerFromMarkList($markNo) + { + $markList = MarkList::where('mark_no', $markNo)->first(); + + if ($markList && $markList->customer_id) { + return User::where('customer_id', $markList->customer_id)->first(); } - // If no items left → reset mark_no lock - if (empty($items)) { - session()->forget(['mark_no', 'origin', 'destination']); - } - - return redirect()->to(route('admin.orders.index') . '#createOrderForm') - ->with('success', 'Item removed successfully.'); + return null; } - // ------------------------------------------------------------------------- - // 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 SHOW PAGE - // ------------------------------------------------------------------------- - - public function show($id) + private function getCustomerFromOrder($order) { - $order = Order::with('items', 'markList')->findOrFail($id); - - $user = null; if ($order->markList && $order->markList->customer_id) { - $user = \App\Models\User::where('customer_id', $order->markList->customer_id)->first(); + return User::where('customer_id', $order->markList->customer_id)->first(); } - return view('admin.orders_show', compact('order', 'user')); + return null; } - - public function popup($id) - { - // Load order with items + markList - $order = Order::with(['items', 'markList'])->findOrFail($id); - - // Fetch user based on markList customer_id (same as show method) - $user = null; - if ($order->markList && $order->markList->customer_id) { - $user = \App\Models\User::where('customer_id', $order->markList->customer_id)->first(); - } - - return view('admin.popup', compact('order', 'user')); - } - - - - - public function resetTemp() - { - session()->forget(['temp_order_items', 'mark_no', 'origin', 'destination']); - - return redirect()->to(route('admin.orders.index') . '#createOrderForm') - ->with('success', 'Order reset successfully.'); - } - - public function orderShow() - { - $orders = Order::with([ - 'markList', // company, customer, origin, destination, date - 'shipments', // shipment_id, shipment_date, status - 'invoice' // invoice number, dates, amounts, status - ]) - ->latest('id') // show latest orders first - ->get(); - - return view('admin.orders', compact('orders')); - } - } diff --git a/app/Http/Controllers/Admin/ShipmentController.php b/app/Http/Controllers/Admin/ShipmentController.php index c0883fd..d281059 100644 --- a/app/Http/Controllers/Admin/ShipmentController.php +++ b/app/Http/Controllers/Admin/ShipmentController.php @@ -14,7 +14,7 @@ class ShipmentController extends Controller /** * Show shipment page (Create Shipment + Shipment List) */ - public function index() + public function index() { // 1) Get all used order IDs $usedOrderIds = ShipmentItem::pluck('order_id')->toArray(); @@ -29,8 +29,6 @@ class ShipmentController extends Controller return view('admin.shipments', compact('availableOrders', 'shipments')); } - - /** * Store new shipment */ @@ -115,8 +113,6 @@ class ShipmentController extends Controller return redirect()->back()->with('success', "Shipment $newShipmentId created successfully!"); } - - /** * Show shipment details (for modal popup) */ @@ -135,8 +131,6 @@ class ShipmentController extends Controller ]); } - - /** * Update Shipment status from action button */ @@ -164,5 +158,55 @@ class ShipmentController extends Controller ); } + /** + * Update shipment details + */ + public function update(Request $request, $id) + { + $shipment = Shipment::findOrFail($id); -} + $data = $request->validate([ + 'origin' => 'required|string', + 'destination' => 'required|string', + 'shipment_date' => 'required|date', + 'status' => 'required|string', + ]); + + $shipment->update($data); + + // If it's an AJAX request, return JSON response + if ($request->ajax() || $request->wantsJson()) { + return response()->json([ + 'success' => true, + 'message' => 'Shipment updated successfully.' + ]); + } + + return redirect()->back()->with('success', 'Shipment updated successfully.'); + } + + /** + * Delete shipment permanently + */ + public function destroy($id, Request $request) + { + $shipment = Shipment::findOrFail($id); + + // Delete shipment items + ShipmentItem::where('shipment_id', $shipment->id)->delete(); + + // Delete shipment itself + $shipment->delete(); + + // If it's an AJAX request, return JSON response + if ($request->ajax() || $request->wantsJson()) { + return response()->json([ + 'success' => true, + 'message' => 'Shipment deleted successfully.' + ]); + } + + return redirect()->route('admin.shipments') + ->with('success', 'Shipment deleted successfully.'); + } +} \ No newline at end of file diff --git a/app/Models/Order.php b/app/Models/Order.php index d3691c8..029cf35 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -4,11 +4,13 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; + class Order extends Model { - use HasFactory; - + use HasFactory,SoftDeletes; + protected $fillable = [ 'order_id', 'mark_no', diff --git a/app/Models/OrderItem.php b/app/Models/OrderItem.php index 75cd416..6d37939 100644 --- a/app/Models/OrderItem.php +++ b/app/Models/OrderItem.php @@ -4,10 +4,11 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; class OrderItem extends Model { - use HasFactory; + use HasFactory, SoftDeletes; protected $fillable = [ 'order_id', diff --git a/database/migrations/2025_11_24_131305_create_invoice_installments_table.php b/database/migrations/2025_11_24_131305_create_invoice_installments_table.php index f9e33cc..d1c540f 100644 --- a/database/migrations/2025_11_24_131305_create_invoice_installments_table.php +++ b/database/migrations/2025_11_24_131305_create_invoice_installments_table.php @@ -8,23 +8,16 @@ return new class extends Migration { public function up(): void { - Schema::create('invoice_installments', function (Blueprint $table) { - $table->id(); - - $table->unsignedBigInteger('invoice_id'); - $table->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade'); - - $table->date('installment_date'); - $table->string('payment_method')->nullable(); // cash, bank, UPI, cheque, etc - $table->string('reference_no')->nullable(); - $table->decimal('amount', 10, 2); - - $table->timestamps(); + // Table already exists. Add updates here if needed. + Schema::table('invoice_installments', function (Blueprint $table) { + // nothing to update }); } public function down(): void { - Schema::dropIfExists('invoice_installments'); + Schema::table('invoice_installments', function (Blueprint $table) { + // nothing to rollback + }); } }; diff --git a/database/migrations/2025_11_28_182057_add_soft_deletes_to_orders_table.php b/database/migrations/2025_11_28_182057_add_soft_deletes_to_orders_table.php new file mode 100644 index 0000000..4d837c8 --- /dev/null +++ b/database/migrations/2025_11_28_182057_add_soft_deletes_to_orders_table.php @@ -0,0 +1,22 @@ +softDeletes(); + }); + } + + public function down(): void + { + Schema::table('order_items', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + } +}; diff --git a/database/migrations/2025_11_29_045236_add_deleted_at_to_order_items_table.php b/database/migrations/2025_11_29_045236_add_deleted_at_to_order_items_table.php new file mode 100644 index 0000000..660229b --- /dev/null +++ b/database/migrations/2025_11_29_045236_add_deleted_at_to_order_items_table.php @@ -0,0 +1,26 @@ +softDeletes(); // adds deleted_at (nullable timestamp) + } + }); + } + + public function down(): void + { + Schema::table('order_items', function (Blueprint $table) { + if (Schema::hasColumn('order_items', 'deleted_at')) { + $table->dropSoftDeletes(); // drops deleted_at + } + }); + } +}; diff --git a/resources/views/admin/customers.blade.php b/resources/views/admin/customers.blade.php index 17162ce..3321dc2 100644 --- a/resources/views/admin/customers.blade.php +++ b/resources/views/admin/customers.blade.php @@ -5,6 +5,14 @@ @section('content')
- +
-

Customer List

+

Customer List

- -
-
-
{{ $allCustomers->count() }}
-
Total Customers
- + +
+ +
+
+ +
+
+
{{ $allCustomers->count() }}
+
Total Customers
+
-
-
- @php - $newThisMonth = $allCustomers->filter(function($customer) { - return $customer->created_at->format('Y-m') === now()->format('Y-m'); - })->count(); - @endphp - {{ $newThisMonth }} + +
+
+ +
+
+
+ @php + $newThisMonth = $allCustomers->filter(function($customer) { + return $customer->created_at->format('Y-m') === now()->format('Y-m'); + })->count(); + @endphp + {{ $newThisMonth }} +
+
New This Month
-
New This Month
-
-
-
- @php - $activeCustomers = $allCustomers->where('status', 'active')->count(); - @endphp - {{ $activeCustomers }} + +
+
+ +
+
+
+ @php + $activeCustomers = $allCustomers->where('status', 'active')->count(); + @endphp + {{ $activeCustomers }} +
+
Active Customers
-
Active Customers
-
-
-
- @php - $premiumCount = $allCustomers->where('customer_type', 'premium')->count(); - @endphp - {{ $premiumCount }} + +
+
+ +
+
+
+ @php + $premiumCount = $allCustomers->where('customer_type', 'premium')->count(); + @endphp + {{ $premiumCount }} +
+
Premium Customers
-
Premium Customers
-
- +
- +
@@ -511,7 +784,7 @@ name="search" value="{{ $search ?? '' }}" class="search-input" - placeholder="Search Customer Name, Email, or Phone..."> + placeholder="Search customers by name, email, or phone..."> @if(!empty($status)) @endif @@ -547,13 +820,15 @@
- + + + - + @@ -576,20 +851,27 @@ {{ $c->email }}
{{ $c->mobile_no }} -
- {{ $c->orders->count() }} orders • ₹{{ number_format($c->orders->sum('ttl_amount'), 2) }} total -
- + + + + + + - @@ -604,7 +886,7 @@ @empty - diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php index d209de2..c08ec6c 100644 --- a/resources/views/admin/dashboard.blade.php +++ b/resources/views/admin/dashboard.blade.php @@ -12,6 +12,7 @@ html, body { body, .container-fluid { background: #f4f7fc; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } .container-fluid { @@ -34,6 +35,7 @@ body, .container-fluid { letter-spacing: .018em; color: #18213e; margin-bottom: 2px; + font-family: 'Inter', sans-serif; } .dash-title-desc { @@ -42,6 +44,7 @@ body, .container-fluid { margin-bottom: 5px; font-weight: 500; letter-spacing: .01em; + font-family: 'Inter', sans-serif; } /* ===== STATS CARDS - RESPONSIVE GRID ===== */ @@ -127,12 +130,14 @@ body, .container-fluid { color:#63709b; font-weight:600; letter-spacing:.28px; + font-family: 'Inter', sans-serif; } .stats-value { font-size:1.25rem; font-weight:700; color:#194073; + font-family: 'Inter', sans-serif; } .stats-card:hover .stats-icon { @@ -170,6 +175,7 @@ body, .container-fluid { display: flex; align-items: center; gap: 11px; + font-family: 'Inter', sans-serif; } .order-mgmt-title i { @@ -191,6 +197,7 @@ body, .container-fluid { align-items: center; gap: 8px; font-family: inherit; + font-family: 'Inter', sans-serif; } .create-order-btn:hover { @@ -201,7 +208,7 @@ body, .container-fluid { .card-body, .order-mgmt-main { background: #fff; border-radius: 0 0 17px 17px; - padding:25px; + padding:20px; margin-top: 15px; } @@ -213,7 +220,7 @@ body, .container-fluid { margin-top: 15px; } -/* ===== TABLE STYLES ===== */ +/* ===== IMPROVED TABLE STYLES ===== */ .table thead tr { background: #feebbe !important; border-radius: 12px 12px 0 0; @@ -226,9 +233,10 @@ body, .container-fluid { font-weight: 700; color: #343535; letter-spacing: 0.02em; - font-size:15px; - padding-top: 12px; - padding-bottom: 10px; + font-size: 15px; + padding: 16px 12px; + font-family: 'Inter', sans-serif; + border-bottom: 2px solid #e9ecef; } .table thead th:first-child { border-radius: 9px 0 0 0;} @@ -239,20 +247,24 @@ body, .container-fluid { border-radius:9px; box-shadow:0 2px 12px #e2ebf941; border-collapse: separate; - border-spacing: 0 2px; + border-spacing: 0 8px; + font-family: 'Inter', sans-serif; + font-size: 14px; } .table-striped tbody tr { background: #fff; - border-radius: 6px; - box-shadow: 0 1px 3px rgba(0,0,0,0.04); - margin-bottom: 2px; + border-radius: 8px; + box-shadow: 0 2px 6px rgba(0,0,0,0.04); + margin-bottom: 8px; transition: all 0.3s ease; + height: 60px; } .table-striped tbody tr:hover { - box-shadow: 0 2px 6px rgba(0,0,0,0.08); - transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0,0,0,0.08); + transform: translateY(-2px); + background: #f8fafd; } .table-striped tbody tr:nth-of-type(odd) { @@ -264,39 +276,110 @@ body, .container-fluid { } .table td { - padding: 12px 6px; + padding: 14px 12px; border: none; position: relative; + vertical-align: middle; + font-family: 'Inter', sans-serif; + font-size: 14px; + font-weight: 500; + color: #495057; + line-height: 1.5; + border-bottom: 1px solid #f1f3f4; } .table td:first-child { - border-radius: 6px 0 0 6px; + border-radius: 8px 0 0 8px; + border-left: 3px solid transparent; } .table td:last-child { - border-radius: 0 6px 6px 0; + border-radius: 0 8px 8px 0; } .table-responsive { border-radius:10px; } -.badge { - font-size:13px; - font-weight:600; - padding:7px 17px; - border-radius:12px; +/* ===== UPDATED STATUS BADGE STYLES ===== */ +.badge { + padding: 7px 17px !important; + border-radius: 20px !important; + font-weight: 600 !important; + font-size: 13px !important; + border: 2px solid transparent !important; + min-width: 40px !important; + text-align: center !important; + display: inline-flex !important; + align-items: center; + justify-content: center; + line-height: 1.2 !important; + font-family: 'Inter', sans-serif; + gap: 6px; + width: 110px; } -.bg-info { - background-color:#22cbfa!important; - color:#fff!important; +.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; + border-color: #f59e0b !important; +} + +/* In Transit Status - SAME SIZE WITH TRUCK ICON */ +.badge-in_transit { + background: linear-gradient(135deg, #dbeafe, #93c5fd) !important; + color: #1e40af !important; + border-color: #3b82f6 !important; +} + +/* Dispatched Status - SAME SIZE WITH BOX ICON */ +.badge-dispatched { + background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important; + color: #6b21a8 !important; + border-color: #8b5cf6 !important; +} + +/* Delivered Status - SAME SIZE WITH CHECK ICON */ +.badge-delivered { + background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important; + color: #065f46 !important; + border-color: #10b981 !important; +} + +/* Default badge styles - SAME SIZE */ +.badge.bg-info { + background: linear-gradient(135deg, #4cc9f0, #4361ee) !important; + color: white !important; +} + +.badge.bg-success { + background: linear-gradient(135deg, #4ade80, #22c55e) !important; + color: white !important; +} + +.badge.bg-warning { + background: linear-gradient(135deg, #fbbf24, #f59e0b) !important; + color: white !important; +} + +.badge.bg-danger { + background: linear-gradient(135deg, #f87171, #ef4444) !important; + color: white !important; } .form-label { font-weight:600; color:#1d3159; font-size:15px; + font-family: 'Inter', sans-serif; } .form-control, .form-select { @@ -305,6 +388,7 @@ body, .container-fluid { background: #f7f9fe; border:1.2px solid #c7dbfa; font-weight:500; + font-family: 'Inter', sans-serif; } .form-control:focus, .form-select:focus { @@ -316,6 +400,7 @@ body, .container-fluid { color:#2469d6; font-weight:600; text-decoration:underline; + font-family: 'Inter', sans-serif; } /* ===== HORIZONTAL SCROLL CONTAINER ===== */ @@ -327,7 +412,7 @@ body, .container-fluid { border-radius: 12px; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,0.08); - padding: 8px; + padding: 12px; } .table-wrapper::-webkit-scrollbar { @@ -348,7 +433,7 @@ body, .container-fluid { } .table { - min-width: 1200px; + min-width: 2000px; border-radius: 10px; } @@ -394,6 +479,7 @@ body, .container-fluid { font-size: 1.4rem; font-weight: 700; margin: 0; + font-family: 'Inter', sans-serif; } .create-order-modal .close-btn { @@ -425,6 +511,7 @@ body, .container-fluid { color: #1d3159; font-size: 15px; margin-bottom: 5px; + font-family: 'Inter', sans-serif; } .create-order-modal .form-control, @@ -435,6 +522,7 @@ body, .container-fluid { font-size: 15px; font-weight: 500; padding: 8px 12px; + font-family: 'Inter', sans-serif; } .create-order-modal .form-control:focus, @@ -449,6 +537,7 @@ body, .container-fluid { padding: 10px 20px; font-weight: 600; border-radius: 8px; + font-family: 'Inter', sans-serif; } .create-order-modal .btn-info:hover { @@ -461,6 +550,7 @@ body, .container-fluid { padding: 12px 30px; font-weight: 600; border-radius: 8px; + font-family: 'Inter', sans-serif; } .create-order-modal .btn-success:hover { @@ -472,6 +562,7 @@ body, .container-fluid { border: none; color: #000; font-weight: 600; + font-family: 'Inter', sans-serif; } .create-order-modal .btn-warning:hover { @@ -481,6 +572,7 @@ body, .container-fluid { .create-order-modal .btn-danger { background: #dc3545; border: none; + font-family: 'Inter', sans-serif; } .create-order-modal .btn-danger:hover { @@ -505,6 +597,7 @@ body, .container-fluid { position: sticky; top: 0; z-index: 10; + font-family: 'Inter', sans-serif; } /* ===== ORDER DETAILS MODAL STYLES ===== */ @@ -536,6 +629,7 @@ body, .container-fluid { font-weight: 700; color: white; margin: 0; + font-family: 'Inter', sans-serif; } .modal-header .btn-close { @@ -554,6 +648,7 @@ body, .container-fluid { background: #f8fafc; max-height: 70vh; overflow-y: auto; + font-family: 'Inter', sans-serif; } /* ===== PAGINATION STYLES ===== */ @@ -570,6 +665,7 @@ body, .container-fluid { font-size: 13px; color: #9ba5bb; font-weight: 600; + font-family: 'Inter', sans-serif; } .pagination-controls { @@ -593,6 +689,7 @@ body, .container-fluid { justify-content: center; min-width: 40px; height: 32px; + font-family: 'Inter', sans-serif; } .pagination-btn:hover:not(:disabled) { @@ -621,6 +718,7 @@ body, .container-fluid { transition: all 0.3s ease; min-width: 36px; text-align: center; + font-family: 'Inter', sans-serif; } .pagination-page-btn:hover { @@ -645,6 +743,7 @@ body, .container-fluid { color: #9ba5bb; font-size: 13px; padding: 0 4px; + font-family: 'Inter', sans-serif; } .pagination-img-btn { @@ -728,7 +827,7 @@ body, .container-fluid { .table th, .table td { - padding: 10px 5px; + padding: 12px 8px; } .pagination-container { @@ -811,7 +910,7 @@ body, .container-fluid { .table th, .table td { - padding: 8px 4px; + padding: 10px 6px; } .badge { @@ -913,7 +1012,7 @@ body, .container-fluid { .table th, .table td { - padding: 6px 3px; + padding: 8px 4px; } .form-control, .form-select { @@ -1047,67 +1146,80 @@ body, .container-fluid {
-
Customer Info Customer IDOrdersTotal Create Date StatusActionsActions
+ {{ $c->customer_id }} + {{ $c->orders->count() }} + + ₹{{ number_format($c->orders->sum('ttl_amount'), 2) }} + + {{ $c->created_at ? $c->created_at->format('d-m-Y') : '-' }} -
+ No customers found.
- - - - - - - - - - - - - - - - - - - - - - @forelse($orders as $order) - - - - - - - - - - - - - - - +
+
#Order IDMark NoOriginDestinationTotal CTNTotal QTYTotal TTL/QTYTotal Amount (₹)Total CBMTotal TTL CBMTotal KGTotal TTL KGStatusDateActions
{{ $order->id }} - - {{ $order->order_id }} - - {{ $order->mark_no }}{{ $order->origin }}{{ $order->destination }}{{ $order->ctn }}{{ $order->qty }}{{ $order->ttl_qty }}₹{{ number_format($order->ttl_amount, 2) }}{{ $order->cbm }}{{ $order->ttl_cbm }}{{ $order->kg }}{{ $order->ttl_kg }}
+ + + + + + + + + + + + + + + + + + + + + + @forelse($orders as $order) + + - - - - @empty - - - - @endforelse - -
#Order IDMark NoOriginDestinationTotal CTNTotal QTYTotal TTL/QTYTotal Amount (₹)Total CBMTotal TTL CBMTotal KGTotal TTL KGStatusDateActions
{{ $order->id }} - {{ ucfirst($order->status) }} - {{ $order->created_at->format('d-m-Y') }} - - View + + {{ $order->order_id }}
No orders found
+ + {{ $order->mark_no }} + {{ $order->origin }} + {{ $order->destination }} + {{ $order->ctn }} + {{ $order->qty }} + {{ $order->ttl_qty }} + ₹{{ number_format($order->ttl_amount, 2) }} + {{ $order->cbm }} + {{ $order->ttl_cbm }} + {{ $order->kg }} + {{ $order->ttl_kg }} + + + @if($order->status == 'pending') + + @elseif($order->status == 'in_transit') + + @elseif($order->status == 'dispatched') + + @elseif($order->status == 'delivered') + + @endif + {{ ucfirst(str_replace('_', ' ', $order->status)) }} + + + {{ $order->created_at->format('d-m-Y') }} + + + View + + + + @empty + + No orders found + + @endforelse + + +
@@ -1566,7 +1678,13 @@ document.addEventListener('DOMContentLoaded', function() { ${order.kg || ''} ${order.ttl_kg || ''} - ${order.status ? order.status.charAt(0).toUpperCase() + order.status.slice(1) : ''} + + ${order.status === 'pending' ? '' : ''} + ${order.status === 'in_transit' ? '' : ''} + ${order.status === 'dispatched' ? '' : ''} + ${order.status === 'delivered' ? '' : ''} + ${order.status ? order.status.charAt(0).toUpperCase() + order.status.slice(1).replace('_', ' ') : ''} + ${new Date(order.created_at).toLocaleDateString('en-GB')} diff --git a/resources/views/admin/invoice.blade.php b/resources/views/admin/invoice.blade.php index 8582bd9..84aa37a 100644 --- a/resources/views/admin/invoice.blade.php +++ b/resources/views/admin/invoice.blade.php @@ -4,1566 +4,1659 @@ @section('content')
- -
-
- - Invoice Management - -
- - -
- - - - -
- - - -
- - to - + +
+
+ + Invoice Management +
-
- - - Create Invoice - -
+ +
+ +
+ + + +
-
- -
-
-
{{ $invoices->count() }}
-
Total Invoices
-
-
-
₹{{ number_format($invoices->sum('final_amount_with_gst'), 2) }}
-
Total Revenue
-
-
-
{{ $invoices->where('status', 'paid')->count() }}
-
Paid Invoices
-
-
-
{{ $invoices->where('status', 'pending')->count() }}
-
Pending Invoices
-
-
+ +
+ - -
-
-

All Invoices

- - {{ $invoices->count() }} invoices - -
- -
-
- - - - - - - - - - - - - - - + +
+ + to + +
+ - + + + + +
+ +
+
+
{{ $invoices->count() }}
+
Total Invoices
+
+
+
₹{{ number_format($invoices->sum('final_amount_with_gst'), 2) }}
+
Total Revenue
+
+
+
{{ $invoices->where('status', 'paid')->count() }}
+
Paid Invoices
+
+
+
{{ $invoices->where('status', 'pending')->count() }}
+
Pending Invoices
+
+
+ + +
+
+

All Invoices

+ + {{ $invoices->count() }} invoices + +
+ +
+
+
#Invoice NumberCustomerFinal AmountGST %Total w/GSTStatusInvoice DateDue DateAction
+ + + + + + + + + + + + + + + + + @php + $totalInvoices = $invoices->count(); + $sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first + @endphp + + @forelse($sortedInvoices as $i => $invoice) + + + + + + + + + + + + + + + + + + + @empty + + + + @endforelse + +
#Invoice NumberCustomerFinal AmountGST %Total w/GSTStatusInvoice DateDue DateAction
{{ $totalInvoices - $i }} + + {{ $invoice->customer_name }}₹{{ number_format($invoice->final_amount, 2) }}{{ $invoice->gst_percent }}%₹{{ number_format($invoice->final_amount_with_gst, 2) }} + + @if($invoice->status == 'paid') + + @elseif($invoice->status == 'pending') + + @elseif($invoice->status == 'overdue') + + @endif + {{ ucfirst($invoice->status) }} + + {{ $invoice->invoice_date }}{{ $invoice->due_date }} + + Entry + +
No invoices found
+
+
+
+ + +
@php $totalInvoices = $invoices->count(); $sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first @endphp @forelse($sortedInvoices as $i => $invoice) - - {{ $totalInvoices - $i }} - - -
-
- -
- - {{ $invoice->invoice_number }} - +
+
+
+
+ +
+ {{ $invoice->invoice_number }} +
+ + @if($invoice->status == 'paid') + + @elseif($invoice->status == 'pending') + + @elseif($invoice->status == 'overdue') + + @endif + {{ ucfirst($invoice->status) }} +
- - - {{ $invoice->customer_name }} - - ₹{{ number_format($invoice->final_amount, 2) }} - {{ $invoice->gst_percent }}% - ₹{{ number_format($invoice->final_amount_with_gst, 2) }} - - - - @if($invoice->status == 'paid') - - @elseif($invoice->status == 'pending') - - @elseif($invoice->status == 'overdue') - - @endif - {{ ucfirst($invoice->status) }} - - - - {{ $invoice->invoice_date }} - {{ $invoice->due_date }} - - - - Entry - - - + +
+
+ Customer + {{ $invoice->customer_name }} +
+
+ Amount + ₹{{ number_format($invoice->final_amount, 2) }} +
+
+ GST + {{ $invoice->gst_percent }}% +
+
+ Total + ₹{{ number_format($invoice->final_amount_with_gst, 2) }} +
+
+ Invoice Date + {{ $invoice->invoice_date }} +
+
+ Due Date + {{ $invoice->due_date }} +
+
+ + +
@empty - - No invoices found - +
No invoices found
@endforelse - - -
-
-
+
- -
- @php - $totalInvoices = $invoices->count(); - $sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first - @endphp - - @forelse($sortedInvoices as $i => $invoice) -
-
-
-
- -
- {{ $invoice->invoice_number }} + +
+
Showing 1 to {{ $invoices->count() }} of {{ $invoices->count() }} entries
+
+ +
+ +
+ +
- - @if($invoice->status == 'paid') - - @elseif($invoice->status == 'pending') - - @elseif($invoice->status == 'overdue') - - @endif - {{ ucfirst($invoice->status) }} - -
- -
-
- Customer - {{ $invoice->customer_name }} -
-
- Amount - ₹{{ number_format($invoice->final_amount, 2) }} -
-
- GST - {{ $invoice->gst_percent }}% -
-
- Total - ₹{{ number_format($invoice->final_amount_with_gst, 2) }} -
-
- Invoice Date - {{ $invoice->invoice_date }} -
-
- Due Date - {{ $invoice->due_date }} -
-
- -
- @empty -
No invoices found
- @endforelse -
- - -
-
Showing 1 to {{ $invoices->count() }} of {{ $invoices->count() }} entries
-
- -
- -
- -
-
-
{{-- POPUP MODAL --}}