315 lines
9.9 KiB
PHP
315 lines
9.9 KiB
PHP
{{--
|
|
========================================
|
|
NOTIFICHE HEADER
|
|
========================================
|
|
Componente notifiche con badge contatore,
|
|
dropdown e gestione real-time.
|
|
|
|
Props:
|
|
- $maxVisible (int): Max notifiche visibili nel dropdown
|
|
- $showBadge (bool): Mostra badge contatore
|
|
- $realTime (bool): Abilita aggiornamenti real-time
|
|
|
|
Autore: NetGesCon Development Team
|
|
Data: 2024
|
|
========================================
|
|
--}}
|
|
|
|
@props([
|
|
'maxVisible' => 10,
|
|
'showBadge' => true,
|
|
'realTime' => true
|
|
])
|
|
|
|
@php
|
|
// Recupera notifiche utente (sostituire con logica reale)
|
|
$notifications = collect([
|
|
(object)[
|
|
'id' => 1,
|
|
'type' => 'ticket',
|
|
'title' => 'Nuovo ticket aperto',
|
|
'message' => 'Ticket #123 - Problema ascensore',
|
|
'icon' => 'fas fa-ticket-alt',
|
|
'color' => 'warning',
|
|
'time' => '2 min fa',
|
|
'read' => false,
|
|
'url' => '#'
|
|
],
|
|
(object)[
|
|
'id' => 2,
|
|
'type' => 'system',
|
|
'title' => 'Backup completato',
|
|
'message' => 'Backup automatico database eseguito con successo',
|
|
'icon' => 'fas fa-database',
|
|
'color' => 'success',
|
|
'time' => '1 ora fa',
|
|
'read' => false,
|
|
'url' => '#'
|
|
]
|
|
]);
|
|
|
|
$unreadCount = $notifications->where('read', false)->count();
|
|
@endphp
|
|
|
|
<div class="netgescon-notifications dropdown">
|
|
|
|
{{-- Pulsante notifiche --}}
|
|
<button class="btn btn-outline-secondary position-relative"
|
|
type="button"
|
|
data-bs-toggle="dropdown"
|
|
aria-expanded="false"
|
|
aria-label="Notifiche ({{ $unreadCount }} non lette)"
|
|
title="Notifiche">
|
|
|
|
<i class="fas fa-bell"></i>
|
|
|
|
{{-- Badge contatore --}}
|
|
@if($showBadge && $unreadCount > 0)
|
|
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
|
|
{{ $unreadCount > 99 ? '99+' : $unreadCount }}
|
|
<span class="visually-hidden">notifiche non lette</span>
|
|
</span>
|
|
@endif
|
|
|
|
</button>
|
|
|
|
{{-- Dropdown notifiche --}}
|
|
<div class="dropdown-menu dropdown-menu-end notifications-dropdown">
|
|
|
|
{{-- Header --}}
|
|
<div class="dropdown-header d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0">Notifiche</h6>
|
|
@if($unreadCount > 0)
|
|
<button type="button" class="btn btn-sm btn-link p-0" id="markAllRead">
|
|
<small>Segna tutte lette</small>
|
|
</button>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- Lista notifiche --}}
|
|
@if($notifications->count() > 0)
|
|
<div class="notifications-list">
|
|
@foreach($notifications->take($maxVisible) as $notification)
|
|
<a href="{{ $notification->url }}"
|
|
class="dropdown-item notification-item {{ !$notification->read ? 'unread' : '' }}"
|
|
data-notification-id="{{ $notification->id }}">
|
|
|
|
<div class="d-flex align-items-start">
|
|
<div class="notification-icon me-3">
|
|
<i class="{{ $notification->icon }} text-{{ $notification->color }}"></i>
|
|
</div>
|
|
|
|
<div class="notification-content flex-grow-1">
|
|
<div class="notification-title fw-semibold">{{ $notification->title }}</div>
|
|
<div class="notification-message text-muted small">{{ $notification->message }}</div>
|
|
<div class="notification-time text-muted small">
|
|
<i class="fas fa-clock me-1"></i>{{ $notification->time }}
|
|
</div>
|
|
</div>
|
|
|
|
@if(!$notification->read)
|
|
<div class="notification-unread">
|
|
<span class="badge bg-primary rounded-circle"> </span>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
</a>
|
|
@endforeach
|
|
</div>
|
|
|
|
{{-- Footer --}}
|
|
@if($notifications->count() > $maxVisible)
|
|
<div class="dropdown-divider"></div>
|
|
<a href="#" class="dropdown-item text-center text-primary" title="Notifiche (in sviluppo)">
|
|
<i class="fas fa-eye me-1"></i>Vedi tutte le notifiche
|
|
</a>
|
|
@endif
|
|
|
|
@else
|
|
{{-- Nessuna notifica --}}
|
|
<div class="dropdown-item-text text-center py-4">
|
|
<i class="fas fa-bell-slash fa-2x text-muted mb-2"></i>
|
|
<div class="text-muted">Nessuna notifica</div>
|
|
</div>
|
|
@endif
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{{-- CSS per notifiche --}}
|
|
@push('styles')
|
|
<style>
|
|
.notifications-dropdown {
|
|
width: 350px;
|
|
max-height: 500px;
|
|
overflow-y: auto;
|
|
border: none;
|
|
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.notification-item {
|
|
padding: 1rem;
|
|
border-bottom: 1px solid var(--bs-border-color);
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.notification-item:hover {
|
|
background-color: var(--bs-light);
|
|
}
|
|
|
|
.notification-item.unread {
|
|
background-color: rgba(0, 123, 255, 0.05);
|
|
border-left: 3px solid var(--bs-primary);
|
|
}
|
|
|
|
.notification-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.notification-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: var(--bs-light);
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.notification-title {
|
|
line-height: 1.3;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.notification-message {
|
|
line-height: 1.4;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.notification-unread .badge {
|
|
width: 8px;
|
|
height: 8px;
|
|
padding: 0;
|
|
}
|
|
|
|
/* Tema scuro */
|
|
.dark .notifications-dropdown {
|
|
background-color: var(--bs-dark);
|
|
border-color: var(--bs-gray-700);
|
|
}
|
|
|
|
.dark .notification-item:hover {
|
|
background-color: var(--bs-gray-800);
|
|
}
|
|
|
|
.dark .notification-item.unread {
|
|
background-color: rgba(0, 123, 255, 0.1);
|
|
}
|
|
|
|
.dark .notification-icon {
|
|
background-color: var(--bs-gray-700);
|
|
}
|
|
|
|
/* Responsive */
|
|
@media (max-width: 768px) {
|
|
.notifications-dropdown {
|
|
width: 300px;
|
|
}
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
{{-- JavaScript per notifiche --}}
|
|
@push('scripts')
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Segna tutte le notifiche come lette
|
|
const markAllReadBtn = document.getElementById('markAllRead');
|
|
if (markAllReadBtn) {
|
|
markAllReadBtn.addEventListener('click', function() {
|
|
fetch('#', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// Rimuovi badge e classi unread
|
|
document.querySelector('.badge.bg-danger')?.remove();
|
|
document.querySelectorAll('.notification-item.unread').forEach(item => {
|
|
item.classList.remove('unread');
|
|
item.querySelector('.notification-unread')?.remove();
|
|
});
|
|
this.remove();
|
|
}
|
|
})
|
|
.catch(error => console.error('Errore:', error));
|
|
});
|
|
}
|
|
|
|
// Segna singola notifica come letta al click
|
|
document.querySelectorAll('.notification-item').forEach(item => {
|
|
item.addEventListener('click', function() {
|
|
const notificationId = this.dataset.notificationId;
|
|
if (this.classList.contains('unread')) {
|
|
fetch(`#`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ id: notificationId })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
this.classList.remove('unread');
|
|
this.querySelector('.notification-unread')?.remove();
|
|
|
|
// Aggiorna contatore
|
|
const badge = document.querySelector('.badge.bg-danger');
|
|
if (badge) {
|
|
const count = parseInt(badge.textContent) - 1;
|
|
if (count <= 0) {
|
|
badge.remove();
|
|
} else {
|
|
badge.textContent = count > 99 ? '99+' : count;
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.catch(error => console.error('Errore:', error));
|
|
}
|
|
});
|
|
});
|
|
|
|
@if($realTime)
|
|
// Aggiornamenti real-time (WebSocket o polling)
|
|
function checkNewNotifications() {
|
|
fetch('#', {
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.hasNew) {
|
|
// Aggiorna badge o ricarica dropdown
|
|
location.reload(); // Temporaneo - implementare aggiornamento dinamico
|
|
}
|
|
})
|
|
.catch(error => console.error('Errore check notifiche:', error));
|
|
}
|
|
|
|
// Check ogni 30 secondi
|
|
setInterval(checkNewNotifications, 30000);
|
|
@endif
|
|
});
|
|
</script>
|
|
@endpush
|