diff --git a/app/Http/Controllers/UserAuthController.php b/app/Http/Controllers/UserAuthController.php index b6893d4..a2be041 100644 --- a/app/Http/Controllers/UserAuthController.php +++ b/app/Http/Controllers/UserAuthController.php @@ -9,6 +9,25 @@ use App\Models\User; class UserAuthController extends Controller { + + public function refreshToken() + { + try { + $newToken = JWTAuth::refresh(JWTAuth::getToken()); + + return response()->json([ + 'success' => true, + 'token' => $newToken, + ]); + + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Could not refresh token.', + ], 401); + } + } + /** * User Login */ @@ -60,6 +79,8 @@ class UserAuthController extends Controller ]); } + + /** * User Logout */ diff --git a/app/Http/Controllers/user/UserOrderController.php b/app/Http/Controllers/user/UserOrderController.php new file mode 100644 index 0000000..d18f7ae --- /dev/null +++ b/app/Http/Controllers/user/UserOrderController.php @@ -0,0 +1,296 @@ +authenticate(); + + if (!$user) { + return response()->json([ + 'status' => false, + 'message' => 'Unauthorized' + ], 401); + } + + // ------------------------------------- + // Get all orders + // ------------------------------------- + $orders = $user->orders()->with('invoice')->get(); + + // ------------------------------------- + // Counts + // ------------------------------------- + $totalOrders = $orders->count(); + $delivered = $orders->where('status', 'delivered')->count(); + $inTransit = $orders->where('status', '!=', 'delivered')->count(); + $active = $totalOrders; + + // ------------------------------------- + // Total Amount = Invoice.total_with_gst + // ------------------------------------- + $totalAmount = $orders->sum(function ($o) { + return $o->invoice->final_amount_with_gst ?? 0; + }); + + // Format total amount in K, L, Cr + $formattedAmount = $this->formatIndianNumber($totalAmount); + + return response()->json([ + 'status' => true, + + 'summary' => [ + 'active_orders' => $active, + 'in_transit_orders' => $inTransit, + 'delivered_orders' => $delivered, + 'total_value' => $formattedAmount, // formatted value + 'total_raw' => $totalAmount // original value + ] + ]); + } + + /** + * Convert number into Indian Format: + * 1000 -> 1K + * 100000 -> 1L + * 10000000 -> 1Cr + */ + private function formatIndianNumber($num) + { + if ($num >= 10000000) { + return round($num / 10000000, 1) . 'Cr'; + } + + if ($num >= 100000) { + return round($num / 100000, 1) . 'L'; + } + + if ($num >= 1000) { + return round($num / 1000, 1) . 'K'; + } + + return (string)$num; + } + + public function allOrders() +{ + $user = JWTAuth::parseToken()->authenticate(); + + if (!$user) { + return response()->json([ + 'success' => false, + 'message' => 'Unauthorized' + ], 401); + } + + // Fetch orders for this user + $orders = $user->orders() + ->with(['invoice', 'shipments']) + ->orderBy('id', 'desc') + ->get() + ->map(function ($o) { + return [ + 'order_id' => $o->order_id, + 'status' => $o->status, + 'amount' => $o->ttl_amount, + 'description'=> "Order from {$o->origin} to {$o->destination}", + 'created_at' => $o->created_at, + ]; + }); + + return response()->json([ + 'success' => true, + 'orders' => $orders + ]); +} + +public function orderDetails($order_id) +{ + $user = JWTAuth::parseToken()->authenticate(); + + $order = $user->orders() + ->with(['items']) + ->where('order_id', $order_id) + ->first(); + + if (!$order) { + return response()->json(['success' => false, 'message' => 'Order not found'], 404); + } + + return response()->json([ + 'success' => true, + 'order' => $order + ]); +} + + +public function orderShipment($order_id) +{ + $user = JWTAuth::parseToken()->authenticate(); + + // Get order + $order = $user->orders()->where('order_id', $order_id)->first(); + + if (!$order) { + return response()->json(['success' => false, 'message' => 'Order not found'], 404); + } + + // Find shipment only for this order + $shipment = $order->shipments() + ->with(['items' => function ($q) use ($order) { + $q->where('order_id', $order->id); + }]) + ->first(); + + return response()->json([ + 'success' => true, + 'shipment' => $shipment + ]); +} + + +public function orderInvoice($order_id) +{ + $user = JWTAuth::parseToken()->authenticate(); + + $order = $user->orders() + ->with('invoice.items') + ->where('order_id', $order_id) + ->first(); + + if (!$order) { + return response()->json(['success' => false, 'message' => 'Order not found'], 404); + } + + return response()->json([ + 'success' => true, + 'invoice' => $order->invoice + ]); +} + +public function trackOrder($order_id) +{ + $user = JWTAuth::parseToken()->authenticate(); + + $order = $user->orders() + ->with('shipments') + ->where('order_id', $order_id) + ->first(); + + if (!$order) { + return response()->json(['success' => false, 'message' => 'Order not found'], 404); + } + + $shipment = $order->shipments()->first(); + + return response()->json([ + 'success' => true, + 'track' => [ + 'order_id' => $order->order_id, + 'shipment_status' => $shipment->status ?? 'pending', + 'shipment_date' => $shipment->shipment_date ?? null, + ] + ]); +} + +public function allInvoices() +{ + $user = JWTAuth::parseToken()->authenticate(); + + if (!$user) { + return response()->json([ + 'success' => false, + 'message' => 'Unauthorized' + ], 401); + } + + // Fetch all invoices of customer + $invoices = $user->invoices() + ->withCount('installments') + ->orderBy('id', 'desc') + ->get() + ->map(function ($invoice) { + return [ + 'invoice_id' => $invoice->id, + 'invoice_number' => $invoice->invoice_number, + 'invoice_date' => $invoice->invoice_date, + 'status' => $invoice->status, + 'amount' => $invoice->final_amount_with_gst, + 'formatted_amount' => $this->formatIndianNumber($invoice->final_amount_with_gst), + 'pdf_url' => $invoice->pdf_path ? url($invoice->pdf_path) : null, + 'installment_count' => $invoice->installments_count, + ]; + }); + + return response()->json([ + 'success' => true, + 'invoices' => $invoices + ]); +} + +public function invoiceInstallmentsById($invoice_id) +{ + $user = \PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth::parseToken()->authenticate(); + + if (! $user) { + return response()->json(['success' => false, 'message' => 'Unauthorized'], 401); + } + + // Find invoice by numeric id and ensure it belongs to logged-in user (invoice.customer_id = user.id) + $invoice = \App\Models\Invoice::where('id', (int)$invoice_id) + ->where('customer_id', $user->id) + ->with(['installments' => function($q){ + $q->orderBy('installment_date', 'ASC')->orderBy('id', 'ASC'); + }]) + ->first(); + + if (! $invoice) { + return response()->json([ + 'success' => false, + 'message' => 'Invoice not found for this customer' + ], 404); + } + + return response()->json([ + 'success' => true, + 'invoice_id' => $invoice->id, + 'invoice_number' => $invoice->invoice_number, + 'installments' => $invoice->installments + ]); +} + +public function invoiceDetails($invoice_id) +{ + $user = JWTAuth::parseToken()->authenticate(); + + if (! $user) { + return response()->json(['success' => false, 'message' => 'Unauthorized'], 401); + } + + $invoice = \App\Models\Invoice::where('id', $invoice_id) + ->where('customer_id', $user->id) + ->with('items') + ->first(); + + if (! $invoice) { + return response()->json(['success' => false, 'message' => 'Invoice not found'], 404); + } + + return response()->json([ + 'success' => true, + 'invoice' => $invoice + ]); +} + + + + + +} diff --git a/app/Http/Controllers/user/UserProfileController.php b/app/Http/Controllers/user/UserProfileController.php new file mode 100644 index 0000000..a5d644c --- /dev/null +++ b/app/Http/Controllers/user/UserProfileController.php @@ -0,0 +1,113 @@ +authenticate(); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => 'Token invalid or expired', + ], 401); + } + + + if (! $user) { + return response()->json([ + 'success' => false, + 'message' => 'Unauthorized' + ], 401); + } + + // Format response + return response()->json([ + 'success' => true, + 'data' => [ + 'customer_id' => $user->customer_id, + 'customer_name' => $user->customer_name, + 'company_name' => $user->company_name, + 'designation' => $user->designation, + 'email' => $user->email, + 'mobile' => $user->mobile_no, + 'address' => $user->address, + 'pincode' => $user->pincode, + 'status' => $user->status, + 'customer_type' => $user->customer_type, + 'profile_image' => $user->profile_image ? url($user->profile_image) : null, + 'date' => $user->date, + 'created_at' => $user->created_at, + ] + ]); + } + + public function updateProfile(Request $request) +{ + $user = JWTAuth::parseToken()->authenticate(); + + if (! $user) { + return response()->json([ + 'success' => false, + 'message' => 'Unauthorized' + ], 401); + } + + // Validate ONLY profile image + $request->validate([ + 'profile_image' => 'required|image|mimes:jpg,jpeg,png|max:2048' + ]); + + // If new image uploaded + if ($request->hasFile('profile_image')) { + + // DELETE OLD IMAGE + if ($user->profile_image && file_exists(public_path($user->profile_image))) { + @unlink(public_path($user->profile_image)); + } + + // NEW FILE + $file = $request->file('profile_image'); + $filename = 'profile_' . time() . '.' . $file->getClientOriginalExtension(); + + // Correct folder name (from your message) + $folder = 'profile_upload/'; + $fullPath = $folder . $filename; + + // Move file + $file->move(public_path($folder), $filename); + + // Save in DB (same pattern you said) + $user->profile_image = $fullPath; + } + + $user->save(); + + return response()->json([ + 'success' => true, + 'message' => 'Profile image updated successfully', + 'data' => [ + 'customer_id' => $user->customer_id, + 'customer_name' => $user->customer_name, + 'company_name' => $user->company_name, + 'designation' => $user->designation, + 'email' => $user->email, + 'mobile' => $user->mobile_no, + 'address' => $user->address, + 'pincode' => $user->pincode, + 'status' => $user->status, + 'customer_type' => $user->customer_type, + 'profile_image' => $user->profile_image ? url($user->profile_image) : null, + 'date' => $user->date, + ] + ]); +} + +} diff --git a/app/Http/Middleware/JwtRefreshMiddleware.php b/app/Http/Middleware/JwtRefreshMiddleware.php new file mode 100644 index 0000000..5d625e2 --- /dev/null +++ b/app/Http/Middleware/JwtRefreshMiddleware.php @@ -0,0 +1,36 @@ +authenticate(); + } catch (TokenExpiredException $e) { + try { + $newToken = JWTAuth::refresh(JWTAuth::getToken()); + auth()->setToken($newToken); + + $response = $next($request); + + return $response->header('Authorization', 'Bearer ' . $newToken); + } catch (\Exception $e) { + return response()->json(['message' => 'Session expired, please login again'], 401); + } + } catch (TokenInvalidException $e) { + return response()->json(['message' => 'Invalid token'], 401); + } catch (JWTException $e) { + return response()->json(['message' => 'Token missing'], 401); + } + + return $next($request); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index b5df7a5..e67ea2f 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -89,4 +89,11 @@ class User extends Authenticatable implements JWTSubject { return []; } +public function invoices() +{ + return $this->hasMany(\App\Models\Invoice::class, 'customer_id', 'id'); +} + + + } diff --git a/public/invoices/invoice-INV-2025-000017.pdf b/public/invoices/invoice-INV-2025-000017.pdf new file mode 100644 index 0000000..1c25516 Binary files /dev/null and b/public/invoices/invoice-INV-2025-000017.pdf differ diff --git a/public/invoices/invoice-INV-2025-000019.pdf b/public/invoices/invoice-INV-2025-000019.pdf new file mode 100644 index 0000000..431b377 Binary files /dev/null and b/public/invoices/invoice-INV-2025-000019.pdf differ diff --git a/public/invoices/invoice-INV-2025-000023.pdf b/public/invoices/invoice-INV-2025-000023.pdf new file mode 100644 index 0000000..1519813 Binary files /dev/null and b/public/invoices/invoice-INV-2025-000023.pdf differ diff --git a/public/profile_upload/profile_1764394681.jpeg b/public/profile_upload/profile_1764394681.jpeg new file mode 100644 index 0000000..06220af Binary files /dev/null and b/public/profile_upload/profile_1764394681.jpeg differ diff --git a/public/profile_upload/profile_1764568863.jpg b/public/profile_upload/profile_1764568863.jpg new file mode 100644 index 0000000..bdd8e39 Binary files /dev/null and b/public/profile_upload/profile_1764568863.jpg differ diff --git a/resources/views/admin/invoice.blade.php b/resources/views/admin/invoice.blade.php index f76d1eb..2c4e0b4 100644 --- a/resources/views/admin/invoice.blade.php +++ b/resources/views/admin/invoice.blade.php @@ -890,10 +890,10 @@ - +