276 lines
13 KiB
PHP
276 lines
13 KiB
PHP
|
|
<header>
|
|
<!-- Topbar -->
|
|
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">
|
|
|
|
<!-- Sidebar Toggle (Topbar) -->
|
|
<button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
|
|
<i class="fa fa-bars"></i>
|
|
</button>
|
|
|
|
<!-- Topbar Search -->
|
|
<form class="d-none d-sm-inline-block form-inline mr-auto ml-md-3 my-2 my-md-0 mw-100 navbar-search">
|
|
<div class="input-group">
|
|
<input type="text" class="form-control bg-light border-0 small" placeholder="Buscar..."
|
|
aria-label="Search" aria-describedby="basic-addon2">
|
|
<div class="input-group-append">
|
|
<button class="btn btn-primary" type="button">
|
|
<i class="fas fa-search fa-sm"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<ul class="navbar-nav ml-auto">
|
|
|
|
<!-- Nav Item - Search (XS) -->
|
|
<li class="nav-item dropdown no-arrow d-sm-none">
|
|
<a class="nav-link dropdown-toggle" href="#" id="searchDropdown" role="button"
|
|
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
<i class="fas fa-search fa-fw"></i>
|
|
</a>
|
|
<div class="dropdown-menu dropdown-menu-right p-3 shadow animated--grow-in"
|
|
aria-labelledby="searchDropdown">
|
|
<form class="form-inline mr-auto w-100 navbar-search">
|
|
<div class="input-group">
|
|
<input type="text" class="form-control bg-light border-0 small"
|
|
placeholder="Buscar..." aria-label="Search">
|
|
<div class="input-group-append">
|
|
<button class="btn btn-primary" type="button">
|
|
<i class="fas fa-search fa-sm"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</li>
|
|
|
|
<!-- Nav Item - Notificaciones -->
|
|
<li class="nav-item dropdown no-arrow mx-1">
|
|
<a class="nav-link dropdown-toggle" href="#" id="alertsDropdown" role="button"
|
|
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
<i class="fas fa-bell fa-fw"></i>
|
|
@if($headerUnreadNotificationsCount > 0)
|
|
<span class="badge badge-danger badge-counter" id="notif-badge">
|
|
{{ $headerUnreadNotificationsCount > 9 ? '9+' : $headerUnreadNotificationsCount }}
|
|
</span>
|
|
@else
|
|
<span class="badge badge-danger badge-counter d-none" id="notif-badge">0</span>
|
|
@endif
|
|
</a>
|
|
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in"
|
|
aria-labelledby="alertsDropdown">
|
|
<h6 class="dropdown-header">Notificaciones</h6>
|
|
|
|
@forelse($headerUnreadNotifications as $notif)
|
|
@php $data = $notif->data; @endphp
|
|
<a class="dropdown-item d-flex align-items-center"
|
|
href="{{ $data['url'] ?? '#' }}"
|
|
onclick="markNotifRead('{{ $notif->id }}')">
|
|
<div class="mr-3">
|
|
<div class="icon-circle bg-{{ $data['color'] ?? 'primary' }}">
|
|
<i class="fas {{ $data['icon'] ?? 'fa-bell' }} text-white"></i>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="small text-gray-500">{{ $notif->created_at->diffForHumans() }}</div>
|
|
<span class="font-weight-bold">{{ $data['title'] ?? '' }}</span>
|
|
@if(!empty($data['body']))
|
|
<div class="small text-truncate" style="max-width:200px">{{ $data['body'] }}</div>
|
|
@endif
|
|
</div>
|
|
</a>
|
|
@empty
|
|
<div class="dropdown-item text-center text-muted py-3">
|
|
<i class="fas fa-check-circle text-success mr-1"></i> Sin notificaciones nuevas
|
|
</div>
|
|
@endforelse
|
|
|
|
<a class="dropdown-item text-center small text-gray-500"
|
|
href="#" onclick="markAllNotifsRead(); return false;">
|
|
Marcar todas como leídas
|
|
</a>
|
|
</div>
|
|
</li>
|
|
|
|
<!-- Nav Item - Mensajes -->
|
|
<li class="nav-item dropdown no-arrow mx-1">
|
|
<a class="nav-link dropdown-toggle" href="{{ route('chat.index') }}" id="messagesDropdown"
|
|
role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
<i class="fas fa-envelope fa-fw"></i>
|
|
@if($headerUnreadMessagesCount > 0)
|
|
<span class="badge badge-danger badge-counter" id="msg-badge">
|
|
{{ $headerUnreadMessagesCount > 9 ? '9+' : $headerUnreadMessagesCount }}
|
|
</span>
|
|
@else
|
|
<span class="badge badge-danger badge-counter d-none" id="msg-badge">0</span>
|
|
@endif
|
|
</a>
|
|
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in"
|
|
aria-labelledby="messagesDropdown">
|
|
<h6 class="dropdown-header">Mensajes</h6>
|
|
|
|
@forelse($headerUnreadConversations as $msg)
|
|
<a class="dropdown-item d-flex align-items-center"
|
|
href="{{ route('chat.show', $msg->sender) }}"
|
|
data-sender-id="{{ $msg->sender->id }}">
|
|
<div class="dropdown-list-image mr-3">
|
|
<img class="rounded-circle" width="40" height="40"
|
|
style="object-fit:cover"
|
|
src="{{ $msg->sender->profile_photo_url }}" alt="">
|
|
<div class="status-indicator bg-success"></div>
|
|
</div>
|
|
<div class="font-weight-bold overflow-hidden">
|
|
<div class="text-truncate">{{ $msg->body ?? '📎 Archivo adjunto' }}</div>
|
|
<div class="small text-gray-500">
|
|
{{ $msg->sender->name }} · {{ $msg->created_at->diffForHumans() }}
|
|
</div>
|
|
</div>
|
|
</a>
|
|
@empty
|
|
<div class="dropdown-item text-center text-muted py-3">
|
|
<i class="fas fa-check-circle text-success mr-1"></i> Sin mensajes nuevos
|
|
</div>
|
|
@endforelse
|
|
|
|
<a class="dropdown-item text-center small text-gray-500" href="{{ route('chat.index') }}">
|
|
Ver todos los mensajes
|
|
</a>
|
|
</div>
|
|
</li>
|
|
|
|
<div class="topbar-divider d-none d-sm-block"></div>
|
|
|
|
<!-- Nav Item - Usuario -->
|
|
<li class="nav-item dropdown no-arrow">
|
|
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button"
|
|
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
<span class="mr-2 d-none d-lg-inline text-gray-600 small">{{ Auth::user()->name }}</span>
|
|
<<<<<<< Updated upstream
|
|
<img class="img-profile rounded-circle" src="{{ Auth::user()->profile_photo_url }}">
|
|
=======
|
|
{{-- <img class="img-profile rounded-circle"
|
|
src="{{ Auth::user()->profile_photo_url }}"
|
|
alt="{{ Auth::user()->name }}"> --}}
|
|
>>>>>>> Stashed changes
|
|
</a>
|
|
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in"
|
|
aria-labelledby="userDropdown">
|
|
<a class="dropdown-item" href="{{ route('perfil') }}">
|
|
<i class="fas fa-user fa-sm fa-fw mr-2 text-gray-400"></i> Mi perfil
|
|
</a>
|
|
<div class="dropdown-divider"></div>
|
|
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display:none">
|
|
@csrf
|
|
</form>
|
|
<a class="dropdown-item" href="{{ route('logout') }}"
|
|
onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
|
|
<i class="fas fa-sign-out-alt fa-sm fa-fw mr-2" style="color:rgb(252,32,32)"></i>
|
|
Salir
|
|
</a>
|
|
</div>
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
</header>
|
|
|
|
<script>
|
|
// ── Mensajes en tiempo real ──────────────────────────────
|
|
function updateBadge(id, increment) {
|
|
const badge = document.getElementById(id);
|
|
if (!badge) return;
|
|
const current = parseInt(badge.textContent) || 0;
|
|
const next = current + increment;
|
|
if (next <= 0) {
|
|
badge.classList.add('d-none');
|
|
badge.textContent = '0';
|
|
} else {
|
|
badge.classList.remove('d-none');
|
|
badge.textContent = next > 9 ? '9+' : next;
|
|
}
|
|
}
|
|
|
|
function waitForEcho(cb) {
|
|
if (window.Echo) cb(); else setTimeout(() => waitForEcho(cb), 100);
|
|
}
|
|
|
|
waitForEcho(() => {
|
|
const authId = {{ auth()->id() }};
|
|
|
|
// Badge + dropdown de mensajes
|
|
window.Echo.channel(`chat.${authId}`)
|
|
.listen('MessageSent', (e) => {
|
|
updateBadge('msg-badge', 1);
|
|
|
|
const list = document.querySelector('#messagesDropdown + .dropdown-list');
|
|
if (!list) return;
|
|
|
|
// Quitar el "sin mensajes nuevos" si existe
|
|
const empty = list.querySelector('.text-muted.py-3');
|
|
if (empty) empty.remove();
|
|
|
|
// Quitar entrada previa del mismo remitente si ya estaba
|
|
const existing = list.querySelector(`[data-sender-id="${e.sender.id}"]`);
|
|
if (existing) existing.remove();
|
|
|
|
// Insertar nueva entrada al tope (después del header)
|
|
const preview = e.body ? e.body : '📎 Archivo adjunto';
|
|
const item = document.createElement('a');
|
|
item.className = 'dropdown-item d-flex align-items-center';
|
|
item.href = `/chat/${e.sender.id}`;
|
|
item.dataset.senderId = e.sender.id;
|
|
item.innerHTML = `
|
|
<div class="dropdown-list-image mr-3">
|
|
<img class="rounded-circle" width="40" height="40"
|
|
style="object-fit:cover"
|
|
src="${e.sender.avatar}" alt="">
|
|
<div class="status-indicator bg-success"></div>
|
|
</div>
|
|
<div class="font-weight-bold overflow-hidden">
|
|
<div class="text-truncate">${preview}</div>
|
|
<div class="small text-gray-500">${e.sender.name} · ahora</div>
|
|
</div>`;
|
|
list.querySelector('.dropdown-header').insertAdjacentElement('afterend', item);
|
|
});
|
|
|
|
// Badge de notificaciones
|
|
window.Echo.channel(`notifications.${authId}`)
|
|
.listen('NewNotification', (e) => {
|
|
updateBadge('notif-badge', 1);
|
|
// Agregar al dropdown sin recargar
|
|
const list = document.querySelector('#alertsDropdown + .dropdown-list');
|
|
const empty = list.querySelector('.text-muted.py-3');
|
|
if (empty) empty.remove();
|
|
const item = document.createElement('a');
|
|
item.className = 'dropdown-item d-flex align-items-center';
|
|
item.href = e.url ?? '#';
|
|
item.innerHTML = `
|
|
<div class="mr-3">
|
|
<div class="icon-circle bg-${e.color}">
|
|
<i class="fas ${e.icon} text-white"></i>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="small text-gray-500">${e.time}</div>
|
|
<span class="font-weight-bold">${e.title}</span>
|
|
${e.body ? `<div class="small text-truncate" style="max-width:200px">${e.body}</div>` : ''}
|
|
</div>`;
|
|
list.querySelector('.dropdown-header').insertAdjacentElement('afterend', item);
|
|
});
|
|
});
|
|
|
|
// ── Marcar notificaciones como leídas ───────────────────
|
|
function markNotifRead(id) {
|
|
axios.post('/notifications/' + id + '/read').catch(() => {});
|
|
}
|
|
|
|
function markAllNotifsRead() {
|
|
axios.post('/notifications/read-all').then(() => {
|
|
document.getElementById('notif-badge').classList.add('d-none');
|
|
document.querySelectorAll('#alertsDropdown + .dropdown-list .dropdown-item[onclick]')
|
|
.forEach(el => el.classList.remove('font-weight-bold'));
|
|
}).catch(() => {});
|
|
}
|
|
</script>
|