diff --git a/app/Http/Controllers/Admin/AdminCustomerController.php b/app/Http/Controllers/Admin/AdminCustomerController.php new file mode 100644 index 0000000..393db9a --- /dev/null +++ b/app/Http/Controllers/Admin/AdminCustomerController.php @@ -0,0 +1,134 @@ +search; + $status = $request->status; + + $query = User::with(['marks', 'orders'])->orderBy('id', 'desc'); + + // SEARCH FILTER + if (!empty($search)) { + $query->where(function ($q) use ($search) { + $q->where('customer_name', 'like', "%$search%") + ->orWhere('email', 'like', "%$search%") + ->orWhere('mobile_no', 'like', "%$search%") + ->orWhere('customer_id', 'like', "%$search%"); + }); + } + + // STATUS FILTER + if (!empty($status) && in_array($status, ['active', 'inactive'])) { + $query->where('status', $status); + } + + $customers = $query->get(); + + return view('admin.customers', compact('customers', 'search', 'status')); + } + + // --------------------------------------------------------- + // SHOW ADD CUSTOMER FORM + // --------------------------------------------------------- + public function create() + { + return view('admin.customers_add'); + } + + // --------------------------------------------------------- + // STORE NEW CUSTOMER + // --------------------------------------------------------- + public function store(Request $request) + { + $request->validate([ + 'customer_name' => 'required|string|max:255', + 'company_name' => 'nullable|string|max:255', + 'designation' => 'nullable|string|max:255', + 'email' => 'required|email|unique:users,email', + 'mobile_no' => 'required|string|max:20', + 'address' => 'nullable|string', + 'pincode' => 'nullable|string|max:10', + 'customer_type' => 'required|in:regular,premium', + 'status' => 'required|in:active,inactive', + ]); + + // AUTO GENERATE CUSTOMER ID + $year = date('Y'); + $prefix = "CID-$year-"; + + $lastCustomer = User::whereYear('created_at', $year) + ->orderBy('id', 'DESC') + ->first(); + + $next = $lastCustomer ? intval(substr($lastCustomer->customer_id, -6)) + 1 : 1; + $customerId = $prefix . str_pad($next, 6, '0', STR_PAD_LEFT); + + // CREATE CUSTOMER + User::create([ + 'customer_id' => $customerId, + 'customer_name' => $request->customer_name, + 'company_name' => $request->company_name, + 'designation' => $request->designation, + 'email' => $request->email, + 'mobile_no' => $request->mobile_no, + 'address' => $request->address, + 'pincode' => $request->pincode, + 'date' => date('Y-m-d'), + 'customer_type' => $request->customer_type, + 'status' => $request->status, + 'password' => Hash::make('123456'), // DEFAULT PASSWORD + ]); + + return redirect() + ->route('admin.customers.index') + ->with('success', 'Customer added successfully!'); + } + + // --------------------------------------------------------- + // VIEW CUSTOMER FULL DETAILS + // --------------------------------------------------------- + public function view($id) + { + $customer = User::with(['marks', 'orders'])->findOrFail($id); + + $totalOrders = $customer->orders->count(); + $totalAmount = $customer->orders->sum('ttl_amount'); + $recentOrders = $customer->orders()->latest()->take(5)->get(); + + return view('admin.customers_view', compact( + 'customer', + 'totalOrders', + 'totalAmount', + 'recentOrders' + )); + } + + // --------------------------------------------------------- + // TOGGLE STATUS ACTIVE / INACTIVE + // --------------------------------------------------------- + public function toggleStatus($id) + { + $customer = User::findOrFail($id); + + $customer->status = $customer->status === 'active' + ? 'inactive' + : 'active'; + + $customer->save(); + + return back()->with('success', 'Customer status updated.'); + } + +} diff --git a/app/Models/User.php b/app/Models/User.php index d9f952d..b5df7a5 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -15,7 +15,7 @@ class User extends Authenticatable implements JWTSubject * The attributes that are mass assignable. */ protected $fillable = [ - 'customer_id', // CID-2025-000001 format + 'customer_id', 'customer_name', 'company_name', 'designation', @@ -25,10 +25,15 @@ class User extends Authenticatable implements JWTSubject 'pincode', 'date', 'password', + + // newly added customer fields + 'status', // active / inactive + 'customer_type', // premium / regular + 'profile_image', // optional image ]; /** - * The attributes that should be hidden for arrays. + * Attributes that should be hidden. */ protected $hidden = [ 'password', @@ -36,7 +41,7 @@ class User extends Authenticatable implements JWTSubject ]; /** - * The attributes that should be cast. + * Attribute casting. */ protected function casts(): array { @@ -47,7 +52,30 @@ class User extends Authenticatable implements JWTSubject } /** - * JWT Identifier. + * Relationship: User → MarkList (Many) + */ + public function marks() + { + return $this->hasMany(\App\Models\MarkList::class, 'customer_id', 'customer_id'); + } + + /** + * Relationship: User → Orders (Through MarkList) + */ + public function orders() + { + return $this->hasManyThrough( + \App\Models\Order::class, + \App\Models\MarkList::class, + 'customer_id', // MarkList.customer_id + 'mark_no', // Orders.mark_no + 'customer_id', // Users.customer_id + 'mark_no' // MarkList.mark_no + ); + } + + /** + * JWT Identifier */ public function getJWTIdentifier() { @@ -55,7 +83,7 @@ class User extends Authenticatable implements JWTSubject } /** - * JWT Custom Claims. + * JWT Custom Claims */ public function getJWTCustomClaims() { diff --git a/database/migrations/2025_11_17_071001_add_customer_fields_to_users_table.php b/database/migrations/2025_11_17_071001_add_customer_fields_to_users_table.php new file mode 100644 index 0000000..574464e --- /dev/null +++ b/database/migrations/2025_11_17_071001_add_customer_fields_to_users_table.php @@ -0,0 +1,31 @@ +enum('status', ['active', 'inactive'])->default('active')->after('pincode'); + + // premium / regular + $table->enum('customer_type', ['regular', 'premium'])->default('regular')->after('status'); + + // optional: profile image path + $table->string('profile_image')->nullable()->after('customer_type'); + }); + } + + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn(['status', 'customer_type', 'profile_image']); + }); + } + +}; diff --git a/public/invoices/invoice-INV-2025-000005.pdf b/public/invoices/invoice-INV-2025-000005.pdf new file mode 100644 index 0000000..f30024c Binary files /dev/null and b/public/invoices/invoice-INV-2025-000005.pdf differ diff --git a/public/invoices/invoice-INV-2025-000006.pdf b/public/invoices/invoice-INV-2025-000006.pdf new file mode 100644 index 0000000..f1d586a Binary files /dev/null and b/public/invoices/invoice-INV-2025-000006.pdf differ diff --git a/public/invoices/invoice-INV-2025-000007.pdf b/public/invoices/invoice-INV-2025-000007.pdf new file mode 100644 index 0000000..4425ec2 Binary files /dev/null and b/public/invoices/invoice-INV-2025-000007.pdf differ diff --git a/public/invoices/invoice-INV-2025-000008.pdf b/public/invoices/invoice-INV-2025-000008.pdf new file mode 100644 index 0000000..8c6af73 Binary files /dev/null and b/public/invoices/invoice-INV-2025-000008.pdf differ diff --git a/resources/views/admin/customers.blade.php b/resources/views/admin/customers.blade.php index 127e252..f5a787e 100644 --- a/resources/views/admin/customers.blade.php +++ b/resources/views/admin/customers.blade.php @@ -1,12 +1,420 @@ @extends('admin.layouts.app') -@section('page-title', 'Dashboard') +@section('page-title', 'Customers') @section('content') -
-
-

