From 6eea3315305573c1177f52e53d56bc37825e87fe Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Tue, 24 Mar 2026 16:51:14 +0100 Subject: [PATCH] - added new root manage UIs; --- .../components/AppSidebar/index.js | 3 +- .../components/ManageSendPecSection/index.js | 121 +++++++++ src/pages/Admin/index.js | 96 +++++-- src/pages/AdminAmendmentExtend/index.js | 35 +++ src/pages/AdminAmendmentReopen/index.js | 35 +++ src/pages/AdminEliminaDomande/index.js | 48 ++++ src/pages/AdminLog/index.js | 244 ++++++++++++++++++ src/pages/AdminSendPec/index.js | 35 +++ src/pages/AdminSetNdg/index.js | 35 +++ src/pages/AdminSetStatus/index.js | 35 +++ src/routes.js | 28 ++ src/service/admin-service.js | 8 + 12 files changed, 695 insertions(+), 28 deletions(-) create mode 100644 src/pages/Admin/components/ManageSendPecSection/index.js create mode 100644 src/pages/AdminAmendmentExtend/index.js create mode 100644 src/pages/AdminAmendmentReopen/index.js create mode 100644 src/pages/AdminEliminaDomande/index.js create mode 100644 src/pages/AdminLog/index.js create mode 100644 src/pages/AdminSendPec/index.js create mode 100644 src/pages/AdminSetNdg/index.js create mode 100644 src/pages/AdminSetStatus/index.js diff --git a/src/layouts/DefaultLayout/components/AppSidebar/index.js b/src/layouts/DefaultLayout/components/AppSidebar/index.js index efdd7c7..89b5903 100644 --- a/src/layouts/DefaultLayout/components/AppSidebar/index.js +++ b/src/layouts/DefaultLayout/components/AppSidebar/index.js @@ -158,7 +158,8 @@ const AppSidebar = () => { 'ROOT_MANAGE_APPL_VIEW_DELETED', 'ROOT_MANAGE_APPL_DELETE_CONFIRM', 'ROOT_MANAGE_APPL_DELETE', - 'ROOT_MANAGE_PEC_SEND' + 'ROOT_MANAGE_PEC_SEND', + 'ROOT_MANAGE_VIEW_LOG' ]).length }, { diff --git a/src/pages/Admin/components/ManageSendPecSection/index.js b/src/pages/Admin/components/ManageSendPecSection/index.js new file mode 100644 index 0000000..1135369 --- /dev/null +++ b/src/pages/Admin/components/ManageSendPecSection/index.js @@ -0,0 +1,121 @@ +import React, { useState, useRef, useCallback } from 'react'; +import { __ } from '@wordpress/i18n'; + +// api +import AdminService from '../../../../service/admin-service'; + +// components +import { InputText } from 'primereact/inputtext'; +import { InputTextarea } from 'primereact/inputtextarea'; +import { Button } from 'primereact/button'; +import { Toast } from 'primereact/toast'; + +const ManageSendPecSection = () => { + const [subject, setSubject] = useState(''); + const [body, setBody] = useState(''); + const [csvFile, setCsvFile] = useState(null); + const [attachment, setAttachment] = useState(null); + const [localAsyncRequest, setLocalAsyncRequest] = useState(false); + const toast = useRef(null); + const csvInputRef = useRef(null); + const attachmentInputRef = useRef(null); + + const isSubmitDisabled = localAsyncRequest || !subject.trim() || !body.trim() || !csvFile; + + const handleSubmit = useCallback(() => { + const formData = new FormData(); + formData.append('subject', subject); + formData.append('body', body); + formData.append('csv_file', csvFile); + if (attachment) { + formData.append('attachment', attachment); + } + + setLocalAsyncRequest(true); + AdminService.doSendPec( + formData, + (resp) => { + setLocalAsyncRequest(false); + if (resp && resp.status === 'ok') { + const detail = resp.data + ? __(`Invio completato: ${resp.data.total_sent} inviati, ${resp.data.total_errors} errori`, 'gepafin') + : __('Invio completato con successo', 'gepafin'); + if (toast.current) { + toast.current.show({ severity: 'success', summary: '', detail }); + } + setSubject(''); + setBody(''); + setCsvFile(null); + setAttachment(null); + if (csvInputRef.current) csvInputRef.current.value = ''; + if (attachmentInputRef.current) attachmentInputRef.current.value = ''; + } else { + if (toast.current) { + toast.current.show({ severity: 'error', summary: '', detail: resp && resp.detail ? resp.detail : __('Errore durante l\'invio', 'gepafin') }); + } + } + }, + (resp) => { + setLocalAsyncRequest(false); + if (toast.current) { + toast.current.show({ severity: 'error', summary: '', detail: resp && resp.detail ? resp.detail : __('Errore durante l\'invio', 'gepafin') }); + } + } + ); + }, [subject, body, csvFile, attachment]); + + return ( +
+

{__('Invio PEC Massivo', 'gepafin')}

+ +
+
+ + setSubject(e.target.value)} + style={{ width: '100%' }}/> +
+
+ + setBody(e.target.value)} + rows={8} + style={{ width: '100%' }}/> +
+
+ + setCsvFile(e.target.files[0] || null)}/> + {__('Il file CSV deve contenere gli indirizzi PEC nella prima colonna', 'gepafin')} +
+
+ + setAttachment(e.target.files[0] || null)}/> +
+
+
+
+
+ ); +}; + +export default ManageSendPecSection; diff --git a/src/pages/Admin/index.js b/src/pages/Admin/index.js index 9aac328..ff872e8 100644 --- a/src/pages/Admin/index.js +++ b/src/pages/Admin/index.js @@ -1,46 +1,88 @@ import React from 'react'; import { __ } from '@wordpress/i18n'; import { intersection } from 'ramda'; +import { useNavigate } from 'react-router-dom'; // store import { useStoreValue } from '../../store'; // components -import ManageNdgSection from './components/ManageNdgSection'; -import ManageApplStatusSection from './components/ManageApplStatusSection'; -import ManageAmendmentReopenSection from './components/ManageAmendmentReopenSection'; -import ManageAmendmentExtendSection from './components/ManageAmendmentExtendSection'; -import ManageApplDeleteSection from './components/ManageApplDeleteSection'; +import { Card } from 'primereact/card'; + +const navItems = [ + { + permission: ['ROOT_MANAGE_NDG'], + route: '/admin/set-ndg', + label: __('Gestione NDG', 'gepafin'), + subtitle: __('Imposta il codice NDG per le domande', 'gepafin'), + icon: 'pi pi-id-card' + }, + { + permission: ['ROOT_MANAGE_APPL_STATUS'], + route: '/admin/set-status', + label: __('Gestione Stato Domanda', 'gepafin'), + subtitle: __('Modifica lo stato delle domande', 'gepafin'), + icon: 'pi pi-list' + }, + { + permission: ['ROOT_MANAGE_AMENDMENT_REOPEN'], + route: '/admin/amendment-reopen', + label: __('Riapri Integrazione', 'gepafin'), + subtitle: __('Riapri un\'integrazione chiusa', 'gepafin'), + icon: 'pi pi-refresh' + }, + { + permission: ['ROOT_MANAGE_AMENDMENT_EXTEND'], + route: '/admin/amendment-extend', + label: __('Estendi Integrazione', 'gepafin'), + subtitle: __('Estendi la scadenza di un\'integrazione', 'gepafin'), + icon: 'pi pi-calendar-plus' + }, + { + permission: ['ROOT_MANAGE_APPL_VIEW_DELETED', 'ROOT_MANAGE_APPL_DELETE_CONFIRM', 'ROOT_MANAGE_APPL_DELETE'], + route: '/admin/elimina-domande', + label: __('Elimina Domande', 'gepafin'), + subtitle: __('Gestione eliminazione domande', 'gepafin'), + icon: 'pi pi-trash' + }, + { + permission: ['ROOT_MANAGE_VIEW_LOG'], + route: '/admin/log', + label: __('Log Operazioni', 'gepafin'), + subtitle: __('Storico delle operazioni amministrative', 'gepafin'), + icon: 'pi pi-history' + }, + { + permission: ['ROOT_MANAGE_PEC_SEND'], + route: '/admin/send-pec', + label: __('Invio PEC Massivo', 'gepafin'), + subtitle: __('Invia PEC a più destinatari tramite file CSV', 'gepafin'), + icon: 'pi pi-envelope' + } +]; const Admin = () => { const permissions = useStoreValue('getPermissions'); + const navigate = useNavigate(); - const hasNdg = intersection(permissions, ['ROOT_MANAGE_NDG']).length > 0; - const hasApplStatus = intersection(permissions, ['ROOT_MANAGE_APPL_STATUS']).length > 0; - const hasAmendmentReopen = intersection(permissions, ['ROOT_MANAGE_AMENDMENT_REOPEN']).length > 0; - const hasAmendmentExtend = intersection(permissions, ['ROOT_MANAGE_AMENDMENT_EXTEND']).length > 0; - const hasApplDelete = intersection(permissions, [ - 'ROOT_MANAGE_APPL_VIEW_DELETED', - 'ROOT_MANAGE_APPL_DELETE_CONFIRM', - 'ROOT_MANAGE_APPL_DELETE' - ]).length > 0; - - const canViewDeleted = intersection(permissions, ['ROOT_MANAGE_APPL_VIEW_DELETED']).length > 0; - const canDeleteConfirm = intersection(permissions, ['ROOT_MANAGE_APPL_DELETE_CONFIRM']).length > 0; - const canDelete = intersection(permissions, ['ROOT_MANAGE_APPL_DELETE']).length > 0; + const visibleItems = navItems.filter( + (item) => intersection(permissions, item.permission).length > 0 + ); return

