Refactor: Alamat feature

This commit is contained in:
sayasilvi 2026-03-07 23:34:32 +07:00
parent 6097b439fc
commit b0798cc569
15 changed files with 473 additions and 450 deletions

View File

@ -38,13 +38,6 @@ public function loginProcess(Request $request)
$petani = Petani::where('username', $request->username)->first();
if ($petani && Hash::check($request->password, $petani->password)) {
// Validasi Status Akun Petani
if ($petani->status_akun == 'menunggu') {
return back()->withErrors(['login_error' => 'Akun Anda masih dalam proses verifikasi Admin.']);
}
if ($petani->status_akun == 'ditolak') {
return back()->withErrors(['login_error' => 'Pendaftaran Anda ditolak. Silakan hubungi Admin.']);
}
Auth::guard('petani')->login($petani);
$request->session()->regenerate();
@ -100,10 +93,10 @@ public function registerProcess(Request $request)
'no_hp' => $request->no_hp,
'alamat' => $request->alamat,
'nama_usaha' => $request->nama_usaha ?? 'Toko Tani ' . $request->nama_lengkap,
'status_akun' => 'menunggu'
'status_akun' => 'aktif'
]);
return redirect('/login')->with('success', 'Registrasi Petani Berhasil! Mohon tunggu verifikasi Admin.');
return redirect('/login')->with('success', 'Registrasi Petani Berhasil! Silakan langsung Login.');
} else {
Pembeli::create([
@ -121,13 +114,9 @@ public function registerProcess(Request $request)
public function logout(Request $request)
{
// Logout semua guard untuk keamanan
if (Auth::guard('admin')->check())
Auth::guard('admin')->logout();
if (Auth::guard('petani')->check())
Auth::guard('petani')->logout();
if (Auth::guard('pembeli')->check())
Auth::guard('pembeli')->logout();
if (Auth::guard('admin')->check()) Auth::guard('admin')->logout();
if (Auth::guard('petani')->check()) Auth::guard('petani')->logout();
if (Auth::guard('pembeli')->check()) Auth::guard('pembeli')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();

View File

@ -42,10 +42,6 @@ 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'
]);
@ -62,10 +58,6 @@ 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,
@ -110,10 +102,6 @@ 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',
@ -136,10 +124,6 @@ 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

View File

@ -6,6 +6,10 @@
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Laravolt\Indonesia\Models\Province;
use Laravolt\Indonesia\Models\City;
use Laravolt\Indonesia\Models\District;
use Laravolt\Indonesia\Models\Village;
class ProfileController extends Controller
{
@ -13,7 +17,13 @@ class ProfileController extends Controller
public function editPetani()
{
$user = Auth::guard('petani')->user();
return view('petani.profile', compact('user'));
$provinsis = Province::all();
$kotas = $user->provinsi_code ? City::where('province_code', $user->provinsi_code)->get() : [];
$kecamatans = $user->kota_code ? District::where('city_code', $user->kota_code)->get() : [];
$desas = $user->kecamatan_code ? Village::where('district_code', $user->kecamatan_code)->get() : [];
return view('petani.profile', compact('user', 'provinsis', 'kotas', 'kecamatans', 'desas'));
}
public function updatePetani(Request $request)
@ -23,12 +33,24 @@ public function updatePetani(Request $request)
$request->validate([
'nama_lengkap' => 'required|string|max:255',
'email' => 'required|email|unique:petanis,email,' . $user->id,
'password' => 'nullable|min:6|confirmed',
'foto' => 'nullable|image|max:2048'
'password' => 'nullable|min:6|confirmed',
'foto' => 'nullable|image|max:2048',
'provinsi_code' => 'required',
'alamat' => 'required|string',
'kota_code' => 'required',
'kecamatan_code' => 'required',
'desa_code' => 'required',
'link_maps' => 'nullable|url',
]);
$user->nama_lengkap = $request->nama_lengkap;
$user->email = $request->email;
$user->provinsi_code = $request->provinsi_code;
$user->alamat = $request->alamat;
$user->kota_code = $request->kota_code;
$user->kecamatan_code = $request->kecamatan_code;
$user->desa_code = $request->desa_code;
$user->link_maps = $request->link_maps;
if ($request->filled('password')) {
$user->password = Hash::make($request->password);
@ -38,12 +60,11 @@ public function updatePetani(Request $request)
if ($user->foto && Storage::exists('public/' . $user->foto)) {
Storage::delete('public/' . $user->foto);
}
$path = $request->file('foto')->store('avatars', 'public');
$user->foto = $path;
$user->foto = $request->file('foto')->store('avatars', 'public');
}
$user->save();
return back()->with('success', 'Profil berhasil diperbarui!');
return back()->with('success', 'Profil dan Lokasi berhasil diperbarui!');
}
// --- FITUR PEMBELI ---

View File

@ -21,6 +21,11 @@ class Petani extends Authenticatable implements CanResetPassword
'password',
'no_hp',
'alamat',
'provinsi_code',
'kota_code',
'link_maps',
'kecamatan_code',
'desa_code',
'nama_usaha',
'status_akun'
];
@ -43,4 +48,14 @@ public function pesanTerkirim()
{
return $this->morphMany(Pesan::class, 'pengirim');
}
public function kota()
{
return $this->belongsTo(\Laravolt\Indonesia\Models\City::class, 'kota_code', 'code');
}
public function provinsi()
{
return $this->belongsTo(\Laravolt\Indonesia\Models\Province::class, 'provinsi_code', 'code');
}
}

