feat: implementasi fitur pesan dan perbaikan logika cart
This commit is contained in:
parent
db57919bd2
commit
046365a1d6
|
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\Pesan;
|
||||||
|
use App\Models\Petani;
|
||||||
|
use App\Models\Pembeli;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class PesanController extends Controller
|
||||||
|
{
|
||||||
|
// Menampilkan Daftar Percakapan (Inbox Utama)
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$user = $this->getAuthenticatedUser();
|
||||||
|
$isPetani = Auth::guard('petani')->check();
|
||||||
|
|
||||||
|
$allMessages = Pesan::where(function ($q) use ($user) {
|
||||||
|
$q->where('pengirim_id', $user->id)->where('pengirim_type', get_class($user));
|
||||||
|
})->orWhere(function ($q) use ($user) {
|
||||||
|
$q->where('penerima_id', $user->id)->where('penerima_type', get_class($user));
|
||||||
|
})->orderBy('created_at', 'desc')->get();
|
||||||
|
|
||||||
|
// Kelompokkan berdasarkan ID Lawan Bicara
|
||||||
|
$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;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Format data untuk view
|
||||||
|
$chatList = $conversations->map(function ($msgs) use ($user) {
|
||||||
|
$lastMsg = $msgs->first();
|
||||||
|
|
||||||
|
// Tentukan siapa lawan bicaranya
|
||||||
|
if ($lastMsg->pengirim_id == $user->id) {
|
||||||
|
$lawan = $lastMsg->penerima;
|
||||||
|
} else {
|
||||||
|
$lawan = $lastMsg->pengirim;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'lawan_id' => $lawan->id ?? 0,
|
||||||
|
'lawan_type' => get_class($lawan ?? new \stdClass),
|
||||||
|
'nama' => $lawan->nama_lengkap ?? 'User Terhapus',
|
||||||
|
'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()
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
$view = $isPetani ? 'petani.pesan.index' : 'landing.pesan.index';
|
||||||
|
return view($view, compact('chatList'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Menampilkan Detail Chat
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
$user = $this->getAuthenticatedUser();
|
||||||
|
$isPetani = Auth::guard('petani')->check();
|
||||||
|
|
||||||
|
// Tentukan model lawan bicara
|
||||||
|
$lawanType = $isPetani ? Pembeli::class : Petani::class;
|
||||||
|
$lawan = $lawanType::findOrFail($id);
|
||||||
|
|
||||||
|
// Ambil percakapan antara User Login & Lawan Bicara
|
||||||
|
$chats = Pesan::where(function ($q) use ($user, $lawan, $lawanType) {
|
||||||
|
$q->where('pengirim_id', $user->id)->where('pengirim_type', get_class($user))
|
||||||
|
->where('penerima_id', $lawan->id)->where('penerima_type', $lawanType);
|
||||||
|
})->orWhere(function ($q) use ($user, $lawan, $lawanType) {
|
||||||
|
$q->where('pengirim_id', $lawan->id)->where('pengirim_type', $lawanType)
|
||||||
|
->where('penerima_id', $user->id)->where('penerima_type', get_class($user));
|
||||||
|
})->orderBy('created_at', 'asc')->get();
|
||||||
|
|
||||||
|
// Tandai pesan masuk sebagai "Sudah Dibaca"
|
||||||
|
Pesan::where('pengirim_id', $lawan->id)->where('pengirim_type', $lawanType)
|
||||||
|
->where('penerima_id', $user->id)->update(['sudah_dibaca' => true]);
|
||||||
|
|
||||||
|
$view = $isPetani ? 'petani.pesan.show' : 'landing.pesan.show';
|
||||||
|
return view($view, compact('chats', 'lawan'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proses Kirim Pesan
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate(['isi_pesan' => 'required']);
|
||||||
|
$user = $this->getAuthenticatedUser();
|
||||||
|
$isPetani = Auth::guard('petani')->check();
|
||||||
|
|
||||||
|
// Menentukan tipe penerima
|
||||||
|
$penerimaType = $isPetani ? 'App\Models\Pembeli' : 'App\Models\Petani';
|
||||||
|
|
||||||
|
Pesan::create([
|
||||||
|
'pengirim_id' => $user->id,
|
||||||
|
'pengirim_type' => get_class($user),
|
||||||
|
'penerima_id' => $request->penerima_id,
|
||||||
|
'penerima_type' => $penerimaType,
|
||||||
|
'isi_pesan' => $request->isi_pesan,
|
||||||
|
'sudah_dibaca' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return back();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getAuthenticatedUser()
|
||||||
|
{
|
||||||
|
if (Auth::guard('petani')->check()) return Auth::guard('petani')->user();
|
||||||
|
if (Auth::guard('pembeli')->check()) return Auth::guard('pembeli')->user();
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -113,7 +113,7 @@ public function prosesCheckout(Request $request)
|
||||||
$subtotal_transaksi = 0;
|
$subtotal_transaksi = 0;
|
||||||
$kode_invoice = 'INV/' . date('Ymd') . '/' . rand(1000, 9999);
|
$kode_invoice = 'INV/' . date('Ymd') . '/' . rand(1000, 9999);
|
||||||
|
|
||||||
// Buat Header Transaksi dulu
|
// Membuat Header Transaksi
|
||||||
$transaksi = Transaksi::create([
|
$transaksi = Transaksi::create([
|
||||||
'pembeli_id' => $pembeli_id,
|
'pembeli_id' => $pembeli_id,
|
||||||
'tanggal_transaksi' => now(),
|
'tanggal_transaksi' => now(),
|
||||||
|
|
@ -144,7 +144,6 @@ public function prosesCheckout(Request $request)
|
||||||
$transaksi->update(['total_harga' => $subtotal_transaksi]);
|
$transaksi->update(['total_harga' => $subtotal_transaksi]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hapus Keranjang setelah sukses
|
|
||||||
session()->forget('cart');
|
session()->forget('cart');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -223,7 +222,7 @@ public function pesananDetail($id)
|
||||||
{
|
{
|
||||||
$petaniId = Auth::guard('petani')->id();
|
$petaniId = Auth::guard('petani')->id();
|
||||||
|
|
||||||
// Ambil transaksi berdasarkan ID, pastikan transaksi tersebut memiliki produk milik petani ini
|
// Ambil transaksi berdasarkan ID
|
||||||
$pesanan = Transaksi::whereHas('details.produk', function ($q) use ($petaniId) {
|
$pesanan = Transaksi::whereHas('details.produk', function ($q) use ($petaniId) {
|
||||||
$q->where('petani_id', $petaniId);
|
$q->where('petani_id', $petaniId);
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -27,4 +27,14 @@ public function transaksis()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Transaksi::class, 'pembeli_id');
|
return $this->hasMany(Transaksi::class, 'pembeli_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function pesanMasuk()
|
||||||
|
{
|
||||||
|
return $this->morphMany(Pesan::class, 'penerima');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pesanTerkirim()
|
||||||
|
{
|
||||||
|
return $this->morphMany(Pesan::class, 'pengirim');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||||
|
|
||||||
class Pesan extends Model
|
class Pesan extends Model
|
||||||
{
|
{
|
||||||
|
|
@ -20,4 +21,14 @@ class Pesan extends Model
|
||||||
'sudah_dibaca'
|
'sudah_dibaca'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function pengirim(): MorphTo
|
||||||
|
{
|
||||||
|
return $this->morphTo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function penerima(): MorphTo
|
||||||
|
{
|
||||||
|
return $this->morphTo();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable; // PENTING: Ganti ini
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
class Petani extends Authenticatable
|
class Petani extends Authenticatable
|
||||||
|
|
@ -29,4 +29,14 @@ public function produks()
|
||||||
{
|
{
|
||||||
return $this->hasMany(Produk::class, 'petani_id');
|
return $this->hasMany(Produk::class, 'petani_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function pesanMasuk()
|
||||||
|
{
|
||||||
|
return $this->morphMany(Pesan::class, 'penerima');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pesanTerkirim()
|
||||||
|
{
|
||||||
|
return $this->morphMany(Pesan::class, 'pengirim');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
<div class="auth-logo">
|
<div class="auth-logo">
|
||||||
<a href="{{ url('/') }}"><h3 class="fw-bold">TaniDesa</h3></a>
|
<a href="{{ url('/') }}"><h3 class="fw-bold">TaniDesa</h3></a>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="auth-title">Log in.</h1>
|
<h3 class="fw-bold text-center">Log in</h3>
|
||||||
|
|
||||||
{{-- Pesan Sukses Register --}}
|
{{-- Pesan Sukses Register --}}
|
||||||
@if (session('success'))
|
@if (session('success'))
|
||||||
|
|
@ -37,16 +37,16 @@
|
||||||
<form action="{{ route('login.proses') }}" method="POST">
|
<form action="{{ route('login.proses') }}" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
<div class="form-group position-relative has-icon-left mb-4">
|
<div class="form-group position-relative has-icon-left mb-4">
|
||||||
<input type="text" name="username" class="form-control form-control-xl" placeholder="Username">
|
<input type="text" name="username" class="form-control " placeholder="Username">
|
||||||
<div class="form-control-icon"><i class="bi bi-person"></i></div>
|
<div class="form-control-icon"><i class="bi bi-person"></i></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group position-relative has-icon-left mb-4">
|
<div class="form-group position-relative has-icon-left mb-4">
|
||||||
<input type="password" name="password" class="form-control form-control-xl" placeholder="Password">
|
<input type="password" name="password" class="form-control " placeholder="Password">
|
||||||
<div class="form-control-icon"><i class="bi bi-shield-lock"></i></div>
|
<div class="form-control-icon"><i class="bi bi-shield-lock"></i></div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary btn-block btn-lg shadow-lg mt-5">Masuk</button>
|
<button class="btn btn-primary btn-block btn-md shadow-md mt-5">Masuk</button>
|
||||||
</form>
|
</form>
|
||||||
<div class="text-center mt-5 text-lg fs-4">
|
<div class="text-center mt-5 text-md fs-6">
|
||||||
<p class="text-gray-600">Belum punya akun? <a href="{{ route('register') }}" class="font-bold">Daftar</a>.</p>
|
<p class="text-gray-600">Belum punya akun? <a href="{{ route('register') }}" class="font-bold">Daftar</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,40 @@ class="fa fa-shopping-bag me-2 text-primary"></i> Masukkan Keranjang</button>
|
||||||
</a>
|
</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<button type="button" class="btn btn-outline-success rounded-pill px-4" data-bs-toggle="modal"
|
||||||
|
data-bs-target="#chatModal">
|
||||||
|
<i class="fa fa-envelope me-2"></i> Chat Petani
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="modal fade" id="chatModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Kirim Pesan ke {{ $produk->petani->nama_lengkap }}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<form action="{{ route('pesan.kirim') }}" method="POST">
|
||||||
|
@csrf
|
||||||
|
<div class="modal-body">
|
||||||
|
{{-- Hidden Input untuk Target Penerima (Petani) --}}
|
||||||
|
<input type="hidden" name="penerima_id" value="{{ $produk->petani_id }}">
|
||||||
|
<input type="hidden" name="penerima_type" value="App\Models\Petani">
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Isi Pesan</label>
|
||||||
|
<textarea name="isi_pesan" class="form-control" rows="4" required
|
||||||
|
placeholder="Halo, apakah stok produk ini masih ada?"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary"
|
||||||
|
data-bs-dismiss="modal">Batal</button>
|
||||||
|
<button type="submit" class="btn btn-success">Kirim Pesan</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<nav>
|
<nav>
|
||||||
|
|
@ -83,10 +117,12 @@ class="fa fa-shopping-bag me-2 text-primary"></i> Masukkan Keranjang</button>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="tab-content mb-5">
|
<div class="tab-content mb-5">
|
||||||
<div class="tab-pane active" id="nav-about" role="tabpanel" aria-labelledby="nav-about-tab">
|
<div class="tab-pane active" id="nav-about" role="tabpanel"
|
||||||
|
aria-labelledby="nav-about-tab">
|
||||||
<p>{{ $produk->deskripsi }}</p>
|
<p>{{ $produk->deskripsi }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="nav-mission" role="tabpanel" aria-labelledby="nav-mission-tab">
|
<div class="tab-pane" id="nav-mission" role="tabpanel"
|
||||||
|
aria-labelledby="nav-mission-tab">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<img src="{{ asset('template/frontend/img/avatar.jpg') }}"
|
<img src="{{ asset('template/frontend/img/avatar.jpg') }}"
|
||||||
class="img-fluid rounded-circle p-3" style="width: 100px; height: 100px;"
|
class="img-fluid rounded-circle p-3" style="width: 100px; height: 100px;"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
@extends('layouts.frontend')
|
||||||
|
@section('title', 'Pesan Saya')
|
||||||
|
@section('content')
|
||||||
|
<div class="container py-5">
|
||||||
|
<h2 class="mb-4">Pesan Saya</h2>
|
||||||
|
<div class="card shadow-sm border-0">
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
@forelse($chatList as $chat)
|
||||||
|
<a href="{{ route('pembeli.pesan.show', $chat['lawan_id']) }}" class="list-group-item list-group-item-action py-3 d-flex align-items-center">
|
||||||
|
<img src="{{ asset('template/frontend/img/avatar.jpg') }}" class="rounded-circle me-3" width="50" height="50">
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<div class="d-flex w-100 justify-content-between">
|
||||||
|
<h5 class="mb-1 text-primary">{{ $chat['nama'] }}</h5>
|
||||||
|
<small class="text-muted">{{ $chat['time'] }}</small>
|
||||||
|
</div>
|
||||||
|
<p class="mb-1 text-muted">{{ Str::limit($chat['last_message'], 50) }}</p>
|
||||||
|
</div>
|
||||||
|
@if($chat['unread'] > 0)
|
||||||
|
<span class="badge bg-danger rounded-pill ms-2">{{ $chat['unread'] }}</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
@empty
|
||||||
|
<div class="text-center py-5">
|
||||||
|
<i class="fa fa-envelope-open fa-3x text-muted mb-3"></i>
|
||||||
|
<p class="text-muted">Belum ada percakapan. Mulai chat dari halaman detail produk.</p>
|
||||||
|
<a href="{{ route('shop') }}" class="btn btn-outline-primary rounded-pill">Lihat Produk</a>
|
||||||
|
</div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
@extends('layouts.frontend')
|
||||||
|
|
||||||
|
@section('title', 'Chat dengan ' . $lawan->nama_lengkap)
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container-fluid page-header py-5 mb-5">
|
||||||
|
<h1 class="text-center text-white display-6">Percakapan</h1>
|
||||||
|
<ol class="breadcrumb justify-content-center mb-0">
|
||||||
|
<li class="breadcrumb-item"><a href="{{ route('pembeli.pesan.index') }}" class="text-white">Pesan Saya</a></li>
|
||||||
|
<li class="breadcrumb-item active text-white">Detail</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container py-3">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8 col-md-10">
|
||||||
|
|
||||||
|
<div class="card border shadow-sm">
|
||||||
|
<div class="card-header bg-white py-3 border-bottom d-flex align-items-center">
|
||||||
|
<a href="{{ route('pembeli.pesan.index') }}"
|
||||||
|
class="btn btn-sm btn-outline-secondary rounded-circle me-3">
|
||||||
|
<i class="fas fa-arrow-left"></i>
|
||||||
|
</a>
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-0 fw-bold">{{ $lawan->nama_lengkap }}</h6>
|
||||||
|
<small class="text-muted">Petani</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body bg-light overflow-auto" id="chatBox" style="height: 500px;">
|
||||||
|
@foreach ($chats as $chat)
|
||||||
|
@php
|
||||||
|
$isMe =
|
||||||
|
$chat->pengirim_id == Auth::guard('pembeli')->id() &&
|
||||||
|
$chat->pengirim_type == 'App\Models\Pembeli';
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="d-flex mb-3 {{ $isMe ? 'justify-content-end' : 'justify-content-start' }}">
|
||||||
|
|
||||||
|
<div class="p-3 rounded-3"
|
||||||
|
style="max-width: 75%;
|
||||||
|
{{ $isMe ? 'background-color: #0d6efd; color: white;' : 'background-color: white; border: 1px solid #dee2e6;' }}">
|
||||||
|
|
||||||
|
<p class="mb-1">{{ $chat->isi_pesan }}</p>
|
||||||
|
|
||||||
|
<div class="text-end">
|
||||||
|
<small style="font-size: 11px; {{ $isMe ? 'color: #e0e0e0;' : 'color: #6c757d;' }}">
|
||||||
|
{{ $chat->created_at->format('H:i') }}
|
||||||
|
@if ($isMe)
|
||||||
|
<i
|
||||||
|
class="fas fa-check {{ $chat->sudah_dibaca ? 'text-warning' : '' }} ms-1"></i>
|
||||||
|
@endif
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-footer bg-white p-3">
|
||||||
|
<form action="{{ route('pesan.kirim') }}" method="POST">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="penerima_id" value="{{ $lawan->id }}">
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" name="isi_pesan" class="form-control" placeholder="Tulis pesan..."
|
||||||
|
required autocomplete="off">
|
||||||
|
<button type="submit" class="btn btn-primary px-4">
|
||||||
|
<i class="fas fa-paper-plane"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('js')
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
var chatBox = document.getElementById("chatBox");
|
||||||
|
chatBox.scrollTop = chatBox.scrollHeight;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endsection
|
||||||
|
|
@ -113,6 +113,14 @@ class="bi bi-x bi-middle"></i></a>
|
||||||
<span>Pesanan Masuk</span>
|
<span>Pesanan Masuk</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
{{-- Manajemen Kotak Masuk(Pesan) --}}
|
||||||
|
<li class="sidebar-item {{ request()->is('petani/pesan*') ? 'active' : '' }}">
|
||||||
|
<a href="{{ route('petani.pesan.index') }}" class='sidebar-link'>
|
||||||
|
<i class="bi bi-chat-dots-fill"></i>
|
||||||
|
<span>Kotak Masuk</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<li class="sidebar-title">Akun</li>
|
<li class="sidebar-title">Akun</li>
|
||||||
|
|
|
||||||
|
|
@ -97,13 +97,12 @@ class="position-absolute bg-secondary rounded-circle d-flex align-items-center j
|
||||||
class="d-none d-xl-inline ms-1">{{ Auth::guard('pembeli')->user()->nama_lengkap }}</span>
|
class="d-none d-xl-inline ms-1">{{ Auth::guard('pembeli')->user()->nama_lengkap }}</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu m-0 bg-secondary rounded-0">
|
<div class="dropdown-menu m-0 bg-secondary rounded-0">
|
||||||
<!-- Menu Profil -->
|
|
||||||
<a href="#" class="dropdown-item">Profil Saya</a>
|
<a href="#" class="dropdown-item">Profil Saya</a>
|
||||||
|
|
||||||
<!-- Menu Pesanan Saya -->
|
<a href="{{ route('pembeli.pesan.index') }}" class="dropdown-item">Pesan Saya</a>
|
||||||
|
|
||||||
<a href="{{ route('pesanan.saya') }}" class="dropdown-item">Pesanan Saya</a>
|
<a href="{{ route('pesanan.saya') }}" class="dropdown-item">Pesanan Saya</a>
|
||||||
|
|
||||||
<!-- Form Logout -->
|
|
||||||
<form action="{{ route('logout') }}" method="POST">
|
<form action="{{ route('logout') }}" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
<button type="submit" class="dropdown-item">Logout</button>
|
<button type="submit" class="dropdown-item">Logout</button>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
@extends('layouts.admin')
|
||||||
|
@section('title', 'Chat')
|
||||||
|
@section('content')
|
||||||
|
<div class="card" style="height: 75vh;">
|
||||||
|
<div class="row g-0 h-100">
|
||||||
|
<div class="col-md-4 border-end h-100 overflow-auto">
|
||||||
|
<div class="p-3 bg-light border-bottom">
|
||||||
|
<h6 class="mb-0">Daftar Percakapan</h6>
|
||||||
|
</div>
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
@forelse($chatList as $chat)
|
||||||
|
<a href="{{ route('petani.pesan.show', $chat['lawan_id']) }}"
|
||||||
|
class="list-group-item list-group-item-action py-3">
|
||||||
|
<div class="d-flex w-100 justify-content-between">
|
||||||
|
<h6 class="mb-1 text-primary">{{ $chat['nama'] }}</h6>
|
||||||
|
<small class="text-muted" style="font-size: 11px">{{ $chat['time'] }}</small>
|
||||||
|
</div>
|
||||||
|
<p class="mb-1 text-truncate small text-secondary">{{ $chat['last_message'] }}</p>
|
||||||
|
@if ($chat['unread'] > 0)
|
||||||
|
<span class="badge bg-danger rounded-pill">{{ $chat['unread'] }}</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
@empty
|
||||||
|
<div class="p-4 text-center text-muted">Belum ada pesan.</div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-8 h-100 d-flex align-items-center justify-content-center bg-white">
|
||||||
|
<div class="text-center text-muted">
|
||||||
|
<i class="bi bi-chat-dots display-1"></i>
|
||||||
|
<p class="mt-3">Pilih kontak di sebelah kiri untuk mulai chatting.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
@extends('layouts.admin')
|
||||||
|
|
||||||
|
@section('title', 'Chat dengan ' . $lawan->nama_lengkap)
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<section class="section">
|
||||||
|
<div class="card" style="height: 85vh; display: flex; flex-direction: column;">
|
||||||
|
|
||||||
|
{{-- HEADER CHAT --}}
|
||||||
|
<div class="card-header bg-primary py-3 d-flex align-items-center justify-content-between">
|
||||||
|
<div class="d-flex align-items-center text-white">
|
||||||
|
<a href="{{ route('petani.pesan.index') }}" class="btn btn-light btn-sm me-3 rounded-circle"
|
||||||
|
style="width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;">
|
||||||
|
<i class="bi bi-arrow-left text-primary"></i>
|
||||||
|
</a>
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-0 text-white font-bold" style="font-size: 1.1rem;">{{ $lawan->nama_lengkap }}</h6>
|
||||||
|
<small style="opacity: 0.8; font-size: 0.8rem;">
|
||||||
|
{{ $lawan->role ?? 'Pembeli' }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- INBOX SECTION (SCROLLABLE) --}}
|
||||||
|
<div class="card-body p-4" id="chatContainer"
|
||||||
|
style="flex-grow: 1; overflow-y: auto; background-color: #f2f7ff;">
|
||||||
|
<div class="d-flex flex-column gap-3">
|
||||||
|
@forelse($chats as $chat)
|
||||||
|
@php
|
||||||
|
$isMe =
|
||||||
|
$chat->pengirim_id == Auth::guard('petani')->id() &&
|
||||||
|
$chat->pengirim_type == 'App\Models\Petani';
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
{{-- Logic Posisi: Kalau SAYA di Kanan (end), Kalau DIA di Kiri (start) --}}
|
||||||
|
<div class="d-flex w-100 {{ $isMe ? 'justify-content-end' : 'justify-content-start' }}">
|
||||||
|
|
||||||
|
<div style="max-width: 70%; min-width: 120px;">
|
||||||
|
{{-- Bubble Chat --}}
|
||||||
|
<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' }};">
|
||||||
|
|
||||||
|
<p class="mb-1" style="font-size: 0.95rem; line-height: 1.4;">
|
||||||
|
{{ $chat->isi_pesan }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end align-items-center mt-1">
|
||||||
|
<small style="font-size: 0.7rem; opacity: 0.8; margin-right: 4px;">
|
||||||
|
{{ $chat->created_at->format('H:i') }}
|
||||||
|
</small>
|
||||||
|
@if ($isMe)
|
||||||
|
<i class="bi {{ $chat->sudah_dibaca ? 'bi-check-all text-info' : 'bi-check' }}"
|
||||||
|
style="font-size: 0.9rem;"></i>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@empty
|
||||||
|
<div class="text-center my-5">
|
||||||
|
<span class="badge bg-light-secondary text-secondary p-3 rounded-pill">
|
||||||
|
Belum ada percakapan. Mulailah menyapa! 👋
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- FOOTER INPUT PESAN --}}
|
||||||
|
<div class="card-footer bg-white py-3 border-top">
|
||||||
|
<form action="{{ route('pesan.kirim') }}" method="POST" class="d-flex gap-2 align-items-center">
|
||||||
|
@csrf
|
||||||
|
{{-- Input Hidden Data Penerima --}}
|
||||||
|
<input type="hidden" name="penerima_id" value="{{ $lawan->id }}">
|
||||||
|
|
||||||
|
{{-- Input Text --}}
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" name="isi_pesan" class="form-control form-control-lg border-0 bg-light"
|
||||||
|
placeholder="Ketik pesan..." required autocomplete="off" style="border-radius: 20px;">
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg ms-2 rounded-circle"
|
||||||
|
style="width: 50px; height: 50px; display: flex; align-items: center; justify-content: center;">
|
||||||
|
<i class="bi bi-send-fill fs-5"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
var chatBox = document.getElementById("chatContainer");
|
||||||
|
if (chatBox) {
|
||||||
|
chatBox.scrollTop = chatBox.scrollHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endsection
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
use App\Http\Controllers\AdminController;
|
use App\Http\Controllers\AdminController;
|
||||||
use App\Http\Controllers\CartController;
|
use App\Http\Controllers\CartController;
|
||||||
use App\Http\Controllers\LandingController;
|
use App\Http\Controllers\LandingController;
|
||||||
|
use App\Http\Controllers\PesanController;
|
||||||
use App\Http\Controllers\Petani\DashboardController;
|
use App\Http\Controllers\Petani\DashboardController;
|
||||||
use App\Http\Controllers\Petani\ProdukController;
|
use App\Http\Controllers\Petani\ProdukController;
|
||||||
use App\Http\Controllers\TransaksiController;
|
use App\Http\Controllers\TransaksiController;
|
||||||
|
|
@ -25,6 +26,11 @@
|
||||||
Route::post('/cart/add', [CartController::class, 'addToCart'])->name('cart.add');
|
Route::post('/cart/add', [CartController::class, 'addToCart'])->name('cart.add');
|
||||||
Route::delete('/cart/remove', [CartController::class, 'remove'])->name('cart.remove');
|
Route::delete('/cart/remove', [CartController::class, 'remove'])->name('cart.remove');
|
||||||
|
|
||||||
|
// --- ROUTE GLOBAL (BISA DIAKSES PETANI & PEMBELI) ---
|
||||||
|
Route::post('/pesan/kirim', [PesanController::class, 'store'])
|
||||||
|
->middleware('auth:pembeli,petani')
|
||||||
|
->name('pesan.kirim');
|
||||||
|
|
||||||
|
|
||||||
// --- AUTH ROUTES (Guest Only) ---
|
// --- AUTH ROUTES (Guest Only) ---
|
||||||
Route::middleware('guest')->group(function () {
|
Route::middleware('guest')->group(function () {
|
||||||
|
|
@ -55,6 +61,10 @@
|
||||||
|
|
||||||
Route::patch('/cart/update', [CartController::class, 'updateCart'])->name('cart.update');
|
Route::patch('/cart/update', [CartController::class, 'updateCart'])->name('cart.update');
|
||||||
Route::delete('/cart/remove', [CartController::class, 'remove'])->name('cart.remove');
|
Route::delete('/cart/remove', [CartController::class, 'remove'])->name('cart.remove');
|
||||||
|
|
||||||
|
// Route Pesan untuk Pembeli
|
||||||
|
Route::get('/pesan', [PesanController::class, 'index'])->name('pembeli.pesan.index');
|
||||||
|
Route::get('/pesan/{id}', [PesanController::class, 'show'])->name('pembeli.pesan.show');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -84,4 +94,8 @@
|
||||||
Route::get('/petani/pesanan', [TransaksiController::class, 'pesananMasuk'])->name('petani.pesanan.index');
|
Route::get('/petani/pesanan', [TransaksiController::class, 'pesananMasuk'])->name('petani.pesanan.index');
|
||||||
Route::patch('/petani/pesanan/{id}', [TransaksiController::class, 'updateStatus'])->name('petani.pesanan.update');
|
Route::patch('/petani/pesanan/{id}', [TransaksiController::class, 'updateStatus'])->name('petani.pesanan.update');
|
||||||
Route::get('/petani/pesanan/{id}', [TransaksiController::class, 'pesananDetail'])->name('petani.pesanan.detail');
|
Route::get('/petani/pesanan/{id}', [TransaksiController::class, 'pesananDetail'])->name('petani.pesanan.detail');
|
||||||
|
|
||||||
|
// Route Pesan untuk Petani
|
||||||
|
Route::get('/petani/pesan', [PesanController::class, 'index'])->name('petani.pesan.index');
|
||||||
|
Route::get('/petani/pesan/{id}', [PesanController::class, 'show'])->name('petani.pesan.show');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue