Merge pull request #32 from Kitzanos/feature/93-user-details-page

Feature/93 user details page
This commit is contained in:
Vitalii Kiiko
2025-01-16 11:33:58 +01:00
committed by GitHub
28 changed files with 541 additions and 126 deletions

View File

@@ -147,6 +147,10 @@
max-width: 100%;
}
.p-dropdown {
width: 100%;
}
.p-password.p-inputwrapper {
width: 100%;

View File

@@ -22,7 +22,10 @@ const ChartDomandePerBando = ({ title, data = [] }) => {
<div className="chartCard__tooltip">
<p className="chartCard__tooltipTitle">{label}</p>
<p className="chartCard__tooltipText">
{__('Domande', 'gepafin')}: {payload[0].value}
{__('In bozza', 'gepafin')}: {payload[0].value}
</p>
<p className="chartCard__tooltipText">
{__('Inviate', 'gepafin')}: {payload[1].value}
</p>
</div>
);
@@ -50,7 +53,8 @@ const ChartDomandePerBando = ({ title, data = [] }) => {
<YAxis/>
<Tooltip content={<CustomTooltip/>}/>
<Legend/>
<Bar dataKey="numberOfApplications" fill="#EEC137" name={__('Quantità delle domande', 'gepafin')}/>
<Bar dataKey="numberOfDraftApplications" fill="#8884d8" name={__('Domande in bozza', 'gepafin')}/>
<Bar dataKey="numberOfSubmitedApplications" fill="#EEC137" name={__('Domande inviate', 'gepafin')}/>
</BarChart>
</ResponsiveContainer>
</div> : null}

View File

@@ -38,7 +38,7 @@ const ChartStatoDomande = ({ title, data = [] }) => {
label={({ percent }) => `${(percent * 100).toFixed(0)}%`}
outerRadius={120}
fill="#8884d8"
dataKey="numberOfApplications"
dataKey="numberOfSubmitedApplications"
nameKey="status"
>
{data.map((entry, index) => (

View File

@@ -17,12 +17,13 @@ const NotificationItemChosen = ({ item, closeFn, markReadFn }) => {
<span>{getDateFromISOstring(item.createdDate)}</span>
{item.message}
<Button
{item.status === 'UNREAD'
? <Button
style={{marginTop: '20px'}}
type="button"
outlined
onClick={() => markReadFn(item.id)}
label={__('Letto', 'gepafin')}/>
label={__('Letto', 'gepafin')}/> : null}
</div>
)
}

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
import { head, isEmpty, pathOr } from 'ramda';
import SockJS from 'sockjs-client';
@@ -19,6 +19,7 @@ import { Sidebar } from 'primereact/sidebar';
import { TabPanel, TabView } from 'primereact/tabview';
import NotificationItem from './components/NotificationItem';
import NotificationItemChosen from './components/NotificationItemChosen';
import PaginatorBasic from '../PaginatorBasic';
const socketUrl = process.env.REACT_APP_API_ADDRESS_WS;
@@ -35,22 +36,22 @@ const NotificationsSidebar = () => {
const stomp = useRef(null);
const [currentSubscription, setCurrentSubscription] = useState(null);
const [isConnected, setIsConnected] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [totalRecordsNum, setTotalRecordsNum] = useState(0);
const [totalPagesNum, setTotalPagesNum] = useState(0);
const perPage = 10;
// Handle tab change
const handleTabChange = (e) => {
setActiveIndex(e.index);
fetchTabData(e.index);
};
const fetchTabData = (index) => {
setChosenMsg({});
if (0 === index) {
fetchMessages();
} else {
fetchMessages('READ');
if (e.index === activeIndex) {
return
}
}
setTotalRecordsNum(0);
setTotalPagesNum(0);
setChosenMsg({});
setActiveIndex(e.index);
setCurrentPage(1);
};
const chooseNotification = (id) => {
const properItems = activeIndex === 0 ? notifications : notificationsRead;
@@ -64,10 +65,27 @@ const NotificationsSidebar = () => {
setChosenMsg({});
}
const fetchMessages = (status = 'UNREAD') => {
const getPaginationQuery = (status = 'UNREAD', curPage = 1) => {
return {
'globalFilters': {
'page': curPage,
'limit': perPage,
'sortBy': {
'columnName': 'id',
'sortDesc': true
}
},
'status': [
status
]
}
}
const fetchMessages = useCallback((status = 'UNREAD') => {
const chosenCompanyId = storeGet.main.chosenCompanyId();
const userData = storeGet.main.userData();
const role = pathOr('', ['role', 'roleType'], userData);
const bodyParams = getPaginationQuery(status, currentPage);
if (currentSubscription) {
//console.log('UNsubscribed')
@@ -77,37 +95,72 @@ const NotificationsSidebar = () => {
if (userData.id && chosenCompanyId !== 0 && role === 'ROLE_BENEFICIARY') {
setLoading(true);
NotificationService.getNotifications(
userData.id,
status === 'UNREAD' ? getNotifications : getNotificationsRead,
errGetNotifications,
[
['status', status],
['companyId', chosenCompanyId]
]
);
if (isConnected && socket.current) {
subscribeTo(`/topic/notifications_user_${userData.id}_company_${chosenCompanyId}`)
}
} else if (userData.id && role !== 'ROLE_BENEFICIARY') {
setLoading(true);
NotificationService.getNotifications(
NotificationService.getNotificationsByCompanyId(
userData.id,
chosenCompanyId,
status === 'UNREAD' ? getNotifications : getNotificationsRead,
errGetNotifications,
[
['status', status]
]
);
if (isConnected && socket.current) {
subscribeTo(`/topic/notifications_user_${userData.id}_company_${chosenCompanyId}`)
}
} else if (userData.id && role !== 'ROLE_BENEFICIARY') {
setLoading(true);
/*NotificationService.getNotifications(
userData.id,
status === 'UNREAD' ? getNotifications : getNotificationsRead,
errGetNotifications,
[
['status', status]
]
);*/
NotificationService.getNotificationsPagination(
userData.id,
bodyParams,
status === 'UNREAD' ? getNotificationsPagi : getNotificationsReadPagi,
errGetNotifications
);
if (isConnected && socket.current) {
subscribeTo(`/topic/notifications_user_${userData.id}`)
}
}
}, [currentPage]);
const getNotificationsPagi = (resp) => {
if (resp.status === 'SUCCESS') {
const { body, totalRecords, currentPage, totalPages } = resp.data;
setNotifications(body);
setTotalRecordsNum(totalRecords);
setTotalPagesNum(totalPages);
if (currentPage > totalPages) {
setCurrentPage(totalPages);
}
}
set404FromErrorResponse(resp);
setLoading(false);
}
const getNotificationsReadPagi = (resp) => {
if (resp.status === 'SUCCESS') {
const { body, totalRecords, currentPage, totalPages } = resp.data;
setNotificationsRead(body);
setTotalRecordsNum(totalRecords);
setTotalPagesNum(totalPages);
if (currentPage > totalPages) {
setCurrentPage(totalPages);
}
}
set404FromErrorResponse(resp);
setLoading(false);
}
const getNotifications = (resp) => {
if (resp.status === 'SUCCESS') {
setNotifications(resp.data);
setTotalRecordsNum(resp.data.length);
}
set404FromErrorResponse(resp);
setLoading(false);
@@ -116,6 +169,7 @@ const NotificationsSidebar = () => {
const getNotificationsRead = (resp) => {
if (resp.status === 'SUCCESS') {
setNotificationsRead(resp.data);
setTotalRecordsNum(resp.data.length);
}
set404FromErrorResponse(resp);
setLoading(false);
@@ -139,6 +193,7 @@ const NotificationsSidebar = () => {
const msgs = notificationsRead.map(o => o.id === resp.data.id ? resp.data : o);
setNotificationsRead(msgs);
}
setTotalRecordsNum(totalRecordsNum - 1);
}
set404FromErrorResponse(resp);
}
@@ -152,7 +207,7 @@ const NotificationsSidebar = () => {
stomp.current = Stomp.over(socket.current);
stomp.current.configure({
debug: function(str) {
debug: function (str) {
//console.log(str);
},
reconnectDelay: 5000,
@@ -190,10 +245,22 @@ const NotificationsSidebar = () => {
setCurrentSubscription(subscription);
}
const onPageChange = (num) => {
setCurrentPage(num);
};
useEffect(() => {
fetchMessages();
}, [chosenCompanyId, userData.id, isConnected]);
useEffect(() => {
if (0 === activeIndex) {
fetchMessages();
} else {
fetchMessages('READ');
}
}, [currentPage, activeIndex]);
useEffect(() => {
connectWebSocket();
@@ -215,7 +282,7 @@ const NotificationsSidebar = () => {
<>
<i className="pi pi-bell p-overlay-badge topBar__icon notificationsIcon"
onClick={() => setNotificationsVisible(true)}>
<Badge value={notifications.filter(o => o.status === 'UNREAD').length}></Badge>
<Badge value={totalRecordsNum}></Badge>
</i>
<Sidebar
className="notificationsSidebar"
@@ -234,12 +301,19 @@ const NotificationsSidebar = () => {
closeFn={closeChosenMsg}
markReadFn={makeNotificationRead}/>
: (notifications.length > 0
? <ul className="notificationsSidebar__list">
{notifications.map(o => <NotificationItem
key={o.id}
item={o}
clickFn={chooseNotification}/>)}
</ul>
? <>
<ul className="notificationsSidebar__list">
{notifications.map(o => <NotificationItem
key={o.id}
item={o}
clickFn={chooseNotification}/>)}
</ul>
<PaginatorBasic
totalPages={totalPagesNum}
currentPage={currentPage}
clickFn={onPageChange}
/>
</>
: <div className="notificationsSidebar__loading">
<i className="pi pi-megaphone" style={{ fontSize: '2rem' }}></i>
{__('Vuoto', 'gepafin')}
@@ -256,17 +330,24 @@ const NotificationsSidebar = () => {
closeFn={closeChosenMsg}
markReadFn={makeNotificationRead}/>
: (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>)}
? <>
<ul className="notificationsSidebar__list">
{notificationsRead.map(o => <NotificationItem
key={o.id}
item={o}
clickFn={chooseNotification}/>)}
</ul>
<PaginatorBasic
totalPages={totalPagesNum}
currentPage={currentPage}
clickFn={onPageChange}
/>
</>
:
<div className="notificationsSidebar__loading">
<i className="pi pi-megaphone" style={{ fontSize: '2rem' }}></i>
{__('Vuoto', 'gepafin')}
</div>)}
</TabPanel>
</TabView>
</Sidebar>

View File

@@ -0,0 +1,59 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
const PaginatorBasic = ({
currentPage = 0,
totalPages = 0,
clickFn = () => {
}
}) => {
const handleClick = (num) => {
const newNum = num < 0
? 0
: num > totalPages ? totalPages : num;
clickFn(newNum);
}
const prevDisabled = currentPage <= 1;
const nextDisabled = currentPage >= totalPages
return (
totalPages !== 0
? <div className="p-paginator p-component" data-pc-name="paginator" data-pc-section="root">
<button
type="button"
className={`p-paginator-prev p-paginator-element p-link${prevDisabled ? ' p-disabled' : ''}`}
disabled={prevDisabled}
onClick={prevDisabled ? () => {
} : () => handleClick(currentPage - 1)}
aria-label={__('Pagina precedente', 'gepafin')}
data-pc-section="prevpagebutton">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"
className="p-icon p-paginator-icon" aria-hidden="true" data-pc-section="prevpageicon">
<path
d="M8.75 11.185C8.65146 11.1854 8.55381 11.1662 8.4628 11.1284C8.37179 11.0906 8.28924 11.0351 8.22 10.965L4.72 7.46496C4.57955 7.32433 4.50066 7.13371 4.50066 6.93496C4.50066 6.73621 4.57955 6.54558 4.72 6.40496L8.22 2.93496C8.36095 2.84357 8.52851 2.80215 8.69582 2.81733C8.86312 2.83252 9.02048 2.90344 9.14268 3.01872C9.26487 3.134 9.34483 3.28696 9.36973 3.4531C9.39463 3.61924 9.36303 3.78892 9.28 3.93496L6.28 6.93496L9.28 9.93496C9.42045 10.0756 9.49934 10.2662 9.49934 10.465C9.49934 10.6637 9.42045 10.8543 9.28 10.995C9.13526 11.1257 8.9448 11.1939 8.75 11.185Z"
fill="currentColor"></path>
</svg>
</button>
<span aria-live="polite" className="p-paginator-current" data-pc-section="current">
({currentPage} {__('di', 'gepafin')} {totalPages})
</span>
<button
type="button"
disabled={nextDisabled}
onClick={nextDisabled ? () => {
} : () => handleClick(currentPage + 1)}
className={`p-paginator-next p-paginator-element p-link${nextDisabled ? ' p-disabled' : ''}`}
aria-label={__('Pagina successiva', 'gepafin')}
data-pc-section="nextpagebutton">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"
className="p-icon p-paginator-icon" aria-hidden="true" data-pc-section="nextpageicon">
<path
d="M5.25 11.1728C5.14929 11.1694 5.05033 11.1455 4.9592 11.1025C4.86806 11.0595 4.78666 10.9984 4.72 10.9228C4.57955 10.7822 4.50066 10.5916 4.50066 10.3928C4.50066 10.1941 4.57955 10.0035 4.72 9.86283L7.72 6.86283L4.72 3.86283C4.66067 3.71882 4.64765 3.55991 4.68275 3.40816C4.71785 3.25642 4.79932 3.11936 4.91585 3.01602C5.03238 2.91268 5.17819 2.84819 5.33305 2.83149C5.4879 2.81479 5.64411 2.84671 5.78 2.92283L9.28 6.42283C9.42045 6.56346 9.49934 6.75408 9.49934 6.95283C9.49934 7.15158 9.42045 7.34221 9.28 7.48283L5.78 10.9228C5.71333 10.9984 5.63193 11.0595 5.5408 11.1025C5.44966 11.1455 5.35071 11.1694 5.25 11.1728Z"
fill="currentColor"></path>
</svg>
</button>
</div> : null
)
}
export default PaginatorBasic;

View File

@@ -45,77 +45,77 @@ const AppSidebar = () => {
label: __('Bandi osservati', 'gepafin'),
icon: 'pi pi-star',
href: '/bandi-osservati',
id: 13,
id: 5,
enable: intersection(permissions, ['VIEW_CALLS']).length
},
{
label: __('Gestione domande', 'gepafin'),
icon: 'pi pi-file',
href: '/domande',
id: 5,
id: 6,
enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length
},
{
label: __('Domande da valutare', 'gepafin'),
icon: 'pi pi-calendar-clock',
href: '/domande',
id: 6,
id: 7,
enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length
},
{
label: __('Archivio domande', 'gepafin'),
icon: 'pi pi-briefcase',
href: '/domande',
id: 7,
id: 8,
enable: intersection(permissions, ['APPLY_CALLS']).length
},
{
label: __('Archivio domande', 'gepafin'),
icon: 'pi pi-briefcase',
href: '/domande-archivio',
id: 5,
id: 9,
enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length
},
{
label: __('Archivio domande', 'gepafin'),
icon: 'pi pi-briefcase',
href: '/domande-archivio',
id: 6,
id: 10,
enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length
},
{
label: __('Soccorso istruttorio', 'gepafin'),
icon: <HelpIcon/>,
href: '/soccorso-istruttorio',
id: 8,
id: 11,
enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length
},
{
label: __('Gestione utenti', 'gepafin'),
icon: 'pi pi-users',
href: '/utenti',
id: 9,
id: 12,
enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length
},
{
label: __('Configurazione', 'gepafin'),
icon: 'pi pi-cog',
//href: '/configurazione',
id: 10,
id: 13,
enable: false
},
{
label: __('Report e Analisi', 'gepafin'),
icon: 'pi pi-chart-bar',
//href: '/stats',
id: 11,
id: 14,
enable: false
},
{
label: __('Log di Sistema', 'gepafin'),
icon: 'pi pi-receipt',
clickFn: () => {},
id: 12,
id: 15,
enable: false
}
]

View File

@@ -121,7 +121,7 @@ const AllBandiTable = () => {
return(
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
<DataTable value={items} paginator showGridlines rows={5} loading={localAsyncRequest} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}

View File

@@ -206,7 +206,7 @@ const AllBandiAccordion = ({ showOnlyPreferred = false }) => {
<div className="appPageSection__table">
<DataTable value={items}
paginator
rows={10}
rows={5}
loading={isAsyncRequest}
dataKey="id"
filters={filters}

View File

@@ -186,7 +186,7 @@ const AllBandiPreferredAccordion = () => {
<div className="appPageSection__table">
<DataTable value={items}
paginator
rows={10}
rows={5}
loading={isAsyncRequest}
dataKey="id"
filters={filters}

View File

@@ -136,7 +136,7 @@ const DraftApplicationsTable = () => {
return (
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
<DataTable value={items} paginator showGridlines rows={5} loading={localAsyncRequest} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}

View File

@@ -21,17 +21,43 @@ const LatestBandiTable = () => {
const [filters, setFilters] = useState(null);
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [, setStatuses] = useState([]);
/*const [totalRecordsNum, setTotalRecordsNum] = useState(0);
const [perPageNum, setPerPageNum] = useState(0);
const getPaginationQuery = () => {
return {
"globalFilters": {
"page": 1,
"limit": 5,
"sortBy": {
"columnName": "ID",
"sortDesc": true
}
}
}
}
const onPageChange = (e) => {
console.log('onPageChange', e)
}*/
useEffect(() => {
setLocalAsyncRequest(true);
BandoService.getBandi(getCallback, errGetCallbacks);
//const paginationQuery = getPaginationQuery();
//BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks);
}, []);
const getCallback = (data) => {
if (data.status === 'SUCCESS') {
/*const { body, totalRecords, currentPage, totalPages, pageSize } = data.data;
setTotalRecordsNum(totalRecords);
setPerPageNum(pageSize);
const newItems = body.filter(o => o.status === 'PUBLISH');
setItems(getFormattedBandiData(newItems));
setStatuses(uniq(body.map(o => o.status)));*/
const newItems = data.data.filter(o => o.status === 'PUBLISH');
setItems(getFormattedBandiData(newItems));
setStatuses(uniq(data.data.map(o => o.status)))
setStatuses(uniq(data.data.map(o => o.status)));
initFilters();
}
setLocalAsyncRequest(false);
@@ -106,7 +132,10 @@ const LatestBandiTable = () => {
return(
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
<DataTable value={items}
paginator showGridlines
/*lazy totalRecords={totalRecordsNum} onPage={onPageChange}*/
rows={5} loading={localAsyncRequest} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}

View File

@@ -103,7 +103,7 @@ const LatestUsersActivityTable = () => {
return(
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={loading} dataKey="id"
<DataTable value={items} paginator showGridlines rows={5} loading={loading} dataKey="id"
filters={filters}
globalFilterFields={['name', 'status']}
header={header}

View File

@@ -189,7 +189,7 @@ const LatestBandiTable = () => {
return (
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={loading} dataKey="id"
<DataTable value={items} paginator showGridlines rows={5} loading={loading} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}

View File

@@ -204,7 +204,7 @@ const MyLatestSubmissionsTable = () => {
return (
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
<DataTable value={items} paginator showGridlines rows={5} loading={localAsyncRequest} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}

View File

@@ -25,6 +25,8 @@ import { Tag } from 'primereact/tag';
import translationStrings from '../../../../translationStringsForComponents';
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
const PreInstructorDomandeTable = () => {
const userData = useStore().main.userData();
const [items, setItems] = useState(null);
@@ -136,7 +138,7 @@ const PreInstructorDomandeTable = () => {
return(
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
<DataTable value={items} paginator showGridlines rows={5} loading={localAsyncRequest} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}
@@ -147,12 +149,14 @@ const PreInstructorDomandeTable = () => {
<Column field="protocolNumber" header={__('Protocollo', 'gepafin')}
sortable filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '6rem' }}/>
<Column field="ndg" header={__('NDG', 'gepafin')}
{APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
? <Column field="ndg" header={__('NDG', 'gepafin')}
sortable filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '6rem' }}/>
<Column field="appointmentId" header={__('ID appuntamento', 'gepafin')}
style={{ minWidth: '6rem' }}/> : null}
{APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
? <Column field="appointmentId" header={__('ID appuntamento', 'gepafin')}
sortable filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '6rem' }}/>
style={{ minWidth: '6rem' }}/> : null}
<Column field="callName" header={__('Bando', 'gepafin')}
filter sortable
filterPlaceholder={__('Cerca', 'gepafin')}

View File

@@ -562,14 +562,16 @@ const DomandaEditPreInstructor = () => {
<span>{__('Protocollo', 'gepafin')}</span>
<span>{data.protocolNumber}</span>
</p>
<p className="appPageSection__pMeta">
{APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
? <p className="appPageSection__pMeta">
<span>{__('NDG', 'gepafin')}</span>
<span>{data.ndg}</span>
</p>
<p className="appPageSection__pMeta">
</p> : null}
{APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
? <p className="appPageSection__pMeta">
<span>{__('Appuntamento', 'gepafin')}</span>
<span>{data.appointmentId}</span>
</p>
</p> : null}
<p className="appPageSection__pMeta">
<span>{__('Bando', 'gepafin')}</span>
<span>{data.callName}</span>

View File

@@ -564,14 +564,16 @@ const DomandaEditPreInstructor = () => {
<span>{__('Protocollo', 'gepafin')}</span>
<span>{data.protocolNumber}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('NDG', 'gepafin')}</span>
<span>{data.ndg}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Appuntamento', 'gepafin')}</span>
<span>{data.appointmentId}</span>
</p>
{APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
? <p className="appPageSection__pMeta">
<span>{__('NDG', 'gepafin')}</span>
<span>{data.ndg}</span>
</p> : null}
{APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
? <p className="appPageSection__pMeta">
<span>{__('Appuntamento', 'gepafin')}</span>
<span>{data.appointmentId}</span>
</p> : null}
<p className="appPageSection__pMeta">
<span>{__('Bando', 'gepafin')}</span>
<span>{data.callName}</span>
@@ -840,7 +842,7 @@ const DomandaEditPreInstructor = () => {
type="button"
disabled={APP_EVALUATION_FLOW_ID === '1'
&& (!['EVALUATION', 'ADMISSIBLE', 'APPOINTMENT'].includes(data.applicationStatus)
|| evaluationShouldBeBlocked(data))}
|| evaluationShouldBeBlocked(data))}
onClick={initiateRejecting}
label={__('Respingi domanda', 'gepafin')}
icon="pi pi-times" iconPos="right"/> : null}

View File

@@ -150,7 +150,7 @@ const AllDomandeTable = ({ openDialogFn, updaterString = '' }) => {
return (
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
<DataTable value={items} paginator showGridlines rows={5} loading={localAsyncRequest} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n';
import { is, uniq } from 'ramda';
import { Link, useLocation } from 'react-router-dom';
import { Link } from 'react-router-dom';
// api
import ApplicationService from '../../../../service/application-service';
@@ -29,7 +29,6 @@ const AllDomandeArchiveTable = ({ updaterString = '' }) => {
const [filters, setFilters] = useState(null);
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [statuses, setStatuses] = useState([]);
const location = useLocation();
useEffect(() => {
setLocalAsyncRequest(true);
@@ -141,7 +140,7 @@ const AllDomandeArchiveTable = ({ updaterString = '' }) => {
return (
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
<DataTable value={items} paginator showGridlines rows={5} loading={localAsyncRequest} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}

View File

@@ -157,7 +157,7 @@ const BeneficiarioDomandeTable = () => {
return (
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
<DataTable value={items} paginator showGridlines rows={5} loading={localAsyncRequest} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}

View File

@@ -135,7 +135,7 @@ const PreInstructorSoccorsiTable = ({ openDialogFn }) => {
return(
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
<DataTable value={items} paginator showGridlines rows={5} loading={localAsyncRequest} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}

View File

@@ -0,0 +1,105 @@
import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n';
import { is } from 'ramda';
// translation
import translationStrings from '../../../../translationStringsForComponents';
// components
import { FilterMatchMode, FilterOperator } from 'primereact/api';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Button } from 'primereact/button';
const UserActivityTable = ({ data = [] }) => {
const [items, setItems] = useState([]);
const [filters, setFilters] = useState(null);
useEffect(() => {
if (data) {
setItems(getFormattedData(data));
initFilters();
}
}, [data]);
const getFormattedData = (data) => {
return data.map((d) => {
d.createdDate = is(String, d.createdDate) ? new Date(d.createdDate) : (d.createdDate ? d.createdDate : '');
d.updatedDate = is(String, d.updatedDate) ? new Date(d.updatedDate) : (d.updatedDate ? d.updatedDate : '');
return d;
});
};
const formatDate = (value) => {
return value.toLocaleDateString('it-IT', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
});
};
const clearFilter = () => {
initFilters();
};
const initFilters = () => {
setFilters({
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
actionType: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }]
},
ipAddress: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }]
},
createdDate: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
}
});
};
const renderHeader = () => {
return (
<div className="appTableHeader">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
onClick={clearFilter}/>
</div>
);
};
const dateBodyTemplate = (rowData) => {
return formatDate(rowData.createdDate);
};
const header = renderHeader();
return (
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}
onFilter={(e) => setFilters(e.filters)}>
<Column header={__('Timestamp', 'gepafin')}
filterField="createdDate" dataType="date"
style={{ minWidth: '8rem' }}
body={dateBodyTemplate}/>
<Column field="actionType" header={__('Tipo di attività', 'gepafin')}
filter sortable
filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column field="actionContext" header={__('Azione', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column field="ipAddress" header={__('IP', 'gepafin')}
filter sortable
filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '8rem' }}/>
</DataTable>
</div>
)
}
export default UserActivityTable;

View File

@@ -15,6 +15,8 @@ import getDateFromISOstring from '../../helpers/getDateFromISOstring';
import { Button } from 'primereact/button';
import { Toast } from 'primereact/toast';
import { Dropdown } from 'primereact/dropdown';
import UserActionService from '../../service/user-action-service';
import UserActivityTable from './components/UserActivityTable';
const UserActivity = () => {
@@ -25,6 +27,11 @@ const UserActivity = () => {
const [user, setUser] = useState({});
const [roles, setRoles] = useState([]);
const [chosenRole, setChosenRole] = useState(0);
const [actionsContext, setActionsContext] = useState([]);
const [userActions, setUserActions] = useState({});
const [chosenPeriod, setChosenPeriod] = useState('');
const [chosenActivity, setChosenActivity] = useState('');
const goBack = () => {
navigate(`/utenti`);
@@ -56,13 +63,13 @@ const UserActivity = () => {
}
const getStatValue = (key, fallback = 0) => {
return pathOr(fallback, [key], {});
return pathOr(fallback, [key], userActions);
}
const handleRoleUpdate = () => {
if (user.role?.id !== chosenRole) {
setLoading(true);
UserService.updateUser(user.id, {roleId: chosenRole}, updateRoleCallback, errUpdateRoleCallback)
UserService.updateUser(user.id, { roleId: chosenRole }, updateRoleCallback, errUpdateRoleCallback)
}
}
@@ -78,11 +85,50 @@ const UserActivity = () => {
setLoading(false);
}
const getActionsContextCallback = (resp) => {
if (resp.status === 'SUCCESS') {
setActionsContext(resp.data)
}
setLoading(false);
}
const errGetActionsContextCallback = (resp) => {
set404FromErrorResponse(resp);
setLoading(false);
}
const getUserActionsCallback = (resp) => {
if (resp.status === 'SUCCESS') {
setUserActions(resp.data)
}
setLoading(false);
}
const errGetUserActionsCallback = (resp) => {
set404FromErrorResponse(resp);
setLoading(false);
}
const doFilterUserActivity = () => {
let queryParams = [];
if (!isEmpty(chosenPeriod)) {
queryParams.push(['timeFilter', chosenPeriod])
}
if (!isEmpty(chosenActivity)) {
queryParams.push(['actionContext', chosenActivity])
}
UserActionService.getUserActions(id, getUserActionsCallback, errGetUserActionsCallback, queryParams);
}
useEffect(() => {
if (id && !isEmpty(id)) {
setLoading(true);
UserService.getUser(id, getUserCallback, errGetUserCallback);
UserService.getRoles(getRolesCallback, errGetRolesCallback);
UserActionService.getActionContext(id, getActionsContextCallback, errGetActionsContextCallback);
UserActionService.getUserActions(id, getUserActionsCallback, errGetUserActionsCallback);
}
}, [id])
@@ -143,7 +189,10 @@ const UserActivity = () => {
disabled={isEmpty(roles) || loading}
value={chosenRole}
onChange={(e) => setChosenRole(e.value)}
options={roles.filter(o => [3, 5].includes(o.id)).map(o => ({ label: o.roleName, value: o.id }))}
options={roles.filter(o => [3, 5].includes(o.id)).map(o => ({
label: o.roleName,
value: o.id
}))}
optionLabel="label"
placeholder={__('Seleziona ruolo', 'gepafin')}/>
<Button
@@ -159,32 +208,82 @@ const UserActivity = () => {
<div className="appPage__spacer"></div>
{/*<div className="appPageSection">
<h2>{__('Statistiche attività', 'gepafin')}</h2>
<div className="statsBigBadges__grid grid-small">
<div className="statsBigBadges__gridItem">
<span>{__('Login totali', 'gepafin')}</span>
<span>{<NumberFlow
value={getStatValue('numberOfActiveCalls', 0)}
format={{ notation: 'compact' }}
locales="it-IT"/>}</span>
{!isEmpty(userActions)
? <div className="appPageSection">
<h2>{__('Statistiche attività', 'gepafin')}</h2>
<div className="statsBigBadges__grid grid-small">
<div className="statsBigBadges__gridItem">
<span>{__('Login totali', 'gepafin')}</span>
<span>{<NumberFlow
value={getStatValue('numberOfLoginAttempts', 0)}
format={{ notation: 'compact' }}
locales="it-IT"/>}</span>
</div>
<div className="statsBigBadges__gridItem">
<span>{__('Bandi gestiti', 'gepafin')}</span>
<span>{<NumberFlow
value={getStatValue('applicationsProcessed', 0)}
format={{ notation: 'compact' }}
locales="it-IT"/>}</span>
</div>
{/*<div className="statsBigBadges__gridItem">
<span>{__('Domande processate', 'gepafin')}</span>
<span>{<NumberFlow
value={getStatValue('numberOfActiveCalls', 0)}
format={{ notation: 'compact' }}
locales="it-IT"/>}</span>
</div>*/}
</div>
<div className="statsBigBadges__gridItem">
<span>{__('Bandi gestiti', 'gepafin')}</span>
<span>{<NumberFlow
value={getStatValue('numberOfActiveCalls', 0)}
format={{ notation: 'compact' }}
locales="it-IT"/>}</span>
</div> : null}
<div className="appPage__spacer"></div>
{!isEmpty(userActions)
? <div className="appPageSection">
<h2>{__('Filtri attività', 'gepafin')}</h2>
<div className="appPageSection columns">
<div className="row">
<label>{__('Periodo', 'gepafin')}</label>
<Dropdown
value={chosenPeriod}
onChange={(e) => setChosenPeriod(e.value)}
options={[
{ value: 'LAST_WEEK', label: __('Ultima settimana', 'gepafin') },
{ value: 'LAST_QUARTER', label: __('Ultimo trimestre', 'gepafin') },
{ value: 'LAST_SEMESTER', label: __('Ultimo semestre', 'gepafin') },
{ value: 'LAST_YEAR', label: __('Ultimo anno', 'gepafin') }
]}
optionLabel="label"/>
</div>
{!isEmpty(actionsContext)
? <div className="row">
<label>{__('Tipo di attività', 'gepafin')}</label>
<Dropdown
className="fullWidth"
value={chosenActivity}
onChange={(e) => setChosenActivity(e.value)}
options={actionsContext.map(o => ({
value: o.actionContext,
label: o.description
}))}
optionLabel="label"/>
</div> : null}
<div className="row">
<Button
type="button"
onClick={doFilterUserActivity}
label={__('Applica', 'gepafin')}/>
</div>
</div>
<div className="statsBigBadges__gridItem">
<span>{__('Domande processate', 'gepafin')}</span>
<span>{<NumberFlow
value={getStatValue('numberOfActiveCalls', 0)}
format={{ notation: 'compact' }}
locales="it-IT"/>}</span>
</div>
</div>
</div>*/}
</div> : null}
<div className="appPage__spacer"></div>
{!isEmpty(userActions)
? <div className="appPageSection">
<h2>{__('Attività dettagliate', 'gepafin')}</h2>
<UserActivityTable data={userActions.userActions}/>
</div> : null}
</div>
)
}

View File

@@ -141,7 +141,7 @@ const AllUsersTable = () => {
return (
<div className="appPageSection__table">
<DataTable value={users} paginator showGridlines rows={10} loading={loading} dataKey="id"
<DataTable value={users} paginator showGridlines rows={5} loading={loading} dataKey="id"
filters={filters} stripedRows removableSort
header={header}
emptyMessage={translationStrings.emptyMessage}

View File

@@ -8,6 +8,10 @@ export default class BandoService {
NetworkService.get(`${API_BASE_URL}/call`, callback, errCallback, queryParams);
};
static getBandiPaginated = (body, callback, errCallback, queryParams) => {
NetworkService.post(`${API_BASE_URL}/call/pagination`, body, callback, errCallback, queryParams);
};
static getBando = (id, callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/call/${id}`, callback, errCallback, queryParams);
};

View File

@@ -4,8 +4,16 @@ const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class NotificationService {
static getNotifications = (id, callback, errCallback, queryParams) => {
/*static getNotifications = (id, callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/notification/user/${id}`, callback, errCallback, queryParams);
};*/
static getNotificationsByCompanyId = (id, companyId, callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/notification/user/${id}/company/${companyId}/notifications`, callback, errCallback, queryParams);
};
static getNotificationsPagination = (id, body, callback, errCallback, queryParams) => {
NetworkService.post(`${API_BASE_URL}/notification/user/${id}/pagination`, body, callback, errCallback, queryParams);
};
static notificationMakeRead = (id, callback, errCallback) => {

View File

@@ -0,0 +1,14 @@
import { NetworkService } from './network-service';
const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class UserActionService {
static getActionContext = (id, callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/userAction/user/${id}/action-context`, callback, errCallback, queryParams);
};
static getUserActions = (id, callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/userAction/user/${id}`, callback, errCallback, queryParams);
};
}