diff --git a/app/Http/Controllers/Public/ClassificationController.php b/app/Http/Controllers/Public/ClassificationController.php index bae2f75..320a0cd 100644 --- a/app/Http/Controllers/Public/ClassificationController.php +++ b/app/Http/Controllers/Public/ClassificationController.php @@ -6,27 +6,73 @@ use App\Models\Classification; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Http; + class ClassificationController extends Controller { - public function store(Request $request) + public function index() + { + $classifications = Classification::where('user_id', auth()->id())->get(); + return inertia('user/classification/ClassificationIndex', [ + 'classifications' => $classifications, + ]); + } + + + public function predict(Request $request) { $request->validate([ 'image' => 'required|image|max:2048', - 'result' => 'required|string', - 'confidence' => 'required', ]); - // Simpan gambar ke storage public - $path = $request->file('image')->store('classifications', 'public'); + $image = $request->file('image'); - // Simpan ke Database - Classification::create([ - 'user_id' => auth()->id(), // null jika guest, id jika login - 'image_path' => $path, - 'result' => $request->result, - 'confidence' => (float) filter_var($request->confidence, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION), - ]); + try { + // 1. Kirim ke AI Server untuk prediksi + $response = Http::attach( + 'image', + file_get_contents($image), + $image->getClientOriginalName() + )->post('http://127.0.0.1:5001/predict'); - return response()->json(['message' => 'Data tersimpan ke history']); + if ($response->failed()) { + return response()->json(['error' => 'Gagal terhubung ke AI server'], 500); + } + + $data = $response->json(); + + // 2. Jika sukses, simpan gambar dan hasil ke database + $path = $image->store('classifications', 'public'); + + Classification::create([ + 'user_id' => auth()->id() ?? null, + 'image_path' => $path, + 'result' => $data['label'], + 'confidence' => (float) filter_var($data['confidence'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION), + ]); + + return response()->json([ + 'label' => $data['label'], + 'confidence' => $data['confidence'], + 'message' => 'Hasil klasifikasi berhasil disimpan' + ]); + } catch (\Exception $e) { + return response()->json(['error' => 'Terjadi kesalahan: ' . $e->getMessage()], 500); + } + } + public function destroy(Classification $classification) + { + if ($classification->user_id !== auth()->id()) { + abort(403); + } + + // Hapus file dari storage jika ada + if ($classification->image_path && \Storage::disk('public')->exists($classification->image_path)) { + \Storage::disk('public')->delete($classification->image_path); + } + + $classification->delete(); + + return back()->with('success', 'Klasifikasi berhasil dihapus'); } } \ No newline at end of file diff --git a/resources/js/app.ts b/resources/js/app.ts index 2903edf..d112344 100644 --- a/resources/js/app.ts +++ b/resources/js/app.ts @@ -20,6 +20,8 @@ createInertiaApp({ return [AppLayout, SettingsLayout]; case name.startsWith('admin/'): return [AppLayout]; + case name.startsWith('user/'): + return [AppLayout]; default: return PublicLayout; } diff --git a/resources/js/components/AppSidebar.vue b/resources/js/components/AppSidebar.vue index a7ab867..7c28627 100644 --- a/resources/js/components/AppSidebar.vue +++ b/resources/js/components/AppSidebar.vue @@ -2,7 +2,7 @@ import { Link, usePage } from '@inertiajs/vue3'; import { computed } from 'vue'; import { route } from 'ziggy-js'; -import { BookOpen, FolderGit2, LayoutGrid, Users } from 'lucide-vue-next'; +import { BookOpen, FolderGit2, LayoutGrid, Users, ScanSearch } from 'lucide-vue-next'; import AppLogo from '@/components/AppLogo.vue'; import NavFooter from '@/components/NavFooter.vue'; import NavMain from '@/components/NavMain.vue'; @@ -23,8 +23,7 @@ const page = usePage(); // Ambil data user dari auth yang dikirim via HandleInertiaRequests const user = computed(() => page.props.auth.user); -// 1. Definisikan semua menu, tambahkan properti 'role' untuk menu yang dibatasi -const allNavItems: NavItem[] = [ + const adminNavItems: NavItem[] = [ { title: 'Dashboard', href: route('admin.dashboard'), @@ -34,20 +33,41 @@ const allNavItems: NavItem[] = [ title: 'Users Management', href: route('admin.users.index'), icon: Users, - role: 'admin', // Hanya untuk admin }, ]; -// 2. Filter menu berdasarkan role user yang sedang login -const filteredNavItems = computed(() => { - return allNavItems.filter(item => { - // Jika menu tidak punya batasan role, tampilkan untuk semua - if (!item.role) return true; - // Jika ada batasan role, cek apakah role user cocok (admin === admin) - return item.role === user.value?.role; - }); +const userNavItems: NavItem[] = [ + { + title: 'Dashboard', + href: route('dashboard'), + icon: LayoutGrid, + }, + { + title: 'Klasifikasi', + href: route('classifications.index'), + icon: ScanSearch, + }, +]; + + +const navItems = computed(() => { + if (user.value?.role === 'admin') { + return adminNavItems; + } else { + return userNavItems; + } }); +// 2. Filter menu berdasarkan role user yang sedang login +// const filteredNavItems = computed(() => { +// return allNavItems.filter(item => { +// // Jika menu tidak punya batasan role, tampilkan untuk semua +// if (!item.role) return true; +// // Jika ada batasan role, cek apakah role user cocok (admin === admin) +// return item.role === user.value?.role; +// }); +// }); + const footerNavItems: NavItem[] = [ { title: 'Repository', @@ -77,7 +97,7 @@ const footerNavItems: NavItem[] = [ - + diff --git a/resources/js/components/public/FinalCta.vue b/resources/js/components/public/FinalCta.vue index dc50ff7..dc1e1dc 100644 --- a/resources/js/components/public/FinalCta.vue +++ b/resources/js/components/public/FinalCta.vue @@ -4,11 +4,14 @@

Pantau Riwayat Klasifikasi

-

+

Masuk ke akun Anda sekarang untuk menyimpan hasil dan melihat kembali seluruh riwayat klasifikasi biji kopi yang pernah Anda lakukan.

+

+ Pantau riwayat klasifikasi biji kopi Anda sekarang. +

-
+
+
+ + Dashboard + +
+

Gratis sepenuhnya. Jangan biarkan riwayat klasifikasi Anda hilang.

@@ -31,5 +43,9 @@ diff --git a/resources/js/components/public/Navbar.vue b/resources/js/components/public/Navbar.vue index 82e59c5..d831128 100644 --- a/resources/js/components/public/Navbar.vue +++ b/resources/js/components/public/Navbar.vue @@ -31,18 +31,37 @@ @@ -88,9 +107,13 @@ diff --git a/resources/js/components/public/Scanner.vue b/resources/js/components/public/Scanner.vue index 4428403..0995ed1 100644 --- a/resources/js/components/public/Scanner.vue +++ b/resources/js/components/public/Scanner.vue @@ -98,6 +98,8 @@ \ No newline at end of file diff --git a/resources/js/pages/user/classification/ClassificationIndex.vue b/resources/js/pages/user/classification/ClassificationIndex.vue new file mode 100644 index 0000000..57b34cb --- /dev/null +++ b/resources/js/pages/user/classification/ClassificationIndex.vue @@ -0,0 +1,204 @@ + + + diff --git a/resources/views/app.blade.php b/resources/views/app.blade.php index ca7be5d..2715e4b 100644 --- a/resources/views/app.blade.php +++ b/resources/views/app.blade.php @@ -3,6 +3,7 @@ + {{-- Inline script to detect system dark mode preference and apply it immediately --}}