all(), [ 'tomato_image' => [ 'required', 'file', 'image', 'mimes:jpeg,jpg,png', 'max:5120', // 5MB max ], ], [ 'tomato_image.required' => 'Gambar tomat harus diunggah.', 'tomato_image.file' => 'File harus berupa gambar.', 'tomato_image.image' => 'File harus berupa gambar.', 'tomato_image.mimes' => 'Format file yang diizinkan: JPG, JPEG, PNG.', 'tomato_image.max' => 'Ukuran file maksimal 5MB.', ]); if ($validator->fails()) { return redirect() ->route('upload.index') ->withErrors($validator) ->withInput(); } try { $file = $request->file('tomato_image'); // Generate unique filename $filename = Str::uuid() . '.' . $file->getClientOriginalExtension(); // Create directory if it doesn't exist $uploadPath = 'uploads/tomatoes'; if (!Storage::disk('public')->exists($uploadPath)) { Storage::disk('public')->makeDirectory($uploadPath); } // Store the file $path = $file->storeAs($uploadPath, $filename, 'public'); // Simulate AI classification (replace with actual AI logic) $classification = $this->classifyTomato($path); // Redirect to result page with classification data return redirect()->route('upload.result', [ 'image' => $path, 'category' => $classification['category'], 'probability' => $classification['probability'] ]); } catch (\Exception $e) { return redirect() ->route('upload.index') ->withErrors(['upload' => 'Terjadi kesalahan saat mengunggah file: ' . $e->getMessage()]) ->withInput(); } } /** * Display the classification result. * * @param \Illuminate\Http\Request $request * @return \Illuminate\View\View */ public function result(Request $request) { // Get parameters from query string $imagePath = $request->get('image'); $category = $request->get('category', 'matang'); $probability = (int) $request->get('probability', 85); // Validate inputs if (!$imagePath) { return redirect()->route('upload.index')->withErrors(['error' => 'Tidak ada gambar yang dianalisis.']); } // Get color classes based on category $colors = $this->getCategoryColors($category); // Get description based on category $description = $this->getCategoryDescription($category, $probability); return view('result', [ 'imagePath' => $imagePath, 'category' => $category, 'probability' => $probability, 'maturityColor' => $colors['badge'], 'progressColor' => $colors['progress'], 'description' => $description ]); } /** * Simulate tomato classification (replace with actual AI implementation). * * @param string $imagePath * @return array */ private function classifyTomato($imagePath) { // Simulate AI classification with random results // In real implementation, this would call your AI model $categories = ['mentah', 'setengah matang', 'matang']; $category = $categories[array_rand($categories)]; $probability = rand(75, 98); // Random probability between 75-98% return [ 'category' => $category, 'probability' => $probability ]; } /** * Get color classes based on maturity category. * * @param string $category * @return array */ private function getCategoryColors($category) { $colors = [ 'mentah' => [ 'badge' => 'bg-green-500', 'progress' => 'bg-green-500' ], 'setengah matang' => [ 'badge' => 'bg-yellow-500', 'progress' => 'bg-yellow-500' ], 'matang' => [ 'badge' => 'bg-red-500', 'progress' => 'bg-red-500' ] ]; return $colors[$category] ?? $colors['matang']; } /** * Get description based on maturity category and probability. * * @param string $category * @param int $probability * @return string */ private function getCategoryDescription($category, $probability) { $descriptions = [ 'mentah' => "Tomat ini masih dalam tahap pertumbuhan awal dengan tingkat kematangan {$probability}%. Warna hijau menunjukkan bahwa tomat belum matang sempurna dan teksturnya masih keras. Direkomendasikan untuk menunggu beberapa hari agar tomat mencapai kematangan optimal.", 'setengah matang' => "Tomat ini dalam proses pematangan dengan tingkat kematangan {$probability}%. Perpaduan warna hijau dan merah menunjukkan tomat sedang transisi. Tekstur mulai melunak dan rasa mulai terasa. Ideal untuk penggunaan dalam salad atau masakan yang membutuhkan tomat sedikit masak.", 'matang' => "Tomat ini telah mencapai kematangan optimal dengan tingkat keyakinan {$probability}%. Warna merah cerah dan tekstur lembut menunjukkan tomat siap dikonsumsi. Kandungan nutrisi dan rasa manis alami telah mencapai puncaknya. Sempurna untuk dimakan langsung atau diolah dalam berbagai masakan." ]; return $descriptions[$category] ?? $descriptions['matang']; } /** * Handle admin login. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse */ public function adminLogin(Request $request) { // Validate the request $validator = Validator::make($request->all(), [ 'username' => 'required|string|max:255', 'password' => 'required|string|min:6', ], [ 'username.required' => 'Nama pengguna harus diisi.', 'username.string' => 'Nama pengguna harus berupa string.', 'username.max' => 'Nama pengguna maksimal 255 karakter.', 'password.required' => 'Kata sandi harus diisi.', 'password.string' => 'Kata sandi harus berupa string.', 'password.min' => 'Kata sandi minimal 6 karakter.', ]); if ($validator->fails()) { return redirect() ->route('admin.login') ->withErrors($validator) ->withInput(); } // Simple authentication (replace with proper authentication in production) $username = $request->input('username'); $password = $request->input('password'); // Demo credentials - replace with proper authentication if ($username === 'admin.tomat' && $password === 'admin123') { // Store session session(['admin_logged_in' => true, 'admin_username' => $username]); return redirect()->route('admin.dashboard')->with('success', 'Login berhasil! Selamat datang, ' . $username); } // Failed login return redirect() ->route('admin.login') ->withErrors(['login' => 'Nama pengguna atau kata sandi salah.']) ->withInput($request->except('password')); } }