- implemented root manager functionality;

This commit is contained in:
Vitalii Kiiko
2026-03-25 14:21:31 +01:00
parent 6eea331530
commit cc847b5e48
7 changed files with 389 additions and 11 deletions

View File

@@ -74,6 +74,9 @@ const getBandoLabel = (status) => {
case 'REJECTED':
return __('Respinto', 'gepafin');
case 'DELETED':
return __('Cancellato', 'gepafin');
case 'TECHNICAL_EVALUATION_REJECTED':
return __('Respinto Tec-Fin', 'gepafin');

View File

@@ -72,6 +72,9 @@ const getBandoSeverity = (status) => {
case 'REJECTED':
return 'danger';
case 'DELETED':
return 'danger';
case 'TECHNICAL_EVALUATION_REJECTED':
return 'danger';

View File

@@ -1,10 +1,370 @@
import React from 'react';
import React, { useEffect, useState, useCallback, useRef } from 'react';
import { __ } from '@wordpress/i18n';
import translationStrings from '../../../../translationStringsForComponents';
// api
import ApplicationService from '../../../../service/application-service';
import AdminService from '../../../../service/admin-service';
// helpers
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
// components
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Button } from 'primereact/button';
import { Dialog } from 'primereact/dialog';
import { Tag } from 'primereact/tag';
import { Dropdown } from 'primereact/dropdown';
import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import { Toast } from 'primereact/toast';
const allStatuses = [
'SUBMIT', 'EVALUATION', 'SOCCORSO', 'APPOINTMENT', 'NDG', 'ADMISSIBLE',
'AWAITING_TECHNICAL_EVALUATION', 'TECHNICAL_EVALUATION'
];
const initialDeletedLazyState = {
first: 0,
rows: 10,
page: 0
};
const initialPreDeleteLazyState = {
first: 0,
rows: 5,
page: 0,
sortField: null,
sortOrder: null,
filters: {
id: { value: null, matchMode: 'equals' },
callTitle: { value: null, matchMode: 'contains' },
companyName: { value: null, matchMode: 'contains' },
status: { value: null, matchMode: 'equals' }
}
};
const ManageApplDeleteSection = ({ canViewDeleted, canDeleteConfirm, canDelete }) => {
return <div className="appPageSection">
<h2>{__('Gestione domande eliminate', 'gepafin')}</h2>
</div>
}
const toast = useRef(null);
// --- Table 1: Deleted applications ---
const [deletedLoading, setDeletedLoading] = useState(false);
const [deletedItems, setDeletedItems] = useState(null);
const [deletedTotal, setDeletedTotal] = useState(0);
const [deletedLazyState, setDeletedLazyState] = useState(initialDeletedLazyState);
const [deletedRefreshKey, setDeletedRefreshKey] = useState(0);
const [finalDeleteDialogVisible, setFinalDeleteDialogVisible] = useState(false);
const [finalDeleteAppId, setFinalDeleteAppId] = useState(null);
const [finalDeleteLoading, setFinalDeleteLoading] = useState(false);
// --- Table 2: Pre-delete applications ---
const [preDeleteLoading, setPreDeleteLoading] = useState(false);
const [preDeleteItems, setPreDeleteItems] = useState(null);
const [preDeleteTotal, setPreDeleteTotal] = useState(0);
const [preDeleteLazyState, setPreDeleteLazyState] = useState(initialPreDeleteLazyState);
const [preDeleteRefreshKey, setPreDeleteRefreshKey] = useState(0);
const [preDeleteDialogVisible, setPreDeleteDialogVisible] = useState(false);
const [preDeleteAppId, setPreDeleteAppId] = useState(null);
const [preDeleteActionLoading, setPreDeleteActionLoading] = useState(false);
// ---- Table 1 handlers ----
const getDeletedCallback = (resp) => {
if (resp.status === 'success') {
const { body, totalRecords } = resp.data;
setDeletedTotal(totalRecords);
setDeletedItems(body);
}
setDeletedLoading(false);
};
const errDeletedCallback = () => {
setDeletedLoading(false);
};
const openFinalDeleteDialog = (appId) => {
setFinalDeleteAppId(appId);
setFinalDeleteDialogVisible(true);
};
const hideFinalDeleteDialog = () => {
setFinalDeleteDialogVisible(false);
setFinalDeleteAppId(null);
};
const handleFinalDelete = useCallback(() => {
setFinalDeleteLoading(true);
AdminService.doFinalDelete(
{ application_id: finalDeleteAppId },
() => {
setDeletedRefreshKey(k => k + 1);
setFinalDeleteLoading(false);
hideFinalDeleteDialog();
},
(resp) => {
if (toast.current) {
toast.current.show({ severity: 'error', summary: '', detail: resp.detail });
}
setFinalDeleteLoading(false);
hideFinalDeleteDialog();
}
);
}, [finalDeleteAppId]);
const deletedActionsBodyTemplate = (rowData) => (
<div className="appPageSection__tableActions lessGap">
<Button
severity="danger"
onClick={() => openFinalDeleteDialog(rowData.id)}
label={__('Conferma eliminazione', 'gepafin')}
icon="pi pi-trash"
size="small"
iconPos="right"/>
</div>
);
const finalDeleteDialogFooter = (
<div>
<Button label={__('Annulla', 'gepafin')} icon="pi pi-times" outlined onClick={hideFinalDeleteDialog}
disabled={finalDeleteLoading}/>
<Button label={__('Conferma', 'gepafin')} icon="pi pi-check" severity="danger"
onClick={handleFinalDelete} disabled={finalDeleteLoading} loading={finalDeleteLoading}/>
</div>
);
useEffect(() => {
if (!canViewDeleted) return;
setDeletedLoading(true);
AdminService.getDeletedAppl(getDeletedCallback, errDeletedCallback, {
page: deletedLazyState.page + 1,
page_size: deletedLazyState.rows
});
}, [deletedLazyState, deletedRefreshKey, canViewDeleted]);
// ---- Table 2 handlers ----
const getPreDeletePaginationQuery = useCallback(
() => getQueryParamsForPaginatedEndpoint(preDeleteLazyState, allStatuses, 'id'),
[preDeleteLazyState]
);
const getPreDeleteCallback = (resp) => {
if (resp.status === 'SUCCESS') {
const { body, totalRecords } = resp.data;
setPreDeleteTotal(totalRecords);
setPreDeleteItems(body);
}
setPreDeleteLoading(false);
};
const errPreDeleteCallback = () => {
setPreDeleteLoading(false);
};
const openPreDeleteDialog = (appId) => {
setPreDeleteAppId(appId);
setPreDeleteDialogVisible(true);
};
const hidePreDeleteDialog = () => {
setPreDeleteDialogVisible(false);
setPreDeleteAppId(null);
};
const handlePreDelete = useCallback(() => {
setPreDeleteActionLoading(true);
AdminService.doPreDelete(
{ application_id: preDeleteAppId },
() => {
setPreDeleteRefreshKey(k => k + 1);
setDeletedRefreshKey(k => k + 1);
setPreDeleteActionLoading(false);
hidePreDeleteDialog();
},
(resp) => {
if (toast.current) {
toast.current.show({ severity: 'error', summary: '', detail: resp.detail });
}
setPreDeleteActionLoading(false);
hidePreDeleteDialog();
}
);
}, [preDeleteAppId]);
const preDeleteActionsBodyTemplate = (rowData) => (
<div className="appPageSection__tableActions lessGap">
<Button
severity="danger"
onClick={() => openPreDeleteDialog(rowData.id)}
label={__('Cancella', 'gepafin')}
icon="pi pi-trash"
size="small"
iconPos="right"/>
</div>
);
const statusBodyTemplate = (rowData) => <ProperBandoLabel status={rowData.status}/>;
const statusItemTemplate = (option) => (
<Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)}/>
);
const statusFilterTemplate = (options) => (
<Dropdown
value={options.value}
options={allStatuses}
valueTemplate={getBandoLabel(options.value)}
onChange={(e) => {
options.filterCallback(e.value, options.index);
const filters = { ...preDeleteLazyState.filters };
if (e.value) {
filters['status'] = { value: e.value, matchMode: 'equals' };
} else {
delete filters['status'];
}
setPreDeleteLazyState({ ...preDeleteLazyState, filters, first: 0 });
}}
itemTemplate={statusItemTemplate}
placeholder={translationStrings.selectOneLabel}
className="p-column-filter"/>
);
const preDeleteDialogFooter = (
<div>
<Button label={__('Annulla', 'gepafin')} icon="pi pi-times" outlined onClick={hidePreDeleteDialog}
disabled={preDeleteActionLoading}/>
<Button label={__('Conferma', 'gepafin')} icon="pi pi-check" severity="danger"
onClick={handlePreDelete} disabled={preDeleteActionLoading} loading={preDeleteActionLoading}/>
</div>
);
useEffect(() => {
if (!canDelete) return;
setPreDeleteLoading(true);
const paginationQuery = getPreDeletePaginationQuery();
ApplicationService.getApplicationsPaginated(paginationQuery, getPreDeleteCallback, errPreDeleteCallback);
}, [preDeleteLazyState, preDeleteRefreshKey, canDelete]);
return (
<div className="appPageSection">
<h2>{__('Gestione domande eliminate', 'gepafin')}</h2>
<Toast ref={toast}/>
{canViewDeleted && (
<>
<h3>{__('Domande eliminate', 'gepafin')}</h3>
<div className="appPageSection__table">
<DataTable
value={deletedItems} stripedRows showGridlines
lazy dataKey="id" paginator
first={deletedLazyState.first}
rows={deletedLazyState.rows}
totalRecords={deletedTotal}
onPage={(e) => setDeletedLazyState(e)}
loading={deletedLoading}
header={
<div className="flex justify-content-between">
<Button type="button" icon="pi pi-refresh" label={__('Aggiorna', 'gepafin')}
outlined onClick={() => setDeletedLazyState({ ...deletedLazyState })}/>
</div>
}
emptyMessage={translationStrings.emptyMessage}>
<Column field="id" header={__('ID domanda', 'gepafin')}
body={(rowData) => rowData.id}
style={{ minWidth: '6rem' }}/>
<Column field="call_name" header={__('Bando', 'gepafin')}
style={{ minWidth: '10rem' }}/>
<Column field="company_name" header={__('Azienda Beneficiaria', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column field="status" header={__('Stato', 'gepafin')}
style={{ minWidth: '8rem' }}
body={statusBodyTemplate}/>
{canDeleteConfirm && (
<Column header={__('Azioni', 'gepafin')}
body={deletedActionsBodyTemplate}/>
)}
</DataTable>
</div>
<Dialog
visible={finalDeleteDialogVisible}
modal
header={__('Conferma eliminazione definitiva', 'gepafin')}
footer={finalDeleteDialogFooter}
style={{ maxWidth: '500px', width: '100%' }}
onHide={hideFinalDeleteDialog}>
<p>{__('Sei sicuro di voler eliminare definitivamente questa domanda? L\'operazione non è reversibile.', 'gepafin')}</p>
</Dialog>
</>
)}
{canDelete && (
<>
<div className="appPage__spacer"></div>
<h3>{__('Cancella domanda', 'gepafin')}</h3>
<div className="appPageSection__table">
<DataTable
value={preDeleteItems} stripedRows showGridlines
lazy filterDisplay="menu" dataKey="id" paginator
first={preDeleteLazyState.first}
rows={preDeleteLazyState.rows}
totalRecords={preDeleteTotal}
onPage={(e) => setPreDeleteLazyState(e)}
onSort={(e) => { e['first'] = 0; e['page'] = 0; setPreDeleteLazyState(e); }}
sortField={preDeleteLazyState.sortField}
sortOrder={preDeleteLazyState.sortOrder}
onFilter={(e) => { e['first'] = 0; e['page'] = 0; setPreDeleteLazyState(e); }}
filters={preDeleteLazyState.filters}
loading={preDeleteLoading}
header={
<div className="flex justify-content-between">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')}
outlined onClick={() => setPreDeleteLazyState(initialPreDeleteLazyState)}/>
</div>
}
emptyMessage={translationStrings.emptyMessage}>
<Column field="id" header={__('ID domanda', 'gepafin')}
sortable
filterField="id" filter
filterMatchModeOptions={translationStrings.numberFilterOptions}
filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '6rem' }}/>
<Column field="callTitle" header={__('Bando', 'gepafin')}
filterField="callTitle" filter
filterMatchModeOptions={translationStrings.textFilterOptions}
filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '10rem' }}/>
<Column field="companyName" header={__('Azienda Beneficiaria', 'gepafin')}
filterField="companyName" filter
filterMatchModeOptions={translationStrings.textFilterOptions}
filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column field="status" header={__('Stato', 'gepafin')}
filterElement={statusFilterTemplate} filter
filterMatchModeOptions={translationStrings.statusFilterOptions}
style={{ minWidth: '8rem' }}
body={statusBodyTemplate}/>
<Column header={__('Azioni', 'gepafin')}
body={preDeleteActionsBodyTemplate}/>
</DataTable>
</div>
<Dialog
visible={preDeleteDialogVisible}
modal
header={__('Conferma cancellazione', 'gepafin')}
footer={preDeleteDialogFooter}
style={{ maxWidth: '500px', width: '100%' }}
onHide={hidePreDeleteDialog}>
<p>{__('Sei sicuro di voler cancellare questa domanda?', 'gepafin')}</p>
</Dialog>
</>
)}
</div>
);
};
export default ManageApplDeleteSection;