{__('Admin', 'gepafin')}

- {hasNdg && <>
} - {hasApplStatus && <>
} - {hasAmendmentReopen && <>
} - {hasAmendmentExtend && <>
} - {hasApplDelete && <>
} +
+ {visibleItems.map((item) => ( + {item.label}} + subTitle={item.subtitle} + style={{ width: '280px', cursor: 'pointer', padding: '0.5rem' }} + onClick={() => navigate(item.route)} + /> + ))} +
} diff --git a/src/pages/AdminAmendmentExtend/index.js b/src/pages/AdminAmendmentExtend/index.js new file mode 100644 index 0000000..ac82607 --- /dev/null +++ b/src/pages/AdminAmendmentExtend/index.js @@ -0,0 +1,35 @@ +import React, { useEffect } from 'react'; +import { __ } from '@wordpress/i18n'; +import { intersection } from 'ramda'; +import { useNavigate } from 'react-router-dom'; + +// store +import { useStoreValue } from '../../store'; + +// components +import { Button } from 'primereact/button'; +import ManageAmendmentExtendSection from '../Admin/components/ManageAmendmentExtendSection'; + +const AdminAmendmentExtend = () => { + const permissions = useStoreValue('getPermissions'); + const navigate = useNavigate(); + const hasPermission = intersection(permissions, ['ROOT_MANAGE_AMENDMENT_EXTEND']).length > 0; + + useEffect(() => { + if (!hasPermission) navigate('/admin'); + }, [hasPermission]); + + return
+
+

