diff --git a/README.md b/README.md
index 2b73786..e961413 100644
--- a/README.md
+++ b/README.md
@@ -66,9 +66,9 @@ ### 1. Admin Desa (Administrator)
### 2. Petani (Penjual)
*Hanya bisa berjualan setelah akun diverifikasi oleh Admin Desa.*
- [ ] **Registrasi Petani:** Pendaftaran akun dengan status awal "Menunggu Persetujuan".
-- [ ] **Dashboard Petani:** Ringkasan aktivitas toko.
+- [x] **Dashboard Petani:** Ringkasan aktivitas toko.
- [x] **Manajemen Produk (CRUD):** Tambah, Edit, Hapus, dan Lihat daftar produk pertanian.
-- [ ] **Manajemen Pesanan:** Menerima pesanan masuk dan mengubah status menjadi "Dikirim".
+- [x] **Manajemen Pesanan:** Menerima pesanan masuk dan mengubah status menjadi "Dikirim".
- [ ] **Kotak Masuk (Pesan):** Fitur tanya jawab dengan pembeli.
### 3. Pembeli (Publik)
@@ -76,10 +76,10 @@ ### 3. Pembeli (Publik)
- [ ] **Registrasi & Login:** Membuat akun pembeli.
- [x] **Katalog Produk:** Melihat dan mencari produk yang dijual petani.
- [x] **Detail Produk:** Melihat rincian produk.
-- [x] **Keranjang Belanja:** Menambah produk ke cart sebelum checkout.
-- [ ] **Checkout (Transaksi):** Melakukan pemesanan barang (Metode COD).
-- [ ] **Riwayat Transaksi:** Melacak status pesanan (Diproses/Dikirim/Selesai).
-- [ ] **Kirim Pesan:** Menghubungi petani terkait produk.
+- [ ] **Keranjang Belanja:** Menambah produk ke cart sebelum checkout.
+- [x] **Checkout (Transaksi):** Melakukan pemesanan barang (Metode COD).
+- [x] **Riwayat Transaksi:** Melacak status pesanan (Diproses/Dikirim/Selesai).
+- [ ] **Kirim Pesan:** Menghubungi petani terkait produk.
---
@@ -101,7 +101,7 @@ ## Installation Guide
### Step 1: Clone Repository
Unduh kode sumber dari repositori.
```bash
-git clone [https://github.com/sayasilvi/web-pertanian.git)
+git clone [https://github.com/sayasilvi/web-pertanian.git]
cd web-pertanian
```
@@ -127,15 +127,28 @@ ### Step 4: Environment Configuration
```
*Edit file `.env` lalu atur parameter `DB_DATABASE`, `DB_USERNAME`, dan `DB_PASSWORD` sesuai konfigurasi lokal Anda.*
-### Step 5: Application Key & Database
+### Step 5: Application Key
Generate key aplikasi dan jalankan migrasi database serta seeder data dummy.
```bash
php artisan key:generate
+```
+
+### Step 6: Database Migration
+Jalankan migrasi database serta seeder data dummy.
+
+```bash
php artisan migrate --seed
```
-### Step 6: Start Application
+### Step 7: Konfigurasi Storage Link
+Jalankan perintah untuk konfigurasi storage link untuk menyimpan gambar
+
+```bash
+php artisan storage:link
+```
+
+### Step 8: Start Application
**Terminal (Laravel Server):**
```bash
php artisan serve
@@ -178,7 +191,7 @@ ## Development Roadmap
- [x] **Module:** Admin Verification Workflow
- [x] **Module:** Shopping Cart & Checkout
- [ ] **Module:** Product Management (Vendor)
-- [ ] **Feature:** Transaction History Tracking
+- [x] **Feature:** Transaction History Tracking
- [ ] **Feature:** In-app Messaging System
---
diff --git a/app/Http/Controllers/CartController.php b/app/Http/Controllers/CartController.php
index 751c744..8af6b22 100644
--- a/app/Http/Controllers/CartController.php
+++ b/app/Http/Controllers/CartController.php
@@ -19,36 +19,47 @@ public function addToCart(Request $request)
{
$id = $request->id;
$produk = Produk::findOrFail($id);
-
+
$cart = session()->get('cart', []);
-
- // Jika produk sudah ada di cart, tambahkan jumlahnya
- if(isset($cart[$id])) {
- $cart[$id]['quantity'] += $request->qty;
+
+ // Validasi qty default ke 1 jika kosong
+ $quantity = $request->qty ? $request->qty : 1;
+
+ if (isset($cart[$id])) {
+ $cart[$id]['quantity'] += $quantity;
} else {
- // Jika belum ada, masukkan data baru
$cart[$id] = [
"name" => $produk->nama_produk,
- "quantity" => $request->qty,
+ "quantity" => $quantity,
"price" => $produk->harga,
"photo" => $produk->foto_produk
];
}
-
+
session()->put('cart', $cart);
return redirect()->back()->with('success', 'Produk berhasil masuk keranjang!');
}
+ public function updateCart(Request $request)
+ {
+ if ($request->id && $request->quantity) {
+ $cart = session()->get('cart');
+ $cart[$request->id]["quantity"] = $request->quantity;
+ session()->put('cart', $cart);
+ session()->flash('success', 'Keranjang berhasil diperbarui');
+ }
+ }
+
// Menghapus Item dari Keranjang
public function remove(Request $request)
{
- if($request->id) {
+ if ($request->id) {
$cart = session()->get('cart');
- if(isset($cart[$request->id])) {
+ if (isset($cart[$request->id])) {
unset($cart[$request->id]);
session()->put('cart', $cart);
}
return redirect()->back()->with('success', 'Produk dihapus dari keranjang');
}
}
-}
\ No newline at end of file
+}
diff --git a/app/Http/Controllers/Petani/DashboardController.php b/app/Http/Controllers/Petani/DashboardController.php
new file mode 100644
index 0000000..1630bb8
--- /dev/null
+++ b/app/Http/Controllers/Petani/DashboardController.php
@@ -0,0 +1,35 @@
+id();
+
+ //
+ $totalProduk = Produk::where('petani_id', $petaniId)->count();
+
+ // Hitung Pesanan Baru
+ $pesananBaru = DetailTransaksi::whereHas('produk', function($q) use ($petaniId) {
+ $q->where('petani_id', $petaniId);
+ })->whereHas('transaksi', function($q) {
+ $q->where('status', 'dibayar');
+ })->count();
+
+ // Hitung Total Pendapatan
+ $totalPendapatan = DetailTransaksi::whereHas('produk', function($q) use ($petaniId) {
+ $q->where('petani_id', $petaniId);
+ })->whereHas('transaksi', function($q) {
+ $q->where('status', 'selesai');
+ })->sum('subtotal');
+
+ return view('petani.dashboard', compact('totalProduk', 'pesananBaru', 'totalPendapatan'));
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/TransaksiController.php b/app/Http/Controllers/TransaksiController.php
index 1b2651e..ba148d1 100644
--- a/app/Http/Controllers/TransaksiController.php
+++ b/app/Http/Controllers/TransaksiController.php
@@ -16,52 +16,137 @@ class TransaksiController extends Controller
// Tampilkan Halaman Checkout
public function checkoutPage(Request $request)
{
+ // Beli Langsung (Buy Now)
if ($request->has('produk_id')) {
$produk = Produk::with('petani')->findOrFail($request->produk_id);
- return view('landing.checkout', compact('produk'));
+ $items = collect([
+ (object) [
+ 'id' => $produk->id,
+ 'produk' => $produk,
+ 'nama_produk' => $produk->nama_produk,
+ 'harga' => $produk->harga,
+ 'jumlah' => $request->qty ?? 1,
+ 'subtotal' => $produk->harga * ($request->qty ?? 1),
+ 'foto' => $produk->foto_produk
+ ]
+ ]);
+ $total_belanja = $items->sum('subtotal');
+ return view('landing.checkout', compact('items', 'total_belanja'));
}
- return redirect()->route('shop')->with('error', 'Silakan pilih produk yang ingin dibeli terlebih dahulu.');
+ // Checkout dari Keranjang
+ $cart = session()->get('cart');
+ if ($cart && count($cart) > 0) {
+ $items = collect();
+ foreach ($cart as $id => $details) {
+ $produk = Produk::find($id);
+ if ($produk) {
+ $items->push((object) [
+ 'id' => $id,
+ 'produk' => $produk,
+ 'nama_produk' => $details['name'],
+ 'harga' => $details['price'],
+ 'jumlah' => $details['quantity'],
+ 'subtotal' => $details['price'] * $details['quantity'],
+ 'foto' => $details['photo']
+ ]);
+ }
+ }
+ $total_belanja = $items->sum('subtotal');
+ return view('landing.checkout', compact('items', 'total_belanja'));
+ }
+
+ return redirect()->route('shop')->with('error', 'Keranjang Anda kosong, silakan belanja dulu.');
}
- // Proses Simpan Transaksi
+ // fungsi prosesCheckout
public function prosesCheckout(Request $request)
{
$request->validate([
- 'produk_id' => 'required|exists:produks,id',
- 'jumlah' => 'required|integer|min:1',
'alamat_pengiriman' => 'required|string',
- 'metode_pembayaran' => 'required|in:cod',
+ 'metode_pembayaran' => 'required|in:cod',
]);
- $produk = Produk::findOrFail($request->produk_id);
+ $pembeli_id = Auth::guard('pembeli')->id();
- if ($produk->stok < $request->jumlah) {
- return back()->with('error', 'Stok produk tidak mencukupi!');
- }
+ DB::transaction(function () use ($request, $pembeli_id) {
- $total_harga = $produk->harga * $request->jumlah;
+ if ($request->has('produk_id')) {
+ // LOGIKA BELI LANGSUNG (Single Item)
+ $produk = Produk::findOrFail($request->produk_id);
+ $total_harga = $produk->harga * $request->jumlah;
- DB::transaction(function () use ($request, $produk, $total_harga) {
- $transaksi = Transaksi::create([
- 'pembeli_id' => Auth::guard('pembeli')->id(),
- 'tanggal_transaksi' => now(),
- 'alamat_pengiriman' => $request->alamat_pengiriman,
- 'total_harga' => $total_harga,
- 'status' => 'menunggu_konfirmasi',
- 'kode_invoice' => 'INV/' . date('Ymd') . '/' . rand(1000, 9999),
- ]);
+ $transaksi = Transaksi::create([
+ 'pembeli_id' => $pembeli_id,
+ 'tanggal_transaksi' => now(),
+ 'alamat_pengiriman' => $request->alamat_pengiriman,
+ 'total_harga' => $total_harga,
+ 'status' => 'menunggu_konfirmasi',
+ 'kode_invoice' => 'INV/' . date('Ymd') . '/' . rand(1000, 9999),
+ ]);
- DetailTransaksi::create([
- 'transaksi_id' => $transaksi->id,
- 'produk_id' => $produk->id,
- 'jumlah' => $request->jumlah,
- 'harga_satuan' => $produk->harga,
- 'subtotal' => $total_harga,
- ]);
+ DetailTransaksi::create([
+ 'transaksi_id' => $transaksi->id,
+ 'produk_id' => $produk->id,
+ 'jumlah' => $request->jumlah,
+ 'harga_satuan' => $produk->harga,
+ 'subtotal' => $total_harga,
+ ]);
- // C. Kurangi Stok
- $produk->decrement('stok', $request->jumlah);
+ $produk->decrement('stok', $request->jumlah);
+ } else {
+ $cart = session()->get('cart');
+
+ // Kelompokkan produk berdasarkan Petani ID
+ $cartItems = [];
+ foreach ($cart as $id => $details) {
+ $produk = Produk::find($id);
+ if ($produk) {
+ $cartItems[$produk->petani_id][] = [
+ 'produk' => $produk,
+ 'qty' => $details['quantity']
+ ];
+ }
+ }
+
+ foreach ($cartItems as $petani_id => $items) {
+ $subtotal_transaksi = 0;
+ $kode_invoice = 'INV/' . date('Ymd') . '/' . rand(1000, 9999);
+
+ // Buat Header Transaksi dulu
+ $transaksi = Transaksi::create([
+ 'pembeli_id' => $pembeli_id,
+ 'tanggal_transaksi' => now(),
+ 'alamat_pengiriman' => $request->alamat_pengiriman,
+ 'total_harga' => 0,
+ 'status' => 'menunggu_konfirmasi',
+ 'kode_invoice' => $kode_invoice,
+ ]);
+
+ foreach ($items as $item) {
+ $produk = $item['produk'];
+ $qty = $item['qty'];
+ $total_per_item = $produk->harga * $qty;
+ $subtotal_transaksi += $total_per_item;
+
+ DetailTransaksi::create([
+ 'transaksi_id' => $transaksi->id,
+ 'produk_id' => $produk->id,
+ 'jumlah' => $qty,
+ 'harga_satuan' => $produk->harga,
+ 'subtotal' => $total_per_item,
+ ]);
+
+ $produk->decrement('stok', $qty);
+ }
+
+ // Update total harga transaksi
+ $transaksi->update(['total_harga' => $subtotal_transaksi]);
+ }
+
+ // Hapus Keranjang setelah sukses
+ session()->forget('cart');
+ }
});
return redirect()->route('pesanan.saya')->with('success', 'Pesanan berhasil dibuat! Menunggu konfirmasi petani.');
@@ -78,6 +163,22 @@ public function pesananSaya()
return view('landing.pesanan_saya', compact('transaksis'));
}
+ // Konfirmasi Pesanan Diterima oleh Pembeli
+ public function konfirmasiSelesai($id)
+ {
+ $transaksi = Transaksi::where('pembeli_id', Auth::guard('pembeli')->id())
+ ->findOrFail($id);
+
+ // Hanya bisa selesai jika status sebelumnya 'dikirim'
+ if ($transaksi->status == 'dikirim') {
+ $transaksi->status = 'selesai';
+ $transaksi->save();
+ return back()->with('success', 'Terima kasih! Pesanan telah diselesaikan.');
+ }
+
+ return back()->with('error', 'Pesanan belum dikirim atau sudah selesai.');
+ }
+
// --- FITUR PETANI ---
@@ -89,9 +190,9 @@ public function pesananMasuk()
$pesanans = Transaksi::whereHas('details.produk', function ($q) use ($petaniId) {
$q->where('petani_id', $petaniId);
})
- ->with(['pembeli', 'details.produk'])
- ->latest()
- ->get();
+ ->with(['pembeli', 'details.produk'])
+ ->latest()
+ ->get();
return view('petani.pesanan.index', compact('pesanans'));
}
@@ -100,7 +201,7 @@ public function pesananMasuk()
public function updateStatus(Request $request, $id)
{
$transaksi = Transaksi::findOrFail($id);
-
+
$request->validate([
'status' => 'required|in:diproses,dikirim,selesai,batal'
]);
@@ -126,9 +227,9 @@ public function pesananDetail($id)
$pesanan = Transaksi::whereHas('details.produk', function ($q) use ($petaniId) {
$q->where('petani_id', $petaniId);
})
- ->with(['pembeli', 'details.produk'])
- ->findOrFail($id);
+ ->with(['pembeli', 'details.produk'])
+ ->findOrFail($id);
return view('petani.pesanan.detail', compact('pesanan'));
}
-}
\ No newline at end of file
+}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index e58eee8..0bc7961 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -2,6 +2,7 @@
namespace Database\Seeders;
+use App\Models\Pembeli;
use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
@@ -19,6 +20,9 @@ public function run(): void
$this->call([
AdminSeeder::class,
+ PembeliSeeder::class,
+ PetaniSeeder::class,
+ ProdukSeeder::class,
]);
}
}
diff --git a/resources/views/landing/cart.blade.php b/resources/views/landing/cart.blade.php
index 4776311..c679476 100644
--- a/resources/views/landing/cart.blade.php
+++ b/resources/views/landing/cart.blade.php
@@ -3,7 +3,6 @@
@section('title', 'Keranjang Belanja')
@section('content')
-
-
+ {{-- Alert Notification --}}
+ @if(session('success'))
+
+ {{ session('success') }}
+
+
+ @endif
+
@@ -34,12 +40,12 @@
$total = $details['price'] * $details['quantity'];
$total_belanja += $total;
@endphp
-
+
 : asset('template/frontend/img/vegetable-item-3.png') }})
