From f01c00fa87a3a29e4a42f9f0d1c8589da21ea3e3 Mon Sep 17 00:00:00 2001 From: sayasilvi Date: Mon, 2 Mar 2026 02:52:24 +0700 Subject: [PATCH] refactor: transaksi controller, cart dan filter wilayah lengkap --- .gitignore | 3 + README.md | 11 +- app/Http/Controllers/CartController.php | 11 +- app/Http/Controllers/LandingController.php | 29 +- .../Controllers/Petani/ProdukController.php | 109 ++-- app/Http/Controllers/TransaksiController.php | 149 ++--- app/Http/Controllers/WilayahController.php | 29 + app/Models/Produk.php | 6 +- composer.json | 3 +- composer.lock | 84 ++- config/laravolt/indonesia.php | 24 + ...16_08_03_072729_create_provinces_table.php | 42 ++ .../2016_08_03_072750_create_cities_table.php | 48 ++ ...16_08_03_072804_create_districts_table.php | 48 ++ ...016_08_03_072819_create_villages_table.php | 48 ++ ..._01_125919_add_lokasi_to_produks_table.php | 29 + database/seeders/TransaksiSeeder.php | 2 +- .../frontend/img/{testimonial-1.jpg => .jpg} | Bin .../img/{best-product-4.jpg => no-image.jpg} | Bin resources/views/landing/checkout.blade.php | 1 + resources/views/landing/home.blade.php | 385 +++++++------ resources/views/landing/pesan/index.blade.php | 141 +++-- resources/views/landing/pesan/show.blade.php | 1 + resources/views/landing/shop.blade.php | 388 ++++++++----- resources/views/layouts/admin.blade.php | 49 +- resources/views/layouts/frontend.blade.php | 141 +++-- .../views/petani/produk/create.blade.php | 501 ++++++++++------- resources/views/petani/produk/edit.blade.php | 532 +++++++++++------- routes/web.php | 5 + 29 files changed, 1827 insertions(+), 992 deletions(-) create mode 100644 app/Http/Controllers/WilayahController.php create mode 100644 config/laravolt/indonesia.php create mode 100644 database/migrations/2016_08_03_072729_create_provinces_table.php create mode 100644 database/migrations/2016_08_03_072750_create_cities_table.php create mode 100644 database/migrations/2016_08_03_072804_create_districts_table.php create mode 100644 database/migrations/2016_08_03_072819_create_villages_table.php create mode 100644 database/migrations/2026_03_01_125919_add_lokasi_to_produks_table.php rename public/template/frontend/img/{testimonial-1.jpg => .jpg} (100%) rename public/template/frontend/img/{best-product-4.jpg => no-image.jpg} (100%) diff --git a/.gitignore b/.gitignore index b71b1ea..d71020c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ Homestead.json Homestead.yaml Thumbs.db +/storage/framework/views/*.php +/storage/framework/cache/data/* +/storage/framework/sessions/* \ No newline at end of file diff --git a/README.md b/README.md index 6dc6eec..77cd149 100644 --- a/README.md +++ b/README.md @@ -141,14 +141,21 @@ ### Step 6: Database Migration php artisan migrate --seed ``` -### Step 7: Konfigurasi Storage Link +### Step 7: Import Data Wilayah Indonesia (Wajib) +Jalankan perintah ini untuk mengisi data wilayah resmi Indonesia ke dalam database. + +```bash +php artisan laravolt:indonesia:seed +``` + +### Step 8: Konfigurasi Storage Link Jalankan perintah untuk konfigurasi storage link untuk menyimpan gambar ```bash php artisan storage:link ``` -### Step 8: Start Application +### Step 9: Start Application **Terminal (Laravel Server):** ```bash php artisan serve diff --git a/app/Http/Controllers/CartController.php b/app/Http/Controllers/CartController.php index 1322def..f4074dc 100644 --- a/app/Http/Controllers/CartController.php +++ b/app/Http/Controllers/CartController.php @@ -12,15 +12,16 @@ class CartController extends Controller // Menampilkan Halaman Keranjang public function index() { - // Cek Login Pembeli if (!Auth::guard('pembeli')->check()) { return redirect()->route('login')->with('error', 'Silakan login terlebih dahulu untuk melihat keranjang.'); } $pembeli_id = Auth::guard('pembeli')->id(); - // Ambil data keranjang dari Database - $cart = Cart::with('produk')->where('pembeli_id', $pembeli_id)->get(); + $cart = Cart::with('produk') + ->where('pembeli_id', $pembeli_id) + ->latest() + ->get(); return view('landing.cart', compact('cart')); } @@ -54,8 +55,8 @@ public function addToCart(Request $request) } else { Cart::create([ 'pembeli_id' => $pembeli_id, - 'produk_id' => $produk_id, - 'quantity' => $quantity + 'produk_id' => $produk_id, + 'quantity' => $quantity ]); } diff --git a/app/Http/Controllers/LandingController.php b/app/Http/Controllers/LandingController.php index 25c94df..c3e260f 100644 --- a/app/Http/Controllers/LandingController.php +++ b/app/Http/Controllers/LandingController.php @@ -25,11 +25,13 @@ public function index(Request $request) return view('landing.partials.product_list', compact('produks'))->render(); } - $produkTerlaris = Produk::withSum(['detailTransaksis as total_terjual' => function ($query) { - $query->whereHas('transaksi', function ($q) { - $q->where('status', '!=', 'batal'); - }); - }], 'jumlah') + $produkTerlaris = Produk::withSum([ + 'detailTransaksis as total_terjual' => function ($query) { + $query->whereHas('transaksi', function ($q) { + $q->where('status', '!=', 'batal'); + }); + } + ], 'jumlah') ->orderByDesc('total_terjual') ->take(4) ->get(); @@ -41,6 +43,21 @@ public function shop(Request $request) { $query = Produk::where('stok', '>', 0); + // --- FILTER LOKASI --- + if ($request->filled('provinsi')) { + $query->where('provinsi_code', $request->provinsi); + } + if ($request->filled('kota')) { + $query->where('kota_code', $request->kota); + } + if ($request->filled('kecamatan')) { + $query->where('kecamatan_code', $request->kecamatan); + } + if ($request->filled('desa')) { + $query->where('desa_code', $request->desa); + } + + // Filter Search if ($request->has('search') && $request->search != '') { $query->where('nama_produk', 'like', '%' . $request->search . '%'); } @@ -91,4 +108,4 @@ public function detail($id) return view('landing.detail', compact('produk', 'produk_terkait')); } -} +} \ No newline at end of file diff --git a/app/Http/Controllers/Petani/ProdukController.php b/app/Http/Controllers/Petani/ProdukController.php index a753ec9..b3dc943 100644 --- a/app/Http/Controllers/Petani/ProdukController.php +++ b/app/Http/Controllers/Petani/ProdukController.php @@ -6,38 +6,47 @@ use Illuminate\Http\Request; use App\Models\Produk; use App\Models\ProdukImage; -use App\Models\Kategori; // PENTING: Import Model Kategori +use App\Models\Kategori; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Storage; +use Laravolt\Indonesia\Models\City; +use Laravolt\Indonesia\Models\District; +use Laravolt\Indonesia\Models\Province; +use Laravolt\Indonesia\Models\Village; class ProdukController extends Controller { public function index() { $produks = Produk::with('kategori') - ->where('petani_id', Auth::guard('petani')->id()) - ->latest() - ->get(); - + ->where('petani_id', Auth::guard('petani')->id()) + ->latest() + ->get(); + return view('petani.produk.index', compact('produks')); } public function create() { $kategoris = Kategori::all(); - - return view('petani.produk.create', compact('kategoris')); + $provinsis = Province::all(); + + return view('petani.produk.create', compact('kategoris', 'provinsis')); } public function store(Request $request) { $request->validate([ - 'nama_produk' => 'required|string|max:255', - 'kategori_id' => 'required|exists:kategoris,id', - 'harga' => 'required|numeric|min:0', - 'stok' => 'required|integer|min:0', - 'deskripsi' => 'required|string', - 'foto_produk' => 'required|image|mimes:jpeg,png,jpg|max:2048', + 'nama_produk' => 'required|string|max:255', + 'kategori_id' => 'required|exists:kategoris,id', + 'harga' => 'required|numeric|min:0', + 'stok' => 'required|integer|min:0', + 'deskripsi' => 'required|string', + 'provinsi_code' => 'required', + 'kota_code' => 'required', + 'kecamatan_code' => 'required', + 'desa_code' => 'required', + 'foto_produk' => 'required|image|mimes:jpeg,png,jpg|max:2048', 'foto_tambahan.*' => 'nullable|image|mimes:jpeg,png,jpg|max:2048' ]); @@ -49,12 +58,16 @@ public function store(Request $request) // Simpan Data Produk $produk = Produk::create([ - 'petani_id' => Auth::guard('petani')->id(), - 'kategori_id' => $request->kategori_id, + 'petani_id' => Auth::guard('petani')->id(), + 'kategori_id' => $request->kategori_id, 'nama_produk' => $request->nama_produk, - 'harga' => $request->harga, - 'stok' => $request->stok, - 'deskripsi' => $request->deskripsi, + 'harga' => $request->harga, + 'provinsi_code' => $request->provinsi_code, + 'kota_code' => $request->kota_code, + 'kecamatan_code' => $request->kecamatan_code, + 'desa_code' => $request->desa_code, + 'stok' => $request->stok, + 'deskripsi' => $request->deskripsi, 'foto_produk' => $fotoPath, ]); @@ -64,7 +77,7 @@ public function store(Request $request) $path = $file->store('produk/gallery', 'public'); ProdukImage::create([ 'produk_id' => $produk->id, - 'foto' => $path + 'foto' => $path ]); } } @@ -77,7 +90,12 @@ public function edit($id) $produk = Produk::with('images')->where('id', $id)->where('petani_id', Auth::guard('petani')->id())->firstOrFail(); $kategoris = Kategori::all(); - return view('petani.produk.edit', compact('produk', 'kategoris')); + $provinsis = Province::all(); + $kotas = City::where('province_code', $produk->provinsi_code)->get(); + $kecamatans = District::where('city_code', $produk->kota_code)->get(); + $desas = Village::where('district_code', $produk->kecamatan_code)->get(); + + return view('petani.produk.edit', compact('produk', 'kategoris', 'provinsis', 'kotas', 'kecamatans', 'desas')); } public function update(Request $request, $id) @@ -89,13 +107,17 @@ public function update(Request $request, $id) $sisaSlot = max(3 - $jumlahGambarLama, 0); $request->validate([ - 'nama_produk' => 'required|string|max:255', - 'kategori_id' => 'required|exists:kategoris,id', - 'harga' => 'required|numeric|min:0', - 'stok' => 'required|integer|min:0', - 'deskripsi' => 'required|string', - 'foto_produk' => 'nullable|image|mimes:jpeg,png,jpg|max:2048', - 'foto_tambahan' => 'array|max:' . $sisaSlot, + 'nama_produk' => 'required|string|max:255', + 'kategori_id' => 'required|exists:kategoris,id', + 'harga' => 'required|numeric|min:0', + 'provinsi_code' => 'required', + 'kota_code' => 'required', + 'kecamatan_code' => 'required', + 'desa_code' => 'required', + 'stok' => 'required|integer|min:0', + 'deskripsi' => 'required|string', + 'foto_produk' => 'nullable|image|mimes:jpeg,png,jpg|max:2048', + 'foto_tambahan' => 'array|max:' . $sisaSlot, 'foto_tambahan.*' => 'image|mimes:jpeg,png,jpg|max:2048' ], [ 'foto_tambahan.max' => "Anda hanya bisa menambah $sisaSlot foto lagi." @@ -112,22 +134,27 @@ public function update(Request $request, $id) // Update Data $produk->update([ 'nama_produk' => $request->nama_produk, - 'kategori_id' => $request->kategori_id, - 'harga' => $request->harga, - 'stok' => $request->stok, - 'deskripsi' => $request->deskripsi, - 'foto_produk' => $produk->foto_produk + 'kategori_id' => $request->kategori_id, + 'harga' => $request->harga, + 'provinsi_code' => $request->provinsi_code, + 'kota_code' => $request->kota_code, + 'kecamatan_code' => $request->kecamatan_code, + 'desa_code' => $request->desa_code, + 'stok' => $request->stok, + 'deskripsi' => $request->deskripsi, + 'foto_produk' => $produk->foto_produk ]); - + // Tambah Foto Galeri if ($request->hasFile('foto_tambahan')) { foreach ($request->file('foto_tambahan') as $file) { - if ($produk->images()->count() >= 3) break; - + if ($produk->images()->count() >= 3) + break; + $path = $file->store('produk/gallery', 'public'); ProdukImage::create([ 'produk_id' => $produk->id, - 'foto' => $path + 'foto' => $path ]); } } @@ -138,18 +165,18 @@ public function update(Request $request, $id) public function destroy($id) { $produk = Produk::where('id', $id)->where('petani_id', Auth::guard('petani')->id())->firstOrFail(); - + if ($produk->foto_produk && Storage::disk('public')->exists($produk->foto_produk)) { Storage::disk('public')->delete($produk->foto_produk); } - foreach($produk->images as $img) { + foreach ($produk->images as $img) { if (Storage::disk('public')->exists($img->foto)) { Storage::disk('public')->delete($img->foto); } } - - $produk->images()->delete(); + + $produk->images()->delete(); $produk->delete(); return redirect()->route('petani.produk.index')->with('success', 'Produk berhasil dihapus.'); @@ -158,7 +185,7 @@ public function destroy($id) public function deleteImage($id) { $image = ProdukImage::findOrFail($id); - + if ($image->produk->petani_id != Auth::guard('petani')->id()) { abort(403); } diff --git a/app/Http/Controllers/TransaksiController.php b/app/Http/Controllers/TransaksiController.php index af96605..e8de055 100644 --- a/app/Http/Controllers/TransaksiController.php +++ b/app/Http/Controllers/TransaksiController.php @@ -6,6 +6,7 @@ use App\Models\Produk; use App\Models\Transaksi; use App\Models\DetailTransaksi; +use App\Models\Cart; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; @@ -13,10 +14,16 @@ class TransaksiController extends Controller { // --- FITUR PEMBELI --- - // Tampilkan Halaman Checkout + /** + * Tampilkan Halaman Checkout + * Mendukung Beli Langsung dan Checkout dari Keranjang (Database) + */ public function checkoutPage(Request $request) { - // Beli Langsung (Buy Now) + // Mendefinisikan ID Pembeli agar tidak error 'Undefined Variable' + $pembeli_id = Auth::guard('pembeli')->id(); + + // 1. LOGIKA BELI LANGSUNG (Buy Now) if ($request->has('produk_id')) { $produk = Produk::with('petani')->findOrFail($request->produk_id); $items = collect([ @@ -34,32 +41,48 @@ public function checkoutPage(Request $request) return view('landing.checkout', compact('items', 'total_belanja')); } - // 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')); + // 2. LOGIKA CHECKOUT DARI KERANJANG (Database) + $cartIds = $request->query('cart_ids'); + + if (!$cartIds) { + return redirect()->route('cart')->with('error', 'Pilih minimal satu produk di keranjang untuk dicheckout.'); } - return redirect()->route('shop')->with('error', 'Keranjang Anda kosong, silakan belanja dulu.'); + $selectedIds = explode(',', $cartIds); + + // Ambil data dari Tabel Cart yang ID-nya dicentang oleh user + $cartItems = Cart::with('produk.petani') + ->where('pembeli_id', $pembeli_id) + ->whereIn('id', $selectedIds) + ->get(); + + if ($cartItems->isEmpty()) { + return redirect()->route('cart')->with('error', 'Barang di keranjang tidak ditemukan atau sudah dihapus.'); + } + + // Transformasi data agar sesuai dengan format yang dibaca di View Checkout + $items = $cartItems->map(function ($item) { + return (object) [ + 'cart_id' => $item->id, + 'id' => $item->produk->id, + 'produk' => $item->produk, + 'nama_produk' => $item->produk->nama_produk, + 'harga' => $item->produk->harga, + 'jumlah' => $item->quantity, + 'subtotal' => $item->produk->harga * $item->quantity, + 'foto' => $item->produk->foto_produk + ]; + }); + + $total_belanja = $items->sum('subtotal'); + + return view('landing.checkout', compact('items', 'total_belanja')); } - // fungsi prosesCheckout + /** + * Proses Pembuatan Transaksi + * Mengelompokkan pesanan berdasarkan Petani dan membersihkan keranjang + */ public function prosesCheckout(Request $request) { $request->validate([ @@ -70,9 +93,9 @@ public function prosesCheckout(Request $request) $pembeli_id = Auth::guard('pembeli')->id(); DB::transaction(function () use ($request, $pembeli_id) { - + if ($request->has('produk_id')) { - // --- LOGIKA BELI LANGSUNG (Single Item) --- + // --- LOGIKA BELI LANGSUNG --- $produk = Produk::findOrFail($request->produk_id); $total_harga = $produk->harga * $request->jumlah; @@ -97,65 +120,64 @@ public function prosesCheckout(Request $request) $produk->decrement('stok', $request->jumlah); } else { - // --- LOGIKA KERANJANG (Cart) --- - $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'] - ]; - } + // --- LOGIKA KERANJANG (DATABASE) --- + if (!$request->cart_ids) { + throw new \Exception("ID keranjang tidak ditemukan."); } - foreach ($cartItems as $petani_id => $items) { - $subtotal_transaksi = 0; - $kode_invoice = 'INV/' . date('Ymd') . '/' . rand(1000, 9999); + $cartIds = explode(',', $request->cart_ids); + + $cartItems = Cart::with('produk') + ->whereIn('id', $cartIds) + ->where('pembeli_id', $pembeli_id) + ->get(); - // Membuat Header Transaksi per Petani + // Kelompokkan produk berdasarkan Petani ID agar Invoice terpisah per toko + $groupedByPetani = []; + foreach ($cartItems as $item) { + $groupedByPetani[$item->produk->petani_id][] = $item; + } + + foreach ($groupedByPetani as $petani_id => $items) { $transaksi = Transaksi::create([ 'pembeli_id' => $pembeli_id, 'petani_id' => $petani_id, 'tanggal_transaksi' => now(), 'alamat_pengiriman' => $request->alamat_pengiriman, - 'total_harga' => 0, + 'total_harga' => 0, // Diupdate setelah menghitung subtotal 'status' => 'menunggu konfirmasi', - 'kode_invoice' => $kode_invoice, + 'kode_invoice' => 'INV/' . date('Ymd') . '/' . rand(1000, 9999), ]); + $subtotal_transaksi = 0; foreach ($items as $item) { - $produk = $item['produk']; - $qty = $item['qty']; - $total_per_item = $produk->harga * $qty; + $total_per_item = $item->produk->harga * $item->quantity; $subtotal_transaksi += $total_per_item; DetailTransaksi::create([ 'transaksi_id' => $transaksi->id, - 'produk_id' => $produk->id, - 'jumlah' => $qty, - 'harga_satuan' => $produk->harga, + 'produk_id' => $item->produk->id, + 'jumlah' => $item->quantity, + 'harga_satuan' => $item->produk->harga, 'subtotal' => $total_per_item, ]); - $produk->decrement('stok', $qty); + $item->produk->decrement('stok', $item->quantity); } - // Update total harga transaksi + // Update total harga transaksi per petani $transaksi->update(['total_harga' => $subtotal_transaksi]); } - session()->forget('cart'); + // OTOMATIS BERSIHKAN ITEM KERANJANG YANG SUDAH DIBELI + Cart::whereIn('id', $cartIds)->delete(); } }); - return redirect()->route('pesanan.saya')->with('success', 'Pesanan berhasil dibuat! Menunggu konfirmasi petani.'); + return redirect()->route('pesanan.saya')->with('success', 'Pesanan berhasil dibuat! Silakan pantau status pesanan Anda.'); } - // Riwayat Pesanan + // Riwayat Pesanan Pembeli public function pesananSaya() { $transaksis = Transaksi::with(['detailTransaksis.produk.petani']) @@ -172,11 +194,10 @@ 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('success', 'Terima kasih! Pesanan telah selesai.'); } return back()->with('error', 'Pesanan belum dikirim atau sudah selesai.'); @@ -185,7 +206,7 @@ public function konfirmasiSelesai($id) // --- FITUR PETANI --- - // Daftar Pesanan Masuk + // Daftar Pesanan Masuk untuk Dashboard Petani public function pesananMasuk() { $petaniId = Auth::guard('petani')->id(); @@ -200,7 +221,7 @@ public function pesananMasuk() return view('petani.pesanan.index', compact('pesanans')); } - // Update Status (Terima/Tolak/Kirim) + // Update Status Pesanan oleh Petani public function updateStatus(Request $request, $id) { $transaksi = Transaksi::findOrFail($id); @@ -212,8 +233,9 @@ public function updateStatus(Request $request, $id) $transaksi->status = $request->status; $transaksi->save(); + // Jika dibatalkan, kembalikan stok produk if ($request->status == 'batal') { - foreach ($transaksi->details as $detail) { + foreach ($transaksi->detailTransaksis as $detail) { $detail->produk->increment('stok', $detail->jumlah); } } @@ -221,12 +243,11 @@ public function updateStatus(Request $request, $id) return back()->with('success', 'Status pesanan berhasil diperbarui.'); } - // Detail Pesanan (Petani) + // Detail Pesanan untuk Sisi Petani public function pesananDetail($id) { $petaniId = Auth::guard('petani')->id(); - // Ambil transaksi berdasarkan ID $pesanan = Transaksi::whereHas('detailTransaksis.produk', function ($q) use ($petaniId) { $q->where('petani_id', $petaniId); }) @@ -235,4 +256,4 @@ public function pesananDetail($id) return view('petani.pesanan.detail', compact('pesanan')); } -} +} \ No newline at end of file diff --git a/app/Http/Controllers/WilayahController.php b/app/Http/Controllers/WilayahController.php new file mode 100644 index 0000000..bdbfe7a --- /dev/null +++ b/app/Http/Controllers/WilayahController.php @@ -0,0 +1,29 @@ +code)->get(); + return response()->json($kota); + } + + public function getKecamatan(Request $request) + { + $kecamatan = District::where('city_code', $request->code)->get(); + return response()->json($kecamatan); + } + + public function getDesa(Request $request) + { + $desa = Village::where('district_code', $request->code)->get(); + return response()->json($desa); + } +} \ No newline at end of file diff --git a/app/Models/Produk.php b/app/Models/Produk.php index e5ac3a5..70b9638 100644 --- a/app/Models/Produk.php +++ b/app/Models/Produk.php @@ -17,7 +17,11 @@ class Produk extends Model 'harga', 'stok', 'deskripsi', - 'foto_produk' + 'foto_produk', + 'provinsi_code', + 'kota_code', + 'kecamatan_code', + 'desa_code', ]; public function petani() diff --git a/composer.json b/composer.json index 44c6054..17f22e1 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,8 @@ "require": { "php": "^8.2", "laravel/framework": "^12.0", - "laravel/tinker": "^2.10.1" + "laravel/tinker": "^2.10.1", + "laravolt/indonesia": "^0.39.0" }, "require-dev": { "fakerphp/faker": "^1.23", diff --git a/composer.lock b/composer.lock index 6efa171..f6c5f78 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c514d8f7b9fc5970bdd94287905ef584", + "content-hash": "766a52f13bbfa552b799835b6f5f3e18", "packages": [ { "name": "brick/math", @@ -1457,6 +1457,88 @@ }, "time": "2025-01-27T14:24:01+00:00" }, + { + "name": "laravolt/indonesia", + "version": "v0.39", + "source": { + "type": "git", + "url": "https://github.com/laravolt/indonesia.git", + "reference": "351e1c0e69b9415b0a56e03677fcfe0bbeca513a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravolt/indonesia/zipball/351e1c0e69b9415b0a56e03677fcfe0bbeca513a", + "reference": "351e1c0e69b9415b0a56e03677fcfe0bbeca513a", + "shasum": "" + }, + "require": { + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.3|^8.0" + }, + "require-dev": { + "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0", + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^9.0|^10.5|^11.5.3" + }, + "suggest": { + "laravolt/suitable": "Required if you want to access editor panel", + "spatie/geocoder": "Synchronize latitude longitude data directly using Google's Geocoding Service" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Indonesia": "Laravolt\\Indonesia\\Facade" + }, + "providers": [ + "Laravolt\\Indonesia\\ServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Laravolt\\Indonesia\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bayu Hendra Winata", + "email": "bayu.hendra@javan.co.id" + }, + { + "name": "Akbar Adhatama", + "email": "am.adhatama@gmail.com" + }, + { + "name": "Deri Ramdani", + "email": "deri.ramdani1@gmail.com" + } + ], + "description": "Package Laravel yang berisi data Provinsi, Kabupaten/Kota, Kecamatan, dan Keluarahan/Desa di seluruh Indonesia.", + "keywords": [ + "desa", + "indonesia", + "kabupaten", + "kecamatan", + "kelurahan", + "kota", + "laravel", + "laravolt", + "provinsi" + ], + "support": { + "issues": "https://github.com/laravolt/indonesia/issues", + "source": "https://github.com/laravolt/indonesia/tree/v0.39" + }, + "time": "2026-01-03T13:26:02+00:00" + }, { "name": "league/commonmark", "version": "2.7.1", diff --git a/config/laravolt/indonesia.php b/config/laravolt/indonesia.php new file mode 100644 index 0000000..24cd1dd --- /dev/null +++ b/config/laravolt/indonesia.php @@ -0,0 +1,24 @@ + 'indonesia_', + 'route' => [ + 'enabled' => false, + 'middleware' => ['web', 'auth'], + 'prefix' => 'indonesia', + ], + 'view' => [ + 'layout' => 'ui::layouts.app', + ], + 'menu' => [ + 'enabled' => false, + ], + 'cache' => [ + 'ttl' => env('INDONESIA_CACHE_TTL', 3600), + 'prefix' => env('INDONESIA_CACHE_PREFIX', 'indonesia_service'), + 'store' => env('INDONESIA_CACHE_STORE', 'redis'), + ], + 'database' => [ + 'connection' => env('INDONESIA_DB_CONNECTION', null), + ], +]; diff --git a/database/migrations/2016_08_03_072729_create_provinces_table.php b/database/migrations/2016_08_03_072729_create_provinces_table.php new file mode 100644 index 0000000..d416d57 --- /dev/null +++ b/database/migrations/2016_08_03_072729_create_provinces_table.php @@ -0,0 +1,42 @@ +connection())->create(config('laravolt.indonesia.table_prefix').'provinces', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->char('code', 2)->unique(); + $table->string('name', 255); + $table->text('meta')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop(config('laravolt.indonesia.table_prefix').'provinces'); + } +} diff --git a/database/migrations/2016_08_03_072750_create_cities_table.php b/database/migrations/2016_08_03_072750_create_cities_table.php new file mode 100644 index 0000000..78c29e3 --- /dev/null +++ b/database/migrations/2016_08_03_072750_create_cities_table.php @@ -0,0 +1,48 @@ +connection())->create(config('laravolt.indonesia.table_prefix').'cities', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->char('code', 4)->unique(); + $table->char('province_code', 2); + $table->string('name', 255); + $table->text('meta')->nullable(); + $table->timestamps(); + + $table->foreign('province_code') + ->references('code') + ->on(config('laravolt.indonesia.table_prefix').'provinces') + ->onUpdate('cascade')->onDelete('restrict'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop(config('laravolt.indonesia.table_prefix').'cities'); + } +} diff --git a/database/migrations/2016_08_03_072804_create_districts_table.php b/database/migrations/2016_08_03_072804_create_districts_table.php new file mode 100644 index 0000000..451478f --- /dev/null +++ b/database/migrations/2016_08_03_072804_create_districts_table.php @@ -0,0 +1,48 @@ +connection())->create(config('laravolt.indonesia.table_prefix').'districts', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->char('code', 7)->unique(); + $table->char('city_code', 4); + $table->string('name', 255); + $table->text('meta')->nullable(); + $table->timestamps(); + + $table->foreign('city_code') + ->references('code') + ->on(config('laravolt.indonesia.table_prefix').'cities') + ->onUpdate('cascade')->onDelete('restrict'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop(config('laravolt.indonesia.table_prefix').'districts'); + } +} diff --git a/database/migrations/2016_08_03_072819_create_villages_table.php b/database/migrations/2016_08_03_072819_create_villages_table.php new file mode 100644 index 0000000..14eea55 --- /dev/null +++ b/database/migrations/2016_08_03_072819_create_villages_table.php @@ -0,0 +1,48 @@ +connection())->create(config('laravolt.indonesia.table_prefix').'villages', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->char('code', 10)->unique(); + $table->char('district_code', 7); + $table->string('name', 255); + $table->text('meta')->nullable(); + $table->timestamps(); + + $table->foreign('district_code') + ->references('code') + ->on(config('laravolt.indonesia.table_prefix').'districts') + ->onUpdate('cascade')->onDelete('restrict'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop(config('laravolt.indonesia.table_prefix').'villages'); + } +} diff --git a/database/migrations/2026_03_01_125919_add_lokasi_to_produks_table.php b/database/migrations/2026_03_01_125919_add_lokasi_to_produks_table.php new file mode 100644 index 0000000..1ad90c0 --- /dev/null +++ b/database/migrations/2026_03_01_125919_add_lokasi_to_produks_table.php @@ -0,0 +1,29 @@ +dropColumn('lokasi'); + } + + $table->char('provinsi_code', 2)->nullable()->after('deskripsi'); + $table->char('kota_code', 4)->nullable()->after('provinsi_code'); + $table->char('kecamatan_code', 7)->nullable()->after('kota_code'); + $table->char('desa_code', 10)->nullable()->after('kecamatan_code'); + }); + } + + public function down(): void + { + Schema::table('produks', function (Blueprint $table) { + $table->dropColumn(['provinsi_code', 'kota_code', 'kecamatan_code', 'desa_code']); + $table->string('lokasi')->nullable(); + }); + } +}; \ No newline at end of file diff --git a/database/seeders/TransaksiSeeder.php b/database/seeders/TransaksiSeeder.php index 9ee9983..c8ab4fb 100644 --- a/database/seeders/TransaksiSeeder.php +++ b/database/seeders/TransaksiSeeder.php @@ -69,7 +69,7 @@ public function run(): void 'kode_invoice' => 'INV-' . $tahunIni . '03-003', 'pembeli_id' => 1, 'petani_id' => 1, - 'tanggal_transaksi' => Carbon::create($tahunIni, 3, 5, 9, 15, 0), + 'tanggal_transaksi' => Carbon::create($tahunIni, 1, 5, 9, 15, 0), 'alamat_pengiriman' => 'Perumahan Indah Blok C2, Surabaya', 'total_harga' => 150000, 'status' => 'selesai', diff --git a/public/template/frontend/img/testimonial-1.jpg b/public/template/frontend/img/.jpg similarity index 100% rename from public/template/frontend/img/testimonial-1.jpg rename to public/template/frontend/img/.jpg diff --git a/public/template/frontend/img/best-product-4.jpg b/public/template/frontend/img/no-image.jpg similarity index 100% rename from public/template/frontend/img/best-product-4.jpg rename to public/template/frontend/img/no-image.jpg diff --git a/resources/views/landing/checkout.blade.php b/resources/views/landing/checkout.blade.php index 20ff068..82a3266 100644 --- a/resources/views/landing/checkout.blade.php +++ b/resources/views/landing/checkout.blade.php @@ -79,6 +79,7 @@ Kembali + diff --git a/resources/views/landing/home.blade.php b/resources/views/landing/home.blade.php index 9af55ef..cdbb7e7 100644 --- a/resources/views/landing/home.blade.php +++ b/resources/views/landing/home.blade.php @@ -4,45 +4,39 @@ @section('content') -
-
-
-
-
- 🌾 Mitra Petani Padi Indonesia -
-

- Pusat Jual Beli
- Gabah Panen Raya. -

-

- Platform langsung yang menghubungkan petani padi lokal dengan pembeli. - Dapatkan harga gabah terbaik dan gabah berkualitas. -

- +
+
+
+
+
+ 🌾 Mitra Petani Padi Indonesia
-
-
- Padi Premium -
-
-
- -
-
-
100% Panen Baru
- Kualitas Terjamin -
+

+ Pusat Jual Beli
+ Gabah Panen Raya. +

+

+ Platform langsung yang menghubungkan petani padi lokal dengan pembeli. + Dapatkan harga gabah terbaik dan gabah berkualitas. +

+ + Belanja + +
+
+
+ Padi Premium +
+
+
+ +
+
+
100% Panen Baru
+ Kualitas Terjamin
@@ -50,197 +44,198 @@
+
-
-
-
-
-
- -
-
Siap Antar
-

Armada pickup siap kirim langsung ke lokasi Anda.

+
+
+
+
+
+
+
Siap Antar
+

Armada pickup siap kirim langsung ke lokasi Anda.

-
-
-
- -
-
Harga Petani
-

Harga tangan pertama langsung tanpa tengkulak.

+
+
+
+
+
+
Harga Petani
+

Harga tangan pertama langsung tanpa tengkulak.

-
-
-
- -
-
Kualitas Super
-

Jaminan padi berkualitas, bulir utuh, bersih, dan bebas campuran.

+
+
+
+
+
+
Kualitas Super
+

Jaminan padi berkualitas, bulir utuh, bersih, dan bebas campuran.

-
-
-
- -
-
Transaksi Aman
-

Pembayaran fleksibel, bisa transfer atau COD.

+
+
+
+
+
+
Transaksi Aman
+

Pembayaran fleksibel, bisa transfer atau COD.

+
- {{-- Produk terlaris --}} -
-
-

Produk Terlaris

-

Pilihan favorit pelanggan kami minggu ini.

-
-
+{{-- Produk terlaris --}} +
+
+

Produk Terlaris

+

Pilihan favorit pelanggan kami minggu ini.

+
+
- @forelse($produkTerlaris as $item) -
-
-
- {{ $item->nama_produk }} -
+ @forelse($produkTerlaris as $item) +
+
+
+ {{ $item->nama_produk }} +
-
- Terjual {{ $item->total_terjual ?? 0 }} -
+
+ Terjual {{ $item->total_terjual ?? 0 }} +
-
-

{{ $item->nama_produk }}

-

- {{ Str::limit($item->deskripsi, 50) }} +

+

{{ $item->nama_produk }}

+

+ {{ Str::limit($item->deskripsi, 50) }} +

+ +
+
+

Rp {{ number_format($item->harga, 0, ',', '.') }}

- -
-
-

Rp {{ number_format($item->harga, 0, ',', '.') }} -

- / kg -
- - - -
+ / kg
+ + +
- @empty -
-

Belum ada data penjualan.

-
- @endforelse +
+
+ @empty +
+

Belum ada data penjualan.

+
+ @endforelse +
+
+ +
+
+
+
+ +

150+

+

Petani Mitra

+
+
+ +

2,500+

+

Transaksi

+
+
+ +

4.8

+

Rating Rata-rata

+
+
+ +

100%

+

Garansi Kualitas

+
+
-
-
-
-
- -

150+

-

Petani Mitra

-
-
- -

2,500+

-

Transaksi

-
-
- -

4.8

-

Rating Rata-rata

-
-
- -

100%

-

Garansi Kualitas

-
+{{-- Katalog --}} +
+
+

Stok Gudang Kami

+

Pilih varietas gabah yang Anda butuhkan hari ini.

+
+ + @php + $kategoriList = \App\Models\Kategori::all(); + @endphp + +
+
+
+ + Semua + + + {{-- 2. Looping Kategori dari Database --}} + @foreach($kategoriList as $kat) + + {{ $kat->nama_kategori }} + + @endforeach
- {{-- Katalog --}} -
-
-

Stok Gudang Kami

-

Pilih varietas gabah yang Anda butuhkan hari ini.

-
- - @php - $kategoriList = \App\Models\Kategori::all(); - @endphp - -
-
-
- - Semua - - - {{-- 2. Looping Kategori dari Database --}} - @foreach($kategoriList as $kat) - - {{ $kat->nama_kategori }} - - @endforeach -
-
-
- -
- @include('landing.partials.product_list') -
- - +
+ @include('landing.partials.product_list')
- {{-- Banner --}} -
-
-
-
-

Butuh Suplai Besar?

-

- Kami melayani kerjasama untuk penggilingan padi, restoran, dan toko - kelontong dengan harga grosir spesial. -

- - Hubungi Kami - -
-
- -
+ +
+ +{{-- Banner --}} +
+
+
+
+

Butuh Suplai Besar?

+

+ Kami melayani kerjasama untuk penggilingan padi, restoran, dan toko + kelontong dengan harga grosir spesial. +

+ + Hubungi Kami + +
+
+
+
@endsection @section('js') - + @endsection \ No newline at end of file diff --git a/resources/views/landing/pesan/index.blade.php b/resources/views/landing/pesan/index.blade.php index b83108e..91fdcf1 100644 --- a/resources/views/landing/pesan/index.blade.php +++ b/resources/views/landing/pesan/index.blade.php @@ -5,17 +5,18 @@ @endsection @section('content') -
-

Pesan Saya

+
+

Pesan Saya

- {{-- Container Utama --}} -
- - {{-- KOLOM KIRI: DAFTAR PESAN --}} -
- + {{-- Container Utama --}} +
- + {{-- KOLOM KIRI: DAFTAR PESAN --}} +
+ {{-- Header Sidebar --}} +
+
Pesan Masuk
+
- {{-- KOLOM KANAN: PLACEHOLDER --}} -
-
- + + {{-- KOLOM KANAN: PLACEHOLDER --}} +
+
+
+ +
+
Selamat Datang di Chat
+

Pilih salah satu percakapan di sebelah kiri
untuk mulai berdiskusi dengan Petani. +

+
@endsection \ No newline at end of file diff --git a/resources/views/landing/pesan/show.blade.php b/resources/views/landing/pesan/show.blade.php index 104136e..a015455 100644 --- a/resources/views/landing/pesan/show.blade.php +++ b/resources/views/landing/pesan/show.blade.php @@ -107,6 +107,7 @@ @section('content')
+

Pesan Saya

{{-- SIDEBAR KIRI --}} diff --git a/resources/views/landing/shop.blade.php b/resources/views/landing/shop.blade.php index 612c64b..e3ecc1d 100644 --- a/resources/views/landing/shop.blade.php +++ b/resources/views/landing/shop.blade.php @@ -3,186 +3,268 @@ @section('title', 'Belanja Padi & Beras') @section('content') - {{-- Header Halaman --}} -
-
-

Belanja Produk Kami

- -
+{{-- Header Halaman --}} +
+
+

Belanja Produk Kami

+
+
- {{-- Konten Utama --}} -
-
+{{-- Konten Utama --}} +
+
- {{-- SIDEBAR FILTER --}} -
+ {{-- SIDEBAR FILTER --}} +
- {{-- Card Kategori --}} -
-
-
Kategori
-
- - @php - $kategoriList = \App\Models\Kategori::all(); - $currentKat = request('kategori'); - @endphp - - {{-- Tombol Semua Kategori --}} - - Semua Kategori - - - {{-- Looping Kategori Database --}} - @foreach($kategoriList as $kat) - - - {{ $kat->nama_kategori }} - - @if($currentKat == $kat->slug) - - @endif - - @endforeach + {{-- Filter Wilayah Lengkap--}} +
+
+
Filter Wilayah Lengkap
+
+ @foreach(request()->only(['search', 'kategori', 'sort']) as $key => $val) + + @endforeach +
+ +
-
-
- {{-- Banner Info Kecil --}} -
-
-
Produk Segar
-

Semua produk kami diambil langsung dari petani lokal terpercaya. -

+
+ + +
+ +
+ + +
+ +
+ + +
+ + + +
+
+ + {{-- Card Kategori --}} +
+
+
Kategori
+
+ + @php + $kategoriList = \App\Models\Kategori::all(); + $currentKat = request('kategori'); + @endphp + + {{-- Tombol Semua Kategori --}} + + Semua Kategori + + + {{-- Looping Kategori Database --}} + @foreach($kategoriList as $kat) + + + {{ $kat->nama_kategori }} + + @if($currentKat == $kat->slug) + + @endif + + @endforeach +
- {{-- LIST PRODUK --}} -
+ {{-- Banner Info Kecil --}} +
+
+
Produk Segar
+

Semua produk kami diambil langsung dari petani lokal terpercaya. +

+
+
+
- {{-- Top Bar (Info Jumlah & Sorting) --}} -
-
- Menampilkan {{ $produks->firstItem() ?? 0 }}-{{ $produks->lastItem() ?? 0 }} - dari {{ $produks->total() }} produk -
+ {{-- LIST PRODUK --}} +
-
- -
- {{-- Pertahankan query lain (search/kategori) saat sorting --}} - @foreach(request()->except('sort') as $key => $value) - - @endforeach - - -
-
+ {{-- Top Bar (Info Jumlah & Sorting) --}} +
+
+ Menampilkan {{ $produks->firstItem() ?? 0 }}-{{ $produks->lastItem() + ?? 0 }} + dari {{ $produks->total() }} produk
- {{-- Grid Produk --}} -
- @forelse($produks as $produk) - - + setupCascading('#filter_provinsi', '#filter_kota', 'get-kota', 'Semua Kota'); + setupCascading('#filter_kota', '#filter_kecamatan', 'get-kecamatan', 'Semua Kecamatan'); + setupCascading('#filter_kecamatan', '#filter_desa', 'get-desa', 'Semua Desa'); +}); + + + @endsection \ No newline at end of file diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php index 725d807..1739e09 100644 --- a/resources/views/layouts/admin.blade.php +++ b/resources/views/layouts/admin.blade.php @@ -231,12 +231,7 @@ - {{-- VERIFIKASI PETANI --}} - + {{-- MENU VERIFIKASI PETANI DIHAPUS SESUAI REVISI --}} {{-- GAPOKTAN --}} + + {{-- Menu Pesanan Masuk (Dengan Badge) --}} - @endif diff --git a/resources/views/layouts/frontend.blade.php b/resources/views/layouts/frontend.blade.php index 47dd931..b74caec 100644 --- a/resources/views/layouts/frontend.blade.php +++ b/resources/views/layouts/frontend.blade.php @@ -38,7 +38,12 @@ line-height: 1.6; } - h1, h2, h3, h4, h5, h6 { + h1, + h2, + h3, + h4, + h5, + h6 { font-family: 'Inter', sans-serif; font-weight: 700; color: var(--dark-text); @@ -46,9 +51,17 @@ } /* Utility Colors */ - .text-primary { color: var(--primary-color) !important; } - .bg-primary { background-color: var(--primary-color) !important; } - .border-primary { border-color: var(--primary-color) !important; } + .text-primary { + color: var(--primary-color) !important; + } + + .bg-primary { + background-color: var(--primary-color) !important; + } + + .border-primary { + border-color: var(--primary-color) !important; + } /* BUTTON */ .btn { @@ -144,7 +157,8 @@ -
+
@@ -153,7 +167,8 @@
- Desa Mancon, Kecamatan Wilangan, Kabupaten Nganjuk + Desa Mancon, Kecamatan Wilangan, + Kabupaten Nganjuk info@tanidesa.com
@@ -164,22 +179,27 @@