539 lines
21 KiB
PHP
539 lines
21 KiB
PHP
<div class="navbar">
|
|
<div class="container-fluid px-3">
|
|
<div class="toggle-btn" onclick="toggleSidebar()">
|
|
<img src="{{ asset('icon/menu-burger.svg') }}" alt="Menu Toggle" class="menu-burger-icon">
|
|
</div>
|
|
<a href="{{ url()->current() }}" class="dashboard-text no-select">@yield('page-title')</a>
|
|
|
|
<!-- Menampilkan nama pengguna yang login -->
|
|
@if(Auth::check())
|
|
<div class="notifications-dropdown mr-3">
|
|
<div class="notification-icon" id="notificationDropdown">
|
|
<i class="fas fa-bell"></i>
|
|
@php
|
|
$notifications = \Illuminate\Notifications\DatabaseNotification::where('notifiable_id', Auth::id())
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
|
|
$unreadCount = $notifications->whereNull('read_at')->count();
|
|
|
|
// Debug untuk melihat data
|
|
// dd([
|
|
// 'user_id' => Auth::id(),
|
|
// 'notifications' => $notifications,
|
|
// 'unread_count' => $unreadCount
|
|
// ]);
|
|
@endphp
|
|
@if($unreadCount > 0)
|
|
<span class="badge badge-danger" id="notification-badge">{{ $unreadCount }}</span>
|
|
@else
|
|
<span class="badge badge-danger" id="notification-badge" style="display: none;">0</span>
|
|
@endif
|
|
</div>
|
|
<div class="dropdown-menu notification-menu" style="display: none;">
|
|
<div class="notification-header">
|
|
<span>Notifikasi</span>
|
|
<div class="notification-actions">
|
|
@if($unreadCount > 0)
|
|
<a href="#" class="mark-all-read">Tandai sudah dibaca semua</a>
|
|
@endif
|
|
<a href="#" class="delete-read-notifications">Hapus yang sudah dibaca</a>
|
|
</div>
|
|
</div>
|
|
<div class="notification-list">
|
|
@forelse($notifications as $notification)
|
|
<div class="notification-item {{ is_null($notification->read_at) ? 'unread' : 'read' }}"
|
|
data-notification-id="{{ $notification->id }}"
|
|
data-type="{{ $notification->data['type'] ?? '' }}">
|
|
<div class="notification-content">
|
|
{{ $notification->data['message'] }}
|
|
<small class="text-muted d-block">
|
|
{{ \Carbon\Carbon::parse($notification->created_at)->diffForHumans() }}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="no-notifications">
|
|
Tidak ada notifikasi
|
|
</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
@if(Auth::check())
|
|
<div class="profile float-right">
|
|
|
|
<img src="{{ asset('icon/profile.png') }}" alt="Profile" width="40" height="40" id="profile-menu">
|
|
<div class="dropdown-menu" style="display: none;">
|
|
<a class="dropdown-item" href="/logout" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">Logout</a>
|
|
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
|
|
@csrf
|
|
</form>
|
|
</div>
|
|
<div class="mr-3">{{ Auth::user()->name }}</div>
|
|
</div>
|
|
@else
|
|
<div class="profile float-right">
|
|
<img src="{{ asset('icon/profile.png') }}" alt="Profile" width="40" height="40" id="profile-menu">
|
|
<div class="dropdown-menu" style="display: none;">
|
|
<a class="dropdown-item" href="/login">Login</a>
|
|
<a class="dropdown-item" href="/register">Register</a>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Elemen audio untuk suara notifikasi -->
|
|
<audio id="notification-sound" preload="auto">
|
|
<source src="{{ asset('icon/notifikasi.mp3') }}" type="audio/mpeg">
|
|
<source src="{{ asset('icon/notifikasi.wav') }}" type="audio/wav">
|
|
</audio>
|
|
|
|
<script src="https://js.pusher.com/7.0/pusher.min.js"></script>
|
|
<script>
|
|
// Script di bawah ini harus ditambahkan di bagian atas untuk memastikan CSRF token tersedia
|
|
$.ajaxSetup({
|
|
headers: {
|
|
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
|
}
|
|
});
|
|
|
|
// Inisialisasi Pusher dengan konfigurasi yang benar
|
|
var pusher = new Pusher('d82783da6b2fe32e1dec', {
|
|
cluster: 'ap1',
|
|
authEndpoint: '/broadcasting/auth',
|
|
auth: {
|
|
headers: {
|
|
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
|
}
|
|
},
|
|
// Tambahkan opsi debug untuk melihat log Pusher di console browser
|
|
enabledTransports: ['ws', 'wss'],
|
|
forceTLS: true,
|
|
enableLogging: true
|
|
});
|
|
|
|
// Mendengarkan channel publik laporan-channel
|
|
var laporanChannel = pusher.subscribe('laporan-channel');
|
|
laporanChannel.bind('LaporanDibuat', function(data) {
|
|
console.log('Laporan baru diterima:', data);
|
|
showToast('Laporan baru: ' + (data.laporan.jenis_bencana || data.laporan.jenis_rusak || 'Laporan'));
|
|
// Play notification sound
|
|
playNotificationSound();
|
|
});
|
|
|
|
// Mendengarkan notifikasi real-time pada private channel
|
|
@if(Auth::check())
|
|
console.log('Subscribing to private-notifications.{{ Auth::id() }}');
|
|
var notificationChannel = pusher.subscribe('private-notifications.{{ Auth::id() }}');
|
|
|
|
notificationChannel.bind('notification.received', function(data) {
|
|
console.log('Notifikasi baru diterima:', data);
|
|
|
|
// Update badge count
|
|
var badge = document.getElementById('notification-badge');
|
|
var currentCount = parseInt(badge.innerText) || 0;
|
|
badge.innerText = currentCount + 1;
|
|
badge.style.display = 'block';
|
|
|
|
// Tambahkan notifikasi baru ke daftar
|
|
var notificationList = document.querySelector('.notification-list');
|
|
var noNotificationsMsg = document.querySelector('.no-notifications');
|
|
|
|
if (noNotificationsMsg) {
|
|
noNotificationsMsg.remove();
|
|
}
|
|
|
|
// Format waktu dengan Carbon
|
|
var timeAgo = 'baru saja';
|
|
|
|
// Buat elemen notifikasi baru
|
|
var newNotification = document.createElement('div');
|
|
newNotification.className = 'notification-item unread';
|
|
newNotification.dataset.notificationId = data.notification.id;
|
|
newNotification.dataset.type = data.notification.data.type || '';
|
|
|
|
newNotification.innerHTML = `
|
|
<div class="notification-content">
|
|
${data.notification.data.message}
|
|
<small class="text-muted d-block">${timeAgo}</small>
|
|
</div>
|
|
`;
|
|
|
|
// Tambahkan ke awal daftar
|
|
if (notificationList.firstChild) {
|
|
notificationList.insertBefore(newNotification, notificationList.firstChild);
|
|
} else {
|
|
notificationList.appendChild(newNotification);
|
|
}
|
|
|
|
// Tambahkan tombol "Tandai sudah dibaca semua" jika belum ada
|
|
var markAllReadBtn = document.querySelector('.mark-all-read');
|
|
var notificationActions = document.querySelector('.notification-actions');
|
|
if (!markAllReadBtn && notificationActions) {
|
|
var newMarkAllBtn = document.createElement('a');
|
|
newMarkAllBtn.href = '#';
|
|
newMarkAllBtn.className = 'mark-all-read';
|
|
newMarkAllBtn.innerText = 'Tandai sudah dibaca semua';
|
|
|
|
// Tambahkan event listener ke tombol baru
|
|
newMarkAllBtn.addEventListener('click', handleMarkAllRead);
|
|
|
|
notificationActions.insertBefore(newMarkAllBtn, notificationActions.firstChild);
|
|
}
|
|
|
|
// Tambahkan juga event listener untuk notifikasi baru
|
|
newNotification.addEventListener('click', function() {
|
|
handleNotificationClick(this);
|
|
});
|
|
|
|
// Tampilkan notifikasi toast
|
|
showToast('Notifikasi baru: ' + data.notification.data.message);
|
|
|
|
// Play notification sound
|
|
playNotificationSound();
|
|
});
|
|
|
|
// Debug untuk pusher errors
|
|
pusher.connection.bind('error', function(err) {
|
|
console.error('Pusher connection error:', err);
|
|
});
|
|
|
|
// Debug connection state
|
|
pusher.connection.bind('state_change', function(states) {
|
|
console.log('Pusher state changed from', states.previous, 'to', states.current);
|
|
});
|
|
@endif
|
|
|
|
// Fungsi untuk memainkan suara notifikasi
|
|
function playNotificationSound() {
|
|
var sound = document.getElementById('notification-sound');
|
|
if (sound) {
|
|
sound.currentTime = 0; // Reset audio ke awal
|
|
sound.play().catch(function(error) {
|
|
console.log('Gagal memainkan suara notifikasi:', error);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Fungsi untuk menampilkan toast notifikasi
|
|
function showToast(message) {
|
|
// Cek apakah elemen toast container sudah ada
|
|
var toastContainer = document.getElementById('toast-container');
|
|
if (!toastContainer) {
|
|
// Buat container untuk toast
|
|
toastContainer = document.createElement('div');
|
|
toastContainer.id = 'toast-container';
|
|
toastContainer.style.position = 'fixed';
|
|
toastContainer.style.top = '20px';
|
|
toastContainer.style.right = '20px';
|
|
toastContainer.style.zIndex = '9999';
|
|
document.body.appendChild(toastContainer);
|
|
}
|
|
|
|
// Buat toast notifikasi
|
|
var toast = document.createElement('div');
|
|
toast.className = 'toast-notification';
|
|
toast.innerHTML = message;
|
|
toast.style.backgroundColor = '#F9C416';
|
|
toast.style.color = '#333';
|
|
toast.style.padding = '12px 20px';
|
|
toast.style.borderRadius = '5px';
|
|
toast.style.marginBottom = '10px';
|
|
toast.style.boxShadow = '0 2px 8px rgba(0,0,0,0.2)';
|
|
toast.style.minWidth = '250px';
|
|
toast.style.opacity = '0';
|
|
toast.style.transition = 'opacity 0.3s ease';
|
|
|
|
// Tambahkan toast ke container
|
|
toastContainer.appendChild(toast);
|
|
|
|
// Tampilkan toast dengan animasi
|
|
setTimeout(function() {
|
|
toast.style.opacity = '1';
|
|
}, 10);
|
|
|
|
// Hilangkan toast setelah 5 detik
|
|
setTimeout(function() {
|
|
toast.style.opacity = '0';
|
|
setTimeout(function() {
|
|
toastContainer.removeChild(toast);
|
|
}, 300);
|
|
}, 5000);
|
|
}
|
|
</script>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.notifications-dropdown {
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
|
|
.notification-icon {
|
|
cursor: pointer;
|
|
padding: 8px;
|
|
position: relative;
|
|
}
|
|
|
|
.notification-icon .badge {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
font-size: 10px;
|
|
}
|
|
|
|
.notification-menu {
|
|
position: absolute;
|
|
right: 0;
|
|
width: 300px;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
background: white;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
z-index: 1000;
|
|
}
|
|
|
|
.notification-header {
|
|
padding: 10px;
|
|
border-bottom: 1px solid #ddd;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
.notification-list {
|
|
padding: 0;
|
|
}
|
|
|
|
.notification-item {
|
|
padding: 10px;
|
|
border-bottom: 1px solid #eee;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.notification-item:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.notification-item.unread {
|
|
background-color: #f0f7ff;
|
|
}
|
|
|
|
.notification-content {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.no-notifications {
|
|
padding: 20px;
|
|
text-align: center;
|
|
color: #666;
|
|
}
|
|
|
|
.mark-all-read {
|
|
color: #007bff;
|
|
font-size: 12px;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.mark-all-read:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.notification-actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.delete-read-notifications {
|
|
color: #dc3545;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.delete-read-notifications:hover {
|
|
text-decoration: underline;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const notificationDropdown = document.getElementById('notificationDropdown');
|
|
const notificationMenu = document.querySelector('.notification-menu');
|
|
|
|
if (notificationDropdown && notificationMenu) {
|
|
notificationDropdown.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
notificationMenu.style.display = notificationMenu.style.display === 'none' ? 'block' : 'none';
|
|
});
|
|
|
|
document.addEventListener('click', function(e) {
|
|
if (!e.target.closest('.notifications-dropdown')) {
|
|
notificationMenu.style.display = 'none';
|
|
}
|
|
});
|
|
}
|
|
|
|
// Mark notification as read dan redirect
|
|
function handleNotificationClick(item) {
|
|
const notificationId = item.dataset.notificationId;
|
|
const notificationType = item.dataset.type;
|
|
|
|
// Mark as read
|
|
fetch('/notifications/' + notificationId + '/mark-as-read', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Content-Type': 'application/json'
|
|
}
|
|
});
|
|
|
|
// Redirect berdasarkan tipe notifikasi
|
|
if (notificationType === 'jalan') {
|
|
window.location.href = 'https://jagajalan.oyi.web.id/jalan';
|
|
} else if (notificationType === 'bencana') {
|
|
window.location.href = 'https://jagajalan.oyi.web.id/bencana';
|
|
}
|
|
}
|
|
|
|
// Attach click handler to all existing notification items
|
|
document.querySelectorAll('.notification-item').forEach(item => {
|
|
item.addEventListener('click', function() {
|
|
handleNotificationClick(this);
|
|
});
|
|
});
|
|
|
|
// Tambahkan event listener untuk menandai semua notifikasi sebagai sudah dibaca
|
|
function handleMarkAllRead(e) {
|
|
e.preventDefault();
|
|
fetch('/notifications/mark-all-read', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Content-Type': 'application/json'
|
|
}
|
|
}).then(response => {
|
|
if (response.ok) {
|
|
// Update UI
|
|
document.querySelectorAll('.notification-item.unread').forEach(item => {
|
|
item.classList.remove('unread');
|
|
item.classList.add('read');
|
|
});
|
|
// Sembunyikan badge notifikasi
|
|
const badge = document.querySelector('.notification-icon .badge');
|
|
if (badge) {
|
|
badge.innerText = '0';
|
|
badge.style.display = 'none';
|
|
}
|
|
// Sembunyikan tombol mark-all-read
|
|
const markAllReadBtn = document.querySelector('.mark-all-read');
|
|
if (markAllReadBtn) {
|
|
markAllReadBtn.style.display = 'none';
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
const markAllReadBtn = document.querySelector('.mark-all-read');
|
|
if (markAllReadBtn) {
|
|
markAllReadBtn.addEventListener('click', handleMarkAllRead);
|
|
}
|
|
|
|
// Tambahkan event listener untuk menghapus notifikasi yang sudah dibaca
|
|
const deleteReadBtn = document.querySelector('.delete-read-notifications');
|
|
if (deleteReadBtn) {
|
|
deleteReadBtn.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
if (confirm('Apakah Anda yakin ingin menghapus semua notifikasi yang sudah dibaca?')) {
|
|
fetch('/notifications/delete-read', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Content-Type': 'application/json'
|
|
}
|
|
}).then(response => {
|
|
if (response.ok) {
|
|
// Hapus notifikasi yang sudah dibaca dari tampilan
|
|
document.querySelectorAll('.notification-item.read').forEach(item => {
|
|
item.remove();
|
|
});
|
|
|
|
// Jika tidak ada notifikasi yang tersisa, tampilkan pesan
|
|
const notificationList = document.querySelector('.notification-list');
|
|
if (notificationList && notificationList.children.length === 0) {
|
|
notificationList.innerHTML = '<div class="no-notifications">Tidak ada notifikasi</div>';
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
setInterval(function() {
|
|
fetch('/get-notifications-count', {
|
|
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
var currentCount = parseInt(document.getElementById('notification-badge').innerText || '0');
|
|
console.log('Current count:', currentCount, 'New count:', data.count);
|
|
|
|
if (data.count > currentCount) {
|
|
// Ada notifikasi baru, play sound dulu baru refresh
|
|
try {
|
|
console.log('Mencoba memainkan suara notifikasi...');
|
|
var sound = document.getElementById('notification-sound');
|
|
if (sound) {
|
|
// Mengatasi masalah autoplay policy
|
|
var playPromise = sound.play();
|
|
|
|
if (playPromise !== undefined) {
|
|
playPromise.then(() => {
|
|
console.log('Suara notifikasi berhasil diputar');
|
|
// Berikan waktu untuk suara terdengar
|
|
setTimeout(function() {
|
|
console.log('Refreshing halaman...');
|
|
location.reload();
|
|
}, 1500);
|
|
}).catch(error => {
|
|
console.error('Autoplay diblokir atau error:', error);
|
|
// Tetap refresh meskipun suara gagal diputar
|
|
location.reload();
|
|
});
|
|
} else {
|
|
// Browser lama yang tidak mengembalikan promise
|
|
console.log('Browser lama, langsung refresh');
|
|
setTimeout(function() {
|
|
location.reload();
|
|
}, 1000);
|
|
}
|
|
} else {
|
|
console.error('Elemen audio tidak ditemukan!');
|
|
location.reload();
|
|
}
|
|
} catch (e) {
|
|
console.error('Error saat memainkan notifikasi:', e);
|
|
location.reload();
|
|
}
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.log('Error checking notifications:', error);
|
|
});
|
|
}, 3000); // Check setiap 3 detik
|
|
</script>
|