@omniumretail/component-library 1.2.27 → 1.2.28
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 +22 -0
- package/dist/types/components/Header/Header.types.d.ts +4 -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 +141 -0
- package/src/components/BellNotifications/styles.module.scss +163 -0
- package/src/components/Header/Header.types.ts +5 -0
- package/src/components/Header/index.tsx +14 -9
- package/src/components/ResponsiveTable/index.tsx +1 -2
- package/src/components/index.tsx +1 -0
- package/src/locales/en.json +4 -0
- package/src/locales/es.json +4 -0
- package/src/locales/pt.json +4 -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}}.oibCayv_cOyhyWCKzHSr{display:flex;justify-content:space-between;align-items:center;padding:10px;background-color:#f5f5f5;border-bottom:1px solid #e8e8e8}.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,22 @@
|
|
|
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
|
+
}
|
|
21
|
+
export declare const BellNotifications: ({ notifications, onFilterChange, onNotificationClick }: BellNotificationsProps) => import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
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,7 @@ 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
|
+
notifications?: BellNotification[];
|
|
21
25
|
}
|
|
@@ -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 }: 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,141 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { Badge, Dropdown, Menu } from 'antd';
|
|
3
|
+
import { BellOutlined, ExclamationCircleOutlined } 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
|
+
}
|
|
30
|
+
|
|
31
|
+
export const BellNotifications = ({ notifications, onFilterChange, onNotificationClick }: BellNotificationsProps) => {
|
|
32
|
+
const { t } = useTranslation();
|
|
33
|
+
const [maxChars, setMaxChars] = useState(30);
|
|
34
|
+
const [filter, setFilter] = useState<'all' | 'unread'>('all');
|
|
35
|
+
const [isShaking, setIsShaking] = useState(false);
|
|
36
|
+
|
|
37
|
+
const unreadCount = notifications.filter((notif) => !notif.IsRead).length;
|
|
38
|
+
|
|
39
|
+
const handleNotificationClick = (id: string, url: string) => {
|
|
40
|
+
onNotificationClick(id, url);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const handleFilterChange = (checked: boolean) => {
|
|
44
|
+
const newFilter = checked ? 'unread' : 'all';
|
|
45
|
+
setFilter(newFilter);
|
|
46
|
+
onFilterChange(newFilter);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const handleResize = () => {
|
|
51
|
+
if (window.innerWidth < 1024) {
|
|
52
|
+
setMaxChars(30);
|
|
53
|
+
} else {
|
|
54
|
+
setMaxChars(60);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
handleResize();
|
|
59
|
+
window.addEventListener('resize', handleResize);
|
|
60
|
+
|
|
61
|
+
return () => {
|
|
62
|
+
window.removeEventListener('resize', handleResize);
|
|
63
|
+
};
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (unreadCount > 0) {
|
|
68
|
+
const interval = setInterval(() => {
|
|
69
|
+
setIsShaking(true);
|
|
70
|
+
|
|
71
|
+
// Parar a animação após 2 segundos
|
|
72
|
+
const stopShakeTimeout = setTimeout(() => setIsShaking(false), 2000);
|
|
73
|
+
|
|
74
|
+
return () => clearTimeout(stopShakeTimeout);
|
|
75
|
+
}, 15000); // Repetir a cada 30 segundos
|
|
76
|
+
|
|
77
|
+
return () => clearInterval(interval);
|
|
78
|
+
}
|
|
79
|
+
}, [unreadCount]);
|
|
80
|
+
|
|
81
|
+
const menu = (
|
|
82
|
+
<Menu className={styles.notificationMenu}>
|
|
83
|
+
<div className={styles.switchContainer}>
|
|
84
|
+
<span>{t('components.bellNotifications.onlyShowNoRead')}</span>
|
|
85
|
+
|
|
86
|
+
<Switch
|
|
87
|
+
checked={filter === 'unread'}
|
|
88
|
+
onChange={handleFilterChange}
|
|
89
|
+
size="small"
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
{notifications.map((notif) => (
|
|
94
|
+
<Menu.Item
|
|
95
|
+
key={notif.Id}
|
|
96
|
+
className={styles.notificationItem}
|
|
97
|
+
onClick={() => handleNotificationClick(notif.Id, notif.RedirectURL)}
|
|
98
|
+
>
|
|
99
|
+
<div className={styles.notificationContent}>
|
|
100
|
+
{notif.Criticality?.Name === 'High' && (
|
|
101
|
+
<ExclamationCircleOutlined
|
|
102
|
+
className={styles.criticalityIcon}
|
|
103
|
+
style={{ color: notif.Criticality?.ColorHex }}
|
|
104
|
+
/>
|
|
105
|
+
)}
|
|
106
|
+
|
|
107
|
+
<img src={notif.Icon} alt="Notification Icon" className={styles.notificationIcon} />
|
|
108
|
+
|
|
109
|
+
<div className={styles.notificationText}>
|
|
110
|
+
<div
|
|
111
|
+
className={styles.notificationTitle}
|
|
112
|
+
>
|
|
113
|
+
{notif.Title}
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div className={styles.notificationDescription}>
|
|
117
|
+
{notif.Description.length > maxChars
|
|
118
|
+
? `${notif.Description.slice(0, maxChars)}...`
|
|
119
|
+
: notif.Description}
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
{!notif.IsRead && <div className={styles.unreadIndicator}></div>}
|
|
124
|
+
</div>
|
|
125
|
+
</Menu.Item>
|
|
126
|
+
))}
|
|
127
|
+
|
|
128
|
+
{notifications.length === 0 && <Menu.Item disabled>{t('components.bellNotifications.noNotifications')}</Menu.Item>}
|
|
129
|
+
</Menu>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<Dropdown overlay={menu} trigger={['click']}>
|
|
134
|
+
<Badge count={unreadCount} className={styles.badgeStyle} overflowCount={99} offset={[8, 0]}>
|
|
135
|
+
<BellOutlined className={`${styles.bellIcon} ${isShaking ? styles.shake : ''}`} />
|
|
136
|
+
</Badge>
|
|
137
|
+
</Dropdown>
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export default BellNotifications;
|
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
.switchContainer {
|
|
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
|
+
span {
|
|
45
|
+
font-size: 14px;
|
|
46
|
+
font-weight: 500;
|
|
47
|
+
color: #595959;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.notificationContent {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
position: relative;
|
|
55
|
+
padding-left: 12px;
|
|
56
|
+
|
|
57
|
+
@media (max-width: 768px) {
|
|
58
|
+
padding-left: 8px;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.criticalityIcon {
|
|
63
|
+
position: absolute;
|
|
64
|
+
left: 0;
|
|
65
|
+
top: 50%;
|
|
66
|
+
transform: translateY(-50%);
|
|
67
|
+
font-size: 16px;
|
|
68
|
+
|
|
69
|
+
@media (max-width: 768px) {
|
|
70
|
+
font-size: 12px;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.notificationIcon {
|
|
75
|
+
width: 50px;
|
|
76
|
+
height: 50px;
|
|
77
|
+
margin-left: 10px;
|
|
78
|
+
margin-right: 10px;
|
|
79
|
+
|
|
80
|
+
@media (max-width: 768px) {
|
|
81
|
+
width: 40px;
|
|
82
|
+
height: 40px;
|
|
83
|
+
margin-left: 8px;
|
|
84
|
+
margin-right: 8px;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.notificationText {
|
|
89
|
+
flex: 1;
|
|
90
|
+
overflow: hidden;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.notificationTitle {
|
|
94
|
+
font-weight: bold;
|
|
95
|
+
font-size: 14px;
|
|
96
|
+
|
|
97
|
+
@media (max-width: 768px) {
|
|
98
|
+
font-size: 12px;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.notificationDescription {
|
|
103
|
+
font-size: 12px;
|
|
104
|
+
color: #666;
|
|
105
|
+
margin-top: 2px;
|
|
106
|
+
|
|
107
|
+
@media (max-width: 768px) {
|
|
108
|
+
font-size: 10px;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.unreadIndicator {
|
|
113
|
+
width: 6px;
|
|
114
|
+
height: 6px;
|
|
115
|
+
background-color: #1890ff;
|
|
116
|
+
border-radius: 50%;
|
|
117
|
+
margin-left: 10px;
|
|
118
|
+
|
|
119
|
+
@media (max-width: 768px) {
|
|
120
|
+
width: 4px;
|
|
121
|
+
height: 4px;
|
|
122
|
+
margin-left: 8px;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.badgeStyle {
|
|
127
|
+
font-size: 12px;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.bellIcon {
|
|
131
|
+
font-size: 20px;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
|
|
134
|
+
@media (max-width: 768px) {
|
|
135
|
+
font-size: 18px;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.shake {
|
|
140
|
+
animation: shake 1s ease-in-out infinite;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@keyframes shake {
|
|
144
|
+
0% {
|
|
145
|
+
transform: rotate(0deg);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
25% {
|
|
149
|
+
transform: rotate(10deg);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
50% {
|
|
153
|
+
transform: rotate(-10deg);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
75% {
|
|
157
|
+
transform: rotate(10deg);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
100% {
|
|
161
|
+
transform: rotate(0deg);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -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,7 @@ 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
|
+
notifications?: BellNotification[];
|
|
23
28
|
}
|
|
@@ -1,17 +1,18 @@
|
|
|
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 = ({ menuList, actionButton, logout, homeUrl, profileUrl, onLeavingPage, userName, pageTitle }: HeaderProps) => {
|
|
15
|
+
export const Header = ({ menuList, actionButton, logout, homeUrl, profileUrl, onLeavingPage, userName, pageTitle, onFilterChange, notifications, onNotificationClick }: HeaderProps) => {
|
|
15
16
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
16
17
|
const [activeDropdown, setActiveDropdown] = useState<number | null>(null);
|
|
17
18
|
const headerRef = useRef<HTMLDivElement>(null);
|
|
@@ -21,7 +22,7 @@ export const Header = ({ menuList, actionButton, logout, homeUrl, profileUrl, on
|
|
|
21
22
|
* Handle the logout action. If no logout function is provided, redirect to home as discussed in a meet
|
|
22
23
|
*/
|
|
23
24
|
const onLogout = () => {
|
|
24
|
-
if(
|
|
25
|
+
if (logout) {
|
|
25
26
|
logout();
|
|
26
27
|
} else {
|
|
27
28
|
onHome();
|
|
@@ -96,9 +97,9 @@ export const Header = ({ menuList, actionButton, logout, homeUrl, profileUrl, on
|
|
|
96
97
|
}
|
|
97
98
|
}}
|
|
98
99
|
>
|
|
99
|
-
<p
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
<p
|
|
101
|
+
className={styles.name}
|
|
102
|
+
dangerouslySetInnerHTML={{ __html: link.name }}></p>
|
|
102
103
|
|
|
103
104
|
{
|
|
104
105
|
link.dropdownMenu && link.dropdownMenu.length > 0 && <div className={styles.arrow} onClick={(event) => handleDropdownClick(event, index)}>
|
|
@@ -133,16 +134,20 @@ export const Header = ({ menuList, actionButton, logout, homeUrl, profileUrl, on
|
|
|
133
134
|
|
|
134
135
|
return (
|
|
135
136
|
<div className={styles.header} ref={headerRef}>
|
|
136
|
-
<BarsOutlined onClick={onToggleMenu} className={
|
|
137
|
+
<BarsOutlined onClick={onToggleMenu} className={styles.toggleMenuOpen} />
|
|
137
138
|
|
|
138
139
|
<div className={styles.logoContainer}>
|
|
139
140
|
{
|
|
140
|
-
pageTitle
|
|
141
|
-
? <p className={
|
|
141
|
+
pageTitle
|
|
142
|
+
? <p className={styles.title}>{pageTitle}</p>
|
|
142
143
|
: <img src={omniumIcon} alt="Omnium Retail Blue Logo" />
|
|
143
144
|
}
|
|
144
145
|
</div>
|
|
145
146
|
|
|
147
|
+
{notifications &&
|
|
148
|
+
<BellNotifications onFilterChange={onFilterChange!} notifications={notifications} onNotificationClick={onNotificationClick!} />
|
|
149
|
+
}
|
|
150
|
+
|
|
146
151
|
<div className={classNames(styles.menu, { [styles.active]: isMenuOpen })}>
|
|
147
152
|
<div className={styles.head}>
|
|
148
153
|
<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
package/src/locales/es.json
CHANGED