- Real-time updates (Laravel Reverb): live operator queue, live ticket chat/detail updates for operator and client, periodic fallback refresh with a visible countdown as a backstop for dropped websocket connections. - SLA automation rules (Admin > Automatyzacja SLA): act on a ticket after N minutes of customer silence (change priority/status/team/assignee), evaluated every 15 minutes, reusing TicketService's own setters so automated changes get the same history/notification/broadcast a manual change would. - New notification: every operator on a matching team gets notified when a new ticket lands in one of their subcategories. - BookStack knowledge-base sidebar now also shown on the client's own ticket view (previously operator-only); suggestions everywhere now load in after first paint instead of blocking it. - Client ticket view: shows assigned operator + team; page widened to match the operator's. - Notification bell shows unread only; read notifications disappear instead of just dimming. - Stats dashboard: sectioned layout, new breakdowns (by subcategory, CSAT by team/operator, top clients, client x subcategory cross-tab). - Mobile: nav dropdowns (theme/notifications/profile) now expand full width instead of overflowing off-screen below 640px. - Fixed two bugs that silently disabled all real-time updates (missing CSRF header on Echo's private-channel auth; a script-load-order race that could miss the livewire:init event) and the mariadb healthcheck (world-writable credentials file on this stack's NFS mount). - Assorted test-suite fixes (roles virtual attribute needs the roles table seeded; a few missing seeds/wrong assertions found along the way). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
39 lines
2.6 KiB
PHP
39 lines
2.6 KiB
PHP
<div x-data="{ open: false }" @click.outside="open = false" class="nav-dropdown-wrap" style="position:relative;display:inline-block" wire:poll.30s="$refresh">
|
|
<button type="button" class="btn btn-secondary" @click="open = !open" style="position:relative;display:flex;align-items:center;gap:0;padding:8px">
|
|
<span class="material-symbols-outlined" style="font-size:18px">notifications</span>
|
|
@if ($this->unreadCount)
|
|
<span style="position:absolute;top:2px;right:2px;min-width:16px;height:16px;padding:0 3px;border-radius:8px;background:var(--color-accent);color:#fff;font-size:10px;line-height:16px;text-align:center">{{ $this->unreadCount > 9 ? '9+' : $this->unreadCount }}</span>
|
|
@endif
|
|
</button>
|
|
|
|
<div
|
|
x-show="open"
|
|
x-cloak
|
|
class="nav-dropdown"
|
|
style="position:absolute;top:100%;right:0;margin-top:6px;background:var(--color-surface);border:1px solid var(--color-divider);border-radius:8px;box-shadow:var(--shadow-md);width:320px;max-height:420px;overflow-y:auto;z-index:30"
|
|
>
|
|
<div style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-bottom:1px solid var(--color-divider)">
|
|
<span style="font-size:12.5px;font-weight:600">Powiadomienia</span>
|
|
@if ($this->unreadCount)
|
|
<button type="button" wire:click="markAllAsRead" style="font-size:11.5px;background:none;border:none;color:var(--color-accent);cursor:pointer;padding:0">Oznacz wszystkie jako przeczytane</button>
|
|
@endif
|
|
</div>
|
|
|
|
@forelse ($this->notifications as $notification)
|
|
<a
|
|
wire:key="notification-{{ $notification->id }}"
|
|
href="{{ $notification->data['url'] ?? '#' }}"
|
|
wire:navigate
|
|
wire:click="markAsRead('{{ $notification->id }}')"
|
|
@click="open = false"
|
|
style="display:block;padding:10px 14px;text-decoration:none;color:var(--color-text);border-bottom:1px solid var(--color-divider);font-size:12.5px;background:color-mix(in srgb, var(--color-accent) 6%, transparent)"
|
|
>
|
|
<div>{{ $notification->data['message'] ?? '' }}</div>
|
|
<div style="font-size:11px;color:color-mix(in srgb, var(--color-text) 55%, transparent);margin-top:2px">{{ $notification->created_at->diffForHumans() }}</div>
|
|
</a>
|
|
@empty
|
|
<div style="padding:20px 14px;text-align:center;font-size:12.5px;color:color-mix(in srgb, var(--color-text) 55%, transparent)">Brak powiadomień</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|