From f4b0a63e23343f2fa40370efb49f91b3a0aa8ed6 Mon Sep 17 00:00:00 2001 From: smanylov Date: Mon, 30 Mar 2026 19:11:48 +0700 Subject: [PATCH] add notification list, add notification page --- src/app/[locale]/pages/notifications/page.tsx | 12 ++ src/app/styles/header.scss | 67 ++++++--- src/app/styles/pages-styles.scss | 76 ++++++++++ src/app/ui/header/notificationsButton.tsx | 124 +++++++++++++--- .../ui/notifications/notifications-list.tsx | 132 ++++++++++++++++++ src/i18n/messages/en.json | 6 +- src/i18n/messages/ru.json | 6 +- 7 files changed, 376 insertions(+), 47 deletions(-) create mode 100644 src/app/[locale]/pages/notifications/page.tsx create mode 100644 src/app/ui/notifications/notifications-list.tsx diff --git a/src/app/[locale]/pages/notifications/page.tsx b/src/app/[locale]/pages/notifications/page.tsx new file mode 100644 index 0000000..29385b4 --- /dev/null +++ b/src/app/[locale]/pages/notifications/page.tsx @@ -0,0 +1,12 @@ +import PageTitle from '@/app/ui/page-title'; +import { NotificationsList } from '@/app/ui/notifications/notifications-list'; + +export default function Page() { + return ( +
+ + + +
+ ) +} \ No newline at end of file diff --git a/src/app/styles/header.scss b/src/app/styles/header.scss index e978741..07f7367 100644 --- a/src/app/styles/header.scss +++ b/src/app/styles/header.scss @@ -124,8 +124,10 @@ } } - .notification-dropdown { - .notification-header { + .notification { + /* &-dropdown {} */ + + &-header { padding: 15px 20px; border-bottom: 1px solid v.$b-color-1; display: flex; @@ -138,29 +140,30 @@ font-weight: 600; color: v.$text-p; } - - .notification-count { - font-size: 12px; - color: v.$p-color; - font-weight: 500; - } } - .notification-list { - max-height: 400px; + &-count { + font-size: 12px; + color: v.$p-color; + font-weight: 500; + cursor: pointer; + } + + &-list { + max-height: 600px; overflow-y: auto; } - .notification-item { + &-item { display: flex; - gap: 12px; - padding: 15px 20px; + gap: 4px; + padding: 5px; border-bottom: 1px solid v.$b-color-1; transition: background-color 0.2s; cursor: pointer; } - .notification-title { + &-title { display: block; font-size: 15px; font-weight: 700; @@ -168,24 +171,43 @@ margin-bottom: 6px; } - .notification-icon { - font-size: 24px; - flex-shrink: 0; + &-content { + width: 100%; + position: relative; + padding: 15px 5px; + /* display: flex; + align-items: center; */ } - .notification-text { + &-icon { + font-size: 24px; + flex-shrink: 0; + display: flex; + align-items: center; + + .icon { + color: v.$p-color; + } + } + + &-text { font-size: 13px; color: v.$text-s; - margin-bottom: 4px; + /* margin-bottom: 4px; */ font-weight: 500; } - .notification-time { - font-size: 12px; + &-data { + font-size: 10px; color: v.$text-m; + display: flex; + justify-content: end; + position: absolute; + top: 0px; + right: 0px; } - .notification-footer { + &-footer { padding: 12px 20px; border-top: 1px solid v.$b-color-1; text-align: center; @@ -195,6 +217,7 @@ font-size: 14px; font-weight: 500; text-decoration: none; + cursor: pointer; } } } diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss index c88d634..f24bb2c 100644 --- a/src/app/styles/pages-styles.scss +++ b/src/app/styles/pages-styles.scss @@ -5046,4 +5046,80 @@ background: #2b7fff; padding: 10px 16px; } +} + +.notifications-list { + &-wrapper { + width: 100%; + overflow-x: auto; + 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); + } + + &-header { + background-color: v.$bg-light; + padding: 20px 20px; + font-size: 18px; + font-weight: 600; + border-bottom: 1px solid v.$b-color-2; + + display: flex; + justify-content: space-between; + align-items: center; + + h4 { + font-size: 20px; + height: 36px; + display: flex; + align-items: center; + } + } + + &-action { + .btn-primary { + /* background: v.$p-color; */ + background: #2b7fff; + padding: 5px 20px; + } + } + + &-body { + padding: 0; + + .notification { + &-item { + border-bottom: 1px solid v.$b-color-2; + padding: 20px; + display: flex; + gap: 15px; + + &:last-child { + border-bottom: none; + } + } + + &-check { + display: flex; + align-items: center; + + button { + cursor: pointer; + border: 2px solid v.$border-color-1; + border-radius: 4px; + justify-content: center; + align-items: center; + width: 24px; + height: 24px; + transition: all .3s ease-in; + display: flex; + + &:hover { + border: 2px solid v.$border-color-1-hover; + } + } + } + + &-text {} + } + } } \ No newline at end of file diff --git a/src/app/ui/header/notificationsButton.tsx b/src/app/ui/header/notificationsButton.tsx index b7fb4de..38a23c7 100644 --- a/src/app/ui/header/notificationsButton.tsx +++ b/src/app/ui/header/notificationsButton.tsx @@ -1,18 +1,60 @@ "use client" -import { useState, useRef } from 'react'; +import { useState, useRef, useEffect } from 'react'; import { useClickOutside } from '@/app/hooks/useClickOutside'; import { useTranslations } from 'next-intl'; import Link from 'next/link'; import { IconNotification } from '@/app/ui/icons/icons'; +interface Notification { + id: string, + notificationText: string, + data: string, + link: string, +} + export default function NotificationsButton() { const [isOpen, setIsOpen] = useState(false); - const [data, setData] = useState(null); + const [notificationData, setNotificationData] = useState([]); const [error, setError] = useState(null); const menuRef = useRef(null); const t = useTranslations('Global'); + useEffect(() => { + setNotificationData([ + { + id: '1', + notificationText: 'text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1', + data: '20.20.20', + link: '#' + }, + { + id: '2', + notificationText: 'text2', + data: '20.20.20', + link: '#' + }, + { + id: '3', + notificationText: 'text3', + data: '20.20.20', + link: '#' + }, + { + id: '4', + notificationText: 'text4', + data: '20.20.20', + link: '#' + }, + { + id: '5', + notificationText: 'text5', + data: '20.20.20', + link: '#' + } + ]) + }, []) + useClickOutside( menuRef, () => { @@ -29,6 +71,14 @@ export default function NotificationsButton() { } }; + function markAsReadHandler() { + console.log('click: mark as read'); + } + + function notificationClickHandler(id: string) { + console.log(`click: ${id}`); + } + return (
- {data ? ( -
-
-
- -
-
-
- {t('tokens-added')} + {notificationData.length !== 0 ? ( +
+ {notificationData.map((item) => { + return ( +
{ + notificationClickHandler(item.id); + }} + > +
+ +
+
+
+ {item.data} +
+ + {/*
+ {t('tokens-added')} +
*/} + +
+ {item.notificationText} +
+
-
{t('added')} 100 {t('tokens')}.
-
4 {t('days-ago')}
-
-
- {JSON.stringify(data)} + ) + })}
) : (

- {t('no-data')} + {/* {t('no-data')} */}

)}
- - {t('view-all')} - +
)} diff --git a/src/app/ui/notifications/notifications-list.tsx b/src/app/ui/notifications/notifications-list.tsx new file mode 100644 index 0000000..e597261 --- /dev/null +++ b/src/app/ui/notifications/notifications-list.tsx @@ -0,0 +1,132 @@ +'use client' + +import { useTranslations } from 'next-intl'; +import { IconCheck } from '@/app/ui/icons/icons'; +import { useState } from 'react'; + +interface Notification { + id: string, + notificationText: string, + data: string, + link: string, +} + +const noitficationList: Notification[] = [ + { + id: '1', + notificationText: 'text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1 text1', + data: '20.20.20', + link: '#' + }, + { + id: '2', + notificationText: 'text2', + data: '20.20.20', + link: '#' + }, + { + id: '3', + notificationText: 'text3', + data: '20.20.20', + link: '#' + }, + { + id: '4', + notificationText: 'text4', + data: '20.20.20', + link: '#' + }, + { + id: '5', + notificationText: 'text5', + data: '20.20.20', + link: '#' + } +]; + +export function NotificationsList() { + const t = useTranslations('Global'); + const [selectedFiles, setSelectedFiles] = useState>(new Set()); + + function selectHandler(fileId: string) { + setSelectedFiles((prev) => { + const newSet = new Set(prev) + if (newSet.has(fileId)) { + newSet.delete(fileId) + } else { + newSet.add(fileId) + } + return newSet; + }); + } + + function clearHandler() { + setSelectedFiles(new Set()); + } + + return ( +
+
+
+

+ {t('all-notifications')} +

+ +
+ {(selectedFiles.size !== 0) && ( + + )} +
+
+
+ {(noitficationList.length !== 0) && ( + noitficationList.map(item => { + return ( +
+
+ +
+ +
+ {item.notificationText} +
+
+ ) + }) + )} +
+
+
+ ) +} \ No newline at end of file diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 39573db..405994b 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -382,7 +382,11 @@ "no-information-about-the-complaint": "No information about the complaint", "the-complaint-has-not-been-registered": "The complaint has not been registered", "the-complaint-has-been-registered": "The complaint has been registered", - "all-statuses": "All statuses" + "all-statuses": "All statuses", + "all": "All", + "mark-all-as-read": "Mark all as read", + "all-notifications": "All notifications", + "deselect": "Deselect" }, "Login-register-form": { "and": "and", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index 2516715..cb7bb0d 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -382,7 +382,11 @@ "no-information-about-the-complaint": "Нет информации о жалобе", "the-complaint-has-not-been-registered": "Жалоба не зарегистрирована", "the-complaint-has-been-registered": "Жалоба зарегистрирована", - "all-statuses": "Все статусы" + "all-statuses": "Все статусы", + "all": "Все", + "mark-all-as-read": "Отметить все как прочитанное", + "all-notifications": "Все уведомления", + "deselect": "Снять выделение" }, "Login-register-form": { "and": "и",