{__('Estendi Integrazione', 'gepafin')}

+
+
+
+
+
+ {hasPermission && } +
+} + +export default AdminAmendmentExtend; diff --git a/src/pages/AdminAmendmentReopen/index.js b/src/pages/AdminAmendmentReopen/index.js new file mode 100644 index 0000000..e2a3206 --- /dev/null +++ b/src/pages/AdminAmendmentReopen/index.js @@ -0,0 +1,35 @@ +import React, { useEffect } from 'react'; +import { __ } from '@wordpress/i18n'; +import { intersection } from 'ramda'; +import { useNavigate } from 'react-router-dom'; + +// store +import { useStoreValue } from '../../store'; + +// components +import { Button } from 'primereact/button'; +import ManageAmendmentReopenSection from '../Admin/components/ManageAmendmentReopenSection'; + +const AdminAmendmentReopen = () => { + const permissions = useStoreValue('getPermissions'); + const navigate = useNavigate(); + const hasPermission = intersection(permissions, ['ROOT_MANAGE_AMENDMENT_REOPEN']).length > 0; + + useEffect(() => { + if (!hasPermission) navigate('/admin'); + }, [hasPermission]); + + return
+
+

{__('Riapri Integrazione', 'gepafin')}

+
+
+
+
+
+ {hasPermission && } +
+} + +export default AdminAmendmentReopen; diff --git a/src/pages/AdminEliminaDomande/index.js b/src/pages/AdminEliminaDomande/index.js new file mode 100644 index 0000000..7aee2ae --- /dev/null +++ b/src/pages/AdminEliminaDomande/index.js @@ -0,0 +1,48 @@ +import React, { useEffect } from 'react'; +import { __ } from '@wordpress/i18n'; +import { intersection } from 'ramda'; +import { useNavigate } from 'react-router-dom'; + +// store +import { useStoreValue } from '../../store'; + +// components +import { Button } from 'primereact/button'; +import ManageApplDeleteSection from '../Admin/components/ManageApplDeleteSection'; + +const DELETE_PERMISSIONS = [ + 'ROOT_MANAGE_APPL_VIEW_DELETED', + 'ROOT_MANAGE_APPL_DELETE_CONFIRM', + 'ROOT_MANAGE_APPL_DELETE' +]; + +const AdminEliminaDomande = () => { + const permissions = useStoreValue('getPermissions'); + const navigate = useNavigate(); + const hasPermission = intersection(permissions, DELETE_PERMISSIONS).length > 0; + const canViewDeleted = intersection(permissions, ['ROOT_MANAGE_APPL_VIEW_DELETED']).length > 0; + const canDeleteConfirm = intersection(permissions, ['ROOT_MANAGE_APPL_DELETE_CONFIRM']).length > 0; + const canDelete = intersection(permissions, ['ROOT_MANAGE_APPL_DELETE']).length > 0; + + useEffect(() => { + if (!hasPermission) navigate('/admin'); + }, [hasPermission]); + + return
+
+