Welcome to the Admin customer page

-

Here you can manage all system modules.

-
+ + + +
+ +
+

Customer List

+
+ + +
+
+
{{ $totalCustomers ?? '1,247' }}
+
Total Customers
+ +
+ +
+
{{ $newThisMonth ?? '342' }}
+
New This Month
+ +
+ +
+
{{ $activeThisMonth ?? '123' }}
+
Active This Month
+ +
+ +
+
{{ $premiumCount ?? '23' }}
+
Premium Customers
+ +
+
+ + +
+
+
+
+
+ + + @if(!empty($status)) + + @endif +
+
+
+ +
+
+ + +
+
+
Customer List
+
+ +
+
+ + + + + + + + + + + + @forelse($customers as $c) + + + + + + + + + + + + + + + + + @empty + + + + @endforelse + +
Customer InfoCustomer IDCreate DateStatusActions
+
+
+ {{ strtoupper(substr($c->customer_name,0,1)) }} +
+
+
{{ $c->customer_name }}
+ @if($c->customer_type == 'premium') + Premium Customer + @else + Regular Customer + @endif +
+ {{ $c->email }}
+ {{ $c->mobile_no }} +
+
+ {{ $c->orders->count() }} orders • ₹{{ number_format($c->orders->sum('ttl_amount'), 2) }} total +
+
+
+
+ {{ $c->customer_id }} + + {{ $c->created_at ? $c->created_at->format('d-m-Y') : '-' }} + + @if($c->status === 'active') + Active + @else + Inactive + @endif + +
+ + + + +
+ @csrf + +
+
+
+ + No customers found. +
+
+
+
-@endsection + +@endsection \ No newline at end of file diff --git a/resources/views/admin/customers_add.blade.php b/resources/views/admin/customers_add.blade.php new file mode 100644 index 0000000..27c0169 --- /dev/null +++ b/resources/views/admin/customers_add.blade.php @@ -0,0 +1,542 @@ +@extends('admin.layouts.app') + +@section('page-title', 'Add Customer') + +@section('content') + + + +
+ +
+
+ +
+