View File

@ -18,10 +18,6 @@ class Produk extends Model
'stok',
'deskripsi',
'foto_produk',
'provinsi_code',
'kota_code',
'kecamatan_code',
'desa_code',
];
public function petani()

View File

@ -7,23 +7,19 @@
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');
Schema::table('petanis', function (Blueprint $table) {
$table->char('provinsi_code', 2)->nullable()->after('alamat');
$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');
$table->text('link_maps')->nullable()->after('desa_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();
Schema::table('petanis', function (Blueprint $table) {
$table->dropColumn(['provinsi_code', 'kota_code', 'kecamatan_code', 'desa_code', 'link_maps']);
});
}
};

View File

@ -8,54 +8,63 @@
class PetaniSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// Petani SUDAH AKTIF
DB::table('petanis')->insert([
'nik' => '3518012345678901',
'nama_lengkap' => 'Siti Aminah',
'username' => 'siti_sayur',
'email' => 'siti@gmail.com',
'password' => Hash::make('password123'),
'no_hp' => '085678901234',
'alamat' => 'Dusun Krajan RT 02 RW 01',
'nama_usaha' => 'Sayur Segar Bu Siti',
'status_akun' => 'aktif',
'created_at' => now(),
'updated_at' => now(),
]);
// Petani BARU DAFTAR menunggu verifikasi
DB::table('petanis')->insert([
'nik' => '3518012345678902',
'nama_lengkap' => 'Budi Santoso',
'username' => 'budi_tani',
'email' => 'budisantoso@gmail.com',
'password' => Hash::make('password123'),
'no_hp' => '081234567890',
'alamat' => 'Jl. Raya Desa Sukamaju No. 12',
'nama_usaha' => 'Budi Farm Organik',
'status_akun' => 'menunggu',
'created_at' => now(),
'updated_at' => now(),
]);
// Petani DITOLAK
DB::table('petanis')->insert([
'nik' => '3518012345678903',
'nama_lengkap' => 'Joko Widodo',
'username' => 'joko_tani',
'email' => 'wiwokdetok@gmail.com',
'password' => Hash::make('password123'),
'no_hp' => '089876543210',
'alamat' => 'Jl. Buntu No. 99',
'nama_usaha' => 'Joko Tani Jaya',
'status_akun' => 'ditolak',
'created_at' => now(),
'updated_at' => now(),
[
'nik' => '3518012345678901',
'nama_lengkap' => 'Siti Aminah',
'username' => 'siti_sayur',
'email' => 'siti@gmail.com',
'password' => Hash::make('password123'),
'no_hp' => '085678901234',
'alamat' => 'Dusun Krajan RT 02 RW 01',
'provinsi_code' => '35',
'kota_code' => '3518',
'kecamatan_code' => '3518100',
'desa_code' => '3518100001',
'link_maps' => 'https://maps.app.goo.gl/XXdummy123',
'nama_usaha' => 'Sayur Segar Bu Siti',
'status_akun' => 'aktif',
'created_at' => now(),
'updated_at' => now(),
],
[
'nik' => '3518012345678902',
'nama_lengkap' => 'Budi Santoso',
'username' => 'budi_tani',
'email' => 'budisantoso@gmail.com',
'password' => Hash::make('password123'),
'no_hp' => '081234567890',
'alamat' => 'Jl. Raya Desa Sukamaju No. 12',
'provinsi_code' => '35',
'kota_code' => '3518',
'kecamatan_code' => '3518100',
'desa_code' => '3518100001',
'link_maps' => 'https://maps.app.goo.gl/XXdummy123',
'nama_usaha' => 'Budi Farm Organik',
'status_akun' => 'aktif',
'created_at' => now(),
'updated_at' => now(),
],
[
'nik' => '3518012345678903',
'nama_lengkap' => 'Joko Widodo',
'username' => 'joko_tani',
'email' => 'joko@gmail.com',
'password' => Hash::make('password123'),
'no_hp' => '089876543210',
'alamat' => 'Jl. Buntu No. 99',
'provinsi_code' => '35',
'kota_code' => '3518',
'kecamatan_code' => '3518100',
'desa_code' => '3518100001',
'link_maps' => 'https://maps.app.goo.gl/XXdummy123',
'nama_usaha' => 'Joko Tani Jaya',
'status_akun' => 'aktif',
'created_at' => now(),
'updated_at' => now(),
]
]);
}
}

