- 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/myTable.scss";
|
||||||
@import "./components/evaluation.scss";
|
@import "./components/evaluation.scss";
|
||||||
@import "./components/fieldsRepeater.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';
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
@@ -8,14 +8,12 @@ import LogoIcon from '../../../../icons/LogoIcon';
|
|||||||
import { IconField } from 'primereact/iconfield';
|
import { IconField } from 'primereact/iconfield';
|
||||||
import { InputIcon } from 'primereact/inputicon';
|
import { InputIcon } from 'primereact/inputicon';
|
||||||
import { InputText } from 'primereact/inputtext';
|
import { InputText } from 'primereact/inputtext';
|
||||||
import { Badge } from 'primereact/badge';
|
|
||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import TopBarProfileMenu from '../../../../components/TopBarProfileMenu';
|
import TopBarProfileMenu from '../../../../components/TopBarProfileMenu';
|
||||||
import { Sidebar } from 'primereact/sidebar';
|
import NotificationsSidebar from '../../../../components/NotificationsSidebar';
|
||||||
|
|
||||||
const AppTopbar = () => {
|
const AppTopbar = () => {
|
||||||
const menuLeft = useRef(null);
|
const menuLeft = useRef(null);
|
||||||
const [notificationsVisible, setNotificationsVisible] = useState(false);
|
|
||||||
|
|
||||||
const startContent = <Link to="/">
|
const startContent = <Link to="/">
|
||||||
<LogoIcon/>
|
<LogoIcon/>
|
||||||
@@ -26,14 +24,13 @@ const AppTopbar = () => {
|
|||||||
<InputIcon className="pi pi-search"> </InputIcon>
|
<InputIcon className="pi pi-search"> </InputIcon>
|
||||||
<InputText v-model="value1" placeholder={__('Cerca', 'gepafin')} disabled={true}/>
|
<InputText v-model="value1" placeholder={__('Cerca', 'gepafin')} disabled={true}/>
|
||||||
</IconField>
|
</IconField>
|
||||||
<i className="pi pi-bell p-overlay-badge topBar__icon">
|
<NotificationsSidebar/>
|
||||||
<Badge value="0"></Badge>
|
|
||||||
</i>
|
|
||||||
<i className="pi pi-envelope topBar__icon"></i>
|
<i className="pi pi-envelope topBar__icon"></i>
|
||||||
{/*<i className="pi pi-envelope p-overlay-badge topBar__icon">
|
{/*<i className="pi pi-envelope p-overlay-badge topBar__icon">
|
||||||
<Badge severity="danger"></Badge>
|
<Badge severity="danger"></Badge>
|
||||||
</i>*/}
|
</i>*/}
|
||||||
<Button
|
<Button
|
||||||
|
type="button"
|
||||||
className="topBar__profileBtn"
|
className="topBar__profileBtn"
|
||||||
outlined
|
outlined
|
||||||
onClick={(event) => menuLeft.current.toggle(event)} aria-controls="topBar_profileMenu" aria-haspopup>
|
onClick={(event) => menuLeft.current.toggle(event)} aria-controls="topBar_profileMenu" aria-haspopup>
|
||||||
@@ -44,18 +41,7 @@ const AppTopbar = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<Toolbar start={startContent} end={endContent} className="topBar"/>
|
<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