+ + Add New Customer +

+
+ + +
+
+ @csrf + +
+ +
+ + +
Minimum 2 characters, letters only
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
Valid email format required
+
+ + +
+ + +
10 digits without spaces
+
+ + +
+ + +
6-digit pincode
+
+ + +
+ + +
+ + +
+ + +
Premium customers get special benefits
+
+ + +
+ + +
Active customers can place orders
+
+
+ + +
+ + + Cancel + + +
+
+
+
+
+
+ + + +@endsection \ No newline at end of file diff --git a/resources/views/admin/customers_view.blade.php b/resources/views/admin/customers_view.blade.php new file mode 100644 index 0000000..dc56665 --- /dev/null +++ b/resources/views/admin/customers_view.blade.php @@ -0,0 +1,784 @@ +@extends('admin.layouts.app') + +@section('page-title', 'Customer Details') + +@section('content') + + + +
+ + {{-- HEADER - FIXED BUTTON POSITION --}} + + + {{-- CUSTOMER PROFILE CARD --}} +
+
+
+
+
+
+ {{ strtoupper(substr($customer->customer_name,0,1)) }} +
+
+
+
+
+
+

{{ $customer->customer_name }}

+ @if($customer->customer_type == 'premium') + + Premium + + @else + + Regular + + @endif +
+

+ + {{ $customer->company_name ?? 'No company specified' }} +

+
+ + + {{ ucfirst($customer->status) }} + + + + Joined {{ $customer->created_at ? $customer->created_at->format('M d, Y') : 'N/A' }} + +
+
+
+
+ + {{-- CUSTOMER INFORMATION --}} +
+ {{-- Contact Information --}} +
+
+
Contact Information
+
+
+ + Email: + {{ $customer->email }} +
+
+ + Mobile: + {{ $customer->mobile_no }} +
+
+ + Address: + {{ $customer->address ?? 'N/A' }} +
+
+ + Pincode: + {{ $customer->pincode ?? 'N/A' }} +
+
+
+
+ + {{-- Account Information --}} +
+
+
Account Information
+
+
+ + Customer ID: + {{ $customer->customer_id }} +
+
+ + Registered On: + {{ $customer->created_at ? $customer->created_at->format('d M, Y') : '-' }} +
+
+ + Designation: + {{ $customer->designation ?? 'N/A' }} +
+
+
+
+
+
+ + {{-- STATISTICS --}} +
+ {{-- Total Orders --}} +
+
+
+ +
+
{{ $totalOrders }}
+
Total Orders
+
+
+ + {{-- Total Amount --}} +
+
+
+ +
+
₹{{ number_format($totalAmount, 2) }}
+
Total Amount Spent
+
+
+ + {{-- Mark Count --}} +
+
+
+ +
+
{{ $customer->marks->count() }}
+
Mark Numbers
+
+
+
+ + {{-- MARK NUMBERS SECTION --}} +
+
+
+ + Customer Mark Numbers + {{ $customer->marks->count() }} +
+
+ +
+ @if($customer->marks->count() == 0) +
+ +

No mark numbers found for this customer.

