finish notifications featur

This commit is contained in:
smanylov
2026-04-06 13:02:38 +07:00
parent 1fe49c24e8
commit b377c11c41
6 changed files with 62 additions and 20 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "no-copy-frontend", "name": "no-copy-frontend",
"version": "0.79.0", "version": "0.80.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev -p 2999", "dev": "next dev -p 2999",
+4 -4
View File
@@ -62,9 +62,9 @@ export async function fetchAllNotifications(page: number = 0, size: number = 10)
page: page, page: page,
size: size, size: size,
sortBy: "createdAt", sortBy: "createdAt",
sortDirection: "DESC", sortDirection: "ASC", //DESC, ASC
types: [], //"SEARCH_RESULT", "MONITORING_RESULT" types: [], //"SEARCH_RESULT", "MONITORING_RESULT"
statuses: [] //"NEW" statuses: ["NEW", "READIED"] //"NEW"
} }
}), }),
headers: { headers: {
@@ -76,7 +76,7 @@ export async function fetchAllNotifications(page: number = 0, size: number = 10)
if (response.ok) { if (response.ok) {
let parsed = await response.json(); let parsed = await response.json();
if (parsed?.message_body) { if (parsed?.message_code === 0) {
return parsed?.message_body return parsed?.message_body
} else { } else {
throw parsed; throw parsed;
@@ -114,7 +114,7 @@ export async function notificationsMarkAsRead(ids: number[]) {
if (response.ok) { if (response.ok) {
let parsed = await response.json(); let parsed = await response.json();
if (parsed?.message_body) { if (parsed?.message_code === 0) {
return parsed?.message_body return parsed?.message_body
} else { } else {
throw parsed; throw parsed;
+19
View File
@@ -50,6 +50,10 @@
width: 30px; width: 30px;
height: 30px; height: 30px;
} }
/* &.active {
background: v.$color-warning;
} */
} }
} }
@@ -241,6 +245,21 @@
} }
} }
} }
&-unread-count {
position: absolute;
right: -5px;
bottom: -5px;
width: 20px;
height: 20px;
border-radius: 50%;
background: v.$red;
color: v.$white;
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
}
} }
.user-menu { .user-menu {
+15 -1
View File
@@ -5114,6 +5114,10 @@
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }
&.loading {
opacity: 0.6;
}
} }
&-header { &-header {
@@ -5127,7 +5131,7 @@
} }
&-title { &-title {
font-weight: 600; font-weight: 800;
font-size: 18px; font-size: 18px;
margin-bottom: 4px; margin-bottom: 4px;
} }
@@ -5135,6 +5139,7 @@
&-text { &-text {
color: v.$text-s; color: v.$text-s;
margin-bottom: 4px; margin-bottom: 4px;
font-weight: 600;
} }
&-status { &-status {
@@ -5188,6 +5193,15 @@
} }
} }
} }
.notification-item.READIED {
.notification-title,
.notification-text {
font-weight: 500;
opacity: 0.8;
}
}
} }
&-footer { &-footer {
+10 -2
View File
@@ -8,6 +8,7 @@ import { IconNotification } from '@/app/ui/icons/icons';
import { useActiveNotifications } from '@/app/hooks/react-query/useActiveNotificationsList'; import { useActiveNotifications } from '@/app/hooks/react-query/useActiveNotificationsList';
import { NotificationBody, notificationsMarkAsRead } from '@/app/actions/notificationActions'; import { NotificationBody, notificationsMarkAsRead } from '@/app/actions/notificationActions';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { formatDate } from '@/app/lib/formatDate';
export default function NotificationsButton() { export default function NotificationsButton() {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@@ -77,7 +78,7 @@ export default function NotificationsButton() {
return ( return (
<div className="notification-wrapper" ref={menuRef}> <div className="notification-wrapper" ref={menuRef}>
<button <button
className="icon-btn" className={`icon-btn ${notificationList?.unreadCount ? 'active' : ''}`}
id="notifications-btn" id="notifications-btn"
title="Уведомления" title="Уведомления"
onClick={handleButtonClick} onClick={handleButtonClick}
@@ -85,6 +86,13 @@ export default function NotificationsButton() {
<svg viewBox="0 0 24 24" fill="currentColor" style={{ width: "20px", height: "20px" }}> <svg viewBox="0 0 24 24" fill="currentColor" style={{ width: "20px", height: "20px" }}>
<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"></path> <path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"></path>
</svg> </svg>
{notificationList?.unreadCount && (
<span
className="notification-unread-count"
>
{notificationList?.unreadCount ? notificationList?.unreadCount : ''}
</span>
)}
</button> </button>
<div className={`notification-dropdown ${isOpen ? 'show' : ''}`}> <div className={`notification-dropdown ${isOpen ? 'show' : ''}`}>
{error ? ( {error ? (
@@ -118,7 +126,7 @@ export default function NotificationsButton() {
> >
<div className="notification-content"> <div className="notification-content">
<div className="notification-data"> <div className="notification-data">
{item.createdAt ? item.createdAt : '#'} {item.createdAt ? formatDate(item.createdAt) : '#'}
</div> </div>
{/* <div className="notification-title"> {/* <div className="notification-title">
+12 -11
View File
@@ -6,6 +6,7 @@ import { useEffect, useState } from 'react';
import { useAllNotifications } from '@/app/hooks/react-query/useAllNotificationsList'; import { useAllNotifications } from '@/app/hooks/react-query/useAllNotificationsList';
import { notificationsMarkAsRead } from '@/app/actions/notificationActions'; import { notificationsMarkAsRead } from '@/app/actions/notificationActions';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { formatDate, formatDateTime } from '@/app/lib/formatDate';
export function NotificationsList() { export function NotificationsList() {
const t = useTranslations('Global'); const t = useTranslations('Global');
@@ -88,12 +89,12 @@ export function NotificationsList() {
}); });
try { try {
/* const response = await notificationsMarkAsRead(ids); const response = await notificationsMarkAsRead(ids);
if (response.success) { if (response.success) {
await queryClient.invalidateQueries({ queryKey: ['activeNotifications'] }); await queryClient.invalidateQueries({ queryKey: ['activeNotifications'] });
await queryClient.invalidateQueries({ queryKey: ['allNotifications'] }); await queryClient.invalidateQueries({ queryKey: ['allNotifications'] });
} */ }
} catch (error) { } catch (error) {
setError(error instanceof Error ? error.message : 'Failed to mark as read'); setError(error instanceof Error ? error.message : 'Failed to mark as read');
} finally { } finally {
@@ -173,7 +174,7 @@ export function NotificationsList() {
allNotifications?.notifications.map(item => { allNotifications?.notifications.map(item => {
return ( return (
<div <div
className="notification-item" className={`notification-item ${loadingIds.has(item.id) ? 'loading' : ''} ${item.status}`}
key={item.id} key={item.id}
> >
<div <div
@@ -199,13 +200,13 @@ export function NotificationsList() {
<div <div
className="notification-title" className="notification-title"
> >
{item.type} {item.type} {item.id}
</div> </div>
<div {/* <div
className="notification-status" className="notification-status"
> >
{item.status} {item.status}
</div> </div> */}
</div> </div>
<div <div
className="notification-text" className="notification-text"
@@ -218,7 +219,7 @@ export function NotificationsList() {
<div <div
className="notification-date" className="notification-date"
> >
{item.createdAt} {item.createdAt ? `${formatDate(item.createdAt)}: ${formatDateTime(item.createdAt)}` : '---'}
</div> </div>
<button <button
@@ -226,7 +227,7 @@ export function NotificationsList() {
onClick={() => { onClick={() => {
removeHandler(item.id); removeHandler(item.id);
}} }}
disabled={false} disabled={loadingIds.has(item.id)}
> >
{t('remove')} {t('remove')}
</button> </button>