MIF_E31222307/resources/views/user/notifications/index.blade.php

1238 lines
50 KiB
PHP

@extends('layoutuser.app')
@section('title', 'Notifikasi')
@section('content')
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-lg-10">
<!-- Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="mb-0">
<i class="fas fa-bell me-2 text-primary"></i>Pengaturan Notifikasi
</h4>
<div>
<button type="button" class="btn btn-warning btn-sm me-2" onclick="testNotification()">
<i class="fas fa-bell me-1"></i>Test Database
</button>
<button type="button" class="btn btn-success btn-sm me-2" onclick="testBrowserNotification()">
<i class="fas fa-desktop me-1"></i>Test Popup Device
</button>
<button type="button" class="btn btn-info btn-sm me-2" onclick="testBackgroundNotification()">
<i class="fas fa-broadcast-tower me-1"></i>Test Background
</button>
<button type="button" class="btn btn-danger btn-sm" onclick="testRepeatingNotification()">
<i class="fas fa-alarm me-1"></i>Test Alarm Berulang
</button>
</div>
</div>
<!-- Tabs -->
<ul class="nav nav-tabs mb-4" id="notificationTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="notifications-tab" data-bs-toggle="tab" data-bs-target="#notifications" type="button" role="tab">
<i class="fas fa-list me-2"></i>Daftar Notifikasi
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="sound-tab" data-bs-toggle="tab" data-bs-target="#sound" type="button" role="tab">
<i class="fas fa-volume-up me-2"></i>Pengaturan Suara
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="permission-tab" data-bs-toggle="tab" data-bs-target="#permission" type="button" role="tab">
<i class="fas fa-shield-alt me-2"></i>Permission
</button>
</li>
</ul>
<!-- Tab Content -->
<div class="tab-content" id="notificationTabsContent">
<!-- Tab 1: Daftar Notifikasi -->
<div class="tab-pane fade show active" id="notifications" role="tabpanel">
<div class="card shadow-sm">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h6 class="mb-0">Riwayat Notifikasi</h6>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="markAllAsRead()">
<i class="fas fa-check-double me-1"></i>Tandai Semua Dibaca
</button>
</div>
<div class="card-body">
@if($notifications->count() > 0)
<div class="list-group">
@foreach($notifications as $notification)
<div class="list-group-item list-group-item-action {{ !$notification->is_read ? 'list-group-item-warning' : '' }}"
id="notification-{{ $notification->id }}">
<div class="d-flex w-100 justify-content-between align-items-start">
<div class="flex-grow-1">
<div class="d-flex align-items-center mb-2">
<h6 class="mb-1">
@if(!$notification->is_read)
<span class="badge bg-warning me-2">Baru</span>
@endif
{{ $notification->title }}
</h6>
<span class="badge bg-{{ $notification->type == 'reminder' ? 'warning' : ($notification->type == 'success' ? 'success' : 'info') }} ms-2">
{{ ucfirst($notification->type) }}
</span>
</div>
<p class="mb-1">{{ $notification->message }}</p>
<small class="text-muted">
<i class="fas fa-clock me-1"></i>
{{ $notification->created_at->diffForHumans() }}
</small>
</div>
<div class="ms-3">
@if(!$notification->is_read)
<button type="button" class="btn btn-sm btn-outline-success me-1"
onclick="markAsRead({{ $notification->id }})" title="Tandai Dibaca">
<i class="fas fa-check"></i>
</button>
@endif
<button type="button" class="btn btn-sm btn-outline-danger"
onclick="deleteNotification({{ $notification->id }})" title="Hapus">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
</div>
@endforeach
</div>
<!-- Pagination -->
<div class="d-flex justify-content-center mt-4">
{{ $notifications->links() }}
</div>
@else
<div class="text-center py-5">
<i class="fas fa-bell-slash fa-3x text-muted mb-3"></i>
<h5 class="text-muted">Belum ada notifikasi</h5>
<p class="text-muted">Notifikasi akan muncul di sini ketika ada informasi penting untuk Anda.</p>
</div>
@endif
</div>
</div>
</div>
<!-- Tab 2: Pengaturan Suara -->
<div class="tab-pane fade" id="sound" role="tabpanel">
<div class="card shadow-sm">
<div class="card-header bg-white">
<h6 class="mb-0">Pengaturan Suara Notifikasi</h6>
</div>
<div class="card-body">
<!-- Toggle Suara -->
<div class="mb-4">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="sound_enabled" checked>
<label class="form-check-label" for="sound_enabled">
<strong>Aktifkan Suara Notifikasi</strong>
</label>
</div>
<small class="text-muted">Putar suara saat notifikasi muncul</small>
</div>
<!-- Auto Sound Control -->
<div class="mb-4">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="auto_sound_enabled">
<label class="form-check-label" for="auto_sound_enabled">
<strong>Suara Otomatis Saat Berpindah Menu</strong>
</label>
</div>
<small class="text-muted">Nonaktifkan untuk mencegah suara muncul saat berpindah halaman</small>
</div>
<!-- Volume Control -->
<div class="mb-4">
<label class="form-label">
<strong>Volume Suara:</strong>
<span id="volume_value" class="text-primary">50</span>%
</label>
<input type="range" class="form-range" id="sound_volume" min="0" max="100" value="50">
<div class="d-flex justify-content-between">
<small class="text-muted">0%</small>
<small class="text-muted">100%</small>
</div>
</div>
<!-- Pilihan Suara -->
<div class="mb-4">
<label class="form-label"><strong>Pilih Suara Notifikasi:</strong></label>
<div id="sound-options" class="row">
<div class="col-12 text-center">
<i class="fas fa-spinner fa-spin"></i>
<small class="d-block mt-1">Memuat pilihan suara...</small>
</div>
</div>
</div>
<!-- Test Suara -->
<div class="text-center">
<button type="button" class="btn btn-primary" onclick="testSound()">
<i class="fas fa-play me-2"></i>Test Suara
</button>
</div>
<!-- Pengaturan Alarm Berulang -->
<hr class="my-4">
<h6 class="mb-3"><i class="fas fa-alarm me-2"></i>Pengaturan Alarm Berulang</h6>
<div class="row">
<div class="col-md-6">
<label class="form-label">
<strong>Interval Notifikasi:</strong>
<span id="notification_interval_value" class="text-primary">30</span> detik
</label>
<input type="range" class="form-range" id="notification_interval" min="10" max="120" value="30">
<div class="d-flex justify-content-between">
<small class="text-muted">10 detik</small>
<small class="text-muted">2 menit</small>
</div>
</div>
<div class="col-md-6">
<label class="form-label">
<strong>Interval Suara:</strong>
<span id="audio_interval_value" class="text-primary">10</span> detik
</label>
<input type="range" class="form-range" id="audio_interval" min="5" max="60" value="10">
<div class="d-flex justify-content-between">
<small class="text-muted">5 detik</small>
<small class="text-muted">1 menit</small>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Tab 3: Permission -->
<div class="tab-pane fade" id="permission" role="tabpanel">
<div class="card shadow-sm">
<div class="card-header bg-white">
<h6 class="mb-0">Pengaturan Permission Notifikasi</h6>
</div>
<div class="card-body">
<!-- Status Permission -->
<div class="mb-4">
<h6>Status Permission:</h6>
<div id="permission-status" class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<span id="permission-text">Memeriksa status permission...</span>
</div>
</div>
<!-- Browser Notifications -->
<div class="mb-4">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="browser_notifications_enabled">
<label class="form-check-label" for="browser_notifications_enabled">
<strong>Aktifkan Notifikasi Browser</strong>
</label>
</div>
<small class="text-muted">Notifikasi akan muncul di sistem operasi (Windows, macOS, Linux)</small>
</div>
<!-- Background Notifications -->
<div class="mb-4">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="background_notifications_enabled">
<label class="form-check-label" for="background_notifications_enabled">
<strong>Notifikasi Background</strong>
</label>
</div>
<small class="text-muted">Notifikasi akan muncul meski tab browser tidak aktif</small>
</div>
<!-- Action Buttons -->
<div class="text-center">
<button type="button" class="btn btn-primary me-2" onclick="requestNotificationPermission()">
<i class="fas fa-shield-alt me-2"></i>Minta Permission
</button>
<button type="button" class="btn btn-outline-secondary" onclick="resetNotificationSettings()">
<i class="fas fa-undo me-2"></i>Reset Pengaturan
</button>
</div>
<!-- Test Notifications Info -->
<div class="mt-4">
<div class="alert alert-info">
<h6><i class="fas fa-info-circle me-2"></i>Test Notifikasi:</h6>
<div class="row">
<div class="col-md-3">
<strong>Test Database:</strong>
<ul class="mb-0 small">
<li>Menyimpan notifikasi ke database</li>
<li>Muncul di daftar notifikasi</li>
<li>Tidak memerlukan permission browser</li>
</ul>
</div>
<div class="col-md-3">
<strong>Test Popup Device:</strong>
<ul class="mb-0 small">
<li>Menampilkan popup di sistem operasi</li>
<li>Memerlukan permission browser</li>
<li>Muncul saat tab aktif</li>
</ul>
</div>
<div class="col-md-3">
<strong>Test Background:</strong>
<ul class="mb-0 small">
<li>Notifikasi background/offline</li>
<li>Muncul meski tab tidak aktif</li>
<li>Menggunakan Service Worker</li>
</ul>
</div>
<div class="col-md-3">
<strong>Test Alarm Berulang:</strong>
<ul class="mb-0 small">
<li>Notifikasi berulang setiap 30 detik</li>
<li>Suara berulang setiap 10 detik</li>
<li>Tombol Dismiss & Snooze</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Info -->
<div class="mt-4">
<div class="alert alert-warning">
<h6><i class="fas fa-exclamation-triangle me-2"></i>Penting:</h6>
<ul class="mb-0">
<li>Notifikasi browser memerlukan permission dari pengguna</li>
<li>Website harus menggunakan HTTPS untuk notifikasi background</li>
<li>Jika permission ditolak, notifikasi akan menggunakan fallback (visual + audio)</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Sound Settings
let currentVolume = 50;
let currentSound = 'default';
let soundEnabled = true;
let autoSoundEnabled = false;
let availableSounds = [];
// Permission Settings
let browserNotificationsEnabled = false;
let backgroundNotificationsEnabled = false;
// Global variables untuk notifikasi berulang
let repeatingNotificationInterval = null;
let repeatingAudioInterval = null;
let currentRepeatingNotification = null;
let isRepeatingNotificationActive = false;
// Initialize
document.addEventListener('DOMContentLoaded', function() {
loadSoundSettings();
loadPermissionSettings();
updatePermissionStatus();
loadAvailableSounds();
registerServiceWorker();
});
// Load available sounds from server
function loadAvailableSounds() {
fetch('{{ route("user.notifications.available-sounds") }}')
.then(response => response.json())
.then(data => {
availableSounds = data.sounds;
renderSoundOptions();
})
.catch(error => {
console.error('Error loading sounds:', error);
renderSoundOptions(); // Render with fallback
});
}
// Render sound options dynamically
function renderSoundOptions() {
const container = document.getElementById('sound-options');
if (availableSounds.length === 0) {
container.innerHTML = `
<div class="col-12">
<div class="alert alert-warning">
<i class="fas fa-exclamation-triangle me-2"></i>
Tidak ada file audio yang tersedia. Upload file audio ke folder <code>public/audio/</code>
</div>
</div>
`;
return;
}
let html = '';
availableSounds.forEach((sound, index) => {
const isChecked = index === 0 ? 'checked' : '';
const icon = getSoundIcon(sound.name);
html += `
<div class="col-md-6 mb-2">
<div class="form-check">
<input class="form-check-input" type="radio" name="notification_sound"
id="sound_${sound.name}" value="${sound.name}" ${isChecked}>
<label class="form-check-label" for="sound_${sound.name}">
<i class="${icon} me-2"></i>${sound.display_name}
</label>
</div>
</div>
`;
});
container.innerHTML = html;
// Add event listeners to new radio buttons
document.querySelectorAll('input[name="notification_sound"]').forEach(radio => {
radio.addEventListener('change', function() {
currentSound = this.value;
localStorage.setItem('notification_sound', currentSound);
});
});
// Update sound selection after rendering
updateSoundSelection();
}
// Get appropriate icon for sound type
function getSoundIcon(soundName) {
const name = soundName.toLowerCase();
if (name.includes('bell') || name.includes('notification')) {
return 'fas fa-bell';
} else if (name.includes('chime') || name.includes('music')) {
return 'fas fa-music';
} else if (name.includes('beep') || name.includes('alert')) {
return 'fas fa-volume-up';
} else if (name.includes('ding') || name.includes('ping')) {
return 'fas fa-bell-ring';
} else {
return 'fas fa-volume-up';
}
}
// Sound Functions
function loadSoundSettings() {
// Load from localStorage
const savedVolume = localStorage.getItem('notification_volume');
const savedSound = localStorage.getItem('notification_sound');
const savedEnabled = localStorage.getItem('notification_sound_enabled');
const savedAutoSound = localStorage.getItem('notification_auto_sound_enabled');
const savedNotificationInterval = localStorage.getItem('notification_interval') || 30;
const savedAudioInterval = localStorage.getItem('audio_interval') || 10;
if (savedVolume) {
currentVolume = parseInt(savedVolume * 100);
document.getElementById('sound_volume').value = currentVolume;
document.getElementById('volume_value').textContent = currentVolume;
}
if (savedEnabled !== null) {
soundEnabled = savedEnabled === 'true';
document.getElementById('sound_enabled').checked = soundEnabled;
}
if (savedAutoSound !== null) {
autoSoundEnabled = savedAutoSound === 'true';
document.getElementById('auto_sound_enabled').checked = autoSoundEnabled;
}
// Load interval settings
document.getElementById('notification_interval').value = savedNotificationInterval;
document.getElementById('notification_interval_value').textContent = savedNotificationInterval;
document.getElementById('audio_interval').value = savedAudioInterval;
document.getElementById('audio_interval_value').textContent = savedAudioInterval;
// Event listeners
document.getElementById('sound_volume').addEventListener('input', function() {
currentVolume = this.value;
document.getElementById('volume_value').textContent = currentVolume;
localStorage.setItem('notification_volume', currentVolume / 100);
});
document.getElementById('sound_enabled').addEventListener('change', function() {
soundEnabled = this.checked;
localStorage.setItem('notification_sound_enabled', soundEnabled);
});
document.getElementById('auto_sound_enabled').addEventListener('change', function() {
autoSoundEnabled = this.checked;
localStorage.setItem('notification_auto_sound_enabled', autoSoundEnabled);
});
// Interval event listeners
document.getElementById('notification_interval').addEventListener('input', function() {
const value = this.value;
document.getElementById('notification_interval_value').textContent = value;
localStorage.setItem('notification_interval', value);
});
document.getElementById('audio_interval').addEventListener('input', function() {
const value = this.value;
document.getElementById('audio_interval_value').textContent = value;
localStorage.setItem('audio_interval', value);
});
}
// Update sound selection after sounds are loaded
function updateSoundSelection() {
const savedSound = localStorage.getItem('notification_sound');
if (savedSound && availableSounds.length > 0) {
// Check if saved sound exists in available sounds
const soundExists = availableSounds.find(sound => sound.name === savedSound);
if (soundExists) {
currentSound = savedSound;
const radioButton = document.getElementById(`sound_${savedSound}`);
if (radioButton) {
radioButton.checked = true;
}
} else {
// If saved sound doesn't exist, use first available
currentSound = availableSounds[0].name;
localStorage.setItem('notification_sound', currentSound);
const radioButton = document.getElementById(`sound_${currentSound}`);
if (radioButton) {
radioButton.checked = true;
}
}
} else if (availableSounds.length > 0) {
// Set default sound if none selected
currentSound = availableSounds[0].name;
localStorage.setItem('notification_sound', currentSound);
const radioButton = document.getElementById(`sound_${currentSound}`);
if (radioButton) {
radioButton.checked = true;
}
}
}
function testSound() {
if (!soundEnabled) {
alert('Suara notifikasi dinonaktifkan!');
return;
}
if (availableSounds.length === 0) {
alert('Tidak ada file audio yang tersedia!');
return;
}
// Find the selected sound
const selectedSound = availableSounds.find(sound => sound.name === currentSound);
if (!selectedSound) {
alert('File audio tidak ditemukan!');
return;
}
const audio = new Audio(selectedSound.path);
audio.volume = currentVolume / 100;
audio.play().catch(e => {
console.log('Error playing sound:', e);
alert('Tidak bisa memutar suara. Pastikan file audio tersedia.');
});
}
// Permission Functions
function loadPermissionSettings() {
const savedBrowser = localStorage.getItem('browser_notifications_enabled');
const savedBackground = localStorage.getItem('background_notifications_enabled');
if (savedBrowser !== null) {
browserNotificationsEnabled = savedBrowser === 'true';
document.getElementById('browser_notifications_enabled').checked = browserNotificationsEnabled;
}
if (savedBackground !== null) {
backgroundNotificationsEnabled = savedBackground === 'true';
document.getElementById('background_notifications_enabled').checked = backgroundNotificationsEnabled;
}
// Event listeners
document.getElementById('browser_notifications_enabled').addEventListener('change', function() {
browserNotificationsEnabled = this.checked;
localStorage.setItem('browser_notifications_enabled', browserNotificationsEnabled);
});
document.getElementById('background_notifications_enabled').addEventListener('change', function() {
backgroundNotificationsEnabled = this.checked;
localStorage.setItem('background_notifications_enabled', backgroundNotificationsEnabled);
});
}
function updatePermissionStatus() {
const statusDiv = document.getElementById('permission-status');
const textSpan = document.getElementById('permission-text');
if (!('Notification' in window)) {
statusDiv.className = 'alert alert-danger';
textSpan.textContent = 'Browser tidak mendukung notifikasi';
return;
}
switch(Notification.permission) {
case 'granted':
statusDiv.className = 'alert alert-success';
textSpan.textContent = 'Permission diberikan. Notifikasi browser aktif.';
break;
case 'denied':
statusDiv.className = 'alert alert-warning';
textSpan.textContent = 'Permission ditolak. Notifikasi akan menggunakan fallback.';
break;
default:
statusDiv.className = 'alert alert-info';
textSpan.textContent = 'Permission belum diatur. Klik "Minta Permission" untuk mengaktifkan.';
break;
}
}
async function requestNotificationPermission() {
if (!('Notification' in window)) {
alert('Browser Anda tidak mendukung notifikasi!');
return;
}
try {
const permission = await Notification.requestPermission();
updatePermissionStatus();
if (permission === 'granted') {
// Register service worker for background notifications
if ('serviceWorker' in navigator && backgroundNotificationsEnabled) {
await registerServiceWorker();
}
alert('Permission diberikan! Notifikasi browser sekarang aktif.');
} else {
alert('Permission ditolak. Notifikasi akan menggunakan fallback (visual + audio).');
}
} catch (error) {
console.error('Error requesting permission:', error);
alert('Terjadi kesalahan saat meminta permission.');
}
}
function resetNotificationSettings() {
if (confirm('Apakah Anda yakin ingin mereset semua pengaturan notifikasi?')) {
localStorage.removeItem('notification_volume');
localStorage.removeItem('notification_sound');
localStorage.removeItem('notification_sound_enabled');
localStorage.removeItem('notification_auto_sound_enabled');
localStorage.removeItem('notification_interval');
localStorage.removeItem('audio_interval');
localStorage.removeItem('browser_notifications_enabled');
localStorage.removeItem('background_notifications_enabled');
location.reload();
}
}
// Notification Functions
function markAsRead(id) {
fetch(`/user/notifications/${id}/mark-read`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
});
}
function markAllAsRead() {
fetch('/user/notifications/mark-all-read', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
});
}
function deleteNotification(id) {
if (confirm('Apakah Anda yakin ingin menghapus notifikasi ini?')) {
fetch(`/user/notifications/${id}`, {
method: 'DELETE',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById(`notification-${id}`).remove();
}
});
}
}
function testNotification() {
fetch('/user/notifications/test', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Test notifikasi berhasil dikirim!');
location.reload();
}
});
}
function testBrowserNotification() {
if (!('Notification' in window)) {
alert('Browser ini tidak mendukung notifikasi desktop');
return;
}
if (Notification.permission === 'denied') {
alert('Izin notifikasi ditolak. Silakan aktifkan di pengaturan browser.');
return;
}
if (Notification.permission === 'default') {
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
showTestNotification();
} else {
alert('Izin notifikasi diperlukan untuk fitur ini');
}
});
} else {
showTestNotification();
}
}
function showTestNotification() {
const notificationData = {
title: '🧪 Test Notifikasi',
body: 'Ini adalah test notifikasi popup dengan audio dan tombol action!',
icon: '/logo/baru/dutdut.png',
badge: '/logo/baru/dutdut.png',
tag: 'test-notification',
vibrate: [200, 100, 200, 100, 200],
data: {
url: '/user/notifications',
type: 'test'
}
};
try {
showTestBrowserNotification(notificationData);
console.log('Test notification sent successfully');
} catch (error) {
console.error('Error showing test notification:', error);
alert('Gagal menampilkan notifikasi: ' + error.message);
}
}
function showTestBrowserNotification(notificationData) {
// Buat notifikasi test dengan action buttons
const notification = new Notification(notificationData.title, {
body: notificationData.body,
icon: notificationData.icon,
badge: notificationData.badge,
tag: notificationData.tag,
requireInteraction: false,
silent: false, // Akan memutar suara sistem
vibrate: notificationData.vibrate,
data: notificationData.data,
actions: [
{
action: 'view',
title: 'Lihat',
icon: notificationData.icon
},
{
action: 'dismiss',
title: 'Tutup',
icon: notificationData.icon
}
]
});
// Event handlers
notification.onclick = function(event) {
// Jika user mengklik notifikasi (bukan action button)
if (!event.action) {
window.focus();
notification.close();
// Navigate ke halaman notifikasi
if (notificationData.data && notificationData.data.url) {
window.location.href = notificationData.data.url;
}
}
};
// Handle action buttons
notification.onactionclick = function(event) {
if (event.action === 'view') {
window.focus();
notification.close();
if (notificationData.data && notificationData.data.url) {
window.location.href = notificationData.data.url;
}
} else if (event.action === 'dismiss') {
notification.close();
}
};
notification.onclose = function() {
console.log('Test notification closed');
};
notification.onshow = function() {
console.log('Test notification shown');
// Putar suara custom jika diaktifkan
playCustomNotificationSound();
};
// Auto close setelah 10 detik
setTimeout(() => {
notification.close();
}, 10000);
}
// Fungsi untuk memutar suara custom
function playCustomNotificationSound() {
const soundEnabled = localStorage.getItem('notification_sound_enabled') !== 'false';
if (!soundEnabled) {
console.log('Notification sound disabled by user');
return;
}
const currentSound = localStorage.getItem('notification_sound') || 'default';
const volume = localStorage.getItem('notification_volume') || 0.3;
console.log('Playing notification sound:', currentSound, 'at volume:', volume);
try {
const audio = new Audio(`/audio/${currentSound}.mp3`);
audio.volume = parseFloat(volume);
// Tambahkan event listeners untuk debugging
audio.addEventListener('loadstart', () => console.log('Audio loading started'));
audio.addEventListener('canplay', () => console.log('Audio can play'));
audio.addEventListener('play', () => console.log('Audio started playing'));
audio.addEventListener('ended', () => console.log('Audio finished playing'));
audio.addEventListener('error', (e) => console.log('Audio error:', e));
audio.play().then(() => {
console.log('Custom notification sound played successfully');
}).catch(e => {
console.log('Error playing custom notification sound:', e);
// Fallback ke suara sistem
console.log('Using system sound as fallback');
});
} catch (error) {
console.log('Error with custom audio:', error);
}
}
function showSuccessMessage(message) {
// Buat elemen pesan sukses
const successDiv = document.createElement('div');
successDiv.className = 'alert alert-success alert-dismissible fade show position-fixed';
successDiv.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
successDiv.innerHTML = `
<i class="fas fa-check-circle me-2"></i>
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(successDiv);
// Auto remove setelah 3 detik
setTimeout(() => {
if (successDiv.parentNode) {
successDiv.remove();
}
}, 3000);
}
function testBackgroundNotification() {
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
alert('Browser tidak mendukung background notifications');
return;
}
navigator.serviceWorker.ready.then(registration => {
// Kirim message ke service worker untuk menampilkan notifikasi
registration.active.postMessage({
type: 'SHOW_NOTIFICATION',
notification: {
title: '🔔 Background Test',
body: 'Ini adalah test notifikasi background dengan audio!',
icon: '/logo/baru/dutdut.png',
badge: '/logo/baru/dutdut.png',
tag: 'background-test',
requireInteraction: false,
silent: false,
vibrate: [200, 100, 200, 100, 200],
data: {
url: '/user/notifications',
type: 'background-test'
}
}
});
console.log('Background notification test sent to service worker');
}).catch(error => {
console.error('Error with service worker:', error);
alert('Gagal mengirim background notification: ' + error.message);
});
}
function testRepeatingNotification() {
// Cek apakah browser mendukung notifikasi
if (!('Notification' in window)) {
alert('Browser Anda tidak mendukung notifikasi popup!');
return;
}
// Cek permission
if (Notification.permission === 'denied') {
alert('Permission notifikasi ditolak. Silakan aktifkan permission di pengaturan browser.');
return;
}
if (Notification.permission === 'default') {
// Minta permission terlebih dahulu
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
startRepeatingNotification();
} else {
alert('Permission ditolak. Tidak bisa menampilkan notifikasi popup.');
}
});
} else if (Notification.permission === 'granted') {
startRepeatingNotification();
}
}
function startRepeatingNotification() {
if (isRepeatingNotificationActive) {
alert('Notifikasi berulang sudah aktif! Silakan dismiss notifikasi yang sedang berjalan.');
return;
}
isRepeatingNotificationActive = true;
// Ambil interval dari pengaturan
const notificationInterval = parseInt(localStorage.getItem('notification_interval')) || 30;
const audioInterval = parseInt(localStorage.getItem('audio_interval')) || 10;
// Tampilkan notifikasi pertama
showRepeatingNotification();
// Mulai interval untuk notifikasi berulang
repeatingNotificationInterval = setInterval(() => {
showRepeatingNotification();
}, notificationInterval * 1000); // Konversi ke milidetik
// Mulai interval untuk suara berulang
repeatingAudioInterval = setInterval(() => {
playRepeatingNotificationSound();
}, audioInterval * 1000); // Konversi ke milidetik
// Tampilkan pesan sukses dengan informasi interval
showSuccessMessage(`Alarm berulang dimulai! Notifikasi setiap ${notificationInterval} detik, suara setiap ${audioInterval} detik. Klik "Dismiss" untuk menghentikan.`);
}
function showRepeatingNotification() {
// Tutup notifikasi sebelumnya jika ada
if (currentRepeatingNotification) {
currentRepeatingNotification.close();
}
// Buat notifikasi dengan tombol action
const notification = new Notification('HeartChoice - Alarm Berulang', {
body: 'Ini adalah alarm berulang. Klik "Dismiss" untuk menghentikan alarm ini.',
icon: '{{ asset("logo/baru/dutdut.png") }}',
badge: '{{ asset("logo/baru/dutdut.png") }}',
tag: 'repeating-alarm',
requireInteraction: true, // Notifikasi tidak akan hilang otomatis
silent: false,
vibrate: [200, 100, 200, 100, 200], // Vibrasi lebih panjang
data: {
type: 'repeating_alarm',
timestamp: new Date().toISOString()
},
actions: [
{
action: 'dismiss',
title: 'Dismiss',
icon: '{{ asset("logo/baru/dutdut.png") }}'
},
{
action: 'snooze',
title: 'Snooze 5m',
icon: '{{ asset("logo/baru/dutdut.png") }}'
}
]
});
currentRepeatingNotification = notification;
// Event handlers
notification.onclick = function(event) {
// Jika user mengklik notifikasi (bukan action button)
if (!event.action) {
window.focus();
showDismissDialog();
}
};
notification.onclose = function() {
console.log('Repeating notification closed');
};
notification.onshow = function() {
console.log('Repeating notification shown');
// Putar suara saat notifikasi muncul
playRepeatingNotificationSound();
};
// Handle action buttons
notification.onactionclick = function(event) {
if (event.action === 'dismiss') {
dismissRepeatingNotification();
} else if (event.action === 'snooze') {
snoozeRepeatingNotification();
}
};
}
function playRepeatingNotificationSound() {
const soundEnabled = localStorage.getItem('notification_sound_enabled') !== 'false';
if (!soundEnabled) return;
const currentSound = localStorage.getItem('notification_sound') || 'default';
const volume = localStorage.getItem('notification_volume') || 0.3;
try {
const audio = new Audio(`/audio/${currentSound}.mp3`);
audio.volume = volume;
audio.play().catch(e => {
console.log('Error playing repeating notification sound:', e);
});
} catch (error) {
console.log('Error with audio:', error);
}
}
function dismissRepeatingNotification() {
// Hentikan semua interval
if (repeatingNotificationInterval) {
clearInterval(repeatingNotificationInterval);
repeatingNotificationInterval = null;
}
if (repeatingAudioInterval) {
clearInterval(repeatingAudioInterval);
repeatingAudioInterval = null;
}
// Tutup notifikasi yang sedang aktif
if (currentRepeatingNotification) {
currentRepeatingNotification.close();
currentRepeatingNotification = null;
}
// Reset status
isRepeatingNotificationActive = false;
// Tampilkan pesan sukses
showSuccessMessage('Alarm berulang berhasil dihentikan!');
}
function snoozeRepeatingNotification() {
// Hentikan sementara untuk 5 menit
if (repeatingNotificationInterval) {
clearInterval(repeatingNotificationInterval);
}
if (repeatingAudioInterval) {
clearInterval(repeatingAudioInterval);
}
// Tutup notifikasi yang sedang aktif
if (currentRepeatingNotification) {
currentRepeatingNotification.close();
currentRepeatingNotification = null;
}
// Tampilkan pesan snooze
showSuccessMessage('Alarm di-snooze selama 5 menit...');
// Mulai kembali setelah 5 menit
setTimeout(() => {
if (isRepeatingNotificationActive) {
startRepeatingNotification();
}
}, 300000); // 5 menit
}
function showDismissDialog() {
// Tampilkan dialog konfirmasi dismiss
const dialogHtml = `
<div class="modal fade" id="dismissDialog" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title">
<i class="fas fa-alarm me-2"></i>Alarm Berulang
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>Apakah Anda ingin menghentikan alarm berulang ini?</p>
<div class="alert alert-warning">
<i class="fas fa-exclamation-triangle me-2"></i>
<strong>Peringatan:</strong> Alarm akan terus berulang sampai Anda menekan "Dismiss"
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-clock me-1"></i>Snooze 5m
</button>
<button type="button" class="btn btn-danger" onclick="dismissRepeatingNotification(); $('#dismissDialog').modal('hide');">
<i class="fas fa-stop me-1"></i>Dismiss
</button>
</div>
</div>
</div>
</div>
`;
// Hapus dialog lama jika ada
const existingDialog = document.getElementById('dismissDialog');
if (existingDialog) {
existingDialog.remove();
}
// Tambahkan dialog baru
document.body.insertAdjacentHTML('beforeend', dialogHtml);
// Tampilkan dialog
const dialog = new bootstrap.Modal(document.getElementById('dismissDialog'));
dialog.show();
// Event untuk snooze button
document.querySelector('#dismissDialog .btn-secondary').addEventListener('click', function() {
snoozeRepeatingNotification();
});
}
// Register Service Worker untuk background notifications
async function registerServiceWorker() {
if ('serviceWorker' in navigator) {
try {
const registration = await navigator.serviceWorker.register('/sw.js');
console.log('Service Worker registered successfully:', registration);
// Update service worker jika ada versi baru
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
console.log('New service worker available');
}
});
});
return registration;
} catch (error) {
console.error('Service Worker registration failed:', error);
return null;
}
} else {
console.log('Service Worker not supported');
return null;
}
}
// Load audio files untuk dropdown
function loadAudioFiles() {
const audioSelect = document.getElementById('notification_sound');
if (!audioSelect) return;
// Daftar file audio yang tersedia
const audioFiles = [
{ value: 'default', label: 'Default Notification' },
{ value: 'notification', label: 'Notification Sound' },
{ value: 'funny-alarm-317531', label: 'Funny Alarm' }
];
// Clear existing options
audioSelect.innerHTML = '';
// Add options
audioFiles.forEach(audio => {
const option = document.createElement('option');
option.value = audio.value;
option.textContent = audio.label;
audioSelect.appendChild(option);
});
// Set current value
const currentSound = localStorage.getItem('notification_sound') || 'default';
audioSelect.value = currentSound;
}
</script>
<style>
.form-range::-webkit-slider-thumb {
background: #007bff;
}
.form-range::-moz-range-thumb {
background: #007bff;
}
.nav-tabs .nav-link {
border: none;
border-bottom: 2px solid transparent;
color: #6c757d;
}
.nav-tabs .nav-link.active {
border-bottom-color: #007bff;
color: #007bff;
background: none;
}
.list-group-item-action:hover {
background-color: #f8f9fa;
}
.alert {
border-radius: 0.5rem;
}
</style>
@endsection