get(); return view('admin.kategori', compact('categories')); } public function store(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string|max:1000', 'merk' => 'nullable|string|max:255', 'model' => 'nullable|string|max:255', 'kategori' => 'nullable|string|max:255', 'price' => 'required|numeric|min:0', 'year' => 'required|integer|min:1900|max:' . date('Y'), 'mesin_cc' => 'nullable|integer|min:0', 'transmisi' => 'nullable|string|max:255', 'bahan_bakar' => 'nullable|string|max:255', 'status' => 'required|in:available,reserved,sold', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', ], [ 'name.required' => 'Nama kategori wajib diisi.', 'description.required' => 'Deskripsi kategori wajib diisi.', 'price.required' => 'Harga mobil wajib diisi.', 'price.numeric' => 'Harga mobil harus berupa angka.', 'price.min' => 'Harga mobil tidak boleh negatif.', 'year.required' => 'Tahun mobil wajib diisi.', 'year.integer' => 'Tahun mobil harus berupa angka.', 'year.min' => 'Tahun mobil tidak valid.', 'year.max' => 'Tahun mobil tidak valid.', 'status.required' => 'Status mobil wajib dipilih.', 'status.in' => 'Status mobil tidak valid.', 'image.image' => 'File harus berupa gambar.', 'image.mimes' => 'Format gambar harus jpeg, png, jpg, atau gif.', 'image.max' => 'Ukuran gambar maksimal 2MB.', ]); if ($request->hasFile('image')) { $validated['image'] = $request->file('image')->store('categories', 'public'); } Category::create($validated); return redirect()->route('kategori')->with('success', 'Kategori berhasil ditambahkan.'); } public function edit(Category $category) { return view('admin.kategori-edit', compact('category')); } public function update(Request $request, Category $category) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string|max:1000', 'merk' => 'nullable|string|max:255', 'model' => 'nullable|string|max:255', 'kategori' => 'nullable|string|max:255', 'price' => 'required|numeric|min:0', 'year' => 'required|integer|min:1900|max:' . date('Y'), 'mesin_cc' => 'nullable|integer|min:0', 'transmisi' => 'nullable|string|max:255', 'bahan_bakar' => 'nullable|string|max:255', 'status' => 'required|in:available,reserved,sold', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', ], [ 'name.required' => 'Nama kategori wajib diisi.', 'description.required' => 'Deskripsi kategori wajib diisi.', 'price.required' => 'Harga mobil wajib diisi.', 'price.numeric' => 'Harga mobil harus berupa angka.', 'price.min' => 'Harga mobil tidak boleh negatif.', 'year.required' => 'Tahun mobil wajib diisi.', 'year.integer' => 'Tahun mobil harus berupa angka.', 'year.min' => 'Tahun mobil tidak valid.', 'year.max' => 'Tahun mobil tidak valid.', 'status.required' => 'Status mobil wajib dipilih.', 'status.in' => 'Status mobil tidak valid.', 'image.image' => 'File harus berupa gambar.', 'image.mimes' => 'Format gambar harus jpeg, png, jpg, atau gif.', 'image.max' => 'Ukuran gambar maksimal 2MB.', ]); if ($request->hasFile('image')) { if ($category->image) { Storage::disk('public')->delete($category->image); } $validated['image'] = $request->file('image')->store('categories', 'public'); } $category->update($validated); return redirect()->route('kategori')->with('success', 'Kategori berhasil diperbarui.'); } public function destroy(Category $category) { if ($category->image) { Storage::disk('public')->delete($category->image); } $category->delete(); return redirect()->route('kategori')->with('success', 'Kategori berhasil dihapus.'); } public function userIndex(Request $request, CarRecommendationService $recommendationService) { Category::releaseExpiredReservations(); $budget = $request->query('budget'); $budgetValue = is_numeric($budget) ? (float) $budget : null; $year = $request->query('year'); $yearValue = is_numeric($year) ? (int) $year : null; $mesinCc = $request->query('mesin_cc'); $mesinCcValue = is_numeric($mesinCc) ? (int) $mesinCc : null; $categories = Category::where('status', 'available')->orderBy('created_at', 'desc')->get(); $hasExactBudgetMatch = false; $recommendationSource = null; $recommendationMeta = null; if ($budgetValue) { $recommendation = $recommendationService->recommendForBudget($budgetValue, 50, [ 'year' => $yearValue, 'mesin_cc' => $mesinCcValue, ]); $recommended = $recommendation['cars']; $hasExactBudgetMatch = $recommendation['has_exact_budget_match']; $recommendationSource = $recommendation['source']; $recommendationMeta = $recommendation['meta']; } else { $recommended = Category::where('status', 'available') ->orderBy('created_at', 'desc') ->get(); } return view('user.cars', compact( 'categories', 'recommended', 'budget', 'year', 'mesinCc', 'hasExactBudgetMatch', 'recommendationSource', 'recommendationMeta' )); } public function allCars() { Category::releaseExpiredReservations(); $categories = Category::where('status', 'available') ->orderBy('created_at', 'desc') ->paginate(12); return view('user.all-cars', compact('categories')); } public function budgetCars(Request $request, CarRecommendationService $recommendationService) { Category::releaseExpiredReservations(); $budget = $request->query('budget'); $budgetValue = is_numeric($budget) ? (float) $budget : null; $year = $request->query('year'); $yearValue = is_numeric($year) ? (int) $year : null; $mesinCc = $request->query('mesin_cc'); $mesinCcValue = is_numeric($mesinCc) ? (int) $mesinCc : null; $availableCount = Category::where('status', 'available')->count(); $hasExactBudgetMatch = false; $recommendationSource = null; $recommendationMeta = null; $recommended = collect(); if ($budgetValue) { $recommendation = $recommendationService->recommendForBudget($budgetValue, 50, [ 'year' => $yearValue, 'mesin_cc' => $mesinCcValue, ]); $recommended = $recommendation['cars']; $hasExactBudgetMatch = $recommendation['has_exact_budget_match']; $recommendationSource = $recommendation['source']; $recommendationMeta = $recommendation['meta']; } return view('user.budget-cars', compact( 'budget', 'year', 'mesinCc', 'availableCount', 'recommended', 'hasExactBudgetMatch', 'recommendationSource', 'recommendationMeta' )); } public function show(Category $category) { Category::releaseExpiredReservations(); $category->refresh(); return view('user.car-detail', compact('category')); } public function addToFavorites(Category $category) { Category::releaseExpiredReservations(); $category->refresh(); $username = auth()->user()->username; if ($category->status !== 'available') { return response()->json(['message' => 'Mobil ini sedang tidak tersedia.'], 422); } // Check if already favorited if (Favorite::where('username', $username)->where('category_id', $category->id)->exists()) { return response()->json(['message' => 'Already in favorites'], 409); } Favorite::create([ 'username' => $username, 'category_id' => $category->id, ]); return response()->json(['message' => 'Added to favorites']); } public function removeFromFavorites(Category $category) { $username = auth()->user()->username; Favorite::where('username', $username) ->where('category_id', $category->id) ->delete(); return response()->json(['message' => 'Removed from favorites']); } }