{__('Elimina Domande', 'gepafin')}

+
+
+
+
+
+ {hasPermission && } +
+} + +export default AdminEliminaDomande; diff --git a/src/pages/AdminLog/index.js b/src/pages/AdminLog/index.js new file mode 100644 index 0000000..69bae14 --- /dev/null +++ b/src/pages/AdminLog/index.js @@ -0,0 +1,244 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { __ } from '@wordpress/i18n'; +import { intersection } from 'ramda'; +import { useNavigate } from 'react-router-dom'; + +// store +import { useStoreValue } from '../../store'; + +// api +import AdminService from '../../service/admin-service'; + +// components +import { Button } from 'primereact/button'; +import { InputText } from 'primereact/inputtext'; +import { Calendar } from 'primereact/calendar'; +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import { Dialog } from 'primereact/dialog'; +import { Toast } from 'primereact/toast'; + +const buildDiffRows = (oldData, newData) => { + const keys = Array.from(new Set([ + ...Object.keys(oldData || {}), + ...Object.keys(newData || {}) + ])); + return keys.map((key) => ({ + key, + old: oldData ? String(oldData[key] ?? '') : '', + new: newData ? String(newData[key] ?? '') : '' + })); +}; + +const AdminLog = () => { + const permissions = useStoreValue('getPermissions'); + const navigate = useNavigate(); + const toast = useRef(null); + const hasPermission = intersection(permissions, ['ROOT_MANAGE_VIEW_LOG']).length > 0; + + const [callId, setCallId] = useState(''); + const [userId, setUserId] = useState(''); + const [dateFrom, setDateFrom] = useState(null); + const [dateTo, setDateTo] = useState(null); + const [items, setItems] = useState(null); + const [totalRecordsNum, setTotalRecordsNum] = useState(0); + const [loading, setLoading] = useState(false); + const [lazyState, setLazyState] = useState({ first: 0, rows: 10, page: 0 }); + + const [detailRow, setDetailRow] = useState(null); + + useEffect(() => { + if (!hasPermission) navigate('/admin'); + }, [hasPermission]); + + const formatDate = (date) => { + if (!date) return undefined; + const d = new Date(date); + const year = d.getFullYear(); + const month = String(d.getMonth() + 1).padStart(2, '0'); + const day = String(d.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; + }; + + const validate = () => { + if ((dateFrom && !dateTo) || (!dateFrom && dateTo)) { + toast.current.show({ + severity: 'warn', + summary: __('Attenzione', 'gepafin'), + detail: __('Inserire sia la data di inizio che la data di fine.', 'gepafin') + }); + return false; + } + if (dateFrom && dateTo && dateFrom > dateTo) { + toast.current.show({ + severity: 'warn', + summary: __('Attenzione', 'gepafin'), + detail: __('La data di inizio non può essere superiore alla data di fine.', 'gepafin') + }); + return false; + } + return true; + }; + + const fetchData = (page = 0, rows = lazyState.rows) => { + if (!validate()) return; + setLoading(true); + + const queryParams = [ + ['page', page + 1], + ['limit', rows] + ]; + if (callId) queryParams.push(['call_id', callId]); + if (userId) queryParams.push(['user_id', userId]); + if (dateFrom) queryParams.push(['date_from', formatDate(dateFrom)]); + if (dateTo) queryParams.push(['date_to', formatDate(dateTo)]); + + AdminService.getAdminLog( + (resp) => { + if (resp && resp.status === 'ok') { + setItems(resp.records || []); + setTotalRecordsNum(resp.totalRecords || 0); + } + setLoading(false); + }, + () => { setLoading(false); }, + queryParams + ); + }; + + const onPage = (event) => { + setLazyState(event); + fetchData(event.page, event.rows); + }; + + const azioniTemplate = (row) => ( +