View File

@@ -117,7 +117,7 @@ const ManageApplStatusSection = () => {
hideStatusDialog();
}
const errCallback = () => {
const errCallback = (resp) => {
if (toast.current) {
toast.current.show({
severity: 'error',

View File

@@ -822,7 +822,7 @@ const DomandaEditInstructorManager = () => {
storeSet('unsetAsyncRequest');
}
const doCreateAppointment = () => {
/*const doCreateAppointment = () => {
setAppointmentData({
title: '',
text: '',
@@ -830,7 +830,7 @@ const DomandaEditInstructorManager = () => {
amount: 0
});
setIsVisibleAppointmentDialog(true);
}
}*/
const setAppointmentFieldValue = (name, value) => {
const newData = wrap(appointmentData).set(name, value).value();

View File

@@ -822,7 +822,7 @@ const DomandaEditPreInstructor = () => {
storeSet('unsetAsyncRequest');
}
const doCreateAppointment = () => {
/*const doCreateAppointment = () => {
setAppointmentData({
title: '',
text: '',
@@ -830,7 +830,7 @@ const DomandaEditPreInstructor = () => {
amount: 0
});
setIsVisibleAppointmentDialog(true);
}
}*/
const setAppointmentFieldValue = (name, value) => {
const newData = wrap(appointmentData).set(name, value).value();

View File

@@ -21,10 +21,22 @@ export default class AdminService {
};
static doSendPec = (body, callback, errCallback, queryParams) => {
NetworkService.post(`${API_ADMIN_BASE_URL}/send-pec`, body, callback, errCallback, queryParams);
NetworkService.postMultiPart(`${API_ADMIN_BASE_URL}/send-pec`, body, callback, errCallback, queryParams);
};
static getAdminLog = (callback, errCallback, queryParams) => {
NetworkService.get(`${API_ADMIN_BASE_URL}/admin-operations`, callback, errCallback, queryParams);
};
static getDeletedAppl = (callback, errCallback, queryParams) => {
NetworkService.get(`${API_ADMIN_BASE_URL}/status-deleted`, callback, errCallback, queryParams);
};
static doPreDelete = (body, callback, errCallback, queryParams) => {
NetworkService.delete(`${API_ADMIN_BASE_URL}/pre-delete`, body, callback, errCallback, queryParams);
};
static doFinalDelete = (body, callback, errCallback, queryParams) => {
NetworkService.delete(`${API_ADMIN_BASE_URL}/def-delete`, body, callback, errCallback, queryParams);
};
}