Files
frontend/app/pages/notifications.vue

211 lines
6.9 KiB
Vue

<script setup lang="ts">
import type { Notification } from '~/types/api'
definePageMeta({
middleware: 'auth',
})
useHead({ title: 'Notifiche - Visione' })
const { fetchNotifications, markNotificationRead, refreshUnreadNotifications } = useAuth()
const router = useRouter()
const page = ref(1)
const perPage = ref(15)
const { data: notificationsData, refresh: refreshNotifications } = await useAsyncData(
'notifications',
() => fetchNotifications({ page: page.value, per_page: perPage.value }),
{ watch: [page] }
)
const notifications = computed<Notification[]>(() => notificationsData.value?.notifications || [])
const totalNotifications = computed(() => notificationsData.value?.notifications?.length || 0)
const unreadCount = computed(() => notifications.value.filter((notification) => !notification.read_at).length)
const handleMarkAsRead = async (notificationId: number) => {
try {
await markNotificationRead(notificationId)
await refreshNotifications()
await refreshUnreadNotifications()
} catch (error) {
console.error('Error marking notification as read:', error)
}
}
const handleNotificationClick = async (notification: Notification) => {
if (!notification.read_at) {
await handleMarkAsRead(notification.id)
}
if (notification.report_id) {
router.push(`/reports/${notification.report_id}`)
}
}
const formatDate = (dateString: string): string => {
const date = new Date(dateString)
const now = new Date()
const diffMs = now.getTime() - date.getTime()
const diffMins = Math.floor(diffMs / 60000)
const diffHours = Math.floor(diffMins / 60)
const diffDays = Math.floor(diffHours / 24)
if (diffMins < 1) return 'Proprio ora'
if (diffMins < 60) return `${diffMins}m fa`
if (diffHours < 24) return `${diffHours}h fa`
if (diffDays < 7) return `${diffDays}d fa`
return date.toLocaleDateString('it-IT', {
day: '2-digit',
month: '2-digit',
year: '2-digit'
})
}
const getNotificationIcon = (type: string): string => {
const icons: Record<string, string> = {
'report_created': 'it-pencil',
'report_assigned': 'it-check-circle',
'report_updated': 'it-refresh',
'report_resolved': 'it-check-circle',
'message_received': 'it-comment',
'feedback_requested': 'it-star',
'reward_earned': 'it-star',
}
return icons[type] || 'it-bell'
}
const getNotificationColor = (type: string): string => {
const colors: Record<string, string> = {
'report_created': 'primary',
'report_assigned': 'info',
'report_updated': 'info',
'report_resolved': 'success',
'message_received': 'primary',
'feedback_requested': 'warning',
'reward_earned': 'warning',
}
return colors[type] || 'secondary'
}
</script>
<template>
<div>
<div class="bg-light border-bottom py-4 mb-4">
<div class="container">
<div class="row">
<div class="col-12">
<h1 class="h3 font-weight-bold mb-2">
<BIIcon name="it-bell" size="sm" color="primary" class="me-2" />
Centro Notifiche
</h1>
<p class="text-muted mb-0">
{{ unreadCount > 0 ? `Hai ${unreadCount} notifiche non lette` : 'Tutte le notifiche lette' }}
</p>
</div>
</div>
</div>
</div>
<div class="container mb-5">
<div class="row">
<div class="col-12 col-lg-10 offset-lg-1">
<div v-if="notifications.length === 0" class="card card-bg border-0 shadow-sm">
<div class="card-body py-5 text-center">
<BIIcon name="it-bell" size="lg" color="secondary" class="mb-3 d-block mx-auto" />
<h2 class="h5 mb-2">Nessuna notifica</h2>
<p class="text-muted mb-3">Quando arriveranno aggiornamenti sulle tue segnalazioni, li troverai qui.</p>
<NuxtLink to="/reports" class="btn btn-outline-primary btn-sm">
Vai alle segnalazioni
</NuxtLink>
</div>
</div>
<div v-else class="card card-bg border-0 shadow-sm">
<div class="card-body p-0">
<div
v-for="notification in notifications"
:key="notification.id"
class="notification-row p-3"
:class="{ 'notification-row-unread': !notification.read_at }"
>
<div class="d-flex align-items-start gap-3">
<div class="flex-shrink-0 mt-1">
<div
class="rounded-circle d-flex align-items-center justify-content-center notification-icon"
:class="`bg-${getNotificationColor(notification.type)}`"
>
<BIIcon :name="getNotificationIcon(notification.type)" size="sm" color="white" />
</div>
</div>
<div class="flex-grow-1">
<div class="d-flex flex-wrap justify-content-between gap-2">
<div>
<h2 class="h6 mb-1">{{ notification.title }}</h2>
<p class="mb-1 text-muted">{{ notification.body }}</p>
<small class="text-muted">{{ formatDate(notification.created_at) }}</small>
</div>
<div v-if="!notification.read_at" class="flex-shrink-0">
<span class="badge bg-primary">Nuovo</span>
</div>
</div>
<div class="mt-3 d-flex flex-wrap gap-2">
<button
type="button"
class="btn btn-outline-primary btn-sm"
@click="handleNotificationClick(notification)"
>
Apri notifica
</button>
<NuxtLink v-if="notification.report_id" :to="`/reports/${notification.report_id}`" class="btn btn-outline-primary btn-sm">
Vedi segnalazione
</NuxtLink>
<button
v-if="!notification.read_at"
type="button"
class="btn btn-outline-secondary btn-sm"
@click.stop="handleMarkAsRead(notification.id)"
>
Segna come letta
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<Pagination
v-if="notifications.length > 0"
:current-page="page"
:per-page="perPage"
:total-items="totalNotifications"
@page-change="page = $event"
/>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.notification-row + .notification-row {
border-top: 1px solid #e9ecef;
}
.notification-row-unread {
border-left: 4px solid #0066cc;
background: #f8fbff;
}
.notification-icon {
width: 40px;
height: 40px;
}
</style>