fix(cart): resolve quantity button conflict with template script

This commit is contained in:
sayasilvi 2025-12-02 23:26:08 +07:00
parent 477de4932e
commit c4e1fee8b2
11 changed files with 548 additions and 272 deletions

View File

@ -66,9 +66,9 @@ ### 1. Admin Desa (Administrator)
### 2. Petani (Penjual) ### 2. Petani (Penjual)
*Hanya bisa berjualan setelah akun diverifikasi oleh Admin Desa.* *Hanya bisa berjualan setelah akun diverifikasi oleh Admin Desa.*
- [ ] **Registrasi Petani:** Pendaftaran akun dengan status awal "Menunggu Persetujuan". - [ ] **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. - [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. - [ ] **Kotak Masuk (Pesan):** Fitur tanya jawab dengan pembeli.
### 3. Pembeli (Publik) ### 3. Pembeli (Publik)
@ -76,9 +76,9 @@ ### 3. Pembeli (Publik)
- [ ] **Registrasi & Login:** Membuat akun pembeli. - [ ] **Registrasi & Login:** Membuat akun pembeli.
- [x] **Katalog Produk:** Melihat dan mencari produk yang dijual petani. - [x] **Katalog Produk:** Melihat dan mencari produk yang dijual petani.
- [x] **Detail Produk:** Melihat rincian produk. - [x] **Detail Produk:** Melihat rincian produk.
- [x] **Keranjang Belanja:** Menambah produk ke cart sebelum checkout. - [ ] **Keranjang Belanja:** Menambah produk ke cart sebelum checkout.
- [ ] **Checkout (Transaksi):** Melakukan pemesanan barang (Metode COD). - [x] **Checkout (Transaksi):** Melakukan pemesanan barang (Metode COD).
- [ ] **Riwayat Transaksi:** Melacak status pesanan (Diproses/Dikirim/Selesai). - [x] **Riwayat Transaksi:** Melacak status pesanan (Diproses/Dikirim/Selesai).
- [ ] **Kirim Pesan:** Menghubungi petani terkait produk. - [ ] **Kirim Pesan:** Menghubungi petani terkait produk.
--- ---
@ -101,7 +101,7 @@ ## Installation Guide
### Step 1: Clone Repository ### Step 1: Clone Repository
Unduh kode sumber dari repositori. Unduh kode sumber dari repositori.
```bash ```bash
git clone [https://github.com/sayasilvi/web-pertanian.git) git clone [https://github.com/sayasilvi/web-pertanian.git]
cd web-pertanian 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.* *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. Generate key aplikasi dan jalankan migrasi database serta seeder data dummy.
```bash ```bash
php artisan key:generate php artisan key:generate
```
### Step 6: Database Migration
Jalankan migrasi database serta seeder data dummy.
```bash
php artisan migrate --seed 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):** **Terminal (Laravel Server):**
```bash ```bash
php artisan serve php artisan serve
@ -178,7 +191,7 @@ ## Development Roadmap
- [x] **Module:** Admin Verification Workflow - [x] **Module:** Admin Verification Workflow
- [x] **Module:** Shopping Cart & Checkout - [x] **Module:** Shopping Cart & Checkout
- [ ] **Module:** Product Management (Vendor) - [ ] **Module:** Product Management (Vendor)
- [ ] **Feature:** Transaction History Tracking - [x] **Feature:** Transaction History Tracking
- [ ] **Feature:** In-app Messaging System - [ ] **Feature:** In-app Messaging System
--- ---

View File

@ -22,14 +22,15 @@ public function addToCart(Request $request)
$cart = session()->get('cart', []); $cart = session()->get('cart', []);
// Jika produk sudah ada di cart, tambahkan jumlahnya // Validasi qty default ke 1 jika kosong
if(isset($cart[$id])) { $quantity = $request->qty ? $request->qty : 1;
$cart[$id]['quantity'] += $request->qty;
if (isset($cart[$id])) {
$cart[$id]['quantity'] += $quantity;
} else { } else {
// Jika belum ada, masukkan data baru
$cart[$id] = [ $cart[$id] = [
"name" => $produk->nama_produk, "name" => $produk->nama_produk,
"quantity" => $request->qty, "quantity" => $quantity,
"price" => $produk->harga, "price" => $produk->harga,
"photo" => $produk->foto_produk "photo" => $produk->foto_produk
]; ];
@ -39,12 +40,22 @@ public function addToCart(Request $request)
return redirect()->back()->with('success', 'Produk berhasil masuk keranjang!'); 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 // Menghapus Item dari Keranjang
public function remove(Request $request) public function remove(Request $request)
{ {
if($request->id) { if ($request->id) {
$cart = session()->get('cart'); $cart = session()->get('cart');
if(isset($cart[$request->id])) { if (isset($cart[$request->id])) {
unset($cart[$request->id]); unset($cart[$request->id]);
session()->put('cart', $cart); session()->put('cart', $cart);
} }

View File

@ -0,0 +1,35 @@
<?php
namespace App\Http\Controllers\Petani;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use App\Models\Produk;
use App\Models\DetailTransaksi;
class DashboardController extends Controller
{
public function index()
{
$petaniId = Auth::guard('petani')->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'));
}
}

View File

@ -16,35 +16,68 @@ class TransaksiController extends Controller
// Tampilkan Halaman Checkout // Tampilkan Halaman Checkout
public function checkoutPage(Request $request) public function checkoutPage(Request $request)
{ {
// Beli Langsung (Buy Now)
if ($request->has('produk_id')) { if ($request->has('produk_id')) {
$produk = Produk::with('petani')->findOrFail($request->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'));
} }
// Proses Simpan Transaksi return redirect()->route('shop')->with('error', 'Keranjang Anda kosong, silakan belanja dulu.');
}
// fungsi prosesCheckout
public function prosesCheckout(Request $request) public function prosesCheckout(Request $request)
{ {
$request->validate([ $request->validate([
'produk_id' => 'required|exists:produks,id',
'jumlah' => 'required|integer|min:1',
'alamat_pengiriman' => 'required|string', 'alamat_pengiriman' => 'required|string',
'metode_pembayaran' => 'required|in:cod', 'metode_pembayaran' => 'required|in:cod',
]); ]);
$pembeli_id = Auth::guard('pembeli')->id();
DB::transaction(function () use ($request, $pembeli_id) {
if ($request->has('produk_id')) {
// LOGIKA BELI LANGSUNG (Single Item)
$produk = Produk::findOrFail($request->produk_id); $produk = Produk::findOrFail($request->produk_id);
if ($produk->stok < $request->jumlah) {
return back()->with('error', 'Stok produk tidak mencukupi!');
}
$total_harga = $produk->harga * $request->jumlah; $total_harga = $produk->harga * $request->jumlah;
DB::transaction(function () use ($request, $produk, $total_harga) {
$transaksi = Transaksi::create([ $transaksi = Transaksi::create([
'pembeli_id' => Auth::guard('pembeli')->id(), 'pembeli_id' => $pembeli_id,
'tanggal_transaksi' => now(), 'tanggal_transaksi' => now(),
'alamat_pengiriman' => $request->alamat_pengiriman, 'alamat_pengiriman' => $request->alamat_pengiriman,
'total_harga' => $total_harga, 'total_harga' => $total_harga,
@ -60,8 +93,60 @@ public function prosesCheckout(Request $request)
'subtotal' => $total_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.'); 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')); 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 --- // --- FITUR PETANI ---

View File

@ -2,6 +2,7 @@
namespace Database\Seeders; namespace Database\Seeders;
use App\Models\Pembeli;
use App\Models\User; use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
@ -19,6 +20,9 @@ public function run(): void
$this->call([ $this->call([
AdminSeeder::class, AdminSeeder::class,
PembeliSeeder::class,
PetaniSeeder::class,
ProdukSeeder::class,
]); ]);
} }
} }

View File

@ -3,7 +3,6 @@
@section('title', 'Keranjang Belanja') @section('title', 'Keranjang Belanja')
@section('content') @section('content')
<!-- Single Page Header -->
<div class="container-fluid page-header py-5"> <div class="container-fluid page-header py-5">
<h1 class="text-center text-white display-6">Keranjang Belanja</h1> <h1 class="text-center text-white display-6">Keranjang Belanja</h1>
<ol class="breadcrumb justify-content-center mb-0"> <ol class="breadcrumb justify-content-center mb-0">
@ -12,9 +11,16 @@
</ol> </ol>
</div> </div>
<!-- Cart Page -->
<div class="container-fluid py-5"> <div class="container-fluid py-5">
<div class="container py-5"> <div class="container py-5">
{{-- Alert Notification --}}
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fa fa-check-circle me-2"></i> {{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
<div class="table-responsive"> <div class="table-responsive">
<table class="table"> <table class="table">
<thead> <thead>
@ -34,12 +40,12 @@
$total = $details['price'] * $details['quantity']; $total = $details['price'] * $details['quantity'];
$total_belanja += $total; $total_belanja += $total;
@endphp @endphp
<tr> <tr data-id="{{ $id }}">
<th scope="row"> <th scope="row">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<img src="{{ $details['photo'] ? asset('storage/' . $details['photo']) : asset('template/frontend/img/vegetable-item-3.png') }}" <img src="{{ $details['photo'] ? asset('storage/' . $details['photo']) : asset('template/frontend/img/vegetable-item-3.png') }}"
class="img-fluid me-5 rounded-circle" style="width: 80px; height: 80px;" class="img-fluid me-5 rounded-circle" style="width: 80px; height: 80px;"
alt=""> alt="{{ $details['name'] }}">
</div> </div>
</th> </th>
<td> <td>
@ -51,16 +57,16 @@ class="img-fluid me-5 rounded-circle" style="width: 80px; height: 80px;"
<td> <td>
<div class="input-group quantity mt-4" style="width: 100px;"> <div class="input-group quantity mt-4" style="width: 100px;">
<div class="input-group-btn"> <div class="input-group-btn">
<button class="btn btn-sm btn-minus rounded-circle bg-light border" <button type="button" class="btn btn-sm btn-minus-custom rounded-circle bg-light border">
onclick="this.parentNode.parentNode.querySelector('input').stepDown()">
<i class="fa fa-minus"></i> <i class="fa fa-minus"></i>
</button> </button>
</div> </div>
<input type="text" class="form-control form-control-sm text-center border-0"
value="{{ $details['quantity'] }}"> <input type="text" class="form-control form-control-sm text-center border-0 qty-input bg-white"
value="{{ $details['quantity'] }}" readonly>
<div class="input-group-btn"> <div class="input-group-btn">
<button class="btn btn-sm btn-plus rounded-circle bg-light border" <button type="button" class="btn btn-sm btn-plus-custom rounded-circle bg-light border">
onclick="this.parentNode.parentNode.querySelector('input').stepUp()">
<i class="fa fa-plus"></i> <i class="fa fa-plus"></i>
</button> </button>
</div> </div>
@ -74,7 +80,7 @@ class="img-fluid me-5 rounded-circle" style="width: 80px; height: 80px;"
@csrf @csrf
@method('DELETE') @method('DELETE')
<input type="hidden" name="id" value="{{ $id }}"> <input type="hidden" name="id" value="{{ $id }}">
<button class="btn btn-md rounded-circle bg-light border mt-4"> <button class="btn btn-md rounded-circle bg-light border mt-4" onclick="return confirm('Hapus produk ini?')">
<i class="fa fa-times text-danger"></i> <i class="fa fa-times text-danger"></i>
</button> </button>
</form> </form>
@ -83,9 +89,12 @@ class="img-fluid me-5 rounded-circle" style="width: 80px; height: 80px;"
@empty @empty
<tr> <tr>
<td colspan="6" class="text-center py-5"> <td colspan="6" class="text-center py-5">
<div class="d-flex flex-column align-items-center">
<i class="fa fa-shopping-basket fa-3x text-muted mb-3"></i>
<h4>Keranjang belanja masih kosong.</h4> <h4>Keranjang belanja masih kosong.</h4>
<a href="{{ route('shop') }}" <a href="{{ route('shop') }}"
class="btn btn-primary rounded-pill px-4 py-2 mt-3 text-white">Belanja Sekarang</a> class="btn btn-primary rounded-pill px-4 py-2 mt-3 text-white">Mulai Belanja</a>
</div>
</td> </td>
</tr> </tr>
@endforelse @endforelse
@ -103,18 +112,13 @@ class="btn btn-primary rounded-pill px-4 py-2 mt-3 text-white">Belanja Sekarang<
<h5 class="mb-0 me-4">Subtotal:</h5> <h5 class="mb-0 me-4">Subtotal:</h5>
<p class="mb-0">Rp {{ number_format($total_belanja, 0, ',', '.') }}</p> <p class="mb-0">Rp {{ number_format($total_belanja, 0, ',', '.') }}</p>
</div> </div>
<div class="d-flex justify-content-between">
<h5 class="mb-0 me-4">Pengiriman</h5>
<div class="">
<p class="mb-0">Flat Rate: Rp 10.000</p>
</div>
</div>
<p class="mb-0 text-end"></p>
</div> </div>
<div class="py-4 mb-4 border-top border-bottom d-flex justify-content-between px-4"> <div class="py-4 mb-4 border-top border-bottom d-flex justify-content-between px-4">
<h5 class="mb-0 ps-4 me-4">Total</h5> <h5 class="mb-0 ps-4 me-4">Total</h5>
<p class="mb-0 pe-4">Rp {{ number_format($total_belanja + 10000, 0, ',', '.') }}</p> <p class="mb-0 pe-4">Rp {{ number_format($total_belanja, 0, ',', '.') }}</p>
</div> </div>
{{-- Button Checkout --}}
<a href="{{ route('checkout') }}" <a href="{{ route('checkout') }}"
class="btn border-secondary rounded-pill px-4 py-3 text-primary text-uppercase mb-4 ms-4" class="btn border-secondary rounded-pill px-4 py-3 text-primary text-uppercase mb-4 ms-4"
type="button">Proses Checkout</a> type="button">Proses Checkout</a>
@ -125,3 +129,54 @@ class="btn border-secondary rounded-pill px-4 py-3 text-primary text-uppercase m
</div> </div>
</div> </div>
@endsection @endsection
@section('js')
<script>
$(document).ready(function () {
$(".quantity button").off("click");
// Event saat tombol Plus Custom diklik
$(".btn-plus-custom").click(function (e) {
e.preventDefault();
var ele = $(this);
var input = ele.closest("tr").find(".qty-input");
var currentVal = parseInt(input.val()) || 0;
var newVal = currentVal + 1;
input.val(newVal);
updateCart(ele.closest("tr").attr("data-id"), newVal);
});
// Event saat tombol Minus Custom diklik
$(".btn-minus-custom").click(function (e) {
e.preventDefault();
var ele = $(this);
var input = ele.closest("tr").find(".qty-input");
var currentVal = parseInt(input.val()) || 0;
if (currentVal > 1) {
var newVal = currentVal - 1;
input.val(newVal);
updateCart(ele.closest("tr").attr("data-id"), newVal);
}
});
// Fungsi AJAX
function updateCart(id, qty) {
$.ajax({
url: "{{ route('cart.update') }}",
method: "patch",
data: {
_token: '{{ csrf_token() }}',
id: id,
quantity: qty
},
success: function (response) {
window.location.reload();
}
});
}
});
</script>
@endsection

View File

@ -3,7 +3,7 @@
@section('title', 'Checkout') @section('title', 'Checkout')
@section('content') @section('content')
<div class="container py-5"> <div class="container py-5">
<div class="row"> <div class="row">
<div class="col-md-8 mx-auto"> <div class="col-md-8 mx-auto">
<div class="card border-0 shadow-sm rounded"> <div class="card border-0 shadow-sm rounded">
@ -11,36 +11,39 @@
<h4 class="mb-0 text-primary fw-bold">Konfirmasi Pembelian</h4> <h4 class="mb-0 text-primary fw-bold">Konfirmasi Pembelian</h4>
</div> </div>
<div class="card-body p-4"> <div class="card-body p-4">
{{-- Form ini akan mengirim data ke TransaksiController --}}
<form action="{{ route('checkout.proses') }}" method="POST"> <form action="{{ route('checkout.proses') }}" method="POST">
@csrf @csrf
<input type="hidden" name="produk_id" value="{{ $produk->id }}"> @if (count($items) == 1 && request()->has('produk_id'))
<input type="hidden" name="produk_id" value="{{ $items->first()->id }}">
<input type="hidden" name="jumlah" value="{{ $items->first()->jumlah }}">
@endif
<div class="row mb-4"> <h5 class="mb-3">Daftar Barang</h5>
<div class="col-md-4"> <ul class="list-group mb-4">
<img src="{{ $produk->foto_produk ? asset('storage/'.$produk->foto_produk) : asset('template/frontend/img/fruite-item-5.jpg') }}" @foreach ($items as $item)
class="img-fluid rounded" alt="Produk"> <li class="list-group-item d-flex justify-content-between align-items-center">
</div> <div class="d-flex align-items-center">
<div class="col-md-8"> <img src="{{ $item->foto ? asset('storage/' . $item->foto) : asset('template/frontend/img/fruite-item-5.jpg') }}"
<h5>{{ $produk->nama_produk }}</h5> width="50" class="rounded me-3">
<p class="text-muted small mb-2">Penjual: {{ $produk->petani->nama_lengkap }}</p> <div>
<h4 class="text-success fw-bold">Rp {{ number_format($produk->harga, 0, ',', '.') }} / unit</h4> <h6 class="my-0">{{ $item->nama_produk }}</h6>
<p class="text-secondary mt-3">{{ $produk->deskripsi }}</p> <small class="text-muted">{{ $item->jumlah }} x Rp
{{ number_format($item->harga, 0, ',', '.') }}</small>
</div> </div>
</div> </div>
<span class="text-muted">Rp {{ number_format($item->subtotal, 0, ',', '.') }}</span>
<hr> </li>
@endforeach
<div class="mb-3"> <li class="list-group-item d-flex justify-content-between bg-light">
<label class="form-label fw-bold">Jumlah Pembelian</label> <span>Total (IDR)</span>
<input type="number" name="jumlah" class="form-control w-25" value="1" min="1" max="{{ $produk->stok }}" id="jumlah" onchange="hitungTotal()"> <strong>Rp {{ number_format($total_belanja, 0, ',', '.') }}</strong>
<small class="text-muted">Stok tersedia: {{ $produk->stok }}</small> </li>
</div> </ul>
<div class="mb-3"> <div class="mb-3">
<label class="form-label fw-bold">Alamat Pengiriman</label> <label class="form-label fw-bold">Alamat Pengiriman</label>
<textarea name="alamat_pengiriman" class="form-control" rows="3" required>{{ Auth::guard('pembeli')->user()->alamat }}</textarea> <textarea name="alamat_pengiriman" class="form-control" rows="3" required
<small class="text-muted">Pastikan alamat lengkap untuk memudahkan kurir.</small> placeholder="Tulis alamat lengkap Anda...">{{ Auth::guard('pembeli')->user()->alamat }}</textarea>
</div> </div>
<div class="mb-4"> <div class="mb-4">
@ -50,42 +53,17 @@ class="img-fluid rounded" alt="Produk">
</select> </select>
</div> </div>
<div class="d-flex justify-content-between align-items-center bg-light p-3 rounded mb-4">
<h5 class="mb-0">Total Bayar:</h5>
<h3 class="fw-bold text-primary" id="total_bayar">Rp {{ number_format($produk->harga, 0, ',', '.') }}</h3>
</div>
<div class="d-grid gap-2"> <div class="d-grid gap-2">
{{-- PERBAIKAN: Gunakan button type="submit" agar form terkirim --}} <button type="submit" class="btn btn-primary rounded-pill px-4 py-3 font-weight-bold">
<button type="submit" class="btn btn-primary rounded-pill px-4 py-3 mb-2 font-weight-bold"> <i class="fa fa-paper-plane me-2"></i> Buat Pesanan
<i class="fa fa-paper-plane me-2"></i> Buat Pesanan Sekarang
</button> </button>
<a href="{{ route('cart') }}"
<a href="{{ route('shop') }}" class="btn btn-outline-secondary py-2 rounded-pill">Batal</a> class="btn btn-outline-secondary py-2 rounded-pill">Kembali</a>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@endsection
@section('js')
<script>
function hitungTotal() {
let harga = {{ $produk->harga }};
let jumlah = document.getElementById('jumlah').value;
// Validasi agar tidak minus
if(jumlah < 1) {
jumlah = 1;
document.getElementById('jumlah').value = 1;
}
let total = harga * jumlah;
document.getElementById('total_bayar').innerText = 'Rp ' + new Intl.NumberFormat('id-ID').format(total);
}
</script>
@endsection @endsection

View File

@ -41,37 +41,92 @@
<td class="fw-bold text-primary">#{{ $trx->kode_invoice }}</td> <td class="fw-bold text-primary">#{{ $trx->kode_invoice }}</td>
<td>{{ \Carbon\Carbon::parse($trx->tanggal_transaksi)->format('d M Y') }}</td> <td>{{ \Carbon\Carbon::parse($trx->tanggal_transaksi)->format('d M Y') }}</td>
<td> <td>
@foreach($trx->details as $detail) {{-- Tampilkan 1 produk utama saja agar tabel rapi --}}
<div class="d-flex align-items-center mb-1"> <div class="d-flex align-items-center mb-1">
<img src="{{ $detail->produk->foto_produk ? asset('storage/'.$detail->produk->foto_produk) : asset('template/frontend/img/fruite-item-5.jpg') }}" width="40" class="rounded me-2"> @php $firstItem = $trx->details->first(); @endphp
<img src="{{ $firstItem->produk->foto_produk ? asset('storage/'.$firstItem->produk->foto_produk) : asset('template/frontend/img/fruite-item-5.jpg') }}" width="50" class="rounded me-2">
<div> <div>
<small class="d-block fw-bold">{{ $detail->produk->nama_produk }}</small> <small class="d-block fw-bold">{{ $firstItem->produk->nama_produk }}</small>
<small class="text-muted">{{ $detail->jumlah }} x Rp {{ number_format($detail->harga_satuan, 0, ',', '.') }}</small> @if($trx->details->count() > 1)
<small class="text-muted">+ {{ $trx->details->count() - 1 }} produk lainnya</small>
@else
<small class="text-muted">{{ $firstItem->jumlah }} x Rp {{ number_format($firstItem->harga_satuan, 0, ',', '.') }}</small>
@endif
</div> </div>
</div> </div>
@endforeach
</td> </td>
<td class="fw-bold">Rp {{ number_format($trx->total_harga, 0, ',', '.') }}</td> <td class="fw-bold">Rp {{ number_format($trx->total_harga, 0, ',', '.') }}</td>
<td> <td>
@if($trx->status == 'menunggu_konfirmasi') @if($trx->status == 'menunggu_konfirmasi')
<span class="badge bg-warning text-dark">Menunggu Konfirmasi</span> <span class="badge bg-warning text-dark"><i class="fas fa-clock me-1"></i> Menunggu Konfirmasi</span>
@elseif($trx->status == 'diproses') @elseif($trx->status == 'diproses')
<span class="badge bg-info text-white">Sedang Diproses</span> <span class="badge bg-info text-white"><i class="fas fa-cog me-1"></i> Sedang Diproses</span>
@elseif($trx->status == 'dikirim') @elseif($trx->status == 'dikirim')
<span class="badge bg-primary">Sedang Dikirim</span> <span class="badge bg-primary"><i class="fas fa-truck me-1"></i> Sedang Dikirim</span>
@elseif($trx->status == 'selesai') @elseif($trx->status == 'selesai')
<span class="badge bg-success">Selesai</span> <span class="badge bg-success"><i class="fas fa-check-circle me-1"></i> Selesai</span>
@else @else
<span class="badge bg-danger">Dibatalkan</span> <span class="badge bg-danger">Dibatalkan</span>
@endif @endif
</td> </td>
<td> <td>
<a href="#" class="btn btn-sm btn-outline-primary rounded-pill">Detail</a> <div class="d-flex gap-2">
{{-- Tombol Detail (Trigger Modal) --}}
<button type="button" class="btn btn-sm btn-outline-secondary rounded-pill" data-bs-toggle="modal" data-bs-target="#detailModal{{ $trx->id }}">
Detail
</button>
{{-- LOGIKA TOMBOL SELESAI --}}
@if($trx->status == 'dikirim')
<form action="{{ route('pesanan.selesai', $trx->id) }}" method="POST" onsubmit="return confirm('Apakah Anda yakin barang sudah diterima dengan baik?')">
@csrf
<button type="submit" class="btn btn-sm btn-success rounded-pill">
<i class="fas fa-check me-1"></i> Diterima
</button>
</form>
@endif
</div>
{{-- MODAL DETAIL PESANAN --}}
<div class="modal fade" id="detailModal{{ $trx->id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Detail Pesanan #{{ $trx->kode_invoice }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p><strong>Status:</strong> {{ strtoupper($trx->status) }}</p>
<p><strong>Alamat Pengiriman:</strong><br>{{ $trx->alamat_pengiriman }}</p>
<hr>
<h6>Daftar Produk:</h6>
<ul class="list-group">
@foreach($trx->details as $d)
<li class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center">
<img src="{{ $d->produk->foto_produk ? asset('storage/'.$d->produk->foto_produk) : asset('template/frontend/img/fruite-item-5.jpg') }}" width="40" class="me-2 rounded">
<div>
{{ $d->produk->nama_produk }}
<small class="d-block text-muted">{{ $d->jumlah }} x Rp {{ number_format($d->harga_satuan, 0, ',', '.') }}</small>
</div>
</div>
<span class="fw-bold">Rp {{ number_format($d->subtotal, 0, ',', '.') }}</span>
</li>
@endforeach
</ul>
</div>
<div class="modal-footer justify-content-between">
<span class="fw-bold fs-5">Total: Rp {{ number_format($trx->total_harga, 0, ',', '.') }}</span>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
</td> </td>
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>
</table> </table>
</div> </div>
@endif @endif
</div> </div>

View File

@ -78,11 +78,14 @@ class="nav-item nav-link {{ request()->is('shop*') ? 'active' : '' }}">Belanja</
</form> </form>
<!-- Cart --> <!-- Cart -->
<a href="#" class="position-relative me-4 my-auto"> <a href="{{ route('cart') }}" class="position-relative me-4 my-auto">
<i class="fa fa-shopping-bag fa-2x"></i> <i class="fa fa-shopping-bag fa-2x"></i>
<span <span
class="position-absolute bg-secondary rounded-circle d-flex align-items-center justify-content-center text-dark px-1" class="position-absolute bg-secondary rounded-circle d-flex align-items-center justify-content-center text-dark px-1"
style="top: -5px; left: 15px; height: 20px; min-width: 20px;">0</span> style="top: -5px; left: 15px; height: 20px; min-width: 20px;">
{{ count((array) session('cart')) }}
</span>
</a> </a>
<!-- User Account --> <!-- User Account -->
@ -120,6 +123,15 @@ class="d-none d-xl-inline ms-1">{{ Auth::guard('pembeli')->user()->nama_lengkap
<!-- Main Content --> <!-- Main Content -->
<div style="margin-top: 150px;"> <div style="margin-top: 150px;">
@if(session('success'))
<div class="container mt-3">
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fa fa-check-circle me-2"></i> {{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
</div>
@endif
@yield('content') @yield('content')
</div> </div>

View File

@ -1,66 +1,61 @@
@extends('layouts.admin') @extends('layouts.admin')
@section('title', 'Dashboard Petani') @section('title', 'Dashboard Petani')
@section('page-title', 'Panel Petani') @section('page-title', 'Overview Toko')
@section('content') @section('content')
<div class="page-heading"> <section class="row">
<h3>Statistik Toko</h3>
</div>
<div class="page-content">
<section class="row">
<div class="col-12 col-lg-12"> <div class="col-12 col-lg-12">
{{-- Baris Statistik --}}
<div class="row"> <div class="row">
<!-- Statistik Produk --> <div class="col-6 col-lg-4 col-md-6">
<div class="col-6 col-lg-3 col-md-6">
<div class="card"> <div class="card">
<div class="card-body px-3 py-4-5"> <div class="card-body px-3 py-4-5">
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<div class="stats-icon purple mb-2"> <div class="stats-icon purple">
{{-- Ikon Produk --}} <i class="bi bi-basket-fill"></i>
<i class="bi bi-box-seam-fill text-white"></i>
</div> </div>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<h6 class="text-muted font-semibold">Produk Aktif</h6> <h6 class="text-muted font-semibold">Produk Saya</h6>
<h6 class="font-extrabold mb-0"> <h6 class="font-extrabold mb-0">{{ $totalProduk }}</h6>
{{ \App\Models\Produk::where('petani_id', Auth::guard('petani')->id())->count() }}
</h6>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- Statistik Pesanan --> <div class="col-6 col-lg-4 col-md-6">
<div class="col-6 col-lg-3 col-md-6">
<div class="card"> <div class="card">
<div class="card-body px-3 py-4-5"> <div class="card-body px-3 py-4-5">
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<div class="stats-icon blue mb-2"> <div class="stats-icon blue">
<i class="bi bi-cart-check-fill text-white"></i> <i class="bi bi-receipt"></i>
</div> </div>
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
<h6 class="text-muted font-semibold">Pesanan Masuk</h6> <h6 class="text-muted font-semibold">Pesanan Baru</h6>
<h6 class="font-extrabold mb-0">{{ $pesananBaru }}</h6>
<small class="text-xs text-muted">Perlu diproses</small>
</div>
</div>
</div>
</div>
</div>
{{-- LOGIKA HITUNG PESANAN --}} <div class="col-6 col-lg-4 col-md-6">
@php <div class="card">
$jumlahPesanan = \App\Models\Transaksi::whereHas( <div class="card-body px-3 py-4-5">
'details.produk', <div class="row">
function ($q) { <div class="col-md-4">
$q->where('petani_id', Auth::guard('petani')->id()); <div class="stats-icon green">
}, <i class="bi bi-cash"></i>
) </div>
->where('status', '!=', 'selesai') </div>
->count(); <div class="col-md-8">
@endphp <h6 class="text-muted font-semibold">Total Pendapatan</h6>
<h6 class="font-extrabold mb-0">Rp {{ number_format($totalPendapatan, 0, ',', '.') }}</h6>
<h6 class="font-extrabold mb-0">{{ $jumlahPesanan }}</h6>
</div> </div>
</div> </div>
</div> </div>
@ -68,7 +63,19 @@ function ($q) {
</div> </div>
</div> </div>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h4>Selamat Datang, {{ Auth::guard('petani')->user()->nama_lengkap }}!</h4>
</div> </div>
</section> <div class="card-body">
<p>Kelola produk dan pantau pesanan Anda melalui menu di sebelah kiri.</p>
<a href="{{ route('petani.produk.create') }}" class="btn btn-primary">Tambah Produk Baru</a>
</div> </div>
</div>
</div>
</div>
</div>
</section>
@endsection @endsection

View File

@ -5,6 +5,7 @@
use App\Http\Controllers\AdminController; use App\Http\Controllers\AdminController;
use App\Http\Controllers\CartController; use App\Http\Controllers\CartController;
use App\Http\Controllers\LandingController; use App\Http\Controllers\LandingController;
use App\Http\Controllers\Petani\DashboardController;
use App\Http\Controllers\Petani\ProdukController; use App\Http\Controllers\Petani\ProdukController;
use App\Http\Controllers\TransaksiController; use App\Http\Controllers\TransaksiController;
@ -48,6 +49,12 @@
// Riwayat Pesanan // Riwayat Pesanan
Route::get('/pesanan-saya', [TransaksiController::class, 'pesananSaya'])->name('pesanan.saya'); 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');
}); });
@ -68,9 +75,7 @@
// --- PETANI AREA --- // --- PETANI AREA ---
Route::middleware(['auth:petani'])->group(function () { Route::middleware(['auth:petani'])->group(function () {
Route::get('/petani/dashboard', function () { Route::get('/petani/dashboard', [DashboardController::class, 'index'])->name('petani.dashboard');
return view('petani.dashboard');
})->name('petani.dashboard');
// CRUD Produk Petani // CRUD Produk Petani
Route::resource('petani/produk', ProdukController::class)->names('petani.produk'); Route::resource('petani/produk', ProdukController::class)->names('petani.produk');