View File

@ -7,23 +7,18 @@
use App\Models\ProdukImage;
use App\Models\Kategori;
use App\Models\Petani;
use Illuminate\Support\Str;
class ProdukSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$petani = Petani::inRandomOrder()->first();
$petani = Petani::first();
if (!$petani) {
$this->command->error('Tabel petani kosong. Jalankan PetaniSeeder terlebih dahulu!');
return;
}
// KATEGORI
$kategoriGabah = [
['nama' => 'Gabah Kering Panen (GKP)', 'slug' => 'gkp', 'ikon' => 'fas fa-seedling'],
['nama' => 'Gabah Kering Giling (GKG)', 'slug' => 'gkg', 'ikon' => 'fas fa-sun'],
@ -42,7 +37,6 @@ public function run(): void
);
}
// DAFTAR PRODUK
$daftarProduk = [
[
'nama' => 'Gabah Ciherang (GKP) Baru Panen',
@ -68,45 +62,19 @@ public function run(): void
'kategori_slug' => 'benih-padi',
'gallery' => 1
],
[
'nama' => 'Gabah Padi Ketan Putih Super',
'harga' => 8000,
'stok' => 300,
'deskripsi' => 'Padi ketan putih murni, tidak tercampur padi biasa. Cocok untuk industri tape atau olahan ketan. Kualitas super.',
'kategori_slug' => 'padi-ketan',
'gallery' => 2
],
[
'nama' => 'Gabah Mentik Wangi Organik',
'harga' => 9000,
'stok' => 100,
'deskripsi' => 'Gabah Mentik Wangi (Pandan Wangi Jawa) ditanam full organik tanpa pestisida kimia. Aroma sangat wangi, beras pulen.',
'kategori_slug' => 'padi-organik',
'gallery' => 2
],
[
'nama' => 'Gabah Kering Giling (GKG) Situbagendit',
'harga' => 7200,
'stok' => 1500,
'deskripsi' => 'Gabah Situbagendit kering jemur 3 hari. Cocok untuk stok gudang penggilingan. Lokasi Nganjuk.',
'kategori_slug' => 'gkg',
'gallery' => 1
],
];
// INSERT DATA
foreach ($daftarProduk as $item) {
$kategori = Kategori::where('slug', $item['kategori_slug'])->first();
// Create Produk
$produk = Produk::create([
'petani_id' => $petani->id,
'kategori_id' => $kategori->id,
'nama_produk' => $item['nama'],
'harga' => $item['harga'],
'stok' => $item['stok'],
'deskripsi' => $item['deskripsi'],
'foto_produk' => null,
'petani_id' => $petani->id,
'kategori_id' => $kategori->id,
'nama_produk' => $item['nama'],
'harga' => $item['harga'],
'stok' => $item['stok'],
'deskripsi' => $item['deskripsi'],
'foto_produk' => null,
]);
for ($i = 0; $i < $item['gallery']; $i++) {

View File

@ -4,230 +4,243 @@
@section('content')
{{-- BREADCRUMB --}}
<div class="container-fluid py-3 mb-3">
<div class="container">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="{{ url('/') }}" class="text-decoration-none">Home</a></li>
<li class="breadcrumb-item"><a href="{{ route('shop') }}" class="text-decoration-none">Belanja</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ Str::limit($produk->nama_produk, 40) }}</li>
</ol>
</nav>
</div>
{{-- BREADCRUMB --}}
<div class="container-fluid py-3 mb-3">
<div class="container">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="{{ url('/') }}" class="text-decoration-none">Home</a></li>
<li class="breadcrumb-item"><a href="{{ route('shop') }}" class="text-decoration-none">Belanja</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ Str::limit($produk->nama_produk, 40) }}</li>
</ol>
</nav>
</div>
</div>
<div class="container pb-5">
<div class="row g-4 g-lg-5">
<div class="container pb-5">
<div class="row g-4 g-lg-5">
{{-- GALERI PRODUK --}}
<div class="col-lg-5">
<div class="sticky-top" style="top: 120px; z-index: 1;">
{{-- GAMBAR UTAMA (BESAR) --}}
<div class="card border-0 rounded-4 shadow-sm overflow-hidden mb-3">
<div class="ratio ratio-4x3 bg-light">
<img id="main-image"
src="{{ $produk->foto_produk ? asset('storage/' . $produk->foto_produk) : asset('template/frontend/img/no-image.jpg') }}"
class="object-fit-cover cursor-pointer" alt="{{ $produk->nama_produk }}"
onerror="this.src='{{ asset('template/frontend/img/no-image.jpg') }}';">
</div>
</div>
{{-- THUMBNAIL (GAMBAR KECIL) --}}
<div class="d-flex gap-2 overflow-auto pb-2" style="scrollbar-width: thin;">
@if($produk->foto_produk)
<div class="flex-shrink-0" style="width: 80px; height: 80px;">
<img src="{{ asset('storage/' . $produk->foto_produk) }}"
class="img-thumbnail w-100 h-100 object-fit-cover rounded-3 border-primary thumbnail-active"
onclick="changeImage(this)" style="cursor: pointer; border-width: 2px;">
</div>
@endif
@if($produk->images && $produk->images->count() > 0)
@foreach($produk->images as $img)
<div class="flex-shrink-0" style="width: 80px; height: 80px;">
<img src="{{ asset('storage/' . $img->foto) }}"
class="img-thumbnail w-100 h-100 object-fit-cover rounded-3 border-0 opacity-75"
onclick="changeImage(this)" style="cursor: pointer; transition: all 0.2s;">
</div>
@endforeach
@endif
{{-- GALERI PRODUK --}}
<div class="col-lg-5">
<div class="sticky-top" style="top: 120px; z-index: 1;">
{{-- GAMBAR UTAMA (BESAR) --}}
<div class="card border-0 rounded-4 shadow-sm overflow-hidden mb-3">
<div class="ratio ratio-4x3 bg-light">
<img id="main-image"
src="{{ $produk->foto_produk ? asset('storage/' . $produk->foto_produk) : asset('template/frontend/img/no-image.jpg') }}"
class="object-fit-cover cursor-pointer" alt="{{ $produk->nama_produk }}"
onerror="this.src='{{ asset('template/frontend/img/no-image.jpg') }}';">
</div>
</div>
</div>
{{-- THUMBNAIL (GAMBAR KECIL) --}}
<div class="d-flex gap-2 overflow-auto pb-2" style="scrollbar-width: thin;">
{{-- INFO & AKSI --}}
<div class="col-lg-7">
<div class="ps-lg-4">
{{-- Kategori Badge --}}
<div class="mb-3">
<span
class="badge bg-primary bg-opacity-10 px-3 py-2 rounded-pill fw-bold border border-primary border-opacity-10">
<i class="fas fa-tag me-1"></i> {{ $produk->kategori->nama_kategori ?? 'Sayur & Buah' }}
</span>
@if($produk->foto_produk)
<div class="flex-shrink-0" style="width: 80px; height: 80px;">
<img src="{{ asset('storage/' . $produk->foto_produk) }}"
class="img-thumbnail w-100 h-100 object-fit-cover rounded-3 border-primary thumbnail-active"
onclick="changeImage(this)" style="cursor: pointer; border-width: 2px;">
</div>
@endif
{{-- Nama Produk --}}
<h1 class="display-6 fw-bold text-dark mb-2">{{ $produk->nama_produk }}</h1>
{{-- Harga --}}
<div class="d-flex align-items-baseline gap-2 mb-4 pb-4 border-bottom">
<h3 class="text-primary fw-bold mb-0">
Rp {{ number_format($produk->harga, 0, ',', '.') }}
</h3>
<span class="text-muted fs-5">/ kg</span>
@if($produk->images && $produk->images->count() > 0)
@foreach($produk->images as $img)
<div class="flex-shrink-0" style="width: 80px; height: 80px;">
<img src="{{ asset('storage/' . $img->foto) }}"
class="img-thumbnail w-100 h-100 object-fit-cover rounded-3 border-0 opacity-75"
onclick="changeImage(this)" style="cursor: pointer; transition: all 0.2s;">
</div>
{{-- Deskripsi --}}
<div class="mb-4">
<h6 class="fw-bold text-dark">Deskripsi Produk</h6>
<p class="text-black-50 small mb-0" style="line-height: 1.8;">
{{ $produk->deskripsi }}
</p>
</div>
{{-- Info Penjual --}}
<div class="d-flex align-items-center bg-light border rounded-3 p-3 mb-4">
<div class="bg-white rounded-circle p-2 me-3 shadow-sm text-primary d-flex align-items-center justify-content-center"
style="width: 45px; height: 45px;">
<i class="fas fa-store fs-5"></i>
</div>
<div class="flex-grow-1">
<p class="text-muted small mb-0 fw-bold text-uppercase" style="font-size: 0.7rem;">Dijual Oleh
</p>
<h6 class="fw-bold text-dark mb-0">{{ $produk->petani->nama_lengkap ?? 'Petani Mitra' }}</h6>
</div>
<button type="button" class="btn btn-outline-success btn-sm rounded-pill px-3 fw-bold"
data-bs-toggle="modal" data-bs-target="#chatModal">
<i class="fab fa-whatsapp me-1"></i> Chat
</button>
</div>
{{-- FORM PEMBELIAN --}}
<form action="{{ route('cart.add') }}" method="POST">
@csrf
<input type="hidden" name="id" value="{{ $produk->id }}">
<div class="mb-4">
{{-- Input Jumlah --}}
<div class="mb-3">
<label class="form-label small fw-bold text-muted text-uppercase mb-2">Atur Jumlah</label>
<div class="d-flex align-items-center">
<div class="input-group" style="max-width: 180px;">
<button type="button" class="btn btn-outline-primary" onclick="updateQty(-1)">
<i class="fas fa-minus"></i>
</button>
<input type="text" class="form-control text-center fw-bold bg-white"
id="display-qty" value="1" readonly>
<input type="hidden" name="qty" id="real-input-qty" value="1" min="1"
max="{{ $produk->stok }}">
<button type="button" class="btn btn-outline-primary" onclick="updateQty(1)">
<i class="fas fa-plus"></i>
</button>
</div>
<div class="ms-3 small text-muted">
Stok: <span class="fw-bold text-dark">{{ $produk->stok }} Kg</span>
</div>
</div>
</div>
{{-- Tombol Aksi --}}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary rounded-pill py-2 shadow-sm fw-bold fs-6"
style="height: 50px;">
<i class="fas fa-cart-plus me-2"></i> Masukkan Keranjang
</button>
<a href="javascript:void(0);" onclick="beliLangsung()"
class="btn btn-outline-dark rounded-pill py-2 border-2 fw-bold fs-6"
style="height: 50px; line-height: 32px;">
Beli Sekarang
</a>
</div>
</div>
{{-- Footer Info --}}
<div class="d-flex gap-4 pt-3 border-top">
<div class="d-flex align-items-center text-muted small">
<i class="fas fa-truck me-2 text-primary"></i> Pengiriman Cepat
</div>
<div class="d-flex align-items-center text-muted small">
<i class="fas fa-shield-alt me-2 text-primary"></i> Produk Segar
</div>
</div>
</form>
@endforeach
@endif
</div>
</div>
</div>
{{-- BAGIAN PRODUK TERKAIT --}}
@if (isset($produk_terkait) && $produk_terkait->count() > 0)
<div class="mt-5 pt-4 border-top">
<div class="d-flex align-items-center justify-content-between mb-4">
<h4 class="fw-bold m-0 text-dark">Produk Sejenis</h4>
<a href="{{ route('shop') }}" class="btn btn-sm btn-outline-primary rounded-pill px-3">
Lihat Lainnya <i class="fas fa-arrow-right ms-1"></i>
</a>
{{-- INFO & AKSI --}}
<div class="col-lg-7">
<div class="ps-lg-4">
{{-- Kategori Badge --}}
<div class="mb-3">
<span
class="badge bg-primary bg-opacity-10 px-3 py-2 rounded-pill fw-bold border border-primary border-opacity-10">
<i class="fas fa-tag me-1"></i> {{ $produk->kategori->nama_kategori ?? 'Sayur & Buah' }}
</span>
</div>
<div class="row g-4">
@include('landing.partials.product_list', ['produks' => $produk_terkait])
</div>
</div>
@endif
</div>
{{-- Nama Produk --}}
<h1 class="display-6 fw-bold text-dark mb-2">{{ $produk->nama_produk }}</h1>
{{-- MODAL CHAT --}}
<div class="modal fade" id="chatModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow-lg rounded-3">
<div class="modal-header bg-primary text-white border-0">
<h5 class="modal-title fw-semibold text-white">
<i class="fab fa-whatsapp me-2"></i>Hubungi Penjual
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"
aria-label="Close"></button>
{{-- Harga --}}
<div class="d-flex align-items-baseline gap-2 mb-4 pb-4 border-bottom">
<h3 class="text-primary fw-bold mb-0">
Rp {{ number_format($produk->harga, 0, ',', '.') }}
</h3>
<span class="text-muted fs-5">/ kg</span>
</div>
<form action="{{ route('pesan.kirim') }}" method="POST">
{{-- Deskripsi --}}
<div class="mb-4">
<h6 class="fw-bold text-dark">Deskripsi Produk</h6>
<p class="text-black-50 small mb-0" style="line-height: 1.8;">
{{ $produk->deskripsi }}
</p>
</div>
{{-- Info Penjual --}}
<div class="d-flex align-items-center bg-light border rounded-3 p-3 mb-4">
<div class="bg-white rounded-circle p-2 me-3 shadow-sm text-primary d-flex align-items-center justify-content-center"
style="width: 45px; height: 45px;">
<i class="fas fa-store fs-5"></i>
</div>
<div class="flex-grow-1">
<h6 class="fw-bold text-dark mb-0">{{ $produk->petani->nama_lengkap ?? 'Petani Mitra' }}</h6>
<small class="text-muted d-block" style="font-size: 0.8rem;">
<i class="fas fa-map-marker-alt text-danger me-1"></i>
{{ Str::limit($produk->petani->alamat ?? 'Alamat tidak tersedia', 35) }}
</small>
</div>
<button type="button" class="btn btn-outline-success btn-sm rounded-pill px-3 fw-bold"
data-bs-toggle="modal" data-bs-target="#chatModal">
<i class="fab fa-whatsapp me-1"></i> Chat
</button>
</div>
{{-- Link Maps --}}
@if(isset($produk->petani->link_maps) && $produk->petani->link_maps != '')
<a href="{{ $produk->petani->link_maps }}" target="_blank"
class="btn btn-light border-danger text-danger border fw-bold mb-4 w-100 text-start shadow-sm"
style="border-radius: 10px;">
<i class="fas fa-map-marker-alt me-2 fs-5 align-middle"></i>
Lihat Lokasi Lahan di Google Maps
<i class="fas fa-external-link-alt float-end mt-1 text-muted opacity-50"></i>
</a>
@endif
{{-- FORM PEMBELIAN --}}
<form action="{{ route('cart.add') }}" method="POST">
@csrf
<div class="modal-body p-4">
<input type="hidden" name="penerima_id" value="{{ $produk->petani_id }}">
<input type="hidden" name="penerima_type" value="App\Models\Petani">
<input type="hidden" name="id" value="{{ $produk->id }}">
<div class="mb-4">
{{-- Input Jumlah --}}
<div class="mb-3">
<label class="form-label fw-bold">Kepada:</label>
<input type="text" class="form-control"
value="{{ $produk->petani->nama_lengkap ?? 'Petani Mitra' }}" readonly>
<label class="form-label small fw-bold text-muted text-uppercase mb-2">Atur Jumlah</label>
<div class="d-flex align-items-center">
<div class="input-group" style="max-width: 180px;">
<button type="button" class="btn btn-outline-primary" onclick="updateQty(-1)">
<i class="fas fa-minus"></i>
</button>
<input type="text" class="form-control text-center fw-bold bg-white"
id="display-qty" value="1" readonly>
<input type="hidden" name="qty" id="real-input-qty" value="1" min="1"
max="{{ $produk->stok }}">
<button type="button" class="btn btn-outline-primary" onclick="updateQty(1)">
<i class="fas fa-plus"></i>
</button>
</div>
<div class="ms-3 small text-muted">
Stok: <span class="fw-bold text-dark">{{ $produk->stok }} Kg</span>
</div>
</div>
</div>
<div class="mb-0">
<label class="form-label fw-bold">Pesan Anda:</label>
<textarea name="isi_pesan" class="form-control" rows="4" required
placeholder="Contoh: Halo, apakah stok untuk produk {{ $produk->nama_produk }} masih tersedia?"></textarea>
{{-- Tombol Aksi --}}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary rounded-pill py-2 shadow-sm fw-bold fs-6"
style="height: 50px;">
<i class="fas fa-cart-plus me-2"></i> Masukkan Keranjang
</button>
<a href="javascript:void(0);" onclick="beliLangsung()"
class="btn btn-outline-dark rounded-pill py-2 border-2 fw-bold fs-6"
style="height: 50px; line-height: 32px;">
Beli Sekarang
</a>
</div>
</div>
<div class="modal-footer border-0 px-4 pb-4 pt-0">
<button type="button" class="btn btn-light rounded-pill px-4" data-bs-dismiss="modal">Batal</button>
<button type="submit" class="btn btn-primary rounded-pill px-4">
<i class="fab fa-whatsapp me-2"></i>Kirim Pesan
</button>
{{-- Footer Info --}}
<div class="d-flex gap-4 pt-3 border-top">
<div class="d-flex align-items-center text-muted small">
<i class="fas fa-truck me-2 text-primary"></i> Pengiriman Cepat
</div>
<div class="d-flex align-items-center text-muted small">
<i class="fas fa-shield-alt me-2 text-primary"></i> Produk Segar
</div>
</div>
</form>
</div>
</div>
</div>
{{-- BAGIAN PRODUK TERKAIT --}}
@if (isset($produk_terkait) && $produk_terkait->count() > 0)
<div class="mt-5 pt-4 border-top">
<div class="d-flex align-items-center justify-content-between mb-4">
<h4 class="fw-bold m-0 text-dark">Produk Sejenis</h4>
<a href="{{ route('shop') }}" class="btn btn-sm btn-outline-primary rounded-pill px-3">
Lihat Lainnya <i class="fas fa-arrow-right ms-1"></i>
</a>
</div>
<div class="row g-4">
@include('landing.partials.product_list', ['produks' => $produk_terkait])
</div>
</div>
@endif
</div>
{{-- MODAL CHAT --}}
<div class="modal fade" id="chatModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow-lg rounded-3">
<div class="modal-header bg-primary text-white border-0">
<h5 class="modal-title fw-semibold text-white">
<i class="fab fa-whatsapp me-2"></i>Hubungi Penjual
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<form action="{{ route('pesan.kirim') }}" method="POST">
@csrf
<div class="modal-body p-4">
<input type="hidden" name="penerima_id" value="{{ $produk->petani_id }}">
<input type="hidden" name="penerima_type" value="App\Models\Petani">
<div class="mb-3">
<label class="form-label fw-bold">Kepada:</label>
<input type="text" class="form-control"
value="{{ $produk->petani->nama_lengkap ?? 'Petani Mitra' }}" readonly>
</div>
<div class="mb-0">
<label class="form-label fw-bold">Pesan Anda:</label>
<textarea name="isi_pesan" class="form-control" rows="4" required
placeholder="Contoh: Halo, apakah stok untuk produk {{ $produk->nama_produk }} masih tersedia?"></textarea>
</div>
</div>
<div class="modal-footer border-0 px-4 pb-4 pt-0">
<button type="button" class="btn btn-light rounded-pill px-4" data-bs-dismiss="modal">Batal</button>
<button type="submit" class="btn btn-primary rounded-pill px-4">
<i class="fab fa-whatsapp me-2"></i>Kirim Pesan
</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('js')
<script>
const hargaSatuan = {{ $produk->harga }};
<script>
const hargaSatuan = {{ $produk->harga }};
function updateQty(change) {
let display = document.getElementById('display-qty');
@ -271,5 +284,5 @@ function changeImage(element) {
element.style.borderWidth = '2px';
element.style.opacity = '1';
}
</script>
</script>
@endsection

View File

@ -188,7 +188,7 @@ class="btn {{ request('kategori') == '' || request('kategori') == 'Semua' ? 'btn
Semua
</a>
{{-- 2. Looping Kategori dari Database --}}
{{-- Looping Kategori dari Database --}}
@foreach($kategoriList as $kat)
<a href="#" data-kategori="{{ $kat->slug }}"
class="btn {{ request('kategori') == $kat->slug ? 'btn-primary' : 'btn-outline-primary' }} rounded-pill px-4 py-2 m-1 btn-category">

View File

@ -18,7 +18,12 @@ class="card-img-top w-100 h-100 object-fit-cover" alt="{{ $produk->nama_produk }
{{ $produk->kategori->nama_kategori ?? 'Umum' }}
</span>
<h5 class="fw-bold mb-2 text-dark">{{ $produk->nama_produk }}</h5>
<h6 class="fw-bold mb-1 text-dark text-truncate" style="max-width: 100%;">{{ $produk->nama_produk }}</h6>
<small class="text-muted d-block mb-2" style="font-size: 0.8rem;">
<i class="fas fa-map-marker-alt text-danger me-1"></i>
{{ Str::limit($produk->petani->alamat ?? 'Alamat tidak tersedia', 35) }}
</small>
<p class="text-muted small mb-3" style="line-height: 1.5;">
{{ Str::limit($produk->deskripsi, 60) }}
@ -32,6 +37,7 @@ class="card-img-top w-100 h-100 object-fit-cover" alt="{{ $produk->nama_produk }
Rp {{ number_format($produk->harga, 0, ',', '.') }}
</h6>
</div>
<a href="{{ route('produk.detail', $produk->id) }}"
class="btn btn-primary btn-sm rounded-pill px-3 stretched-link">
Lihat <i class="fa fa-arrow-right ms-1"></i>

View File

@ -175,7 +175,12 @@ class="card-img-top w-100 h-100" alt="{{ $produk->nama_produk }}"
<div>
<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 text-truncate" style="max-width: 100%;">{{
$produk->nama_produk }}</h6>
<small class="text-muted d-block mb-2" style="font-size: 0.8rem;">
<i class="fas fa-map-marker-alt text-danger me-1"></i>
{{ Str::limit($produk->petani->alamat ?? 'Alamat tidak tersedia', 35) }}
</small>
<p class="text-muted small mb-3">{{ Str::limit($produk->deskripsi, 60) }}</p>
</div>

