Redesigned the Admin Chat UI according to branding, updated the specific grain seeder, and fixed bugs in category routing and product image previews

This commit is contained in:
sayasilvi 2025-12-15 03:00:26 +07:00
parent bd3c6193c0
commit ea955f9e46
42 changed files with 3118 additions and 1313 deletions

View File

@ -0,0 +1,70 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Kategori;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class KategoriController extends Controller
{
public function index()
{
$kategoris = Kategori::withCount('produks')->latest()->get();
return view('admin.kategori.index', compact('kategoris'));
}
public function create()
{
return view('admin.kategori.create');
}
public function store(Request $request)
{
$request->validate([
'nama_kategori' => 'required|string|max:255|unique:kategoris,nama_kategori',
'ikon' => 'nullable|string|max:50',
]);
Kategori::create([
'nama_kategori' => $request->nama_kategori,
'slug' => Str::slug($request->nama_kategori),
'ikon' => $request->ikon,
]);
return redirect()->route('admin.kategori.index')->with('success', 'Kategori berhasil ditambahkan.');
}
public function edit($id)
{
$kategori = Kategori::findOrFail($id);
return view('admin.kategori.edit', compact('kategori'));
}
public function update(Request $request, $id)
{
$kategori = Kategori::findOrFail($id);
$request->validate([
'nama_kategori' => 'required|string|max:255|unique:kategoris,nama_kategori,'.$id,
'ikon' => 'nullable|string|max:50',
]);
$kategori->update([
'nama_kategori' => $request->nama_kategori,
'slug' => Str::slug($request->nama_kategori),
'ikon' => $request->ikon,
]);
return redirect()->route('admin.kategori.index')->with('success', 'Kategori berhasil diperbarui.');
}
public function destroy($id)
{
$kategori = Kategori::findOrFail($id);
$kategori->delete();
return redirect()->route('admin.kategori.index')->with('success', 'Kategori berhasil dihapus.');
}
}

View File

@ -16,7 +16,10 @@ public function dashboard()
$totalProduk = Produk::count();
$totalTransaksi = Transaksi::count();
$transaksiTerbaru = Transaksi::with(['pembeli', 'details'])->latest()->take(5)->get();
$transaksiTerbaru = Transaksi::with(['pembeli', 'petani'])
->latest()
->take(5)
->get();
return view('admin.dashboard', compact(
'totalPetani',
@ -30,15 +33,28 @@ public function dashboard()
public function monitoring()
{
$produks = Produk::with('petani')->latest()->paginate(10);
$transaksis = Transaksi::with(['pembeli'])->latest()->paginate(10);
$transaksis = Transaksi::with(['pembeli', 'petani'])
->latest()
->paginate(10);
return view('admin.monitoring', compact('produks', 'transaksis'));
}
// --- FITUR BARU: DETAIL TRANSAKSI ---
public function transaksiDetail($id)
{
$transaksi = Transaksi::with(['pembeli', 'petani', 'detailTransaksis.produk'])
->findOrFail($id);
return view('admin.transaksi_detail', compact('transaksi'));
}
// --- VERIFIKASI PETANI ---
public function verifikasiIndex()
{
// Ambil data petani yang statusnya 'menunggu'
$petanis = Petani::orderBy('created_at', 'desc')->get();
$petanis = Petani::orderBy('created_at', 'desc')->get();
return view('admin.verifikasi.index', compact('petanis'));
}

View File

@ -19,8 +19,12 @@ public function index(Request $request)
return view('landing.partials.product_list', compact('produks'))->render();
}
$produkTerlaris = Produk::with('petani')
->inRandomOrder()
$produkTerlaris = Produk::withSum(['detailTransaksis as total_terjual' => function ($query) {
$query->whereHas('transaksi', function ($q) {
$q->where('status', '!=', 'batal');
});
}], 'jumlah')
->orderByDesc('total_terjual')
->take(4)
->get();
@ -29,25 +33,53 @@ public function index(Request $request)
public function shop(Request $request)
{
$query = Produk::with('petani');
$query = Produk::where('stok', '>', 0);
if ($request->has('search') && $request->search != '') {
$query->where('nama_produk', 'like', '%' . $request->search . '%');
// Filter Kategori Berdasarkan Slug
if ($request->has('kategori') && $request->kategori != '') {
$slug = $request->kategori;
$query->whereHas('kategori', function ($q) use ($slug) {
$q->where('slug', $slug);
});
}
$produks = $query->paginate(12);
// Sorting (Urutkan)
if ($request->has('sort')) {
switch ($request->sort) {
case 'termurah':
$query->orderBy('harga', 'asc');
break;
case 'termahal':
$query->orderBy('harga', 'desc');
break;
case 'terbaru':
$query->latest();
break;
default:
$query->latest();
break;
}
} else {
$query->latest();
}
$produks = $query->paginate(9);
return view('landing.shop', compact('produks'));
}
public function detail($id)
{
$produk = Produk::with('petani')->findOrFail($id);
$related_products = Produk::where('petani_id', $produk->petani_id)
->where('id', '!=', $id)
$produk = Produk::with(['kategori', 'petani', 'images'])->findOrFail($id);
$produk_terkait = Produk::where('kategori_id', $produk->kategori_id)
->where('id', '!=', $produk->id)
->where('stok', '>', 0)
->inRandomOrder()
->take(4)
->get();
return view('landing.detail', compact('produk', 'related_products'));
return view('landing.detail', compact('produk', 'produk_terkait'));
}
}

View File

@ -20,27 +20,36 @@ private function getChatList($user)
})->orderBy('created_at', 'desc')->get();
$conversations = $allMessages->groupBy(function ($pesan) use ($user) {
return $pesan->pengirim_id == $user->id
? $pesan->penerima_type . '_' . $pesan->penerima_id
: $pesan->pengirim_type . '_' . $pesan->pengirim_id;
if ($pesan->pengirim_id == $user->id && $pesan->pengirim_type == get_class($user)) {
return $pesan->penerima_type . '_' . $pesan->penerima_id;
} else {
return $pesan->pengirim_type . '_' . $pesan->pengirim_id;
}
});
return $conversations->map(function ($msgs) use ($user) {
$lastMsg = $msgs->first();
if ($lastMsg->pengirim_id == $user->id) {
if ($lastMsg->pengirim_id == $user->id && $lastMsg->pengirim_type == get_class($user)) {
$lawan = $lastMsg->penerima;
} else {
$lawan = $lastMsg->pengirim;
}
// Fallback
$lawanObj = $lawan ?? new \stdClass;
return [
'lawan_id' => $lawan->id ?? 0,
'lawan_type' => get_class($lawan ?? new \stdClass),
'lawan_type' => get_class($lawanObj),
'nama' => $lawan->nama_lengkap ?? 'User Terhapus',
'foto' => $lawan->foto ?? null, // Pastikan ada accessor url foto
'foto' => $lawan->foto ?? null,
'last_message' => $lastMsg->isi_pesan,
'time' => $lastMsg->created_at->diffForHumans(),
'unread' => $msgs->where('penerima_id', $user->id)->where('sudah_dibaca', false)->count()
'unread' => $msgs->where('penerima_id', $user->id)
->where('penerima_type', get_class($user))
->where('sudah_dibaca', false)
->count()
];
});
}

View File

