diff --git a/src/app/actions/notificationActions.ts b/src/app/actions/notificationActions.ts index 81d3fe2..a2ebdb7 100644 --- a/src/app/actions/notificationActions.ts +++ b/src/app/actions/notificationActions.ts @@ -47,7 +47,7 @@ export async function fetchActiveNotifications() { } } -export async function fetchAllNotifications() { +export async function fetchAllNotifications(page: number = 0, size: number = 10) { const token = await getSessionData('token'); try { @@ -59,11 +59,11 @@ export async function fetchAllNotifications() { message_body: { action: 'list', authToken: token, - page: 0, - size: 10, + page: page, + size: size, sortBy: "createdAt", sortDirection: "DESC", - types: [], //"SEARCH_RESULT", "MONITORING_RESULT" + types: [], //"SEARCH_RESULT", "MONITORING_RESULT" statuses: [] //"NEW" } }), diff --git a/src/app/hooks/react-query/useAllNotificationsList.ts b/src/app/hooks/react-query/useAllNotificationsList.ts index 9bc5d90..e680a6f 100644 --- a/src/app/hooks/react-query/useAllNotificationsList.ts +++ b/src/app/hooks/react-query/useAllNotificationsList.ts @@ -3,20 +3,24 @@ import { fetchAllNotifications, NotificationBody } from '@/app/actions/notificat export interface UseAllNotifications { notifications: NotificationBody[], - unreadCount: number + currentPage: number, + totalElements: number, + totalPages: number, } -export const useAllNotifications = () => { +export const useAllNotifications = (page: number, size: number = 10) => { return useQuery({ - queryKey: ['allNotifications'], + queryKey: ['allNotifications', page, size], queryFn: () => { - return fetchAllNotifications() + return fetchAllNotifications(page, size) }, select: (data: UseAllNotifications | null) => { if (!data) { return { notifications: [], - unreadCount: 0 + currentPage: 0, + totalElements: 0, + totalPages: 0, } } return data; diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss index 2711554..718834a 100644 --- a/src/app/styles/pages-styles.scss +++ b/src/app/styles/pages-styles.scss @@ -4735,65 +4735,6 @@ } } - .pagination-controls { - display: flex; - align-items: center; - gap: 0.5rem; - - .arrow { - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; - border: 2px solid v.$border-color-1; - border-radius: 10px; - box-shadow: 0 1px 2px #0000000d; - color: v.$p-color; - - .icon { - width: 18px; - } - - &:hover { - background-color: v.$bg-light; - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } - } - - &-pages { - display: flex; - align-items: center; - gap: 0.25rem; - - button { - border: 2px solid v.$border-color-1; - padding: 0.25rem 0.75rem; - border-radius: 10px; - color: v.$p-color; - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } - - &.current { - background-color: v.$p-color; - color: v.$white; - } - - &.other { - &:hover { - background-color: v.$bg-light; - } - } - } - } - } - .violation-page-note { margin-top: auto; margin-bottom: 0; @@ -5117,6 +5058,7 @@ border-radius: 0.5rem; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); max-width: calc(100vw - var(--side-bar-width) - 60px); + background-color: v.$white; @media (max-width: 440px) { max-width: calc(100vw - var(--side-bar-width) - 30px); @@ -5247,6 +5189,11 @@ } } } + + &-footer { + padding: 10px; + border-top: 1px solid v.$b-color-2; + } } .payment-user-cards { @@ -5374,4 +5321,63 @@ color: #9ca3af; line-height: 1.6; } +} + +.pagination-controls { + display: flex; + align-items: center; + gap: 0.5rem; + + .arrow { + padding-left: 0.5rem; + padding-right: 0.5rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + border: 2px solid v.$border-color-1; + border-radius: 10px; + box-shadow: 0 1px 2px #0000000d; + color: v.$p-color; + + .icon { + width: 18px; + } + + &:hover { + background-color: v.$bg-light; + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + } + + &-pages { + display: flex; + align-items: center; + gap: 0.25rem; + + button { + border: 2px solid v.$border-color-1; + padding: 0.25rem 0.75rem; + border-radius: 10px; + color: v.$p-color; + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + &.current { + background-color: v.$p-color; + color: v.$white; + } + + &.other { + &:hover { + background-color: v.$bg-light; + } + } + } + } } \ No newline at end of file diff --git a/src/app/ui/header/notificationsButton.tsx b/src/app/ui/header/notificationsButton.tsx index 3b113f9..aaadc8b 100644 --- a/src/app/ui/header/notificationsButton.tsx +++ b/src/app/ui/header/notificationsButton.tsx @@ -20,10 +20,6 @@ export default function NotificationsButton() { const { data: notificationList } = useActiveNotifications(); const queryClient = useQueryClient(); - useEffect(() => { - console.log(notificationList); - }, [notificationList]) - useClickOutside( menuRef, () => { diff --git a/src/app/ui/notifications/notifications-list.tsx b/src/app/ui/notifications/notifications-list.tsx index 3326878..424b335 100644 --- a/src/app/ui/notifications/notifications-list.tsx +++ b/src/app/ui/notifications/notifications-list.tsx @@ -1,14 +1,59 @@ 'use client' import { useTranslations } from 'next-intl'; -import { IconCheck } from '@/app/ui/icons/icons'; +import { IconCheck, IconArrowLeft, IconArrowRight, IconDoubleArrowLeft, IconDoubleArrowRight } from '@/app/ui/icons/icons'; import { useEffect, useState } from 'react'; import { useAllNotifications } from '@/app/hooks/react-query/useAllNotificationsList'; +import { notificationsMarkAsRead } from '@/app/actions/notificationActions'; +import { useQueryClient } from '@tanstack/react-query'; export function NotificationsList() { const t = useTranslations('Global'); + const queryClient = useQueryClient(); + const [selectedFiles, setSelectedFiles] = useState>(new Set()); - const { data: allNotifications } = useAllNotifications(); + const [loadingIds, setLoadingIds] = useState>(new Set()); + const [error, setError] = useState(null); + const [currentPage, setCurrentPage] = useState(0); + const [pageSize] = useState(10); + const { data: allNotifications, isLoading } = useAllNotifications(currentPage, pageSize); + + + const totalPages = allNotifications?.totalPages || 0; + const currentPageNum = allNotifications?.currentPage || 0; + + const goToFirstPage = () => setCurrentPage(0); + const goToPrevPage = () => setCurrentPage(prev => Math.max(0, prev - 1)); + const goToNextPage = () => setCurrentPage(prev => Math.min(totalPages - 1, prev + 1)); + const goToLastPage = () => setCurrentPage(totalPages - 1); + const goToPage = (page: number) => setCurrentPage(page); + + const getPageNumbers = (): (number | string)[] => { + const delta: number = 2; + const range: number[] = []; + const rangeWithDots: (number | string)[] = []; + let l: number | undefined; + + for (let i = 0; i < totalPages; i++) { + if (i === 0 || i === totalPages - 1 || (i >= currentPageNum - delta && i <= currentPageNum + delta)) { + range.push(i); + } + } + + range.forEach((i: number) => { + if (l !== undefined) { + if (i - l === 2) { + rangeWithDots.push(l + 1); + } else if (i - l !== 1) { + rangeWithDots.push('...'); + } + } + rangeWithDots.push(i); + l = i; + }); + + return rangeWithDots; + }; useEffect(() => { console.log(allNotifications); @@ -35,6 +80,46 @@ export function NotificationsList() { setSelectedFiles(new Set(allNotifications?.notifications.map(item => item.id))); } + async function markAsReadHandler(ids: number[]) { + setLoadingIds(prev => { + const newSet = new Set(prev); + ids.forEach(id => newSet.add(id)); + return newSet; + }); + + try { + /* const response = await notificationsMarkAsRead(ids); + + if (response.success) { + await queryClient.invalidateQueries({ queryKey: ['activeNotifications'] }); + await queryClient.invalidateQueries({ queryKey: ['allNotifications'] }); + } */ + } catch (error) { + setError(error instanceof Error ? error.message : 'Failed to mark as read'); + } finally { + setLoadingIds(prev => { + const newSet = new Set(prev); + ids.forEach(id => newSet.delete(id)); + return newSet; + }); + + setSelectedFiles(prev => { + const newSet = new Set(prev); + ids.forEach(id => newSet.delete(id)); + return newSet; + }); + } + } + + function removeHandler(id: number) { + console.log(id); + + setSelectedFiles(prev => { + prev.delete(id) + return new Set(prev); + }); + } + return (
{ - clearHandler(); + const array = Array.from(selectedFiles); + markAsReadHandler(array); }} > {t('mark-all-as-read')} @@ -137,6 +223,10 @@ export function NotificationsList() { @@ -147,6 +237,57 @@ export function NotificationsList() { }) )}
+ +
+ {totalPages > 0 && ( +
+ + + +
+ {getPageNumbers().map((pageNum, index) => ( + + ))} +
+ + + +
+ )} +
) } \ No newline at end of file