View File

@ -52,36 +52,6 @@
<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 --}}

View File

@ -51,49 +51,6 @@
<label class="fw-bold">Deskripsi</label>
<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>
{{-- KELOLA GAMBAR --}}

View File

@ -4,60 +4,154 @@
@section('page-title', 'Pengaturan Akun')
@section('content')
<div class="card">
<div class="card-body">
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
<div class="card">
<div class="card-body">
@if (session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
<form action="{{ route('petani.profile.update') }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
<form action="{{ route('petani.profile.update') }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
<div class="row">
<div class="col-md-4 text-center">
<img src="{{ $user->foto ? asset('storage/' . $user->foto) : asset('template/admin/static/images/faces/2.jpg') }}"
alt="Avatar" class="rounded-circle img-thumbnail mb-3"
style="width: 150px; height: 150px; object-fit: cover;">
<div class="row">
<div class="col-md-4 text-center">
<img src="{{ $user->foto ? asset('storage/' . $user->foto) : asset('template/admin/static/images/faces/2.jpg') }}"
alt="Avatar" class="rounded-circle img-thumbnail mb-3"
style="width: 150px; height: 150px; object-fit: cover;">
<div class="mb-3">
<label class="form-label btn btn-sm btn-outline-primary">
Ganti Foto <input type="file" name="foto" hidden accept="image/*">
</label>
</div>
</div>
<div class="col-md-8">
<div class="mb-3">
<label class="form-label">Nama Lengkap / Nama Toko</label>
<input type="text" name="nama_lengkap" class="form-control" value="{{ $user->nama_lengkap }}"
required>
</div>
<div class="mb-3">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control" value="{{ $user->email }}" required>
</div>
<hr class="my-4">
<h6 class="text-muted mb-3">Ganti Password (Opsional)</h6>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Password Baru</label>
<input type="password" name="password" class="form-control"
placeholder="Kosongkan jika tidak diganti">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Konfirmasi Password</label>
<input type="password" name="password_confirmation" class="form-control"
placeholder="Ulangi password baru">
</div>
</div>
<button type="submit" class="btn btn-primary">Simpan Perubahan</button>
<div class="mb-3">
<label class="form-label btn btn-sm btn-outline-primary">
Ganti Foto <input type="file" name="foto" hidden accept="image/*">
</label>
</div>
</div>
</form>
</div>
<div class="col-md-8">
<div class="mb-3">
<label class="form-label">Nama Lengkap / Nama Toko</label>
<input type="text" name="nama_lengkap" class="form-control" value="{{ $user->nama_lengkap }}"
required>
</div>
<div class="mb-3">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control" value="{{ $user->email }}" required>
</div>
<hr class="my-4">
<h6 class="text-muted mb-3">Ganti Password (Opsional)</h6>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Password Baru</label>
<input type="password" name="password" class="form-control"
placeholder="Kosongkan jika tidak diganti">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Konfirmasi Password</label>
<input type="password" name="password_confirmation" class="form-control"
placeholder="Ulangi password baru">
</div>
</div>
<div class="mb-3">
<label class="form-label">Alamat Lengkap (Jalan/Dusun/RT/RW)</label>
<textarea name="alamat" class="form-control" rows="3" required>{{ $user->alamat }}</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 }}" {{ $user->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</option>
@foreach($kotas as $kota)
<option value="{{ $kota->code }}" {{ $user->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 $kecamatan)
<option value="{{ $kecamatan->code }}" {{ $user->kecamatan_code == $kecamatan->code ?
'selected' : '' }}>{{ $kecamatan->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 }}" {{ $user->desa_code == $desa->code ? 'selected' : ''
}}>{{ $desa->name }}</option>
@endforeach
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label fw-bold">Link Google Maps Lahan/Toko (Opsional)</label>
<input type="url" name="link_maps" class="form-control"
placeholder="Contoh: http://maps.google.com/..." value="{{ $user->link_maps }}">
</div>
<button type="submit" class="btn btn-primary">Simpan Perubahan</button>
</div>
</div>
</form>
</div>
</div>
@endsection
@section('js')
<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>');
if(code) {
$.post("{{ url('get-kota') }}", {code: code, _token: '{{ csrf_token() }}'}, function(data) {
$('#kota').html('<option value="">Pilih Kota</option>');
$.each(data, function(key, val) { $('#kota').append(`<option value="${val.code}">${val.name}</option>`); });
});
}
});
$('#kota').on('change', function() {
let code = $(this).val();
$('#kecamatan').html('<option value="">Memuat...</option>');
if(code) {
$.post("{{ url('get-kecamatan') }}", {code: code, _token: '{{ csrf_token() }}'}, function(data) {
$('#kecamatan').html('<option value="">Pilih Kecamatan</option>');
$.each(data, function(key, val) { $('#kecamatan').append(`<option value="${val.code}">${val.name}</option>`); });
});
}
});
$('#kecamatan').on('change', function() {
let code = $(this).val();
$('#desa').html('<option value="">Memuat...</option>');
if(code) {
$.post("{{ url('get-desa') }}", {code: code, _token: '{{ csrf_token() }}'}, function(data) {
$('#desa').html('<option value="">Pilih Desa</option>');
$.each(data, function(key, val) { $('#desa').append(`<option value="${val.code}">${val.name}</option>`); });
});
}
});
});
</script>
@endsection