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.yaml
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
```
### 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

View File

@ -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'));
}

View File

@ -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) {
$produkTerlaris = Produk::withSum([
'detailTransaksis as total_terjual' => function ($query) {
$query->whereHas('transaksi', function ($q) {
$q->where('status', '!=', 'batal');
});
}], 'jumlah')
}
], '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 . '%');
}

View File

@ -6,9 +6,13 @@
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
{
@ -25,8 +29,9 @@ public function index()
public function create()
{
$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)
@ -37,6 +42,10 @@ public function store(Request $request)
'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'
]);
@ -53,6 +62,10 @@ public function store(Request $request)
'kategori_id' => $request->kategori_id,
'nama_produk' => $request->nama_produk,
'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,
@ -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)
@ -92,6 +110,10 @@ public function update(Request $request, $id)
'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',
@ -114,6 +136,10 @@ public function update(Request $request, $id)
'nama_produk' => $request->nama_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
@ -122,7 +148,8 @@ public function update(Request $request, $id)
// 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([
@ -143,7 +170,7 @@ public function destroy($id)
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);
}

View File

@ -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']
]);
// 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.');
}
$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'));
}
return redirect()->route('shop')->with('error', 'Keranjang Anda kosong, silakan belanja dulu.');
}
// fungsi prosesCheckout
/**
* Proses Pembuatan Transaksi
* Mengelompokkan pesanan berdasarkan Petani dan membersihkan keranjang
*/
public function prosesCheckout(Request $request)
{
$request->validate([
@ -72,7 +95,7 @@ public function prosesCheckout(Request $request)
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);
// 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([
'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);
})

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',
'stok',
'deskripsi',
'foto_produk'
'foto_produk',
'provinsi_code',
'kota_code',
'kecamatan_code',
'desa_code',
];
public function petani()

View File

@ -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",

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",
"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",

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',
'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',

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') }}"
class="btn btn-outline-secondary py-2 rounded-pill">Kembali</a>
</div>
<input type="hidden" name="cart_ids" value="{{ request('cart_ids') }}">
</form>
</div>
</div>

View File

@ -4,7 +4,7 @@
@section('content')
<div class="container-fluid mb-5 bg-white">
<div class="container-fluid mb-5 bg-white">
<div class="container">
<div class="row align-items-center g-5 py-5">
<div class="col-lg-6 order-2 order-lg-1">
@ -19,14 +19,9 @@
Platform langsung yang menghubungkan petani padi lokal dengan pembeli.
Dapatkan harga gabah terbaik dan gabah berkualitas.
</p>
<div class="d-flex gap-2">
<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
</a>
<a href="#katalog" class="btn btn-outline-secondary rounded-pill py-3 px-5">
Lihat Stok
</a>
</div>
</div>
<div class="col-lg-6 order-1 order-lg-2 text-center">
<div class="position-relative d-inline-block">
@ -49,9 +44,9 @@
</div>
</div>
</div>
</div>
</div>
<div class="container py-5">
<div class="container py-5">
<div class="row g-4">
<div class="col-md-6 col-lg-3">
<div class="h-100 p-4 text-center bg-white border rounded-4 shadow-sm">
@ -94,10 +89,10 @@
</div>
</div>
</div>
</div>
</div>
{{-- Produk terlaris --}}
<div class="container py-5">
{{-- Produk terlaris --}}
<div class="container py-5">
<div class="text-center mx-auto mb-5" style="max-width: 700px;">
<h1 class="display-6 fw-bold">Produk Terlaris</h1>
<p class="text-muted">Pilihan favorit pelanggan kami minggu ini.</p>
@ -145,9 +140,9 @@ class="btn btn-outline-primary rounded-pill px-3">
@endforelse
</div>
</div>
</div>
<div class="container-fluid py-5 bg-light">
<div class="container-fluid py-5 bg-light">
<div class="container">
<div class="row g-5">
<div class="col-md-6 col-lg-3 text-center">
@ -172,10 +167,10 @@ class="btn btn-outline-primary rounded-pill px-3">
</div>
</div>
</div>
</div>
</div>
{{-- Katalog --}}
<div class="container py-5" id="katalog">
{{-- Katalog --}}
<div class="container py-5" id="katalog">
<div class="text-center mx-auto mb-5" style="max-width: 700px;">
<h2 class="fw-bold display-6 text-dark">Stok Gudang Kami</h2>
<p class="text-muted">Pilih varietas gabah yang Anda butuhkan hari ini.</p>
@ -213,10 +208,10 @@ class="btn {{ request('kategori') == $kat->slug ? 'btn-primary' : 'btn-outline-p
Lihat Semua Produk
</a>
</div>
</div>
</div>
{{-- Banner --}}
<div class="container-fluid bg-primary py-5 mt-5">
{{-- Banner --}}
<div class="container-fluid bg-primary py-5 mt-5">
<div class="container py-5">
<div class="row align-items-center">
<div class="col-lg-8 text-center text-lg-start mb-4 mb-lg-0">
@ -235,11 +230,11 @@ class="btn btn-light rounded-pill px-5 py-3 text-success fw-bold shadow">
</div>
</div>
</div>
</div>
</div>
@endsection
@section('js')
<script>
<script>
$(document).ready(function () {
$('.btn-category').click(function (e) {
e.preventDefault();
@ -275,5 +270,5 @@ class="btn btn-light rounded-pill px-5 py-3 text-success fw-bold shadow">
});
});
});
</script>
</script>
@endsection

View File

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

View File

@ -107,6 +107,7 @@
@section('content')
<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">
{{-- SIDEBAR KIRI --}}

View File

@ -3,8 +3,8 @@
@section('title', 'Belanja Padi & Beras')
@section('content')
{{-- Header Halaman --}}
<div class="container-fluid py-5 bg-light border-bottom mb-5">
{{-- Header Halaman --}}
<div class="container-fluid py-5 bg-light border-bottom mb-5">
<div class="container text-center">
<h1 class="display-5 fw-bold text-dark mb-3">Belanja Produk Kami</h1>
<nav aria-label="breadcrumb">
@ -14,15 +14,66 @@
</ol>
</nav>
</div>
</div>
</div>
{{-- Konten Utama --}}
<div class="container pb-5">
{{-- Konten Utama --}}
<div class="container pb-5">
<div class="row g-4">
{{-- SIDEBAR FILTER --}}
<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 --}}
<div class="card border-0 shadow-sm mb-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) --}}
<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">
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
</div>
@ -88,13 +140,13 @@ class="d-flex justify-content-between align-items-center text-decoration-none {{
<select name="sort" class="form-select form-select-sm border-0 bg-light fw-bold text-dark"
onchange="document.getElementById('sortForm').submit()"
style="width: 160px; cursor: pointer;">
<option value="terbaru" {{ request('sort') == 'terbaru' ? 'selected' : '' }}>Paling Baru
<option value="terbaru" {{ request('sort')=='terbaru' ? 'selected' : '' }}>Paling Baru
</option>
<option value="termurah" {{ request('sort') == 'termurah' ? 'selected' : '' }}>Harga Terendah
<option value="termurah" {{ request('sort')=='termurah' ? 'selected' : '' }}>Harga Terendah
</option>
<option value="termahal" {{ request('sort') == 'termahal' ? 'selected' : '' }}>Harga Tertinggi
<option value="termahal" {{ request('sort')=='termahal' ? 'selected' : '' }}>Harga Tertinggi
</option>
<option value="terlaris" {{ request('sort') == 'terlaris' ? 'selected' : '' }}>Paling Laris
<option value="terlaris" {{ request('sort')=='terlaris' ? 'selected' : '' }}>Paling Laris
</option>
</select>
</form>
@ -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>
<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>
<p class="text-muted small mb-3">{{ Str::limit($produk->deskripsi, 60) }}</p>
</div>
@ -163,9 +216,38 @@ class="card-img-top w-100 h-100" alt="{{ $produk->nama_produk }}"
@endif
</div>
</div>
</div>
</div>
<style>
<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>
/* Hover Effect */
.product-card {
transition: all 0.3s ease;
@ -184,5 +266,5 @@ class="card-img-top w-100 h-100" alt="{{ $produk->nama_produk }}"
.overflow-hidden img {
transition: transform 0.5s ease;
}
</style>
</style>
@endsection

View File

@ -231,12 +231,7 @@
</a>
</li>
{{-- VERIFIKASI PETANI --}}
<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>
{{-- MENU VERIFIKASI PETANI DIHAPUS SESUAI REVISI --}}
{{-- GAPOKTAN --}}
<li class="sidebar-item {{ request()->is('admin/gapoktan*') ? 'active' : '' }}">
@ -256,6 +251,23 @@
{{-- Menu Dashboard Petani --}}
@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' : '' }}">
<a href="{{ route('petani.dashboard') }}" class='sidebar-link'>
<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>
</a>
</li>
{{-- Menu Pesanan Masuk (Dengan Badge) --}}
<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>
</div>
@if($notifPesanan > 0)
<span class="badge bg-danger rounded-pill">{{ $notifPesanan }}</span>
@endif
</a>
</li>
<li
class="sidebar-item {{ request()->is('petani/pesan') || request()->is('petani/pesan/*') ? 'active' : '' }}">
<a href="{{ route('petani.pesan.index') }}" class='sidebar-link'>
{{-- Menu Kotak Masuk (Dengan Badge) --}}
<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>
</div>
@if($notifPesan > 0)
<span class="badge bg-danger rounded-pill">{{ $notifPesan }}</span>
@endif
</a>
</li>
@endif

View File

@ -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 @@
<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>
@ -153,7 +167,8 @@
<div class="container">
<div class="d-flex justify-content-start text-white" style="font-size: 0.85rem;">
<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>
</div>
</div>
@ -164,22 +179,27 @@
<div class="container">
<nav class="navbar navbar-light navbar-expand-xl py-3">
<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>
<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>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<div class="navbar-nav mx-auto">
<a href="{{ url('/') }}" 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>
<a href="{{ url('/') }}"
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 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">
<input type="search" name="search" class="form-control border-end-0"
placeholder="Cari produk..." value="{{ request('search') }}"
@ -193,10 +213,12 @@
<a href="{{ route('cart') }}" class="position-relative me-3 text-dark">
<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 --}}
@if(Auth::guard('pembeli')->check())
@if (Auth::guard('pembeli')->check())
{{ \App\Models\Cart::where('pembeli_id', Auth::guard('pembeli')->id())->count() }}
@else
0
@ -207,20 +229,48 @@
@if (Auth::guard('pembeli')->check())
<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">
<div class="bg-light rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 35px; height: 35px;">
<a href="#"
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>
</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>
<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.pesan.index') }}" class="dropdown-item py-2"><i class="bi bi-chat-dots me-2"></i> Pesan</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.profile') }}" class="dropdown-item py-2"><i
class="bi bi-person me-2"></i> Profil</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>
<form action="{{ route('logout') }}" method="POST">
@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>
</div>
</div>
@ -237,10 +287,12 @@
<div style="margin-top: 170px;">
@if (session('success'))
<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>
<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>
@endif
@ -253,27 +305,37 @@
<div class="row g-5">
<div class="col-lg-3 col-md-6">
<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">
<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 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>
<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
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 class="col-lg-3 col-md-6">
<h5 class="text-white mb-4">Tautan Cepat</h5>
<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="#"><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>
<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="#"><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 class="col-lg-3 col-md-6">
<h5 class="text-white mb-4">Akun Saya</h5>
<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('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>
<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('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 class="col-lg-3 col-md-6">
@ -286,12 +348,15 @@
</div>
<div class="container-fluid copyright bg-dark py-4 border-top border-secondary">
<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>
<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://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js"></script>

View File

@ -4,7 +4,7 @@
@section('page-title', 'Tambah Produk Baru')
@section('content')
<div class="row">
<div class="row">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-body">
@ -27,7 +27,8 @@
<select name="kategori_id" class="form-select" required>
<option value="" disabled selected>Pilih Kategori</option>
@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 }}
</option>
@endforeach
@ -51,6 +52,36 @@
<textarea name="deskripsi" class="form-control" rows="4"
placeholder="Jelaskan kualitas produk Anda..." required></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 }}">{{ $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>
{{-- UPLOAD GAMBAR --}}
@ -123,9 +154,86 @@ class="form-control form-control-sm" accept="image/*" multiple>
</div>
</div>
</div>
</div>
</div>
<script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></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
// ----------------------------------------------------
@ -136,7 +244,7 @@ function previewMainImage(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
reader.onload = function(e) {
preview.src = e.target.result;
// Tampilkan Gambar, Sembunyikan Ikon
@ -163,7 +271,7 @@ function previewMainImage(input) {
// "Keranjang" virtual untuk menampung file
let dt = new DataTransfer();
inputVisual.addEventListener('change', function () {
inputVisual.addEventListener('change', function() {
const newFiles = this.files;
// Cek Limit Total
@ -181,7 +289,7 @@ function previewMainImage(input) {
// Buat HTML Preview
const reader = new FileReader();
reader.onload = function (e) {
reader.onload = function(e) {
const col = document.createElement('div');
col.className = 'col-4 position-relative new-image-preview';
@ -192,7 +300,7 @@ function previewMainImage(input) {
`;
// Event Listener Tombol Hapus (X)
col.querySelector('.remove-btn').addEventListener('click', function () {
col.querySelector('.remove-btn').addEventListener('click', function() {
// Hapus file dari keranjang berdasarkan nama & size
for (let j = 0; j < dt.items.length; j++) {
@ -231,5 +339,8 @@ function updateBadge() {
countBadge.className = 'badge bg-primary rounded-pill';
}
}
</script>
</script>
@endsection

View File

@ -4,13 +4,12 @@
@section('page-title', 'Edit Produk')
@section('content')
<div class="row">
<div class="row">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-body">
{{-- Form Utama --}}
<form action="{{ route('petani.produk.update', $produk->id) }}" method="POST"
enctype="multipart/form-data">
<form action="{{ route('petani.produk.update', $produk->id) }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
@ -20,8 +19,7 @@
<h5 class="mb-4">Informasi Produk</h5>
<div class="form-group mb-3">
<label class="fw-bold">Nama Produk</label>
<input type="text" name="nama_produk" class="form-control"
value="{{ $produk->nama_produk }}" required>
<input type="text" name="nama_produk" class="form-control" value="{{ $produk->nama_produk }}" required>
</div>
<div class="row">
@ -30,7 +28,8 @@
<select name="kategori_id" class="form-select" required>
<option value="" disabled>Pilih Kategori</option>
@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 }}
</option>
@endforeach
@ -39,21 +38,61 @@
</div>
<div class="col-md-6 mb-3">
<label class="fw-bold">Stok</label>
<input type="number" name="stok" class="form-control" value="{{ $produk->stok }}"
required>
<input type="number" name="stok" class="form-control" value="{{ $produk->stok }}" required>
</div>
</div>
<div class="form-group mb-3">
<label class="fw-bold">Harga (Rp)</label>
<input type="number" name="harga" class="form-control" value="{{ $produk->harga }}"
required>
<input type="number" name="harga" class="form-control" value="{{ $produk->harga }}" required>
</div>
<div class="form-group mb-3">
<label class="fw-bold">Deskripsi</label>
<textarea name="deskripsi" class="form-control" rows="4"
required>{{ $produk->deskripsi }}</textarea>
<textarea name="deskripsi" class="form-control" rows="4" 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>
@ -67,23 +106,18 @@
<label class="fw-bold mb-2">Foto Utama</label>
{{-- AREA PREVIEW --}}
<div class="mb-2 bg-white border rounded d-flex align-items-center justify-content-center position-relative overflow-hidden"
style="height: 150px;">
<div class="mb-2 bg-white border rounded d-flex align-items-center justify-content-center position-relative overflow-hidden" style="height: 150px;">
<div id="placeholder-utama"
class="text-center text-muted {{ $produk->foto_produk ? 'd-none' : '' }}">
<div id="placeholder-utama" class="text-center text-muted {{ $produk->foto_produk ? 'd-none' : '' }}">
<i class="bi bi-cloud-arrow-up fs-1"></i>
<div class="small mt-1">Upload foto baru</div>
</div>
<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' }}">
<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' }}">
</div>
{{-- Input File --}}
<input type="file" name="foto_produk" class="form-control form-control-sm"
accept="image/*" onchange="previewMainImage(this)">
<input type="file" name="foto_produk" class="form-control form-control-sm" accept="image/*" onchange="previewMainImage(this)">
<small class="text-muted">Biarkan kosong jika tidak ingin mengganti foto.</small>
</div>
</div>
@ -101,17 +135,11 @@ class="img-fluid w-100 h-100 object-fit-contain {{ $produk->foto_produk ? '' : '
{{-- Gambar Lama dari Database --}}
@foreach($produk->images as $img)
<div class="col-4 position-relative existing-image-wrapper"
id="existing-img-{{ $img->id }}">
<img src="{{ asset('storage/' . $img->foto) }}"
class="rounded w-100 border bg-white"
style="height: 70px; object-fit: cover;">
<div class="col-4 position-relative existing-image-wrapper" id="existing-img-{{ $img->id }}">
<img src="{{ asset('storage/' . $img->foto) }}" class="rounded w-100 border bg-white" style="height: 70px; object-fit: cover;">
{{-- Tombol Hapus Gambar Lama --}}
<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>
<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>
</div>
@endforeach
@ -119,13 +147,11 @@ class="position-absolute top-0 end-0 badge bg-danger text-white rounded-circle t
{{-- INPUT FILE --}}
<div class="mb-3">
<input type="file" id="input-gallery-visual"
class="form-control form-control-sm" accept="image/*" multiple>
<input type="file" id="input-gallery-visual" class="form-control form-control-sm" accept="image/*" multiple>
</div>
{{-- INPUT FILE HIDDEN --}}
<input type="file" name="foto_tambahan[]" id="real-input-gallery" class="d-none"
multiple>
<input type="file" name="foto_tambahan[]" id="real-input-gallery" class="d-none" multiple>
<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.
@ -143,17 +169,90 @@ class="form-control form-control-sm" accept="image/*" multiple>
{{-- FORM TERSEMBUNYI UNTUK HAPUS GAMBAR LAMA --}}
@foreach($produk->images as $img)
<form id="form-del-img-{{ $img->id }}" action="{{ route('petani.produk.image.delete', $img->id) }}"
method="POST" style="display: none;">
<form id="form-del-img-{{ $img->id }}" action="{{ route('petani.produk.image.delete', $img->id) }}" method="POST" style="display: none;">
@csrf @method('DELETE')
</form>
@endforeach
</div>
</div>
</div>
</div>
</div>
<script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></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
// ----------------------------------------------------
@ -193,7 +292,7 @@ function deleteExistingImage(id) {
let existingCount = document.querySelectorAll('.existing-image-wrapper').length;
updateBadge();
inputVisual.addEventListener('change', function () {
inputVisual.addEventListener('change', function() {
const newFiles = this.files;
if (existingCount + dt.items.length + newFiles.length > 3) {
@ -211,7 +310,7 @@ function deleteExistingImage(id) {
// Buat Preview Element
const reader = new FileReader();
reader.onload = function (e) {
reader.onload = function(e) {
const col = document.createElement('div');
col.className = 'col-4 position-relative new-image-preview';
@ -223,7 +322,7 @@ function deleteExistingImage(id) {
`;
// Event Hapus Preview
col.querySelector('.remove-btn').addEventListener('click', function () {
col.querySelector('.remove-btn').addEventListener('click', function() {
for (let j = 0; j < dt.items.length; j++) {
if (dt.files[j].name === file.name && dt.files[j].size === file.size) {
dt.items.remove(j);
@ -262,5 +361,6 @@ function updateBadge() {
inputVisual.removeAttribute('title');
}
}
</script>
</script>
@endsection

View File

@ -123,6 +123,11 @@
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 ---
Route::post('/cek-nik-gapoktan', function (Request $request) {