+
+ @else + @foreach($customer->marks as $index => $mark) +
+
+ {{ $mark->mark_no }} +
+
+ + {{ $mark->origin }} → {{ $mark->destination }} +
+
+ @endforeach + @endif +
+
+ +
+ + + +@endsection \ No newline at end of file diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php index 9f066d1..b29e4d5 100644 --- a/resources/views/admin/dashboard.blade.php +++ b/resources/views/admin/dashboard.blade.php @@ -4,18 +4,30 @@ @section('content')
@@ -755,68 +901,70 @@ body, .container-fluid { background: #f4f7fc; }
Recent Orders
-
- - - - - - - - - - - - - - - - - - - - - - - @forelse($orders as $order) - - - +
+
+
#Order IDMark NoOriginDestinationTotal CTNTotal QTYTotal TTL/QTYTotal Amount (₹)Total CBMTotal TTL CBMTotal KGTotal TTL KGStatusDateAction
{{ $order->id }} - - {{ $order->order_id }} - -
+ + + + + + + + + + + + + + + + + + + + + + @forelse($orders as $order) + + + - - - - - - - - - - - - - - - - @empty - - - - @endforelse - -
#Order IDMark NoOriginDestinationTotal CTNTotal QTYTotal TTL/QTYTotal Amount (₹)Total CBMTotal TTL CBMTotal KGTotal TTL KGStatusDateAction
{{ $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 }} - {{ ucfirst($order->status) }} - {{ $order->created_at->format('d-m-Y') }} - - View - -
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 }} + + {{ ucfirst($order->status) }} + + {{ $order->created_at->format('d-m-Y') }} + + + View + + + + @empty + + No orders found + + @endforelse + + +
@@ -997,6 +1145,22 @@ body, .container-fluid { background: #f4f7fc; } + + + - - + document.getElementById('orderDetailsBody').innerHTML = + "

Loading...

"; - - @endsection \ No newline at end of file diff --git a/resources/views/admin/invoice.blade.php b/resources/views/admin/invoice.blade.php index 85f36a6..9bcc41c 100644 --- a/resources/views/admin/invoice.blade.php +++ b/resources/views/admin/invoice.blade.php @@ -173,7 +173,7 @@ width: 100%; min-width: 1100px; border-collapse: separate; - border-spacing: 0; + border-spacing: 0 8px; /* Add gap between rows */ margin-bottom: 0; } @@ -189,7 +189,6 @@ border-top-right-radius: 17px; } - .table thead th { background: transparent !important; border: none; @@ -197,7 +196,7 @@ color: #343535; letter-spacing: 0.02em; font-size: 13px; - padding: 12px 8px; + padding: 16px 12px; white-space: nowrap; position: relative; border-bottom: 2px solid #e8e2cf; @@ -206,50 +205,58 @@ } .table thead th:first-child { - padding-left: 20px; + padding-left: 25px; } .table thead th:last-child { - padding-right: 20px; + padding-right: 25px; } +/* Soft blue background for ALL table rows */ .table-striped tbody tr { - background: #fff; + background: #f0f8ff !important; /* Soft blue background for all rows */ transition: all 0.2s ease; - border-bottom: 1px solid #f1f3f4; + border-radius: 12px; /* Rounded corners for each row */ + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); /* Subtle shadow for separation */ } .table-striped tbody tr:hover { - background: #f8fafc !important; -} - -.table-striped tbody tr:nth-of-type(odd) { - background: #f9fafc; + background: #e6f3ff !important; /* Slightly darker blue on hover */ + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); /* Enhanced shadow on hover */ + transform: translateY(-1px); /* Lift effect on hover */ } +/* Remove striped pattern - all rows same soft blue */ +.table-striped tbody tr:nth-of-type(odd), .table-striped tbody tr:nth-of-type(even) { - background: #ffffff; + background: #f0f8ff !important; /* Same soft blue for all rows */ } -/* Center all table cells */ +/* Center all table cells with proper spacing */ .table td { - padding: 10px 8px; + padding: 16px 12px; /* Increased vertical padding */ border: none; vertical-align: middle; white-space: nowrap; font-size: 13px; color: #4a5568; - border-bottom: 1px solid #f1f3f4; text-align: center !important; /* Center all cell content */ + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; } +/* First and last cell rounded corners */ .table td:first-child { - padding-left: 20px; + padding-left: 25px; font-weight: 600; color: #2d3748; + border-top-left-radius: 12px; + border-bottom-left-radius: 12px; } .table td:last-child { - padding-right: 20px; + padding-right: 25px; + border-top-right-radius: 12px; + border-bottom-right-radius: 12px; } /* Invoice Number with Curved Boxes */ @@ -257,13 +264,13 @@ display: flex; align-items: center; justify-content: center; /* Center the invoice number */ - gap: 8px; - padding: 6px 0; + gap: 12px; + padding: 8px 0; } .invoice-icon { - width: 32px; - height: 32px; + width: 36px; + height: 36px; border-radius: 8px; /* Curved border radius */ display: flex; align-items: center; @@ -324,31 +331,67 @@ text-decoration: none; } -/* Badge Styling - Centered */ +/* Badge Styling - Centered with custom backgrounds and icons */ .badge { - font-size: 11px; - font-weight: 600; - padding: 4px 10px; - border-radius: 8px; + font-size: 11px !important; + font-weight: 600 !important; + padding: 6px 12px 6px 8px !important; + border-radius: 20px !important; text-transform: uppercase; letter-spacing: 0.3px; - display: inline-block; - text-align: center; + display: inline-flex !important; + align-items: center; + justify-content: center; + background-size: cover !important; + background-position: center !important; + color: #fff !important; + text-shadow: 0 1px 2px rgba(0,0,0,0.3); + border: 2px solid transparent !important; + min-width: 40px; + box-sizing: border-box; + line-height: 1.2; + gap: 6px; } -.bg-success { - background: linear-gradient(135deg, #18ce77 60%, #08ac52 110%) !important; - color: #fff !important; +/* Status icons */ +.status-icon { + font-size: 12px; + display: flex; + align-items: center; + justify-content: center; } -.bg-warning { - background: linear-gradient(135deg, #ffb23c 53%, #e17800 110%) !important; - color: #fff !important; +/* Custom status badge backgrounds with icons */ +.badge-paid { + background: url('/images/status-bg-paid.png') !important; } -.bg-danger { - background: linear-gradient(135deg, #ff5a4e 65%, #d90010 110%) !important; - color: #fff !important; +.badge-pending { + background: url('/images/status-bg-pending.png') !important; +} + +.badge-overdue { + background: url('/images/status-bg-overdue.png') !important; +} + +/* Fallback colors if images don't load - ALL WITH SAME SIZE */ +.badge.badge-paid { + background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important; + color: #065f46 !important; + border-color: #10b981 !important; + width: 98px; +} + +.badge.badge-pending { + background: linear-gradient(135deg, #fef3c7, #fde68a) !important; + color: #d97706 !important; + border-color: #f59e0b !important; +} + +.badge.badge-overdue { + background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important; + color: #6b21a8 !important; + border-color: #8b5cf6 !important; } /* Action Button - Centered */ @@ -356,7 +399,7 @@ background: linear-gradient(135deg, #3492f8 55%, #1256cc 110%); border: none; border-radius: 6px; - padding: 5px 10px; + padding: 6px 12px; font-weight: 600; font-size: 12px; transition: all 0.3s ease; @@ -461,102 +504,33 @@ overflow-y: auto; } -/* Responsive Design */ -@media (max-width: 1200px) { - .invoice-tools-row { - flex-direction: column; - align-items: stretch; - gap: 15px; - } - - .search-box { - max-width: 100%; - min-width: auto; - } - - .filter-group { - justify-content: space-between; - width: 100%; - } +/* Date Range Picker Styles */ +.date-range-container { + display: flex; + align-items: center; + gap: 8px; } -@media (max-width: 768px) { - .invoice-management-bar { - padding: 12px 20px; - } - - .invoice-tools-row { - padding: 15px; - } - - .table th, .table td { - font-size: 12px; - padding: 8px 6px; - } - - .badge { - font-size: 10px; - padding: 3px 8px; - } - - .btn-primary { - padding: 4px 8px; - font-size: 11px; - } - - .filter-group { - flex-direction: column; - align-items: stretch; - } - - .filter-select { - min-width: auto; - width: 100%; - } - - .create-invoice-btn { - width: 100%; - justify-content: center; - } - - .invoice-icon { - width: 28px; - height: 28px; - font-size: 12px; - } -} - -/* Empty State - Centered */ -.text-muted { - color: #8a9bb9 !important; - font-style: italic; - padding: 30px !important; - text-align: center; +.date-input { + background: white; + border: 1px solid #e2e8f0; + border-radius: 8px; + padding: 8px 12px; font-size: 14px; + color: #334155; + outline: none; + min-width: 130px; + box-shadow: 0 2px 5px rgba(0,0,0,0.04); } -/* Card Styling */ -.card { - border-radius: 0 0 17px 17px; - border: none; - margin-bottom: 0 !important; - box-shadow: none; +.date-input:focus { + border-color: #3b82f6; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); } -.card-header { - background: #f8f9fa !important; - border-bottom: 1px solid #e9ecef; - border-radius: 0 !important; - padding: 15px 25px; - text-align: center; -} - -.card-header h4 { - margin: 0; - color: #2451af; - font-weight: 700; - font-size: 1.3rem; - text-align: center; +.date-separator { + color: #64748b; + font-weight: 500; } /* Stats Summary - Centered */ @@ -622,6 +596,262 @@ .table tbody tr td .btn-primary { margin: 0 auto; } + +/* Empty State - Centered */ +.text-muted { + color: #8a9bb9 !important; + font-style: italic; + padding: 30px !important; + text-align: center; + font-size: 14px; +} + +/* Card Styling */ +.card { + border-radius: 0 0 17px 17px; + border: none; + margin-bottom: 0 !important; + box-shadow: none; +} + +.card-header { + background: #f8f9fa !important; + border-bottom: 1px solid #e9ecef; + border-radius: 0 !important; + padding: 15px 25px; + text-align: center; +} + +.card-header h4 { + margin: 0; + color: #2451af; + font-weight: 700; + font-size: 1.3rem; + text-align: center; +} + +/* Mobile Responsive Styles */ +.mobile-invoice-card { + display: none; + background: #f0f8ff; + border-radius: 12px; + padding: 15px; + margin-bottom: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); + border-left: 4px solid #667eea; +} + +.mobile-invoice-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + padding-bottom: 8px; + border-bottom: 1px solid #e2e8f0; +} + +.mobile-invoice-number { + display: flex; + align-items: center; + gap: 8px; +} + +.mobile-invoice-icon { + width: 32px; + height: 32px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 12px; + font-weight: bold; +} + +.mobile-invoice-number-text { + font-weight: 600; + color: #2469d6; + font-size: 14px; +} + +.mobile-invoice-status { + font-size: 10px !important; + padding: 4px 8px 4px 6px !important; + min-width: 70px; +} + +.mobile-invoice-details { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + margin-bottom: 12px; +} + +.mobile-detail-item { + display: flex; + flex-direction: column; +} + +.mobile-detail-label { + font-size: 11px; + color: #718096; + margin-bottom: 2px; +} + +.mobile-detail-value { + font-size: 13px; + font-weight: 500; + color: #2d3748; +} + +.mobile-invoice-actions { + display: flex; + justify-content: space-between; + gap: 8px; +} + +.mobile-action-btn { + flex: 1; + text-align: center; + padding: 6px 10px; + font-size: 12px; + border-radius: 6px; + text-decoration: none; + display: flex; + align-items: center; + justify-content: center; + gap: 4px; +} + +.mobile-action-btn.view { + background: #f0f8ff; + color: #2469d6; + border: 1px solid #2469d6; +} + +.mobile-action-btn.edit { + background: linear-gradient(135deg, #3492f8 55%, #1256cc 110%); + color: white; + border: none; +} + +/* Responsive Design */ +@media (max-width: 1200px) { + .invoice-tools-row { + flex-direction: column; + align-items: stretch; + gap: 15px; + } + + .search-box { + max-width: 100%; + min-width: auto; + } + + .filter-group { + justify-content: space-between; + width: 100%; + } +} + +@media (max-width: 992px) { + .stats-summary { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 768px) { + .invoice-management-bar { + padding: 12px 20px; + } + + .invoice-tools-row { + padding: 15px; + } + + .table th, .table td { + font-size: 12px; + padding: 12px 8px; + } + + .badge { + font-size: 10px !important; + padding: 5px 10px 5px 6px !important; + min-width: 70px; + gap: 4px; + } + + .status-icon { + font-size: 10px; + } + + .btn-primary { + padding: 4px 8px; + font-size: 11px; + } + + .filter-group { + flex-direction: column; + align-items: stretch; + } + + .filter-select { + min-width: auto; + width: 100%; + } + + .create-invoice-btn { + width: 100%; + justify-content: center; + } + + .invoice-icon { + width: 28px; + height: 28px; + font-size: 12px; + } + + /* Show mobile cards and hide table on small screens */ + .desktop-table { + display: none; + } + + .mobile-invoice-card { + display: block; + } + + .date-range-container { + flex-direction: column; + width: 100%; + } + + .date-input { + width: 100%; + } + + .stats-summary { + grid-template-columns: 1fr; + margin: 15px; + } +} + +@media (max-width: 576px) { + .invoice-management-title { + font-size: 1.1rem; + } + + .card-header h4 { + font-size: 1.1rem; + } + + .mobile-invoice-details { + grid-template-columns: 1fr; + } + + .mobile-invoice-actions { + flex-direction: column; + } +}
@@ -650,19 +880,12 @@ - - - + +
+ + to + +
@@ -692,8 +915,8 @@ - -
+ +

All Invoices

@@ -747,10 +970,14 @@ ₹{{ number_format($invoice->final_amount_with_gst, 2) }} - + + @if($invoice->status == 'paid') + + @elseif($invoice->status == 'pending') + + @elseif($invoice->status == 'overdue') + + @endif {{ ucfirst($invoice->status) }} @@ -775,6 +1002,75 @@
+ + +
+ @php + $totalInvoices = $invoices->count(); + $sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first + @endphp + + @forelse($sortedInvoices as $i => $invoice) +
+
+
+
+ +
+ {{ $invoice->invoice_number }} +
+ + @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 +
@@ -827,20 +1123,26 @@ document.addEventListener('DOMContentLoaded', function() { } }); - // Search functionality + // Search and filter functionality for both desktop and mobile const searchInput = document.getElementById('invoiceSearch'); const statusFilter = document.getElementById('statusFilter'); - const dateFilter = document.getElementById('dateFilter'); - const amountFilter = document.getElementById('amountFilter'); + const startDateInput = document.getElementById('startDate'); + const endDateInput = document.getElementById('endDate'); + + // Desktop table elements const table = document.getElementById('invoicesTable'); - const tableRows = table.getElementsByTagName('tbody')[0].getElementsByTagName('tr'); + const tableRows = table ? table.getElementsByTagName('tbody')[0].getElementsByTagName('tr') : []; + + // Mobile card elements + const mobileCards = document.querySelectorAll('.mobile-invoice-card'); function filterInvoices() { const searchTerm = searchInput.value.toLowerCase(); const statusValue = statusFilter.value; - const dateValue = dateFilter.value; - const amountValue = amountFilter.value; + const startDate = startDateInput.value; + const endDate = endDateInput.value; + // Filter desktop table rows for (let row of tableRows) { const cells = row.getElementsByTagName('td'); if (cells.length < 9) continue; @@ -848,25 +1150,55 @@ document.addEventListener('DOMContentLoaded', function() { const invoiceNumber = cells[1].textContent.toLowerCase(); const customerName = cells[2].textContent.toLowerCase(); const status = cells[6].textContent.toLowerCase(); - const amount = parseFloat(cells[3].textContent.replace('₹', '').replace(',', '')); + const invoiceDate = cells[7].textContent; const matchesSearch = invoiceNumber.includes(searchTerm) || customerName.includes(searchTerm); const matchesStatus = !statusValue || status.includes(statusValue); - const matchesAmount = !amountValue || ( - amountValue === '0-1000' ? amount <= 1000 : - amountValue === '1000-5000' ? amount > 1000 && amount <= 5000 : - amountValue === '5000+' ? amount > 5000 : true - ); + + // Date filtering + let matchesDate = true; + if (startDate || endDate) { + const cellDate = new Date(invoiceDate); + const start = startDate ? new Date(startDate) : null; + const end = endDate ? new Date(endDate) : null; + + if (start && cellDate < start) matchesDate = false; + if (end && cellDate > end) matchesDate = false; + } - row.style.display = matchesSearch && matchesStatus && matchesAmount ? '' : 'none'; + row.style.display = matchesSearch && matchesStatus && matchesDate ? '' : 'none'; + } + + // Filter mobile cards + for (let card of mobileCards) { + const invoiceNumber = card.querySelector('.mobile-invoice-number-text').textContent.toLowerCase(); + const customerName = card.querySelector('.mobile-detail-item:nth-child(1) .mobile-detail-value').textContent.toLowerCase(); + const status = card.querySelector('.badge').textContent.toLowerCase(); + const invoiceDate = card.querySelector('.mobile-detail-item:nth-child(5) .mobile-detail-value').textContent; + + const matchesSearch = invoiceNumber.includes(searchTerm) || customerName.includes(searchTerm); + const matchesStatus = !statusValue || status.includes(statusValue); + + // Date filtering + let matchesDate = true; + if (startDate || endDate) { + const cellDate = new Date(invoiceDate); + const start = startDate ? new Date(startDate) : null; + const end = endDate ? new Date(endDate) : null; + + if (start && cellDate < start) matchesDate = false; + if (end && cellDate > end) matchesDate = false; + } + + card.style.display = matchesSearch && matchesStatus && matchesDate ? '' : 'none'; } } // Add event listeners for filtering searchInput.addEventListener('input', filterInvoices); statusFilter.addEventListener('change', filterInvoices); - dateFilter.addEventListener('change', filterInvoices); - amountFilter.addEventListener('change', filterInvoices); + startDateInput.addEventListener('change', filterInvoices); + endDateInput.addEventListener('change', filterInvoices); // Add hover effects to table rows for (let row of tableRows) { diff --git a/resources/views/admin/layouts/app.blade.php b/resources/views/admin/layouts/app.blade.php index 87b276c..f6fef2c 100644 --- a/resources/views/admin/layouts/app.blade.php +++ b/resources/views/admin/layouts/app.blade.php @@ -189,7 +189,12 @@ Invoice - Customers + + Customers + + + Reports Chat Support -
- -
KENT LOGISTICS
-
Official Invoice
-
- -
- Invoice No: {{ $invoice->invoice_number }}
- Invoice Date: {{ $invoice->invoice_date }}
- Due Date: {{ $invoice->due_date }}
- Status: {{ ucfirst($invoice->status) }} -
- -
Customer Details
-
- {{ $invoice->customer_name }}
- {{ $invoice->company_name }}
- {{ $invoice->customer_mobile }}
- {{ $invoice->customer_email }}
- {{ $invoice->customer_address }} -
- -
Invoice Items
- - - - - - - - - - - - - - - - - - - - @foreach($invoice->items as $i => $item) - - - - - - - - - - - - - - - - @endforeach - -
#DescriptionCTNQTYTTL/QTYUnitPriceTTL AmountCBMTTL CBMKGTTL KGShop No
{{ $i + 1 }}{{ $item->description }}{{ $item->ctn }}{{ $item->qty }}{{ $item->ttl_qty }}{{ $item->unit }}{{ number_format($item->price, 2) }}{{ number_format($item->ttl_amount, 2) }}{{ $item->cbm }}{{ $item->ttl_cbm }}{{ $item->kg }}{{ $item->ttl_kg }}{{ $item->shop_no }}
- -
Totals
-
- Amount: ₹{{ number_format($invoice->final_amount, 2) }}
- GST ({{ $invoice->gst_percent }}%): ₹{{ number_format($invoice->gst_amount, 2) }}
- Total With GST: ₹{{ number_format($invoice->final_amount_with_gst, 2) }} +
+ +
+
+ +
+
{{ $invoice->company_name ?? 'Kent International Pvt. Ltd.' }}
+
+ {{ $invoice->company_address ?? '123 Business Park, Sector 5' }}
+ {{ $invoice->company_city ?? 'Gurugram, Haryana 122001' }}
+ {{ $invoice->company_country ?? 'India' }}
+ GST: {{ $invoice->company_gst ?? 'GST123456789' }}
+ Email: {{ $invoice->company_email ?? 'billing@kent.com' }}
+ Phone: {{ $invoice->company_phone ?? '+91 124 123 4567' }} +
+
+
+
INVOICE
+ Invoice #: {{ $invoice->invoice_number }}
+ Issue Date: {{ date('d M Y', strtotime($invoice->invoice_date)) }}
+ Due Date: {{ date('d M Y', strtotime($invoice->due_date)) }} + @if(strtolower($invoice->status) == 'paid') + + + @else + + @endif +
+
+ +
+
Bill To
+
+ {{ $invoice->customer_name }}
+ {{ $invoice->customer_address }}
+ GST: {{ $invoice->customer_gst ?? '-' }}
+ Email: {{ $invoice->customer_email }}
+ Phone: {{ $invoice->customer_mobile }} +
+
+ + + + + + + + + + + + @foreach($invoice->items as $item) + + + + + + + @endforeach + + + + + + + + + + + + + +
DescriptionQtyRate (₹)Amount (₹)
{{ $item->description }}{{ $item->qty }}{{ number_format($item->price, 0) }}{{ number_format($item->ttl_amount, 0) }}
Subtotal:{{ number_format($invoice->subtotal, 0) }}
GST ({{ $invoice->gst_percent }}%):{{ number_format($invoice->gst_amount, 0) }}
Total:{{ number_format($invoice->final_amount_with_gst, 0) }}
+ +
+ Payment Method: {{ $invoice->payment_method ?? 'Bank Transfer' }} +
+
+ Reference Number: {{ $invoice->reference_no ?? "REF123456789" }} +
+ +
diff --git a/resources/views/admin/shipments.blade.php b/resources/views/admin/shipments.blade.php index 937b4c8..81b7d96 100644 --- a/resources/views/admin/shipments.blade.php +++ b/resources/views/admin/shipments.blade.php @@ -9,6 +9,25 @@ overflow-x: hidden !important; } + .table thead th, + .table tbody td { + white-space: nowrap !important; + padding: 8px 4px !important; + } + + .table th:nth-child(10), /* if Date is column 10 */ + .table td:nth-child(10) { + max-width: 110px !important; + width: 110px !important; + overflow: hidden; + text-overflow: ellipsis; + } + + .table { + table-layout: fixed !important; + } + + .table, .custom-table-modal, .shipment-details-table { min-width: unset !important; width: 100% !important; @@ -226,62 +245,79 @@ border-bottom: none; } - /* UPDATED: Status Badge Styles */ + /* UPDATED: Status Badge Styles - ALL SAME SIZE */ .badge { - padding: 8px 16px; - border-radius: 20px; - font-weight: 600; - font-size: 0.8rem; - border: 2px solid transparent; + 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-block !important; + line-height: 1.2 !important; } - /* Pending Status */ + /* Pending Status - SAME SIZE */ .badge-pending { background: linear-gradient(135deg, #fef3c7, #fde68a) !important; color: #d97706 !important; - border-color: #f59e0b; + border-color: #f59e0b !important; + width: 110px; } - /* In Transit Status */ + /* In Transit Status - SAME SIZE */ .badge-in_transit { background: linear-gradient(135deg, #dbeafe, #93c5fd) !important; color: #1e40af !important; - border-color: #3b82f6; + border-color: #3b82f6 !important; + width: 110px; } - /* Dispatched Status */ + /* Dispatched Status - SAME SIZE */ .badge-dispatched { background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important; color: #6b21a8 !important; - border-color: #8b5cf6; + border-color: #8b5cf6 !important; + width: 110px; } - /* Delivered Status */ + /* Delivered Status - SAME SIZE */ .badge-delivered { background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important; color: #065f46 !important; - border-color: #10b981; + border-color: #10b981 !important; + width: 110px; } - /* Default badge styles */ + /* Default badge styles - SAME SIZE */ .badge.bg-info { background: linear-gradient(135deg, #4cc9f0, #4361ee) !important; - color: white; + color: white !important; } .badge.bg-success { background: linear-gradient(135deg, #4ade80, #22c55e) !important; - color: white; + color: white !important; } .badge.bg-warning { background: linear-gradient(135deg, #fbbf24, #f59e0b) !important; - color: white; + color: white !important; } .badge.bg-danger { background: linear-gradient(135deg, #f87171, #ef4444) !important; - color: white; + color: white !important; + } + + /* Light badges for quantity, kg, cbm */ + .badge.bg-light { + background: #f8f9fa !important; + color: #212529 !important; + border: 1px solid #dee2e6 !important; + min-width: 80px !important; + padding: 6px 12px !important; } /* NEW: Action Button Styles */ @@ -851,6 +887,36 @@ .loading { animation: pulse 1.5s infinite; } + + /* Status Filter Styles */ + .status-filter-container { + position: relative; + } + + .status-filter-select { + cursor: pointer; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right 12px center; + background-size: 16px; + padding-right: 40px !important; + } + + /* Shipment row styling for filtering */ + .shipment-row { + transition: all 0.3s ease; + } + + .shipment-row.hidden { + display: none !important; + } + + .shipment-row.visible { + display: table-row; + }
@@ -877,18 +943,21 @@
🔍 - - - +
+ +
+