@omniumretail/component-library 1.2.27 → 1.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.js +1 -1
- package/dist/main.css +1 -0
- package/dist/types/components/BellNotifications/BellNotifications.stories.d.ts +5 -0
- package/dist/types/components/BellNotifications/index.d.ts +23 -0
- package/dist/types/components/Header/Header.types.d.ts +5 -0
- package/dist/types/components/Header/index.d.ts +1 -1
- package/dist/types/components/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/BellNotifications/BellNotifications.stories.tsx +116 -0
- package/src/components/BellNotifications/index.tsx +161 -0
- package/src/components/BellNotifications/styles.module.scss +186 -0
- package/src/components/Header/Header.types.ts +6 -0
- package/src/components/Header/index.tsx +28 -9
- package/src/components/ResponsiveTable/index.tsx +1 -2
- package/src/components/index.tsx +1 -0
- package/src/locales/en.json +5 -0
- package/src/locales/es.json +5 -0
- package/src/locales/pt.json +5 -0
package/dist/main.css
CHANGED
|
@@ -31,3 +31,4 @@
|
|
|
31
31
|
.aZEBTQus3Y3MyodeDwkf{display:flex;flex-direction:column;align-items:center;justify-content:center}.cc1pbQlAgw2CmXVml3bs{height:300px;width:400px}.aTf_CXAsyp0tySsoZdD_{display:flex;justify-content:space-between;margin:10px 10px 0px 10px;font-size:20px}.kuu1hf_JTvYD55IW6Nxl{color:#ff4d4f}.N8ODEO3zDbY7gmecjTrp{color:#52c41a}.WIsL9Shu6c0mGJq9qSSz{margin-top:20px;border-radius:4px;overflow:hidden}.WIsL9Shu6c0mGJq9qSSz img{width:100%;height:auto}
|
|
32
32
|
.OJKeuXyOSPIeCCssVum2{display:flex;gap:32px}.jzxszvL7wzCV5nztJL0S{padding:12px}.OGA8oJw8RDyohcRftjmo{width:100%}.y2_7QVPOhjRZlAHCRWKf{display:grid;grid-template-columns:minmax(200px, 1fr) auto auto auto auto;gap:46px}.Smkcs3LGp1uPuQge2GxE{margin-top:16px}Label{margin-bottom:8px}.djarlYFkE9Kb7wUjUnrY{height:36px}
|
|
33
33
|
:root{--color-grey: #e4e4e4;--color-grey-100: #E5E5E5}.SF5f_Q_Ggz_4JHUHS2Xa{display:flex;align-items:center;padding:13px 20px;box-sizing:border-box;height:72px}.SF5f_Q_Ggz_4JHUHS2Xa *{box-sizing:border-box}.gSVYvBn1sLbcMsGI32b9{display:flex;flex-grow:1;align-items:flex-end;justify-content:center;max-width:calc(100% - 60px)}.gSVYvBn1sLbcMsGI32b9 img{max-width:196px}.gSVYvBn1sLbcMsGI32b9 .fVSt3bSZBcJGsOdA5nIN{font-weight:var(--font-weight-semibold);text-transform:uppercase;color:var(--color-black)}.BIBQmS_NwoMw2Ncq5yuN{cursor:pointer;padding:4px 4px 4px 0;font-size:20px;width:30px;display:flex;align-items:center;justify-content:center}.ZinSgUtysxITkbGRKdux{position:fixed;top:0;left:-100%;bottom:0;width:100%;max-width:430px;background-color:var(--color-white);padding-inline:15px;z-index:999;box-shadow:0 3px 12px rgba(0,0,0,.45);transition:.6s ease-out}.ZinSgUtysxITkbGRKdux.FW1pOdad0uN5LO9h5PgQ{left:0;transition:.3s ease-in}.ZfA8OEmbRzRsNqLAmdtn{background-color:rgba(0,0,0,.3);backdrop-filter:blur(1px);position:absolute;left:0;top:0;right:0;bottom:0;z-index:998;pointer-events:none;opacity:0;transition:.3s}.ZfA8OEmbRzRsNqLAmdtn.FW1pOdad0uN5LO9h5PgQ{opacity:1;pointer-events:all}.YKSQJmdRP_7tKwvQpeuV{width:calc(100% + 30px);margin-left:-15px;background-color:var(--color-orange);display:flex;align-items:center;justify-content:center}.ISssYC5Uz452JVWP0xDc img{max-width:160px}.E6YZPpa60LxA7qqIW7jQ{position:absolute;top:23px;right:15px;font-size:25px;cursor:pointer;color:var(--color-white)}.YYp3pzktjEDEyW8knAZQ{overflow-y:auto;height:calc(100% - 51px);margin:0;padding:0}.u7k2VQ30AQDk6mKA22GX{height:60px;padding:10px;display:flex;align-items:center;border-bottom:1px solid var(--color-black);cursor:pointer;transition:.3s;text-transform:uppercase;width:100%}.u7k2VQ30AQDk6mKA22GX:hover{background-color:var(--color-grey);color:var(--color-black)}.QQoDB9XPcAK18l2DIym4{list-style:none}.QQoDB9XPcAK18l2DIym4.wrXCksst57UpQuCNgGkN{height:auto;display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;padding-bottom:0}.QQoDB9XPcAK18l2DIym4.wrXCksst57UpQuCNgGkN>.u7k2VQ30AQDk6mKA22GX{position:relative}.QQoDB9XPcAK18l2DIym4.wrXCksst57UpQuCNgGkN>.u7k2VQ30AQDk6mKA22GX .NytmT59TmaWHc4FLM2wa{position:absolute;width:60px;height:100%;background:var(--color-black);right:0;display:flex;align-items:center;justify-content:center}.QQoDB9XPcAK18l2DIym4.wrXCksst57UpQuCNgGkN>.u7k2VQ30AQDk6mKA22GX .NytmT59TmaWHc4FLM2wa:hover{background-color:var(--color-orange)}.QQoDB9XPcAK18l2DIym4.wrXCksst57UpQuCNgGkN>.u7k2VQ30AQDk6mKA22GX .zo7CFksIKx7Fahu4e4S_{color:var(--color-white)}.QQoDB9XPcAK18l2DIym4.wrXCksst57UpQuCNgGkN.yk4DnBgiUlzk23Yg_q6E{background:var(--color-grey)}.QQoDB9XPcAK18l2DIym4.wrXCksst57UpQuCNgGkN.yk4DnBgiUlzk23Yg_q6E .NytmT59TmaWHc4FLM2wa{background-color:var(--color-orange)}.QQoDB9XPcAK18l2DIym4.wrXCksst57UpQuCNgGkN.yk4DnBgiUlzk23Yg_q6E .uLgjMU9sbvfaa0qKEguP{height:auto;opacity:1}.uLgjMU9sbvfaa0qKEguP{padding:0;width:100%;margin:0;background-color:var(--color-orange);color:var(--color-white);height:0;opacity:0;overflow:hidden;transition:height .3s ease,opacity .3s ease}.uLgjMU9sbvfaa0qKEguP .u7k2VQ30AQDk6mKA22GX{min-height:60px;height:auto;padding-inline:40px}.efnjUnudkE0UTVQ6wrgH{cursor:pointer}.u7k2VQ30AQDk6mKA22GX .fjNRYZMP1O0pTvBDBB3L span{margin-left:8px;transform:translateY(-2px);display:inline-block;font-size:var(--font-size-body-3);text-transform:capitalize}
|
|
34
|
+
.msELhnfhfo6un3c4Spoc{max-width:300px;max-height:400px;overflow-y:auto;padding:0}@media(max-width: 768px){.msELhnfhfo6un3c4Spoc{max-width:100%;max-height:300px}}@media(min-width: 1024px){.msELhnfhfo6un3c4Spoc{max-width:600px;max-height:700px}}.GeNg2HVTmhQDmsLLTpWj{display:flex;flex-direction:column;align-items:unset !important;padding:10px;border-bottom:1px solid #f0f0f0;background-color:#fff;cursor:pointer}.GeNg2HVTmhQDmsLLTpWj:hover{background-color:#f9f9f9}@media(max-width: 768px){.GeNg2HVTmhQDmsLLTpWj{padding:8px}}.GBb48KXbGnQtAY8xqmUp{display:flex;justify-content:space-between;align-items:center;padding:10px;background-color:#f5f5f5;border-bottom:1px solid #e8e8e8}@media(max-width: 768px){.GBb48KXbGnQtAY8xqmUp{flex-direction:column;align-items:flex-start;gap:8px}}.LoCH7sYpmK3VgRASqTgz{font-size:20px;cursor:pointer;color:#595959}.Xjs10kZPSsD_BVFomnIe{color:#ff674c;margin-right:8px}.oibCayv_cOyhyWCKzHSr{display:flex;align-items:center;gap:10px}.oibCayv_cOyhyWCKzHSr span{font-size:14px;font-weight:500;color:#595959}.hhwYXSd0CR75D8ETnUML{display:flex;align-items:center;position:relative;padding-left:12px}@media(max-width: 768px){.hhwYXSd0CR75D8ETnUML{padding-left:8px}}.LkP1cqeAyAYI9rCurnE8{position:absolute;left:0;top:50%;transform:translateY(-50%);font-size:16px}@media(max-width: 768px){.LkP1cqeAyAYI9rCurnE8{font-size:12px}}.u_jT9kmq2kyThRlj8HT1{width:50px;height:50px;margin-left:10px;margin-right:10px}@media(max-width: 768px){.u_jT9kmq2kyThRlj8HT1{width:40px;height:40px;margin-left:8px;margin-right:8px}}.ieGdf5FTgLUPcfuZi7Xz{flex:1;overflow:hidden}.QgZ3oHW9IRTexL8xWVqP{font-weight:bold;font-size:14px}@media(max-width: 768px){.QgZ3oHW9IRTexL8xWVqP{font-size:12px}}.JwfbSOSIRAz5OO2hiSFc{font-size:12px;color:#666;margin-top:2px}@media(max-width: 768px){.JwfbSOSIRAz5OO2hiSFc{font-size:10px}}.XfXQBYqMfZc4p8s2Mhla{width:6px;height:6px;background-color:#1890ff;border-radius:50%;margin-left:10px}@media(max-width: 768px){.XfXQBYqMfZc4p8s2Mhla{width:4px;height:4px;margin-left:8px}}.RGXTlVxCxbZpD6__J5_g{font-size:12px}.rWa5cnbZQLDtxFxJU6qA{font-size:20px;cursor:pointer}@media(max-width: 768px){.rWa5cnbZQLDtxFxJU6qA{font-size:18px}}.Pus_rAoqX05D4Qz2pYxo{animation:Pus_rAoqX05D4Qz2pYxo 1s ease-in-out infinite}@keyframes Pus_rAoqX05D4Qz2pYxo{0%{transform:rotate(0deg)}25%{transform:rotate(10deg)}50%{transform:rotate(-10deg)}75%{transform:rotate(10deg)}100%{transform:rotate(0deg)}}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface Criticality {
|
|
2
|
+
Id: string;
|
|
3
|
+
Name: string;
|
|
4
|
+
ColorHex: string;
|
|
5
|
+
}
|
|
6
|
+
export interface BellNotification {
|
|
7
|
+
Id: string;
|
|
8
|
+
Title: string;
|
|
9
|
+
Description: string;
|
|
10
|
+
RedirectURL: string;
|
|
11
|
+
Icon: string;
|
|
12
|
+
ExpirationDate: number;
|
|
13
|
+
Criticality: Criticality;
|
|
14
|
+
IsRead: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface BellNotificationsProps {
|
|
17
|
+
notifications: BellNotification[];
|
|
18
|
+
onFilterChange: (filter: 'all' | 'unread') => void;
|
|
19
|
+
onNotificationClick: (id: string, url: string) => void;
|
|
20
|
+
handleMarkAllAsRead: () => void;
|
|
21
|
+
}
|
|
22
|
+
export declare const BellNotifications: ({ notifications, onFilterChange, onNotificationClick, handleMarkAllAsRead }: BellNotificationsProps) => import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export default BellNotifications;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BellNotification } from "components/BellNotifications";
|
|
1
2
|
export interface MenuItem {
|
|
2
3
|
name: string;
|
|
3
4
|
action: () => void;
|
|
@@ -18,4 +19,8 @@ export interface HeaderProps {
|
|
|
18
19
|
onLeavingPage?: (targetRoute: string) => void;
|
|
19
20
|
userName?: string;
|
|
20
21
|
pageTitle?: string;
|
|
22
|
+
onFilterChange?: (filter: 'all' | 'unread') => void;
|
|
23
|
+
onNotificationClick?: (id: string, url: string) => void;
|
|
24
|
+
handleMarkAllAsRead?: () => void;
|
|
25
|
+
notifications?: BellNotification[];
|
|
21
26
|
}
|
|
@@ -3,5 +3,5 @@ import { HeaderProps } from './Header.types';
|
|
|
3
3
|
* Header component to display navigation bar with dropdown menus and action button.
|
|
4
4
|
* @param {HeaderProps} props - Properties passed to the component.
|
|
5
5
|
*/
|
|
6
|
-
export declare const Header: ({ menuList, actionButton, logout, homeUrl, profileUrl, onLeavingPage, userName, pageTitle }: HeaderProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare const Header: ({ menuList, actionButton, logout, homeUrl, profileUrl, onLeavingPage, userName, pageTitle, onFilterChange, notifications, onNotificationClick, handleMarkAllAsRead }: HeaderProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
export default Header;
|
package/package.json
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Meta, Story } from "@storybook/react";
|
|
2
|
+
import { BellNotifications, BellNotificationsProps } from '.';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'BellNotifications',
|
|
6
|
+
component: BellNotifications,
|
|
7
|
+
} as Meta;
|
|
8
|
+
|
|
9
|
+
const Template: Story<BellNotificationsProps> = (args) => <BellNotifications {...args}></BellNotifications>;
|
|
10
|
+
|
|
11
|
+
const notifications = [
|
|
12
|
+
{
|
|
13
|
+
"Id": "A31EB2C3-4937-44F6-B533-18A039A4AA96",
|
|
14
|
+
"Title": "Creation of an Evaluation Cycle",
|
|
15
|
+
"Description": "A new evaluation cycle was created. You must answer it.",
|
|
16
|
+
"RedirectURL": "https://www.google.pt",
|
|
17
|
+
"Icon": "https://omniumfont.s3.eu-west-1.amazonaws.com/Assets/images/aacp-image.png",
|
|
18
|
+
"ExpirationDate": 1736899200,
|
|
19
|
+
"Criticality": {
|
|
20
|
+
"Id": "F133ED4C-6C72-4CDC-86A1-27A76E6BDE7F",
|
|
21
|
+
"Name": "High",
|
|
22
|
+
"ColorHex": "#E05151"
|
|
23
|
+
},
|
|
24
|
+
"IsRead": false
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"Id": "137C1FE7-296C-4523-AC59-F136D2FA91AB",
|
|
28
|
+
"Title": "Creation of an Evaluation Cycle",
|
|
29
|
+
"Description": "A new evaluation cycle was created. You must answer it.",
|
|
30
|
+
"RedirectURL": "https://www.google.pt",
|
|
31
|
+
"Icon": "https://omniumfont.s3.eu-west-1.amazonaws.com/Assets/images/aacp-image.png",
|
|
32
|
+
"ExpirationDate": 1736899200,
|
|
33
|
+
"Criticality": {
|
|
34
|
+
"Id": "F133ED4C-6C72-4CDC-86A1-27A76E6BDE7F",
|
|
35
|
+
"Name": "High",
|
|
36
|
+
"ColorHex": "#E05151"
|
|
37
|
+
},
|
|
38
|
+
"IsRead": false
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"Id": "662C390B-A38F-45AB-B97C-2D00F0A8ACAD",
|
|
42
|
+
"Title": "Creation of an Evaluation Cycle",
|
|
43
|
+
"Description": "A new evaluation cycle was created. You must answer it.",
|
|
44
|
+
"RedirectURL": "https://www.google.pt",
|
|
45
|
+
"Icon": "https://omniumfont.s3.eu-west-1.amazonaws.com/Assets/images/aacp-image.png",
|
|
46
|
+
"ExpirationDate": 1736899200,
|
|
47
|
+
"Criticality": {
|
|
48
|
+
"Id": "F133ED4C-6C72-4CDC-86A1-27A76E6BDE7F",
|
|
49
|
+
"Name": "High",
|
|
50
|
+
"ColorHex": "#E05151"
|
|
51
|
+
},
|
|
52
|
+
"IsRead": false
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"Id": "0419654F-66FE-41EA-BBA4-D8BB6E3D6B33",
|
|
56
|
+
"Title": "Creation of an Evaluation Cycle",
|
|
57
|
+
"Description": "A new evaluation cycle was created. You must answer it.",
|
|
58
|
+
"RedirectURL": "https://www.google.pt",
|
|
59
|
+
"Icon": "https://omniumfont.s3.eu-west-1.amazonaws.com/Assets/images/aacp-image.png",
|
|
60
|
+
"ExpirationDate": 1736899200,
|
|
61
|
+
"Criticality": {
|
|
62
|
+
"Id": "F133ED4C-6C72-4CDC-86A1-27A76E6BDE7F",
|
|
63
|
+
"Name": "High",
|
|
64
|
+
"ColorHex": "#E05151"
|
|
65
|
+
},
|
|
66
|
+
"IsRead": false
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"Id": "CC98EDA1-0461-4D08-982E-8424EF4AFB3B",
|
|
70
|
+
"Title": "Creation of an Evaluation Cycle",
|
|
71
|
+
"Description": "A new evaluation cycle was created. You must answer it.",
|
|
72
|
+
"RedirectURL": "https://www.google.pt",
|
|
73
|
+
"Icon": "https://omniumfont.s3.eu-west-1.amazonaws.com/Assets/images/aacp-image.png",
|
|
74
|
+
"ExpirationDate": 1736899200,
|
|
75
|
+
"Criticality": {
|
|
76
|
+
"Id": "F133ED4C-6C72-4CDC-86A1-27A76E6BDE7F",
|
|
77
|
+
"Name": "High",
|
|
78
|
+
"ColorHex": "#E05151"
|
|
79
|
+
},
|
|
80
|
+
"IsRead": false
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"Id": "3DB9310A-4BF8-4B90-8D5A-7583473664BB",
|
|
84
|
+
"Title": "Creation of an Evaluation Cycle",
|
|
85
|
+
"Description": "A new evaluation cycle was created. You must answer it.",
|
|
86
|
+
"RedirectURL": "https://www.google.pt",
|
|
87
|
+
"Icon": "https://omniumfont.s3.eu-west-1.amazonaws.com/Assets/images/aacp-image.png",
|
|
88
|
+
"ExpirationDate": 1736899200,
|
|
89
|
+
"Criticality": {
|
|
90
|
+
"Id": "F133ED4C-6C72-4CDC-86A1-27A76E6BDE7F",
|
|
91
|
+
"Name": "High",
|
|
92
|
+
"ColorHex": "#E05151"
|
|
93
|
+
},
|
|
94
|
+
"IsRead": true
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"Id": "D6FB93EE-3255-42B2-8EC1-27E7AC3F5249",
|
|
98
|
+
"Title": "Creation of a new user",
|
|
99
|
+
"Description": "A new user was created in the platform with the mecanographic number 8111.",
|
|
100
|
+
"RedirectURL": "https://www.google.pt",
|
|
101
|
+
"Icon": "https://omniumfont.s3.eu-west-1.amazonaws.com/Assets/images/aacp-image.png",
|
|
102
|
+
"ExpirationDate": 1736985600,
|
|
103
|
+
"Criticality": {
|
|
104
|
+
"Id": "F133ED4C-6C72-4CDC-86A1-27A76E6BDE7F",
|
|
105
|
+
"Name": "High",
|
|
106
|
+
"ColorHex": "#E05151"
|
|
107
|
+
},
|
|
108
|
+
"IsRead": true
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
export const Primary = Template.bind({});
|
|
113
|
+
Primary.args = {
|
|
114
|
+
notifications: notifications
|
|
115
|
+
};
|
|
116
|
+
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { Badge, Dropdown, Menu } from 'antd';
|
|
3
|
+
import { BellOutlined, CheckCircleOutlined, ExclamationCircleOutlined, MoreOutlined } from '@ant-design/icons';
|
|
4
|
+
import styles from './styles.module.scss';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Switch } from '../Switch';
|
|
7
|
+
|
|
8
|
+
export interface Criticality {
|
|
9
|
+
Id: string;
|
|
10
|
+
Name: string;
|
|
11
|
+
ColorHex: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface BellNotification {
|
|
15
|
+
Id: string;
|
|
16
|
+
Title: string;
|
|
17
|
+
Description: string;
|
|
18
|
+
RedirectURL: string;
|
|
19
|
+
Icon: string;
|
|
20
|
+
ExpirationDate: number;
|
|
21
|
+
Criticality: Criticality;
|
|
22
|
+
IsRead: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface BellNotificationsProps {
|
|
26
|
+
notifications: BellNotification[];
|
|
27
|
+
onFilterChange: (filter: 'all' | 'unread') => void;
|
|
28
|
+
onNotificationClick: (id: string, url: string) => void;
|
|
29
|
+
handleMarkAllAsRead: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const BellNotifications = ({ notifications, onFilterChange, onNotificationClick, handleMarkAllAsRead }: BellNotificationsProps) => {
|
|
33
|
+
const { t } = useTranslation();
|
|
34
|
+
const [maxChars, setMaxChars] = useState(30);
|
|
35
|
+
const [filter, setFilter] = useState<'all' | 'unread'>('all');
|
|
36
|
+
const [isShaking, setIsShaking] = useState(false);
|
|
37
|
+
|
|
38
|
+
const unreadCount = notifications.filter((notif) => !notif.IsRead).length;
|
|
39
|
+
|
|
40
|
+
const handleNotificationClick = (id: string, url: string) => {
|
|
41
|
+
onNotificationClick(id, url);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const handleFilterChange = (checked: boolean) => {
|
|
45
|
+
const newFilter = checked ? 'unread' : 'all';
|
|
46
|
+
setFilter(newFilter);
|
|
47
|
+
onFilterChange(newFilter);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
const handleResize = () => {
|
|
52
|
+
if (window.innerWidth < 1024) {
|
|
53
|
+
setMaxChars(30);
|
|
54
|
+
} else {
|
|
55
|
+
setMaxChars(60);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
handleResize();
|
|
60
|
+
window.addEventListener('resize', handleResize);
|
|
61
|
+
|
|
62
|
+
return () => {
|
|
63
|
+
window.removeEventListener('resize', handleResize);
|
|
64
|
+
};
|
|
65
|
+
}, []);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (unreadCount > 0) {
|
|
69
|
+
const interval = setInterval(() => {
|
|
70
|
+
setIsShaking(true);
|
|
71
|
+
|
|
72
|
+
// Parar a animação após 2 segundos
|
|
73
|
+
const stopShakeTimeout = setTimeout(() => setIsShaking(false), 2000);
|
|
74
|
+
|
|
75
|
+
return () => clearTimeout(stopShakeTimeout);
|
|
76
|
+
}, 15000); // Repetir a cada 30 segundos
|
|
77
|
+
|
|
78
|
+
return () => clearInterval(interval);
|
|
79
|
+
}
|
|
80
|
+
}, [unreadCount]);
|
|
81
|
+
|
|
82
|
+
const optionsMenu = (
|
|
83
|
+
<Menu>
|
|
84
|
+
<Menu.Item key="markAllAsRead" onClick={handleMarkAllAsRead}>
|
|
85
|
+
<CheckCircleOutlined className={styles.checkIconStyle} />
|
|
86
|
+
|
|
87
|
+
{t('components.bellNotifications.markAllAsRead')}
|
|
88
|
+
</Menu.Item>
|
|
89
|
+
</Menu>
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const menu = (
|
|
93
|
+
<Menu className={styles.notificationMenu}>
|
|
94
|
+
|
|
95
|
+
{unreadCount > 0 && (
|
|
96
|
+
<div className={styles.headerContainer}>
|
|
97
|
+
<div className={styles.switchContainer}>
|
|
98
|
+
<span>{t('components.bellNotifications.onlyShowNoRead')}</span>
|
|
99
|
+
|
|
100
|
+
<Switch
|
|
101
|
+
checked={filter === 'unread'}
|
|
102
|
+
onChange={handleFilterChange}
|
|
103
|
+
size="small"
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<Dropdown overlay={optionsMenu} trigger={['click']}>
|
|
108
|
+
<MoreOutlined className={styles.moreOutlinedIconStyle} />
|
|
109
|
+
</Dropdown>
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
|
|
113
|
+
{notifications.map((notif) => (
|
|
114
|
+
<Menu.Item
|
|
115
|
+
key={notif.Id}
|
|
116
|
+
className={styles.notificationItem}
|
|
117
|
+
onClick={() => handleNotificationClick(notif.Id, notif.RedirectURL)}
|
|
118
|
+
>
|
|
119
|
+
<div className={styles.notificationContent}>
|
|
120
|
+
{notif.Criticality?.Name === 'High' && (
|
|
121
|
+
<ExclamationCircleOutlined
|
|
122
|
+
className={styles.criticalityIcon}
|
|
123
|
+
style={{ color: notif.Criticality?.ColorHex }}
|
|
124
|
+
/>
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
<img src={notif.Icon} alt="Notification Icon" className={styles.notificationIcon} />
|
|
128
|
+
|
|
129
|
+
<div className={styles.notificationText}>
|
|
130
|
+
<div
|
|
131
|
+
className={styles.notificationTitle}
|
|
132
|
+
>
|
|
133
|
+
{notif.Title}
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div className={styles.notificationDescription}>
|
|
137
|
+
{notif.Description.length > maxChars
|
|
138
|
+
? `${notif.Description.slice(0, maxChars)}...`
|
|
139
|
+
: notif.Description}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
{!notif.IsRead && <div className={styles.unreadIndicator}></div>}
|
|
144
|
+
</div>
|
|
145
|
+
</Menu.Item>
|
|
146
|
+
))}
|
|
147
|
+
|
|
148
|
+
{notifications.length === 0 && <Menu.Item disabled>{t('components.bellNotifications.noNotifications')}</Menu.Item>}
|
|
149
|
+
</Menu>
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<Dropdown overlay={menu} trigger={['click']}>
|
|
154
|
+
<Badge count={unreadCount} className={styles.badgeStyle} overflowCount={99} offset={[8, 0]}>
|
|
155
|
+
<BellOutlined className={`${styles.bellIcon} ${isShaking ? styles.shake : ''}`} />
|
|
156
|
+
</Badge>
|
|
157
|
+
</Dropdown>
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export default BellNotifications;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
.notificationMenu {
|
|
2
|
+
max-width: 300px;
|
|
3
|
+
max-height: 400px;
|
|
4
|
+
overflow-y: auto;
|
|
5
|
+
padding: 0;
|
|
6
|
+
|
|
7
|
+
@media (max-width: 768px) {
|
|
8
|
+
max-width: 100%;
|
|
9
|
+
max-height: 300px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@media (min-width: 1024px) {
|
|
13
|
+
max-width: 600px;
|
|
14
|
+
max-height: 700px;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.notificationItem {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
align-items: unset !important;
|
|
22
|
+
padding: 10px;
|
|
23
|
+
border-bottom: 1px solid #f0f0f0;
|
|
24
|
+
background-color: #fff;
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
|
|
27
|
+
&:hover {
|
|
28
|
+
background-color: #f9f9f9;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@media (max-width: 768px) {
|
|
32
|
+
padding: 8px;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.headerContainer {
|
|
37
|
+
display: flex;
|
|
38
|
+
justify-content: space-between;
|
|
39
|
+
align-items: center;
|
|
40
|
+
padding: 10px;
|
|
41
|
+
background-color: #f5f5f5;
|
|
42
|
+
border-bottom: 1px solid #e8e8e8;
|
|
43
|
+
|
|
44
|
+
@media (max-width: 768px) {
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
align-items: flex-start;
|
|
47
|
+
gap: 8px;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.moreOutlinedIconStyle {
|
|
52
|
+
font-size: 20px;
|
|
53
|
+
cursor: pointer;
|
|
54
|
+
color: #595959;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.checkIconStyle {
|
|
58
|
+
color: #FF674C;
|
|
59
|
+
margin-right: 8px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.switchContainer {
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
gap: 10px;
|
|
66
|
+
|
|
67
|
+
span {
|
|
68
|
+
font-size: 14px;
|
|
69
|
+
font-weight: 500;
|
|
70
|
+
color: #595959;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.notificationContent {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
position: relative;
|
|
78
|
+
padding-left: 12px;
|
|
79
|
+
|
|
80
|
+
@media (max-width: 768px) {
|
|
81
|
+
padding-left: 8px;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.criticalityIcon {
|
|
86
|
+
position: absolute;
|
|
87
|
+
left: 0;
|
|
88
|
+
top: 50%;
|
|
89
|
+
transform: translateY(-50%);
|
|
90
|
+
font-size: 16px;
|
|
91
|
+
|
|
92
|
+
@media (max-width: 768px) {
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.notificationIcon {
|
|
98
|
+
width: 50px;
|
|
99
|
+
height: 50px;
|
|
100
|
+
margin-left: 10px;
|
|
101
|
+
margin-right: 10px;
|
|
102
|
+
|
|
103
|
+
@media (max-width: 768px) {
|
|
104
|
+
width: 40px;
|
|
105
|
+
height: 40px;
|
|
106
|
+
margin-left: 8px;
|
|
107
|
+
margin-right: 8px;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.notificationText {
|
|
112
|
+
flex: 1;
|
|
113
|
+
overflow: hidden;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.notificationTitle {
|
|
117
|
+
font-weight: bold;
|
|
118
|
+
font-size: 14px;
|
|
119
|
+
|
|
120
|
+
@media (max-width: 768px) {
|
|
121
|
+
font-size: 12px;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.notificationDescription {
|
|
126
|
+
font-size: 12px;
|
|
127
|
+
color: #666;
|
|
128
|
+
margin-top: 2px;
|
|
129
|
+
|
|
130
|
+
@media (max-width: 768px) {
|
|
131
|
+
font-size: 10px;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.unreadIndicator {
|
|
136
|
+
width: 6px;
|
|
137
|
+
height: 6px;
|
|
138
|
+
background-color: #1890ff;
|
|
139
|
+
border-radius: 50%;
|
|
140
|
+
margin-left: 10px;
|
|
141
|
+
|
|
142
|
+
@media (max-width: 768px) {
|
|
143
|
+
width: 4px;
|
|
144
|
+
height: 4px;
|
|
145
|
+
margin-left: 8px;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.badgeStyle {
|
|
150
|
+
font-size: 12px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.bellIcon {
|
|
154
|
+
font-size: 20px;
|
|
155
|
+
cursor: pointer;
|
|
156
|
+
|
|
157
|
+
@media (max-width: 768px) {
|
|
158
|
+
font-size: 18px;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.shake {
|
|
163
|
+
animation: shake 1s ease-in-out infinite;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@keyframes shake {
|
|
167
|
+
0% {
|
|
168
|
+
transform: rotate(0deg);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
25% {
|
|
172
|
+
transform: rotate(10deg);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
50% {
|
|
176
|
+
transform: rotate(-10deg);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
75% {
|
|
180
|
+
transform: rotate(10deg);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
100% {
|
|
184
|
+
transform: rotate(0deg);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BellNotification } from "components/BellNotifications";
|
|
2
|
+
|
|
1
3
|
export interface MenuItem {
|
|
2
4
|
name: string;
|
|
3
5
|
action: () => void;
|
|
@@ -20,4 +22,8 @@ export interface HeaderProps {
|
|
|
20
22
|
onLeavingPage?: (targetRoute: string) => void;
|
|
21
23
|
userName?: string;
|
|
22
24
|
pageTitle?: string;
|
|
25
|
+
onFilterChange?: (filter: 'all' | 'unread') => void;
|
|
26
|
+
onNotificationClick?: (id: string, url: string) => void;
|
|
27
|
+
handleMarkAllAsRead?: () => void;
|
|
28
|
+
notifications?: BellNotification[];
|
|
23
29
|
}
|
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
import { useRef, useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { DownOutlined, UpOutlined, BarsOutlined, CloseOutlined } from '@ant-design/icons';
|
|
3
3
|
import omniumIcon from '../../assets/images/omnium-retail-logo.png';
|
|
4
4
|
import omniumWhiteIcon from '../../assets/images/omnium-retail-logo-white.png';
|
|
5
5
|
import styles from './styles.module.scss';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
7
|
import { HeaderProps, MenuItem } from './Header.types';
|
|
8
8
|
import { getMenuTopList, getMenuBottomList } from './Header.data';
|
|
9
|
+
import { BellNotifications } from '../BellNotifications';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Header component to display navigation bar with dropdown menus and action button.
|
|
12
13
|
* @param {HeaderProps} props - Properties passed to the component.
|
|
13
14
|
*/
|
|
14
|
-
export const Header = ({
|
|
15
|
+
export const Header = ({
|
|
16
|
+
menuList,
|
|
17
|
+
actionButton,
|
|
18
|
+
logout,
|
|
19
|
+
homeUrl,
|
|
20
|
+
profileUrl,
|
|
21
|
+
onLeavingPage,
|
|
22
|
+
userName,
|
|
23
|
+
pageTitle,
|
|
24
|
+
onFilterChange,
|
|
25
|
+
notifications,
|
|
26
|
+
onNotificationClick,
|
|
27
|
+
handleMarkAllAsRead
|
|
28
|
+
}: HeaderProps) => {
|
|
29
|
+
|
|
15
30
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
16
31
|
const [activeDropdown, setActiveDropdown] = useState<number | null>(null);
|
|
17
32
|
const headerRef = useRef<HTMLDivElement>(null);
|
|
@@ -21,7 +36,7 @@ export const Header = ({ menuList, actionButton, logout, homeUrl, profileUrl, on
|
|
|
21
36
|
* Handle the logout action. If no logout function is provided, redirect to home as discussed in a meet
|
|
22
37
|
*/
|
|
23
38
|
const onLogout = () => {
|
|
24
|
-
if(
|
|
39
|
+
if (logout) {
|
|
25
40
|
logout();
|
|
26
41
|
} else {
|
|
27
42
|
onHome();
|
|
@@ -96,9 +111,9 @@ export const Header = ({ menuList, actionButton, logout, homeUrl, profileUrl, on
|
|
|
96
111
|
}
|
|
97
112
|
}}
|
|
98
113
|
>
|
|
99
|
-
<p
|
|
100
|
-
|
|
101
|
-
|
|
114
|
+
<p
|
|
115
|
+
className={styles.name}
|
|
116
|
+
dangerouslySetInnerHTML={{ __html: link.name }}></p>
|
|
102
117
|
|
|
103
118
|
{
|
|
104
119
|
link.dropdownMenu && link.dropdownMenu.length > 0 && <div className={styles.arrow} onClick={(event) => handleDropdownClick(event, index)}>
|
|
@@ -133,16 +148,20 @@ export const Header = ({ menuList, actionButton, logout, homeUrl, profileUrl, on
|
|
|
133
148
|
|
|
134
149
|
return (
|
|
135
150
|
<div className={styles.header} ref={headerRef}>
|
|
136
|
-
<BarsOutlined onClick={onToggleMenu} className={
|
|
151
|
+
<BarsOutlined onClick={onToggleMenu} className={styles.toggleMenuOpen} />
|
|
137
152
|
|
|
138
153
|
<div className={styles.logoContainer}>
|
|
139
154
|
{
|
|
140
|
-
pageTitle
|
|
141
|
-
? <p className={
|
|
155
|
+
pageTitle
|
|
156
|
+
? <p className={styles.title}>{pageTitle}</p>
|
|
142
157
|
: <img src={omniumIcon} alt="Omnium Retail Blue Logo" />
|
|
143
158
|
}
|
|
144
159
|
</div>
|
|
145
160
|
|
|
161
|
+
{notifications &&
|
|
162
|
+
<BellNotifications onFilterChange={onFilterChange!} notifications={notifications} onNotificationClick={onNotificationClick!} handleMarkAllAsRead={handleMarkAllAsRead!} />
|
|
163
|
+
}
|
|
164
|
+
|
|
146
165
|
<div className={classNames(styles.menu, { [styles.active]: isMenuOpen })}>
|
|
147
166
|
<div className={styles.head}>
|
|
148
167
|
<p className={styles.logoWrapper}>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
|
-
import { Space, TableProps, Select, Dropdown
|
|
2
|
+
import { Space, TableProps, Select, Dropdown } from 'antd';
|
|
3
3
|
import { Table as AntdTable } from 'antd';
|
|
4
4
|
import type { ColumnsType } from 'antd/es/table/interface';
|
|
5
5
|
import styles from './styles.module.scss';
|
|
@@ -7,7 +7,6 @@ import { useTranslation } from 'react-i18next';
|
|
|
7
7
|
import { MoreOutlined } from '@ant-design/icons';
|
|
8
8
|
import classnames from 'classnames';
|
|
9
9
|
import { Button } from '../Button';
|
|
10
|
-
import classNames from 'classnames';
|
|
11
10
|
|
|
12
11
|
export interface FilterTableOptions {
|
|
13
12
|
value: string;
|
package/src/components/index.tsx
CHANGED
package/src/locales/en.json
CHANGED