@ -5,42 +5,52 @@
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Produk;
use App\Models\ProdukImage;
use App\Models\Kategori; // PENTING: Import Model Kategori
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
class ProdukController extends Controller
{
// Tampilkan Daftar Produk Petani
public function index()
{
$produks = Produk::where('petani_id', Auth::guard('petani')->id())->latest()->get();
$produks = Produk::with('kategori')
->where('petani_id', Auth::guard('petani')->id())
->latest()
->get();
return view('petani.produk.index', compact('produks'));
}
// Form Tambah Produk
public function create()
{
return view('petani.produk.create');
$kategoris = Kategori::all();
return view('petani.produk.create', compact('kategoris'));
}
// Simpan Produk Baru
public function store(Request $request)
{
$request->validate([
'nama_produk' => 'required|string|max:255',
'harga' => 'required|numeric|min:0',
'stok' => 'required|integer|min:0',
'deskripsi' => 'required|string',
'foto_produk' => 'required|image|mimes:jpeg,png,jpg|max:2048',
'nama_produk' => 'required|string|max:255',
'kategori_id' => 'required|exists:kategoris,id',
'harga' => 'required|numeric|min:0',
'stok' => 'required|integer|min:0',
'deskripsi' => 'required|string',
'foto_produk' => 'required|image|mimes:jpeg,png,jpg|max:2048',
'foto_tambahan.*' => 'nullable|image|mimes:jpeg,png,jpg|max:2048'
]);
// Simpan Foto Utama
$fotoPath = null;
if ($request->hasFile('foto_produk')) {
$fotoPath = $request->file('foto_produk')->store('produk', 'public');
}
Produk::create([
// Simpan Data Produk
$produk = Produk::create([
'petani_id' => Auth::guard('petani')->id(),
'kategori_id' => $request->kategori_id,
'nama_produk' => $request->nama_produk,
'harga' => $request->harga,
'stok' => $request->stok,
@ -48,29 +58,50 @@ public function store(Request $request)
'foto_produk' => $fotoPath,
]);
// Simpan Foto Tambahan
if ($request->hasFile('foto_tambahan')) {
foreach ($request->file('foto_tambahan') as $file) {
$path = $file->store('produk/gallery', 'public');
ProdukImage::create([
'produk_id' => $produk->id,
'foto' => $path
]);
}
}
return redirect()->route('petani.produk.index')->with('success', 'Produk berhasil ditambahkan.');
}
// Form Edit Produk
public function edit($id)
{
$produk = Produk::where('id', $id)->where('petani_id', Auth::guard('petani')->id())->firstOrFail();
return view('petani.produk.edit', compact('produk'));
$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'));
}
// Update Produk
public function update(Request $request, $id)
{
$produk = Produk::where('id', $id)->where('petani_id', Auth::guard('petani')->id())->firstOrFail();
$produk = Produk::with('images')->where('id', $id)->where('petani_id', Auth::guard('petani')->id())->firstOrFail();
// Hitung slot gambar tersisa
$jumlahGambarLama = $produk->images->count();
$sisaSlot = max(3 - $jumlahGambarLama, 0);
$request->validate([
'nama_produk' => 'required|string|max:255',
'harga' => 'required|numeric|min:0',
'stok' => 'required|integer|min:0',
'deskripsi' => 'required|string',
'foto_produk' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'nama_produk' => 'required|string|max:255',
'kategori_id' => 'required|exists:kategoris,id',
'harga' => 'required|numeric|min:0',
'stok' => 'required|integer|min:0',
'deskripsi' => 'required|string',
'foto_produk' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'foto_tambahan' => 'array|max:' . $sisaSlot,
'foto_tambahan.*' => 'image|mimes:jpeg,png,jpg|max:2048'
], [
'foto_tambahan.max' => "Anda hanya bisa menambah $sisaSlot foto lagi."
]);
// Update Foto Utama
if ($request->hasFile('foto_produk')) {
if ($produk->foto_produk && Storage::disk('public')->exists($produk->foto_produk)) {
Storage::disk('public')->delete($produk->foto_produk);
@ -78,21 +109,32 @@ public function update(Request $request, $id)
$produk->foto_produk = $request->file('foto_produk')->store('produk', 'public');
}
// Update Data
$produk->update([
'nama_produk' => $request->nama_produk,
'kategori_id' => $request->kategori_id,
'harga' => $request->harga,
'stok' => $request->stok,
'deskripsi' => $request->deskripsi,
'foto_produk' => $produk->foto_produk
]);
if ($request->hasFile('foto_produk')) {
$produk->save();
// Tambah Foto Galeri
if ($request->hasFile('foto_tambahan')) {
foreach ($request->file('foto_tambahan') as $file) {
if ($produk->images()->count() >= 3) break;
$path = $file->store('produk/gallery', 'public');
ProdukImage::create([
'produk_id' => $produk->id,
'foto' => $path
]);
}
}
return redirect()->route('petani.produk.index')->with('success', 'Produk berhasil diperbarui.');
}
// Hapus Produk
public function destroy($id)
{
$produk = Produk::where('id', $id)->where('petani_id', Auth::guard('petani')->id())->firstOrFail();
@ -101,8 +143,32 @@ public function destroy($id)
Storage::disk('public')->delete($produk->foto_produk);
}
foreach($produk->images as $img) {
if (Storage::disk('public')->exists($img->foto)) {
Storage::disk('public')->delete($img->foto);
}
}
$produk->images()->delete();
$produk->delete();
return redirect()->route('petani.produk.index')->with('success', 'Produk berhasil dihapus.');
}
public function deleteImage($id)
{
$image = ProdukImage::findOrFail($id);
if ($image->produk->petani_id != Auth::guard('petani')->id()) {
abort(403);
}
if (Storage::disk('public')->exists($image->foto)) {
Storage::disk('public')->delete($image->foto);
}
$image->delete();
return back()->with('success', 'Foto berhasil dihapus.');
}
}

View File

@ -72,16 +72,17 @@ 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 (Single Item) ---
$produk = Produk::findOrFail($request->produk_id);
$total_harga = $produk->harga * $request->jumlah;
$transaksi = Transaksi::create([
'pembeli_id' => $pembeli_id,
'petani_id' => $produk->petani_id,
'tanggal_transaksi' => now(),
'alamat_pengiriman' => $request->alamat_pengiriman,
'total_harga' => $total_harga,
'status' => 'menunggu_konfirmasi',
'status' => 'menunggu konfirmasi',
'kode_invoice' => 'INV/' . date('Ymd') . '/' . rand(1000, 9999),
]);
@ -94,7 +95,9 @@ public function prosesCheckout(Request $request)
]);
$produk->decrement('stok', $request->jumlah);
} else {
// --- LOGIKA KERANJANG (Cart) ---
$cart = session()->get('cart');
// Kelompokkan produk berdasarkan Petani ID
@ -113,13 +116,14 @@ public function prosesCheckout(Request $request)
$subtotal_transaksi = 0;
$kode_invoice = 'INV/' . date('Ymd') . '/' . rand(1000, 9999);
// Membuat Header Transaksi
// Membuat Header Transaksi per Petani
$transaksi = Transaksi::create([
'pembeli_id' => $pembeli_id,
'petani_id' => $petani_id,
'tanggal_transaksi' => now(),
'alamat_pengiriman' => $request->alamat_pengiriman,
'total_harga' => 0,
'status' => 'menunggu_konfirmasi',
'status' => 'menunggu konfirmasi',
'kode_invoice' => $kode_invoice,
]);
@ -154,7 +158,7 @@ public function prosesCheckout(Request $request)
// Riwayat Pesanan
public function pesananSaya()
{
$transaksis = Transaksi::with(['details.produk.petani'])
$transaksis = Transaksi::with(['detailTransaksis.produk.petani'])
->where('pembeli_id', Auth::guard('pembeli')->id())
->latest()
->get();
@ -186,10 +190,10 @@ public function pesananMasuk()
{
$petaniId = Auth::guard('petani')->id();
$pesanans = Transaksi::whereHas('details.produk', function ($q) use ($petaniId) {
$pesanans = Transaksi::whereHas('detailTransaksis.produk', function ($q) use ($petaniId) {
$q->where('petani_id', $petaniId);
})
->with(['pembeli', 'details.produk'])
->with(['pembeli', 'detailTransaksis.produk'])
->latest()
->get();
@ -223,10 +227,10 @@ public function pesananDetail($id)
$petaniId = Auth::guard('petani')->id();
// Ambil transaksi berdasarkan ID
$pesanan = Transaksi::whereHas('details.produk', function ($q) use ($petaniId) {
$pesanan = Transaksi::whereHas('detailTransaksis.produk', function ($q) use ($petaniId) {
$q->where('petani_id', $petaniId);
})
->with(['pembeli', 'details.produk'])
->with(['pembeli', 'detailTransaksis.produk'])
->findOrFail($id);
return view('petani.pesanan.detail', compact('pesanan'));

18
app/Models/Kategori.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Kategori extends Model
{
use HasFactory;
protected $fillable = ['nama_kategori', 'slug', 'ikon'];
public function produks()
{
return $this->hasMany(Produk::class, 'kategori_id');
}
}

View File

@ -14,7 +14,6 @@ class Produk extends Model
protected $fillable = [
'petani_id',
'nama_produk',
'kategori',
'harga',
'stok',
'deskripsi',
@ -25,9 +24,19 @@ public function petani()
{
return $this->belongsTo(Petani::class, 'petani_id');
}
public function detailTransaksis()
{
return $this->hasMany(DetailTransaksi::class, 'produk_id');
}
}
public function images()
{
return $this->hasMany(ProdukImage::class, 'produk_id');
}
public function kategori()
{
return $this->belongsTo(Kategori::class, 'kategori_id');
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ProdukImage extends Model
{
use HasFactory;
protected $fillable = [
'produk_id',
'foto'
];
public function produk()
{
return $this->belongsTo(Produk::class, 'produk_id');
}
}

View File

@ -13,6 +13,7 @@ class Transaksi extends Model
protected $fillable = [
'pembeli_id',
'petani_id',
'tanggal_transaksi',
'alamat_pengiriman',
'total_harga',
@ -25,7 +26,12 @@ public function pembeli()
return $this->belongsTo(Pembeli::class, 'pembeli_id');
}
public function details()
public function petani()
{
return $this->belongsTo(Petani::class, 'petani_id');
}
public function detailTransaksis()
{
return $this->hasMany(DetailTransaksi::class, 'transaksi_id');
}

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::create('kategoris', function (Blueprint $table) {
$table->id();
$table->string('nama_kategori');
$table->string('slug')->unique();
$table->string('ikon')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('kategoris');
}
};

View File

@ -14,8 +14,8 @@ public function up(): void
Schema::create('produks', function (Blueprint $table) {
$table->id();
$table->foreignId('petani_id')->constrained('petanis')->onDelete('cascade');
$table->foreignId('kategori_id')->nullable()->constrained('kategoris')->onDelete('set null');
$table->string('nama_produk');
$table->string('kategori')->default('Lainnya');
$table->decimal('harga', 12, 0);
$table->integer('stok');
$table->text('deskripsi');
@ -31,4 +31,4 @@ public function down(): void
{
Schema::dropIfExists('produks');
}
};
};

View File

@ -10,20 +10,21 @@
* Run the migrations.
*/
public function up(): void
{
Schema::create('transaksis', function (Blueprint $table) {
$table->id();
$table->foreignId('pembeli_id')->constrained('pembelis')->onDelete('cascade');
$table->dateTime('tanggal_transaksi');
$table->text('alamat_pengiriman');
$table->decimal('total_harga', 15, 0);
$table->enum('status', ['menunggu_konfirmasi', 'diproses', 'dikirim', 'selesai', 'batal'])
->default('menunggu_konfirmasi');
$table->string('kode_invoice')->unique()->nullable();
$table->timestamps();
});
}
{
Schema::create('transaksis', function (Blueprint $table) {
$table->id();
$table->foreignId('pembeli_id')->constrained('pembelis')->onDelete('cascade');
$table->foreignId('petani_id')->constrained('petanis')->onDelete('cascade');
$table->dateTime('tanggal_transaksi');
$table->text('alamat_pengiriman');
$table->decimal('total_harga', 15, 0);
$table->enum('status', ['menunggu konfirmasi', 'diproses', 'dikirim', 'selesai', 'batal'])
->default('menunggu konfirmasi');
$table->string('kode_invoice')->unique()->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.

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
{
/**
* Run the migrations.
*/
public function up()
{
Schema::create('produk_images', function (Blueprint $table) {
$table->id();
$table->foreignId('produk_id')->constrained('produks')->onDelete('cascade');
$table->string('foto');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('produk_images');
}
};

View File

@ -13,6 +13,19 @@ class PetaniSeeder extends Seeder
*/
public function run(): void
{
// Petani SUDAH AKTIF -> Bisa login & jualan
DB::table('petanis')->insert([
'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([
'nama_lengkap' => 'Budi Santoso',
@ -27,19 +40,6 @@ public function run(): void
'updated_at' => now(),
]);
// Petani SUDAH AKTIF -> Bisa login & jualan
DB::table('petanis')->insert([
'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 DITOLAK
DB::table('petanis')->insert([

View File

@ -3,7 +3,11 @@
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use App\Models\Produk;
use App\Models\ProdukImage;
use App\Models\Kategori;
use App\Models\Petani;
use Illuminate\Support\Str;
class ProdukSeeder extends Seeder
{
@ -12,49 +16,105 @@ class ProdukSeeder extends Seeder
*/
public function run(): void
{
$data = [
[
'petani_id' => 2,
'nama_produk' => 'Cabai Merah Keriting Segar',
'harga' => 45000,
'stok' => 50,
'deskripsi' => 'Cabai merah keriting hasil panen pagi ini. Pedas mantap, cocok untuk sambal. Bebas pestisida kimia.',
'foto_produk' => null,
'created_at' => now(),
'updated_at' => now(),
],
[
'petani_id' => 2,
'nama_produk' => 'Tomat Buah Organik',
'harga' => 12000,
'stok' => 100,
'deskripsi' => 'Tomat buah ukuran besar, merah merona. Sangat segar untuk jus atau lalapan.',
'foto_produk' => null,
'created_at' => now(),
'updated_at' => now(),
],
[
'petani_id' => 2,
'nama_produk' => 'Sawi Hijau (Caisim)',
'harga' => 5000,
'stok' => 25,
'deskripsi' => 'Sawi hijau segar, satu ikat besar kira-kira 500gram. Renyah dan tidak pahit.',
'foto_produk' => null,
'created_at' => now(),
'updated_at' => now(),
],
[
'petani_id' => 2,
'nama_produk' => 'Wortel Brastagi Super',
'harga' => 15000,
'stok' => 40,
'deskripsi' => 'Wortel impor lokal kualitas super. Manis, renyah, dan warna oranye pekat.',
'foto_produk' => null,
'created_at' => now(),
'updated_at' => now(),
]
$petani = Petani::inRandomOrder()->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'],
['nama' => 'Benih Padi Unggul', 'slug' => 'benih-padi', 'ikon' => 'fas fa-leaf'],
['nama' => 'Padi Ketan', 'slug' => 'padi-ketan', 'ikon' => 'fas fa-box'],
['nama' => 'Padi Organik', 'slug' => 'padi-organik', 'ikon' => 'fas fa-heart'],
];
DB::table('produks')->insert($data);
foreach ($kategoriGabah as $k) {
Kategori::firstOrCreate(
['slug' => $k['slug']],
[
'nama_kategori' => $k['nama'],
'ikon' => $k['ikon']
]
);
}
// DAFTAR PRODUK
$daftarProduk = [
[
'nama' => 'Gabah Ciherang (GKP) Baru Panen',
'harga' => 5200,
'stok' => 2000,
'deskripsi' => 'Gabah varietas Ciherang kondisi Kering Panen (GKP). Baru dipotong pagi ini. Bulir kuning bersih, hampa rendah. Lokasi sawah pinggir jalan raya, akses truk mudah.',
'kategori_slug' => 'gkp',
'gallery' => 2
],
[
'nama' => 'Gabah IR-64 Siap Giling (GKG)',
'harga' => 7500,
'stok' => 500,
'deskripsi' => 'Gabah IR-64 kondisi Kering Giling (GKG). Kadar air sudah dibawah 14%, siap masuk huller/penggilingan. Dijamin rendemen beras tinggi.',
'kategori_slug' => 'gkg',
'gallery' => 3
],
[
'nama' => 'Benih Padi Inpari 32 Bersertifikat',
'harga' => 15000,
'stok' => 50,
'deskripsi' => 'Benih padi varietas Inpari 32 label ungu. Tahan wereng dan penyakit hawar daun. Potensi hasil panen hingga 8 ton/hektar.',
'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,
]);
for ($i = 0; $i < $item['gallery']; $i++) {
ProdukImage::create([
'produk_id' => $produk->id,
'foto' => 'dummy/gabah/sample_' . $i . '.jpg',
]);
}
}
}
}

BIN
public/images/banner.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

View File

@ -4,123 +4,158 @@
@section('page-title', 'Overview Sistem')
@section('content')
<section class="row">
<div class="col-12">
<div class="row">
{{-- Statistik Petani Aktif --}}
<div class="col-6 col-lg-3 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4">
<div class="stats-icon purple"><i class="bi bi-people-fill"></i></div>
<section class="row">
<div class="col-12">
<div class="row">
{{-- Statistik Petani Aktif --}}
<div class="col-6 col-lg-3 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4 d-flex justify-content-center align-items-center">
<div class="stats-icon purple"><i class="bi bi-people-fill"></i></div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Petani Aktif</h6>
<h6 class="font-extrabold mb-0">{{ $totalPetani }}</h6>
</div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Petani Aktif</h6>
<h6 class="font-extrabold mb-0">{{ $totalPetani }}</h6>
</div>
</div>
</div>
{{-- Statistik Menunggu Verifikasi --}}
<div class="col-6 col-lg-3 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4 d-flex justify-content-center align-items-center">
<div class="stats-icon red"><i class="bi bi-person-plus-fill"></i></div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Verifikasi Pending</h6>
<h6 class="font-extrabold mb-0">{{ $petaniPending }}</h6>
</div>
</div>
</div>
</div>
</div>
{{-- Statistik Total Produk --}}
<div class="col-6 col-lg-3 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4 d-flex justify-content-center align-items-center">
<div class="stats-icon green"><i class="bi bi-basket-fill"></i></div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Total Produk</h6>
<h6 class="font-extrabold mb-0">{{ $totalProduk }}</h6>
</div>
</div>
</div>
</div>
</div>
{{-- Statistik Total Transaksi --}}
<div class="col-6 col-lg-3 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4 d-flex justify-content-center align-items-center">
<div class="stats-icon blue"><i class="bi bi-receipt"></i></div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Total Transaksi</h6>
<h6 class="font-extrabold mb-0">{{ $totalTransaksi }}</h6>
</div>
</div>
</div>
</div>
</div>
</div>
{{-- Statistik Menunggu Verifikasi --}}
<div class="col-6 col-lg-3 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4">
<div class="stats-icon red"><i class="bi bi-person-plus-fill"></i></div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Verifikasi Pending</h6>
<h6 class="font-extrabold mb-0">{{ $petaniPending }}</h6>
</div>
{{-- Tabel Ringkasan Transaksi --}}
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h4>Transaksi Terbaru di Platform</h4>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Invoice</th>
<th>Petani / Usaha</th>
<th>Pembeli</th>
<th>Total</th>
<th>Status</th>
<th>Tanggal</th>
</tr>
</thead>
<tbody>
@forelse($transaksiTerbaru as $trx)
<tr>
<td class="fw-bold text-primary">#{{ $trx->kode_invoice }}</td>
<td>
<div class="d-flex flex-column">
<span class="fw-bold text-dark">
{{ $trx->petani->nama_lengkap ?? 'Petani Tidak Ditemukan' }}
</span>
<small class="text-muted" style="font-size: 0.85em;">
<i class="bi bi-shop me-1"></i>
{{ $trx->petani->nama_usaha ?? '-' }}
</small>
</div>
</td>
{{-- Statistik Total Produk --}}
<div class="col-6 col-lg-3 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4">
<div class="stats-icon green"><i class="bi bi-basket-fill"></i></div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Total Produk</h6>
<h6 class="font-extrabold mb-0">{{ $totalProduk }}</h6>
</div>
</div>
</div>
</div>
</div>
<td>{{ $trx->pembeli->nama_lengkap ?? 'Guest' }}</td>
{{-- Statistik Total Transaksi --}}
<div class="col-6 col-lg-3 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4">
<div class="stats-icon blue"><i class="bi bi-receipt"></i></div>
<td class="fw-bold">Rp {{ number_format($trx->total_harga, 0, ',', '.') }}</td>
<td>
@php
$badgeClass = match ($trx->status) {
'selesai' => 'bg-success',
'batal' => 'bg-danger',
'dikirim' => 'bg-primary',
'diproses' => 'bg-info',
'menunggu konfirmasi' => 'bg-warning text-dark',
default => 'bg-secondary',
};
@endphp
<span class="badge {{ $badgeClass }} rounded-pill px-3">
{{ ucwords($trx->status) }}
</span>
</td>
<td class="text-muted small">{{ $trx->created_at->format('d M Y') }}</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center py-4 text-muted">
<div class="d-flex flex-column align-items-center">
<i class="bi bi-inbox fs-1 mb-2"></i>
Belum ada transaksi baru.
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Total Transaksi</h6>
<h6 class="font-extrabold mb-0">{{ $totalTransaksi }}</h6>
<div class="text-center mt-3">
<a href="{{ route('admin.monitoring') }}" class="btn btn-sm btn-primary">
Lihat Semua Data
</a>
</div>
</div>
</div>
</div>
</div>
</div>
{{-- Tabel Ringkasan Transaksi Terbaru --}}
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h4>Transaksi Terbaru di Platform</h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Invoice</th>
<th>Pembeli</th>
<th>Total</th>
<th>Status</th>
<th>Tanggal</th>
</tr>
</thead>
<tbody>
@forelse($transaksiTerbaru as $trx)
<tr>
<td>#{{ $trx->kode_invoice }}</td>
<td>{{ $trx->pembeli->nama_lengkap }}</td>
<td>Rp {{ number_format($trx->total_harga, 0, ',', '.') }}</td>
<td>
<span class="badge bg-{{ $trx->status == 'selesai' ? 'success' : ($trx->status == 'batal' ? 'danger' : 'warning') }}">
{{ ucfirst($trx->status) }}
</span>
</td>
<td>{{ $trx->created_at->diffForHumans() }}</td>
</tr>
@empty
<tr><td colspan="5" class="text-center">Belum ada transaksi.</td></tr>
@endforelse
</tbody>
</table>
</div>
<div class="text-center mt-3">
<a href="{{ route('admin.monitoring') }}" class="btn btn-sm btn-primary">Lihat Semua Data</a>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</section>
@endsection

View File

@ -0,0 +1,44 @@
@extends('layouts.admin')
@section('title', 'Tambah Kategori')
@section('page-title', 'Tambah Kategori Baru')
@section('content')
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card border-0 shadow-sm">
<div class="card-body p-4">
<form action="{{ route('admin.kategori.store') }}" method="POST">
@csrf
<div class="mb-3">
<label class="form-label fw-bold">Nama Kategori <span class="text-danger">*</span></label>
<input type="text" name="nama_kategori"
class="form-control @error('nama_kategori') is-invalid @enderror"
placeholder="Contoh: Beras, Sayuran, Buah" value="{{ old('nama_kategori') }}" required>
@error('nama_kategori')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-4">
<label class="form-label fw-bold">Kode Ikon (FontAwesome/Bootstrap)</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-emoji-smile"></i></span>
<input type="text" name="ikon" class="form-control" placeholder="Contoh: fas fa-carrot"
value="{{ old('ikon') }}">
</div>
<div class="form-text small">Opsional. Gunakan class dari FontAwesome atau Bootstrap Icons.
</div>
</div>
<div class="d-flex justify-content-end gap-2">
<a href="{{ route('admin.kategori.index') }}" class="btn btn-light border px-4">Batal</a>
<button type="submit" class="btn btn-primary px-4">Simpan Kategori</button>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,45 @@
@extends('layouts.admin')
@section('title', 'Edit Kategori')
@section('page-title', 'Edit Kategori')
@section('content')
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card border-0 shadow-sm">
<div class="card-body p-4">
<form action="{{ route('admin.kategori.update', $kategori->id) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-3">
<label class="form-label fw-bold">Nama Kategori <span class="text-danger">*</span></label>
<input type="text" name="nama_kategori"
class="form-control @error('nama_kategori') is-invalid @enderror"
value="{{ old('nama_kategori', $kategori->nama_kategori) }}" required>
@error('nama_kategori')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-4">
<label class="form-label fw-bold">Kode Ikon</label>
<div class="input-group">
<span class="input-group-text">
<i class="{{ $kategori->ikon ?? 'bi bi-emoji-smile' }}"></i>
</span>
<input type="text" name="ikon" class="form-control" placeholder="Contoh: fas fa-carrot"
value="{{ old('ikon', $kategori->ikon) }}">
</div>
</div>
<div class="d-flex justify-content-end gap-2">
<a href="{{ route('admin.kategori.index') }}" class="btn btn-light border px-4">Batal</a>
<button type="submit" class="btn btn-success px-4">Update Kategori</button>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,76 @@
@extends('layouts.admin')
@section('title', 'Master Kategori')
@section('page-title', 'Kelola Kategori Produk')
@section('content')
<div class="card border-0 shadow-sm">
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-bold">Daftar Kategori</h5>
<a href="{{ route('admin.kategori.create') }}" class="btn btn-primary btn-sm rounded-pill px-4">
<i class="bi bi-plus-lg me-1"></i> Tambah Baru
</a>
</div>
<div class="card-body">
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="bg-light">
<tr>
<th width="5%">No</th>
<th>Ikon</th>
<th>Nama Kategori</th>
<th>Slug</th>
<th>Jumlah Produk</th>
<th width="15%" class="text-center">Aksi</th>
</tr>
</thead>
<tbody>
@forelse($kategoris as $index => $kat)
<tr>
<td>{{ $loop->iteration }}</td>
<td>
@if($kat->ikon)
<i class="{{ $kat->ikon }} fs-5 text-secondary"></i>
@else
<span class="text-muted">-</span>
@endif
</td>
<td class="fw-bold">{{ $kat->nama_kategori }}</td>
<td><code class="text-primary">{{ $kat->slug }}</code></td>
<td>
<span class="badge bg-info bg-opacity-10 text-info rounded-pill px-3">
{{ $kat->produks_count }} Produk
</span>
</td>
<td class="text-center">
<a href="{{ route('admin.kategori.edit', $kat->id) }}" class="btn btn-sm btn-warning me-1">
<i class="bi bi-pencil"></i>
</a>
<form action="{{ route('admin.kategori.destroy', $kat->id) }}" method="POST"
class="d-inline"
onsubmit="return confirm('Yakin hapus kategori ini? Produk terkait akan kehilangan kategorinya.')">
@csrf @method('DELETE')
<button type="submit" class="btn btn-sm btn-danger">
<i class="bi bi-trash"></i>
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center py-5 text-muted">Belum ada kategori data.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection

View File

@ -4,75 +4,122 @@
@section('page-title', 'Monitoring Data')
@section('content')
<div class="row">
{{-- TABEL SEMUA TRANSAKSI --}}
<div class="col-12 mb-4">
<div class="card">
<div class="card-header">
<h4>Semua Transaksi</h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Invoice</th>
<th>Pembeli</th>
<th>Total</th>
<th>Status</th>
<th>Waktu</th>
</tr>
</thead>
<tbody>
@foreach($transaksis as $t)
<tr>
<td>{{ $t->kode_invoice }}</td>
<td>{{ $t->pembeli->nama_lengkap }}</td>
<td>Rp {{ number_format($t->total_harga, 0, ',', '.') }}</td>
<td>{{ strtoupper($t->status) }}</td>
<td>{{ $t->created_at->format('d M Y H:i') }}</td>
</tr>
@endforeach
</tbody>
</table>
<div class="row">
{{-- TABEL SEMUA TRANSAKSI --}}
<div class="col-12 mb-4">
<div class="card">
<div class="card-header">
<h4>Semua Transaksi</h4>
</div>
{{ $transaksis->links() }}
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Invoice</th>
<th>Petani / Usaha</th>
<th>Pembeli</th>
<th>Total</th>
<th>Status</th>
<th>Tanggal</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($transaksis as $trx)
<tr>
<td class="fw-bold text-primary">#{{ $trx->kode_invoice }}</td>
<td>
<div class="d-flex flex-column">
<span class="fw-bold text-dark">
{{ $trx->petani->nama_lengkap ?? 'Petani Tidak Ditemukan' }}
</span>
<small class="text-muted" style="font-size: 0.85em;">
<i class="bi bi-shop me-1"></i>
{{ $trx->petani->nama_usaha ?? '-' }}
</small>
</div>
</td>
{{-- TABEL SEMUA PRODUK --}}
<div class="col-12">
<div class="card">
<div class="card-header">
<h4>Semua Produk Terdaftar</h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Nama Produk</th>
<th>Pemilik (Petani)</th>
<th>Harga</th>
<th>Stok</th>
</tr>
</thead>
<tbody>
@foreach($produks as $p)
<tr>
<td>{{ $p->nama_produk }}</td>
<td>{{ $p->petani->nama_lengkap }}</td>
<td>Rp {{ number_format($p->harga, 0, ',', '.') }}</td>
<td>{{ $p->stok }}</td>
</tr>
@endforeach
</tbody>
</table>
<td>{{ $trx->pembeli->nama_lengkap ?? 'Guest' }}</td>
<td class="fw-bold">Rp {{ number_format($trx->total_harga, 0, ',', '.') }}</td>
<td>
@php
$badgeClass = match ($trx->status) {
'selesai' => 'bg-success',
'batal' => 'bg-danger',
'dikirim' => 'bg-primary',
'diproses' => 'bg-info',
'menunggu konfirmasi' => 'bg-warning text-dark',
default => 'bg-secondary',
};
@endphp
<span class="badge {{ $badgeClass }} rounded-pill px-3">
{{ ucwords($trx->status) }}
</span>
</td>
<td class="text-muted small">{{ $trx->created_at->format('d M Y') }}</td>
<td>
<a href="{{ route('admin.transaksi.detail', $trx->id) }}"
class="btn btn-sm btn-info text-white" data-bs-toggle="tooltip"
title="Lihat Detail">
<i class="bi bi-eye"></i> Detail
</a>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center py-4 text-muted">
<div class="d-flex flex-column align-items-center">
<i class="bi bi-inbox fs-1 mb-2"></i>
Belum ada transaksi baru.
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
{{-- TABEL SEMUA PRODUK --}}
<div class="col-12">
<div class="card">
<div class="card-header">
<h4>Semua Produk Terdaftar</h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Nama Produk</th>
<th>Pemilik (Petani)</th>
<th>Harga</th>
<th>Stok</th>
</tr>
</thead>
<tbody>
@foreach($produks as $p)
<tr>
<td>{{ $p->nama_produk }}</td>
<td>{{ $p->petani->nama_lengkap }}</td>
<td>Rp {{ number_format($p->harga, 0, ',', '.') }}</td>
<td>{{ $p->stok }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
{{ $produks->links() }}
</div>
{{ $produks->links() }}
</div>
</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,170 @@
@extends('layouts.admin')
@section('title', 'Detail Transaksi')
@section('content')
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h3 class="mb-0">Detail Transaksi</h3>
<span class="text-muted">Invoice: #{{ $transaksi->kode_invoice }}</span>
</div>
<a href="{{ route('admin.monitoring') }}" class="btn btn-secondary">
<i class="bi bi-arrow-left"></i> Kembali
</a>
</div>
<div class="row">
<div class="col-md-8">
<div class="card mb-4 border-0 shadow-sm" style="border-radius: 12px; overflow: hidden;">
{{-- Header Card yang Bersih --}}
<div class="card-header bg-white py-3 border-bottom">
<h5 class="mb-0 fw-bold text-dark" style="font-size: 1.1rem;">
<i class="bi bi-cart3 me-2 text-primary"></i>Daftar Produk
</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table align-middle mb-0">
<thead style="background-color: #f8f9fa;">
<tr>
<th scope="col" class="ps-4 py-3 text-secondary small text-uppercase fw-bold"
style="width: 45%;">Produk</th>
<th scope="col" class="text-end py-3 text-secondary small text-uppercase fw-bold">
Harga</th>
<th scope="col"
class="text-center py-3 text-secondary small text-uppercase fw-bold">Qty</th>
<th scope="col"
class="text-end pe-4 py-3 text-secondary small text-uppercase fw-bold">Subtotal
</th>
</tr>
</thead>
<tbody>
@foreach($transaksi->detailTransaksis as $detail)
<tr>
<td class="ps-4 py-3">
<div class="d-flex align-items-center">
<div class="me-3">
<img src="{{ $detail->produk->foto_produk ? asset('storage/' . $detail->produk->foto_produk) : 'https://images.unsplash.com/photo-1586201375761-83865001e31c?q=80&w=200&auto=format&fit=crop' }}"
alt="{{ $detail->produk->nama_produk }}" class="rounded-3 shadow-sm"
style="width: 55px; height: 55px; object-fit: cover;">
</div>
{{-- Detail Nama & Badge --}}
<div>
<div class="fw-bold text-dark mb-1">{{ $detail->produk->nama_produk }}
</div>
{{-- Badge Kategori --}}
@php
$kategori = $detail->produk->kategori->nama_kategori ?? 'Umum';
$badgeColor = match ($kategori) {
default => 'bg-primary bg-opacity-10 text-primary'
};
@endphp
<span
class="badge {{ $badgeColor }} border border-opacity-10 rounded-pill px-2">
{{ $kategori }}
</span>
</div>
</div>
</td>
{{-- Harga Satuan --}}
<td class="text-end text-muted">
<small>Rp {{ number_format($detail->harga_satuan, 0, ',', '.') }}</small>
</td>
{{-- Qty --}}
<td class="text-center">
<span class="badge bg-light text-dark border px-2 py-1 rounded-pill">
x{{ $detail->jumlah }}
</span>
</td>
{{-- Subtotal --}}
<td class="text-end fw-bold pe-4 text-dark">
Rp {{ number_format($detail->subtotal, 0, ',', '.') }}
</td>
</tr>
@endforeach
</tbody>
{{-- Footer Total --}}
<tfoot class="bg-white">
<tr>
<td colspan="4" class="p-0">
<div class="d-flex justify-content-end align-items-center p-4"
style="background: linear-gradient(to right, #fff, #f8f9fa);">
<div class="text-end">
<span class="text-muted d-block small mb-1">Total Transaksi</span>
<h4 class="fw-bolder text-primary mb-0">
Rp {{ number_format($transaksi->total_harga, 0, ',', '.') }}
</h4>
</div>
</div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
{{-- Info Pihak Terkait --}}
<div class="col-md-4">
{{-- Info Status --}}
<div class="card mb-3">
<div class="card-body">
<h6 class="card-title text-muted">Status Pesanan</h6>
@php
$badgeClass = match ($transaksi->status) {
'selesai' => 'bg-success',
'batal' => 'bg-danger',
'dikirim' => 'bg-primary',
'diproses' => 'bg-info',
'menunggu konfirmasi' => 'bg-warning text-dark',
default => 'bg-secondary',
};
@endphp
<span class="badge {{ $badgeClass }} fs-6 w-100 py-2">
{{ ucwords($transaksi->status) }}
</span>
<small class="text-muted d-block mt-2 text-center">
Tanggal: {{ $transaksi->created_at->format('d M Y H:i') }}
</small>
</div>
</div>
{{-- Info Pembeli --}}
<div class="card mb-3">
<div class="card-header bg-white">
<i class="bi bi-person-circle me-1"></i> Data Pembeli
</div>
<div class="card-body">
<h6 class="fw-bold">{{ $transaksi->pembeli->nama_lengkap ?? 'Guest' }}</h6>
<p class="mb-1 small text-muted"><i class="bi bi-telephone"></i>
{{ $transaksi->pembeli->no_hp ?? '-' }}</p>
<hr>
<small class="text-muted">Alamat Pengiriman:</small>
<p class="mb-0 small">{{ $transaksi->alamat_pengiriman }}</p>
</div>
</div>
{{-- Info Petani --}}
<div class="card">
<div class="card-header bg-white">
<i class="bi bi-shop me-1"></i> Data Penjual (Petani)
</div>
<div class="card-body">
<h6 class="fw-bold">{{ $transaksi->petani->nama_lengkap ?? 'Tidak Diketahui' }}</h6>
<p class="mb-0 small text-muted">{{ $transaksi->petani->nama_usaha ?? '-' }}</p>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Masuk - TaniDesa</title>
<title>Masuk - GriyaPadi.id</title>
<link href="{{ asset('template/frontend/css/bootstrap.min.css') }}" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" />
@ -23,7 +23,7 @@
<div class="card-body">
<div class="text-center mb-4">
<a href="{{ url('/') }}" class="text-decoration-none">
<h2 class="fw-bold text-tani"><i class="fas fa-leaf me-2"></i>TaniDesa</h2>
<h2 class="fw-bold text-tani"><i class="fas fa-leaf me-2"></i>GriyaPadi.id</h2>
</a>
<p class="text-muted small">Masuk untuk melanjutkan</p>
</div>
@ -58,7 +58,7 @@
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-tani rounded-pill py-2">MASUK</button>
<button type="submit" class="btn btn-tani rounded-pill py-2">Login</button>
</div>
</form>

View File

@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Daftar - TaniDesa</title>
<title>Daftar - GriyaPadi.id</title>
<link href="{{ asset('template/frontend/css/bootstrap.min.css') }}" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" />
@ -82,7 +82,7 @@
<div class="card-body">
<div class="text-center mb-4">
<h2 class="fw-bold text-tani">Daftar Akun</h2>
<p class="text-muted small">Gabung komunitas TaniDesa</p>
<p class="text-muted small">Gabung komunitas GriyaPadi.id</p>
</div>
<form action="{{ route('register.proses') }}" method="POST">
@ -150,7 +150,7 @@
</div>
<div class="d-grid mt-4">
<button type="submit" class="btn btn-tani rounded-pill py-2">DAFTAR SEKARANG</button>
<button type="submit" class="btn btn-tani rounded-pill py-2">Register</button>
</div>
</form>

View File

@ -2,7 +2,7 @@
<html lang="id">
<head>
<title>Reset Password - TaniDesa</title>
<title>Reset Password - GriyaPadi.id</title>
<link href="{{ asset('template/frontend/css/bootstrap.min.css') }}" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" />
<style>

View File

@ -47,25 +47,32 @@ class="rounded-3 me-3"
</div>
</td>
<td class="text-muted">Rp
{{ number_format($details['price'], 0, ',', '.') }}</td>
<td>
<div class="input-group quantity" style="width: 100px;">
<button class="btn btn-sm btn-light border btn-minus-custom">
<i class="fa fa-minus x-small"></i>
{{ number_format($details['price'], 0, ',', '.') }}
</td>
<td class="align-middle text-center">
<div class="input-group input-group-sm flex-nowrap mx-auto shadow-sm"
style="width: 120px;">
{{-- Tombol Minus --}}
<button class="btn btn-light border btn-minus-custom" type="button">
<i class="fas fa-minus text-secondary" style="font-size: 0.8rem;"></i>
</button>
{{-- Input Jumlah --}}
<input type="text"
class="form-control form-control-sm text-center border-0 qty-input bg-white"
value="{{ $details['quantity'] }}" readonly>
<button class="btn btn-sm btn-light border btn-plus-custom">
<i class="fa fa-plus x-small"></i>
class="form-control text-center border-top border-bottom border-light bg-white qty-input px-0"
value="{{ $details['quantity'] }}" readonly style="min-width: 40px;">
{{-- Tombol Plus --}}
<button class="btn btn-light border btn-plus-custom" type="button">
<i class="fas fa-plus text-secondary" style="font-size: 0.8rem;"></i>
</button>
</div>
</td>
<td class="fw-bold text-dark">Rp {{ number_format($total, 0, ',', '.') }}
</td>
<td class="pe-4 text-end">
<form action="{{ route('cart.remove') }}" method="POST"
class="d-inline">
<form action="{{ route('cart.remove') }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<input type="hidden" name="id" value="{{ $id }}">
@ -97,12 +104,10 @@ class="d-inline">
<span class="fw-bold fs-5 text-primary">Rp
{{ number_format($total_belanja, 0, ',', '.') }}</span>
</div>
<a href="{{ route('checkout') }}"
class="btn btn-primary w-100 rounded-pill py-3 fw-bold shadow-sm">
<a href="{{ route('checkout') }}" class="btn btn-primary w-100 rounded-pill py-3 fw-bold shadow-sm">
Checkout Sekarang
</a>
<a href="{{ route('shop') }}"
class="btn btn-link text-muted w-100 mt-2 text-decoration-none small">
<a href="{{ route('shop') }}" class="btn btn-link text-muted w-100 mt-2 text-decoration-none small">
Lanjut Belanja
</a>
</div>
@ -126,10 +131,10 @@ class="btn btn-link text-muted w-100 mt-2 text-decoration-none small">
@section('js')
<script>
$(document).ready(function() {
$(document).ready(function () {
$(".quantity button").off("click");
$(".btn-plus-custom").click(function(e) {
$(".btn-plus-custom").click(function (e) {
e.preventDefault();
var ele = $(this);
var input = ele.closest("tr").find(".qty-input");
@ -139,7 +144,7 @@ class="btn btn-link text-muted w-100 mt-2 text-decoration-none small">
updateCart(ele.closest("tr").attr("data-id"), newVal);
});
$(".btn-minus-custom").click(function(e) {
$(".btn-minus-custom").click(function (e) {
e.preventDefault();
var ele = $(this);
var input = ele.closest("tr").find(".qty-input");
@ -160,11 +165,11 @@ function updateCart(id, qty) {
id: id,
quantity: qty
},
success: function(response) {
success: function (response) {
window.location.reload();
}
});
}
});
</script>
@endsection
@endsection

View File

@ -4,136 +4,216 @@
@section('content')
<div class="container-fluid bg-light py-3 mb-4">
{{-- BREADCRUMB --}}
<div class="container-fluid py-3 mb-3">
<div class="container">
<nav aria-label="breadcrumb">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item"><a href="{{ url('/') }}" class="text-muted text-decoration-none">Home</a>
</li>
<li class="breadcrumb-item"><a href="{{ route('shop') }}"
class="text-muted text-decoration-none">Belanja</a></li>
<li class="breadcrumb-item active text-primary fw-bold" aria-current="page">{{ $produk->nama_produk }}
</li>
<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-5">
<div class="col-lg-6">
<div class="rounded-4 overflow-hidden shadow-sm border position-relative bg-white">
<img src="{{ $produk->foto_produk ? asset('storage/' . $produk->foto_produk) : asset('template/frontend/img/fruite-item-5.jpg') }}"
class="img-fluid w-100" style="object-fit: cover; max-height: 500px;"
alt="{{ $produk->nama_produk }}">
<div class="row g-4 g-lg-5">
<div class="position-absolute top-0 start-0 m-3">
@if ($produk->stok > 0)
<span class="badge bg-primary shadow-sm px-3 py-2 rounded-pill">Stok Tersedia</span>
@else
<span class="badge bg-danger shadow-sm px-3 py-2 rounded-pill">Stok Habis</span>
{{-- 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
</div>
</div>
</div>
<div class="col-lg-6">
<div class="h-100 d-flex flex-column justify-content-center">
<div class="mb-2">
{{-- INFO & AKSI --}}
<div class="col-lg-7">
<div class="ps-lg-4">
{{-- Kategori Badge --}}
<div class="mb-3">
<span
class="badge bg-light text-dark border border-secondary text-uppercase px-3 py-2 rounded-pill fw-bold">
{{ $produk->kategori ?? 'Hasil Tani' }}
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>
<h1 class="fw-bold display-6 text-dark mb-2">{{ $produk->nama_produk }}</h1>
{{-- Nama Produk --}}
<h1 class="display-6 fw-bold text-dark mb-2">{{ $produk->nama_produk }}</h1>
<div class="mb-4">
<h2 class="fw-bold text-primary display-5 mb-0">
{{-- 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, ',', '.') }}
<span class="fs-6 text-muted fw-normal">/ kg</span>
</h2>
</h3>
<span class="text-muted fs-5">/ kg</span>
</div>
<p class="text-secondary mb-4" style="line-height: 1.8; font-size: 1.05rem;">
{{ $produk->deskripsi }}
</p>
{{-- 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>
<div class="d-flex align-items-center bg-white border rounded-4 p-3 mb-4 shadow-sm">
<div class="bg-light rounded-circle p-2 me-3 d-flex align-items-center justify-content-center text-primary"
style="width: 50px; height: 50px;">
<i class="fas fa-user-tie fs-4"></i>
{{-- 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="mb-0 text-muted small">Dijual oleh:</p>
<h6 class="mb-0 fw-bold text-dark">{{ $produk->petani->nama_lengkap ?? 'Petani Mitra' }}</h6>
<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-primary btn-sm rounded-pill px-3"
<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="fas fa-comment-dots me-1"></i> Chat
<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="d-flex align-items-center mb-4 flex-wrap gap-3">
<div class="input-group shadow-sm" style="width: 140px;">
<button type="button" class="btn btn-light border" onclick="updateQty(-1)">
<i class="fa fa-minus small"></i>
</button>
<input type="number" name="qty" id="input-qty"
class="form-control text-center bg-white border-top border-bottom border-0"
value="1" min="1" max="{{ $produk->stok }}" readonly>
<button type="button" class="btn btn-light border" onclick="updateQty(1)">
<i class="fa fa-plus small"></i>
</button>
<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>
<span class="text-muted small">
Sisa Stok: <strong class="text-dark">{{ $produk->stok }} Kg</strong>
</span>
</div>
<div class="d-grid gap-2 d-sm-flex">
<button type="submit" class="btn btn-primary btn-lg rounded-pill px-5 shadow-sm flex-grow-1">
<i class="fa fa-shopping-basket me-2"></i> + Keranjang
</button>
<a href="javascript:void(0);" onclick="beliLangsung()"
class="btn btn-outline-secondary btn-lg rounded-pill px-4 flex-grow-1">
Beli Langsung
</a>
{{-- 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>
@if (isset($produk_terkait) && count($produk_terkait) > 0)
<div class="mt-5 pt-5 border-top">
{{-- 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">
<h3 class="fw-bold m-0 text-dark">Produk Sejenis</h3>
<a href="{{ route('shop') }}" class="text-primary text-decoration-none fw-bold small">
Lihat Semua <i class="fas fa-arrow-right ms-1"></i>
<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">
@foreach ($produk_terkait as $related)
@foreach ($produk_terkait as $produk)
<div class="col-6 col-md-3">
<a href="{{ route('produk.detail', $related->id) }}" class="text-decoration-none">
<div class="card h-100 border-0 shadow-sm overflow-hidden rounded-4">
<div class="position-relative">
<img src="{{ $related->foto_produk ? asset('storage/' . $related->foto_produk) : asset('template/frontend/img/fruite-item-5.jpg') }}"
class="card-img-top" style="height: 200px; object-fit: cover;"
alt="{{ $related->nama_produk }}">
<a href="{{ route('produk.detail', $produk->id) }}" class="text-decoration-none">
<div class="card border-0 shadow-sm h-100 product-card">
<!-- Stock Badge -->
<div class="position-absolute top-0 end-0 m-2 z-1">
<span class="badge bg-warning text-dark">
Stok: {{ $produk->stok }} Kg
</span>
</div>
<div class="card-body p-3">
<h6 class="fw-bold text-dark mb-1 text-truncate">{{ $related->nama_produk }}</h6>
<p class="text-primary fw-bold mb-0">Rp
{{ number_format($related->harga, 0, ',', '.') }}</p>
<!-- Image -->
<div class="overflow-hidden" style="height: 200px;">
<img src="{{ $produk->foto_produk ? asset('storage/' . $produk->foto_produk) : 'https://images.unsplash.com/photo-1586201375761-83865001e31c?q=80&w=800&auto=format&fit=crop' }}"
class="card-img-top w-100 h-100" alt="{{ $produk->nama_produk }}"
style="object-fit: cover;">
</div>
<div class="card-body d-flex justify-content-between flex-column p-3">
<div>
<span class="badge bg-success mb-2">{{ $produk->kategori ?? 'Umum' }}</span>
<h6 class="fw-bold mb-2">{{ $produk->nama_produk }}</h6>
<p class="text-muted small mb-3">{{ Str::limit($produk->deskripsi, 60) }}</p>
</div>
<div class="d-flex justify-content-between align-items-center pt-2 border-top">
<div>
<small class="text-muted">Harga/Kg</small>
<h6 class="fw-bold text-success mb-0">
Rp {{ number_format($produk->harga, 0, ',', '.') }}
</h6>
</div>
<a href="{{ route('produk.detail', $produk->id) }}"
class="btn btn-success btn-sm rounded-pill">
<i class="fa fa-eye"></i> Detail
</a>
</div>
</div>
</div>
</a>
@ -144,12 +224,16 @@ class="card-img-top" style="height: 200px; object-fit: cover;"
@endif
</div>
<div class="modal fade" id="chatModal" tabindex="-1">
{{-- 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-4">
<div class="modal-header bg-primary text-white border-0 rounded-top-4">
<h5 class="modal-title fw-bold">Hubungi Petani</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
<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
@ -158,41 +242,75 @@ class="card-img-top" style="height: 200px; object-fit: cover;"
<input type="hidden" name="penerima_type" value="App\Models\Petani">
<div class="mb-3">
<label class="form-label fw-bold small text-muted text-uppercase">Pesan Anda</label>
<textarea name="isi_pesan" class="form-control bg-light border-0 rounded-3 p-3" rows="4" required
placeholder="Halo, saya tertarik dengan produk ini..."></textarea>
<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 shadow-sm">
<i class="fas fa-paper-plane me-2"></i> Kirim Pesan
<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>
// Fungsi untuk update input number
const hargaSatuan = {{ $produk->harga }};
function updateQty(change) {
let input = document.getElementById('input-qty');
let newVal = parseInt(input.value) + change;
let display = document.getElementById('display-qty');
let input = document.getElementById('real-input-qty');
let subtotalEl = document.getElementById('subtotal');
let currentVal = parseInt(input.value);
let newVal = currentVal + change;
let max = parseInt(input.getAttribute('max'));
if (newVal >= 1 && newVal <= max) {
input.value = newVal;
display.value = newVal;
// Update subtotal
let subtotal = newVal * hargaSatuan;
subtotalEl.textContent = 'Rp ' + subtotal.toLocaleString('id-ID');
}
}
// Redirect dengan membawa QTY
function beliLangsung() {
let qty = document.getElementById('input-qty').value;
let qty = document.getElementById('real-input-qty').value;
let produkId = "{{ $produk->id }}";
window.location.href = "{{ route('checkout') }}?produk_id=" + produkId + "&qty=" + qty;
}
// FUNGSI GANTI GAMBAR GALERI
function changeImage(element) {
document.getElementById('main-image').src = element.src;
let thumbnails = document.querySelectorAll('.img-thumbnail');
thumbnails.forEach(img => {
img.classList.remove('border-primary', 'thumbnail-active');
img.classList.add('border-0', 'opacity-75');
img.style.borderWidth = '1px';
});
// Highlight thumbnail yang sedang aktif
element.classList.remove('border-0', 'opacity-75');
element.classList.add('border-primary', 'thumbnail-active');
element.style.borderWidth = '2px';
element.style.opacity = '1';
}
</script>
@endsection
@endsection

View File

@ -4,15 +4,15 @@
@section('content')
<div class="container-fluid py-5 mb-5 hero-header bg-light">
<div class="container py-5">
<div class="row align-items-center g-5">
<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">
<div class="d-inline-block border border-secondary text-secondary rounded-pill px-3 py-2 mb-3 fw-bold">
🌾 Mitra Petani Padi Indonesia
</div>
<h1 class="display-4 fw-bold text-dark mb-4">
Jual Beli Gabah & <br>
Jual Gabah & <br>
<span class="text-primary">Beras Berkualitas.</span>
</h1>
<p class="lead text-muted mb-5">
@ -30,7 +30,7 @@
</div>
<div class="col-lg-6 order-1 order-lg-2 text-center">
<div class="position-relative d-inline-block">
<img src="https://images.unsplash.com/photo-1586201375761-83865001e31c?q=80&w=1000&auto=format&fit=crop"
<img src="{{ asset('images/banner.jpg') }}"
class="img-fluid rounded-circle shadow-lg w-100" style="max-height: 450px; object-fit: cover;"
alt="Padi Premium">
<div class="position-absolute bg-white p-3 rounded-3 shadow d-none d-md-block"

View File

@ -2,33 +2,166 @@
@section('title', 'Chat dengan ' . $lawan->nama_lengkap)
@section('content')
<div class="container py-5">
<div class="row g-0 chat-container">
<style>
.chat-layout {
height: 80vh;
background-color: #fff;
border: 1px solid #e9ecef;
border-radius: 12px;
overflow: hidden;
}
<div class="col-md-4 chat-sidebar d-none d-md-block">
<div class="p-3 bg-white sticky-top border-bottom">
<input type="text" class="form-control rounded-pill" placeholder="Cari pesan...">
/* --- Sidebar Kiri (List User) --- */
.chat-sidebar {
background-color: #fff;
border-right: 1px solid #f0f0f0;
height: 100%;
display: flex;
flex-direction: column;
}
.chat-item {
transition: all 0.2s;
border-bottom: 1px solid #f8f9fa;
}
.chat-item:hover,
.chat-item.active {
background-color: #f9fdf0;
border-left: 4px solid #81c408;
}
/* --- Area Chat Kanan --- */
.chat-main {
background-color: #ffffff;
display: flex;
flex-direction: column;
height: 100%;
}
.chat-header {
padding: 15px 20px;
background: #fff;
border-bottom: 1px solid #f0f0f0;
}
.chat-content {
flex-grow: 1;
overflow-y: auto;
padding: 25px;
background-color: #fafafa;
display: flex;
flex-direction: column;
gap: 15px;
}
/* --- Bubble Chat Style --- */
.chat-bubble {
max-width: 75%;
padding: 12px 18px;
position: relative;
font-size: 0.95rem;
line-height: 1.5;
border-radius: 18px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.03);
}
/* Pesan SAYA */
.bubble-me {
background-color: #81c408;
color: #ffffff;
align-self: flex-end;
border-bottom-right-radius: 4px;
}
/* Pesan LAWAN */
.bubble-other {
background-color: #f1f3f5;
color: #333333;
align-self: flex-start;
border-bottom-left-radius: 4px;
}
.chat-time {
font-size: 0.7rem;
margin-top: 4px;
display: block;
text-align: right;
opacity: 0.7;
}
.chat-footer {
padding: 15px 20px;
background: #fff;
border-top: 1px solid #f0f0f0;
}
.btn-send {
background-color: #81c408;
color: white;
border: none;
transition: 0.3s;
}
.btn-send:hover {
background-color: #6da705;
color: white;
}
::-webkit-scrollbar {
width: 5px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #ddd;
border-radius: 10px;
}
</style>
<div class="container py-5">
<div class="row g-0 chat-layout shadow-sm">
{{-- SIDEBAR KIRI --}}
<div class="col-md-4 chat-sidebar d-none d-md-flex">
{{-- 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" placeholder="Cari percakapan...">
</div>
<div class="list-group list-group-flush">
{{-- List Chat --}}
<div class="flex-grow-1 overflow-auto">
@foreach ($chatList as $chat)
<a href="{{ route('pembeli.pesan.show', $chat['lawan_id']) }}"
class="list-group-item list-group-item-action chat-list-item py-3 {{ $chat['lawan_id'] == $lawan->id ? 'active' : '' }}">
<div class="d-flex align-items-center">
<img src="{{ asset('template/frontend/img/avatar.jpg') }}" class="rounded-circle"
width="50" height="50" style="object-fit: cover;">
<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 fw-bold {{ $chat['lawan_id'] == $lawan->id ? 'text-dark' : 'text-dark' }}">
{{ $chat['nama'] }}</h6>
<small
class="{{ $chat['lawan_id'] == $lawan->id ? 'text-muted' : 'text-muted' }}">{{ $chat['time'] }}</small>
</div>
<p
class="mb-0 small text-truncate {{ $chat['lawan_id'] == $lawan->id ? 'text-dark' : 'text-muted' }}">
{{ Str::limit($chat['last_message'], 30) }}
class="d-flex align-items-center p-3 text-decoration-none text-dark chat-item {{ $chat['lawan_id'] == $lawan->id ? 'active' : '' }}">
{{-- Avatar --}}
<div class="position-relative">
<img src="{{ asset('template/frontend/img/avatar.jpg') }}" class="rounded-circle" width="45"
height="45" style="object-fit: cover;">
@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>
<div class="ms-3 flex-grow-1 overflow-hidden">
<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>
<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>
@if($chat['unread'] > 0)
<span class="badge bg-danger rounded-pill"
style="font-size: 0.6rem;">{{ $chat['unread'] }}</span>
@endif
</div>
</div>
</a>
@ -36,70 +169,80 @@ class="mb-0 small text-truncate {{ $chat['lawan_id'] == $lawan->id ? 'text-dark'
</div>
</div>
<div class="col-md-8 chat-content">
{{-- AREA CHAT KANAN --}}
<div class="col-md-8 chat-main">
{{-- Chat Header --}}
<div class="chat-header d-flex align-items-center bg-white shadow-sm">
<a href="{{ route('pembeli.pesan.index') }}" class="d-md-none me-3 text-dark">
<i class="fas fa-arrow-left"></i>
</a>
{{-- Header Chat --}}
<div class="chat-header d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center">
<a href="{{ route('pembeli.pesan.index') }}" class="d-md-none me-3 text-dark"><i
class="fas fa-arrow-left"></i></a>
<div class="bg-success text-white rounded-circle d-flex align-items-center justify-content-center fw-bold me-3"
style="width: 45px; height: 45px;">
{{ substr($lawan->nama_lengkap, 0, 1) }}
</div>
<div>
<h6 class="mb-0 fw-bold text-dark">{{ $lawan->nama_lengkap }}</h6>
<small class="text-success"><i class="fas fa-store me-1"></i>{{ $lawan->nama_usaha }}</small>
{{-- Inisial Nama Lawan --}}
<div class="rounded-circle d-flex align-items-center justify-content-center fw-bold text-white me-3"
style="width: 40px; height: 40px; background-color: #81c408; font-size: 1.1rem;">
{{ substr($lawan->nama_lengkap, 0, 1) }}
</div>
<div>
<h6 class="mb-0 fw-bold" style="color: #333;">{{ $lawan->nama_lengkap }}</h6>
<small class="text-muted" style="font-size: 0.8rem;">
<i class="fas fa-store me-1 text-success"></i> {{ $lawan->nama_usaha ?? 'Online' }}
</small>
</div>
</div>
</div>
{{-- Chat Box --}}
<div class="chat-box" id="chatBox">
{{-- Isi Pesan (Chat Box) --}}
<div class="chat-content" id="chatBox">
@php
$currentUser = Auth::guard('petani')->check() ? Auth::guard('petani')->user() : Auth::guard('pembeli')->user();
$currentUserType = get_class($currentUser);
@endphp
@forelse ($chats as $chat)
@php
$isMe =
$chat->pengirim_id == Auth::guard('pembeli')->id() &&
$chat->pengirim_type == 'App\Models\Pembeli';
$isMe = ($chat->pengirim_id == $currentUser->id) && ($chat->pengirim_type == $currentUserType);
@endphp
<div class="d-flex {{ $isMe ? 'justify-content-end' : 'justify-content-start' }} mb-3">
<div class="message-bubble shadow-sm {{ $isMe ? 'message-me' : 'message-other' }}">
<div>{{ $chat->isi_pesan }}</div>
<div class="text-end mt-1" style="font-size: 0.65rem; opacity: 0.8;">
{{ $chat->created_at->format('H:i') }}
@if ($isMe)
<i class="fas fa-check-double ms-1"></i>
@endif
</div>
</div>
{{-- BUBBLE CHAT --}}
<div class="chat-bubble {{ $isMe ? 'bubble-me' : 'bubble-other' }}">
{{ $chat->isi_pesan }}
<span class="chat-time">
{{ $chat->created_at->format('H:i') }}
@if ($isMe)
<i class="fas fa-check ms-1" style="font-size: 0.6rem;"></i>
@endif
</span>
</div>
@empty
<div class="text-center py-5 mt-5">
<p class="text-muted bg-white d-inline-block px-3 py-1 rounded-pill border">Mulai percakapan
dengan {{ $lawan->nama_lengkap }}</p>
<div class="text-center mt-5 opacity-50">
<i class="far fa-comments fa-3x mb-3 text-secondary"></i>
<p class="small text-muted">Belum ada percakapan.<br>Sapa
<strong>{{ $lawan->nama_lengkap }}</strong> sekarang!</p>
</div>
@endforelse
</div>
{{-- Footer Chat --}}
{{-- Input Footer --}}
<div class="chat-footer">
<form action="{{ route('pesan.kirim') }}" method="POST" class="d-flex gap-2">
<form action="{{ route('pesan.kirim') }}" method="POST" class="d-flex gap-2 align-items-center">
@csrf
<input type="hidden" name="penerima_id" value="{{ $lawan->id }}">
<input type="hidden" name="penerima_type" value="{{ get_class($lawan) }}">
<input type="text" name="isi_pesan" class="form-control rounded-pill border-success"
placeholder="Ketik pesan..." required autocomplete="off">
<input type="text" name="isi_pesan" class="form-control bg-light border-0 rounded-pill px-4 py-2"
placeholder="Tulis pesan Anda..." required autocomplete="off">
<button type="submit"
class="btn btn-success rounded-circle d-flex align-items-center justify-content-center"
class="btn btn-send rounded-circle d-flex align-items-center justify-content-center shadow-sm"
style="width: 45px; height: 45px;">
<i class="fas fa-paper-plane"></i>
</button>
</form>
</div>
</div>
</div>
</div>
@ -107,15 +250,13 @@ class="btn btn-success rounded-circle d-flex align-items-center justify-content-
@section('js')
<script>
$(document).ready(function() {
$(document).ready(function () {
// Auto scroll ke bawah
const chatBox = document.getElementById("chatBox");
if (chatBox) {
chatBox.scrollTop = chatBox.scrollHeight;
}
// Fokus ke input
$('input[name="isi_pesan"]').focus();
});
</script>
@endsection
@endsection

View File

@ -4,95 +4,109 @@
@section('content')
<div class="container py-5 mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold text-dark">Riwayat Pesanan</h2>
<a href="{{ route('shop') }}" class="btn btn-outline-primary btn-sm rounded-pill">
<i class="fas fa-plus me-1"></i> Pesan Lagi
{{-- Header Section --}}
<div class="d-flex justify-content-between align-items-center mb-5">
<div>
<h2 class="fw-bold text-dark mb-0">Riwayat Pesanan</h2>
<p class="text-muted small mb-0">Pantau status belanjaan Anda di sini</p>
</div>
<a href="{{ route('shop') }}" class="btn btn-primary rounded-pill px-4 shadow-sm">
<i class="fas fa-shopping-bag me-2"></i>Belanja Lagi
</a>
</div>
@if (session('success'))
<div class="alert alert-success alert-dismissible fade show shadow-sm border-0" role="alert">
<i class="fa fa-check-circle me-2"></i> {{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
@if ($transaksis->isEmpty())
<div class="text-center py-5 bg-light rounded-3">
<i class="fas fa-receipt fa-4x text-muted mb-3 opacity-25"></i>
<h5 class="text-muted">Belum ada riwayat pesanan.</h5>
<a href="{{ route('shop') }}" class="btn btn-primary rounded-pill mt-3 px-4">Mulai Belanja</a>
{{-- Empty State --}}
<div class="text-center py-5">
<div class="bg-light rounded-circle d-inline-flex align-items-center justify-content-center mb-4"
style="width: 100px; height: 100px;">
<i class="fas fa-receipt fa-3x text-secondary opacity-50"></i>
</div>
<h5 class="fw-bold text-dark">Belum ada pesanan</h5>
<p class="text-muted mb-4">Yuk, mulai isi keranjang belanja Anda dengan produk segar!</p>
<a href="{{ route('shop') }}" class="btn btn-outline-primary rounded-pill px-4">Mulai Belanja</a>
</div>
@else
<div class="row">
{{-- List Transaksi --}}
<div class="row g-4">
@foreach ($transaksis as $trx)
<div class="col-12 mb-3">
<div class="card border-0 shadow-sm">
<div class="col-12">
<div class="card border-0 shadow-sm rounded-4 overflow-hidden">
{{-- Invoice & Status --}}
<div
class="card-header bg-white border-bottom border-light p-4 d-flex flex-wrap justify-content-between align-items-center">
<div class="d-flex align-items-center gap-3">
<div class="bg-light p-2 rounded text-center" style="width: 50px;">
<i class="fas fa-file-invoice text-primary fs-4"></i>
</div>
<div>
<h6 class="fw-bold mb-0 text-dark">#{{ $trx->kode_invoice }}</h6>
<small
class="text-muted">{{ \Carbon\Carbon::parse($trx->tanggal_transaksi)->format('d M Y • H:i') }}</small>
</div>
</div>
@php
$statusClass = match ($trx->status) {
'menunggu_konfirmasi', 'menunggu konfirmasi' => 'bg-warning text-dark bg-opacity-25 border border-warning',
'diproses' => 'bg-info text-dark bg-opacity-25 border border-info',
'dikirim' => 'bg-primary bg-opacity-10 border border-primary',
'selesai' => 'bg-success bg-opacity-10 border border-success',
'batal' => 'bg-danger bg-opacity-10 border border-danger',
default => 'bg-secondary text-secondary bg-opacity-10'
};
@endphp
<span class="badge {{ $statusClass }} px-3 py-2 rounded-pill mt-2 mt-sm-0">
{{ ucwords(str_replace('_', ' ', $trx->status)) }}
</span>
</div>
<div class="card-body p-4">
<div class="row align-items-center">
<div class="col-md-8">
<div class="d-flex align-items-center mb-3">
<div class="bg-light p-2 rounded me-3 text-center" style="width: 50px;">
<i class="fas fa-file-invoice text-primary fs-4"></i>
</div>
<div>
<h6 class="mb-0 fw-bold">Order #{{ $trx->kode_invoice }}</h6>
<small
class="text-muted">{{ \Carbon\Carbon::parse($trx->tanggal_transaksi)->format('d F Y') }}</small>
</div>
<div class="ms-3">
@if ($trx->status == 'menunggu_konfirmasi')
<span
class="badge bg-warning text-dark bg-opacity-25 border border-warning">Menunggu</span>
@elseif($trx->status == 'diproses')
<span
class="badge bg-info text-dark bg-opacity-25 border border-info">Diproses</span>
@elseif($trx->status == 'dikirim')
<span
class="badge bg-primary text-primary bg-opacity-10 border border-primary">Dikirim</span>
@elseif($trx->status == 'selesai')
<span
class="badge bg-success text-success bg-opacity-10 border border-success">Selesai</span>
@else
<span
class="badge bg-danger text-danger bg-opacity-10 border border-danger">Batal</span>
@endif
</div>
</div>
@php $firstItem = $trx->details->first(); @endphp
<div class="d-flex align-items-center bg-light rounded p-2 mt-2">
<img src="{{ $firstItem->produk->foto_produk ? asset('storage/' . $firstItem->produk->foto_produk) : asset('template/frontend/img/fruite-item-5.jpg') }}"
width="40" height="40" class="rounded object-fit-cover me-3">
<div class="text-truncate">
<span class="fw-bold text-dark">{{ $firstItem->produk->nama_produk }}</span>
@if ($trx->details->count() > 1)
<span class="text-muted small ms-1">+ {{ $trx->details->count() - 1 }}
produk lainnya</span>
@else
<span class="text-muted small ms-1">({{ $firstItem->jumlah }} kg)</span>
@endif
{{-- Preview Produk --}}
<div class="col-md-7 mb-3 mb-md-0">
@php $firstItem = $trx->detailTransaksis->first(); @endphp
@if($firstItem)
<div class="d-flex align-items-center">
<img src="{{ $firstItem->produk->foto_produk ? asset('storage/' . $firstItem->produk->foto_produk) : 'https://images.unsplash.com/photo-1586201375761-83865001e31c?q=80&w=200&auto=format&fit=crop' }}"
class="rounded-3 object-fit-cover me-3 shadow-sm" width="60" height="60"
alt="Produk">
<div>
<h6 class="fw-bold text-dark mb-1">{{ $firstItem->produk->nama_produk }}</h6>
<p class="text-muted small mb-0">
{{ $firstItem->jumlah }} kg x Rp
{{ number_format($firstItem->harga_satuan, 0, ',', '.') }}
@if ($trx->detailTransaksis->count() > 1)
<span class="text-primary fw-bold ms-1">+
{{ $trx->detailTransaksis->count() - 1 }} produk lainnya</span>
@endif
</p>
</div>
</div>
</div>
@endif
</div>
<div class="col-md-4 text-md-end mt-3 mt-md-0">
<p class="text-muted mb-1 small">Total Belanja</p>
<h5 class="fw-bold text-primary mb-3">Rp
{{ number_format($trx->total_harga, 0, ',', '.') }}</h5>
{{-- Total & Button --}}
<div class="col-md-5 text-md-end border-start-md ps-md-4">
<small class="text-muted d-block mb-1">Total Tagihan</small>
<h5 class="fw-bold text-dark mb-3">Rp {{ number_format($trx->total_harga, 0, ',', '.') }}
</h5>
<div class="d-flex justify-content-md-end gap-2">
<button class="btn btn-outline-secondary btn-sm rounded-pill px-3"
data-bs-toggle="modal" data-bs-target="#detailModal{{ $trx->id }}">
Detail
<button class="btn btn-light btn-sm rounded-pill px-3 border" data-bs-toggle="modal"
data-bs-target="#detailModal{{ $trx->id }}">
Lihat Detail
</button>
@if ($trx->status == 'dikirim')
<form action="{{ route('pesanan.selesai', $trx->id) }}" method="POST"
onsubmit="return confirm('Konfirmasi terima barang?')">
onsubmit="return confirm('Apakah Anda yakin barang sudah diterima dengan baik?')">
@csrf
<button type="submit" class="btn btn-success btn-sm rounded-pill px-3">
Diterima
<button type="submit" class="btn btn-success btn-sm rounded-pill px-3 shadow-sm">
<i class="fas fa-check me-1"></i> Pesanan Diterima
</button>
</form>
@endif
@ -105,41 +119,143 @@ class="badge bg-danger text-danger bg-opacity-10 border border-danger">Batal</sp
{{-- MODAL DETAIL --}}
<div class="modal fade" id="detailModal{{ $trx->id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0">
<div class="modal-header bg-light">
<h5 class="modal-title fw-bold">Detail #{{ $trx->kode_invoice }}</h5>
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content border-0 rounded-4 shadow-lg overflow-hidden">
{{-- Header: Putih Bersih --}}
<div class="modal-header border-bottom-0 pt-4 px-4 pb-0">
<div>
<h5 class="modal-title fw-bold text-dark" style="font-size: 1.25rem;">Detail Pesanan</h5>
<p class="text-muted small mb-0">Invoice: <span
class="font-monospace">#{{ $trx->kode_invoice }}</span></p>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="fw-bold small text-muted">Alamat Pengiriman</label>
<p class="mb-0 bg-light p-2 rounded">{{ $trx->alamat_pengiriman }}</p>
<div class="modal-body p-4">
{{-- 1. STATUS BANNER (Meniru Style Referensi) --}}
@php
$statusConfig = match ($trx->status) {
'menunggu_konfirmasi', 'menunggu konfirmasi' => ['bg' => '#fff3cd', 'text' => '#856404', 'icon' => 'fa-clock', 'title' => 'Menunggu Konfirmasi', 'desc' => 'Menunggu penjual menerima pesanan Anda.'],
'diproses' => ['bg' => '#cff4fc', 'text' => '#055160', 'icon' => 'fa-box-open', 'title' => 'Sedang Diproses', 'desc' => 'Penjual sedang mengemas barang Anda.'],
'dikirim' => ['bg' => '#cfe2ff', 'text' => '#084298', 'icon' => 'fa-truck', 'title' => 'Dalam Pengiriman', 'desc' => 'Kurir sedang menuju ke lokasi Anda.'],
'selesai' => ['bg' => '#d1e7dd', 'text' => '#0f5132', 'icon' => 'fa-check-circle', 'title' => 'Transaksi Selesai', 'desc' => 'Terima kasih telah berbelanja produk lokal!'],
'batal' => ['bg' => '#f8d7da', 'text' => '#842029', 'icon' => 'fa-times-circle', 'title' => 'Dibatalkan', 'desc' => 'Transaksi ini telah dibatalkan.'],
default => ['bg' => '#e2e3e5', 'text' => '#41464b', 'icon' => 'fa-question-circle', 'title' => 'Status Tidak Dikenal', 'desc' => '']
};
@endphp
<div class="d-flex align-items-center p-3 rounded-3 mb-4"
style="background-color: {{ $statusConfig['bg'] }}; color: {{ $statusConfig['text'] }};">
<i class="fas {{ $statusConfig['icon'] }} fs-3 me-3"></i>
<div>
<h6 class="fw-bold mb-0">{{ $statusConfig['title'] }}</h6>
<small style="opacity: 0.9;">{{ $statusConfig['desc'] }}</small>
</div>
</div>
<h6 class="fw-bold mt-4 mb-3">Daftar Item</h6>
<ul class="list-group list-group-flush">
@foreach ($trx->details as $d)
<li
class="list-group-item d-flex justify-content-between align-items-center px-0">
<div class="row g-4 mb-4">
{{-- 2. INFO PENJUAL (Kiri) --}}
<div class="col-md-6">
<label class="small text-secondary fw-bold text-uppercase mb-2"
style="font-size: 0.75rem; letter-spacing: 0.5px;">Dijual Oleh</label>
<div class="bg-light p-3 rounded-3 d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center">
<img src="{{ $d->produk->foto_produk ? asset('storage/' . $d->produk->foto_produk) : asset('template/frontend/img/fruite-item-5.jpg') }}"
width="40" height="40" class="rounded me-2 object-fit-cover">
<div class="bg-white rounded-circle shadow-sm d-flex align-items-center justify-content-center me-3"
style="width: 45px; height: 45px;">
<i class="fas fa-store text-primary"></i>
</div>
<div>
<div class="fw-bold">{{ $d->produk->nama_produk }}</div>
<small class="text-muted">{{ $d->jumlah }} kg x
<h6 class="fw-bold text-dark mb-0" style="font-size: 0.95rem;">
{{ $trx->petani->nama_lengkap ?? 'Petani' }}</h6>
<small class="text-muted"
style="font-size: 0.85rem;">{{ $trx->petani->nama_usaha ?? 'Tani Desa Seller' }}</small>
</div>
</div>
@if($trx->petani)
<a href="https://wa.me/{{ $trx->petani->no_hp }}" target="_blank"
class="btn btn-success btn-sm rounded-circle d-flex align-items-center justify-content-center"
style="width: 35px; height: 35px;" data-bs-toggle="tooltip"
title="Chat WhatsApp">
<i class="fab fa-whatsapp"></i>
</a>
@endif
</div>
</div>
{{-- 3. INFO PENGIRIMAN (Kanan) --}}
<div class="col-md-6">
<label class="small text-secondary fw-bold text-uppercase mb-2"
style="font-size: 0.75rem; letter-spacing: 0.5px;">Info Pengiriman</label>
<div class="bg-light p-3 rounded-3 h-100">
<div class="d-flex mb-2">
<i class="fas fa-map-marker-alt text-danger mt-1 me-2"></i>
<div>
<h6 class="fw-bold text-dark mb-0" style="font-size: 0.95rem;">Alamat Tujuan
</h6>
<small class="text-muted d-block text-truncate"
style="max-width: 200px;">{{ $trx->alamat_pengiriman }}</small>
</div>
</div>
<div class="ms-4 ps-1 border-top pt-2 mt-2">
<small class="text-muted">Metode Bayar: <span class="fw-bold text-dark">Cash on
Delivery (COD)</span></small>
</div>
</div>
</div>
</div>
{{-- 4. RINCIAN BARANG (Style Card List) --}}
<label class="small text-secondary fw-bold text-uppercase mb-2"
style="font-size: 0.75rem; letter-spacing: 0.5px;">Rincian Barang</label>
<div class="border rounded-3 overflow-hidden mb-4">
@foreach ($trx->detailTransaksis as $d)
<div class="d-flex align-items-center justify-content-between p-3 bg-white border-bottom">
<div class="d-flex align-items-center">
<img src="{{ $d->produk->foto_produk ? asset('storage/' . $d->produk->foto_produk) : 'https://images.unsplash.com/photo-1586201375761-83865001e31c?q=80&w=200&auto=format&fit=crop' }}"
class="rounded-3 object-fit-cover me-3"
style="width: 60px; height: 60px; background-color: #f8f9fa;">
<div>
<h6 class="mb-1 fw-bold text-dark">{{ $d->produk->nama_produk }}</h6>
<small class="text-muted d-block">{{ $d->jumlah }} kg x Rp
{{ number_format($d->harga_satuan, 0, ',', '.') }}</small>
</div>
</div>
<span
class="fw-bold text-dark">{{ number_format($d->subtotal, 0, ',', '.') }}</span>
</li>
<div class="fw-bold text-dark">
Rp {{ number_format($d->subtotal, 0, ',', '.') }}
</div>
</div>
@endforeach
</ul>
</div>
{{-- 5. TOTAL BLOCK (Hijau Solid seperti referensi) --}}
@php
// Menggunakan warna background sesuai status agar konsisten, atau default hijau fresh
$footerBg = ($trx->status == 'batal') ? '#dc3545' : (($trx->status == 'menunggu_konfirmasi') ? '#ffc107' : '#82c91e');
$footerText = ($trx->status == 'menunggu_konfirmasi') ? '#000' : '#fff';
@endphp
<div class="p-3 rounded-3" style="background-color: {{ $footerBg }}; color: {{ $footerText }};">
<div class="d-flex justify-content-between align-items-center mb-1">
<span style="opacity: 0.9;">Subtotal Produk</span>
<span class="fw-bold">Rp {{ number_format($trx->total_harga, 0, ',', '.') }}</span>
</div>
<div class="d-flex justify-content-between align-items-center">
<span style="opacity: 0.9;">Biaya Pengiriman (COD)</span>
<span class="fw-bold">Gratis</span>
</div>
<hr class="my-2" style="opacity: 0.3;">
<div class="d-flex justify-content-between align-items-center">
<span class="fw-bold fs-5">Total Pembayaran</span>
<span class="fw-bold fs-5">Rp {{ number_format($trx->total_harga, 0, ',', '.') }}</span>
</div>
</div>
</div>
<div class="modal-footer justify-content-between bg-light">
<span class="text-muted small">Total Tagihan</span>
<span class="fw-bold fs-5 text-primary">Rp
{{ number_format($trx->total_harga, 0, ',', '.') }}</span>
<div class="modal-footer border-top-0 pt-0 pb-4 px-4">
<button type="button" class="btn btn-light border rounded-pill px-4 w-100"
data-bs-dismiss="modal">Tutup</button>
</div>
</div>
</div>
@ -149,3 +265,13 @@ class="fw-bold text-dark">{{ number_format($d->subtotal, 0, ',', '.') }}</span>
@endif
</div>
@endsection
@push('style')
<style>
@media (min-width: 768px) {
.border-start-md {
border-left: 1px solid #dee2e6 !important;
}
}
</style>
@endpush

View File

@ -3,118 +3,175 @@
@section('title', 'Belanja Padi & Beras')
@section('content')
<!-- Page Header -->
<div class="container-fluid py-5 bg-light">
<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">Belanja Produk Kami</h1>
<h1 class="display-5 fw-bold text-dark mb-3">Belanja Produk Kami</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-0">
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
<li class="breadcrumb-item active">Shop</li>
<li class="breadcrumb-item"><a href="{{ route('home') }}" class="text-decoration-none">Home</a></li>
<li class="breadcrumb-item active text-muted">Shop</li>
</ol>
</nav>
</div>
</div>
<!-- Shop Section -->
<div class="container py-5">
<div class="container pb-5">
<div class="row g-4">
<!-- Sidebar -->
<div class="col-lg-3">
<!-- Search -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h5 class="fw-bold mb-3">Cari Produk</h5>
<div class="card-body p-4">
<h6 class="fw-bold mb-3 text-uppercase small text-muted">Cari Produk</h6>
<form action="{{ route('shop') }}" method="GET">
@if(request('kategori')) <input type="hidden" name="kategori" value="{{ request('kategori') }}">
@endif
@if(request('sort')) <input type="hidden" name="sort" value="{{ request('sort') }}"> @endif
<div class="input-group">
<input type="search" name="search" class="form-control" placeholder="Cari..."
value="{{ request('search') }}">
<button class="btn btn-success" type="submit">
<i class="fa fa-search"></i>
<input type="search" name="search" class="form-control bg-light border-0"
placeholder="Nama produk..." value="{{ request('search') }}">
<button class="btn btn-primary" type="submit">
<i class="fas fa-search"></i>
</button>
</div>
</form>
</div>
</div>
<!-- Info -->
<div class="card border-0 shadow-sm">
<div class="card-body">
<h5 class="fw-bold mb-3">Informasi</h5>
<div class="d-flex align-items-center mb-2">
<i class="fas fa-check-circle text-success me-2"></i>
<small>Total {{ $produks->total() }} Produk</small>
</div>
<div class="d-flex align-items-center mb-2">
<i class="fas fa-leaf text-success me-2"></i>
<small>100% Organik</small>
</div>
<div class="d-flex align-items-center">
<i class="fas fa-truck text-success me-2"></i>
<small>Gratis Ongkir Min. 50 Kg</small>
<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">Kategori</h6>
<div class="d-flex flex-column gap-2">
@php
$kategoriList = \App\Models\Kategori::all();
$currentKat = request('kategori');
@endphp
<a href="{{ route('shop', array_merge(request()->query(), ['kategori' => null])) }}"
class="d-flex justify-content-between align-items-center text-decoration-none {{ !$currentKat ? 'fw-bold text-primary' : 'text-secondary' }}">
<span>Semua Kategori</span>
</a>
{{-- Looping Data Database --}}
@foreach($kategoriList as $kat)
<a href="{{ route('shop', array_merge(request()->query(), ['kategori' => $kat->slug])) }}"
class="d-flex justify-content-between align-items-center text-decoration-none {{ $currentKat == $kat->slug ? 'fw-bold text-primary' : 'text-secondary' }}">
<span>{{ $kat->nama_kategori }}</span>
@if($currentKat == $kat->slug)
<i class="fas fa-check small"></i>
@endif
</a>
@endforeach
</div>
</div>
</div>
<div class="card border-0 shadow-sm bg-primary text-white">
<div class="card-body p-4">
<h5 class="fw-bold mb-2"><i class="fas fa-leaf me-2"></i>Produk Segar</h5>
<p class="small mb-0 opacity-75">Semua produk kami diambil langsung dari petani lokal terpercaya.
</p>
</div>
</div>
</div>
<!-- Product Grid -->
<div class="col-lg-9">
<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>
dari <span class="fw-bold text-dark">{{ $produks->total() }}</span> produk
</div>
<div class="d-flex align-items-center">
<label class="small text-muted me-2 text-nowrap">Urutkan:</label>
<form id="sortForm" action="{{ route('shop') }}" method="GET">
{{-- Pertahankan query lain saat sorting --}}
@foreach(request()->except('sort') as $key => $value)
<input type="hidden" name="{{ $key }}" value="{{ $value }}">
@endforeach
<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>
<option value="termurah" {{ request('sort') == 'termurah' ? 'selected' : '' }}>Harga Terendah
</option>
<option value="termahal" {{ request('sort') == 'termahal' ? 'selected' : '' }}>Harga Tertinggi
</option>
<option value="terlaris" {{ request('sort') == 'terlaris' ? 'selected' : '' }}>Paling Laris
</option>
</select>
</form>
</div>
</div>
<div class="row g-4">
@forelse($produks as $produk)
<div class="col-md-6 col-lg-4">
<div class="card border-0 shadow-sm h-100 product-card">
<!-- Stock Badge -->
<div class="position-absolute top-0 end-0 m-2 z-1">
<span class="badge bg-warning text-dark">
Stok: {{ $produk->stok }} Kg
</span>
</div>
<a href="{{ route('produk.detail', $produk->id) }}" class="text-decoration-none">
<div class="card border-0 shadow-sm h-100 product-card">
<!-- Stock Badge -->
<div class="position-absolute top-0 end-0 m-2 z-1">
<span class="badge bg-warning text-dark">
Stok: {{ $produk->stok }} Kg
</span>
</div>
<!-- Image -->
<div class="overflow-hidden" style="height: 200px;">
<img src="{{ $produk->foto_produk ? asset('storage/' . $produk->foto_produk) : 'https://images.unsplash.com/photo-1586201375761-83865001e31c?q=80&w=800&auto=format&fit=crop' }}"
class="card-img-top w-100 h-100" alt="{{ $produk->nama_produk }}"
style="object-fit: cover;">
</div>
<!-- Image -->
<div class="overflow-hidden" style="height: 200px;">
<img src="{{ $produk->foto_produk ? asset('storage/' . $produk->foto_produk) : 'https://images.unsplash.com/photo-1586201375761-83865001e31c?q=80&w=800&auto=format&fit=crop' }}"
class="card-img-top w-100 h-100" alt="{{ $produk->nama_produk }}"
style="object-fit: cover;">
</div>
<div class="card-body p-3">
<span class="badge bg-success mb-2">{{ $produk->kategori ?? 'Umum' }}</span>
<h6 class="fw-bold mb-2">{{ $produk->nama_produk }}</h6>
<p class="text-muted small mb-3">{{ Str::limit($produk->deskripsi, 60) }}</p>
<div class="d-flex justify-content-between align-items-center pt-2 border-top">
<div class="card-body d-flex justify-content-between flex-column p-3">
<div>
<small class="text-muted">Harga/Kg</small>
<h6 class="fw-bold text-success mb-0">
Rp {{ number_format($produk->harga, 0, ',', '.') }}
</h6>
<span class="badge bg-success mb-2">{{ $produk->kategori->nama_kategori ?? 'Umum' }}</span>
<h6 class="fw-bold mb-2">{{ $produk->nama_produk }}</h6>
<p class="text-muted small mb-3">{{ Str::limit($produk->deskripsi, 60) }}</p>
</div>
<div class="d-flex justify-content-between align-items-center pt-2 border-top">
<div>
<small class="text-muted">Harga/Kg</small>
<h6 class="fw-bold text-success mb-0">
Rp {{ number_format($produk->harga, 0, ',', '.') }}
</h6>
</div>
<a href="{{ route('produk.detail', $produk->id) }}"
class="btn btn-success btn-sm rounded-pill">
<i class="fa fa-eye"></i> Detail
</a>
</div>
<a href="{{ route('produk.detail', $produk->id) }}"
class="btn btn-success btn-sm rounded-pill">
<i class="fa fa-eye"></i> Detail
</a>
</div>
</div>
</div>
</a>
</div>
@empty
<div class="col-12">
<div class="text-center py-5">
<i class="fas fa-search fa-4x text-muted mb-3"></i>
<h4 class="text-muted">Produk Tidak Ditemukan</h4>
<p class="text-muted">Coba kata kunci lain atau hapus filter pencarian.</p>
<a href="{{ route('shop') }}" class="btn btn-outline-success rounded-pill mt-2">
<i class="fas fa-sync-alt me-2"></i> Reset Pencarian
<div class="text-center py-5 bg-white rounded-3 shadow-sm border border-dashed">
<i class="fas fa-search fa-3x text-muted opacity-50 mb-3"></i>
<h5 class="fw-bold text-dark">Produk Tidak Ditemukan</h5>
<p class="text-muted mb-4">Coba ubah kata kunci pencarian atau reset filter Anda.</p>
<a href="{{ route('shop') }}" class="btn btn-outline-primary rounded-pill px-4">
<i class="fas fa-sync-alt me-2"></i> Reset Filter
</a>
</div>
</div>
@endforelse
</div>
<!-- Pagination -->
@if ($produks->hasPages())
<div class="d-flex justify-content-center mt-5">
{{ $produks->links() }}
{{ $produks->withQueryString()->links() }}
</div>
@endif
</div>
@ -122,13 +179,23 @@ class="btn btn-success btn-sm rounded-pill">
</div>
<style>
/* Hover Effect */
.product-card {
transition: transform 0.3s, box-shadow 0.3s;
transition: all 0.3s ease;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15) !important;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1) !important;
}
/* Zoom Image on Hover */
.product-card:hover img {
transform: scale(1.05);
}
.transition-transform {
transition: transform 0.5s ease;
}
</style>
@endsection
@endsection

View File

@ -4,20 +4,85 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('title') - TaniDesa</title>
<title>@yield('title') - GriyaPadi.id</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="shortcut icon" href="https://cdn.jsdelivr.net/gh/zuramai/mazer@docs/demo/assets/compiled/svg/favicon.svg"
type="image/x-icon">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/zuramai/mazer@docs/demo/assets/compiled/css/app.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/zuramai/mazer@docs/demo/assets/compiled/css/app-dark.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/zuramai/mazer@docs/demo/assets/compiled/css/iconly.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
@yield('css')
<style>
/* --- CUSTOM GREEN THEME --- */
:root {
--primary-green: #81c408;
--primary-hover: #6da705;
}
.logo a {
font-family: 'Inter', sans-serif;
font-weight: 600;
font-size: 1.7rem;
}
.bg-primary-green {
background-color: var(--primary-green) !important;
color: #ffffff;
}
.text-primary-green {
background-color: var(--primary-green) !important;
color: #ffffff;
}
.sidebar-wrapper .sidebar-menu .sidebar-item.active .sidebar-link {
background-color: var(--primary-green) !important;
box-shadow: 0 5px 10px rgba(129, 196, 8, 0.3);
}
a {
color: var(--primary-green);
}
a:hover {
color: var(--primary-hover);
}
.stats-icon {
display: flex !important;
justify-content: center !important;
align-items: center !important;
width: 3rem;
height: 3rem;
margin-right: 0;
}
.stats-icon i {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
margin: 0 !important;
}
/* Loader/Spinner Color if exists */
.spinner-border.text-primary {
color: var(--primary-green) !important;
}
/* Pagination active color */
.page-item.active .page-link {
background-color: var(--primary-green) !important;
border-color: var(--primary-green) !important;
}
.chat-card {
height: 80vh;
overflow: hidden;
@ -48,7 +113,7 @@
background-color: #fafafa;
}
/* Item List di Sidebar */
/* Item List */
.chat-item {
transition: all 0.3s;
border-bottom: 1px solid #dee2e6;
@ -59,15 +124,58 @@
background-color: #e9ecef;
}
/* Active Chat Item */
.chat-item.active {
background-color: #2d46a18f;
color: white !important;
background-color: rgba(129, 196, 8, 0.2);
border-left: 4px solid var(--primary-green);
color: #333 !important;
}
.chat-item.active p,
.chat-item.active small,
.chat-item.active h6 {
color: white !important;
color: #333 !important;
}
.btn-primary {
background-color: var(--primary-green) !important;
border-color: var(--primary-green) !important;
color: #fff !important;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
background-color: var(--primary-hover) !important;
border-color: var(--primary-hover) !important;
box-shadow: 0 0 0 0.25rem rgba(129, 196, 8, 0.5) !important;
}
/* Tombol Outline */
.btn-outline-primary {
color: var(--primary-green) !important;
border-color: var(--primary-green) !important;
}
.btn-outline-primary:hover {
background-color: var(--primary-green) !important;
color: #fff !important;
}
/* Pagination */
.page-item.active .page-link {
background-color: var(--primary-green) !important;
border-color: var(--primary-green) !important;
}
.page-link {
color: var(--primary-green) !important;
}
/* Form Check / Radio Button / Switch saat aktif */
.form-check-input:checked {
background-color: var(--primary-green) !important;
border-color: var(--primary-green) !important;
}
::-webkit-scrollbar {
@ -93,10 +201,10 @@
<div class="sidebar-wrapper active">
<div class="sidebar-header position-relative">
<div class="d-flex justify-content-between align-items-center">
<div class="logo">
<a href="#">TaniDesa</a>
<div class="logo me-md-2">
<a href="#" style="color: #81c408;">GriyaPadi.id</a>
</div>
<div class="theme-toggle d-flex gap-2 align-items-center mt-2">
<div class="theme-toggle d-flex gap-1 align-items-center mt-2">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true" role="img" class="iconify iconify--system-uicons" width="20"
height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 21 21">
@ -118,16 +226,15 @@
<label class="form-check-label"></label>
</div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true" role="img" class="iconify iconify--mdi" width="20"
height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
aria-hidden="true" role="img" class="iconify iconify--mdi" width="20" height="20"
preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
<path fill="currentColor"
d="m17.75 4.09l-2.53 1.94l.91 3.06l-2.63-1.81l-2.63 1.81l.91-3.06l-2.53-1.94L12.44 4l1.06-3l1.06 3l3.19.09m3.5 6.91l-1.64 1.25l.59 1.98l-1.7-1.17l-1.7 1.17l.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95l2.06.05m-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85c-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14c.4-.4.82-.76 1.27-1.08c.75-.53 1.93.36 1.85 1.19c-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82c-2.81 3.14-2.7 7.96.31 10.98c3.02 3.01 7.84 3.12 10.98.31Z">
</path>
</svg>
</div>
<div class="sidebar-toggler x">
<a href="#" class="sidebar-hide d-xl-none d-block"><i
class="bi bi-x bi-middle"></i></a>
<a href="#" class="sidebar-hide d-xl-none d-block"><i class="bi bi-x bi-middle"></i></a>
</div>
</div>
</div>
@ -138,21 +245,35 @@ class="bi bi-x bi-middle"></i></a>
{{-- Menu Dashboard Admin --}}
@if (Auth::guard('admin')->check())
{{-- DASHBOARD --}}
<li class="sidebar-item {{ request()->is('admin/dashboard') ? 'active' : '' }}">
<a href="{{ route('admin.dashboard') }}" class='sidebar-link'>
<i class="bi bi-grid-fill"></i> <span>Dashboard</span>
</a>
</li>
{{-- MASTER KATEGORI --}}
<li class="sidebar-item {{ request()->is('admin/kategori*') ? 'active' : '' }}">
<a href="{{ route('admin.kategori.index') }}" class='sidebar-link'>
<i class="bi bi-tags-fill"></i> <span>Master Kategori</span>
</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>
{{-- MONITORING --}}
<li class="sidebar-item {{ request()->is('admin/monitoring*') ? 'active' : '' }}">
<a href="{{ route('admin.monitoring') }}" class='sidebar-link'>
<i class="bi bi-eye-fill"></i> <span>Monitoring</span>
</a>
</li>
@endif
{{-- Menu Dashboard Petani --}}
@ -214,7 +335,7 @@ class='sidebar-link border-0 bg-transparent text-danger w-100 text-start'>
</header>
<div class="page-heading">
<h3>@yield('page-title')</h3>
<div>@yield('page-title')</div>
</div>
<div class="page-content">
@ -224,7 +345,7 @@ class='sidebar-link border-0 bg-transparent text-danger w-100 text-start'>
<footer>
<div class="footer clearfix mb-0 text-muted">
<div class="float-start">
<p>2025 &copy; TaniDesa</p>
<p>2025 &copy; GriyaPadi.id</p>
</div>
</div>
</footer>
@ -234,10 +355,10 @@ class='sidebar-link border-0 bg-transparent text-danger w-100 text-start'>
<script src="https://cdn.jsdelivr.net/gh/zuramai/mazer@docs/demo/assets/static/js/components/dark.js"></script>
<script
src="https://cdn.jsdelivr.net/gh/zuramai/mazer@docs/demo/assets/extensions/perfect-scrollbar/perfect-scrollbar.min.js">
</script>
</script>
<script src="https://cdn.jsdelivr.net/gh/zuramai/mazer@docs/demo/assets/compiled/js/app.js"></script>
@yield('js')
</body>
</html>
</html>

View File

@ -3,13 +3,12 @@
<head>
<meta charset="utf-8">
<title>@yield('title') - TaniDesa</title>
<title>@yield('title') - GriyaPadi.id</title>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&family=Raleway:wght@600;800&display=swap"
rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.4.1/font/bootstrap-icons.css" rel="stylesheet">
@ -26,16 +25,33 @@
:root {
--primary-color: #81c408;
--primary-hover: #6da705;
--secondary-color: #ffc107;
--light-bg: #f8f9fa;
--secondary-color: #ffc107f6;
--light-bg: #f5f7f9;
--dark-text: #2c3e50;
--muted-text: #6c757d;
}
body {
font-family: 'Open Sans', sans-serif;
font-family: 'Inter', sans-serif;
background-color: var(--light-bg);
color: var(--dark-text);
line-height: 1.6;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: 'Inter', sans-serif;
font-weight: 700;
color: var(--dark-text);
letter-spacing: -0.5px;
}
/* Utility Colors */
.text-primary {
color: var(--primary-color) !important;
}
@ -48,6 +64,14 @@
border-color: var(--primary-color) !important;
}
/* BUTTON */
.btn {
font-weight: 500;
border-radius: 6px;
padding: 0.5rem 1.25rem;
transition: all 0.2s ease;
}
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
@ -57,6 +81,7 @@
.btn-primary:hover {
background-color: var(--primary-hover);
border-color: var(--primary-hover);
transform: translateY(-1px);
}
.btn-outline-primary {
@ -69,10 +94,15 @@
color: white;
}
.btn-secondary {
background-color: var(--secondary-color);
border-color: var(--secondary-color);
color: #333;
/* NAVBAR */
.navbar {
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.04);
}
.navbar-light .navbar-nav .nav-link {
font-weight: 500;
color: var(--dark-text);
font-size: 0.95rem;
}
.navbar-light .navbar-nav .nav-link.active,
@ -80,115 +110,66 @@
color: var(--primary-color) !important;
}
.rounded-pill {
border-radius: 50rem !important;
/* FORM INPUTS */
.form-control {
border-radius: 6px;
border: 1px solid #dee2e6;
padding: 0.6rem 1rem;
}
.rounded-4 {
border-radius: 1rem !important;
.form-control:focus {
box-shadow: 0 0 0 3px rgba(129, 196, 8, 0.15);
border-color: var(--primary-color);
}
/* CARDS */
.card {
border: none;
border-radius: 16px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.02);
transition: 0.3s;
background: #fff;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05);
}
.footer-accent {
border-bottom: 1px solid rgba(129, 196, 8, 0.5);
}
/* Chat Section */
/* CHAT SECTION */
.chat-container {
height: 80vh;
background: #fff;
border-radius: 15px;
border-radius: 8px;
border: 1px solid #e9ecef;
overflow: hidden;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.chat-sidebar {
background-color: #f8f9fa;
border-right: 1px solid #e9ecef;
height: 100%;
overflow-y: auto;
}
.chat-list-item {
transition: all 0.2s;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
}
.chat-list-item:hover {
background-color: #e9ecef;
}
.chat-list-item.active {
background-color: #e3f2fd;
border-left: 4px solid #81c408;
}
.chat-content {
display: flex;
flex-direction: column;
height: 100%;
background-color: #fff;
}
.chat-header {
padding: 15px;
border-bottom: 1px solid #e9ecef;
background: #fff;
z-index: 10;
}
.chat-box {
flex: 1;
overflow-y: auto;
padding: 20px;
background-color: #fcfcfc;
background-image: radial-gradient(#81c408 0.5px, transparent 0.5px);
background-size: 20px 20px;
opacity: 0.8;
}
/* Bubble Chat */
.message-bubble {
max-width: 70%;
padding: 10px 15px;
border-radius: 15px;
margin-bottom: 10px;
position: relative;
font-size: 0.95rem;
max-width: 75%;
padding: 10px 16px;
border-radius: 8px;
margin-bottom: 8px;
font-size: 0.9rem;
line-height: 1.5;
}
.message-me {
background-color: #81c408;
background-color: var(--primary-color);
color: white;
border-bottom-right-radius: 2px;
margin-left: auto;
}
.message-other {
background-color: #e9ecef;
color: #333;
background-color: #f1f3f5;
color: var(--dark-text);
border-bottom-left-radius: 2px;
margin-right: auto;
}
.chat-footer {
padding: 15px;
background: #fff;
border-top: 1px solid #e9ecef;
}
/* SCROLLBAR */
::-webkit-scrollbar {
width: 6px;
}
@ -201,6 +182,15 @@
background: #ccc;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #bbb;
}
/* Footer Accent */
.footer-accent {
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
</style>
</head>
@ -208,25 +198,21 @@
<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-grow text-primary" role="status"></div>
<div class="spinner-border text-primary" role="status" style="width: 3rem; height: 3rem;"></div>
</div>
<div class="container-fluid fixed-top">
<div class="container-fluid bg-primary d-none d-lg-block">
<div class="container-fluid fixed-top px-0">
<div class="container-fluid bg-primary d-none d-lg-block py-3">
<div class="container">
<div class="topbar">
<div class="d-flex justify-content-between">
<div class="top-info ps-2">
<small class="me-3"><i class="fas fa-map-marker-alt me-2 text-secondary"></i> <a
href="#" class="text-white">Desa Sukamaju, Indonesia</a></small>
<small class="me-3"><i class="fas fa-envelope me-2 text-secondary"></i><a href="#"
class="text-white">info@tanidesa.com</a></small>
</div>
<div class="top-link pe-2">
<a href="#" class="text-white"><small class="text-white mx-2">Kebijakan
Privasi</small>/</a>
<a href="#" class="text-white"><small class="text-white mx-2">Syarat &
Ketentuan</small></a>
</div>
<div class="d-flex justify-content-between text-white" style="font-size: 0.85rem;">
<div>
<span class="me-3"><i class="fas fa-map-marker-alt me-2"></i> Desa Sukamaju, Indonesia</span>
<span class="me-3"><i class="fas fa-envelope me-2"></i> info@tanidesa.com</span>
</div>
<div>
<a href="#" class="text-white text-decoration-none me-2">Bantuan</a>
<span class="text-white-50">|</span>
<a href="#" class="text-white text-decoration-none ms-2">Ikuti Kami</a>
</div>
</div>
</div>
@ -234,67 +220,76 @@ class="text-white">info@tanidesa.com</a></small>
<div class="container-fluid bg-white shadow-sm">
<div class="container">
<nav class="navbar navbar-light navbar-expand-xl">
<a href="{{ url('/') }}" class="navbar-brand">
<h1 class="text-primary display-6">TaniDesa</h1>
<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>
</a>
<button class="navbar-toggler py-2 px-3" type="button" data-bs-toggle="collapse"
<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 bg-white" id="navbarCollapse">
<div class="collapse navbar-collapse" id="navbarCollapse">
<div class="navbar-nav mx-auto">
<a href="{{ url('/') }}"
class="nav-item nav-link {{ request()->is('/') ? 'active' : '' }}">Home</a>
class="nav-item nav-link mx-2 {{ request()->is('/') ? 'active' : '' }}">Home</a>
<a href="{{ route('shop') }}"
class="nav-item nav-link {{ request()->is('shop*') ? 'active' : '' }}">Belanja</a>
<a href="#" class="nav-item nav-link">Kontak</a>
class="nav-item nav-link mx-2 {{ request()->is('shop*') ? 'active' : '' }}">Belanja</a>
</div>
<div class="d-flex m-3 me-0 align-items-center">
<form action="{{ route('shop') }}" method="GET" class="d-flex me-4">
<div class="d-flex align-items-center mt-3 mt-xl-0">
<form action="{{ route('shop') }}" method="GET" class="me-3 d-none d-md-flex">
<div class="input-group">
<input type="search" name="search" class="form-control border border-secondary"
placeholder="Cari..." value="{{ request('search') }}"
style="border-radius: 20px 0 0 20px;">
<button class="btn btn-outline-secondary border border-secondary" type="submit"
style="border-radius: 0 20px 20px 0;">
<i class="fas fa-search text-primary"></i>
<input type="search" name="search" class="form-control border-end-0"
placeholder="Cari produk..." value="{{ request('search') }}"
style="border-radius: 6px 0 0 6px;">
<button class="btn btn-outline-secondary border-start-0" type="submit"
style="border-radius: 0 6px 6px 0; border-color: #dee2e6;">
<i class="fas fa-search text-muted"></i>
</button>
</div>
</form>
<a href="{{ route('cart') }}" class="position-relative me-4 my-auto">
<i class="fa fa-shopping-bag fa-2x text-dark"></i>
<span
class="position-absolute bg-secondary rounded-circle d-flex align-items-center justify-content-center text-dark px-1"
style="top: -5px; left: 15px; height: 20px; min-width: 20px;">
<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;">
{{ count((array) session('cart')) }}
</span>
</a>
@if (Auth::guard('pembeli')->check())
<div class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle my-auto text-dark fw-bold"
<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">
<i class="fas fa-user fa-2x me-1"></i>
<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">{{ Auth::guard('pembeli')->user()->nama_lengkap }}</span>
class="d-none d-xl-inline small">{{ Auth::guard('pembeli')->user()->nama_lengkap }}</span>
</a>
<div class="dropdown-menu m-0 bg-secondary rounded-0">
<a href="{{ route('pembeli.profile') }}" class="dropdown-item">Profil Saya</a>
<a href="{{ route('pembeli.pesan.index') }}" class="dropdown-item">Pesan
Saya</a>
<a href="{{ route('pesanan.saya') }}" class="dropdown-item">Pesanan Saya</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>
<div class="dropdown-divider"></div>
<form action="{{ route('logout') }}" method="POST">
@csrf
<button type="submit" class="dropdown-item">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>
@else
<a href="{{ route('login') }}" class="my-auto btn btn-primary rounded-pill px-4">
<i class="fas fa-user me-2"></i> Login
<a href="{{ route('login') }}" class="btn btn-primary px-4 ms-2">
Login
</a>
@endif
</div>
@ -303,100 +298,79 @@ class="d-none d-xl-inline">{{ Auth::guard('pembeli')->user()->nama_lengkap }}</s
</div>
</div>
</div>
<div style="margin-top: 150px;">
<div style="margin-top: 170px;">
@if (session('success'))
<div class="container mt-3">
<div class="alert alert-success alert-dismissible fade show shadow-sm" role="alert">
<i class="fa fa-check-circle me-2"></i> {{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<div class="container mt-4">
<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>
</div>
</div>
@endif
@yield('content')
</div>
<div class="container-fluid bg-dark text-white-50 footer">
<div class="container-fluid bg-dark text-white-50 footer mt-5 pt-5">
<div class="container py-5">
<div class="pb-4 mb-4 footer-accent">
<div class="row g-4">
<div class="col-lg-3">
<a href="#">
<h1 class="text-primary mb-0">TaniDesa</h1>
<p class="text-secondary mb-0">Platform Pangan Langsung Petani</p>
</a>
</div>
<div class="col-lg-6">
<div class="position-relative mx-auto">
<input class="form-control border-0 w-100 py-3 px-4 rounded-pill" type="text"
placeholder="Email Anda">
<button type="submit"
class="btn btn-primary border-0 border-secondary py-3 px-4 position-absolute rounded-pill text-white"
style="top: 0; right: 0;">Langganan</button>
</div>
</div>
<div class="col-lg-3">
<div class="d-flex justify-content-end pt-3">
<a class="btn btn-outline-secondary me-2 btn-md-square rounded-circle" href=""><i
class="fab fa-twitter"></i></a>
<a class="btn btn-outline-secondary me-2 btn-md-square rounded-circle" href=""><i
class="fab fa-facebook-f"></i></a>
<a class="btn btn-outline-secondary me-2 btn-md-square rounded-circle" href=""><i
class="fab fa-youtube"></i></a>
<a class="btn btn-outline-secondary btn-md-square rounded-circle" href=""><i
class="fab fa-linkedin-in"></i></a>
</div>
</div>
</div>
</div>
<div class="row g-5">
<div class="col-lg-3 col-md-6">
<div class="footer-item">
<h4 class="text-light mb-3">Tentang Kami</h4>
<p class="mb-4">TaniDesa memotong rantai pasok yang panjang, memberikan harga terbaik untuk
petani dan kesegaran maksimal untuk pembeli.</p>
<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>
<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>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="d-flex flex-column text-start footer-item">
<h4 class="text-light mb-3">Menu Cepat</h4>
<a class="btn-link" href="{{ route('shop') }}">Belanja Sekarang</a>
<a class="btn-link" href="#">Hubungi Kami</a>
<a class="btn-link" href="#">Syarat & Ketentuan</a>
<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>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="d-flex flex-column text-start footer-item">
<h4 class="text-light mb-3">Akun</h4>
<a class="btn-link" href="{{ route('pembeli.profile') }}">Profil Saya</a>
<a class="btn-link" href="{{ route('cart') }}">Keranjang Belanja</a>
<a class="btn-link" href="{{ route('pesanan.saya') }}">Riwayat Pesanan</a>
<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>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="footer-item">
<h4 class="text-light mb-3">Kontak</h4>
<p>Alamat: Desa Sukamaju, Nganjuk</p>
<p>Email: info@tanidesa.com</p>
<p>WA: +62 812 3456 7890</p>
</div>
<h5 class="text-white mb-4">Hubungi Kami</h5>
<p class="small mb-2"><i class="fa fa-map-marker-alt me-3"></i>Desa Sukamaju, Nganjuk</p>
<p class="small mb-2"><i class="fa fa-envelope me-3"></i>info@tanidesa.com</p>
<p class="small mb-0"><i class="fa fa-phone-alt me-3"></i>+62 812 3456 7890</p>
</div>
</div>
</div>
<div class="container-fluid copyright bg-dark py-4" style="border-top: 1px solid rgba(255,255,255,0.1);">
<div class="container">
<div class="row">
<div class="col-md-6 text-center text-md-start mb-3 mb-md-0">
<span class="text-light"><a href="#"><i
class="fas fa-copyright text-light me-2"></i>TaniDesa</a>, All right
reserved.</span>
</div>
</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>
</div>
</div>
</div>
<a href="#" class="btn btn-primary border-3 border-primary rounded-circle back-to-top"><i
class="fa fa-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,78 +4,79 @@
@section('page-title', 'Overview Toko')
@section('content')
<section class="row">
<div class="col-12 col-lg-12">
<div class="row">
<div class="col-6 col-lg-4 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4">
<div class="stats-icon purple">
<i class="bi bi-basket-fill"></i>
<section class="row">
<div class="col-12 col-lg-12">
<div class="row">
{{-- Card 1: Produk Saya --}}
<div class="col-6 col-lg-4 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4 d-flex justify-content-center align-items-center">
<div class="stats-icon purple"><i class="bi bi-people-fill"></i></div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Produk Saya</h6>
<h6 class="font-extrabold mb-0">{{ $totalProduk }}</h6>
</div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Produk Saya</h6>
<h6 class="font-extrabold mb-0">{{ $totalProduk }}</h6>
</div>
</div>
</div>
{{-- Card 2: Pesanan Baru --}}
<div class="col-6 col-lg-4 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4 d-flex justify-content-center align-items-center">
<div class="stats-icon blue">
<i class="bi bi-receipt"></i>
</div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Pesanan Baru</h6>
<h6 class="font-extrabold mb-0">{{ $pesananBaru }}</h6>
</div>
</div>
</div>
</div>
</div>
{{-- Card 3: Total Pendapatan --}}
<div class="col-6 col-lg-4 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4 d-flex justify-content-center align-items-center">
<div class="stats-icon green">
<i class="bi bi-cash"></i>
</div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Total Pendapatan</h6>
<h6 class="font-extrabold mb-0">Rp {{ number_format($totalPendapatan, 0, ',', '.') }}
</h6>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-6 col-lg-4 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4">
<div class="stats-icon blue">
<i class="bi bi-receipt"></i>
</div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Pesanan Baru</h6>
<h6 class="font-extrabold mb-0">{{ $pesananBaru }}</h6>
<small class="text-xs text-muted">Perlu diproses</small>
</div>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h4>Selamat Datang, {{ Auth::guard('petani')->user()->nama_lengkap }}!</h4>
</div>
</div>
</div>
</div>
<div class="col-6 col-lg-4 col-md-6">
<div class="card">
<div class="card-body px-3 py-4-5">
<div class="row">
<div class="col-md-4">
<div class="stats-icon green">
<i class="bi bi-cash"></i>
</div>
</div>
<div class="col-md-8">
<h6 class="text-muted font-semibold">Total Pendapatan</h6>
<h6 class="font-extrabold mb-0">Rp {{ number_format($totalPendapatan, 0, ',', '.') }}</h6>
</div>
<div class="card-body">
<p>Kelola produk dan pantau pesanan Anda melalui menu di sebelah kiri.</p>
<a href="{{ route('petani.produk.create') }}" class="btn btn-primary">Tambah Produk Baru</a>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h4>Selamat Datang, {{ Auth::guard('petani')->user()->nama_lengkap }}!</h4>
</div>
<div class="card-body">
<p>Kelola produk dan pantau pesanan Anda melalui menu di sebelah kiri.</p>
<a href="{{ route('petani.produk.create') }}" class="btn btn-primary">Tambah Produk Baru</a>
</div>
</div>
</div>
</div>
</div>
</section>
</section>
@endsection

View File

@ -2,13 +2,40 @@
@section('title', 'Kotak Masuk')
@section('content')
<style>
/* Styling Khusus agar sama dengan Show Blade */
.chat-card {
height: 80vh;
overflow: hidden;
}
.chat-sidebar {
border-right: 1px solid #dee2e6;
height: 100%;
display: flex;
flex-direction: column;
}
.chat-list-container {
flex-grow: 1;
overflow-y: auto;
}
/* Scrollbar Tipis */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: #f1f1f1; }
::-webkit-scrollbar-thumb { background: #ccc; border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: #aaa; }
</style>
<section class="section">
<div class="card chat-card shadow-sm">
<div class="row g-0 h-100">
{{-- DAFTAR CHAT --}}
<div class="col-md-4 chat-sidebar">
<div class="p-4 border-bottom bg-white sticky-top">
<h5 class="mb-3 text-primary"><i class="bi bi-chat-left-text-fill"></i> Percakapan</h5>
<h5 class="mb-3 text-success"><i class="bi bi-chat-left-text-fill"></i> Percakapan</h5>
<div class="form-group position-relative has-icon-right mb-0">
<input type="text" class="form-control" placeholder="Cari pembeli...">
<div class="form-control-icon">
@ -17,45 +44,53 @@
</div>
</div>
<div class="list-group list-group-flush">
<div class="list-group list-group-flush chat-list-container">
@forelse($chatList as $chat)
<a href="{{ route('petani.pesan.show', $chat['lawan_id']) }}" class="list-group-item list-group-item-action chat-item p-3">
<div class="d-flex align-items-center">
<div class="avatar avatar-lg me-3">
<img src="{{ asset('assets/compiled/jpg/1.jpg') }}" alt="Avatar"> @if($chat['unread'] > 0)
{{-- Avatar Inisial --}}
<span class="avatar-content rounded-circle d-flex align-items-center justify-content-center text-white font-bold"
style="width: 48px; height: 48px; background-color: #81c408; font-size: 1.2rem;">
{{ substr($chat['nama'], 0, 1) }}
</span>
@if($chat['unread'] > 0)
<span class="avatar-status bg-danger"></span>
@endif
</div>
<div class="flex-grow-1 text-truncate">
<div class="d-flex justify-content-between align-items-center mb-1">
<h6 class="mb-0 text-dark">{{ $chat['nama'] }}</h6>
<small class="text-muted" style="font-size: 11px">{{ $chat['time'] }}</small>
<h6 class="mb-0 text-dark fw-bold">{{ $chat['nama'] }}</h6>
<small class="text-muted" style="font-size: 0.75rem">{{ $chat['time'] }}</small>
</div>
<p class="mb-0 small text-muted text-truncate">
{{ Str::limit($chat['last_message'], 30) }}
{{ Str::limit($chat['last_message'], 35) }}
</p>
</div>
@if($chat['unread'] > 0)
<span class="badge bg-danger rounded-pill ms-2">{{ $chat['unread'] }}</span>
@endif
</div>
</a>
@empty
<div class="text-center p-5">
<i class="bi bi-inbox x-3 text-muted"></i>
<p class="text-muted mt-2">Belum ada pesan masuk.</p>
<div class="text-center p-5 mt-4">
<i class="bi bi-chat-square-text text-muted" style="font-size: 3rem; opacity: 0.5;"></i>
<p class="text-muted mt-3">Belum ada pesan masuk.</p>
</div>
@endforelse
</div>
</div>
<div class="col-md-8 d-none d-md-flex align-items-center justify-content-center bg-white flex-column">
<div class="text-center opacity-50">
<div class="mb-3">
<i class="bi bi-chat-square-dots text-primary" style="font-size: 5rem;"></i>
<div class="col-md-8 d-none d-md-flex align-items-center justify-content-center bg-light flex-column border-start">
<div class="text-center opacity-75">
<div class="mb-4">
<div class="bg-white p-4 rounded-circle shadow-sm d-inline-block">
<i class="bi bi-chat-dots-fill text-success" style="font-size: 4rem;"></i>
</div>
</div>
<h4 class="text-primary">Selamat Datang, Pak Tani!</h4>
<p class="text-secondary">Pilih percakapan di sebelah kiri untuk melihat detail pesan.</p>
<h4 class="text-dark fw-bold">Selamat Datang di Pesan!</h4>
<p class="text-secondary mt-2">
Pilih salah satu percakapan di sebelah kiri<br>untuk melihat detail pesan dan membalasnya.
</p>
</div>
</div>

View File

@ -2,13 +2,63 @@
@section('title', 'Chat: ' . $lawan->nama_lengkap)
@section('content')
<style>
.chat-card {
height: 80vh;
overflow: hidden;
}
.chat-sidebar {
border-right: 1px solid #dee2e6;
height: 100%;
display: flex;
flex-direction: column;
}
.chat-list-container {
flex-grow: 1;
overflow-y: auto;
}
.chat-window {
height: 100%;
display: flex;
flex-direction: column;
background-color: #f2f7ff;
}
.chat-content {
flex-grow: 1;
overflow-y: auto;
padding: 1.5rem;
}
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #aaa;
}
</style>
<section class="section">
<div class="card chat-card shadow-sm">
<div class="row g-0 h-100">
<div class="col-md-4 chat-sidebar d-none d-md-block">
<div class="p-4 border-bottom bg-white sticky-top">
<h5 class="mb-3 text-primary"><i class="bi bi-chat-left-text-fill"></i> Percakapan</h5>
{{-- SIDEBAR KIRI (DAFTAR CHAT) --}}
<div class="col-md-4 chat-sidebar d-none d-md-flex">
<div class="p-4 border-bottom bg-white">
<h5 class="mb-3 text-success"><i class="bi bi-chat-left-text-fill"></i> Percakapan</h5>
<div class="form-group position-relative has-icon-right mb-0">
<input type="text" class="form-control" placeholder="Cari pembeli...">
<div class="form-control-icon">
@ -17,14 +67,17 @@
</div>
</div>
<div class="list-group list-group-flush">
<div class="list-group list-group-flush chat-list-container">
@foreach ($chatList as $chat)
<a href="{{ route('petani.pesan.show', $chat['lawan_id']) }}"
class="list-group-item list-group-item-action chat-item p-3 {{ $chat['lawan_id'] == $lawan->id ? 'active' : '' }}">
<div class="d-flex align-items-center">
<div class="avatar avatar-lg me-3">
<span class="avatar-content bg-primary text-white">
{{-- Avatar dengan inisial --}}
<span
class="avatar-content rounded-circle d-flex align-items-center justify-content-center text-white font-bold"
style="width: 48px; height: 48px; background-color: #81c408; font-size: 1.2rem;">
{{ substr($chat['nama'], 0, 1) }}
</span>
@if ($chat['unread'] > 0)
@ -35,15 +88,17 @@ class="list-group-item list-group-item-action chat-item p-3 {{ $chat['lawan_id']
<div class="flex-grow-1 text-truncate">
<div class="d-flex justify-content-between align-items-center mb-1">
<h6
class="mb-0 {{ $chat['lawan_id'] == $lawan->id ? 'text-white' : 'text-dark' }}">
class="mb-0 fw-bold {{ $chat['lawan_id'] == $lawan->id ? 'text-white' : 'text-dark' }}">
{{ $chat['nama'] }}
</h6>
<small
class="{{ $chat['lawan_id'] == $lawan->id ? 'text-white' : 'text-muted' }}"
style="font-size: 11px">{{ $chat['time'] }}</small>
class="{{ $chat['lawan_id'] == $lawan->id ? 'text-white-50' : 'text-muted' }}"
style="font-size: 0.75rem">
{{ $chat['time'] }}
</small>
</div>
<p
class="mb-0 small text-truncate {{ $chat['lawan_id'] == $lawan->id ? 'text-white' : 'text-muted' }}">
class="mb-0 small text-truncate {{ $chat['lawan_id'] == $lawan->id ? 'text-white-50' : 'text-muted' }}">
{{ Str::limit($chat['last_message'], 30) }}
</p>
</div>
@ -53,42 +108,45 @@ class="mb-0 small text-truncate {{ $chat['lawan_id'] == $lawan->id ? 'text-white
</div>
</div>
{{-- AREA CHAT KANAN --}}
<div class="col-md-8 chat-window">
<div class="p-3 border-bottom bg-white d-flex align-items-center shadow-sm z-index-1">
{{-- Header Chat --}}
<div class="p-3 border-bottom bg-white d-flex align-items-center shadow-sm" style="z-index: 10;">
<a href="{{ route('petani.pesan.index') }}"
class="btn btn-sm btn-light me-3 d-md-none rounded-circle">
<i class="bi bi-arrow-left"></i>
</a>
<div class="avatar avatar-md me-3">
<div class="avatar-content bg-primary text-white font-bold">
<span
class="avatar-content rounded-circle d-flex align-items-center justify-content-center text-white font-bold"
style="width: 40px; height: 40px; background-color: #81c408;">
{{ substr($lawan->nama_lengkap, 0, 1) }}
</div>
</span>
</div>
<div>
<h6 class="mb-0 text-dark">{{ $lawan->nama_lengkap }}</h6>
<h6 class="mb-0 text-dark fw-bold">{{ $lawan->nama_lengkap }}</h6>
<small class="text-muted">{{ $lawan->role ?? 'Pembeli' }}</small>
</div>
</div>
{{-- Isi Chat --}}
<div class="chat-content" id="chatContainer">
@forelse($chats as $chat)
@php
$isMe =
$chat->pengirim_id == Auth::guard('petani')->id() &&
$chat->pengirim_type == 'App\Models\Petani';
$isMe = ($chat->pengirim_id == Auth::guard('petani')->id() &&
$chat->pengirim_type == 'App\Models\Petani');
@endphp
<div class="d-flex w-100 mb-3 {{ $isMe ? 'justify-content-end' : 'justify-content-start' }}">
<div style="max-width: 70%;">
<div class="p-3 shadow-sm position-relative"
style="border-radius: 15px;
border-{{ $isMe ? 'bottom-right' : 'bottom-left' }}-radius: 0;
background-color: {{ $isMe ? '#435ebe' : '#ffffff' }};
color: {{ $isMe ? '#ffffff' : '#212529' }};">
<div style="max-width: 75%;">
<div class="p-3 shadow-sm position-relative" style="border-radius: 15px;
border-{{ $isMe ? 'bottom-right' : 'bottom-left' }}-radius: 0;
background-color: {{ $isMe ? '#81c408' : '#ffffff' }};
color: {{ $isMe ? '#ffffff' : '#212529' }};">
<p class="mb-1" style="font-size: 0.95rem; line-height: 1.5;">
<p class="mb-1" style="font-size: 0.95rem; line-height: 1.5; word-wrap: break-word;">
{{ $chat->isi_pesan }}
</p>
@ -105,23 +163,24 @@ class="btn btn-sm btn-light me-3 d-md-none rounded-circle">
</div>
</div>
@empty
<div class="text-center my-5">
<span class="badge bg-light-secondary text-secondary p-3 rounded-pill">
Belum ada percakapan. Sapa pembeli Anda! 👋
<div class="text-center my-auto">
<span class="badge bg-light text-secondary p-3 rounded-pill shadow-sm">
👋 Belum ada percakapan. Mulai sapa pembeli Anda!
</span>
</div>
@endforelse
</div>
{{-- Footer Input Pesan --}}
<div class="p-3 bg-white border-top">
<form action="{{ route('pesan.kirim') }}" method="POST" class="d-flex gap-2 align-items-center">
@csrf
<input type="hidden" name="penerima_id" value="{{ $lawan->id }}">
<input type="hidden" name="penerima_type" value="App\Models\Pembeli">
<div class="input-group">
<input type="text" name="isi_pesan" class="form-control" placeholder="Ketik pesan..."
required autocomplete="off">
<button type="submit" class="btn btn-primary">
<button type="submit" class="btn btn-primary-green">
<i class="bi bi-send-fill"></i> Kirim
</button>
</div>
@ -134,11 +193,11 @@ class="btn btn-sm btn-light me-3 d-md-none rounded-circle">
</section>
<script>
document.addEventListener("DOMContentLoaded", function() {
document.addEventListener("DOMContentLoaded", function () {
var chatBox = document.getElementById("chatContainer");
if (chatBox) {
chatBox.scrollTop = chatBox.scrollHeight;
}
});
</script>
@endsection
@endsection

View File

@ -1,174 +1,175 @@
@extends('layouts.admin')
@section('title', 'Detail Pesanan')
@section('page-title', 'Detail Pesanan #' . $pesanan->kode_invoice)
@section('page-title')
<div class="d-flex align-items-center justify-content-between">
<div>
<h4 class="mb-0">Detail Pesanan</h4>
<p class="text-muted font-bold mb-0">Invoice: #{{ $pesanan->kode_invoice }}</p>
</div>
<a href="{{ route('petani.pesanan.index') }}" class="btn btn-light">
<i class="bi bi-arrow-left"></i> Kembali
</a>
</div>
@endsection
@section('content')
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h4>Informasi Pesanan</h4>
</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<th width="30%">Nomor Invoice</th>
<td>: <span class="text-primary font-bold">#{{ $pesanan->kode_invoice }}</span></td>
</tr>
<tr>
<th>Tanggal Pesanan</th>
<td>: {{ \Carbon\Carbon::parse($pesanan->tanggal_transaksi)->format('d F Y H:i') }}</td>
</tr>
<tr>
<th>Status</th>
<td>:
@if($pesanan->status == 'menunggu_konfirmasi')
<span class="badge bg-warning">Perlu Konfirmasi</span>
@elseif($pesanan->status == 'diproses')
<span class="badge bg-info">Diproses</span>
@elseif($pesanan->status == 'dikirim')
<span class="badge bg-primary">Dikirim</span>
@elseif($pesanan->status == 'selesai')
<span class="badge bg-success">Selesai</span>
@else
<span class="badge bg-danger">Batal</span>
@endif
</td>
</tr>
</table>
<hr>
<h5>Daftar Produk</h5>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Produk</th>
<th class="text-center">Jumlah</th>
<th class="text-end">Harga Satuan</th>
<th class="text-end">Subtotal</th>
</tr>
</thead>
<tbody>
@foreach($pesanan->details as $detail)
@if($detail->produk->petani_id == Auth::guard('petani')->id())
<tr>
<td>
<div class="d-flex align-items-center">
<img src="{{ $detail->produk->foto_produk ? asset('storage/'.$detail->produk->foto_produk) : asset('template/frontend/img/fruite-item-5.jpg') }}"
alt="{{ $detail->produk->nama_produk }}"
class="rounded me-2"
style="width: 50px; height: 50px; object-fit: cover;">
<div>
<h6 class="mb-0">{{ $detail->produk->nama_produk }}</h6>
<div class="row">
{{-- KOLOM KIRI: Daftar Produk --}}
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Daftar Produk yang Dipesan</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Produk</th>
<th class="text-center">Qty</th>
<th class="text-end">Harga</th>
<th class="text-end">Subtotal</th>
</tr>
</thead>
<tbody>
@foreach($pesanan->detailTransaksis as $detail)
@if($detail->produk->petani_id == Auth::guard('petani')->id())
<tr>
<td>
<div class="d-flex align-items-center">
<img src="{{ $detail->produk->foto_produk ? asset('storage/' . $detail->produk->foto_produk) : 'https://images.unsplash.com/photo-1586201375761-83865001e31c?q=80&w=200&auto=format&fit=crop' }}"
class="rounded me-3" width="50" height="50" style="object-fit: cover;">
<div>
<h6 class="mb-1">{{ $detail->produk->nama_produk }}</h6>
<span
class="badge bg-primary-green">{{ $detail->produk->kategori->nama_kategori ?? 'Umum' }}</span>
</div>
</div>
</div>
</td>
<td class="text-center">{{ $detail->jumlah }}</td>
<td class="text-end">Rp {{ number_format($detail->harga_satuan, 0, ',', '.') }}</td>
<td class="text-end fw-bold">Rp {{ number_format($detail->subtotal, 0, ',', '.') }}</td>
</tr>
@endif
@endforeach
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-end fw-bold">Total Pendapatan:</td>
<td class="text-end fw-bold text-success">
@php
$totalPetani = $pesanan->details->filter(function($detail) {
return $detail->produk->petani_id == Auth::guard('petani')->id();
})->sum('subtotal');
@endphp
Rp {{ number_format($totalPetani, 0, ',', '.') }}
</td>
</tr>
</tfoot>
</table>
</td>
<td class="text-center">
<span class="badge bg-primary-green">{{ $detail->jumlah }}</span>
</td>
<td class="text-end">Rp {{ number_format($detail->harga_satuan, 0, ',', '.') }}</td>
<td class="text-end fw-bold">Rp {{ number_format($detail->subtotal, 0, ',', '.') }}</td>
</tr>
@endif
@endforeach
</tbody>
</table>
</div>
<div class="mt-4 pt-3 border-top">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">Total</h5>
@php
$totalPetani = $pesanan->detailTransaksis->filter(function ($detail) {
return $detail->produk->petani_id == Auth::guard('petani')->id();
})->sum('subtotal');
@endphp
<h4 class="mb-0 text-primary fw-bold">Rp {{ number_format($totalPetani, 0, ',', '.') }}</h4>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h4>Informasi Pembeli</h4>
</div>
<div class="card-body">
<div class="d-flex align-items-center mb-3">
<div class="avatar avatar-lg me-3">
<img src="{{ asset('template/admin/static/images/faces/2.jpg') }}" alt="Pembeli">
</div>
<div>
<h5 class="mb-0">{{ $pesanan->pembeli->nama_lengkap }}</h5>
<small class="text-muted">{{ $pesanan->pembeli->username }}</small>
</div>
</div>
<hr>
<p><strong><i class="bi bi-telephone me-2"></i> No. HP / WA:</strong><br>
<a href="https://wa.me/{{ $pesanan->pembeli->no_hp }}" target="_blank" class="text-success">{{ $pesanan->pembeli->no_hp }}</a></p>
<p><strong><i class="bi bi-geo-alt me-2"></i> Alamat Pengiriman:</strong><br>
{{ $pesanan->alamat_pengiriman }}</p>
</div>
</div>
{{-- Panel Aksi & Info --}}
<div class="col-lg-4">
@if($pesanan->status == 'menunggu_konfirmasi' || $pesanan->status == 'menunggu konfirmasi')
<div class="card mb-3">
<div class="card-body">
<div class="alert alert-warning mb-3">
<h6 class="alert-heading mb-2">Perlu Konfirmasi</h6>
<p class="mb-0 small">Pastikan stok tersedia sebelum menerima pesanan ini.</p>
</div>
<div class="card">
<div class="card-header">
<h4>Aksi Pesanan</h4>
</div>
<div class="card-body">
@if($pesanan->status == 'menunggu_konfirmasi')
<p class="text-muted mb-3">Pesanan baru masuk. Silakan terima jika stok tersedia atau tolak jika tidak bisa dipenuhi.</p>
<div class="d-grid gap-2">
<form action="{{ route('petani.pesanan.update', $pesanan->id) }}" method="POST">
<form action="{{ route('petani.pesanan.update', $pesanan->id) }}" method="POST" class="mb-2">
@csrf @method('PATCH')
<input type="hidden" name="status" value="diproses">
<button class="btn btn-success w-100" onclick="return confirm('Terima pesanan ini?')">
<i class="bi bi-check-circle me-2"></i> Terima Pesanan
<button class="btn btn-primary w-100" onclick="return confirm('Stok aman? Terima pesanan ini?')">
<i class="bi bi-check-circle"></i> Terima Pesanan
</button>
</form>
<form action="{{ route('petani.pesanan.update', $pesanan->id) }}" method="POST">
@csrf @method('PATCH')
<input type="hidden" name="status" value="batal">
<button class="btn btn-danger w-100" onclick="return confirm('Tolak pesanan ini?')">
<i class="bi bi-x-circle me-2"></i> Tolak Pesanan
<button class="btn btn-danger w-100" onclick="return confirm('Yakin ingin menolak?')">
<i class="bi bi-x-circle"></i> Tolak
</button>
</form>
</div>
@elseif($pesanan->status == 'diproses')
<p class="text-muted mb-3">Pesanan sedang diproses. Jika barang sudah dikirim atau siap diambil, update statusnya.</p>
<form action="{{ route('petani.pesanan.update', $pesanan->id) }}" method="POST">
@csrf @method('PATCH')
<input type="hidden" name="status" value="dikirim">
<button class="btn btn-primary w-100">
<i class="bi bi-truck me-2"></i> Kirim Barang
</button>
</form>
@elseif($pesanan->status == 'dikirim')
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i> Barang sedang dalam pengiriman. Menunggu konfirmasi selesai dari pembeli atau sistem.
</div>
@elseif($pesanan->status == 'diproses')
<div class="card mb-3">
<div class="card-body text-center">
<div class="alert alert-info">
<i class="bi bi-box-seam fs-2 d-block mb-2"></i>
<h6 class="alert-heading">Sedang Diproses</h6>
<p class="mb-0 small">Klik tombol di bawah jika barang sudah diserahkan ke kurir.</p>
</div>
<form action="{{ route('petani.pesanan.update', $pesanan->id) }}" method="POST">
@csrf @method('PATCH')
<input type="hidden" name="status" value="dikirim">
<button class="btn btn-info w-100">
<i class="bi bi-truck"></i> Update Jadi Dikirim
</button>
</form>
</div>
@elseif($pesanan->status == 'selesai')
<div class="alert alert-success">
<i class="bi bi-check-circle-fill me-2"></i> Pesanan Selesai.
</div>
@elseif($pesanan->status == 'batal')
<div class="alert alert-danger">
<i class="bi bi-x-circle-fill me-2"></i> Pesanan Dibatalkan.
</div>
@endif
</div>
@endif
<div class="mt-3 text-center">
<a href="{{ route('petani.pesanan.index') }}" class="btn btn-link text-secondary">Kembali ke Daftar</a>
{{-- STATUS BADGE --}}
<div class="card mb-3">
<div class="card-body">
<h6 class="card-title mb-3">Status Pesanan</h6>
@php
$statusBadge = match ($pesanan->status) {
'menunggu_konfirmasi', 'menunggu konfirmasi' => 'bg-warning',
'diproses' => 'bg-info',
'dikirim' => 'bg-primary',
'selesai' => 'bg-success',
'batal' => 'bg-danger',
default => 'bg-secondary'
};
@endphp
<span class="badge {{ $statusBadge }} fs-6">
{{ str_replace('_', ' ', ucwords($pesanan->status)) }}
</span>
</div>
</div>
{{-- INFO PEMBELI --}}
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Data Pembeli</h5>
</div>
<div class="card-body">
<div class="d-flex align-items-center mb-3">
<div class="d-flex justify-content-center align-items-center me-2">
<div class="stats-icon blue"><i class="bi bi-people-fill"></i></div>
</div>
<div>
<h6 class="mb-0">{{ $pesanan->pembeli->nama_lengkap }}</h6>
<small class="text-muted">{{ $pesanan->pembeli->username }}</small>
</div>
</div>
<div class="mb-3">
<small class="text-muted d-block mb-1">Kontak</small>
<a href="https://wa.me/{{ $pesanan->pembeli->no_hp }}" target="_blank"
class="text-success text-decoration-none">
<i class="bi bi-whatsapp"></i> {{ $pesanan->pembeli->no_hp }}
</a>
</div>
<div>
<small class="text-muted d-block mb-1">Alamat Pengiriman</small>
<p class="mb-0">{{ $pesanan->alamat_pengiriman }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@ -4,93 +4,81 @@
@section('page-title', 'Daftar Pesanan Masuk')
@section('content')
<div class="card">
<div class="card-header">
<h4>Kelola Pesanan</h4>
</div>
<div class="card-body">
@if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
<div class="card">
<div class="card-header">
<h4>Kelola Pesanan</h4>
</div>
<div class="card-body">
@if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
<div class="table-responsive">
<table class="table table-hover" id="table1">
<thead>
<tr>
<th>Invoice</th>
<th>Pembeli</th>
<th>Barang</th>
<th>Alamat</th>
<th>Total</th>
<th>Status</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($pesanans as $pesanan)
<tr>
<td class="text-primary font-bold">#{{ $pesanan->kode_invoice }}</td>
<td>
{{ $pesanan->pembeli->nama_lengkap }}<br>
<small class="text-muted">{{ $pesanan->pembeli->no_hp }}</small>
</td>
<td>
<ul class="list-unstyled mb-0">
@foreach($pesanan->details as $detail)
<li>- {{ $detail->produk->nama_produk }} ({{ $detail->jumlah }})</li>
@endforeach
</ul>
</td>
<td><small>{{ Str::limit($pesanan->alamat_pengiriman, 30) }}</small></td>
<td class="font-bold">Rp {{ number_format($pesanan->total_harga, 0, ',', '.') }}</td>
<td>
@if($pesanan->status == 'menunggu_konfirmasi')
<span class="badge bg-warning">Perlu Konfirmasi</span>
@elseif($pesanan->status == 'diproses')
<span class="badge bg-info">Diproses</span>
@elseif($pesanan->status == 'dikirim')
<span class="badge bg-primary">Dikirim</span>
@elseif($pesanan->status == 'selesai')
<span class="badge bg-success">Selesai</span>
@else
<span class="badge bg-danger">Batal</span>
@endif
</td>
<td>
<div class="d-flex gap-2">
<a href="{{ route('petani.pesanan.detail', $pesanan->id) }}" class="btn btn-sm btn-info text-white" title="Lihat Detail">
<i class="bi bi-eye"></i> Detail
</a>
{{-- Tombol Aksi Status --}}
@if($pesanan->status == 'menunggu_konfirmasi')
<form action="{{ route('petani.pesanan.update', $pesanan->id) }}" method="POST" class="d-inline">
@csrf @method('PATCH')
<input type="hidden" name="status" value="diproses">
<button class="btn btn-sm btn-success" title="Terima Pesanan"><i class="bi bi-check-lg"></i></button>
</form>
<form action="{{ route('petani.pesanan.update', $pesanan->id) }}" method="POST" class="d-inline">
@csrf @method('PATCH')
<input type="hidden" name="status" value="batal">
<button class="btn btn-sm btn-danger" title="Tolak Pesanan" onclick="return confirm('Tolak pesanan ini?')"><i class="bi bi-x-lg"></i></button>
</form>
@elseif($pesanan->status == 'diproses')
<form action="{{ route('petani.pesanan.update', $pesanan->id) }}" method="POST">
@csrf @method('PATCH')
<input type="hidden" name="status" value="dikirim">
<button class="btn btn-sm btn-primary" title="Kirim Barang"><i class="bi bi-truck"></i> Kirim</button>
</form>
@endif
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center">Belum ada pesanan masuk.</td>
</tr>
@endforelse
</tbody>
</table>
<div class="table-responsive">
<table class="table table-hover" id="table1">
<thead>
<tr>
<th>Invoice</th>
<th>Pembeli</th>
<th>Barang</th>
<th>Alamat</th>
<th>Total</th>
<th>Status</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($pesanans as $pesanan)
<tr>
<td class="text-primary font-bold">#{{ $pesanan->kode_invoice }}</td>
<td>
{{ $pesanan->pembeli->nama_lengkap }}<br>
<small class="text-muted">{{ $pesanan->pembeli->no_hp }}</small>
</td>
<td>
<ul class="list-unstyled mb-0">
@foreach($pesanan->detailTransaksis as $detail)
<li>- {{ $detail->produk->nama_produk }} ({{ $detail->jumlah }})</li>
@endforeach
</ul>
</td>
<td><small>{{ Str::limit($pesanan->alamat_pengiriman, 30) }}</small></td>
<td class="font-bold">Rp {{ number_format($pesanan->total_harga, 0, ',', '.') }}</td>
<td>
@if($pesanan->status == 'menunggu konfirmasi')
<span class="badge bg-warning">Perlu Konfirmasi</span>
@elseif($pesanan->status == 'diproses')
<span class="badge bg-info">Diproses</span>
@elseif($pesanan->status == 'dikirim')
<span class="badge bg-primary">Dikirim</span>
@elseif($pesanan->status == 'selesai')
<span class="badge bg-success">Selesai</span>
@else
<span class="badge bg-danger">Batal</span>
@endif
</td>
<td>
<a href="{{ route('petani.pesanan.detail', $pesanan->id) }}"
class="btn btn-sm btn-outline-primary rounded-pill px-3">
Lihat Detail <i class="bi bi-arrow-right ms-1"></i>
</a>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center">Belum ada pesanan masuk.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
@section('js')
<script>
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
</script>
@endsection

View File

@ -4,56 +4,232 @@
@section('page-title', 'Tambah Produk Baru')
@section('content')
<div class="card">
<div class="card-body">
<form action="{{ route('petani.produk.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="row">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-body">
<form action="{{ route('petani.produk.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="form-group mb-3">
<label>Nama Produk</label>
<input type="text" name="nama_produk" class="form-control" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Kategori Padi/Beras</label>
<select name="kategori" class="form-select" required>
<option value="Pandan Wangi">Pandan Wangi</option>
<option value="IR 64">IR 64 (Setra Ramos)</option>
<option value="Ketan">Ketan (Putih/Hitam)</option>
<option value="Merah">Beras Merah</option>
<option value="Hitam">Beras Hitam</option>
<option value="Lainnya">Lainnya</option>
</select>
</div>
<div class="row">
{{-- DATA PRODUK --}}
<div class="col-lg-8">
<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"
placeholder="Contoh: Beras Pandan Wangi Super" required>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group mb-3">
<label>Harga (Rp)</label>
<input type="number" name="harga" class="form-control" required>
<div class="row">
<div class="col-md-6 mb-3">
<label class="fw-bold">Kategori</label>
<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' : '' }}>
{{ $kat->nama_kategori }}
</option>
@endforeach
</select>
</div>
<div class="col-md-6 mb-3">
<label class="fw-bold">Stok (Kg)</label>
<input type="number" name="stok" class="form-control" placeholder="0" required
min="0">
</div>
</div>
<div class="form-group mb-3">
<label class="fw-bold">Harga (Rp)</label>
<input type="number" name="harga" class="form-control" placeholder="0" required min="0">
</div>
<div class="form-group mb-3">
<label class="fw-bold">Deskripsi</label>
<textarea name="deskripsi" class="form-control" rows="4"
placeholder="Jelaskan kualitas produk Anda..." required></textarea>
</div>
</div>
{{-- UPLOAD GAMBAR --}}
<div class="col-lg-4">
<h5 class="mb-4">Media Produk</h5>
{{-- FOTO UTAMA --}}
<div class="card bg-light border-0 mb-4">
<div class="card-body">
<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;">
{{-- Ikon Upload --}}
<div id="placeholder-utama" class="text-center text-muted">
<i class="bi bi-cloud-arrow-up fs-1"></i>
<div class="small mt-1">Klik untuk upload</div>
</div>
{{-- Tampilan Gambar --}}
<img id="preview-utama" src="#"
class="img-fluid w-100 h-100 object-fit-contain d-none">
</div>
<input type="file" name="foto_produk" class="form-control form-control-sm"
accept="image/*" required onchange="previewMainImage(this)">
<small class="text-muted">Wajib. Tampil di halaman depan.</small>
</div>
</div>
{{-- GALERI TAMBAHAN --}}
<div class="card bg-light border-0">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-2">
<label class="fw-bold m-0">Galeri Tambahan</label>
<span class="badge bg-primary rounded-pill" id="count-badge">0/3</span>
</div>
{{-- Container Preview Grid --}}
<div id="gallery-preview-container" class="row g-2 mb-3">
{{-- Gambar preview --}}
</div>
{{-- Input File Visual --}}
<div class="mb-2">
<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>
<small class="text-muted d-block" style="font-size: 0.8rem;">
<i class="bi bi-info-circle me-1"></i>Maksimal 3 foto. Bisa dihapus sebelum
upload.
</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-3">
<label>Stok</label>
<input type="number" name="stok" class="form-control" required>
<div class="border-top pt-3 mt-4 text-end">
<a href="{{ route('petani.produk.index') }}" class="btn btn-secondary me-2">Batal</a>
<button type="submit" class="btn btn-primary px-4">Simpan Produk</button>
</div>
</div>
</form>
</div>
<div class="form-group mb-3">
<label>Deskripsi</label>
<textarea name="deskripsi" class="form-control" rows="4" required></textarea>
</div>
<div class="form-group mb-3">
<label>Foto Produk</label>
<input type="file" name="foto_produk" class="form-control" accept="image/*" required>
</div>
<button type="submit" class="btn btn-primary">Simpan Produk</button>
<a href="{{ route('petani.produk.index') }}" class="btn btn-secondary">Batal</a>
</form>
</div>
</div>
</div>
@endsection
<script>
// ----------------------------------------------------
// PREVIEW FOTO UTAMA
// ----------------------------------------------------
function previewMainImage(input) {
const preview = document.getElementById('preview-utama');
const placeholder = document.getElementById('placeholder-utama');
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
preview.src = e.target.result;
// Tampilkan Gambar, Sembunyikan Ikon
preview.classList.remove('d-none');
placeholder.classList.add('d-none');
}
reader.readAsDataURL(input.files[0]);
} else {
preview.src = '#';
preview.classList.add('d-none');
placeholder.classList.remove('d-none');
}
}
// ----------------------------------------------------
// LOGIC GALERI TAMBAHAN (KERANJANG UPLOAD)
// ----------------------------------------------------
const inputVisual = document.getElementById('input-gallery-visual');
const inputReal = document.getElementById('real-input-gallery');
const container = document.getElementById('gallery-preview-container');
const countBadge = document.getElementById('count-badge');
// "Keranjang" virtual untuk menampung file
let dt = new DataTransfer();
inputVisual.addEventListener('change', function () {
const newFiles = this.files;
// Cek Limit Total
if (dt.items.length + newFiles.length > 3) {
alert('Maksimal hanya boleh 3 gambar tambahan!');
this.value = '';
return;
}
// Loop file baru
for (let i = 0; i < newFiles.length; i++) {
const file = newFiles[i];
dt.items.add(file);
// Buat HTML Preview
const reader = new FileReader();
reader.onload = function (e) {
const col = document.createElement('div');
col.className = 'col-4 position-relative new-image-preview';
col.innerHTML = `
<img src="${e.target.result}" class="rounded w-100 border bg-white" style="height: 70px; object-fit: cover;">
<span class="position-absolute top-0 end-0 badge bg-secondary text-white rounded-circle m-1 shadow-sm remove-btn"
style="cursor: pointer;">&times;</span>
`;
// Event Listener Tombol Hapus (X)
col.querySelector('.remove-btn').addEventListener('click', function () {
// Hapus file dari keranjang berdasarkan nama & size
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);
break;
}
}
inputReal.files = dt.files;
col.remove();
updateBadge();
});
container.appendChild(col);
};
reader.readAsDataURL(file);
}
inputReal.files = dt.files;
this.value = '';
updateBadge();
});
function updateBadge() {
let total = dt.items.length;
countBadge.innerText = total + '/3';
// Visual Feedback jika penuh
if (total >= 3) {
inputVisual.disabled = true;
inputVisual.setAttribute('title', 'Slot penuh (Maks 3)');
countBadge.className = 'badge bg-danger rounded-pill';
} else {
inputVisual.disabled = false;
inputVisual.removeAttribute('title');
countBadge.className = 'badge bg-primary rounded-pill';
}
}
</script>
@endsection

View File

@ -4,61 +4,263 @@
@section('page-title', 'Edit Produk')
@section('content')
<div class="card">
<div class="card-body">
<form action="{{ route('petani.produk.update', $produk->id) }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
<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">
@csrf
@method('PUT')
<div class="form-group mb-3">
<label>Nama Produk</label>
<input type="text" name="nama_produk" class="form-control" value="{{ $produk->nama_produk }}" required>
</div>
<div class="row">
{{-- DATA PRODUK --}}
<div class="col-lg-8">
<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>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Kategori Padi/Beras</label>
<select name="kategori" class="form-select" required>
<option value="Pandan Wangi">Pandan Wangi</option>
<option value="IR 64">IR 64 (Setra Ramos)</option>
<option value="Ketan">Ketan (Putih/Hitam)</option>
<option value="Merah">Beras Merah</option>
<option value="Hitam">Beras Hitam</option>
<option value="Lainnya">Lainnya</option>
</select>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="fw-bold">Kategori</label>
<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' : '' }}>
{{ $kat->nama_kategori }}
</option>
@endforeach
<div class="row">
<div class="col-md-6">
<div class="form-group mb-3">
<label>Harga (Rp)</label>
<input type="number" name="harga" class="form-control" value="{{ $produk->harga }}" required>
</select>
</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>
</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>
</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>
</div>
</div>
{{-- KELOLA GAMBAR --}}
<div class="col-lg-4">
<h5 class="mb-4">Kelola Gambar</h5>
{{-- FOTO UTAMA --}}
<div class="card bg-light border-0 mb-4">
<div class="card-body">
<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 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' }}">
</div>
{{-- Input File --}}
<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>
{{-- GALERI TAMBAHAN --}}
<div class="card bg-light border-0">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-2">
<label class="fw-bold m-0">Galeri Tambahan</label>
<span class="badge bg-primary rounded-pill" id="count-badge">0/3</span>
</div>
{{-- CONTAINER PREVIEW --}}
<div id="gallery-preview-container" class="row g-2 mb-3">
{{-- 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;">
{{-- 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>
</div>
@endforeach
</div>
{{-- INPUT FILE --}}
<div class="mb-3">
<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>
<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.
</small>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-3">
<label>Stok</label>
<input type="number" name="stok" class="form-control" value="{{ $produk->stok }}" required>
<div class="border-top pt-3 mt-4 text-end">
<a href="{{ route('petani.produk.index') }}" class="btn btn-secondary me-2">Batal</a>
<button type="submit" class="btn btn-success px-4">Update Produk</button>
</div>
</div>
</div>
</form>
<div class="form-group mb-3">
<label>Deskripsi</label>
<textarea name="deskripsi" class="form-control" rows="4" required>{{ $produk->deskripsi }}</textarea>
{{-- 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;">
@csrf @method('DELETE')
</form>
@endforeach
</div>
<div class="form-group mb-3">
<label>Foto Produk (Biarkan kosong jika tidak ingin mengganti)</label>
<input type="file" name="foto_produk" class="form-control" accept="image/*">
@if ($produk->foto_produk)
<small class="text-muted">Foto saat ini:</small><br>
<img src="{{ asset('storage/' . $produk->foto_produk) }}" width="100" class="mt-2 rounded">
@endif
</div>
<button type="submit" class="btn btn-success">Update Produk</button>
<a href="{{ route('petani.produk.index') }}" class="btn btn-secondary">Batal</a>
</form>
</div>
</div>
</div>
@endsection
<script>
// ----------------------------------------------------
// PREVIEW FOTO UTAMA
// ----------------------------------------------------
function previewMainImage(input) {
const preview = document.getElementById('preview-utama');
const placeholder = document.getElementById('placeholder-utama');
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
preview.src = e.target.result;
preview.classList.remove('d-none');
placeholder.classList.add('d-none');
}
reader.readAsDataURL(input.files[0]);
}
}
// ----------------------------------------------------
// LOGIC HAPUS GAMBAR LAMA (DATABASE)
// ----------------------------------------------------
function deleteExistingImage(id) {
document.getElementById('form-del-img-' + id).submit();
}
// ----------------------------------------------------
// LOGIC PREVIEW + ACCUMULATE
// ----------------------------------------------------
const inputVisual = document.getElementById('input-gallery-visual');
const inputReal = document.getElementById('real-input-gallery');
const container = document.getElementById('gallery-preview-container');
const countBadge = document.getElementById('count-badge');
let dt = new DataTransfer();
let existingCount = document.querySelectorAll('.existing-image-wrapper').length;
updateBadge();
inputVisual.addEventListener('change', function () {
const newFiles = this.files;
if (existingCount + dt.items.length + newFiles.length > 3) {
alert('Maksimal total hanya boleh 3 gambar tambahan!');
this.value = '';
return;
}
// Loop file yang baru dipilih
for (let i = 0; i < newFiles.length; i++) {
const file = newFiles[i];
// Tambahkan ke keranjang DataTransfer
dt.items.add(file);
// Buat Preview Element
const reader = new FileReader();
reader.onload = function (e) {
const col = document.createElement('div');
col.className = 'col-4 position-relative new-image-preview';
// HTML Preview
col.innerHTML = `
<img src="${e.target.result}" class="rounded w-100 border bg-white" style="height: 70px; object-fit: cover;">
<span class="position-absolute top-0 end-0 badge bg-secondary text-white rounded-circle m-1 shadow-sm remove-btn"
style="cursor: pointer;">&times;</span>
`;
// Event Hapus Preview
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);
break;
}
}
// Sinkronisasi ke Input Real
inputReal.files = dt.files;
col.remove();
updateBadge();
});
container.appendChild(col);
};
reader.readAsDataURL(file);
}
inputReal.files = dt.files;
this.value = '';
updateBadge();
});
function updateBadge() {
let total = existingCount + dt.items.length;
countBadge.innerText = total + '/3';
if (total >= 3) {
inputVisual.disabled = true;
countBadge.classList.remove('bg-primary');
countBadge.classList.add('bg-danger');
inputVisual.setAttribute('title', 'Kapasitas penuh');
} else {
inputVisual.disabled = false;
countBadge.classList.remove('bg-danger');
countBadge.classList.add('bg-primary');
inputVisual.removeAttribute('title');
}
}
</script>
@endsection

View File

@ -1,5 +1,6 @@
<?php
use App\Http\Controllers\Admin\KategoriController;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
use App\Http\Controllers\AdminController;
@ -44,7 +45,7 @@
Route::get('/forgot-password', [ForgotPasswordController::class, 'showLinkRequestForm'])->name('password.request');
Route::post('/forgot-password', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email');
Route::get('/reset-password/{token}', [ForgotPasswordController::class, 'showResetForm'])->name('password.reset');
Route::post('/reset-password', [ForgotPasswordController::class, 'reset'])->name('password.update');
});
@ -78,8 +79,6 @@
Route::put('/profile', [ProfileController::class, 'updatePembeli'])->name('pembeli.profile.update');
});
// --- ADMIN AREA ---
Route::middleware(['auth:admin'])->group(function () {
Route::get('/admin/dashboard', [AdminController::class, 'dashboard'])->name('admin.dashboard');
Route::get('/admin/monitoring', [AdminController::class, 'monitoring'])->name('admin.monitoring');
@ -91,6 +90,12 @@
Route::post('/{id}/approve', 'verifikasiApprove');
Route::post('/{id}/reject', 'verifikasiReject');
});
// CRUD Kategori
Route::resource('admin/kategori', KategoriController::class)->names('admin.kategori');
Route::get('/admin/transaksi/{id}', [AdminController::class, 'transaksiDetail'])
->name('admin.transaksi.detail');
});
@ -101,6 +106,7 @@
// CRUD Produk Petani
Route::resource('petani/produk', ProdukController::class)->names('petani.produk');
Route::delete('/petani/produk/image/{id}', [App\Http\Controllers\Petani\ProdukController::class, 'deleteImage'])->name('petani.produk.image.delete');
// Manajemen Pesanan Masuk (Petani)
Route::get('/petani/pesanan', [TransaksiController::class, 'pesananMasuk'])->name('petani.pesanan.index');