feat(fullstack): Add notifications, user is now an entity, add testcontainers, rework custom permissions, get user from JWT in endpoints
This commit is contained in:
118
legalconsenthub/composables/notification/useNotification.ts
Normal file
118
legalconsenthub/composables/notification/useNotification.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import type { NotificationDto } from '~/.api-client'
|
||||
|
||||
export const useNotification = () => {
|
||||
const {
|
||||
getNotifications,
|
||||
getUnreadNotifications,
|
||||
getUnreadNotificationCount,
|
||||
markAllNotificationsAsRead,
|
||||
markNotificationAsRead
|
||||
} = useNotificationApi()
|
||||
|
||||
const notifications = ref<NotificationDto[]>([])
|
||||
const unreadNotifications = ref<NotificationDto[]>([])
|
||||
const unreadCount = ref<number>(0)
|
||||
const isLoading = ref(false)
|
||||
|
||||
const fetchNotifications = async (page: number = 0, size: number = 20) => {
|
||||
isLoading.value = true
|
||||
try {
|
||||
const response = await getNotifications(page, size)
|
||||
notifications.value = response.content || []
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch notifications:', error)
|
||||
throw error
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const fetchUnreadNotifications = async () => {
|
||||
try {
|
||||
const response = await getUnreadNotifications()
|
||||
unreadNotifications.value = response || []
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch unread notifications:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const fetchUnreadCount = async () => {
|
||||
try {
|
||||
const count = await getUnreadNotificationCount()
|
||||
unreadCount.value = count || 0
|
||||
return count
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch unread count:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const markAllAsRead = async () => {
|
||||
try {
|
||||
await markAllNotificationsAsRead()
|
||||
unreadCount.value = 0
|
||||
unreadNotifications.value = []
|
||||
notifications.value = notifications.value.map((n) => ({ ...n, isRead: true }))
|
||||
} catch (error) {
|
||||
console.error('Failed to mark all as read:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const markAsRead = async (notificationId: string) => {
|
||||
try {
|
||||
await markNotificationAsRead(notificationId)
|
||||
const index = notifications.value.findIndex((n) => n.id === notificationId)
|
||||
if (index !== -1) {
|
||||
notifications.value[index].isRead = true
|
||||
}
|
||||
// Remove from unread notifications
|
||||
unreadNotifications.value = unreadNotifications.value.filter((n) => n.id !== notificationId)
|
||||
|
||||
if (unreadCount.value > 0) {
|
||||
unreadCount.value--
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to mark notification as read:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const handleNotificationClick = async (notification: NotificationDto) => {
|
||||
if (!notification.isRead) {
|
||||
await markAsRead(notification.id)
|
||||
}
|
||||
if (notification.clickTarget) {
|
||||
await navigateTo(notification.clickTarget)
|
||||
}
|
||||
}
|
||||
|
||||
const startPeriodicRefresh = (intervalMs: number = 30000) => {
|
||||
const interval = setInterval(() => {
|
||||
fetchUnreadCount()
|
||||
}, intervalMs)
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(interval)
|
||||
})
|
||||
|
||||
return interval
|
||||
}
|
||||
|
||||
return {
|
||||
notifications: readonly(notifications),
|
||||
unreadNotifications: readonly(unreadNotifications),
|
||||
unreadCount: readonly(unreadCount),
|
||||
isLoading: readonly(isLoading),
|
||||
fetchNotifications,
|
||||
fetchUnreadNotifications,
|
||||
fetchUnreadCount,
|
||||
markAllAsRead,
|
||||
markAsRead,
|
||||
handleNotificationClick,
|
||||
startPeriodicRefresh
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
NotificationApi,
|
||||
Configuration,
|
||||
type NotificationDto,
|
||||
type PagedNotificationDto,
|
||||
type CreateNotificationDto
|
||||
} from '~/.api-client'
|
||||
import { cleanDoubleSlashes, withoutTrailingSlash } from 'ufo'
|
||||
|
||||
export function useNotificationApi() {
|
||||
const appBaseUrl = useRuntimeConfig().app.baseURL
|
||||
const { serverApiBaseUrl, serverApiBasePath, clientProxyBasePath } = useRuntimeConfig().public
|
||||
const { jwt } = useAuth()
|
||||
|
||||
const basePath = withoutTrailingSlash(
|
||||
cleanDoubleSlashes(import.meta.client ? appBaseUrl + clientProxyBasePath : serverApiBaseUrl + serverApiBasePath)
|
||||
)
|
||||
|
||||
const notificationApiClient = new NotificationApi(
|
||||
new Configuration({ basePath, headers: { Authorization: jwt.value ? `Bearer ${jwt.value}` : '' } })
|
||||
)
|
||||
|
||||
async function createNotification(createNotificationDto: CreateNotificationDto): Promise<NotificationDto> {
|
||||
return notificationApiClient.createNotification({ createNotificationDto })
|
||||
}
|
||||
|
||||
async function getNotifications(page?: number, size?: number): Promise<PagedNotificationDto> {
|
||||
return notificationApiClient.getNotifications({ page, size })
|
||||
}
|
||||
|
||||
async function getUnreadNotifications(): Promise<NotificationDto[]> {
|
||||
return notificationApiClient.getUnreadNotifications()
|
||||
}
|
||||
|
||||
async function getUnreadNotificationCount(): Promise<number> {
|
||||
return notificationApiClient.getUnreadNotificationCount()
|
||||
}
|
||||
|
||||
async function markAllNotificationsAsRead(): Promise<void> {
|
||||
return notificationApiClient.markAllNotificationsAsRead()
|
||||
}
|
||||
|
||||
async function markNotificationAsRead(id: string): Promise<NotificationDto> {
|
||||
return notificationApiClient.markNotificationAsRead({ id })
|
||||
}
|
||||
|
||||
return {
|
||||
createNotification,
|
||||
getNotifications,
|
||||
getUnreadNotifications,
|
||||
getUnreadNotificationCount,
|
||||
markAllNotificationsAsRead,
|
||||
markNotificationAsRead
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user