- added sidebar container for notifications;
- styles and interactions for notifications;
This commit is contained in:
55
src/assets/scss/components/notificationsSidebar.scss
Normal file
55
src/assets/scss/components/notificationsSidebar.scss
Normal file
@@ -0,0 +1,55 @@
|
||||
.notificationsIcon {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.notificationsSidebar {
|
||||
max-width: 360px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.notificationsSidebar__loading {
|
||||
padding: 30px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.notificationsSidebar__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.notificationsSidebar__listItem {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #e7e7e7;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: var(--primary-text);
|
||||
}
|
||||
}
|
||||
|
||||
.notificationsSidebar__listItemContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.notificationsSidebar__listItemChosen {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 5px;
|
||||
}
|
||||
@@ -45,3 +45,4 @@
|
||||
@import "./components/myTable.scss";
|
||||
@import "./components/evaluation.scss";
|
||||
@import "./components/fieldsRepeater.scss";
|
||||
@import "./components/notificationsSidebar.scss";
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
|
||||
const NotificationItem = ({ item, clickFn }) => {
|
||||
const handleClick = () => {
|
||||
clickFn(item.id);
|
||||
}
|
||||
|
||||
return (
|
||||
<li className="notificationsSidebar__listItem" onClick={handleClick}>
|
||||
<div className="notificationsSidebar__listItemContent">
|
||||
<strong>{item.title}</strong>
|
||||
<span>{item.createdDate}</span>
|
||||
</div>
|
||||
<i className="pi pi-angle-right"></i>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotificationItem;
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Button } from 'primereact/button';
|
||||
|
||||
const NotificationItemChosen = ({ item, closeFn }) => {
|
||||
return (
|
||||
<div className="notificationsSidebar__listItemChosen">
|
||||
<Button
|
||||
style={{marginBottom: '20px'}}
|
||||
type="button"
|
||||
outlined
|
||||
onClick={closeFn}
|
||||
label={__('Indietro', 'gepafin')}
|
||||
icon="pi pi-arrow-left" iconPos="left"/>
|
||||
<strong>{item.title}</strong>
|
||||
<span>{item.createdDate}</span>
|
||||
{item.message}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotificationItemChosen;
|
||||
121
src/components/NotificationsSidebar/index.js
Normal file
121
src/components/NotificationsSidebar/index.js
Normal file
@@ -0,0 +1,121 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { head, isEmpty } from 'ramda';
|
||||
|
||||
// components
|
||||
import { Badge } from 'primereact/badge';
|
||||
import { Sidebar } from 'primereact/sidebar';
|
||||
import { TabPanel, TabView } from 'primereact/tabview';
|
||||
import NotificationItem from './components/NotificationItem';
|
||||
import NotificationItemChosen from './components/NotificationItemChosen';
|
||||
|
||||
const NotificationsSidebar = () => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [notificationsVisible, setNotificationsVisible] = useState(false);
|
||||
const [notifications, setNotifications] = useState([]);
|
||||
const [notificationsRead, setNotificationsRead] = useState([]);
|
||||
const [chosenMsg, setChosenMsg] = useState({});
|
||||
|
||||
// Handle tab change
|
||||
const handleTabChange = (e) => {
|
||||
setActiveIndex(e.index);
|
||||
fetchTabData(e.index);
|
||||
};
|
||||
|
||||
const fetchTabData = (index) => {
|
||||
setChosenMsg({});
|
||||
console.log('fetchTabData', index);
|
||||
setLoading(true);
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 7000)
|
||||
}
|
||||
|
||||
const chooseNotification = (id) => {
|
||||
const properItems = activeIndex === 0 ? notifications : notificationsRead;
|
||||
const chosen = head(properItems.filter(o => o.id === id));
|
||||
if (chosen) {
|
||||
setChosenMsg(chosen);
|
||||
}
|
||||
}
|
||||
|
||||
const closeChosenMsg = () => {
|
||||
setChosenMsg({});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setNotifications(() => {
|
||||
const msg = {
|
||||
'id': 35,
|
||||
'createdDate': '2024-12-23T14:55:27.278103',
|
||||
'updatedDate': '2024-12-23T14:55:27.278103',
|
||||
'userId': 30,
|
||||
'title': 'Il Risultato della Valutazione per la Richiesta È Disponibile',
|
||||
'message': 'Il risultato della valutazione per la richiesta ai sensi del protocollo n. 10000015 è ora disponibile.',
|
||||
'status': 'UNREAD',
|
||||
'companyId': 103,
|
||||
'redirectUrl': 'EVALUATION_RESULT',
|
||||
'notificationType': 'EVALUATION_RESULT'
|
||||
};
|
||||
return Array.from({ length: 33 }, (_, index) => ({
|
||||
...msg,
|
||||
id: msg.id + index
|
||||
}));
|
||||
})
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<i className="pi pi-bell p-overlay-badge topBar__icon notificationsIcon"
|
||||
onClick={() => setNotificationsVisible(true)}>
|
||||
<Badge value={notifications.length}></Badge>
|
||||
</i>
|
||||
<Sidebar
|
||||
className="notificationsSidebar"
|
||||
position="left"
|
||||
visible={notificationsVisible}
|
||||
onHide={() => setNotificationsVisible(false)}>
|
||||
<TabView activeIndex={activeIndex} onTabChange={handleTabChange}>
|
||||
<TabPanel header={__('Da leggere', 'gepafin')}>
|
||||
{loading
|
||||
? <div className="notificationsSidebar__loading">
|
||||
<i className="pi pi-spin pi-spinner" style={{ fontSize: '2rem' }}></i>
|
||||
</div>
|
||||
: !isEmpty(chosenMsg)
|
||||
? <NotificationItemChosen item={chosenMsg} closeFn={closeChosenMsg}/>
|
||||
: (notifications.length > 0
|
||||
? <ul className="notificationsSidebar__list">
|
||||
{notifications.map(o => <NotificationItem key={o.id} item={o}
|
||||
clickFn={chooseNotification}/>)}
|
||||
</ul>
|
||||
: <div className="notificationsSidebar__loading">
|
||||
<i className="pi pi-megaphone" style={{ fontSize: '2rem' }}></i>
|
||||
{__('Vuoto', 'gepafin')}
|
||||
</div>)}
|
||||
</TabPanel>
|
||||
<TabPanel header={__('Letti', 'gepafin')}>
|
||||
{loading
|
||||
? <div className="notificationsSidebar__loading">
|
||||
<i className="pi pi-spin pi-spinner" style={{ fontSize: '2rem' }}></i>
|
||||
</div>
|
||||
: !isEmpty(chosenMsg)
|
||||
? <NotificationItemChosen item={chosenMsg} closeFn={closeChosenMsg}/>
|
||||
: (notificationsRead.length > 0
|
||||
? <ul className="notificationsSidebar__list">
|
||||
{notificationsRead.map(o => <NotificationItem key={o.id} item={o}
|
||||
clickFn={chooseNotification}/>)}
|
||||
</ul>
|
||||
:
|
||||
<div className="notificationsSidebar__loading">
|
||||
<i className="pi pi-megaphone" style={{ fontSize: '2rem' }}></i>
|
||||
{__('Vuoto', 'gepafin')}
|
||||
</div>)}
|
||||
</TabPanel>
|
||||
</TabView>
|
||||
</Sidebar>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotificationsSidebar;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
// components
|
||||
@@ -8,14 +8,12 @@ import LogoIcon from '../../../../icons/LogoIcon';
|
||||
import { IconField } from 'primereact/iconfield';
|
||||
import { InputIcon } from 'primereact/inputicon';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { Badge } from 'primereact/badge';
|
||||
import { Button } from 'primereact/button';
|
||||
import TopBarProfileMenu from '../../../../components/TopBarProfileMenu';
|
||||
import { Sidebar } from 'primereact/sidebar';
|
||||
import NotificationsSidebar from '../../../../components/NotificationsSidebar';
|
||||
|
||||
const AppTopbar = () => {
|
||||
const menuLeft = useRef(null);
|
||||
const [notificationsVisible, setNotificationsVisible] = useState(false);
|
||||
|
||||
const startContent = <Link to="/">
|
||||
<LogoIcon/>
|
||||
@@ -26,14 +24,13 @@ const AppTopbar = () => {
|
||||
<InputIcon className="pi pi-search"> </InputIcon>
|
||||
<InputText v-model="value1" placeholder={__('Cerca', 'gepafin')} disabled={true}/>
|
||||
</IconField>
|
||||
<i className="pi pi-bell p-overlay-badge topBar__icon">
|
||||
<Badge value="0"></Badge>
|
||||
</i>
|
||||
<NotificationsSidebar/>
|
||||
<i className="pi pi-envelope topBar__icon"></i>
|
||||
{/*<i className="pi pi-envelope p-overlay-badge topBar__icon">
|
||||
<Badge severity="danger"></Badge>
|
||||
</i>*/}
|
||||
<Button
|
||||
type="button"
|
||||
className="topBar__profileBtn"
|
||||
outlined
|
||||
onClick={(event) => menuLeft.current.toggle(event)} aria-controls="topBar_profileMenu" aria-haspopup>
|
||||
@@ -44,18 +41,7 @@ const AppTopbar = () => {
|
||||
</div>
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toolbar start={startContent} end={endContent} className="topBar"/>
|
||||
<Sidebar visible={notificationsVisible} onHide={() => setNotificationsVisible(false)} fullScreen>
|
||||
<h2>Sidebar</h2>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore
|
||||
et dolore magna aliqua.
|
||||
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
||||
consequat.
|
||||
</p>
|
||||
</Sidebar>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user