191 lines
5.9 KiB
Svelte
191 lines
5.9 KiB
Svelte
<script lang="ts">
|
|
import { deleteNotification, setViewed, fetchSubscriptionNotifications } from '$lib/api';
|
|
import type { Notification, Subscription } from '$lib/api';
|
|
let { data } = $props();
|
|
|
|
let subscription: Subscription | null = $state(data.subscription);
|
|
let notifications: Notification[] = $state(data.notifications);
|
|
let selectedNotification: Notification | null = $state(null);
|
|
|
|
// Delete all notifications for this subscription
|
|
async function handleDeleteAllNotifications() {
|
|
if (notifications.length === 0) return;
|
|
const confirmed = window.confirm(
|
|
'Are you sure you want to delete all notifications for this subscription?'
|
|
);
|
|
if (!confirmed) return;
|
|
try {
|
|
await Promise.all(notifications.map((notification) => deleteNotification(notification.id)));
|
|
notifications = [];
|
|
window.showNotification('success', 'All notifications deleted successfully.');
|
|
} catch (error) {
|
|
window.showNotification('error', 'Failed to delete all notifications - ' + error);
|
|
}
|
|
}
|
|
|
|
// Pagination state
|
|
let limit = 20;
|
|
let offset = $derived(notifications.length);
|
|
let loadingMore = $state(false);
|
|
let allLoaded = $derived(notifications.length < limit);
|
|
|
|
async function openNotificationPopup(notification: Notification) {
|
|
if (!notification.viewed) {
|
|
await setViewed(notification.id);
|
|
notifications = notifications.map((n) =>
|
|
n.id === notification.id ? { ...n, viewed: true } : n
|
|
);
|
|
notification.viewed = true;
|
|
}
|
|
|
|
selectedNotification = notification;
|
|
}
|
|
|
|
function closeNotificationPopup() {
|
|
selectedNotification = null;
|
|
}
|
|
|
|
async function handleDeleteNotification(e, id: number) {
|
|
e.stopPropagation();
|
|
|
|
try {
|
|
await deleteNotification(id);
|
|
notifications = notifications.filter((notification) => notification.id !== id);
|
|
window.showNotification('success', 'Notification deleted successfully.');
|
|
} catch (error) {
|
|
window.showNotification('error', 'Failed to delete notification - ' + error);
|
|
}
|
|
}
|
|
async function markAllViewed() {
|
|
try {
|
|
await Promise.all(
|
|
notifications
|
|
.filter((notification) => !notification.viewed)
|
|
.map((notification) => setViewed(notification.id))
|
|
);
|
|
notifications = notifications.map((notification) =>
|
|
notification.viewed ? notification : { ...notification, viewed: true }
|
|
);
|
|
window.showNotification('success', 'All notifications marked as viewed.');
|
|
} catch (error) {
|
|
window.showNotification('error', 'Failed to mark all notifications as viewed.');
|
|
}
|
|
}
|
|
|
|
// Load more notifications
|
|
async function loadMoreNotifications() {
|
|
loadingMore = true;
|
|
try {
|
|
const more = await fetchSubscriptionNotifications(subscription!.id.toString(), limit, offset);
|
|
notifications = [...notifications, ...more];
|
|
offset += more.length;
|
|
if (more.length < limit) {
|
|
allLoaded = true;
|
|
}
|
|
} catch (err) {
|
|
window.showNotification('error', 'Failed to load more notifications - ' + err);
|
|
}
|
|
loadingMore = false;
|
|
}
|
|
</script>
|
|
|
|
<main class="p-4">
|
|
<h1 class="text-2xl font-bold mb-4">Notifications for {subscription!.topic}:</h1>
|
|
|
|
<div class="flex justify-between items-center mb-4">
|
|
<a href="/notifications" class="text-blue-500 hover:underline">← Return to Subscriptions</a>
|
|
<div class="flex gap-2">
|
|
<button class="bg-blue-500 text-white px-4 py-2 rounded" onclick={markAllViewed}>
|
|
Mark All Viewed
|
|
</button>
|
|
<button
|
|
class="bg-red-500 text-white px-4 py-2 rounded"
|
|
onclick={handleDeleteAllNotifications}
|
|
disabled={notifications.length === 0}
|
|
>
|
|
Delete All Notifications
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{#if notifications.length === 0}
|
|
<p>No notifications found for this topic.</p>
|
|
{:else}
|
|
<ul class="space-y-4">
|
|
{#each notifications as notification (notification.id)}
|
|
<li
|
|
class="p-2 rounded flex justify-between items-center border-s-slate-400 border-1"
|
|
class:bg-green-200={notification.viewed}
|
|
>
|
|
<button class="p-2 w-full text-left" onclick={() => openNotificationPopup(notification)}>
|
|
<div>
|
|
<p class="font-semibold">{notification.title}</p>
|
|
<p class="text-sm text-gray-500">
|
|
{new Date(notification.created_at).toLocaleString()}
|
|
</p>
|
|
</div>
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 bg-red-500 text-white rounded hover:bg-red-600 block"
|
|
onclick={(e) => handleDeleteNotification(e, notification.id)}
|
|
>
|
|
Delete
|
|
</button>
|
|
</li>
|
|
{/each}
|
|
</ul>
|
|
{/if}
|
|
{#if !allLoaded && notifications.length > 0}
|
|
<div class="flex justify-center mt-6">
|
|
<button
|
|
class="bg-gray-200 px-4 py-2 rounded hover:bg-gray-300"
|
|
onclick={loadMoreNotifications}
|
|
disabled={loadingMore}
|
|
>
|
|
{loadingMore ? 'Loading...' : 'Load More'}
|
|
</button>
|
|
</div>
|
|
{/if}
|
|
{#if selectedNotification}
|
|
<div class="fixed inset-0 bg-opacity-30 backdrop-blur-sm flex items-center justify-center z-50">
|
|
<div class="bg-white p-6 rounded shadow-lg w-3/4 max-w-2xl max-h-[80vh] overflow-y-auto">
|
|
<h3 class="text-lg font-bold mb-4">Notification Details</h3>
|
|
<div class="mb-4">
|
|
<p class="font-semibold">Title:</p>
|
|
<pre
|
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedNotification.title}</pre>
|
|
</div>
|
|
<div class="mb-4">
|
|
<p class="font-semibold">Message:</p>
|
|
<pre
|
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedNotification.message}</pre>
|
|
</div>
|
|
<div class="mb-4">
|
|
<p class="font-semibold">Priority:</p>
|
|
<pre
|
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedNotification.priority}</pre>
|
|
</div>
|
|
<div class="mb-4">
|
|
<p class="font-semibold">Created At:</p>
|
|
<pre class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{new Date(
|
|
selectedNotification.created_at
|
|
).toLocaleString()}</pre>
|
|
</div>
|
|
<button
|
|
class="mt-4 px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
|
|
onclick={closeNotificationPopup}
|
|
>
|
|
Close
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</main>
|
|
|
|
<style>
|
|
main {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
</style>
|