+ alt="{{ $details['name'] }}">
|
@@ -51,16 +57,16 @@ class="img-fluid me-5 rounded-circle" style="width: 80px; height: 80px;"
|
-
-
Total
- Rp {{ number_format($total_belanja + 10000, 0, ',', '.') }}
+ Rp {{ number_format($total_belanja, 0, ',', '.') }}
+
+ {{-- Button Checkout --}}
Proses Checkout
@@ -125,3 +129,54 @@ class="btn border-secondary rounded-pill px-4 py-3 text-primary text-uppercase m
@endsection
+
+@section('js')
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/landing/checkout.blade.php b/resources/views/landing/checkout.blade.php
index 9284bee..18b4536 100644
--- a/resources/views/landing/checkout.blade.php
+++ b/resources/views/landing/checkout.blade.php
@@ -3,89 +3,67 @@
@section('title', 'Checkout')
@section('content')
-
-
-
-
-
-
- {{-- Form ini akan mengirim data ke TransaksiController --}}
-
+
+ |
+
+ @endforeach
+
+
@endif
diff --git a/resources/views/layouts/frontend.blade.php b/resources/views/layouts/frontend.blade.php
index 89abfb2..0b1973d 100644
--- a/resources/views/layouts/frontend.blade.php
+++ b/resources/views/layouts/frontend.blade.php
@@ -78,11 +78,14 @@ class="nav-item nav-link {{ request()->is('shop*') ? 'active' : '' }}">Belanja
-
+
+
0
+ style="top: -5px; left: 15px; height: 20px; min-width: 20px;">
+ {{ count((array) session('cart')) }}
+
@@ -120,6 +123,15 @@ class="d-none d-xl-inline ms-1">{{ Auth::guard('pembeli')->user()->nama_lengkap
+ @if(session('success'))
+
+
+ {{ session('success') }}
+
+
+
+ @endif
+
@yield('content')
diff --git a/resources/views/petani/dashboard.blade.php b/resources/views/petani/dashboard.blade.php
index 17709fe..c4e0516 100644
--- a/resources/views/petani/dashboard.blade.php
+++ b/resources/views/petani/dashboard.blade.php
@@ -1,74 +1,81 @@
@extends('layouts.admin')
@section('title', 'Dashboard Petani')
-@section('page-title', 'Panel Petani')
+@section('page-title', 'Overview Toko')
@section('content')
-
-
Statistik Toko
-
-
-
-
-
- {{-- Baris Statistik --}}
-
-
-
-
-
-
-
-
- {{-- Ikon Produk --}}
-
-
-
-
-
Produk Aktif
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
Pesanan Masuk
-
- {{-- LOGIKA HITUNG PESANAN --}}
- @php
- $jumlahPesanan = \App\Models\Transaksi::whereHas(
- 'details.produk',
- function ($q) {
- $q->where('petani_id', Auth::guard('petani')->id());
- },
- )
- ->where('status', '!=', 'selesai')
- ->count();
- @endphp
-
-
-
-
+
+
Produk Saya
+
-
-
+
+
+
+
+
+
+
+
Pesanan Baru
+
+ Perlu diproses
+
+
+
+
+
+
+
+
+
+
+
+
+
Total Pendapatan
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Kelola produk dan pantau pesanan Anda melalui menu di sebelah kiri.
+
Tambah Produk Baru
+
+
+
+
-@endsection
+
+@endsection
\ No newline at end of file
diff --git a/routes/web.php b/routes/web.php
index e404259..16e8a50 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -5,6 +5,7 @@
use App\Http\Controllers\AdminController;
use App\Http\Controllers\CartController;
use App\Http\Controllers\LandingController;
+use App\Http\Controllers\Petani\DashboardController;
use App\Http\Controllers\Petani\ProdukController;
use App\Http\Controllers\TransaksiController;
@@ -29,7 +30,7 @@
Route::middleware('guest')->group(function () {
Route::get('/login', [AuthController::class, 'showLoginForm'])->name('login');
Route::post('/login-proses', [AuthController::class, 'loginProcess'])->name('login.proses');
-
+
Route::get('/register', [AuthController::class, 'showRegisterForm'])->name('register');
Route::post('/register-proses', [AuthController::class, 'registerProcess'])->name('register.proses');
});
@@ -42,12 +43,18 @@
Route::middleware(['auth:pembeli'])->group(function () {
// Menampilkan halaman checkout dari cart atau beli langsung
Route::get('/checkout', [TransaksiController::class, 'checkoutPage'])->name('checkout');
-
+
// Proses Simpan Transaksi
Route::post('/checkout/process', [TransaksiController::class, 'prosesCheckout'])->name('checkout.proses');
-
+
// Riwayat Pesanan
Route::get('/pesanan-saya', [TransaksiController::class, 'pesananSaya'])->name('pesanan.saya');
+
+ // Route Konfirmasi Pesanan Selesai
+ Route::post('/pesanan/{id}/selesai', [TransaksiController::class, 'konfirmasiSelesai'])->name('pesanan.selesai');
+
+ Route::patch('/cart/update', [CartController::class, 'updateCart'])->name('cart.update');
+ Route::delete('/cart/remove', [CartController::class, 'remove'])->name('cart.remove');
});
@@ -67,10 +74,8 @@
// --- PETANI AREA ---
Route::middleware(['auth:petani'])->group(function () {
-
- Route::get('/petani/dashboard', function () {
- return view('petani.dashboard');
- })->name('petani.dashboard');
+
+ Route::get('/petani/dashboard', [DashboardController::class, 'index'])->name('petani.dashboard');
// CRUD Produk Petani
Route::resource('petani/produk', ProdukController::class)->names('petani.produk');
@@ -78,5 +83,5 @@
// Manajemen Pesanan Masuk (Petani)
Route::get('/petani/pesanan', [TransaksiController::class, 'pesananMasuk'])->name('petani.pesanan.index');
Route::patch('/petani/pesanan/{id}', [TransaksiController::class, 'updateStatus'])->name('petani.pesanan.update');
- Route::get('/petani/pesanan/{id}', [TransaksiController::class, 'pesananDetail'])->name('petani.pesanan.detail');
-});
\ No newline at end of file
+ Route::get('/petani/pesanan/{id}', [TransaksiController::class, 'pesananDetail'])->name('petani.pesanan.detail');
+});