update
This commit is contained in:
187
resources/views/dashboard/notifications.blade.php
Normal file
187
resources/views/dashboard/notifications.blade.php
Normal file
@@ -0,0 +1,187 @@
|
||||
@extends('layouts.nova')
|
||||
|
||||
@section('content')
|
||||
<x-nova-page-header
|
||||
section="Dashboard"
|
||||
title="Notifications"
|
||||
icon="fa-bell"
|
||||
:breadcrumbs="collect([
|
||||
(object) ['name' => 'Dashboard', 'url' => '/dashboard'],
|
||||
(object) ['name' => 'Notifications', 'url' => route('dashboard.notifications')],
|
||||
])"
|
||||
description="A dedicated feed for follows, likes, comments, and system events so you can triage account activity quickly."
|
||||
>
|
||||
<x-slot name="actions">
|
||||
<form id="mark-all-notifications-read-form" class="contents">
|
||||
<button
|
||||
type="button"
|
||||
id="mark-all-notifications-read"
|
||||
class="inline-flex items-center gap-2 rounded-lg border border-white/[0.08] bg-white/[0.04] px-4 py-2 text-sm font-medium text-white/70 transition-colors hover:bg-white/[0.08] hover:text-white"
|
||||
>
|
||||
<i class="fa-solid fa-check-double text-xs"></i>
|
||||
Mark all read
|
||||
</button>
|
||||
</form>
|
||||
</x-slot>
|
||||
</x-nova-page-header>
|
||||
|
||||
<div class="px-6 pb-16 pt-8 md:px-10">
|
||||
<div class="mb-6 flex flex-wrap items-center gap-2 rounded-2xl border border-white/[0.06] bg-white/[0.025] p-2 shadow-[0_12px_30px_rgba(0,0,0,0.14)]">
|
||||
<a href="{{ route('dashboard.comments.received') }}"
|
||||
class="inline-flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-medium text-white/60 transition hover:bg-white/[0.05] hover:text-white">
|
||||
<i class="fa-solid fa-inbox"></i>
|
||||
Received
|
||||
</a>
|
||||
<a href="{{ route('messages.index') }}"
|
||||
class="inline-flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-medium text-white/60 transition hover:bg-white/[0.05] hover:text-white">
|
||||
<i class="fa-solid fa-envelope"></i>
|
||||
Messages
|
||||
</a>
|
||||
<a href="{{ route('dashboard.notifications') }}"
|
||||
class="inline-flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-medium transition {{ request()->routeIs('dashboard.notifications') ? 'bg-sky-500/15 text-sky-200 ring-1 ring-sky-400/20' : 'text-white/60 hover:bg-white/[0.05] hover:text-white' }}">
|
||||
<i class="fa-solid fa-bell"></i>
|
||||
Notifications
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mb-6 grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
|
||||
<div class="rounded-2xl border border-sky-400/20 bg-sky-500/10 p-5 shadow-[0_18px_45px_rgba(10,132,255,0.10)]">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.22em] text-sky-200/75">Unread</p>
|
||||
<p class="mt-3 text-3xl font-semibold text-white">{{ number_format($unreadCount) }}</p>
|
||||
<p class="mt-2 text-sm text-sky-100/70">Notifications that still need attention.</p>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-violet-400/20 bg-violet-500/10 p-5">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.22em] text-violet-200/75">Loaded</p>
|
||||
<p class="mt-3 text-3xl font-semibold text-white">{{ number_format($notifications->count()) }}</p>
|
||||
<p class="mt-2 text-sm text-violet-100/70">Items shown on this page.</p>
|
||||
</div>
|
||||
<div class="rounded-2xl border border-amber-400/20 bg-amber-500/10 p-5">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.22em] text-amber-200/75">Total feed</p>
|
||||
<p class="mt-3 text-3xl font-semibold text-white">{{ number_format((int) ($notificationsMeta['total'] ?? 0)) }}</p>
|
||||
<p class="mt-2 text-sm text-amber-100/70">All notifications stored for your account.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($notifications->isEmpty())
|
||||
<div class="rounded-[28px] border border-white/[0.07] bg-white/[0.025] px-8 py-16 text-center shadow-[0_18px_45px_rgba(0,0,0,0.18)]">
|
||||
<div class="mx-auto flex h-16 w-16 items-center justify-center rounded-full border border-white/[0.08] bg-white/[0.03] text-white/35">
|
||||
<i class="fa-regular fa-bell text-2xl"></i>
|
||||
</div>
|
||||
<h2 class="mt-5 text-2xl font-semibold text-white">No notifications yet</h2>
|
||||
<p class="mx-auto mt-3 max-w-xl text-sm leading-6 text-white/45">
|
||||
Follows, comments, likes, and system notices will appear here as your account becomes more active.
|
||||
</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="space-y-4">
|
||||
@foreach($notifications as $item)
|
||||
@php
|
||||
$isUnread = !($item['read'] ?? false);
|
||||
$actor = $item['actor'] ?? null;
|
||||
$destination = $item['url'] ?: route('dashboard.comments.received');
|
||||
@endphp
|
||||
<a
|
||||
href="{{ $destination }}"
|
||||
class="block rounded-[24px] border p-5 transition hover:bg-white/[0.04] {{ $isUnread ? 'border-sky-400/20 bg-sky-500/[0.08]' : 'border-white/[0.07] bg-white/[0.025]' }}"
|
||||
data-notification-link
|
||||
data-notification-id="{{ $item['id'] }}"
|
||||
data-notification-read="{{ ($item['read'] ?? false) ? '1' : '0' }}"
|
||||
>
|
||||
<div class="flex items-start gap-4">
|
||||
<img
|
||||
src="{{ $actor['avatar_url'] ?? 'https://files.skinbase.org/default/avatar_default.webp' }}"
|
||||
alt="{{ $actor['name'] ?? 'Notification' }}"
|
||||
class="h-12 w-12 rounded-full object-cover ring-1 ring-white/10"
|
||||
>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex flex-col gap-2 md:flex-row md:items-start md:justify-between">
|
||||
<div class="min-w-0">
|
||||
<p class="text-sm font-semibold text-white/90">{{ $item['message'] ?? 'New activity' }}</p>
|
||||
<div class="mt-1 flex flex-wrap items-center gap-2 text-xs uppercase tracking-[0.16em] text-white/30">
|
||||
<span>{{ $item['type'] ?? 'notification' }}</span>
|
||||
<span>{{ $item['time_ago'] ?? '' }}</span>
|
||||
@if(!empty($actor['username']))
|
||||
<span>{{ '@' . $actor['username'] }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
@if($isUnread)
|
||||
<span class="inline-flex items-center gap-1.5 rounded-full border border-sky-400/25 bg-sky-500/12 px-3 py-1 text-xs font-semibold text-sky-200">
|
||||
<i class="fa-solid fa-circle text-[8px]"></i>
|
||||
Unread
|
||||
</span>
|
||||
@else
|
||||
<span class="inline-flex items-center gap-1.5 rounded-full border border-white/[0.08] bg-white/[0.03] px-3 py-1 text-xs font-semibold text-white/50">
|
||||
Read
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if(($notificationsMeta['last_page'] ?? 1) > 1)
|
||||
<div class="mt-8 flex flex-wrap justify-center gap-2">
|
||||
@for($page = 1; $page <= (int) ($notificationsMeta['last_page'] ?? 1); $page++)
|
||||
<a
|
||||
href="{{ route('dashboard.notifications', ['page' => $page]) }}"
|
||||
class="inline-flex h-10 min-w-10 items-center justify-center rounded-xl border px-3 text-sm font-medium transition {{ (int) ($notificationsMeta['current_page'] ?? 1) === $page ? 'border-sky-400/30 bg-sky-500/15 text-sky-200' : 'border-white/[0.08] bg-white/[0.03] text-white/60 hover:bg-white/[0.06] hover:text-white' }}"
|
||||
>
|
||||
{{ $page }}
|
||||
</a>
|
||||
@endfor
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
(function () {
|
||||
var csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
|
||||
var markAllButton = document.getElementById('mark-all-notifications-read');
|
||||
|
||||
async function postJson(url) {
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'X-CSRF-TOKEN': csrfToken,
|
||||
},
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
}
|
||||
|
||||
if (markAllButton) {
|
||||
markAllButton.addEventListener('click', async function () {
|
||||
markAllButton.disabled = true;
|
||||
try {
|
||||
await postJson('/api/notifications/read-all');
|
||||
window.location.reload();
|
||||
} catch (_error) {
|
||||
markAllButton.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-notification-link]').forEach(function (link) {
|
||||
link.addEventListener('click', async function (event) {
|
||||
if (link.dataset.notificationRead === '1') return;
|
||||
|
||||
event.preventDefault();
|
||||
try {
|
||||
await postJson('/api/notifications/' + link.dataset.notificationId + '/read');
|
||||
} catch (_error) {
|
||||
// Navigate even if mark-read fails.
|
||||
}
|
||||
window.location.assign(link.href);
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
Reference in New Issue
Block a user