refactor: transaksi controller, cart dan filter wilayah lengkap

This commit is contained in:
sayasilvi 2026-03-02 02:52:24 +07:00
parent 69a3234d0b
commit f01c00fa87
29 changed files with 1827 additions and 992 deletions

3
.gitignore vendored
View File

@ -22,3 +22,6 @@
Homestead.json Homestead.json
Homestead.yaml Homestead.yaml
Thumbs.db Thumbs.db
/storage/framework/views/*.php
/storage/framework/cache/data/*
/storage/framework/sessions/*

View File

@ -141,14 +141,21 @@ ### Step 6: Database Migration
php artisan migrate --seed 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 Jalankan perintah untuk konfigurasi storage link untuk menyimpan gambar
```bash ```bash
php artisan storage:link php artisan storage:link
``` ```
### Step 8: Start Application ### Step 9: Start Application
**Terminal (Laravel Server):** **Terminal (Laravel Server):**
```bash ```bash
php artisan serve php artisan serve

View File

@ -12,15 +12,16 @@ class CartController extends Controller
// Menampilkan Halaman Keranjang // Menampilkan Halaman Keranjang
public function index() public function index()
{ {
// Cek Login Pembeli
if (!Auth::guard('pembeli')->check()) { if (!Auth::guard('pembeli')->check()) {
return redirect()->route('login')->with('error', 'Silakan login terlebih dahulu untuk melihat keranjang.'); return redirect()->route('login')->with('error', 'Silakan login terlebih dahulu untuk melihat keranjang.');
} }
$pembeli_id = Auth::guard('pembeli')->id(); $pembeli_id = Auth::guard('pembeli')->id();
// Ambil data keranjang dari Database $cart = Cart::with('produk')
$cart = Cart::with('produk')->where('pembeli_id', $pembeli_id)->get(); ->where('pembeli_id', $pembeli_id)
->latest()
->get();
return view('landing.cart', compact('cart')); return view('landing.cart', compact('cart'));
} }

View File

@ -25,11 +25,13 @@ public function index(Request $request)
return view('landing.partials.product_list', compact('produks'))->render(); return view('landing.partials.product_list', compact('produks'))->render();
} }
$produkTerlaris = Produk::withSum(['detailTransaksis as total_terjual' => function ($query) { $produkTerlaris = Produk::withSum([
'detailTransaksis as total_terjual' => function ($query) {
$query->whereHas('transaksi', function ($q) { $query->whereHas('transaksi', function ($q) {
$q->where('status', '!=', 'batal'); $q->where('status', '!=', 'batal');
}); });
}], 'jumlah') }
], 'jumlah')
->orderByDesc('total_terjual') ->orderByDesc('total_terjual')
->take(4) ->take(4)
->get(); ->get();
@ -41,6 +43,21 @@ public function shop(Request $request)
{ {
$query = Produk::where('stok', '>', 0); $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 != '') { if ($request->has('search') && $request->search != '') {
$query->where('nama_produk', 'like', '%' . $request->search . '%'); $query->where('nama_produk', 'like', '%' . $request->search . '%');
} }

View File

@ -6,9 +6,13 @@
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\Produk; use App\Models\Produk;
use App\Models\ProdukImage; 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\Auth;
use Illuminate\Support\Facades\Storage; 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 class ProdukController extends Controller
{ {
@ -25,8 +29,9 @@ public function index()
public function create() public function create()
{ {
$kategoris = Kategori::all(); $kategoris = Kategori::all();
$provinsis = Province::all();
return view('petani.produk.create', compact('kategoris')); return view('petani.produk.create', compact('kategoris', 'provinsis'));
} }
public function store(Request $request) public function store(Request $request)
@ -37,6 +42,10 @@ public function store(Request $request)
'harga' => 'required|numeric|min:0', 'harga' => 'required|numeric|min:0',
'stok' => 'required|integer|min:0', 'stok' => 'required|integer|min:0',
'deskripsi' => 'required|string', '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_produk' => 'required|image|mimes:jpeg,png,jpg|max:2048',
'foto_tambahan.*' => 'nullable|image|mimes:jpeg,png,jpg|max:2048' 'foto_tambahan.*' => 'nullable|image|mimes:jpeg,png,jpg|max:2048'
]); ]);
@ -53,6 +62,10 @@ public function store(Request $request)
'kategori_id' => $request->kategori_id, 'kategori_id' => $request->kategori_id,
'nama_produk' => $request->nama_produk, 'nama_produk' => $request->nama_produk,
'harga' => $request->harga, '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, 'stok' => $request->stok,
'deskripsi' => $request->deskripsi, 'deskripsi' => $request->deskripsi,
'foto_produk' => $fotoPath, 'foto_produk' => $fotoPath,
@ -77,7 +90,12 @@ public function edit($id)
$produk = Produk::with('images')->where('id', $id)->where('petani_id', Auth::guard('petani')->id())->firstOrFail(); $produk = Produk::with('images')->where('id', $id)->where('petani_id', Auth::guard('petani')->id())->firstOrFail();
$kategoris = Kategori::all(); $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) public function update(Request $request, $id)
@ -92,6 +110,10 @@ public function update(Request $request, $id)
'nama_produk' => 'required|string|max:255', 'nama_produk' => 'required|string|max:255',
'kategori_id' => 'required|exists:kategoris,id', 'kategori_id' => 'required|exists:kategoris,id',
'harga' => 'required|numeric|min:0', 'harga' => 'required|numeric|min:0',
'provinsi_code' => 'required',
'kota_code' => 'required',
'kecamatan_code' => 'required',
'desa_code' => 'required',
'stok' => 'required|integer|min:0', 'stok' => 'required|integer|min:0',
'deskripsi' => 'required|string', 'deskripsi' => 'required|string',
'foto_produk' => 'nullable|image|mimes:jpeg,png,jpg|max:2048', 'foto_produk' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
@ -114,6 +136,10 @@ public function update(Request $request, $id)
'nama_produk' => $request->nama_produk, 'nama_produk' => $request->nama_produk,
'kategori_id' => $request->kategori_id, 'kategori_id' => $request->kategori_id,
'harga' => $request->harga, '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, 'stok' => $request->stok,
'deskripsi' => $request->deskripsi, 'deskripsi' => $request->deskripsi,
'foto_produk' => $produk->foto_produk 'foto_produk' => $produk->foto_produk
@ -122,7 +148,8 @@ public function update(Request $request, $id)
// Tambah Foto Galeri // Tambah Foto Galeri
if ($request->hasFile('foto_tambahan')) { if ($request->hasFile('foto_tambahan')) {
foreach ($request->file('foto_tambahan') as $file) { 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'); $path = $file->store('produk/gallery', 'public');
ProdukImage::create([ ProdukImage::create([

View File

@ -6,6 +6,7 @@
use App\Models\Produk; use App\Models\Produk;
use App\Models\Transaksi; use App\Models\Transaksi;
use App\Models\DetailTransaksi; use App\Models\DetailTransaksi;
use App\Models\Cart;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -13,10 +14,16 @@ class TransaksiController extends Controller
{ {
// --- FITUR PEMBELI --- // --- FITUR PEMBELI ---
// Tampilkan Halaman Checkout /**
* Tampilkan Halaman Checkout
* Mendukung Beli Langsung dan Checkout dari Keranjang (Database)
*/
public function checkoutPage(Request $request) 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')) { if ($request->has('produk_id')) {
$produk = Produk::with('petani')->findOrFail($request->produk_id); $produk = Produk::with('petani')->findOrFail($request->produk_id);
$items = collect([ $items = collect([
@ -34,32 +41,48 @@ public function checkoutPage(Request $request)
return view('landing.checkout', compact('items', 'total_belanja')); return view('landing.checkout', compact('items', 'total_belanja'));
} }
// Checkout dari Keranjang // 2. LOGIKA CHECKOUT DARI KERANJANG (Database)
$cart = session()->get('cart'); $cartIds = $request->query('cart_ids');
if ($cart && count($cart) > 0) {
$items = collect(); if (!$cartIds) {
foreach ($cart as $id => $details) { return redirect()->route('cart')->with('error', 'Pilih minimal satu produk di keranjang untuk dicheckout.');
$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']
]);
} }
$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'); $total_belanja = $items->sum('subtotal');
return view('landing.checkout', compact('items', 'total_belanja')); return view('landing.checkout', compact('items', 'total_belanja'));
} }
return redirect()->route('shop')->with('error', 'Keranjang Anda kosong, silakan belanja dulu.'); /**
} * Proses Pembuatan Transaksi
* Mengelompokkan pesanan berdasarkan Petani dan membersihkan keranjang
// fungsi prosesCheckout */
public function prosesCheckout(Request $request) public function prosesCheckout(Request $request)
{ {
$request->validate([ $request->validate([
@ -72,7 +95,7 @@ public function prosesCheckout(Request $request)
DB::transaction(function () use ($request, $pembeli_id) { DB::transaction(function () use ($request, $pembeli_id) {
if ($request->has('produk_id')) { if ($request->has('produk_id')) {
// --- LOGIKA BELI LANGSUNG (Single Item) --- // --- LOGIKA BELI LANGSUNG ---
$produk = Produk::findOrFail($request->produk_id); $produk = Produk::findOrFail($request->produk_id);
$total_harga = $produk->harga * $request->jumlah; $total_harga = $produk->harga * $request->jumlah;
@ -97,65 +120,64 @@ public function prosesCheckout(Request $request)
$produk->decrement('stok', $request->jumlah); $produk->decrement('stok', $request->jumlah);
} else { } else {
// --- LOGIKA KERANJANG (Cart) --- // --- LOGIKA KERANJANG (DATABASE) ---
$cart = session()->get('cart'); if (!$request->cart_ids) {
throw new \Exception("ID keranjang tidak ditemukan.");
// 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) { $cartIds = explode(',', $request->cart_ids);
$subtotal_transaksi = 0;
$kode_invoice = 'INV/' . date('Ymd') . '/' . rand(1000, 9999);
// Membuat Header Transaksi per Petani $cartItems = Cart::with('produk')
->whereIn('id', $cartIds)
->where('pembeli_id', $pembeli_id)
->get();
// 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([ $transaksi = Transaksi::create([
'pembeli_id' => $pembeli_id, 'pembeli_id' => $pembeli_id,
'petani_id' => $petani_id, 'petani_id' => $petani_id,
'tanggal_transaksi' => now(), 'tanggal_transaksi' => now(),
'alamat_pengiriman' => $request->alamat_pengiriman, 'alamat_pengiriman' => $request->alamat_pengiriman,
'total_harga' => 0, 'total_harga' => 0, // Diupdate setelah menghitung subtotal
'status' => 'menunggu konfirmasi', 'status' => 'menunggu konfirmasi',
'kode_invoice' => $kode_invoice, 'kode_invoice' => 'INV/' . date('Ymd') . '/' . rand(1000, 9999),
]); ]);
$subtotal_transaksi = 0;
foreach ($items as $item) { foreach ($items as $item) {
$produk = $item['produk']; $total_per_item = $item->produk->harga * $item->quantity;
$qty = $item['qty'];
$total_per_item = $produk->harga * $qty;
$subtotal_transaksi += $total_per_item; $subtotal_transaksi += $total_per_item;
DetailTransaksi::create([ DetailTransaksi::create([
'transaksi_id' => $transaksi->id, 'transaksi_id' => $transaksi->id,
'produk_id' => $produk->id, 'produk_id' => $item->produk->id,
'jumlah' => $qty, 'jumlah' => $item->quantity,
'harga_satuan' => $produk->harga, 'harga_satuan' => $item->produk->harga,
'subtotal' => $total_per_item, '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]); $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() public function pesananSaya()
{ {
$transaksis = Transaksi::with(['detailTransaksis.produk.petani']) $transaksis = Transaksi::with(['detailTransaksis.produk.petani'])
@ -172,11 +194,10 @@ public function konfirmasiSelesai($id)
$transaksi = Transaksi::where('pembeli_id', Auth::guard('pembeli')->id()) $transaksi = Transaksi::where('pembeli_id', Auth::guard('pembeli')->id())
->findOrFail($id); ->findOrFail($id);
// Hanya bisa selesai jika status sebelumnya 'dikirim'
if ($transaksi->status == 'dikirim') { if ($transaksi->status == 'dikirim') {
$transaksi->status = 'selesai'; $transaksi->status = 'selesai';
$transaksi->save(); $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.'); return back()->with('error', 'Pesanan belum dikirim atau sudah selesai.');
@ -185,7 +206,7 @@ public function konfirmasiSelesai($id)
// --- FITUR PETANI --- // --- FITUR PETANI ---
// Daftar Pesanan Masuk // Daftar Pesanan Masuk untuk Dashboard Petani
public function pesananMasuk() public function pesananMasuk()
{ {
$petaniId = Auth::guard('petani')->id(); $petaniId = Auth::guard('petani')->id();
@ -200,7 +221,7 @@ public function pesananMasuk()
return view('petani.pesanan.index', compact('pesanans')); return view('petani.pesanan.index', compact('pesanans'));
} }
// Update Status (Terima/Tolak/Kirim) // Update Status Pesanan oleh Petani
public function updateStatus(Request $request, $id) public function updateStatus(Request $request, $id)
{ {
$transaksi = Transaksi::findOrFail($id); $transaksi = Transaksi::findOrFail($id);
@ -212,8 +233,9 @@ public function updateStatus(Request $request, $id)
$transaksi->status = $request->status; $transaksi->status = $request->status;
$transaksi->save(); $transaksi->save();
// Jika dibatalkan, kembalikan stok produk
if ($request->status == 'batal') { if ($request->status == 'batal') {
foreach ($transaksi->details as $detail) { foreach ($transaksi->detailTransaksis as $detail) {
$detail->produk->increment('stok', $detail->jumlah); $detail->produk->increment('stok', $detail->jumlah);
} }
} }
@ -221,12 +243,11 @@ public function updateStatus(Request $request, $id)
return back()->with('success', 'Status pesanan berhasil diperbarui.'); return back()->with('success', 'Status pesanan berhasil diperbarui.');
} }
// Detail Pesanan (Petani) // Detail Pesanan untuk Sisi Petani
public function pesananDetail($id) public function pesananDetail($id)
{ {
$petaniId = Auth::guard('petani')->id(); $petaniId = Auth::guard('petani')->id();
// Ambil transaksi berdasarkan ID
$pesanan = Transaksi::whereHas('detailTransaksis.produk', function ($q) use ($petaniId) { $pesanan = Transaksi::whereHas('detailTransaksis.produk', function ($q) use ($petaniId) {
$q->where('petani_id', $petaniId); $q->where('petani_id', $petaniId);
}) })

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Laravolt\Indonesia\Models\City;
use Laravolt\Indonesia\Models\District;
use Laravolt\Indonesia\Models\Village;
class WilayahController extends Controller
{
public function getKota(Request $request)
{
$kota = City::where('province_code', $request->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);
}
}

View File

@ -17,7 +17,11 @@ class Produk extends Model
'harga', 'harga',
'stok', 'stok',
'deskripsi', 'deskripsi',
'foto_produk' 'foto_produk',
'provinsi_code',
'kota_code',
'kecamatan_code',
'desa_code',
]; ];
public function petani() public function petani()

View File

@ -8,7 +8,8 @@
"require": { "require": {
"php": "^8.2", "php": "^8.2",
"laravel/framework": "^12.0", "laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1" "laravel/tinker": "^2.10.1",
"laravolt/indonesia": "^0.39.0"
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.23", "fakerphp/faker": "^1.23",

84
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "c514d8f7b9fc5970bdd94287905ef584", "content-hash": "766a52f13bbfa552b799835b6f5f3e18",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@ -1457,6 +1457,88 @@
}, },
"time": "2025-01-27T14:24:01+00:00" "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", "name": "league/commonmark",
"version": "2.7.1", "version": "2.7.1",

View File

@ -0,0 +1,24 @@
<?php
return [
'table_prefix' => '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),
],
];

View File

@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateProvincesTable extends Migration
{
protected function connection()
{
// New config (optional)
return config('indonesia.database.connection')
// Backward compatibility
?? config('database.default');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::connection($this->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');
}
}

View File

@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCitiesTable extends Migration
{
protected function connection()
{
// New config (optional)
return config('indonesia.database.connection')
// Backward compatibility
?? config('database.default');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::connection($this->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');
}
}

View File

@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateDistrictsTable extends Migration
{
protected function connection()
{
// New config (optional)
return config('indonesia.database.connection')
// Backward compatibility
?? config('database.default');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::connection($this->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');
}
}

View File

@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateVillagesTable extends Migration
{
protected function connection()
{
// New config (optional)
return config('indonesia.database.connection')
// Backward compatibility
?? config('database.default');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::connection($this->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');
}
}

View File

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::table('produks', function (Blueprint $table) {
if (Schema::hasColumn('produks', 'lokasi')) {
$table->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();
});
}
};

View File

@ -69,7 +69,7 @@ public function run(): void
'kode_invoice' => 'INV-' . $tahunIni . '03-003', 'kode_invoice' => 'INV-' . $tahunIni . '03-003',
'pembeli_id' => 1, 'pembeli_id' => 1,
'petani_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', 'alamat_pengiriman' => 'Perumahan Indah Blok C2, Surabaya',
'total_harga' => 150000, 'total_harga' => 150000,
'status' => 'selesai', 'status' => 'selesai',

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -79,6 +79,7 @@
<a href="{{ route('cart') }}" <a href="{{ route('cart') }}"
class="btn btn-outline-secondary py-2 rounded-pill">Kembali</a> class="btn btn-outline-secondary py-2 rounded-pill">Kembali</a>
</div> </div>
<input type="hidden" name="cart_ids" value="{{ request('cart_ids') }}">
</form> </form>
</div> </div>
</div> </div>

View File

@ -19,14 +19,9 @@
Platform langsung yang menghubungkan petani padi lokal dengan pembeli. Platform langsung yang menghubungkan petani padi lokal dengan pembeli.
Dapatkan harga gabah terbaik dan gabah berkualitas. Dapatkan harga gabah terbaik dan gabah berkualitas.
</p> </p>
<div class="d-flex gap-2">
<a href="{{ route('shop') }}" class="btn btn-primary border-0 rounded-pill py-3 px-5 shadow"> <a href="{{ route('shop') }}" class="btn btn-primary border-0 rounded-pill py-3 px-5 shadow">
<i class="fas fa-shopping-bag me-2"></i> Belanja <i class="fas fa-shopping-bag me-2"></i> Belanja
</a> </a>
<a href="#katalog" class="btn btn-outline-secondary rounded-pill py-3 px-5">
Lihat Stok
</a>
</div>
</div> </div>
<div class="col-lg-6 order-1 order-lg-2 text-center"> <div class="col-lg-6 order-1 order-lg-2 text-center">
<div class="position-relative d-inline-block"> <div class="position-relative d-inline-block">

View File

@ -11,6 +11,7 @@
border-radius: 12px; border-radius: 12px;
overflow: hidden; overflow: hidden;
} }
/* --- Sidebar Kiri --- */ /* --- Sidebar Kiri --- */
.chat-sidebar { .chat-sidebar {
height: 100%; height: 100%;
@ -56,9 +57,18 @@
} }
/* Custom Scrollbar */ /* Custom Scrollbar */
::-webkit-scrollbar { width: 5px; } ::-webkit-scrollbar {
::-webkit-scrollbar-track { background: transparent; } width: 5px;
::-webkit-scrollbar-thumb { background: #ccc; border-radius: 10px; } }
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 10px;
}
</style> </style>
@endsection @endsection
@ -71,36 +81,45 @@
{{-- KOLOM KIRI: DAFTAR PESAN --}} {{-- KOLOM KIRI: DAFTAR PESAN --}}
<div class="col-md-4 chat-sidebar"> <div class="col-md-4 chat-sidebar">
<div class="sidebar-header"> {{-- Header Sidebar --}}
<input type="text" class="form-control bg-light border-0 rounded-pill px-3" placeholder="Cari pesan..."> <div class="p-3 border-bottom">
<h6 class="fw-bold text-dark mb-3">Pesan Masuk</h6>
<input type="text" class="form-control bg-light border-0 rounded-pill px-3"
placeholder="Cari percakapan...">
</div> </div>
<div class="chat-list-container"> {{-- List Chat --}}
<div class="list-group list-group-flush"> <div class="flex-grow-1 overflow-auto">
@forelse ($chatList as $chat) @forelse ($chatList as $chat)
<a href="{{ route('pembeli.pesan.show', $chat['lawan_id']) }}" <a href="{{ route('pembeli.pesan.show', $chat['lawan_id']) }}"
class="list-group-item list-group-item-action chat-item py-3 border-0"> class="d-flex align-items-center p-3 text-decoration-none text-dark chat-item">
<div class="d-flex align-items-center">
{{-- Avatar --}}
<div class="position-relative"> <div class="position-relative">
<img src="{{ asset('template/frontend/img/avatar.jpg') }}" class="rounded-circle shadow-sm" <img src="{{ asset('template/frontend/img/avatar.jpg') }}" class="rounded-circle shadow-sm"
width="50" height="50" style="object-fit: cover;"> width="45" height="45" style="object-fit: cover;">
{{-- Badge Merah Notifikasi --}}
@if($chat['unread'] > 0) @if($chat['unread'] > 0)
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger border border-white"> <span
{{ $chat['unread'] }} class="position-absolute top-0 start-100 translate-middle p-1 bg-danger border border-light rounded-circle"></span>
</span>
@endif @endif
</div> </div>
{{-- Info Pesan --}}
<div class="ms-3 flex-grow-1 overflow-hidden"> <div class="ms-3 flex-grow-1 overflow-hidden">
<div class="d-flex justify-content-between align-items-center mb-1"> <div class="d-flex justify-content-between align-items-center">
<h6 class="mb-0 text-dark fw-bold text-truncate">{{ $chat['nama'] }}</h6> <span class="fw-bold small">{{ $chat['nama'] }}</span>
<small class="text-muted" style="font-size: 0.75rem;">{{ $chat['time'] }}</small> <span class="text-muted" style="font-size: 0.7rem;">{{ $chat['time'] }}</span>
</div> </div>
<p class="mb-0 text-muted text-truncate small"> <div class="d-flex justify-content-between align-items-center mt-1">
{{ Str::limit($chat['last_message'], 35) }} <p class="mb-0 text-muted small text-truncate" style="max-width: 85%;">
{{ Str::limit($chat['last_message'], 25) }}
</p> </p>
{{-- Angka Merah Notifikasi --}}
@if($chat['unread'] > 0)
<span class="badge bg-danger rounded-pill" style="font-size: 0.6rem;">{{ $chat['unread']
}}</span>
@endif
</div> </div>
</div> </div>
</a> </a>
@ -112,7 +131,6 @@ class="list-group-item list-group-item-action chat-item py-3 border-0">
@endforelse @endforelse
</div> </div>
</div> </div>
</div>
{{-- KOLOM KANAN: PLACEHOLDER --}} {{-- KOLOM KANAN: PLACEHOLDER --}}
<div class="col-md-8 d-none d-md-flex chat-placeholder"> <div class="col-md-8 d-none d-md-flex chat-placeholder">
@ -121,7 +139,8 @@ class="list-group-item list-group-item-action chat-item py-3 border-0">
<i class="fas fa-comments fa-4x text-success"></i> <i class="fas fa-comments fa-4x text-success"></i>
</div> </div>
<h5 class="fw-bold text-dark">Selamat Datang di Chat</h5> <h5 class="fw-bold text-dark">Selamat Datang di Chat</h5>
<p class="small">Pilih salah satu percakapan di sebelah kiri<br>untuk mulai berdiskusi dengan Petani.</p> <p class="small">Pilih salah satu percakapan di sebelah kiri<br>untuk mulai berdiskusi dengan Petani.
</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -107,6 +107,7 @@
@section('content') @section('content')
<div class="container py-5"> <div class="container py-5">
<h2 class="mb-4 fw-bold text-dark">Pesan Saya</h2>
<div class="row g-0 chat-layout shadow-lg"> <div class="row g-0 chat-layout shadow-lg">
{{-- SIDEBAR KIRI --}} {{-- SIDEBAR KIRI --}}

View File

@ -23,6 +23,57 @@
{{-- SIDEBAR FILTER --}} {{-- SIDEBAR FILTER --}}
<div class="col-lg-3"> <div class="col-lg-3">
{{-- Filter Wilayah Lengkap--}}
<div class="card border-0 shadow-sm mb-4">
<div class="card-body p-4">
<h6 class="fw-bold mb-3 text-uppercase small text-muted">Filter Wilayah Lengkap</h6>
<form action="{{ route('shop') }}" method="GET">
@foreach(request()->only(['search', 'kategori', 'sort']) as $key => $val)
<input type="hidden" name="{{ $key }}" value="{{ $val }}">
@endforeach
<div class="mb-2">
<label class="small fw-bold">Provinsi</label>
<select name="provinsi" id="filter_provinsi" class="form-select form-select-sm">
<option value="">Semua Provinsi</option>
@foreach(\Laravolt\Indonesia\Models\Province::all() as $prov)
<option value="{{ $prov->code }}" {{ request('provinsi')==$prov->code ? 'selected' : ''
}}>{{ $prov->name }}</option>
@endforeach
</select>
</div>
<div class="mb-2">
<label class="small fw-bold">Kota/Kabupaten</label>
<select name="kota" id="filter_kota" class="form-select form-select-sm" {{
!request('provinsi') ? 'disabled' : '' }}>
<option value="">Semua Kota</option>
</select>
</div>
<div class="mb-2">
<label class="small fw-bold">Kecamatan</label>
<select name="kecamatan" id="filter_kecamatan" class="form-select form-select-sm" {{
!request('kota') ? 'disabled' : '' }}>
<option value="">Semua Kecamatan</option>
</select>
</div>
<div class="mb-3">
<label class="small fw-bold">Desa</label>
<select name="desa" id="filter_desa" class="form-select form-select-sm" {{
!request('kecamatan') ? 'disabled' : '' }}>
<option value="">Semua Desa</option>
</select>
</div>
<button type="submit" class="btn btn-primary btn-sm w-100 rounded-pill shadow-sm">
<i class="fas fa-search me-1"></i> Cari Produk
</button>
</form>
</div>
</div>
{{-- Card Kategori --}} {{-- Card Kategori --}}
<div class="card border-0 shadow-sm mb-4"> <div class="card border-0 shadow-sm mb-4">
<div class="card-body p-4"> <div class="card-body p-4">
@ -73,7 +124,8 @@ class="d-flex justify-content-between align-items-center text-decoration-none {{
{{-- Top Bar (Info Jumlah & Sorting) --}} {{-- Top Bar (Info Jumlah & Sorting) --}}
<div class="d-flex justify-content-between align-items-center mb-4 bg-white p-3 rounded shadow-sm border"> <div class="d-flex justify-content-between align-items-center mb-4 bg-white p-3 rounded shadow-sm border">
<div class="d-none d-md-block text-muted small"> <div class="d-none d-md-block text-muted small">
Menampilkan <span class="fw-bold text-dark">{{ $produks->firstItem() ?? 0 }}-{{ $produks->lastItem() ?? 0 }}</span> Menampilkan <span class="fw-bold text-dark">{{ $produks->firstItem() ?? 0 }}-{{ $produks->lastItem()
?? 0 }}</span>
dari <span class="fw-bold text-dark">{{ $produks->total() }}</span> produk dari <span class="fw-bold text-dark">{{ $produks->total() }}</span> produk
</div> </div>
@ -121,7 +173,8 @@ class="card-img-top w-100 h-100" alt="{{ $produk->nama_produk }}"
<div class="card-body d-flex justify-content-between flex-column p-3"> <div class="card-body d-flex justify-content-between flex-column p-3">
<div> <div>
<span class="badge bg-primary mb-2">{{ $produk->kategori->nama_kategori ?? 'Umum' }}</span> <span class="badge bg-primary mb-2">{{ $produk->kategori->nama_kategori ?? 'Umum'
}}</span>
<h6 class="fw-bold mb-2 text-dark">{{ $produk->nama_produk }}</h6> <h6 class="fw-bold mb-2 text-dark">{{ $produk->nama_produk }}</h6>
<p class="text-muted small mb-3">{{ Str::limit($produk->deskripsi, 60) }}</p> <p class="text-muted small mb-3">{{ Str::limit($produk->deskripsi, 60) }}</p>
</div> </div>
@ -165,6 +218,35 @@ class="card-img-top w-100 h-100" alt="{{ $produk->nama_produk }}"
</div> </div>
</div> </div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
$(document).ready(function() {
function setupCascading(source, target, routeName, placeholder) {
$(source).on('change', function() {
let code = $(this).val();
$(target).html('<option value="">Memuat...</option>').prop('disabled', true);
if(target === '#filter_kota') {
$('#filter_kecamatan, #filter_desa').html('<option value="">Semua...</option>').prop('disabled', true);
}
if (code) {
$.post("{{ url('/') }}/" + routeName, {code: code, _token: '{{ csrf_token() }}'}, function(data) {
$(target).html('<option value="">' + placeholder + '</option>').prop('disabled', false);
$.each(data, function(key, val) {
$(target).append(`<option value="${val.code}">${val.name}</option>`);
});
});
}
});
}
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');
});
</script>
<style> <style>
/* Hover Effect */ /* Hover Effect */
.product-card { .product-card {

View File

@ -231,12 +231,7 @@
</a> </a>
</li> </li>
{{-- VERIFIKASI PETANI --}} {{-- MENU VERIFIKASI PETANI DIHAPUS SESUAI REVISI --}}
<li class="sidebar-item {{ request()->is('admin/verifikasi*') ? 'active' : '' }}">
<a href="{{ route('admin.verifikasi.index') }}" class='sidebar-link'>
<i class="bi bi-person-badge-fill"></i> <span>Verifikasi Petani</span>
</a>
</li>
{{-- GAPOKTAN --}} {{-- GAPOKTAN --}}
<li class="sidebar-item {{ request()->is('admin/gapoktan*') ? 'active' : '' }}"> <li class="sidebar-item {{ request()->is('admin/gapoktan*') ? 'active' : '' }}">
@ -256,6 +251,23 @@
{{-- Menu Dashboard Petani --}} {{-- Menu Dashboard Petani --}}
@if (Auth::guard('petani')->check()) @if (Auth::guard('petani')->check())
{{-- LOGIC UNTUK MENGHITUNG NOTIFIKASI BADGE MERAH --}}
@php
$petaniId = Auth::guard('petani')->id();
// Hitung pesanan dengan status 'menunggu konfirmasi'
$notifPesanan = \App\Models\Transaksi::where('petani_id', $petaniId)
->where('status', 'menunggu konfirmasi')
->count();
// Hitung chat yang belum dibaca (sudah_dibaca = false)
// Asumsi penerima_type menggunakan namespace model penuh 'App\Models\Petani'
$notifPesan = \App\Models\Pesan::where('penerima_id', $petaniId)
->where('penerima_type', 'App\Models\Petani')
->where('sudah_dibaca', false)
->count();
@endphp
<li class="sidebar-item {{ request()->is('petani/dashboard') ? 'active' : '' }}"> <li class="sidebar-item {{ request()->is('petani/dashboard') ? 'active' : '' }}">
<a href="{{ route('petani.dashboard') }}" class='sidebar-link'> <a href="{{ route('petani.dashboard') }}" class='sidebar-link'>
<i class="bi bi-grid-fill"></i> <span>Dashboard</span> <i class="bi bi-grid-fill"></i> <span>Dashboard</span>
@ -266,15 +278,28 @@
<i class="bi bi-basket-fill"></i> <span>Kelola Produk</span> <i class="bi bi-basket-fill"></i> <span>Kelola Produk</span>
</a> </a>
</li> </li>
{{-- Menu Pesanan Masuk (Dengan Badge) --}}
<li class="sidebar-item {{ request()->is('petani/pesanan*') ? 'active' : '' }}"> <li class="sidebar-item {{ request()->is('petani/pesanan*') ? 'active' : '' }}">
<a href="{{ route('petani.pesanan.index') }}" class='sidebar-link'> <a href="{{ route('petani.pesanan.index') }}" class='sidebar-link d-flex justify-content-between align-items-center'>
<div>
<i class="bi bi-receipt"></i> <span>Pesanan Masuk</span> <i class="bi bi-receipt"></i> <span>Pesanan Masuk</span>
</div>
@if($notifPesanan > 0)
<span class="badge bg-danger rounded-pill">{{ $notifPesanan }}</span>
@endif
</a> </a>
</li> </li>
<li
class="sidebar-item {{ request()->is('petani/pesan') || request()->is('petani/pesan/*') ? 'active' : '' }}"> {{-- Menu Kotak Masuk (Dengan Badge) --}}
<a href="{{ route('petani.pesan.index') }}" class='sidebar-link'> <li class="sidebar-item {{ request()->is('petani/pesan') || request()->is('petani/pesan/*') ? 'active' : '' }}">
<a href="{{ route('petani.pesan.index') }}" class='sidebar-link d-flex justify-content-between align-items-center'>
<div>
<i class="bi bi-chat-dots-fill"></i> <span>Kotak Masuk</span> <i class="bi bi-chat-dots-fill"></i> <span>Kotak Masuk</span>
</div>
@if($notifPesan > 0)
<span class="badge bg-danger rounded-pill">{{ $notifPesan }}</span>
@endif
</a> </a>
</li> </li>
@endif @endif

View File

@ -38,7 +38,12 @@
line-height: 1.6; line-height: 1.6;
} }
h1, h2, h3, h4, h5, h6 { h1,
h2,
h3,
h4,
h5,
h6 {
font-family: 'Inter', sans-serif; font-family: 'Inter', sans-serif;
font-weight: 700; font-weight: 700;
color: var(--dark-text); color: var(--dark-text);
@ -46,9 +51,17 @@
} }
/* Utility Colors */ /* Utility Colors */
.text-primary { color: var(--primary-color) !important; } .text-primary {
.bg-primary { background-color: var(--primary-color) !important; } color: var(--primary-color) !important;
.border-primary { border-color: var(--primary-color) !important; } }
.bg-primary {
background-color: var(--primary-color) !important;
}
.border-primary {
border-color: var(--primary-color) !important;
}
/* BUTTON */ /* BUTTON */
.btn { .btn {
@ -144,7 +157,8 @@
<body> <body>
<div id="spinner" class="show w-100 vh-100 bg-white position-fixed translate-middle top-50 start-50 d-flex align-items-center justify-content-center"> <div id="spinner"
class="show w-100 vh-100 bg-white position-fixed translate-middle top-50 start-50 d-flex align-items-center justify-content-center">
<div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;"></div> <div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;"></div>
</div> </div>
@ -153,7 +167,8 @@
<div class="container"> <div class="container">
<div class="d-flex justify-content-start text-white" style="font-size: 0.85rem;"> <div class="d-flex justify-content-start text-white" style="font-size: 0.85rem;">
<div> <div>
<span class="me-3"><i class="fas fa-map-marker-alt me-2"></i> Desa Mancon, Kecamatan Wilangan, Kabupaten Nganjuk</span> <span class="me-3"><i class="fas fa-map-marker-alt me-2"></i> Desa Mancon, Kecamatan Wilangan,
Kabupaten Nganjuk</span>
<span class="me-3"><i class="fas fa-envelope me-2"></i> info@tanidesa.com</span> <span class="me-3"><i class="fas fa-envelope me-2"></i> info@tanidesa.com</span>
</div> </div>
</div> </div>
@ -164,22 +179,27 @@
<div class="container"> <div class="container">
<nav class="navbar navbar-light navbar-expand-xl py-3"> <nav class="navbar navbar-light navbar-expand-xl py-3">
<a href="{{ url('/') }}" class="navbar-brand d-flex align-items-center"> <a href="{{ url('/') }}" class="navbar-brand d-flex align-items-center">
<h1 class="text-primary m-0 fw-bold" style="font-size: 1.8rem; letter-spacing: -1px;">GriyaPadi.id</h1> <h1 class="text-primary m-0 fw-bold" style="font-size: 1.8rem; letter-spacing: -1px;">
GriyaPadi.id</h1>
</a> </a>
<button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse"> <button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarCollapse">
<span class="fa fa-bars text-primary"></span> <span class="fa fa-bars text-primary"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarCollapse"> <div class="collapse navbar-collapse" id="navbarCollapse">
<div class="navbar-nav mx-auto"> <div class="navbar-nav mx-auto">
<a href="{{ url('/') }}" class="nav-item nav-link mx-2 {{ request()->is('/') ? 'active' : '' }}">Home</a> <a href="{{ url('/') }}"
<a href="{{ route('shop') }}" class="nav-item nav-link mx-2 {{ request()->is('shop*') ? 'active' : '' }}">Belanja</a> class="nav-item nav-link mx-2 {{ request()->is('/') ? 'active' : '' }}">Home</a>
<a href="{{ route('shop') }}"
class="nav-item nav-link mx-2 {{ request()->is('shop*') ? 'active' : '' }}">Belanja</a>
</div> </div>
<div class="d-flex align-items-center mt-3 mt-xl-0"> <div class="d-flex align-items-center mt-3 mt-xl-0">
<form action="{{ route('shop') }}" method="GET" class="me-3 d-flex w-100 w-xl-auto mb-3 mb-xl-0"> <form action="{{ route('shop') }}" method="GET"
class="me-3 d-flex w-100 w-xl-auto mb-3 mb-xl-0">
<div class="input-group w-100"> <div class="input-group w-100">
<input type="search" name="search" class="form-control border-end-0" <input type="search" name="search" class="form-control border-end-0"
placeholder="Cari produk..." value="{{ request('search') }}" placeholder="Cari produk..." value="{{ request('search') }}"
@ -193,7 +213,9 @@
<a href="{{ route('cart') }}" class="position-relative me-3 text-dark"> <a href="{{ route('cart') }}" class="position-relative me-3 text-dark">
<i class="bi bi-bag fs-4"></i> <i class="bi bi-bag fs-4"></i>
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger" style="font-size: 0.6rem;"> <span
class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger"
style="font-size: 0.6rem;">
{{-- PERBAIKAN LOGIKA HITUNG CART --}} {{-- PERBAIKAN LOGIKA HITUNG CART --}}
@if (Auth::guard('pembeli')->check()) @if (Auth::guard('pembeli')->check())
@ -207,20 +229,48 @@
@if (Auth::guard('pembeli')->check()) @if (Auth::guard('pembeli')->check())
<div class="nav-item dropdown ms-2"> <div class="nav-item dropdown ms-2">
<a href="#" class="nav-link dropdown-toggle text-dark fw-bold d-flex align-items-center" data-bs-toggle="dropdown"> <a href="#"
<div class="bg-light rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 35px; height: 35px;"> class="nav-link dropdown-toggle text-dark fw-bold d-flex align-items-center"
data-bs-toggle="dropdown">
<div class="bg-light rounded-circle d-flex align-items-center justify-content-center me-2"
style="width: 35px; height: 35px;">
<i class="fas fa-user text-primary"></i> <i class="fas fa-user text-primary"></i>
</div> </div>
<span class="d-none d-xl-inline small">{{ Auth::guard('pembeli')->user()->nama_lengkap }}</span> <span
class="d-none d-xl-inline small">{{ Auth::guard('pembeli')->user()->nama_lengkap }}</span>
</a> </a>
<div class="dropdown-menu dropdown-menu-end border-0 shadow-sm m-0 rounded-3"> <div class="dropdown-menu dropdown-menu-end border-0 shadow-sm m-0 rounded-3">
<a href="{{ route('pembeli.profile') }}" class="dropdown-item py-2"><i class="bi bi-person me-2"></i> Profil</a> <a href="{{ route('pembeli.profile') }}" class="dropdown-item py-2"><i
<a href="{{ route('pembeli.pesan.index') }}" class="dropdown-item py-2"><i class="bi bi-chat-dots me-2"></i> Pesan</a> class="bi bi-person me-2"></i> Profil</a>
<a href="{{ route('pesanan.saya') }}" class="dropdown-item py-2"><i class="bi bi-bag-check me-2"></i> Riwayat Pesanan</a> <a href="{{ route('pembeli.pesan.index') }}"
class="dropdown-item py-2 d-flex justify-content-between align-items-center">
<span><i class="bi bi-chat-dots me-2"></i> Pesan</span>
{{-- Logika Badge Notifikasi Pembeli --}}
@if (Auth::guard('pembeli')->check())
@php
$notifPesanPembeli = \App\Models\Pesan::where(
'penerima_id',
Auth::guard('pembeli')->id(),
)
->where('penerima_type', 'App\Models\Pembeli')
->where('sudah_dibaca', false)
->count();
@endphp
@if ($notifPesanPembeli > 0)
<span class="badge bg-danger rounded-pill"
style="font-size: 0.7rem;">{{ $notifPesanPembeli }}</span>
@endif
@endif
</a>
<a href="{{ route('pesanan.saya') }}" class="dropdown-item py-2"><i
class="bi bi-bag-check me-2"></i> Riwayat Pesanan</a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<form action="{{ route('logout') }}" method="POST"> <form action="{{ route('logout') }}" method="POST">
@csrf @csrf
<button type="submit" class="dropdown-item py-2 text-danger"><i class="bi bi-box-arrow-right me-2"></i> Logout</button> <button type="submit" class="dropdown-item py-2 text-danger"><i
class="bi bi-box-arrow-right me-2"></i> Logout</button>
</form> </form>
</div> </div>
</div> </div>
@ -237,10 +287,12 @@
<div style="margin-top: 170px;"> <div style="margin-top: 170px;">
@if (session('success')) @if (session('success'))
<div class="container mt-4"> <div class="container mt-4">
<div class="alert alert-success border-0 shadow-sm rounded-3 d-flex align-items-center" role="alert"> <div class="alert alert-success border-0 shadow-sm rounded-3 d-flex align-items-center"
role="alert">
<i class="bi bi-check-circle-fill fs-4 me-3"></i> <i class="bi bi-check-circle-fill fs-4 me-3"></i>
<div>{{ session('success') }}</div> <div>{{ session('success') }}</div>
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button> <button type="button" class="btn-close ms-auto" data-bs-dismiss="alert"
aria-label="Close"></button>
</div> </div>
</div> </div>
@endif @endif
@ -253,27 +305,37 @@
<div class="row g-5"> <div class="row g-5">
<div class="col-lg-3 col-md-6"> <div class="col-lg-3 col-md-6">
<h3 class="text-white mb-4">GriyaPadi.id</h3> <h3 class="text-white mb-4">GriyaPadi.id</h3>
<p class="mb-4 small">Menghubungkan petani lokal langsung dengan pembeli untuk harga yang adil dan produk berkualitas tinggi.</p> <p class="mb-4 small">Menghubungkan petani lokal langsung dengan pembeli untuk harga yang adil dan
produk berkualitas tinggi.</p>
<div class="d-flex pt-2"> <div class="d-flex pt-2">
<a class="btn btn-outline-light btn-sm rounded-circle me-2" href=""><i class="fab fa-twitter"></i></a> <a class="btn btn-outline-light btn-sm rounded-circle me-2" href=""><i
<a class="btn btn-outline-light btn-sm rounded-circle me-2" href=""><i class="fab fa-facebook-f"></i></a> class="fab fa-twitter"></i></a>
<a class="btn btn-outline-light btn-sm rounded-circle" href=""><i class="fab fa-youtube"></i></a> <a class="btn btn-outline-light btn-sm rounded-circle me-2" href=""><i
class="fab fa-facebook-f"></i></a>
<a class="btn btn-outline-light btn-sm rounded-circle" href=""><i
class="fab fa-youtube"></i></a>
</div> </div>
</div> </div>
<div class="col-lg-3 col-md-6"> <div class="col-lg-3 col-md-6">
<h5 class="text-white mb-4">Tautan Cepat</h5> <h5 class="text-white mb-4">Tautan Cepat</h5>
<div class="d-flex flex-column justify-content-start"> <div class="d-flex flex-column justify-content-start">
<a class="text-white-50 mb-2 text-decoration-none" href="{{ route('shop') }}"><i class="bi bi-chevron-right me-2 small"></i>Belanja</a> <a class="text-white-50 mb-2 text-decoration-none" href="{{ route('shop') }}"><i
<a class="text-white-50 mb-2 text-decoration-none" href="#"><i class="bi bi-chevron-right me-2 small"></i>Tentang Kami</a> class="bi bi-chevron-right me-2 small"></i>Belanja</a>
<a class="text-white-50 text-decoration-none" href="#"><i class="bi bi-chevron-right me-2 small"></i>Hubungi Kami</a> <a class="text-white-50 mb-2 text-decoration-none" href="#"><i
class="bi bi-chevron-right me-2 small"></i>Tentang Kami</a>
<a class="text-white-50 text-decoration-none" href="#"><i
class="bi bi-chevron-right me-2 small"></i>Hubungi Kami</a>
</div> </div>
</div> </div>
<div class="col-lg-3 col-md-6"> <div class="col-lg-3 col-md-6">
<h5 class="text-white mb-4">Akun Saya</h5> <h5 class="text-white mb-4">Akun Saya</h5>
<div class="d-flex flex-column justify-content-start"> <div class="d-flex flex-column justify-content-start">
<a class="text-white-50 mb-2 text-decoration-none" href="{{ route('pembeli.profile') }}"><i class="bi bi-chevron-right me-2 small"></i>Profil</a> <a class="text-white-50 mb-2 text-decoration-none" href="{{ route('pembeli.profile') }}"><i
<a class="text-white-50 mb-2 text-decoration-none" href="{{ route('cart') }}"><i class="bi bi-chevron-right me-2 small"></i>Keranjang</a> class="bi bi-chevron-right me-2 small"></i>Profil</a>
<a class="text-white-50 text-decoration-none" href="{{ route('pesanan.saya') }}"><i class="bi bi-chevron-right me-2 small"></i>Riwayat Pesanan</a> <a class="text-white-50 mb-2 text-decoration-none" href="{{ route('cart') }}"><i
class="bi bi-chevron-right me-2 small"></i>Keranjang</a>
<a class="text-white-50 text-decoration-none" href="{{ route('pesanan.saya') }}"><i
class="bi bi-chevron-right me-2 small"></i>Riwayat Pesanan</a>
</div> </div>
</div> </div>
<div class="col-lg-3 col-md-6"> <div class="col-lg-3 col-md-6">
@ -286,12 +348,15 @@
</div> </div>
<div class="container-fluid copyright bg-dark py-4 border-top border-secondary"> <div class="container-fluid copyright bg-dark py-4 border-top border-secondary">
<div class="container text-center"> <div class="container text-center">
<span class="text-white-50 small">&copy; <a href="#" class="text-white">GriyaPadi.id</a>, All Right Reserved.</span> <span class="text-white-50 small">&copy; <a href="#" class="text-white">GriyaPadi.id</a>, All
Right
Reserved.</span>
</div> </div>
</div> </div>
</div> </div>
<a href="#" class="btn btn-primary btn-lg-square rounded-circle back-to-top shadow"><i class="bi bi-arrow-up"></i></a> <a href="#" class="btn btn-primary btn-lg-square rounded-circle back-to-top shadow"><i
class="bi bi-arrow-up"></i></a>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"></script>

View File

@ -27,7 +27,8 @@
<select name="kategori_id" class="form-select" required> <select name="kategori_id" class="form-select" required>
<option value="" disabled selected>Pilih Kategori</option> <option value="" disabled selected>Pilih Kategori</option>
@foreach($kategoris as $kat) @foreach($kategoris as $kat)
<option value="{{ $kat->id }}" {{ old('kategori_id') == $kat->id ? 'selected' : '' }}> <option value="{{ $kat->id }}" {{ old('kategori_id')==$kat->id ? 'selected' : ''
}}>
{{ $kat->nama_kategori }} {{ $kat->nama_kategori }}
</option> </option>
@endforeach @endforeach
@ -51,6 +52,36 @@
<textarea name="deskripsi" class="form-control" rows="4" <textarea name="deskripsi" class="form-control" rows="4"
placeholder="Jelaskan kualitas produk Anda..." required></textarea> placeholder="Jelaskan kualitas produk Anda..." required></textarea>
</div> </div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Provinsi</label>
<select name="provinsi_code" id="provinsi" class="form-select" required>
<option value="">Pilih Provinsi</option>
@foreach ($provinsis as $prov)
<option value="{{ $prov->code }}">{{ $prov->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Kota / Kabupaten</label>
<select name="kota_code" id="kota" class="form-select" required disabled>
<option value="">Pilih Kota/Kabupaten</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Kecamatan</label>
<select name="kecamatan_code" id="kecamatan" class="form-select" required disabled>
<option value="">Pilih Kecamatan</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Desa / Kelurahan</label>
<select name="desa_code" id="desa" class="form-select" required disabled>
<option value="">Pilih Desa</option>
</select>
</div>
</div>
</div> </div>
{{-- UPLOAD GAMBAR --}} {{-- UPLOAD GAMBAR --}}
@ -125,7 +156,84 @@ class="form-control form-control-sm" accept="image/*" multiple>
</div> </div>
</div> </div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script> <script>
$(document).ready(function() {
// When Provinsi changes, fetch Kota
$('#provinsi').on('change', function() {
let code = $(this).val();
$('#kota').html('<option value="">Memuat...</option>').prop('disabled', true);
$('#kecamatan').html('<option value="">== Pilih Kecamatan ==</option>').prop('disabled', true);
$('#desa').html('<option value="">== Pilih Desa ==</option>').prop('disabled', true);
if (code) {
$.ajax({
url: "{{ route('get.kota') }}"
, type: "POST"
, data: {
code: code
, _token: '{{ csrf_token() }}'
}
, success: function(data) {
$('#kota').html('<option value="">== Pilih Kota/Kabupaten ==</option>');
$.each(data, function(key, value) {
$('#kota').append('<option value="' + value.code + '">' + value.name + '</option>');
});
$('#kota').prop('disabled', false);
}
});
}
});
// When Kota changes, fetch Kecamatan
$('#kota').on('change', function() {
let code = $(this).val();
$('#kecamatan').html('<option value="">Memuat...</option>').prop('disabled', true);
$('#desa').html('<option value="">== Pilih Desa ==</option>').prop('disabled', true);
if (code) {
$.ajax({
url: "{{ route('get.kecamatan') }}"
, type: "POST"
, data: {
code: code
, _token: '{{ csrf_token() }}'
}
, success: function(data) {
$('#kecamatan').html('<option value="">== Pilih Kecamatan ==</option>');
$.each(data, function(key, value) {
$('#kecamatan').append('<option value="' + value.code + '">' + value.name + '</option>');
});
$('#kecamatan').prop('disabled', false);
}
});
}
});
// When Kecamatan changes, fetch Desa
$('#kecamatan').on('change', function() {
let code = $(this).val();
$('#desa').html('<option value="">Memuat...</option>').prop('disabled', true);
if (code) {
$.ajax({
url: "{{ route('get.desa') }}"
, type: "POST"
, data: {
code: code
, _token: '{{ csrf_token() }}'
}
, success: function(data) {
$('#desa').html('<option value="">== Pilih Desa ==</option>');
$.each(data, function(key, value) {
$('#desa').append('<option value="' + value.code + '">' + value.name + '</option>');
});
$('#desa').prop('disabled', false);
}
});
}
});
});
// ---------------------------------------------------- // ----------------------------------------------------
// PREVIEW FOTO UTAMA // PREVIEW FOTO UTAMA
// ---------------------------------------------------- // ----------------------------------------------------
@ -231,5 +339,8 @@ function updateBadge() {
countBadge.className = 'badge bg-primary rounded-pill'; countBadge.className = 'badge bg-primary rounded-pill';
} }
} }
</script> </script>
@endsection @endsection

View File

@ -9,8 +9,7 @@
<div class="card border-0 shadow-sm"> <div class="card border-0 shadow-sm">
<div class="card-body"> <div class="card-body">
{{-- Form Utama --}} {{-- Form Utama --}}
<form action="{{ route('petani.produk.update', $produk->id) }}" method="POST" <form action="{{ route('petani.produk.update', $produk->id) }}" method="POST" enctype="multipart/form-data">
enctype="multipart/form-data">
@csrf @csrf
@method('PUT') @method('PUT')
@ -20,8 +19,7 @@
<h5 class="mb-4">Informasi Produk</h5> <h5 class="mb-4">Informasi Produk</h5>
<div class="form-group mb-3"> <div class="form-group mb-3">
<label class="fw-bold">Nama Produk</label> <label class="fw-bold">Nama Produk</label>
<input type="text" name="nama_produk" class="form-control" <input type="text" name="nama_produk" class="form-control" value="{{ $produk->nama_produk }}" required>
value="{{ $produk->nama_produk }}" required>
</div> </div>
<div class="row"> <div class="row">
@ -30,7 +28,8 @@
<select name="kategori_id" class="form-select" required> <select name="kategori_id" class="form-select" required>
<option value="" disabled>Pilih Kategori</option> <option value="" disabled>Pilih Kategori</option>
@foreach($kategoris as $kat) @foreach($kategoris as $kat)
<option value="{{ $kat->id }}" {{ (old('kategori_id', $produk->kategori_id) == $kat->id) ? 'selected' : '' }}> <option value="{{ $kat->id }}" {{ (old('kategori_id', $produk->kategori_id) ==
$kat->id) ? 'selected' : '' }}>
{{ $kat->nama_kategori }} {{ $kat->nama_kategori }}
</option> </option>
@endforeach @endforeach
@ -39,21 +38,61 @@
</div> </div>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="fw-bold">Stok</label> <label class="fw-bold">Stok</label>
<input type="number" name="stok" class="form-control" value="{{ $produk->stok }}" <input type="number" name="stok" class="form-control" value="{{ $produk->stok }}" required>
required>
</div> </div>
</div> </div>
<div class="form-group mb-3"> <div class="form-group mb-3">
<label class="fw-bold">Harga (Rp)</label> <label class="fw-bold">Harga (Rp)</label>
<input type="number" name="harga" class="form-control" value="{{ $produk->harga }}" <input type="number" name="harga" class="form-control" value="{{ $produk->harga }}" required>
required>
</div> </div>
<div class="form-group mb-3"> <div class="form-group mb-3">
<label class="fw-bold">Deskripsi</label> <label class="fw-bold">Deskripsi</label>
<textarea name="deskripsi" class="form-control" rows="4" <textarea name="deskripsi" class="form-control" rows="4" required>{{ $produk->deskripsi }}</textarea>
required>{{ $produk->deskripsi }}</textarea> </div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Provinsi</label>
<select name="provinsi_code" id="provinsi" class="form-select" required>
<option value="">Pilih Provinsi</option>
@foreach ($provinsis as $prov)
<option value="{{ $prov->code }}" {{ $produk->provinsi_code == $prov->code ?
'selected' : '' }}>{{ $prov->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Kota / Kabupaten</label>
<select name="kota_code" id="kota" class="form-select" required>
<option value="">Pilih Kota/Kabupaten</option>
@foreach ($kotas as $kota)
<option value="{{ $kota->code }}" {{ $produk->kota_code == $kota->code ?
'selected' : '' }}>{{ $kota->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Kecamatan</label>
<select name="kecamatan_code" id="kecamatan" class="form-select" required>
<option value="">Pilih Kecamatan</option>
@foreach ($kecamatans as $kec)
<option value="{{ $kec->code }}" {{ $produk->kecamatan_code == $kec->code ?
'selected' : '' }}>{{ $kec->name }}</option>
@endforeach
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Desa / Kelurahan</label>
<select name="desa_code" id="desa" class="form-select" required>
<option value="">Pilih Desa</option>
@foreach ($desas as $desa)
<option value="{{ $desa->code }}" {{ $produk->desa_code == $desa->code ?
'selected' : '' }}>{{ $desa->name }}</option>
@endforeach
</select>
</div>
</div> </div>
</div> </div>
@ -67,23 +106,18 @@
<label class="fw-bold mb-2">Foto Utama</label> <label class="fw-bold mb-2">Foto Utama</label>
{{-- AREA PREVIEW --}} {{-- AREA PREVIEW --}}
<div class="mb-2 bg-white border rounded d-flex align-items-center justify-content-center position-relative overflow-hidden" <div class="mb-2 bg-white border rounded d-flex align-items-center justify-content-center position-relative overflow-hidden" style="height: 150px;">
style="height: 150px;">
<div id="placeholder-utama" <div id="placeholder-utama" class="text-center text-muted {{ $produk->foto_produk ? 'd-none' : '' }}">
class="text-center text-muted {{ $produk->foto_produk ? 'd-none' : '' }}">
<i class="bi bi-cloud-arrow-up fs-1"></i> <i class="bi bi-cloud-arrow-up fs-1"></i>
<div class="small mt-1">Upload foto baru</div> <div class="small mt-1">Upload foto baru</div>
</div> </div>
<img id="preview-utama" <img id="preview-utama" src="{{ $produk->foto_produk ? asset('storage/' . $produk->foto_produk) : '#' }}" class="img-fluid w-100 h-100 object-fit-contain {{ $produk->foto_produk ? '' : 'd-none' }}">
src="{{ $produk->foto_produk ? asset('storage/' . $produk->foto_produk) : '#' }}"
class="img-fluid w-100 h-100 object-fit-contain {{ $produk->foto_produk ? '' : 'd-none' }}">
</div> </div>
{{-- Input File --}} {{-- Input File --}}
<input type="file" name="foto_produk" class="form-control form-control-sm" <input type="file" name="foto_produk" class="form-control form-control-sm" accept="image/*" onchange="previewMainImage(this)">
accept="image/*" onchange="previewMainImage(this)">
<small class="text-muted">Biarkan kosong jika tidak ingin mengganti foto.</small> <small class="text-muted">Biarkan kosong jika tidak ingin mengganti foto.</small>
</div> </div>
</div> </div>
@ -101,17 +135,11 @@ class="img-fluid w-100 h-100 object-fit-contain {{ $produk->foto_produk ? '' : '
{{-- Gambar Lama dari Database --}} {{-- Gambar Lama dari Database --}}
@foreach($produk->images as $img) @foreach($produk->images as $img)
<div class="col-4 position-relative existing-image-wrapper" <div class="col-4 position-relative existing-image-wrapper" id="existing-img-{{ $img->id }}">
id="existing-img-{{ $img->id }}"> <img src="{{ asset('storage/' . $img->foto) }}" class="rounded w-100 border bg-white" style="height: 70px; object-fit: cover;">
<img src="{{ asset('storage/' . $img->foto) }}"
class="rounded w-100 border bg-white"
style="height: 70px; object-fit: cover;">
{{-- Tombol Hapus Gambar Lama --}} {{-- Tombol Hapus Gambar Lama --}}
<a href="javascript:void(0)" <a href="javascript:void(0)" class="position-absolute top-0 end-0 badge bg-danger text-white rounded-circle text-decoration-none m-1 shadow-sm" onclick="if(confirm('Hapus foto ini?')) deleteExistingImage({{ $img->id }});" style="cursor: pointer;">&times;</a>
class="position-absolute top-0 end-0 badge bg-danger text-white rounded-circle text-decoration-none m-1 shadow-sm"
onclick="if(confirm('Hapus foto ini?')) deleteExistingImage({{ $img->id }});"
style="cursor: pointer;">&times;</a>
</div> </div>
@endforeach @endforeach
@ -119,13 +147,11 @@ class="position-absolute top-0 end-0 badge bg-danger text-white rounded-circle t
{{-- INPUT FILE --}} {{-- INPUT FILE --}}
<div class="mb-3"> <div class="mb-3">
<input type="file" id="input-gallery-visual" <input type="file" id="input-gallery-visual" class="form-control form-control-sm" accept="image/*" multiple>
class="form-control form-control-sm" accept="image/*" multiple>
</div> </div>
{{-- INPUT FILE HIDDEN --}} {{-- INPUT FILE HIDDEN --}}
<input type="file" name="foto_tambahan[]" id="real-input-gallery" class="d-none" <input type="file" name="foto_tambahan[]" id="real-input-gallery" class="d-none" multiple>
multiple>
<small class="text-muted d-block" style="font-size: 0.8rem;"> <small class="text-muted d-block" style="font-size: 0.8rem;">
<i class="bi bi-info-circle me-1"></i>Maksimal 3 foto tambahan total. <i class="bi bi-info-circle me-1"></i>Maksimal 3 foto tambahan total.
@ -143,8 +169,7 @@ class="form-control form-control-sm" accept="image/*" multiple>
{{-- FORM TERSEMBUNYI UNTUK HAPUS GAMBAR LAMA --}} {{-- FORM TERSEMBUNYI UNTUK HAPUS GAMBAR LAMA --}}
@foreach($produk->images as $img) @foreach($produk->images as $img)
<form id="form-del-img-{{ $img->id }}" action="{{ route('petani.produk.image.delete', $img->id) }}" <form id="form-del-img-{{ $img->id }}" action="{{ route('petani.produk.image.delete', $img->id) }}" method="POST" style="display: none;">
method="POST" style="display: none;">
@csrf @method('DELETE') @csrf @method('DELETE')
</form> </form>
@endforeach @endforeach
@ -153,7 +178,81 @@ class="form-control form-control-sm" accept="image/*" multiple>
</div> </div>
</div> </div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script> <script>
$(document).ready(function() {
$('#provinsi').on('change', function() {
let code = $(this).val();
$('#kota').html('<option value="">Memuat...</option>').prop('disabled', true);
$('#kecamatan').html('<option value="">== Pilih Kecamatan ==</option>').prop('disabled', true);
$('#desa').html('<option value="">== Pilih Desa ==</option>').prop('disabled', true);
if (code) {
$.ajax({
url: "{{ route('get.kota') }}"
, type: "POST"
, data: {
code: code
, _token: '{{ csrf_token() }}'
}
, success: function(data) {
$('#kota').html('<option value="">== Pilih Kota/Kabupaten ==</option>');
$.each(data, function(key, value) {
$('#kota').append('<option value="' + value.code + '">' + value.name + '</option>');
});
$('#kota').prop('disabled', false);
}
});
}
});
$('#kota').on('change', function() {
let code = $(this).val();
$('#kecamatan').html('<option value="">Memuat...</option>').prop('disabled', true);
$('#desa').html('<option value="">== Pilih Desa ==</option>').prop('disabled', true);
if (code) {
$.ajax({
url: "{{ route('get.kecamatan') }}"
, type: "POST"
, data: {
code: code
, _token: '{{ csrf_token() }}'
}
, success: function(data) {
$('#kecamatan').html('<option value="">== Pilih Kecamatan ==</option>');
$.each(data, function(key, value) {
$('#kecamatan').append('<option value="' + value.code + '">' + value.name + '</option>');
});
$('#kecamatan').prop('disabled', false);
}
});
}
});
$('#kecamatan').on('change', function() {
let code = $(this).val();
$('#desa').html('<option value="">Memuat...</option>').prop('disabled', true);
if (code) {
$.ajax({
url: "{{ route('get.desa') }}"
, type: "POST"
, data: {
code: code
, _token: '{{ csrf_token() }}'
}
, success: function(data) {
$('#desa').html('<option value="">== Pilih Desa ==</option>');
$.each(data, function(key, value) {
$('#desa').append('<option value="' + value.code + '">' + value.name + '</option>');
});
$('#desa').prop('disabled', false);
}
});
}
});
});
// ---------------------------------------------------- // ----------------------------------------------------
// PREVIEW FOTO UTAMA // PREVIEW FOTO UTAMA
// ---------------------------------------------------- // ----------------------------------------------------
@ -262,5 +361,6 @@ function updateBadge() {
inputVisual.removeAttribute('title'); inputVisual.removeAttribute('title');
} }
} }
</script> </script>
@endsection @endsection

View File

@ -123,6 +123,11 @@
Route::put('/petani/profile', [ProfileController::class, 'updatePetani'])->name('petani.profile.update'); Route::put('/petani/profile', [ProfileController::class, 'updatePetani'])->name('petani.profile.update');
}); });
// Route untuk Dropdown Wilayah Laravolt
Route::post('/get-kota', [\App\Http\Controllers\WilayahController::class, 'getKota'])->name('get.kota');
Route::post('/get-kecamatan', [\App\Http\Controllers\WilayahController::class, 'getKecamatan'])->name('get.kecamatan');
Route::post('/get-desa', [\App\Http\Controllers\WilayahController::class, 'getDesa'])->name('get.desa');
// --- CEK NIK GAPOKTAN AJAX --- // --- CEK NIK GAPOKTAN AJAX ---
Route::post('/cek-nik-gapoktan', function (Request $request) { Route::post('/cek-nik-gapoktan', function (Request $request) {