feat: Implement admin-scoped data visibility and comprehensive authorization
- Add admin_id FK to kontrakans table - Add authorization checks to KontrakanController (edit, update, destroy, bulkDestroy) - Add authorization checks to BookingController (store, bulkDestroy) - Fix EnsureRole middleware to use auth:admin guard - Fix booking index stats to filter by admin ownership - Add model relationships: Admin.kontrakans(), Admin.isSuperAdmin(), Admin.isAdmin() - Ensure admin biasa sees only their data, super_admin sees all data - Secure bulk delete operations with per-item admin validation
This commit is contained in:
parent
e29c87645b
commit
6bad1aa7b2
|
|
@ -19,6 +19,18 @@ public function index(Request $request)
|
||||||
{
|
{
|
||||||
$query = Booking::with(['kontrakan', 'user'])->latest();
|
$query = Booking::with(['kontrakan', 'user'])->latest();
|
||||||
|
|
||||||
|
// ========== FILTER: ADMIN (Hanya booking dari kontrakan milik admin) ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin) {
|
||||||
|
if ($admin->role !== 'super_admin') {
|
||||||
|
// Admin biasa hanya bisa lihat booking dari kontrakan milik mereka
|
||||||
|
$query->whereHas('kontrakan', function($q) use ($admin) {
|
||||||
|
$q->where('admin_id', $admin->id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Super admin bisa lihat semua booking
|
||||||
|
}
|
||||||
|
|
||||||
// Filter by status
|
// Filter by status
|
||||||
if ($request->filled('status')) {
|
if ($request->filled('status')) {
|
||||||
$query->where('status', $request->status);
|
$query->where('status', $request->status);
|
||||||
|
|
@ -30,7 +42,15 @@ public function index(Request $request)
|
||||||
}
|
}
|
||||||
|
|
||||||
$bookings = $query->paginate(15);
|
$bookings = $query->paginate(15);
|
||||||
$kontrakans = Kontrakan::orderBy('nama')->get();
|
|
||||||
|
// ========== GET KONTRAKANS (sesuai filter admin) ==========
|
||||||
|
$kontrakanQuery = Kontrakan::orderBy('nama');
|
||||||
|
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
$kontrakanQuery->where('admin_id', $admin->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$kontrakans = $kontrakanQuery->get();
|
||||||
|
|
||||||
return view('admin.bookings.index', compact('bookings', 'kontrakans'));
|
return view('admin.bookings.index', compact('bookings', 'kontrakans'));
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +60,15 @@ public function index(Request $request)
|
||||||
*/
|
*/
|
||||||
public function create(Request $request)
|
public function create(Request $request)
|
||||||
{
|
{
|
||||||
$kontrakans = Kontrakan::where('status', 'available')->orderBy('nama')->get();
|
$kontrakanQuery = Kontrakan::where('status', 'available')->orderBy('nama');
|
||||||
|
|
||||||
|
// ========== FILTER: ADMIN (Hanya kontrakan milik admin) ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
$kontrakanQuery->where('admin_id', $admin->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$kontrakans = $kontrakanQuery->get();
|
||||||
$selectedKontrakan = null;
|
$selectedKontrakan = null;
|
||||||
|
|
||||||
if ($request->filled('kontrakan_id')) {
|
if ($request->filled('kontrakan_id')) {
|
||||||
|
|
@ -79,6 +107,14 @@ public function store(Request $request)
|
||||||
// Lock kontrakan row untuk mencegah double booking
|
// Lock kontrakan row untuk mencegah double booking
|
||||||
$kontrakan = Kontrakan::lockForUpdate()->findOrFail($request->kontrakan_id);
|
$kontrakan = Kontrakan::lockForUpdate()->findOrFail($request->kontrakan_id);
|
||||||
|
|
||||||
|
// ========== AUTHORIZATION: Cek apakah admin punya akses ke kontrakan ini ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($kontrakan->admin_id !== $admin->id) {
|
||||||
|
throw new Exception('Anda tidak memiliki akses ke kontrakan ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cek konflik dengan booking aktif lainnya
|
// Cek konflik dengan booking aktif lainnya
|
||||||
$hasConflict = Booking::hasConflict(
|
$hasConflict = Booking::hasConflict(
|
||||||
$request->kontrakan_id,
|
$request->kontrakan_id,
|
||||||
|
|
@ -196,6 +232,15 @@ public function userStore(Request $request)
|
||||||
*/
|
*/
|
||||||
public function show(Booking $booking)
|
public function show(Booking $booking)
|
||||||
{
|
{
|
||||||
|
// ========== AUTHORIZATION: Cek apakah admin punya akses ke booking ini ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
// Admin biasa hanya bisa lihat booking dari kontrakan milik mereka
|
||||||
|
if ($booking->kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke booking ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$booking->load(['kontrakan', 'user']);
|
$booking->load(['kontrakan', 'user']);
|
||||||
return view('admin.bookings.show', compact('booking'));
|
return view('admin.bookings.show', compact('booking'));
|
||||||
}
|
}
|
||||||
|
|
@ -205,11 +250,26 @@ public function show(Booking $booking)
|
||||||
*/
|
*/
|
||||||
public function edit(Booking $booking)
|
public function edit(Booking $booking)
|
||||||
{
|
{
|
||||||
|
// ========== AUTHORIZATION: Cek apakah admin punya akses ke booking ini ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
// Admin biasa hanya bisa edit booking dari kontrakan milik mereka
|
||||||
|
if ($booking->kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke booking ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!in_array($booking->status, [Booking::STATUS_PENDING, Booking::STATUS_CONFIRMED])) {
|
if (!in_array($booking->status, [Booking::STATUS_PENDING, Booking::STATUS_CONFIRMED])) {
|
||||||
return back()->with('error', 'Booking tidak bisa diedit karena statusnya: ' . $booking->status_label);
|
return back()->with('error', 'Booking tidak bisa diedit karena statusnya: ' . $booking->status_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
$kontrakans = Kontrakan::orderBy('nama')->get();
|
$kontrakanQuery = Kontrakan::orderBy('nama');
|
||||||
|
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
$kontrakanQuery->where('admin_id', $admin->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$kontrakans = $kontrakanQuery->get();
|
||||||
return view('admin.bookings.edit', compact('booking', 'kontrakans'));
|
return view('admin.bookings.edit', compact('booking', 'kontrakans'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,6 +278,14 @@ public function edit(Booking $booking)
|
||||||
*/
|
*/
|
||||||
public function update(Request $request, Booking $booking)
|
public function update(Request $request, Booking $booking)
|
||||||
{
|
{
|
||||||
|
// ========== AUTHORIZATION: Cek apakah admin punya akses ke booking ini ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($booking->kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke booking ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!in_array($booking->status, [Booking::STATUS_PENDING, Booking::STATUS_CONFIRMED])) {
|
if (!in_array($booking->status, [Booking::STATUS_PENDING, Booking::STATUS_CONFIRMED])) {
|
||||||
return back()->with('error', 'Booking tidak bisa diedit.');
|
return back()->with('error', 'Booking tidak bisa diedit.');
|
||||||
}
|
}
|
||||||
|
|
@ -274,6 +342,14 @@ public function update(Request $request, Booking $booking)
|
||||||
*/
|
*/
|
||||||
public function confirm(Booking $booking)
|
public function confirm(Booking $booking)
|
||||||
{
|
{
|
||||||
|
// ========== AUTHORIZATION ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($booking->kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke booking ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($booking->confirm()) {
|
if ($booking->confirm()) {
|
||||||
return back()->with('success', 'Booking berhasil dikonfirmasi! Status kontrakan diubah menjadi "Dipesan".');
|
return back()->with('success', 'Booking berhasil dikonfirmasi! Status kontrakan diubah menjadi "Dipesan".');
|
||||||
}
|
}
|
||||||
|
|
@ -286,6 +362,14 @@ public function confirm(Booking $booking)
|
||||||
*/
|
*/
|
||||||
public function checkIn(Booking $booking)
|
public function checkIn(Booking $booking)
|
||||||
{
|
{
|
||||||
|
// ========== AUTHORIZATION ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($booking->kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke booking ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($booking->checkIn()) {
|
if ($booking->checkIn()) {
|
||||||
return back()->with('success', 'Check-in berhasil! Penyewa sudah masuk. Status kontrakan diubah menjadi "Terisi".');
|
return back()->with('success', 'Check-in berhasil! Penyewa sudah masuk. Status kontrakan diubah menjadi "Terisi".');
|
||||||
}
|
}
|
||||||
|
|
@ -298,6 +382,14 @@ public function checkIn(Booking $booking)
|
||||||
*/
|
*/
|
||||||
public function checkOut(Booking $booking)
|
public function checkOut(Booking $booking)
|
||||||
{
|
{
|
||||||
|
// ========== AUTHORIZATION ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($booking->kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke booking ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($booking->checkOut()) {
|
if ($booking->checkOut()) {
|
||||||
return back()->with('success', 'Check-out berhasil! Penyewa sudah keluar. Status kontrakan kembali tersedia.');
|
return back()->with('success', 'Check-out berhasil! Penyewa sudah keluar. Status kontrakan kembali tersedia.');
|
||||||
}
|
}
|
||||||
|
|
@ -310,6 +402,14 @@ public function checkOut(Booking $booking)
|
||||||
*/
|
*/
|
||||||
public function cancel(Request $request, Booking $booking)
|
public function cancel(Request $request, Booking $booking)
|
||||||
{
|
{
|
||||||
|
// ========== AUTHORIZATION ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($booking->kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke booking ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$reason = $request->input('cancellation_reason');
|
$reason = $request->input('cancellation_reason');
|
||||||
|
|
||||||
if ($booking->cancel($reason)) {
|
if ($booking->cancel($reason)) {
|
||||||
|
|
@ -324,6 +424,14 @@ public function cancel(Request $request, Booking $booking)
|
||||||
*/
|
*/
|
||||||
public function markPaid(Request $request, Booking $booking)
|
public function markPaid(Request $request, Booking $booking)
|
||||||
{
|
{
|
||||||
|
// ========== AUTHORIZATION ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($booking->kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke booking ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$method = $request->input('payment_method', 'cash');
|
$method = $request->input('payment_method', 'cash');
|
||||||
|
|
||||||
if ($booking->markAsPaid($method)) {
|
if ($booking->markAsPaid($method)) {
|
||||||
|
|
@ -338,6 +446,14 @@ public function markPaid(Request $request, Booking $booking)
|
||||||
*/
|
*/
|
||||||
public function togglePaymentStatus(Request $request, Booking $booking)
|
public function togglePaymentStatus(Request $request, Booking $booking)
|
||||||
{
|
{
|
||||||
|
// ========== AUTHORIZATION ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($booking->kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke booking ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($booking->payment_status === 'paid') {
|
if ($booking->payment_status === 'paid') {
|
||||||
// Set ke belum lunas
|
// Set ke belum lunas
|
||||||
$booking->update([
|
$booking->update([
|
||||||
|
|
@ -362,6 +478,14 @@ public function togglePaymentStatus(Request $request, Booking $booking)
|
||||||
*/
|
*/
|
||||||
public function destroy(Booking $booking)
|
public function destroy(Booking $booking)
|
||||||
{
|
{
|
||||||
|
// ========== AUTHORIZATION ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($booking->kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke booking ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// delete() akan trigger boot()->deleted() yang auto sync status kontrakan
|
// delete() akan trigger boot()->deleted() yang auto sync status kontrakan
|
||||||
$booking->delete();
|
$booking->delete();
|
||||||
|
|
||||||
|
|
@ -383,6 +507,15 @@ public function bulkDestroy(Request $request)
|
||||||
|
|
||||||
$query = Booking::query();
|
$query = Booking::query();
|
||||||
|
|
||||||
|
// ========== AUTHORIZATION: Filter berdasarkan admin ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
// Admin biasa hanya bisa hapus booking dari kontrakan milik mereka
|
||||||
|
$query->whereHas('kontrakan', function($q) use ($admin) {
|
||||||
|
$q->where('admin_id', $admin->id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if ($request->action === 'selected') {
|
if ($request->action === 'selected') {
|
||||||
$ids = $request->input('booking_ids', []);
|
$ids = $request->input('booking_ids', []);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,16 @@ public function index(Request $request)
|
||||||
// Query builder
|
// Query builder
|
||||||
$query = Kontrakan::query();
|
$query = Kontrakan::query();
|
||||||
|
|
||||||
|
// ========== FILTER: ADMIN (Hanya kontrakan milik admin yang login) ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin) {
|
||||||
|
if ($admin->role !== 'super_admin') {
|
||||||
|
// Admin biasa hanya bisa lihat kontrakan milik mereka
|
||||||
|
$query->where('admin_id', $admin->id);
|
||||||
|
}
|
||||||
|
// Super admin bisa lihat semua kontrakan
|
||||||
|
}
|
||||||
|
|
||||||
// ========== FILTER: SEARCH (Nama, Alamat, Fasilitas) ==========
|
// ========== FILTER: SEARCH (Nama, Alamat, Fasilitas) ==========
|
||||||
if ($request->filled('search')) {
|
if ($request->filled('search')) {
|
||||||
$search = $request->search;
|
$search = $request->search;
|
||||||
|
|
@ -212,8 +222,9 @@ public function store(Request $request)
|
||||||
$file->move(public_path('uploads/kontrakan'), $filename);
|
$file->move(public_path('uploads/kontrakan'), $filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simpan data ke database (DENGAN WHATSAPP)
|
// Simpan data ke database (DENGAN WHATSAPP & ADMIN_ID)
|
||||||
$kontrakan = Kontrakan::create([
|
$kontrakan = Kontrakan::create([
|
||||||
|
'admin_id' => auth()->guard('admin')->id(),
|
||||||
'nama' => $request->nama,
|
'nama' => $request->nama,
|
||||||
'alamat' => $request->alamat,
|
'alamat' => $request->alamat,
|
||||||
'no_whatsapp' => $request->no_whatsapp,
|
'no_whatsapp' => $request->no_whatsapp,
|
||||||
|
|
@ -268,6 +279,14 @@ public function edit($id)
|
||||||
->with('error', 'Data kontrakan tidak ditemukan!');
|
->with('error', 'Data kontrakan tidak ditemukan!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== AUTHORIZATION: Cek apakah admin punya akses ke kontrakan ini ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke kontrakan ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return view('kontrakan.edit', compact('kontrakan'));
|
return view('kontrakan.edit', compact('kontrakan'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -283,6 +302,14 @@ public function update(Request $request, $id)
|
||||||
->with('error', 'Data kontrakan tidak ditemukan!');
|
->with('error', 'Data kontrakan tidak ditemukan!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== AUTHORIZATION: Cek apakah admin punya akses ke kontrakan ini ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses ke kontrakan ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Validasi input (DENGAN WHATSAPP)
|
// Validasi input (DENGAN WHATSAPP)
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'nama' => 'required|string|max:255',
|
'nama' => 'required|string|max:255',
|
||||||
|
|
@ -350,12 +377,6 @@ public function update(Request $request, $id)
|
||||||
*/
|
*/
|
||||||
public function destroy($id)
|
public function destroy($id)
|
||||||
{
|
{
|
||||||
// Proteksi Role - Hanya Admin dan Super Admin yang bisa hapus
|
|
||||||
if (!in_array(auth()->user()->role, ['admin', 'super_admin'])) {
|
|
||||||
return redirect()->route('kontrakan.index')
|
|
||||||
->with('error', 'Anda tidak memiliki akses untuk menghapus data!');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cari kontrakan berdasarkan ID
|
// Cari kontrakan berdasarkan ID
|
||||||
$kontrakan = Kontrakan::find($id);
|
$kontrakan = Kontrakan::find($id);
|
||||||
|
|
||||||
|
|
@ -364,6 +385,14 @@ public function destroy($id)
|
||||||
->with('error', 'Data kontrakan tidak ditemukan atau sudah dihapus sebelumnya!');
|
->with('error', 'Data kontrakan tidak ditemukan atau sudah dihapus sebelumnya!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== AUTHORIZATION: Cek apakah admin punya akses ke kontrakan ini ==========
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($kontrakan->admin_id !== $admin->id) {
|
||||||
|
abort(403, 'Anda tidak memiliki akses untuk menghapus kontrakan ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Hapus foto dari folder sebelum hapus data
|
// Hapus foto dari folder sebelum hapus data
|
||||||
if ($kontrakan->foto && File::exists(public_path('uploads/kontrakan/' . $kontrakan->foto))) {
|
if ($kontrakan->foto && File::exists(public_path('uploads/kontrakan/' . $kontrakan->foto))) {
|
||||||
File::delete(public_path('uploads/kontrakan/' . $kontrakan->foto));
|
File::delete(public_path('uploads/kontrakan/' . $kontrakan->foto));
|
||||||
|
|
@ -387,12 +416,6 @@ public function destroy($id)
|
||||||
*/
|
*/
|
||||||
public function bulkDestroy(Request $request)
|
public function bulkDestroy(Request $request)
|
||||||
{
|
{
|
||||||
// Proteksi Role - Hanya Admin dan Super Admin yang bisa hapus
|
|
||||||
if (!in_array(auth()->user()->role, ['admin', 'super_admin'])) {
|
|
||||||
return redirect()->route('kontrakan.index')
|
|
||||||
->with('error', 'Anda tidak memiliki akses untuk menghapus data!');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle both JSON string and array input
|
// Handle both JSON string and array input
|
||||||
$ids = $request->ids;
|
$ids = $request->ids;
|
||||||
if (is_string($ids)) {
|
if (is_string($ids)) {
|
||||||
|
|
@ -409,12 +432,19 @@ public function bulkDestroy(Request $request)
|
||||||
|
|
||||||
$deletedCount = 0;
|
$deletedCount = 0;
|
||||||
$deletedNames = [];
|
$deletedNames = [];
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
|
||||||
// Loop dan hapus satu per satu
|
// Loop dan hapus satu per satu
|
||||||
foreach ($request->ids as $id) {
|
foreach ($request->ids as $id) {
|
||||||
$kontrakan = Kontrakan::find($id);
|
$kontrakan = Kontrakan::find($id);
|
||||||
|
|
||||||
if ($kontrakan) {
|
if ($kontrakan) {
|
||||||
|
// ========== AUTHORIZATION: Cek apakah admin punya akses ke kontrakan ini ==========
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
if ($kontrakan->admin_id !== $admin->id) {
|
||||||
|
continue; // Skip kontrakan yang bukan milik admin
|
||||||
|
}
|
||||||
|
}
|
||||||
$deletedNames[] = $kontrakan->nama;
|
$deletedNames[] = $kontrakan->nama;
|
||||||
|
|
||||||
// Hapus foto jika ada
|
// Hapus foto jika ada
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,13 @@ class EnsureRole
|
||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next, string ...$roles)
|
public function handle(Request $request, Closure $next, string ...$roles)
|
||||||
{
|
{
|
||||||
if (!Auth::check()) {
|
if (!Auth::guard('admin')->check()) {
|
||||||
return redirect()->route('admin.login');
|
return redirect()->route('admin.login');
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = Auth::user();
|
$admin = Auth::guard('admin')->user();
|
||||||
|
|
||||||
if (!in_array($user->role, $roles, true)) {
|
if (!in_array($admin->role, $roles, true)) {
|
||||||
abort(403, 'Akses ditolak. Anda tidak memiliki izin untuk halaman ini.');
|
abort(403, 'Akses ditolak. Anda tidak memiliki izin untuk halaman ini.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,4 +44,32 @@ protected function casts(): array
|
||||||
'password' => 'hashed',
|
'password' => 'hashed',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ========== RELASI KONTRAKAN ==========
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relasi: 1 Admin punya banyak Kontrakan
|
||||||
|
*/
|
||||||
|
public function kontrakans()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Kontrakan::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper: Cek apakah admin adalah super_admin
|
||||||
|
*/
|
||||||
|
public function isSuperAdmin()
|
||||||
|
{
|
||||||
|
return $this->role === 'super_admin';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper: Cek apakah admin adalah admin biasa
|
||||||
|
*/
|
||||||
|
public function isAdmin()
|
||||||
|
{
|
||||||
|
return $this->role === 'admin';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ class Kontrakan extends Model
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
'admin_id',
|
||||||
'nama',
|
'nama',
|
||||||
'alamat',
|
'alamat',
|
||||||
'no_whatsapp',
|
'no_whatsapp',
|
||||||
|
|
@ -31,6 +32,18 @@ class Kontrakan extends Model
|
||||||
'availability_confirmed_at' => 'datetime',
|
'availability_confirmed_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ========== RELASI ADMIN ==========
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relasi: 1 Kontrakan dimiliki oleh 1 Admin
|
||||||
|
*/
|
||||||
|
public function admin()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Admin::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate Google Maps URL
|
* Generate Google Maps URL
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,14 @@
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::table('users', function (Blueprint $table) {
|
Schema::table('users', function (Blueprint $table) {
|
||||||
// Add role column with default 'user'
|
// Add role column with default 'user' (if not already exists)
|
||||||
$table->string('role')->default('user')->after('password');
|
if (!Schema::hasColumn('users', 'role')) {
|
||||||
// Add phone column (nullable)
|
$table->string('role')->default('user')->after('password');
|
||||||
$table->string('phone')->nullable()->after('role');
|
}
|
||||||
|
// Add phone column (nullable) (if not already exists)
|
||||||
|
if (!Schema::hasColumn('users', 'phone')) {
|
||||||
|
$table->string('phone')->nullable()->after('role');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?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(): void
|
||||||
|
{
|
||||||
|
Schema::table('kontrakans', function (Blueprint $table) {
|
||||||
|
// Tambah kolom admin_id sebagai foreign key ke admins table
|
||||||
|
$table->foreignId('admin_id')
|
||||||
|
->nullable()
|
||||||
|
->after('id')
|
||||||
|
->constrained('admins')
|
||||||
|
->onDelete('set null');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('kontrakans', function (Blueprint $table) {
|
||||||
|
$table->dropForeignKey(['admin_id']);
|
||||||
|
$table->dropColumn('admin_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -76,6 +76,18 @@
|
||||||
{{-- Stats Cards --}}
|
{{-- Stats Cards --}}
|
||||||
<div class="row g-3 mb-4">
|
<div class="row g-3 mb-4">
|
||||||
@php
|
@php
|
||||||
|
// Filter stats berdasarkan admin yang login
|
||||||
|
$admin = auth()->guard('admin')->user();
|
||||||
|
$statsQuery = \App\Models\Booking::query();
|
||||||
|
|
||||||
|
if ($admin && $admin->role !== 'super_admin') {
|
||||||
|
// Admin biasa: hanya lihat booking dari kontrakan milik mereka
|
||||||
|
$statsQuery->whereHas('kontrakan', function($q) use ($admin) {
|
||||||
|
$q->where('admin_id', $admin->id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Super admin: lihat semua
|
||||||
|
|
||||||
$stats = [
|
$stats = [
|
||||||
['status' => 'pending', 'label' => 'Menunggu', 'color' => 'warning', 'icon' => 'hourglass-split'],
|
['status' => 'pending', 'label' => 'Menunggu', 'color' => 'warning', 'icon' => 'hourglass-split'],
|
||||||
['status' => 'confirmed', 'label' => 'Dikonfirmasi', 'color' => 'info', 'icon' => 'check-circle'],
|
['status' => 'confirmed', 'label' => 'Dikonfirmasi', 'color' => 'info', 'icon' => 'check-circle'],
|
||||||
|
|
@ -89,7 +101,7 @@
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<i class="bi bi-{{ $stat['icon'] }} text-{{ $stat['color'] }} fs-3"></i>
|
<i class="bi bi-{{ $stat['icon'] }} text-{{ $stat['color'] }} fs-3"></i>
|
||||||
<h4 class="fw-bold mb-0 mt-2">
|
<h4 class="fw-bold mb-0 mt-2">
|
||||||
{{ \App\Models\Booking::where('status', $stat['status'])->count() }}
|
{{ (clone $statsQuery)->where('status', $stat['status'])->count() }}
|
||||||
</h4>
|
</h4>
|
||||||
<small class="text-muted">{{ $stat['label'] }}</small>
|
<small class="text-muted">{{ $stat['label'] }}</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue