diff --git a/.editorconfig b/ClothingStore/.editorconfig similarity index 100% rename from .editorconfig rename to ClothingStore/.editorconfig diff --git a/.gitattributes b/ClothingStore/.gitattributes similarity index 100% rename from .gitattributes rename to ClothingStore/.gitattributes diff --git a/.gitignore b/ClothingStore/.gitignore similarity index 100% rename from .gitignore rename to ClothingStore/.gitignore diff --git a/README.md b/ClothingStore/README.md similarity index 100% rename from README.md rename to ClothingStore/README.md diff --git a/app/Console/Commands/FixAdminRole.php b/ClothingStore/app/Console/Commands/FixAdminRole.php similarity index 100% rename from app/Console/Commands/FixAdminRole.php rename to ClothingStore/app/Console/Commands/FixAdminRole.php diff --git a/app/Console/Kernel.php b/ClothingStore/app/Console/Kernel.php similarity index 100% rename from app/Console/Kernel.php rename to ClothingStore/app/Console/Kernel.php diff --git a/app/Exceptions/Handler.php b/ClothingStore/app/Exceptions/Handler.php similarity index 100% rename from app/Exceptions/Handler.php rename to ClothingStore/app/Exceptions/Handler.php diff --git a/ClothingStore/app/Http/Controllers/AboutController.php b/ClothingStore/app/Http/Controllers/AboutController.php new file mode 100644 index 0000000..1c79b9c --- /dev/null +++ b/ClothingStore/app/Http/Controllers/AboutController.php @@ -0,0 +1,22 @@ +email || !$request->password) { + return response()->json([ + 'message' => 'Email and password are required' + ], 422); + } + + // Find user by email + $user = User::where('email', $request->email)->first(); + + // Check if user exists and password is correct + if (!$user || !Hash::check($request->password, $user->password)) { + return response()->json([ + 'message' => 'Invalid credentials' + ], 401); + } + + // Generate token without revoking existing ones first + $token = $user->createToken('api-token')->plainTextToken; + + // Return user data and token + return response()->json([ + 'id' => $user->id, + 'name' => $user->name, + 'email' => $user->email, + 'token' => $token + ]); + } catch (\Exception $e) { + // Detailed error response for debugging + return response()->json([ + 'message' => 'Login failed', + 'error' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine() + ], 500); + } + } + + /** + * Get all users (requires authentication) + * + * @return \Illuminate\Http\JsonResponse + */ + public function users() + { + try { + $users = User::all(); + return response()->json($users); + } catch (\Exception $e) { + return response()->json([ + 'message' => 'An error occurred while retrieving users', + 'error' => $e->getMessage() + ], 500); + } + } + + /** + * Logout user (revoke token) + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function logout(Request $request) + { + try { + $request->user()->currentAccessToken()->delete(); + + return response()->json([ + 'message' => 'Successfully logged out' + ]); + } catch (\Exception $e) { + return response()->json([ + 'message' => 'An error occurred during logout', + 'error' => $e->getMessage() + ], 500); + } + } +} diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/ClothingStore/app/Http/Controllers/Auth/AuthenticatedSessionController.php similarity index 100% rename from app/Http/Controllers/Auth/AuthenticatedSessionController.php rename to ClothingStore/app/Http/Controllers/Auth/AuthenticatedSessionController.php diff --git a/ClothingStore/app/Http/Controllers/Auth/ConfirmablePasswordController.php b/ClothingStore/app/Http/Controllers/Auth/ConfirmablePasswordController.php new file mode 100644 index 0000000..f101383 --- /dev/null +++ b/ClothingStore/app/Http/Controllers/Auth/ConfirmablePasswordController.php @@ -0,0 +1,41 @@ +validate([ + 'email' => $request->user()->email, + 'password' => $request->password, + ])) { + throw ValidationException::withMessages([ + 'password' => __('auth.password'), + ]); + } + + $request->session()->put('auth.password_confirmed_at', time()); + + return redirect()->intended(RouteServiceProvider::HOME); + } +} \ No newline at end of file diff --git a/ClothingStore/app/Http/Controllers/Auth/EmailVerificationPromptController.php b/ClothingStore/app/Http/Controllers/Auth/EmailVerificationPromptController.php new file mode 100644 index 0000000..86f2de6 --- /dev/null +++ b/ClothingStore/app/Http/Controllers/Auth/EmailVerificationPromptController.php @@ -0,0 +1,22 @@ +user()->hasVerifiedEmail() + ? redirect()->intended(RouteServiceProvider::HOME) + : view('auth.verify-email'); + } +} \ No newline at end of file diff --git a/ClothingStore/app/Http/Controllers/Auth/NewPasswordController.php b/ClothingStore/app/Http/Controllers/Auth/NewPasswordController.php new file mode 100644 index 0000000..9b1b647 --- /dev/null +++ b/ClothingStore/app/Http/Controllers/Auth/NewPasswordController.php @@ -0,0 +1,61 @@ + $request]); + } + + /** + * Handle an incoming new password request. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function store(Request $request): RedirectResponse + { + $request->validate([ + 'token' => ['required'], + 'email' => ['required', 'email'], + 'password' => ['required', 'confirmed', Rules\Password::defaults()], + ]); + + // Here we will attempt to reset the user's password. If it is successful we + // will update the password on an actual user model and persist it to the + // database. Otherwise we will parse the error and return the response. + $status = Password::reset( + $request->only('email', 'password', 'password_confirmation', 'token'), + function ($user) use ($request) { + $user->forceFill([ + 'password' => Hash::make($request->password), + 'remember_token' => Str::random(60), + ])->save(); + + event(new PasswordReset($user)); + } + ); + + // If the password was successfully reset, we will redirect the user back to + // the application's home authenticated view. If there is an error we can + // redirect them back to where they came from with their error message. + return $status == Password::PASSWORD_RESET + ? redirect()->route('login')->with('status', __($status)) + : back()->withInput($request->only('email')) + ->withErrors(['email' => __($status)]); + } +} \ No newline at end of file diff --git a/ClothingStore/app/Http/Controllers/Auth/PasswordController.php b/ClothingStore/app/Http/Controllers/Auth/PasswordController.php new file mode 100644 index 0000000..afad8d0 --- /dev/null +++ b/ClothingStore/app/Http/Controllers/Auth/PasswordController.php @@ -0,0 +1,29 @@ +validate([ + 'current_password' => ['required', 'current_password'], + 'password' => ['required', Password::defaults(), 'confirmed'], + ]); + + $request->user()->update([ + 'password' => Hash::make($validated['password']), + ]); + + return back()->with('status', 'password-updated'); + } +} \ No newline at end of file diff --git a/ClothingStore/app/Http/Controllers/Auth/PasswordResetLinkController.php b/ClothingStore/app/Http/Controllers/Auth/PasswordResetLinkController.php new file mode 100644 index 0000000..b98e422 --- /dev/null +++ b/ClothingStore/app/Http/Controllers/Auth/PasswordResetLinkController.php @@ -0,0 +1,44 @@ +validate([ + 'email' => ['required', 'email'], + ]); + + // We will send the password reset link to this user. Once we have attempted + // to send the link, we will examine the response then see the message we + // need to show to the user. Finally, we'll send out a proper response. + $status = Password::sendResetLink( + $request->only('email') + ); + + return $status == Password::RESET_LINK_SENT + ? back()->with('status', __($status)) + : back()->withInput($request->only('email')) + ->withErrors(['email' => __($status)]); + } +} \ No newline at end of file diff --git a/ClothingStore/app/Http/Controllers/Auth/SocialController.php b/ClothingStore/app/Http/Controllers/Auth/SocialController.php new file mode 100644 index 0000000..f4d6835 --- /dev/null +++ b/ClothingStore/app/Http/Controllers/Auth/SocialController.php @@ -0,0 +1,82 @@ +redirect(); + } + + /** + * Obtain the user information from the provider. + * + * @param string $provider + * @return \Illuminate\Http\RedirectResponse + */ + public function handleProviderCallback($provider) + { + try { + $socialUser = Socialite::driver($provider)->user(); + + // Check if user already exists + $user = User::where('provider_id', $socialUser->getId()) + ->where('provider', $provider) + ->first(); + + if (!$user) { + // Check if user with same email exists + $existingUser = User::where('email', $socialUser->getEmail())->first(); + + if ($existingUser) { + // Update existing user with provider info + $existingUser->update([ + 'provider' => $provider, + 'provider_id' => $socialUser->getId(), + 'avatar' => $socialUser->getAvatar() + ]); + + $user = $existingUser; + } else { + // Create new user + $user = User::create([ + 'name' => $socialUser->getName(), + 'email' => $socialUser->getEmail(), + 'password' => Hash::make(Str::random(16)), // Random password + 'provider' => $provider, + 'provider_id' => $socialUser->getId(), + 'avatar' => $socialUser->getAvatar(), + 'email_verified_at' => now(), // Mark as verified since it's from a trusted provider + ]); + + // Assign default role if needed + // $user->roles()->attach(Role::where('slug', 'customer')->first()); + } + } + + // Login the user + Auth::login($user, true); + + return redirect()->intended('/dashboard'); + + } catch (Exception $e) { + return redirect('/login')->with('error', 'Something went wrong with ' . $provider . ' login: ' . $e->getMessage()); + } + } +} diff --git a/ClothingStore/app/Http/Controllers/Auth/VerifyEmailController.php b/ClothingStore/app/Http/Controllers/Auth/VerifyEmailController.php new file mode 100644 index 0000000..faa4484 --- /dev/null +++ b/ClothingStore/app/Http/Controllers/Auth/VerifyEmailController.php @@ -0,0 +1,28 @@ +user()->hasVerifiedEmail()) { + return redirect()->intended(RouteServiceProvider::HOME.'?verified=1'); + } + + if ($request->user()->markEmailAsVerified()) { + event(new Verified($request->user())); + } + + return redirect()->intended(RouteServiceProvider::HOME.'?verified=1'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/AuthController.php b/ClothingStore/app/Http/Controllers/AuthController.php similarity index 100% rename from app/Http/Controllers/AuthController.php rename to ClothingStore/app/Http/Controllers/AuthController.php diff --git a/app/Http/Controllers/CartController.php b/ClothingStore/app/Http/Controllers/CartController.php similarity index 100% rename from app/Http/Controllers/CartController.php rename to ClothingStore/app/Http/Controllers/CartController.php diff --git a/app/Http/Controllers/CheckoutController.php b/ClothingStore/app/Http/Controllers/CheckoutController.php similarity index 100% rename from app/Http/Controllers/CheckoutController.php rename to ClothingStore/app/Http/Controllers/CheckoutController.php diff --git a/app/Http/Controllers/ContactController.php b/ClothingStore/app/Http/Controllers/ContactController.php similarity index 100% rename from app/Http/Controllers/ContactController.php rename to ClothingStore/app/Http/Controllers/ContactController.php diff --git a/app/Http/Controllers/Controller.php b/ClothingStore/app/Http/Controllers/Controller.php similarity index 100% rename from app/Http/Controllers/Controller.php rename to ClothingStore/app/Http/Controllers/Controller.php diff --git a/app/Http/Controllers/HomeController.php b/ClothingStore/app/Http/Controllers/HomeController.php similarity index 100% rename from app/Http/Controllers/HomeController.php rename to ClothingStore/app/Http/Controllers/HomeController.php diff --git a/ClothingStore/app/Http/Controllers/Manager/DashboardController.php b/ClothingStore/app/Http/Controllers/Manager/DashboardController.php new file mode 100644 index 0000000..3d01d6f --- /dev/null +++ b/ClothingStore/app/Http/Controllers/Manager/DashboardController.php @@ -0,0 +1,119 @@ +getSalesData(7); + + // Get order status distribution + $orderStatusData = $this->getOrderStatusData(); + + // Get top selling products + $topProducts = $this->getTopSellingProducts(5); + + // Get low stock products + $lowStockProducts = Product::where('stock', '<', 10) + ->orderBy('stock', 'asc') + ->take(5) + ->get(); + + return view('manager.dashboard', compact( + 'salesData', + 'orderStatusData', + 'topProducts', + 'lowStockProducts' + )); + } + + private function getSalesData($days = 7) + { + $startDate = Carbon::now()->subDays($days)->startOfDay(); + + $salesData = Order::where('created_at', '>=', $startDate) + ->select( + DB::raw('DATE(created_at) as date'), + DB::raw('SUM(total_amount) as total_amount'), + DB::raw('COUNT(*) as order_count') + ) + ->groupBy('date') + ->orderBy('date') + ->get(); + + // If no data, create sample data for the last $days + if ($salesData->isEmpty()) { + $sampleData = []; + $today = Carbon::now(); + for ($i = $days - 1; $i >= 0; $i--) { + $date = $today->copy()->subDays($i); + $sampleData[] = [ + 'date' => $date->format('Y-m-d'), + 'total_amount' => 0, + 'order_count' => 0 + ]; + } + $salesData = collect($sampleData); + } + + return $salesData; + } + + private function getOrderStatusData() + { + $statusCounts = [ + 'pending' => Order::where('status', 'pending')->count(), + 'processing' => Order::where('status', 'processing')->count(), + 'completed' => Order::where('status', 'completed')->count(), + 'cancelled' => Order::where('status', 'cancelled')->count() + ]; + + return $statusCounts; + } + + private function getTopSellingProducts($limit = 5) + { + // This query assumes you have OrderItem model with product_id and quantity + // If your database structure is different, adjust accordingly + return DB::table('order_items') + ->join('products', 'order_items.product_id', '=', 'products.id') + ->select( + 'products.id', + 'products.name', + DB::raw('SUM(order_items.quantity) as total_sold'), + DB::raw('SUM(order_items.quantity * order_items.price) as revenue') + ) + ->groupBy('products.id', 'products.name') + ->orderBy('total_sold', 'desc') + ->take($limit) + ->get(); + } + + public function getSalesDataForPeriod(Request $request) + { + $days = $request->days ?? 30; + $salesData = $this->getSalesData($days); + + // Format the dates for better display + $salesData = $salesData->map(function($item) { + $item->date = Carbon::parse($item->date)->format('M d'); + return $item; + }); + + // Log the data for debugging + Log::info('Dashboard sales data', ['data' => $salesData]); + + return response()->json(['data' => $salesData]); + } +} \ No newline at end of file diff --git a/ClothingStore/app/Http/Controllers/OrderController.php b/ClothingStore/app/Http/Controllers/OrderController.php new file mode 100644 index 0000000..4a81db3 --- /dev/null +++ b/ClothingStore/app/Http/Controllers/OrderController.php @@ -0,0 +1,244 @@ +user()->orders()->paginate(10); + return view('orders.index', compact('orders')); + } + + public function show(Order $order) + { + $this->authorize('view', $order); + return view('orders.show', compact('order')); + } + + public function staffIndex() + { + $this->authorize('viewAny', Order::class); + $orders = Order::with('user')->latest()->paginate(10); + return view('staff.orders.index', compact('orders')); + } + + public function updateStatus(Request $request, Order $order) + { + $this->authorize('update', $order); + + $validated = $request->validate([ + 'status' => 'required|in:pending,processing,completed,cancelled' + ]); + + $oldStatus = $order->status; + $order->update($validated); + + // Send status update email + Mail::to($order->user->email)->send(new OrderStatusUpdate($order, $oldStatus, $validated['status'])); + + // If order is shipped (status changed to processing), send shipping notification + if ($oldStatus === 'pending' && $validated['status'] === 'processing') { + Mail::to($order->user->email)->send(new OrderShipped($order)); + } + + return redirect()->route('staff.orders.index') + ->with('success', 'Order status updated successfully.'); + } + + public function salesReport(Request $request) + { + $this->authorize('viewAny', Order::class); + + // Date filtering + $query = Order::with('user', 'items.product'); + + if ($request->filled('start_date')) { + $query->whereDate('created_at', '>=', $request->start_date); + } + + if ($request->filled('end_date')) { + $query->whereDate('created_at', '<=', $request->end_date); + } + + $sales = $query->orderBy('created_at', 'desc')->get(); + + // Generate monthly sales data for chart + try { + $monthlySalesCollection = DB::table('orders') + ->select(DB::raw('DATE_FORMAT(created_at, "%Y-%m") as month'), DB::raw('SUM(total_amount) as total')) + ->groupBy('month') + ->orderBy('month') + ->get(); + + // Convert to array with proper formatting + $monthlySales = []; + foreach ($monthlySalesCollection as $item) { + try { + $date = Carbon::createFromFormat('Y-m', $item->month); + $monthlySales[] = [ + 'month' => $date->format('M Y'), + 'total' => (float)$item->total + ]; + } catch (\Exception $e) { + $monthlySales[] = [ + 'month' => $item->month, + 'total' => (float)$item->total + ]; + } + } + } catch (\Exception $e) { + Log::error("Error generating monthly sales data: " . $e->getMessage()); + $monthlySales = []; + } + + // If no monthly data, create sample data + if (empty($monthlySales)) { + $today = Carbon::now(); + for ($i = 6; $i >= 0; $i--) { + $date = $today->copy()->subMonths($i); + $monthlySales[] = [ + 'month' => $date->format('M Y'), + 'total' => 0 + ]; + } + } + + // Generate customer sales data for chart + try { + $customerSalesCollection = DB::table('orders') + ->join('users', 'orders.user_id', '=', 'users.id') + ->select('users.name', DB::raw('SUM(orders.total_amount) as total')) + ->groupBy('users.id', 'users.name') + ->orderBy('total', 'desc') + ->limit(10) + ->get(); + + // Convert to array with proper formatting + $customerSales = []; + foreach ($customerSalesCollection as $item) { + $customerSales[] = [ + 'name' => $item->name, + 'total' => (float)$item->total + ]; + } + } catch (\Exception $e) { + Log::error("Error generating customer sales data: " . $e->getMessage()); + $customerSales = []; + } + + // If no customer data, use the existing orders to create sample data + if (empty($customerSales) && $sales->isNotEmpty()) { + $customerMap = []; + foreach ($sales as $order) { + $userName = $order->user->name ?? 'Unknown Customer'; + if (!isset($customerMap[$userName])) { + $customerMap[$userName] = 0; + } + $customerMap[$userName] += $order->total_amount; + } + + // Convert to array format + foreach ($customerMap as $name => $total) { + $customerSales[] = [ + 'name' => $name, + 'total' => (float)$total + ]; + } + + // Sort by total amount + usort($customerSales, function($a, $b) { + return $b['total'] <=> $a['total']; + }); + + // Limit to 10 + $customerSales = array_slice($customerSales, 0, 10); + } + + // Order status distribution for pie chart + $statusCounts = [ + 'pending' => Order::where('status', 'pending')->count(), + 'processing' => Order::where('status', 'processing')->count(), + 'completed' => Order::where('status', 'completed')->count(), + 'cancelled' => Order::where('status', 'cancelled')->count() + ]; + + // Debug information to check data structure + Log::info('Monthly sales data', ['data' => $monthlySales]); + Log::info('Customer sales data', ['data' => $customerSales]); + Log::info('Status counts data', ['data' => $statusCounts]); + + return view('manager.reports.sales', compact('sales', 'monthlySales', 'customerSales', 'statusCounts')); + } + + public function salesReportPdf() + { + $this->authorize('viewAny', Order::class); + + $sales = Order::with('user', 'items.product') + ->orderBy('created_at', 'desc') + ->get(); + + // Add data for charts in PDF + $monthlySalesCollection = DB::table('orders') + ->select(DB::raw('DATE_FORMAT(created_at, "%Y-%m") as month'), DB::raw('SUM(total_amount) as total')) + ->groupBy('month') + ->orderBy('month') + ->get(); + + // Convert to array with proper formatting + $monthlySales = []; + foreach ($monthlySalesCollection as $item) { + try { + $date = Carbon::createFromFormat('Y-m', $item->month); + $monthlySales[] = [ + 'month' => $date->format('M Y'), + 'total' => (float)$item->total + ]; + } catch (\Exception $e) { + $monthlySales[] = [ + 'month' => $item->month, + 'total' => (float)$item->total + ]; + } + } + + // Customer sales data for chart + $customerSalesCollection = DB::table('orders') + ->join('users', 'orders.user_id', '=', 'users.id') + ->select('users.name', DB::raw('SUM(orders.total_amount) as total')) + ->groupBy('users.id', 'users.name') + ->orderBy('total', 'desc') + ->limit(10) + ->get(); + + // Convert to array with proper formatting + $customerSales = []; + foreach ($customerSalesCollection as $item) { + $customerSales[] = [ + 'name' => $item->name, + 'total' => (float)$item->total + ]; + } + + $statusCounts = [ + 'pending' => Order::where('status', 'pending')->count(), + 'processing' => Order::where('status', 'processing')->count(), + 'completed' => Order::where('status', 'completed')->count(), + 'cancelled' => Order::where('status', 'cancelled')->count() + ]; + + return view('pdfs.sales_report', compact('sales', 'monthlySales', 'customerSales', 'statusCounts')); + } +} \ No newline at end of file diff --git a/ClothingStore/app/Http/Controllers/ProductController.php b/ClothingStore/app/Http/Controllers/ProductController.php new file mode 100644 index 0000000..2b3cf90 --- /dev/null +++ b/ClothingStore/app/Http/Controllers/ProductController.php @@ -0,0 +1,269 @@ +route()->getPrefix() !== 'admin' && + (!auth()->user() || (!auth()->user()->hasRole('admin') && !auth()->user()->hasRole('manager')))) { + abort(403, 'Unauthorized'); + } + + $products = Product::paginate(10); + return view('products.index', compact('products')); + } + + public function create() + { + // Skip the role check if accessing through admin routes + if (request()->route()->getPrefix() !== 'admin' && + (!auth()->user() || (!auth()->user()->hasRole('admin') && !auth()->user()->hasRole('manager')))) { + abort(403, 'Unauthorized'); + } + return view('products.create'); + } + + public function store(Request $request) + { + // Skip the role check if accessing through admin routes + if (request()->route()->getPrefix() !== 'admin' && + (!auth()->user() || (!auth()->user()->hasRole('admin') && !auth()->user()->hasRole('manager')))) { + abort(403, 'Unauthorized'); + } + $validated = $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'required|string', + 'price' => 'required|numeric|min:0', + 'stock' => 'required|integer|min:0', + 'image' => 'nullable|image|max:2048' + ]); + + if ($request->hasFile('image')) { + $validated['image'] = $request->file('image')->store('products', 'public'); + } + + Product::create($validated); + + // Determine the route prefix based on the user's role + $routePrefix = ''; + if (request()->route()->getPrefix() === 'admin') { + $routePrefix = 'admin.'; + } elseif (request()->route()->getPrefix() === 'manager') { + $routePrefix = 'manager.'; + } + + return redirect()->route($routePrefix . 'products.index') + ->with('success', 'Product created successfully.'); + } + + public function show(Product $product) + { + // Redirect to the shop-details view which already exists + return redirect()->route('shop.details', $product->id); + } + + public function edit(Product $product) + { + // Skip the role check if accessing through admin routes + if (request()->route()->getPrefix() !== 'admin' && + (!auth()->user() || (!auth()->user()->hasRole('admin') && !auth()->user()->hasRole('manager')))) { + abort(403, 'Unauthorized'); + } + return view('products.edit', compact('product')); + } + + public function update(Request $request, Product $product) + { + // Skip the role check if accessing through admin routes + if (request()->route()->getPrefix() !== 'admin' && + (!auth()->user() || (!auth()->user()->hasRole('admin') && !auth()->user()->hasRole('manager')))) { + abort(403, 'Unauthorized'); + } + $validated = $request->validate([ + 'name' => 'required|string|max:255', + 'description' => 'required|string', + 'price' => 'required|numeric|min:0', + 'stock' => 'required|integer|min:0', + 'image' => 'nullable|image|max:2048' + ]); + + if ($request->hasFile('image')) { + $validated['image'] = $request->file('image')->store('products', 'public'); + } + + $product->update($validated); + + // Determine the route prefix based on the route prefix + $routePrefix = ''; + if (request()->route()->getPrefix() === 'admin') { + $routePrefix = 'admin.'; + } elseif (request()->route()->getPrefix() === 'manager') { + $routePrefix = 'manager.'; + } + + return redirect()->route($routePrefix . 'products.index') + ->with('success', 'Product updated successfully.'); + } + + public function destroy(Product $product) + { + // Skip the role check if accessing through admin routes + if (request()->route()->getPrefix() !== 'admin' && + (!auth()->user() || (!auth()->user()->hasRole('admin') && !auth()->user()->hasRole('manager')))) { + abort(403, 'Unauthorized'); + } + $product->delete(); + + // Determine the route prefix based on the route prefix + $routePrefix = ''; + if (request()->route()->getPrefix() === 'admin') { + $routePrefix = 'admin.'; + } elseif (request()->route()->getPrefix() === 'manager') { + $routePrefix = 'manager.'; + } + + return redirect()->route($routePrefix . 'products.index') + ->with('success', 'Product deleted successfully.'); + } + + public function inventoryReport(Request $request) + { + // Apply filters + $query = Product::query(); + + // Search filter + if ($request->filled('search')) { + $query->where('name', 'like', '%' . $request->search . '%'); + } + + // Remove category filter since the column doesn't exist + + // Stock status filter + if ($request->filled('stock_status')) { + switch ($request->stock_status) { + case 'out': + $query->where('stock', '<=', 0); + break; + case 'low': + $query->whereBetween('stock', [1, 9]); + break; + case 'normal': + $query->where('stock', '>=', 10); + break; + } + } + + $products = $query->get(); + + // Use a fixed list of categories instead of querying the database + $categories = ['Electronics', 'Clothing', 'Food', 'Other']; + + // Calculate category-based data for charts + // We'll use a default category for all products since the column doesn't exist + $categoryData = [ + 'Electronics' => [ + 'stock' => $products->sum('stock'), + 'value' => $products->sum(function($product) { + return $product->price * $product->stock; + }) + ] + ]; + + // Calculate stock status distribution + $stockStatus = [ + 'outOfStock' => $products->where('stock', '<=', 0)->count(), + 'critical' => $products->where('stock', '>', 0)->where('stock', '<', 5)->count(), + 'low' => $products->where('stock', '>=', 5)->where('stock', '<', 10)->count(), + 'normal' => $products->where('stock', '>=', 10)->count() + ]; + + return view('manager.reports.inventory', compact('products', 'categories', 'categoryData', 'stockStatus')); + } + + public function inventoryReportPdf() + { + // Recommend installing DomPDF package first if not already installed: + // composer require barryvdh/laravel-dompdf + + $products = Product::all(); + + // If using barryvdh/laravel-dompdf package: + // return PDF::loadView('pdfs.inventory_report', compact('products')) + // ->setPaper('a4', 'landscape') + // ->download('inventory_report_' . date('Y-m-d') . '.pdf'); + + // Since we don't have the PDF package installed yet, we'll return a simplified HTML version + // that can be printed to PDF from the browser + return view('pdfs.inventory_report', compact('products')); + } + + public function shop(Request $request) + { + $products = Product::paginate(12); + foreach ($products as $product) { + $product->image_url = $product->image_url; + } + // Use dummy categories and brands if models do not exist + $categories = [ + (object)['id' => 1, 'name' => 'Electronics', 'slug' => 'electronics'], + (object)['id' => 2, 'name' => 'Fashion', 'slug' => 'fashion'], + (object)['id' => 3, 'name' => 'Home & Living', 'slug' => 'home'], + ]; + $brands = [ + (object)['id' => 1, 'name' => 'Brand A'], + (object)['id' => 2, 'name' => 'Brand B'], + (object)['id' => 3, 'name' => 'Brand C'], + ]; + return view('webfront.shop', compact('products', 'categories', 'brands')); + } + + public function showDetails($id) + { + $product = Product::findOrFail($id); + $product->image_url = $product->image_url; + $relatedProducts = Product::where('id', '!=', $id)->take(4)->get(); + foreach ($relatedProducts as $relatedProduct) { + $relatedProduct->image_url = $relatedProduct->image_url; + } + return view('webfront.shop-details', compact('product', 'relatedProducts')); + } + + public function staffInventory(Request $request) + { + $query = Product::query(); + + // Search functionality + if ($request->has('search')) { + $search = $request->search; + $query->where(function($q) use ($search) { + $q->where('name', 'like', "%{$search}%") + ->orWhere('sku', 'like', "%{$search}%"); + }); + } + + // Stock filter + if ($request->has('stock')) { + switch ($request->stock) { + case 'low': + $query->whereBetween('stock', [1, 5]); + break; + case 'out': + $query->where('stock', 0); + break; + case 'in': + $query->where('stock', '>', 5); + break; + } + } + + $products = $query->latest()->paginate(10); + return view('staff.inventory.index', compact('products')); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/ProfileController.php b/ClothingStore/app/Http/Controllers/ProfileController.php similarity index 100% rename from app/Http/Controllers/ProfileController.php rename to ClothingStore/app/Http/Controllers/ProfileController.php diff --git a/app/Http/Controllers/ReviewController.php b/ClothingStore/app/Http/Controllers/ReviewController.php similarity index 100% rename from app/Http/Controllers/ReviewController.php rename to ClothingStore/app/Http/Controllers/ReviewController.php diff --git a/app/Http/Controllers/RoleController.php b/ClothingStore/app/Http/Controllers/RoleController.php similarity index 100% rename from app/Http/Controllers/RoleController.php rename to ClothingStore/app/Http/Controllers/RoleController.php diff --git a/app/Http/Controllers/SocialClearController.php b/ClothingStore/app/Http/Controllers/SocialClearController.php similarity index 100% rename from app/Http/Controllers/SocialClearController.php rename to ClothingStore/app/Http/Controllers/SocialClearController.php diff --git a/app/Http/Controllers/SocialiteController.php b/ClothingStore/app/Http/Controllers/SocialiteController.php similarity index 73% rename from app/Http/Controllers/SocialiteController.php rename to ClothingStore/app/Http/Controllers/SocialiteController.php index 10b7198..bca6854 100644 --- a/app/Http/Controllers/SocialiteController.php +++ b/ClothingStore/app/Http/Controllers/SocialiteController.php @@ -3,10 +3,12 @@ namespace App\Http\Controllers; use App\Models\User; +use App\Models\Role; use Exception; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; use Laravel\Socialite\Facades\Socialite; @@ -23,7 +25,7 @@ public function redirectToProvider($provider) try { // Special handling for LinkedIn if ($provider === 'linkedin') { - \Log::info("LinkedIn redirect initiated with client_id: " . config('services.linkedin.client_id')); + Log::info("LinkedIn redirect initiated with client_id: " . config('services.linkedin.client_id')); // Use OpenID Connect for LinkedIn return Socialite::driver($provider) @@ -37,7 +39,7 @@ public function redirectToProvider($provider) // Special handling for Twitter if ($provider === 'twitter') { // Log the Twitter configuration - \Log::info("Twitter redirect initiated with keys: " . json_encode([ + Log::info("Twitter redirect initiated with keys: " . json_encode([ 'consumer_key' => config('services.twitter.consumer_key'), 'has_secret' => !empty(config('services.twitter.consumer_secret')), 'redirect' => config('services.twitter.redirect') @@ -51,8 +53,8 @@ public function redirectToProvider($provider) // Return the redirect response return $driver->redirect(); } catch (\Exception $e) { - \Log::error("Twitter driver error: " . $e->getMessage()); - \Log::error("Twitter driver error trace: " . $e->getTraceAsString()); + Log::error("Twitter driver error: " . $e->getMessage()); + Log::error("Twitter driver error trace: " . $e->getTraceAsString()); // Return a more user-friendly error return redirect('/login')->with('error', 'Error connecting to Twitter: ' . $e->getMessage()); @@ -63,8 +65,8 @@ public function redirectToProvider($provider) return Socialite::driver($provider)->redirect(); } catch (\Exception $e) { // Log the error with more details - \Log::error("Error in redirectToProvider for {$provider}: " . $e->getMessage()); - \Log::error("Error trace: " . $e->getTraceAsString()); + Log::error("Error in redirectToProvider for {$provider}: " . $e->getMessage()); + Log::error("Error trace: " . $e->getTraceAsString()); return redirect('/login')->with('error', 'Error connecting to ' . $provider . ': ' . $e->getMessage()); } @@ -79,14 +81,14 @@ public function redirectToProvider($provider) public function handleProviderCallback($provider) { // Log the callback URL for debugging - \Log::info("Callback received for provider: {$provider}"); - \Log::info("Request URL: " . request()->fullUrl()); - \Log::info("Request method: " . request()->method()); + Log::info("Callback received for provider: {$provider}"); + Log::info("Request URL: " . request()->fullUrl()); + Log::info("Request method: " . request()->method()); try { // Special handling for LinkedIn if ($provider === 'linkedin') { - \Log::info("LinkedIn callback received with client_id: " . config('services.linkedin.client_id')); + Log::info("LinkedIn callback received with client_id: " . config('services.linkedin.client_id')); // Get the authorization code from the request $code = request()->input('code'); @@ -94,13 +96,13 @@ public function handleProviderCallback($provider) throw new \Exception('No authorization code provided'); } - \Log::info("LinkedIn authorization code received: " . substr($code, 0, 10) . '...'); + Log::info("LinkedIn authorization code received: " . substr($code, 0, 10) . '...'); // Get the user $socialUser = Socialite::driver($provider)->user(); // Log user data for debugging - \Log::info("LinkedIn user data: " . json_encode([ + Log::info("LinkedIn user data: " . json_encode([ 'id' => $socialUser->getId(), 'name' => $socialUser->getName(), 'email' => $socialUser->getEmail(), @@ -109,7 +111,7 @@ public function handleProviderCallback($provider) } // Special handling for Twitter else if ($provider === 'twitter') { - \Log::info("Twitter callback received with client_id: " . config('services.twitter.client_id')); + Log::info("Twitter callback received with client_id: " . config('services.twitter.client_id')); // Check for denied access if (request()->has('denied')) { @@ -120,7 +122,7 @@ public function handleProviderCallback($provider) $socialUser = Socialite::driver($provider)->user(); // Log user data for debugging - \Log::info("Twitter user data: " . json_encode([ + Log::info("Twitter user data: " . json_encode([ 'id' => $socialUser->getId(), 'name' => $socialUser->getName(), 'nickname' => $socialUser->getNickname(), @@ -146,7 +148,20 @@ public function handleProviderCallback($provider) ->first(); if (!$user) { + // Try to find user by email $user = User::where('email', $email)->first(); + + // If user exists but doesn't have provider info, update their record + if ($user) { + $user->update([ + 'provider' => $provider, + 'provider_id' => $socialUser->getId(), + 'avatar' => $socialUser->getAvatar(), + 'email_verified_at' => now(), // Ensure the email is verified + ]); + + Log::info("Updated existing user with provider info: {$user->email}"); + } } if (!$user) { @@ -158,7 +173,17 @@ public function handleProviderCallback($provider) 'provider' => $provider, 'provider_id' => $socialUser->getId(), 'avatar' => $socialUser->getAvatar(), + 'email_verified_at' => now(), // Verify email for social logins as they come from trusted providers ]); + + // Assign customer role by default + $customerRole = Role::where('slug', 'customer')->first(); + if ($customerRole) { + $user->roles()->attach($customerRole); + Log::info("Assigned customer role to new social login user: {$user->email}"); + } else { + Log::warning("Could not find customer role to assign to new social login user: {$user->email}"); + } } else { // Update existing user with provider info if needed $user->update([ @@ -175,19 +200,19 @@ public function handleProviderCallback($provider) } catch (Exception $e) { // Log the error for debugging with more details - \Log::error("Social login error with {$provider}: " . $e->getMessage()); - \Log::error("Exception trace: " . $e->getTraceAsString()); + Log::error("Social login error with {$provider}: " . $e->getMessage()); + Log::error("Exception trace: " . $e->getTraceAsString()); if ($provider === 'linkedin') { // Log LinkedIn specific configuration for debugging - \Log::error("LinkedIn configuration: " . json_encode([ + Log::error("LinkedIn configuration: " . json_encode([ 'client_id' => config('services.linkedin.client_id'), 'redirect' => config('services.linkedin.redirect'), 'api_version' => config('services.linkedin.api_version'), ])); // Log request details - \Log::error("LinkedIn callback request: " . json_encode([ + Log::error("LinkedIn callback request: " . json_encode([ 'code' => request()->input('code') ? 'present' : 'missing', 'state' => request()->input('state') ? 'present' : 'missing', 'error' => request()->input('error'), @@ -197,14 +222,14 @@ public function handleProviderCallback($provider) if ($provider === 'twitter') { // Log Twitter specific configuration for debugging - \Log::error("Twitter configuration: " . json_encode([ + Log::error("Twitter configuration: " . json_encode([ 'consumer_key' => config('services.twitter.consumer_key'), 'has_secret' => !empty(config('services.twitter.consumer_secret')), 'redirect' => config('services.twitter.redirect'), ])); // Log request details - \Log::error("Twitter callback request: " . json_encode([ + Log::error("Twitter callback request: " . json_encode([ 'oauth_token' => request()->input('oauth_token') ? 'present' : 'missing', 'oauth_verifier' => request()->input('oauth_verifier') ? 'present' : 'missing', 'denied' => request()->input('denied'), diff --git a/app/Http/Controllers/Staff/DashboardController.php b/ClothingStore/app/Http/Controllers/Staff/DashboardController.php similarity index 100% rename from app/Http/Controllers/Staff/DashboardController.php rename to ClothingStore/app/Http/Controllers/Staff/DashboardController.php diff --git a/app/Http/Controllers/StaffController.php b/ClothingStore/app/Http/Controllers/StaffController.php similarity index 100% rename from app/Http/Controllers/StaffController.php rename to ClothingStore/app/Http/Controllers/StaffController.php diff --git a/ClothingStore/app/Http/Controllers/TestDataController.php b/ClothingStore/app/Http/Controllers/TestDataController.php new file mode 100644 index 0000000..72dbe41 --- /dev/null +++ b/ClothingStore/app/Http/Controllers/TestDataController.php @@ -0,0 +1,224 @@ +count(); + $productCount = DB::table('products')->count(); + $orderCount = DB::table('orders')->count(); + $orderItemCount = DB::table('order_items')->count(); + + // Check if DATE_FORMAT works in your MySQL version + try { + $testDate = DB::select("SELECT DATE_FORMAT(NOW(), '%Y-%m') as formatted_date"); + $dateFormatWorks = !empty($testDate); + } catch (\Exception $e) { + $dateFormatWorks = false; + } + + // Get sample data that would be used for charts + $monthlySales = $this->getMonthlySalesData(); + $customerSales = $this->getCustomerSalesData(); + $statusCounts = $this->getStatusData(); + + return response()->json([ + 'database_info' => [ + 'users' => $userCount, + 'products' => $productCount, + 'orders' => $orderCount, + 'order_items' => $orderItemCount, + 'date_format_works' => $dateFormatWorks + ], + 'chart_data' => [ + 'monthly_sales' => $monthlySales, + 'customer_sales' => $customerSales, + 'status_counts' => $statusCounts + ] + ]); + } + + public function generateData() + { + // Check if we already have data + if (Order::count() > 0) { + return response()->json(['message' => 'Data already exists, skipping generation']); + } + + // Create users if needed + if (User::count() < 5) { + for ($i = 1; $i <= 5; $i++) { + User::create([ + 'name' => "Test User {$i}", + 'email' => "test{$i}@example.com", + 'password' => bcrypt('password'), + 'email_verified_at' => now() + ]); + } + } + + // Create products if needed + if (Product::count() < 5) { + $products = [ + ['name' => 'Smartphone', 'price' => 999.99, 'stock' => 50], + ['name' => 'Laptop', 'price' => 1299.99, 'stock' => 25], + ['name' => 'Headphones', 'price' => 199.99, 'stock' => 100], + ['name' => 'Tablet', 'price' => 499.99, 'stock' => 30], + ['name' => 'Smartwatch', 'price' => 299.99, 'stock' => 45] + ]; + + foreach ($products as $product) { + Product::create([ + 'name' => $product['name'], + 'description' => "This is a {$product['name']}", + 'price' => $product['price'], + 'stock' => $product['stock'], + 'image' => 'products/default.jpg' + ]); + } + } + + // Get IDs for reference + $userIds = User::pluck('id')->toArray(); + $productIds = Product::pluck('id')->toArray(); + + // Create orders with different statuses and dates + $statuses = ['pending', 'processing', 'completed', 'cancelled']; + + for ($i = 1; $i <= 20; $i++) { + // Create orders spread across the last 6 months + $date = Carbon::now()->subDays(rand(1, 180))->format('Y-m-d H:i:s'); + $status = $statuses[array_rand($statuses)]; + $userId = $userIds[array_rand($userIds)]; + + // Random total between $50 and $5000 + $total = rand(5000, 500000) / 100; + + $order = Order::create([ + 'user_id' => $userId, + 'status' => $status, + 'total_amount' => $total, + 'created_at' => $date, + 'updated_at' => $date + ]); + + // Add order items + $itemCount = rand(1, 3); + $itemTotal = 0; + + for ($j = 0; $j < $itemCount; $j++) { + $productId = $productIds[array_rand($productIds)]; + $product = Product::find($productId); + + $quantity = rand(1, 5); + $price = $product->price; + + DB::table('order_items')->insert([ + 'order_id' => $order->id, + 'product_id' => $productId, + 'quantity' => $quantity, + 'price' => $price, + 'created_at' => $date, + 'updated_at' => $date + ]); + + $itemTotal += $price * $quantity; + } + + // Update order with correct total from items + $order->update([ + 'total_amount' => $itemTotal + ]); + } + + return response()->json([ + 'success' => true, + 'message' => 'Test data generated successfully', + 'counts' => [ + 'users' => User::count(), + 'products' => Product::count(), + 'orders' => Order::count(), + 'order_items' => DB::table('order_items')->count() + ] + ]); + } + + private function getMonthlySalesData() + { + try { + $data = DB::table('orders') + ->select(DB::raw('DATE_FORMAT(created_at, "%Y-%m") as month'), DB::raw('SUM(total_amount) as total')) + ->groupBy('month') + ->orderBy('month') + ->get() + ->map(function($item) { + try { + $date = Carbon::createFromFormat('Y-m', $item->month); + return [ + 'month' => $date->format('M Y'), + 'total' => (float)$item->total + ]; + } catch (\Exception $e) { + return [ + 'month' => $item->month, + 'total' => (float)$item->total, + 'error' => $e->getMessage() + ]; + } + }) + ->toArray(); + return $data; + } catch (\Exception $e) { + return ['error' => $e->getMessage()]; + } + } + + private function getCustomerSalesData() + { + try { + $data = DB::table('orders') + ->join('users', 'orders.user_id', '=', 'users.id') + ->select('users.name', DB::raw('SUM(orders.total_amount) as total')) + ->groupBy('users.id', 'users.name') + ->orderBy('total', 'desc') + ->limit(10) + ->get() + ->map(function($item) { + return [ + 'name' => $item->name, + 'total' => (float)$item->total + ]; + }) + ->toArray(); + return $data; + } catch (\Exception $e) { + return ['error' => $e->getMessage()]; + } + } + + private function getStatusData() + { + try { + $data = [ + 'pending' => Order::where('status', 'pending')->count(), + 'processing' => Order::where('status', 'processing')->count(), + 'completed' => Order::where('status', 'completed')->count(), + 'cancelled' => Order::where('status', 'cancelled')->count() + ]; + return $data; + } catch (\Exception $e) { + return ['error' => $e->getMessage()]; + } + } +} \ No newline at end of file diff --git a/app/Http/Controllers/UserController.php b/ClothingStore/app/Http/Controllers/UserController.php similarity index 100% rename from app/Http/Controllers/UserController.php rename to ClothingStore/app/Http/Controllers/UserController.php diff --git a/app/Http/Kernel.php b/ClothingStore/app/Http/Kernel.php similarity index 100% rename from app/Http/Kernel.php rename to ClothingStore/app/Http/Kernel.php diff --git a/app/Http/Middleware/AdminMiddleware.php b/ClothingStore/app/Http/Middleware/AdminMiddleware.php similarity index 100% rename from app/Http/Middleware/AdminMiddleware.php rename to ClothingStore/app/Http/Middleware/AdminMiddleware.php diff --git a/app/Http/Middleware/CheckPermission.php b/ClothingStore/app/Http/Middleware/CheckPermission.php similarity index 100% rename from app/Http/Middleware/CheckPermission.php rename to ClothingStore/app/Http/Middleware/CheckPermission.php diff --git a/app/Http/Middleware/CheckRole.php b/ClothingStore/app/Http/Middleware/CheckRole.php similarity index 100% rename from app/Http/Middleware/CheckRole.php rename to ClothingStore/app/Http/Middleware/CheckRole.php diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/ClothingStore/app/Http/Middleware/VerifyCsrfToken.php similarity index 100% rename from app/Http/Middleware/VerifyCsrfToken.php rename to ClothingStore/app/Http/Middleware/VerifyCsrfToken.php diff --git a/app/Http/Requests/Auth/LoginRequest.php b/ClothingStore/app/Http/Requests/Auth/LoginRequest.php similarity index 100% rename from app/Http/Requests/Auth/LoginRequest.php rename to ClothingStore/app/Http/Requests/Auth/LoginRequest.php diff --git a/app/Mail/OrderConfirmation.php b/ClothingStore/app/Mail/OrderConfirmation.php similarity index 100% rename from app/Mail/OrderConfirmation.php rename to ClothingStore/app/Mail/OrderConfirmation.php diff --git a/app/Mail/OrderShipped.php b/ClothingStore/app/Mail/OrderShipped.php similarity index 100% rename from app/Mail/OrderShipped.php rename to ClothingStore/app/Mail/OrderShipped.php diff --git a/app/Mail/OrderStatusUpdate.php b/ClothingStore/app/Mail/OrderStatusUpdate.php similarity index 100% rename from app/Mail/OrderStatusUpdate.php rename to ClothingStore/app/Mail/OrderStatusUpdate.php diff --git a/app/Models/Cart.php b/ClothingStore/app/Models/Cart.php similarity index 100% rename from app/Models/Cart.php rename to ClothingStore/app/Models/Cart.php diff --git a/app/Models/Order.php b/ClothingStore/app/Models/Order.php similarity index 100% rename from app/Models/Order.php rename to ClothingStore/app/Models/Order.php diff --git a/app/Models/OrderItem.php b/ClothingStore/app/Models/OrderItem.php similarity index 100% rename from app/Models/OrderItem.php rename to ClothingStore/app/Models/OrderItem.php diff --git a/app/Models/Permission.php b/ClothingStore/app/Models/Permission.php similarity index 100% rename from app/Models/Permission.php rename to ClothingStore/app/Models/Permission.php diff --git a/app/Models/Product.php b/ClothingStore/app/Models/Product.php similarity index 66% rename from app/Models/Product.php rename to ClothingStore/app/Models/Product.php index 5d40af5..3edcb15 100644 --- a/app/Models/Product.php +++ b/ClothingStore/app/Models/Product.php @@ -25,8 +25,11 @@ class Product extends Model public function getImageUrlAttribute() { if ($this->image) { - return asset('storage/' . $this->image); + // Ensure the path is correct and the file exists + $path = 'storage/' . $this->image; + return asset($path); } - return asset('images/no-image.png'); + // Use a default product image as fallback + return asset('img/product/product-1.jpg'); } } \ No newline at end of file diff --git a/app/Models/Role.php b/ClothingStore/app/Models/Role.php similarity index 100% rename from app/Models/Role.php rename to ClothingStore/app/Models/Role.php diff --git a/app/Models/User.php b/ClothingStore/app/Models/User.php similarity index 100% rename from app/Models/User.php rename to ClothingStore/app/Models/User.php diff --git a/app/Policies/OrderPolicy.php b/ClothingStore/app/Policies/OrderPolicy.php similarity index 100% rename from app/Policies/OrderPolicy.php rename to ClothingStore/app/Policies/OrderPolicy.php diff --git a/app/Policies/UserPolicy.php b/ClothingStore/app/Policies/UserPolicy.php similarity index 100% rename from app/Policies/UserPolicy.php rename to ClothingStore/app/Policies/UserPolicy.php diff --git a/app/Providers/AppServiceProvider.php b/ClothingStore/app/Providers/AppServiceProvider.php similarity index 100% rename from app/Providers/AppServiceProvider.php rename to ClothingStore/app/Providers/AppServiceProvider.php diff --git a/app/Providers/AuthServiceProvider.php b/ClothingStore/app/Providers/AuthServiceProvider.php similarity index 100% rename from app/Providers/AuthServiceProvider.php rename to ClothingStore/app/Providers/AuthServiceProvider.php diff --git a/app/Providers/EventServiceProvider.php b/ClothingStore/app/Providers/EventServiceProvider.php similarity index 100% rename from app/Providers/EventServiceProvider.php rename to ClothingStore/app/Providers/EventServiceProvider.php diff --git a/app/Providers/RoleServiceProvider.php b/ClothingStore/app/Providers/RoleServiceProvider.php similarity index 100% rename from app/Providers/RoleServiceProvider.php rename to ClothingStore/app/Providers/RoleServiceProvider.php diff --git a/app/Providers/RouteServiceProvider.php b/ClothingStore/app/Providers/RouteServiceProvider.php similarity index 94% rename from app/Providers/RouteServiceProvider.php rename to ClothingStore/app/Providers/RouteServiceProvider.php index bcd8867..0767329 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/ClothingStore/app/Providers/RouteServiceProvider.php @@ -12,7 +12,7 @@ class RouteServiceProvider extends ServiceProvider * * @var string */ - public const HOME = '/dashboard'; // Change this to your desired redirect path + public const HOME = '/home'; /** * Define your route model bindings, pattern filters, etc. diff --git a/app/helpers.php b/ClothingStore/app/helpers.php similarity index 100% rename from app/helpers.php rename to ClothingStore/app/helpers.php diff --git a/artisan b/ClothingStore/artisan similarity index 100% rename from artisan rename to ClothingStore/artisan diff --git a/bootstrap/app.php b/ClothingStore/bootstrap/app.php similarity index 100% rename from bootstrap/app.php rename to ClothingStore/bootstrap/app.php diff --git a/bootstrap/cache/.gitignore b/ClothingStore/bootstrap/cache/.gitignore similarity index 100% rename from bootstrap/cache/.gitignore rename to ClothingStore/bootstrap/cache/.gitignore diff --git a/bootstrap/providers.php b/ClothingStore/bootstrap/providers.php similarity index 100% rename from bootstrap/providers.php rename to ClothingStore/bootstrap/providers.php diff --git a/composer.json b/ClothingStore/composer.json similarity index 98% rename from composer.json rename to ClothingStore/composer.json index 130bcd0..6a583d3 100644 --- a/composer.json +++ b/ClothingStore/composer.json @@ -9,6 +9,7 @@ "php": "^8.2", "laravel/framework": "^12.0", "laravel/sanctum": "^4.1", + "laravel/socialite": "^5.20", "laravel/tinker": "^2.10.1" }, "require-dev": { diff --git a/composer.lock b/ClothingStore/composer.lock similarity index 94% rename from composer.lock rename to ClothingStore/composer.lock index 24fa497..5f923be 100644 --- a/composer.lock +++ b/ClothingStore/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "015e02e23a490fcf9d86230851cba120", + "content-hash": "d0d7e1e4d77aef2968ad5521d663c53a", "packages": [ { "name": "brick/math", @@ -510,6 +510,69 @@ ], "time": "2025-03-06T22:45:56+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v6.11.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + }, + "time": "2025-04-09T20:32:01+00:00" + }, { "name": "fruitcake/php-cors", "version": "v1.3.0", @@ -1453,6 +1516,78 @@ }, "time": "2025-03-19T13:51:03+00:00" }, + { + "name": "laravel/socialite", + "version": "v5.20.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "30972c12a41f71abeb418bc9ff157da8d9231519" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/30972c12a41f71abeb418bc9ff157da8d9231519", + "reference": "30972c12a41f71abeb418bc9ff157da8d9231519", + "shasum": "" + }, + "require": { + "ext-json": "*", + "firebase/php-jwt": "^6.4", + "guzzlehttp/guzzle": "^6.0|^7.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "league/oauth1-client": "^1.11", + "php": "^7.2|^8.0", + "phpseclib/phpseclib": "^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", + "phpstan/phpstan": "^1.12.23", + "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Socialite": "Laravel\\Socialite\\Facades\\Socialite" + }, + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Socialite\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.", + "homepage": "https://laravel.com", + "keywords": [ + "laravel", + "oauth" + ], + "support": { + "issues": "https://github.com/laravel/socialite/issues", + "source": "https://github.com/laravel/socialite" + }, + "time": "2025-04-21T14:21:34+00:00" + }, { "name": "laravel/tinker", "version": "v2.10.1", @@ -1896,6 +2031,82 @@ ], "time": "2024-09-21T08:32:55+00:00" }, + { + "name": "league/oauth1-client", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth1-client.git", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "guzzlehttp/psr7": "^1.7|^2.0", + "php": ">=7.1||>=8.0" + }, + "require-dev": { + "ext-simplexml": "*", + "friendsofphp/php-cs-fixer": "^2.17", + "mockery/mockery": "^1.3.3", + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5||9.5" + }, + "suggest": { + "ext-simplexml": "For decoding XML-based responses." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev", + "dev-develop": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth1\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Corlett", + "email": "bencorlett@me.com", + "homepage": "http://www.webcomm.com.au", + "role": "Developer" + } + ], + "description": "OAuth 1.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "bitbucket", + "identity", + "idp", + "oauth", + "oauth1", + "single sign on", + "trello", + "tumblr", + "twitter" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth1-client/issues", + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.11.0" + }, + "time": "2024-12-10T19:59:05+00:00" + }, { "name": "league/uri", "version": "7.5.1", @@ -2572,6 +2783,123 @@ ], "time": "2024-11-21T10:39:51+00:00" }, + { + "name": "paragonie/constant_time_encoding", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", + "shasum": "" + }, + "require": { + "php": "^8" + }, + "require-dev": { + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4|^5" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2024-05-08T12:36:18+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.3", @@ -2647,6 +2975,116 @@ ], "time": "2024-07-20T21:41:07+00:00" }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.43", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "709ec107af3cb2f385b9617be72af8cf62441d02" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02", + "reference": "709ec107af3cb2f385b9617be72af8cf62441d02", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2|^3", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.43" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2024-12-14T21:12:59+00:00" + }, { "name": "psr/clock", "version": "1.0.0", diff --git a/config/app.php b/ClothingStore/config/app.php similarity index 100% rename from config/app.php rename to ClothingStore/config/app.php diff --git a/config/auth.php b/ClothingStore/config/auth.php similarity index 97% rename from config/auth.php rename to ClothingStore/config/auth.php index 0ba5d5d..63a3f23 100644 --- a/config/auth.php +++ b/ClothingStore/config/auth.php @@ -40,6 +40,11 @@ 'driver' => 'session', 'provider' => 'users', ], + + 'api' => [ + 'driver' => 'sanctum', + 'provider' => 'users', + ], ], /* diff --git a/config/cache.php b/ClothingStore/config/cache.php similarity index 100% rename from config/cache.php rename to ClothingStore/config/cache.php diff --git a/config/database.php b/ClothingStore/config/database.php similarity index 100% rename from config/database.php rename to ClothingStore/config/database.php diff --git a/config/filesystems.php b/ClothingStore/config/filesystems.php similarity index 100% rename from config/filesystems.php rename to ClothingStore/config/filesystems.php diff --git a/config/logging.php b/ClothingStore/config/logging.php similarity index 100% rename from config/logging.php rename to ClothingStore/config/logging.php diff --git a/config/mail.php b/ClothingStore/config/mail.php similarity index 100% rename from config/mail.php rename to ClothingStore/config/mail.php diff --git a/config/queue.php b/ClothingStore/config/queue.php similarity index 100% rename from config/queue.php rename to ClothingStore/config/queue.php diff --git a/ClothingStore/config/sanctum.php b/ClothingStore/config/sanctum.php new file mode 100644 index 0000000..1dd1caf --- /dev/null +++ b/ClothingStore/config/sanctum.php @@ -0,0 +1,73 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort(), + // Sanctum::currentRequestHost(), + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, + 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class, + ], + +]; diff --git a/config/services.php b/ClothingStore/config/services.php similarity index 97% rename from config/services.php rename to ClothingStore/config/services.php index fcc8c2e..477b110 100644 --- a/config/services.php +++ b/ClothingStore/config/services.php @@ -64,7 +64,7 @@ 'client_id' => env('LINKEDIN_CLIENT_ID'), 'client_secret' => env('LINKEDIN_CLIENT_SECRET'), 'redirect' => env('LINKEDIN_REDIRECT_URI', 'http://127.0.0.1:8000/auth/linkedin/callback'), - 'api_version' => 'v2', + 'api_version' => env('LINKEDIN_API_VERSION', 'v2'), 'scopes' => ['openid', 'profile', 'email'], ], ]; diff --git a/config/session.php b/ClothingStore/config/session.php similarity index 100% rename from config/session.php rename to ClothingStore/config/session.php diff --git a/database/.gitignore b/ClothingStore/database/.gitignore similarity index 100% rename from database/.gitignore rename to ClothingStore/database/.gitignore diff --git a/database/factories/UserFactory.php b/ClothingStore/database/factories/UserFactory.php similarity index 100% rename from database/factories/UserFactory.php rename to ClothingStore/database/factories/UserFactory.php diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/ClothingStore/database/migrations/0001_01_01_000000_create_users_table.php similarity index 100% rename from database/migrations/0001_01_01_000000_create_users_table.php rename to ClothingStore/database/migrations/0001_01_01_000000_create_users_table.php diff --git a/database/migrations/0001_01_01_000001_create_cache_table.php b/ClothingStore/database/migrations/0001_01_01_000001_create_cache_table.php similarity index 100% rename from database/migrations/0001_01_01_000001_create_cache_table.php rename to ClothingStore/database/migrations/0001_01_01_000001_create_cache_table.php diff --git a/database/migrations/0001_01_01_000002_create_jobs_table.php b/ClothingStore/database/migrations/0001_01_01_000002_create_jobs_table.php similarity index 100% rename from database/migrations/0001_01_01_000002_create_jobs_table.php rename to ClothingStore/database/migrations/0001_01_01_000002_create_jobs_table.php diff --git a/database/migrations/2024_03_18_000000_create_roles_table.php b/ClothingStore/database/migrations/2024_03_18_000000_create_roles_table.php similarity index 100% rename from database/migrations/2024_03_18_000000_create_roles_table.php rename to ClothingStore/database/migrations/2024_03_18_000000_create_roles_table.php diff --git a/database/migrations/2024_03_18_000001_create_role_user_table.php b/ClothingStore/database/migrations/2024_03_18_000001_create_role_user_table.php similarity index 100% rename from database/migrations/2024_03_18_000001_create_role_user_table.php rename to ClothingStore/database/migrations/2024_03_18_000001_create_role_user_table.php diff --git a/database/migrations/2024_03_19_000001_create_products_table.php b/ClothingStore/database/migrations/2024_03_19_000001_create_products_table.php similarity index 100% rename from database/migrations/2024_03_19_000001_create_products_table.php rename to ClothingStore/database/migrations/2024_03_19_000001_create_products_table.php diff --git a/database/migrations/2024_03_19_000002_create_orders_table.php b/ClothingStore/database/migrations/2024_03_19_000002_create_orders_table.php similarity index 100% rename from database/migrations/2024_03_19_000002_create_orders_table.php rename to ClothingStore/database/migrations/2024_03_19_000002_create_orders_table.php diff --git a/database/migrations/2024_03_19_000003_create_order_items_table.php b/ClothingStore/database/migrations/2024_03_19_000003_create_order_items_table.php similarity index 100% rename from database/migrations/2024_03_19_000003_create_order_items_table.php rename to ClothingStore/database/migrations/2024_03_19_000003_create_order_items_table.php diff --git a/database/migrations/2024_03_21_000000_update_orders_table.php b/ClothingStore/database/migrations/2024_03_21_000000_update_orders_table.php similarity index 100% rename from database/migrations/2024_03_21_000000_update_orders_table.php rename to ClothingStore/database/migrations/2024_03_21_000000_update_orders_table.php diff --git a/database/migrations/2024_03_21_000001_create_order_items_table.php b/ClothingStore/database/migrations/2024_03_21_000001_create_order_items_table.php similarity index 100% rename from database/migrations/2024_03_21_000001_create_order_items_table.php rename to ClothingStore/database/migrations/2024_03_21_000001_create_order_items_table.php diff --git a/database/migrations/2024_03_21_add_featured_to_products_table.php b/ClothingStore/database/migrations/2024_03_21_add_featured_to_products_table.php similarity index 100% rename from database/migrations/2024_03_21_add_featured_to_products_table.php rename to ClothingStore/database/migrations/2024_03_21_add_featured_to_products_table.php diff --git a/database/migrations/2024_03_21_add_image_url_to_products_table.php b/ClothingStore/database/migrations/2024_03_21_add_image_url_to_products_table.php similarity index 100% rename from database/migrations/2024_03_21_add_image_url_to_products_table.php rename to ClothingStore/database/migrations/2024_03_21_add_image_url_to_products_table.php diff --git a/database/migrations/2024_03_21_create_carts_table.php b/ClothingStore/database/migrations/2024_03_21_create_carts_table.php similarity index 100% rename from database/migrations/2024_03_21_create_carts_table.php rename to ClothingStore/database/migrations/2024_03_21_create_carts_table.php diff --git a/database/migrations/2025_04_30_000000_add_social_fields_to_users_table.php b/ClothingStore/database/migrations/2025_04_30_000000_add_social_fields_to_users_table.php similarity index 100% rename from database/migrations/2025_04_30_000000_add_social_fields_to_users_table.php rename to ClothingStore/database/migrations/2025_04_30_000000_add_social_fields_to_users_table.php diff --git a/ClothingStore/database/migrations/2025_05_24_193242_create_personal_access_tokens_table.php b/ClothingStore/database/migrations/2025_05_24_193242_create_personal_access_tokens_table.php new file mode 100644 index 0000000..e828ad8 --- /dev/null +++ b/ClothingStore/database/migrations/2025_05_24_193242_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/ClothingStore/database/seeders/DatabaseSeeder.php similarity index 100% rename from database/seeders/DatabaseSeeder.php rename to ClothingStore/database/seeders/DatabaseSeeder.php diff --git a/database/seeders/RolePermissionSeeder.php b/ClothingStore/database/seeders/RolePermissionSeeder.php similarity index 100% rename from database/seeders/RolePermissionSeeder.php rename to ClothingStore/database/seeders/RolePermissionSeeder.php diff --git a/database/seeders/RoleSeeder.php b/ClothingStore/database/seeders/RoleSeeder.php similarity index 100% rename from database/seeders/RoleSeeder.php rename to ClothingStore/database/seeders/RoleSeeder.php diff --git a/database/seeders/TestDataSeeder.php b/ClothingStore/database/seeders/TestDataSeeder.php similarity index 100% rename from database/seeders/TestDataSeeder.php rename to ClothingStore/database/seeders/TestDataSeeder.php diff --git a/database/seeders/UserSeeder.php b/ClothingStore/database/seeders/UserSeeder.php similarity index 100% rename from database/seeders/UserSeeder.php rename to ClothingStore/database/seeders/UserSeeder.php diff --git a/database/websec_project.sql b/ClothingStore/database/websec_project.sql similarity index 76% rename from database/websec_project.sql rename to ClothingStore/database/websec_project.sql index ba7ec1d..6f117d5 100644 --- a/database/websec_project.sql +++ b/ClothingStore/database/websec_project.sql @@ -3,7 +3,7 @@ -- https://www.phpmyadmin.net/ -- -- Host: 127.0.0.1 --- Generation Time: May 16, 2025 at 07:34 PM +-- Generation Time: May 24, 2025 at 11:01 PM -- Server version: 10.4.32-MariaDB -- PHP Version: 8.2.12 @@ -38,8 +38,32 @@ CREATE TABLE `cache` ( -- INSERT INTO `cache` (`key`, `value`, `expiration`) VALUES -('laravel_cache_customer1@example.com|127.0.0.1', 'i:1;', 1747408962), -('laravel_cache_customer1@example.com|127.0.0.1:timer', 'i:1747408962;', 1747408962); +('laravel_cache_0ade7c2cf97f75d009975f4d720d1fa6c19f4897', 'i:1;', 1748023310), +('laravel_cache_0ade7c2cf97f75d009975f4d720d1fa6c19f4897:timer', 'i:1748023310;', 1748023310), +('laravel_cache_12c6fc06c99a462375eeb3f43dfd832b08ca9e17', 'i:2;', 1748029655), +('laravel_cache_12c6fc06c99a462375eeb3f43dfd832b08ca9e17:timer', 'i:1748029655;', 1748029655), +('laravel_cache_1574bddb75c78a6fd2251d61e2993b5146201319', 'i:1;', 1748026853), +('laravel_cache_1574bddb75c78a6fd2251d61e2993b5146201319:timer', 'i:1748026853;', 1748026853), +('laravel_cache_17ba0791499db908433b80f37c5fbc89b870084b', 'i:2;', 1748024866), +('laravel_cache_17ba0791499db908433b80f37c5fbc89b870084b:timer', 'i:1748024866;', 1748024866), +('laravel_cache_472b07b9fcf2c2451e8781e944bf5f77cd8457c8', 'i:1;', 1748028982), +('laravel_cache_472b07b9fcf2c2451e8781e944bf5f77cd8457c8:timer', 'i:1748028982;', 1748028982), +('laravel_cache_4d134bc072212ace2df385dae143139da74ec0ef', 'i:2;', 1748030228), +('laravel_cache_4d134bc072212ace2df385dae143139da74ec0ef:timer', 'i:1748030228;', 1748030228), +('laravel_cache_887309d048beef83ad3eabf2a79a64a389ab1c9f', 'i:1;', 1748031056), +('laravel_cache_887309d048beef83ad3eabf2a79a64a389ab1c9f:timer', 'i:1748031056;', 1748031056), +('laravel_cache_91032ad7bbcb6cf72875e8e8207dcfba80173f7c', 'i:2;', 1748028387), +('laravel_cache_91032ad7bbcb6cf72875e8e8207dcfba80173f7c:timer', 'i:1748028387;', 1748028387), +('laravel_cache_b1d5781111d84f7b3fe45a0852e59758cd7a87e5', 'i:1;', 1748024629), +('laravel_cache_b1d5781111d84f7b3fe45a0852e59758cd7a87e5:timer', 'i:1748024629;', 1748024629), +('laravel_cache_bc33ea4e26e5e1af1408321416956113a4658763', 'i:2;', 1748110898), +('laravel_cache_bc33ea4e26e5e1af1408321416956113a4658763:timer', 'i:1748110898;', 1748110898), +('laravel_cache_bd307a3ec329e10a2cff8fb87480823da114f8f4', 'i:2;', 1748026269), +('laravel_cache_bd307a3ec329e10a2cff8fb87480823da114f8f4:timer', 'i:1748026269;', 1748026269), +('laravel_cache_d435a6cdd786300dff204ee7c2ef942d3e9034e2', 'i:2;', 1748029957), +('laravel_cache_d435a6cdd786300dff204ee7c2ef942d3e9034e2:timer', 'i:1748029957;', 1748029957), +('laravel_cache_f6e1126cedebf23e1463aee73f9df08783640400', 'i:1;', 1748030560), +('laravel_cache_f6e1126cedebf23e1463aee73f9df08783640400:timer', 'i:1748030560;', 1748030560); -- -------------------------------------------------------- @@ -149,7 +173,8 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (11, '2024_03_21_000001_create_order_items_table', 3), (12, '2024_03_21_add_featured_to_products_table', 4), (13, '2024_03_21_add_image_url_to_products_table', 5), -(14, '2024_03_21_create_carts_table', 6); +(14, '2024_03_21_create_carts_table', 6), +(15, '2025_05_24_193242_create_personal_access_tokens_table', 7); -- -------------------------------------------------------- @@ -179,7 +204,10 @@ CREATE TABLE `orders` ( INSERT INTO `orders` (`id`, `user_id`, `total_amount`, `status`, `tracking_number`, `shipping_address`, `shipping_city`, `shipping_state`, `shipping_zipcode`, `shipping_country`, `payment_method`, `created_at`, `updated_at`) VALUES (1, 4, 99.99, 'completed', NULL, 'wsdfrvgtujh', 'Cairo', 'qswdavfbgvn', '555', 'CA', 'credit_card', '2025-05-15 11:53:46', '2025-05-15 19:20:03'), -(2, 4, 2000.00, 'completed', NULL, 'Al Mehwar', 'Cairo', 'qswdavfbgvn', '555', 'CA', 'credit_card', '2025-05-16 12:26:16', '2025-05-16 12:30:38'); +(2, 4, 2000.00, 'completed', NULL, 'Al Mehwar', 'Cairo', 'qswdavfbgvn', '555', 'CA', 'credit_card', '2025-05-16 12:26:16', '2025-05-16 12:30:38'), +(3, 4, 45050.00, 'pending', NULL, 'cierp', 'nnnn', 'nnnnn', 'vvvv', 'GB', 'credit_card', '2025-05-16 18:42:39', '2025-05-16 18:42:39'), +(4, 4, 57500.00, 'pending', NULL, 'a', 'aa', 'a', 'a', 'BR', 'credit_card', '2025-05-16 18:47:14', '2025-05-16 18:47:14'), +(5, 27, 2500.00, 'completed', NULL, 'fhlfklsjasklfjaklfjasklfjk;lf', 'cairo', 'cghfljfl', '12555', 'JP', 'credit_card', '2025-05-24 15:25:32', '2025-05-24 15:27:48'); -- -------------------------------------------------------- @@ -197,13 +225,6 @@ CREATE TABLE `order_items` ( `updated_at` timestamp NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; --- --- Dumping data for table `order_items` --- - -INSERT INTO `order_items` (`id`, `order_id`, `product_id`, `quantity`, `price`, `created_at`, `updated_at`) VALUES -(2, 2, 16, 1, 2000.00, '2025-05-16 12:26:16', '2025-05-16 12:26:16'); - -- -------------------------------------------------------- -- @@ -247,6 +268,32 @@ CREATE TABLE `permission_role` ( -- -------------------------------------------------------- +-- +-- Table structure for table `personal_access_tokens` +-- + +CREATE TABLE `personal_access_tokens` ( + `id` bigint(20) UNSIGNED NOT NULL, + `tokenable_type` varchar(255) NOT NULL, + `tokenable_id` bigint(20) UNSIGNED NOT NULL, + `name` varchar(255) NOT NULL, + `token` varchar(64) NOT NULL, + `abilities` text DEFAULT NULL, + `last_used_at` timestamp NULL DEFAULT NULL, + `expires_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- +-- Dumping data for table `personal_access_tokens` +-- + +INSERT INTO `personal_access_tokens` (`id`, `tokenable_type`, `tokenable_id`, `name`, `token`, `abilities`, `last_used_at`, `expires_at`, `created_at`, `updated_at`) VALUES +(2, 'App\\Models\\User', 1, 'api-token', 'e1395f71d650a13ada887426ae29bd816e93c902ec9ecd363afdbb75e22679e0', '[\"*\"]', NULL, NULL, '2025-05-24 17:52:46', '2025-05-24 17:52:46'); + +-- -------------------------------------------------------- + -- -- Table structure for table `products` -- @@ -270,8 +317,8 @@ CREATE TABLE `products` ( -- INSERT INTO `products` (`id`, `name`, `description`, `price`, `stock`, `image`, `created_at`, `updated_at`, `is_featured`, `is_sale`, `image_url`) VALUES -(16, 'Laptop Prooooo', 'sdsvfbgnjm,k', 2000.00, 5, 'products/LdS35de0gQL8PnviSAVkDHWzN2eLTeGRXQFcv8JG.jpg', '2025-05-15 18:01:54', '2025-05-15 18:23:23', 0, 0, NULL), -(18, 'Address', 'SWDEFRGTHYUJKILO;P\'[', 50.00, 33, 'products/m8rvdSBmF5244g6aQnVDJW9EEkuBFmUTen5IfAX5.jpg', '2025-05-15 18:16:07', '2025-05-15 18:16:07', 0, 0, NULL); +(24, 'Shoes', 'very good shoes', 6000.00, 30, 'products/UZgzjiEHbJany11mwgzlIfIlu4WpXvBVFJSJ2yy9.jpg', '2025-05-24 17:25:43', '2025-05-24 17:43:57', 0, 0, NULL), +(25, 'Polo', 'excellent', 3000.00, 20, 'products/Y8EQNQ8Cwr8wi0IMe56Bi1oHvFheHUC1DUusJEUi.jpg', '2025-05-24 17:44:45', '2025-05-24 17:46:46', 0, 0, NULL); -- -------------------------------------------------------- @@ -320,7 +367,8 @@ INSERT INTO `role_user` (`id`, `role_id`, `user_id`, `created_at`, `updated_at`) (1, 1, 1, NULL, NULL), (2, 2, 2, NULL, NULL), (3, 3, 3, NULL, NULL), -(4, 4, 4, NULL, NULL); +(4, 4, 4, NULL, NULL), +(22, 4, 27, NULL, NULL); -- -------------------------------------------------------- @@ -342,7 +390,7 @@ CREATE TABLE `sessions` ( -- INSERT INTO `sessions` (`id`, `user_id`, `ip_address`, `user_agent`, `payload`, `last_activity`) VALUES -('yCzCldKrNmaUC5vSns4vf566j7gqX76NgS05gKaE', 1, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36', 'YTo0OntzOjY6Il90b2tlbiI7czo0MDoiZ0VoaTZQUFdMQW5adkZ5eDRsYXdzcWJnN2VGbHVsR3JGQk1PM3dmVyI7czo2OiJfZmxhc2giO2E6Mjp7czozOiJvbGQiO2E6MDp7fXM6MzoibmV3IjthOjA6e319czo5OiJfcHJldmlvdXMiO2E6MTp7czozOiJ1cmwiO3M6MjE6Imh0dHA6Ly8xMjcuMC4wLjE6ODAwMCI7fXM6NTA6ImxvZ2luX3dlYl81OWJhMzZhZGRjMmIyZjk0MDE1ODBmMDE0YzdmNThlYTRlMzA5ODlkIjtpOjE7fQ==', 1747409968); +('pSu4NTubjAe7CtDqrUeMvs4cc0VpTu1uejfL5WID', 2, '127.0.0.1', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36', 'YTo0OntzOjY6Il90b2tlbiI7czo0MDoiQ2VWaDZDaWxJcVVaN2NSa1c3dk43YnBBRTVBazlrc3M2ZmxIaktUdyI7czo2OiJfZmxhc2giO2E6Mjp7czozOiJvbGQiO2E6MDp7fXM6MzoibmV3IjthOjA6e319czo5OiJfcHJldmlvdXMiO2E6MTp7czozOiJ1cmwiO3M6NDQ6Imh0dHBzOi8vY2xvdGhpbmdzdG9yZS5sb2NhbGhvc3QuY29tL3Byb2R1Y3RzIjt9czo1MDoibG9naW5fd2ViXzU5YmEzNmFkZGMyYjJmOTQwMTU4MGYwMTRjN2Y1OGVhNGUzMDk4OWQiO2k6Mjt9', 1748120451); -- -------------------------------------------------------- @@ -372,7 +420,8 @@ INSERT INTO `users` (`id`, `name`, `email`, `email_verified_at`, `password`, `pr (1, 'Admin User', 'admin@example.com', '2025-05-13 17:45:30', '$2y$12$wJLMR6mKL42KgL/8mi0k1.4wecUMTBg/CCRo2bHvLJIRhMjDUy00e', NULL, NULL, NULL, NULL, '2025-05-13 17:45:30', '2025-05-15 13:36:19'), (2, 'Manager User', 'manager@example.com', '2025-05-13 17:45:30', '$2y$12$ZKZLKfUhwSiRuOvboI46n.aEbFErPH0/QoHpNlLkRR5HHCEzU3oaG', NULL, NULL, NULL, NULL, '2025-05-13 17:45:30', '2025-05-15 13:36:19'), (3, 'Staff User', 'staff@example.com', '2025-05-13 17:45:30', '$2y$12$Z8BhJjJfPJlZyEZnmp13GOxsbVOldy28bTboVMMtz9DvmrK1FGWWS', NULL, NULL, NULL, NULL, '2025-05-13 17:45:30', '2025-05-15 13:36:19'), -(4, 'Customer User', 'customer@example.com', '2025-05-13 17:45:30', '$2y$12$803eQunmHDPrpMJWqGnQ/OI18NFFs0WsYCpz5AXvHpArdzw8gWpJi', NULL, NULL, NULL, NULL, '2025-05-13 17:45:30', '2025-05-15 13:36:19'); +(4, 'Customer User', 'customer@example.com', '2025-05-13 17:45:30', '$2y$12$803eQunmHDPrpMJWqGnQ/OI18NFFs0WsYCpz5AXvHpArdzw8gWpJi', NULL, NULL, NULL, NULL, '2025-05-13 17:45:30', '2025-05-15 13:36:19'), +(27, 'WebSec Project', 'websecproject71@gmail.com', '2025-05-24 15:20:58', '$2y$12$CvzVDEF.Z77.osNxlbxlreOFoxrrjSaPDu0Af.sZ27Fd8fLkImLV6', 'facebook', '122111608544860346', 'https://graph.facebook.com/v3.3/122111608544860346/picture', 'VYSG8SrUG8yBatTVEbjoKozXo3UCktdeK9CgFx7ssxGpAX5tcvzEGbECWd0o', '2025-05-24 15:20:30', '2025-05-24 17:10:23'); -- -- Indexes for dumped tables @@ -460,6 +509,14 @@ ALTER TABLE `permission_role` ADD KEY `permission_role_permission_id_foreign` (`permission_id`), ADD KEY `permission_role_role_id_foreign` (`role_id`); +-- +-- Indexes for table `personal_access_tokens` +-- +ALTER TABLE `personal_access_tokens` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `personal_access_tokens_token_unique` (`token`), + ADD KEY `personal_access_tokens_tokenable_type_tokenable_id_index` (`tokenable_type`,`tokenable_id`); + -- -- Indexes for table `products` -- @@ -522,19 +579,19 @@ ALTER TABLE `jobs` -- AUTO_INCREMENT for table `migrations` -- ALTER TABLE `migrations` - MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=15; + MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=16; -- -- AUTO_INCREMENT for table `orders` -- ALTER TABLE `orders` - MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6; -- -- AUTO_INCREMENT for table `order_items` -- ALTER TABLE `order_items` - MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7; -- -- AUTO_INCREMENT for table `permissions` @@ -548,11 +605,17 @@ ALTER TABLE `permissions` ALTER TABLE `permission_role` MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT; +-- +-- AUTO_INCREMENT for table `personal_access_tokens` +-- +ALTER TABLE `personal_access_tokens` + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3; + -- -- AUTO_INCREMENT for table `products` -- ALTER TABLE `products` - MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=19; + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=26; -- -- AUTO_INCREMENT for table `roles` @@ -564,13 +627,13 @@ ALTER TABLE `roles` -- AUTO_INCREMENT for table `role_user` -- ALTER TABLE `role_user` - MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5; + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=23; -- -- AUTO_INCREMENT for table `users` -- ALTER TABLE `users` - MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6; + MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=28; -- -- Constraints for dumped tables diff --git a/package-lock.json b/ClothingStore/package-lock.json similarity index 100% rename from package-lock.json rename to ClothingStore/package-lock.json diff --git a/package.json b/ClothingStore/package.json similarity index 100% rename from package.json rename to ClothingStore/package.json diff --git a/phpunit.xml b/ClothingStore/phpunit.xml similarity index 100% rename from phpunit.xml rename to ClothingStore/phpunit.xml diff --git a/public/.htaccess b/ClothingStore/public/.htaccess similarity index 100% rename from public/.htaccess rename to ClothingStore/public/.htaccess diff --git a/ClothingStore/public/clear-cache.php b/ClothingStore/public/clear-cache.php new file mode 100644 index 0000000..75c108d --- /dev/null +++ b/ClothingStore/public/clear-cache.php @@ -0,0 +1,42 @@ +make(Illuminate\Contracts\Console\Kernel::class); +$kernel->bootstrap(); + +echo "
✅ Application cache cleared
"; +} catch (Exception $e) { + echo "❌ Error clearing application cache: " . $e->getMessage() . "
"; +} + +try { + // Clear route cache + $kernel->call('route:clear'); + echo "✅ Route cache cleared
"; +} catch (Exception $e) { + echo "❌ Error clearing route cache: " . $e->getMessage() . "
"; +} + +try { + // Clear config cache + $kernel->call('config:clear'); + echo "✅ Config cache cleared
"; +} catch (Exception $e) { + echo "❌ Error clearing config cache: " . $e->getMessage() . "
"; +} + +try { + // Clear view cache + $kernel->call('view:clear'); + echo "✅ View cache cleared
"; +} catch (Exception $e) { + echo "❌ Error clearing view cache: " . $e->getMessage() . "
"; +} + +echo "Done! Return to home
"; \ No newline at end of file diff --git a/public/css/bootstrap.min.css b/ClothingStore/public/css/bootstrap.min.css similarity index 100% rename from public/css/bootstrap.min.css rename to ClothingStore/public/css/bootstrap.min.css diff --git a/public/css/elegant-icons.css b/ClothingStore/public/css/elegant-icons.css similarity index 100% rename from public/css/elegant-icons.css rename to ClothingStore/public/css/elegant-icons.css diff --git a/public/css/font-awesome.min.css b/ClothingStore/public/css/font-awesome.min.css similarity index 100% rename from public/css/font-awesome.min.css rename to ClothingStore/public/css/font-awesome.min.css diff --git a/public/css/magnific-popup.css b/ClothingStore/public/css/magnific-popup.css similarity index 100% rename from public/css/magnific-popup.css rename to ClothingStore/public/css/magnific-popup.css diff --git a/public/css/nice-select.css b/ClothingStore/public/css/nice-select.css similarity index 100% rename from public/css/nice-select.css rename to ClothingStore/public/css/nice-select.css diff --git a/public/css/owl.carousel.min.css b/ClothingStore/public/css/owl.carousel.min.css similarity index 100% rename from public/css/owl.carousel.min.css rename to ClothingStore/public/css/owl.carousel.min.css diff --git a/public/css/slicknav.min.css b/ClothingStore/public/css/slicknav.min.css similarity index 100% rename from public/css/slicknav.min.css rename to ClothingStore/public/css/slicknav.min.css diff --git a/ClothingStore/public/css/social-login.css b/ClothingStore/public/css/social-login.css new file mode 100644 index 0000000..40494df --- /dev/null +++ b/ClothingStore/public/css/social-login.css @@ -0,0 +1,57 @@ +/** + * Social Login Buttons Styling + */ +.social-btn { + color: white; + margin: 5px; + transition: all 0.3s ease; + padding: 8px 15px; + border-radius: 4px; + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 120px; +} + +.social-btn:hover { + opacity: 0.9; + color: white; + transform: translateY(-2px); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +.social-btn i { + margin-right: 8px; +} + +.facebook-btn { + background-color: #3b5998; +} + +.google-btn { + background-color: #dd4b39; +} + +.github-btn { + background-color: #333333; +} + +.twitter-btn { + background-color: #1DA1F2; +} + +.linkedin-btn { + background-color: #0077b5; +} + +.social-auth-links { + margin-top: 25px; + border-top: 1px solid #e5e5e5; + padding-top: 20px; +} + +.social-auth-links p { + margin-bottom: 15px; + color: #6c757d; + font-weight: 500; +} diff --git a/public/css/style.css b/ClothingStore/public/css/style.css similarity index 93% rename from public/css/style.css rename to ClothingStore/public/css/style.css index 8a01129..9cf1ab6 100644 --- a/public/css/style.css +++ b/ClothingStore/public/css/style.css @@ -1,4 +1,4 @@ -/****************************************************************** +/* ***************************************************************** Template Name: Male Fashion Description: Male Fashion - ecommerce teplate Author: Colorib @@ -44,7 +44,7 @@ h4, h5, h6 { margin: 0; - color: wheat; + color: #111111; font-weight: 400; font-family: "Nunito Sans", sans-serif; } @@ -76,7 +76,7 @@ h6 { p { font-size: 15px; font-family: "Nunito Sans", sans-serif; - color: wheat; + color: #3d3d3d; font-weight: 400; line-height: 25px; margin: 0 0 15px 0; @@ -126,7 +126,7 @@ ol { } .section-title h2 { - color: whitesmoke; + color: #111111; font-weight: 700; line-height: 46px; } @@ -164,14 +164,14 @@ ol { text-transform: uppercase; padding: 14px 30px; color: #ffffff; - background: maroon; + background: #000000; letter-spacing: 4px; } .site-btn { font-size: 14px; color: #ffffff; - background: mediumblue; + background: #111111; font-weight: 700; border: none; text-transform: uppercase; @@ -256,7 +256,7 @@ ol { display: block; height: 100%; padding-top: 30px; - background: wheat; + background: #323232; text-align: center; cursor: pointer; } @@ -268,7 +268,7 @@ ol { height: 100%; left: 0; top: 0; - background: white; + background: #000; z-index: 99999; } @@ -280,7 +280,7 @@ ol { width: 500px; font-size: 40px; border: none; - border-bottom: 2px solid lawngreen; + border-bottom: 2px solid #333; background: 0 0; color: #999; } @@ -316,16 +316,16 @@ ol { -----------------------*/ .header { - background: black; + background: #ffffff; } .header__top { - background: black; + background: #111111; padding: 10px 0; } .header__top__left p { - background: black; + color: #ffffff; margin-bottom: 0; } @@ -339,7 +339,7 @@ ol { } .header__top__links a { - background: black; + color: #ffffff; font-size: 13px; text-transform: uppercase; letter-spacing: 2px; @@ -363,7 +363,7 @@ ol { } .header__top__hover span { - background: black; + color: #ffffff; font-size: 13px; text-transform: uppercase; letter-spacing: 2px; @@ -379,7 +379,7 @@ ol { } .header__top__hover ul { - background: black; + background: #ffffff; display: inline-block; padding: 2px 0; position: absolute; @@ -480,7 +480,7 @@ ol { .header__menu ul li a { font-size: 18px; - color: black; + color: #111111; display: block; font-weight: 600; position: relative; @@ -528,8 +528,7 @@ ol { .header__nav__option .price { font-size: 15px; - color: black; - ; + color: #111111; font-weight: 700; display: inline-block; margin-left: -20px; @@ -571,7 +570,7 @@ ol { .hero__slider.owl-carousel .owl-nav button { font-size: 36px; - color: black; + color: #333333; position: absolute; left: 75px; top: 50%; @@ -711,7 +710,7 @@ ol { } .banner__item__text h2 { - color:white; + color: #111111; font-weight: 700; line-height: 46px; margin-bottom: 10px; @@ -719,7 +718,7 @@ ol { .banner__item__text a { display: inline-block; - color: white; + color: #111111; font-size: 13px; font-weight: 700; letter-spacing: 4px; @@ -890,7 +889,7 @@ ol { } .instagram__text h2 { - color: white; + color: #111111; font-weight: 700; margin-bottom: 30px; } @@ -933,7 +932,7 @@ ol { } .filter__controls li.active { - color: red; + color: #111111; } .product__item { @@ -980,7 +979,7 @@ ol { } .product__item__pic .label { - color: wheat; + color: #111111; font-size: 11px; font-weight: 700; letter-spacing: 2px; @@ -1036,7 +1035,7 @@ ol { top: 5px; height: 15px; width: 15px; - background: wheat; + background: #111111; content: ""; -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); @@ -1070,7 +1069,7 @@ ol { } .product__item__text h6 { - color: wheat; + color: #111111; font-size: 15px; font-weight: 600; margin-bottom: 5px; @@ -1090,7 +1089,7 @@ ol { } .product__item__text h5 { - color: white; + color: #0d0d0d; font-weight: 700; } @@ -1229,7 +1228,7 @@ ol { } .shop__sidebar__accordion .card-heading a { - color: whitesmoke; + color: #111111; font-size: 16px; font-weight: 700; text-transform: uppercase; @@ -1410,7 +1409,7 @@ ol { font-family: "FontAwesome"; font-size: 24px; font-weight: 700; - color: whitesmoke; + color: #111111; position: absolute; right: 0; top: 2px; @@ -1422,7 +1421,7 @@ ol { } .shop__product__option p { - color: wheat; + color: #111111; margin-bottom: 0; } @@ -1454,7 +1453,7 @@ ol { } .shop__product__option__right .nice-select span { - color: wheat; + color: #111111; font-size: 15px; font-weight: 700; } @@ -1472,7 +1471,7 @@ ol { display: inline-block; font-size: 16px; font-weight: 700; - color: wheat; + color: #111111; height: 30px; width: 30px; border: 1px solid transparent; @@ -1482,11 +1481,11 @@ ol { } .product__pagination a.active { - border-color: wheat; + border-color: #111111; } .product__pagination a:hover { - border-color: wheat; + border-color: #111111; } .product__pagination span { @@ -1933,122 +1932,6 @@ ol { text-align: center; } -/*--------------------- - Footer ------------------------*/ - -.footer { - background: #111111; - padding-top: 70px; -} - -.footer__about { - margin-bottom: 30px; -} - -.footer__about .footer__logo { - margin-bottom: 30px; -} - -.footer__about .footer__logo a { - display: inline-block; -} - -.footer__about p { - color: #b7b7b7; - margin-bottom: 30px; -} - -.footer__widget { - margin-bottom: 30px; -} - -.footer__widget h6 { - color: #ffffff; - font-size: 15px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 2px; - margin-bottom: 20px; -} - -.footer__widget ul li { - line-height: 36px; - list-style: none; -} - -.footer__widget ul li a { - color: #b7b7b7; - font-size: 15px; -} - -.footer__widget .footer__newslatter p { - color: #b7b7b7; -} - -.footer__widget .footer__newslatter form { - position: relative; -} - -.footer__widget .footer__newslatter form input { - width: 100%; - font-size: 15px; - color: #3d3d3d; - background: transparent; - border: none; - padding: 15px 0; - border-bottom: 2px solid #ffffff; -} - -.footer__widget .footer__newslatter form input::-webkit-input-placeholder { - color: #3d3d3d; -} - -.footer__widget .footer__newslatter form input::-moz-placeholder { - color: #3d3d3d; -} - -.footer__widget .footer__newslatter form input:-ms-input-placeholder { - color: #3d3d3d; -} - -.footer__widget .footer__newslatter form input::-ms-input-placeholder { - color: #3d3d3d; -} - -.footer__widget .footer__newslatter form input::placeholder { - color: #3d3d3d; -} - -.footer__widget .footer__newslatter form button { - color: #b7b7b7; - font-size: 16px; - position: absolute; - right: 5px; - top: 0; - height: 100%; - background: transparent; - border: none; -} - -.footer__copyright__text { - border-top: 1px solid rgba(255, 255, 255, 0.1); - padding: 20px 0; - margin-top: 40px; -} - -.footer__copyright__text p { - color: #b7b7b7; - margin-bottom: 0; -} - -.footer__copyright__text p i { - color: #e53637; -} - -.footer__copyright__text p a { - color: #e53637; -} /*--------------------- Breadcrumb @@ -2111,11 +1994,11 @@ ol { font-weight: 700; } -/*--------------------- +/* --------------------- About -----------------------*/ -.about { +/* .about { padding-bottom: 70px; } @@ -2139,7 +2022,7 @@ ol { .about__item p { margin-bottom: 0; -} +} */ /*--------------------- Testimonial @@ -3315,11 +3198,11 @@ ol { } .contact__form input:-ms-input-placeholder { - color: #b7b7b7b7; + color: #b7b7b7; } -.contact__form input::-ms-input{ - color: #b7b7b7b7; +.contact__form input::-ms-input-placeholder { + color: #b7b7b7; } .contact__form input::placeholder { @@ -3795,93 +3678,79 @@ ol { } } -/* Dark mode adjustments */ -html, body { - background-color: #121212; - color: #ffffff; -} - -h1, h2, h3, h4, h5, h6 { - color: #ffffff; -} - -p, a, span, li { - color: #e0e0e0; -} - -/* Adjusting buttons for dark mode */ -.primary-btn, .site-btn { - background: #333333; - color: #ffffff; -} - -/* Adjusting header and navbar for dark mode */ -.header { - background: #000000; /* Set header background to black */ -} - -.header__menu ul li a { - color: #ffffff; /* Set navbar text to white */ -} - -.header__top { - background: #000000; /* Set top header background to black */ -} - -.header__top__links a { - color: #ffffff; /* Set top header links to white */ -} - -.header__logo a { - color: #ffffff; /* Set logo text to white */ -} - -/* Adjusting footer for dark mode */ -.footer { - background: #1e1e1e; -} - -.footer__about p, .footer__widget ul li a { - color: #e0e0e0; -} - -.footer__copyright__text p { - color: #e0e0e0; -} - -/* Adjusting other sections for dark mode */ -.section-title span { - color: #bb86fc; -} - -.section-title h2 { - color: #ffffff; -} - -/* Adjusting product section */ -.product__item__text h6, .product__item__text h5 { - color: #ffffff; -} - -.product__item__text a { - color: #bb86fc; -} +/* Small Device = 320px */ -/* Adjusting breadcrumb */ -.breadcrumb-option { - background: #1e1e1e; +@media only screen and (max-width: 479px) { + .cart__total { + padding: 35px 30px 40px; + } + .hero__items { + height: auto; + padding-top: 130px; + padding-bottom: 40px; + } + .hero__text h2 { + font-size: 36px; + line-height: 48px; + } + .hero__social { + margin-top: 145px; + } + .categories__deal__countdown .categories__deal__countdown__timer { + margin-left: 0; + } + .instagram__pic__item { + width: 100%; + } + .testimonial__text { + padding: 60px 40px 60px; + } + .product__details__pic .nav-tabs .nav-item .nav-link .product__thumb__pic { + width: 100%; + } + .product__details__pic .nav-tabs .nav-item { + margin-bottom: 10px; + width: calc(33.33% - 10px); + } + .product__details__last__option h5:before { + width: 280px; + } + .product__details__cart__option .quantity { + margin-right: 0; + margin-bottom: 15px; + } + .product__details__last__option h5 span { + font-size: 16px; + } + .blog__hero__text h2 { + font-size: 36px; + } + .categories__text h2 { + font-size: 30px; + line-height: 55px; + } + .categories__text:before { + height: 250px; + } } -.breadcrumb__text h4, .breadcrumb__links a { - color: #ffffff; +.counter__item__number { + display: flex; + align-items: baseline; + justify-content: center; + gap: 4px; } - - -@media (min-width: 992px) { - .offcanvas-menu-wrapper { - display: none !important; - } - .offcanvas-menu-overlay { - display: none !important; - } +.counter__item__number h2, +.counter__item__number strong { + color: #ffd700 !important; + font-size: 48px; + font-weight: 700; } +.counter__item span { + display: block; + margin-top: 10px; + font-size: 20px; + color: #666; + font-weight: 500; + text-align: center; +} \ No newline at end of file diff --git a/public/css/style.css.map b/ClothingStore/public/css/style.css.map similarity index 100% rename from public/css/style.css.map rename to ClothingStore/public/css/style.css.map diff --git a/public/css/style2.css b/ClothingStore/public/css/style2.css similarity index 100% rename from public/css/style2.css rename to ClothingStore/public/css/style2.css diff --git a/ClothingStore/public/debug-charts.php b/ClothingStore/public/debug-charts.php new file mode 100644 index 0000000..fdd6e91 --- /dev/null +++ b/ClothingStore/public/debug-charts.php @@ -0,0 +1,130 @@ +make(Illuminate\Contracts\Http\Kernel::class); +$response = $kernel->handle( + $request = Illuminate\Http\Request::capture() +); + +use App\Models\Order; +use App\Models\User; +use Illuminate\Support\Facades\DB; +use Carbon\Carbon; +use Illuminate\Support\Facades\Log; + +echo ""; + print_r($monthlySales); + echo ""; + } catch (\Exception $e) { + echo "
" . $e->getMessage() . "
"; + } + + // Generate customer sales data + try { + $customerSales = DB::table('orders') + ->join('users', 'orders.user_id', '=', 'users.id') + ->select('users.name', DB::raw('SUM(orders.total_amount) as total')) + ->groupBy('users.id', 'users.name') + ->orderBy('total', 'desc') + ->limit(10) + ->get() + ->map(function($item) { + return [ + 'name' => $item->name, + 'total' => (float)$item->total + ]; + })->toArray(); + + echo ""; + print_r($customerSales); + echo ""; + } catch (\Exception $e) { + echo "
" . $e->getMessage() . "
"; + } + + // Get order status counts + $statusCounts = [ + 'pending' => Order::where('status', 'pending')->count(), + 'processing' => Order::where('status', 'processing')->count(), + 'completed' => Order::where('status', 'completed')->count(), + 'cancelled' => Order::where('status', 'cancelled')->count() + ]; + + echo ""; + print_r($statusCounts); + echo ""; + + // Output everything as JSON + echo json_encode([ + 'stats' => $stats, + 'recent_orders' => $orderData, + 'chart_data' => [ + 'monthly_sales' => $monthlySales, + 'customer_sales' => $customerSales, + 'status_counts' => $statusCounts + ] + ], JSON_PRETTY_PRINT); + +} catch (Exception $e) { + echo json_encode([ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ], JSON_PRETTY_PRINT); +} \ No newline at end of file diff --git a/ClothingStore/public/direct-charts.php b/ClothingStore/public/direct-charts.php new file mode 100644 index 0000000..07c65f1 --- /dev/null +++ b/ClothingStore/public/direct-charts.php @@ -0,0 +1,316 @@ +make(Illuminate\Contracts\Console\Kernel::class); +$kernel->bootstrap(); + +use App\Models\Order; +use App\Models\User; +use Illuminate\Support\Facades\DB; +use Carbon\Carbon; + +try { + // Generate monthly sales data - manual approach + $monthly = []; + $sixMonthsAgo = Carbon::now()->subMonths(6)->startOfMonth(); + $ordersForChart = Order::where('created_at', '>=', $sixMonthsAgo) + ->get(); + + // Group by month and calculate totals + $ordersByMonth = []; + foreach ($ordersForChart as $order) { + $monthKey = $order->created_at->format('Y-m'); + $monthDisplay = $order->created_at->format('M Y'); + + if (!isset($ordersByMonth[$monthKey])) { + $ordersByMonth[$monthKey] = [ + 'month' => $monthDisplay, + 'total' => 0 + ]; + } + + $ordersByMonth[$monthKey]['total'] += $order->total_amount; + } + + // Sort by month + ksort($ordersByMonth); + + // Convert to indexed array + $monthly = array_values($ordersByMonth); + + // Generate customer sales data - manual approach + $customers = []; + $userTotals = []; + + foreach ($ordersForChart as $order) { + $userId = $order->user_id; + $userName = $order->user->name ?? 'Unknown User'; + + if (!isset($userTotals[$userId])) { + $userTotals[$userId] = [ + 'name' => $userName, + 'total' => 0 + ]; + } + + $userTotals[$userId]['total'] += $order->total_amount; + } + + // Sort by total sales (descending) + usort($userTotals, function($a, $b) { + return $b['total'] <=> $a['total']; + }); + + // Get top 10 customers + $customers = array_slice(array_values($userTotals), 0, 10); + + // Get order status counts + $statusCounts = [ + 'pending' => Order::where('status', 'pending')->count(), + 'processing' => Order::where('status', 'processing')->count(), + 'completed' => Order::where('status', 'completed')->count(), + 'cancelled' => Order::where('status', 'cancelled')->count() + ]; + + // If no data, create sample data + if (empty($monthly)) { + $monthly = [ + ['month' => 'Jan 2023', 'total' => 12500], + ['month' => 'Feb 2023', 'total' => 17800], + ['month' => 'Mar 2023', 'total' => 14200], + ['month' => 'Apr 2023', 'total' => 22000], + ['month' => 'May 2023', 'total' => 25000] + ]; + } + + if (empty($customers)) { + $customers = [ + ['name' => 'Customer User', 'total' => 45050], + ['name' => 'John Doe', 'total' => 23500], + ['name' => 'Jane Smith', 'total' => 18700], + ['name' => 'Robert Johnson', 'total' => 12400], + ['name' => 'Lisa Brown', 'total' => 8900] + ]; + } + + // Convert data to JSON for JavaScript + $monthlyDataJson = json_encode($monthly); + $customerDataJson = json_encode($customers); + $statusDataJson = json_encode($statusCounts); + + // Output HTML with embedded charts + header('Content-Type: text/html'); +} catch (Exception $e) { + $error = $e->getMessage(); + $trace = $e->getTraceAsString(); +} +?> + + + + + +
This page tests if Chart.js is working correctly in your browser.
+ +Free shipping, 30-day return or refund guarantee.
+Free shipping, 30-day return or refund guarantee.
+
+ We are a dedicated team of fashion enthusiasts committed to bringing you the latest trends and highest quality products. Our passion for style and customer satisfaction drives everything we do.
+We curate and deliver exceptional fashion products that help our customers express their unique style. From trendy clothing to accessories, we ensure every item meets our high standards of quality.
+With years of experience in the fashion industry, we offer unmatched quality, competitive prices, and exceptional customer service. Your satisfaction is our top priority.
+"We don’t just design clothes, we design moods. If you’ve ever worn a hoodie and felt like a superhero, yeah… that’s us" +
+ +
+
+
+
+
+
+ Free shipping, 30-day return or refund guarantee.
-| {{ __('Image') }} | -{{ __('Name') }} | -{{ __('Category') }} | -{{ __('Price') }} | -{{ __('Stock') }} | -{{ __('Actions') }} | -
|---|---|---|---|---|---|
- @if($product->image)
-
- @endif
- |
- {{ $product->name }} | -{{ ucfirst($product->category) }} | -${{ number_format($product->price, 2) }} | -{{ $product->stock }} | -- - | -
| {{ __('No products found.') }} | -|||||
-
-
-
-@endsection
\ No newline at end of file
diff --git a/resources/views/webfront/contact.blade.php b/resources/views/webfront/contact.blade.php
deleted file mode 100644
index 3691929..0000000
--- a/resources/views/webfront/contact.blade.php
+++ /dev/null
@@ -1,18 +0,0 @@
-@extends('layouts.master')
-
-@section('title', 'Home - HAM Store')
-
-@section('content')
-
-
-
-
-
-@endsection
\ No newline at end of file
diff --git a/routes/api.php b/routes/api.php
deleted file mode 100644
index d504c74..0000000
--- a/routes/api.php
+++ /dev/null
@@ -1,8 +0,0 @@
-get('/ping', function () {
- return response()->json(['message' => 'pong']);
-});
\ No newline at end of file