Merge pull request #50 from Kitzanos/master-sync-21-05-2025

Master sync 21 05 2025
This commit is contained in:
Vitalii Kiiko
2025-05-21 14:34:29 +02:00
committed by GitHub
28 changed files with 736 additions and 228 deletions

View File

@@ -57,6 +57,7 @@
}, },
"scripts": { "scripts": {
"start": "GENERATE_SOURCEMAP=false react-scripts start", "start": "GENERATE_SOURCEMAP=false react-scripts start",
"start2": "react-scripts start",
"start:dev": "cp environments/dev/* public/loaded-files && rm public/loaded-files/dev.env && cp environments/dev/dev.env .env && PORT=8000 react-scripts start --mode development", "start:dev": "cp environments/dev/* public/loaded-files && rm public/loaded-files/dev.env && cp environments/dev/dev.env .env && PORT=8000 react-scripts start --mode development",
"start:prod": "cp environments/prod/* public/loaded-files && rm public/loaded-files/prod.env && cp environments/prod/prod.env .env && react-scripts start --mode production", "start:prod": "cp environments/prod/* public/loaded-files && rm public/loaded-files/prod.env && cp environments/prod/prod.env .env && react-scripts start --mode production",
"build": "react-scripts build", "build": "react-scripts build",

View File

@@ -295,8 +295,9 @@
.appPageSection__message { .appPageSection__message {
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%;
gap: 20px; gap: 20px;
background: rgba(255, 242, 226, 0.7); background: rgba(242, 242, 242, 0.7);
border-style: solid; border-style: solid;
border-width: 0 0 0 6px; border-width: 0 0 0 6px;
padding: 1.25rem 1.75rem; padding: 1.25rem 1.75rem;
@@ -409,10 +410,18 @@
.appPageSection__actions { .appPageSection__actions {
display: flex; display: flex;
gap: 24px; gap: 24px;
padding: 24px 0 48px; padding: 24px 0 0;
flex-wrap: wrap; flex-wrap: wrap;
} }
.appPageSection .appPageSection__actions:last-of-type {
padding-bottom: 48px;
}
.appPageSection__actions:empty {
display: none;
}
.appPageSection__tableActions { .appPageSection__tableActions {
display: flex; display: flex;
gap: 10px; gap: 10px;

View File

@@ -13,6 +13,7 @@ import getPropeMimeLabels from '../../../../helpers/getPropeMimeLabels';
import { FileUpload } from 'primereact/fileupload'; import { FileUpload } from 'primereact/fileupload';
import { Tag } from 'primereact/tag'; import { Tag } from 'primereact/tag';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { Messages } from 'primereact/messages';
import { defaultMaxFileSize, mimeTypes } from '../../../../configData'; import { defaultMaxFileSize, mimeTypes } from '../../../../configData';
import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText'; import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText';
@@ -47,24 +48,48 @@ const Fileupload = ({
const [acceptFormats, setAcceptFormats] = useState(''); const [acceptFormats, setAcceptFormats] = useState('');
const [formatsForInput, setFormatsForInput] = useState(''); const [formatsForInput, setFormatsForInput] = useState('');
const inputRef = useRef(); const inputRef = useRef();
const messagesRef = useRef(null);
const customBase64Uploader = (event) => { const customBase64Uploader = (event) => {
const formData = new FormData() const formData = new FormData()
const filesToUpload = [];
const uploadedFiles = inputRef.current ? inputRef.current.getUploadedFiles() : [];
const currentFiles = stateFieldData;
for (const file of event.files) { for (const file of event.files) {
const isDuplicate = [...uploadedFiles, ...currentFiles].some(
(uploadedFile)=>uploadedFile.name === file.name
);
if(isDuplicate){
messagesRef.current.show({
severity: 'error',
summary: __('Attenzione', 'gepafin'),
detail: `Il file con nome "${file.name}" è già stato caricato.`,
life: 10000
});
} else {
formData.append('file', file) formData.append('file', file)
filesToUpload.push(file);
} }
}
if(filesToUpload.length > 0 ){
FileUploadService.uploadFile(sourceId, formData, callback, errorCallback, [ FileUploadService.uploadFile(sourceId, formData, callback, errorCallback, [
['documentType', doctype.toUpperCase()], ['documentType', doctype.toUpperCase()],
['sourceType', source.toUpperCase()] ['sourceType', source.toUpperCase()]
]); ]);
}
}; };
const callback = (data) => { const callback = (data, uploadedFiles) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
setStateFieldData(data.data); setStateFieldData(prevState => [...prevState, ...data.data]);
const uploadedFiles = inputRef.current.getUploadedFiles(); const currentUploadedFiles = inputRef.current.getUploadedFiles() || [];
setDataFn(fieldName, [...uploadedFiles, ...data.data], { shouldValidate: true });
inputRef.current.setFiles([]); inputRef.current.setFiles([]);
setDataFn(fieldName, [...currentUploadedFiles, ...data.data], { shouldValidate: true });
saveFormCallback(); saveFormCallback();
} }
} }
@@ -204,6 +229,7 @@ const Fileupload = ({
return ( return (
sourceId || sourceId === 0 sourceId || sourceId === 0
? <> ? <>
<Messages ref={messagesRef} />
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}> <label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? {label}{config.required || config.isRequired ?
<span className="appForm__field--required">*</span> : null} <span className="appForm__field--required">*</span> : null}

View File

@@ -13,6 +13,8 @@ import { FileUpload } from 'primereact/fileupload';
import { Tag } from 'primereact/tag'; import { Tag } from 'primereact/tag';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { head, isEmpty } from 'ramda'; import { head, isEmpty } from 'ramda';
import { Messages } from 'primereact/messages';
import { defaultMaxFileSize, mimeTypes } from '../../../../configData'; import { defaultMaxFileSize, mimeTypes } from '../../../../configData';
import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText'; import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText';
@@ -42,24 +44,50 @@ const FileuploadAsync = ({
const [acceptFormats, setAcceptFormats] = useState(''); const [acceptFormats, setAcceptFormats] = useState('');
const [formatsForInput, setFormatsForInput] = useState(''); const [formatsForInput, setFormatsForInput] = useState('');
const inputRef = useRef(); const inputRef = useRef();
const messagesRef = useRef(null);
const customBase64Uploader = (event) => { const customBase64Uploader = (event) => {
const formData = new FormData() const formData = new FormData()
const filesToUpload = [];
const uploadedFiles = inputRef.current ? inputRef.current.getUploadedFiles() : [];
const currentFiles = stateFieldData;
for (const file of event.files) { for (const file of event.files) {
const isDuplicate = [...uploadedFiles, ...currentFiles].some(
(uploadedFile)=>uploadedFile.name === file.name
);
if(isDuplicate){
messagesRef.current.show({
severity: 'error',
summary: __('Attenzione', 'gepafin'),
detail: `Il file con nome "${file.name}" è già stato caricato.`,
life: 10000
});
} else {
formData.append('file', file) formData.append('file', file)
filesToUpload.push(file);
} }
}
if(filesToUpload.length > 0){
FileUploadService.uploadFile(sourceId, formData, callback, errorCallback, [ FileUploadService.uploadFile(sourceId, formData, callback, errorCallback, [
['documentType', doctype.toUpperCase()], ['documentType', doctype.toUpperCase()],
['sourceType', source.toUpperCase()] ['sourceType', source.toUpperCase()]
]); ]);
}
}; };
const callback = (data) => { const callback = (data, uploadedFiles) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
setStateFieldData(data.data); setStateFieldData(prevState => [...prevState, ...data.data]);
const uploadedFiles = inputRef.current.getUploadedFiles(); const currentUploadedFiles = inputRef.current.getUploadedFiles() || [];
setDataFn(fieldName, [...uploadedFiles, ...data.data], { shouldValidate: true });
inputRef.current.setUploadedFiles([...currentUploadedFiles, ...data.data]);
inputRef.current.setFiles([]); inputRef.current.setFiles([]);
setDataFn(fieldName, [...currentUploadedFiles, ...data.data], { shouldValidate: true });
} }
} }
@@ -186,6 +214,7 @@ const FileuploadAsync = ({
return ( return (
sourceId && sourceId !== 0 sourceId && sourceId !== 0
? <> ? <>
<Messages ref={messagesRef} />
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}> <label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null} {label}{config.required ? '*' : null}
{acceptFormats ? ' (' + getPropeMimeLabels(accept) + ')' : null} {acceptFormats ? ' (' + getPropeMimeLabels(accept) + ')' : null}

View File

@@ -263,3 +263,10 @@ export const classificationType = [
'idTipoprotocollo': 3 'idTipoprotocollo': 3
} }
]; ];
export const resendEmailLabelsByType = {
APPLICATION_AMENDMENT_REQUESTED: 'Invia email (nuovo soccorso)',
APPLICATION_AMENDMENT_REMINDER: 'Invia email (sollecito)',
APPLICATION_ADMISSIBLE: 'Invia email (ammisibile)',
APPLICATION_REJECTED: 'Invia email (respinto)'
}

View File

@@ -115,15 +115,19 @@ const AddCompany = () => {
const checkVatCallback = (data) => { const checkVatCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
const version = data.data.version;
const resp = data.data.vatCheckResponse.data; const resp = data.data.vatCheckResponse.data;
if (!isEmpty(resp)) { if (!isEmpty(resp)) {
let formData = {};
if (version === 'V2') {
const firstItem = resp[0]; const firstItem = resp[0];
const { const {
taxCode, vatCode, address, companyName, pec taxCode, vatCode, address, companyName, pec
} = firstItem; } = firstItem;
const { streetName, zipCode, town } = address?.registeredOffice; const { streetName, zipCode, town } = address?.registeredOffice;
const formData = { formData = {
cap: zipCode, cap: zipCode,
pec, pec,
email: pec, email: pec,
@@ -133,6 +137,22 @@ const AddCompany = () => {
vatNumber: vatCode, vatNumber: vatCode,
companyName companyName
} }
} else {
const {
cap, cf, denominazione, piva, indirizzo, comune, dettaglio: { pec }
} = resp;
formData = {
cap,
pec,
email: pec,
city: comune,
codiceFiscale: cf ? cf : piva,
address: indirizzo,
vatNumber: piva,
companyName: denominazione
}
}
Object.keys(formData).map(k => setValue(k, formData[k])); Object.keys(formData).map(k => setValue(k, formData[k]));
setVatCheckResponse(data.data.vatCheckResponse); setVatCheckResponse(data.data.vatCheckResponse);
} }

View File

@@ -5,6 +5,10 @@ import { uniq } from 'ramda';
// api // api
import BandoService from '../../../../service/bando-service'; import BandoService from '../../../../service/bando-service';
// tools
import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString';
import getTimeFromISOstring from '../../../../helpers/getTimeFromISOstring';
// components // components
import { FilterMatchMode, FilterOperator } from 'primereact/api'; import { FilterMatchMode, FilterOperator } from 'primereact/api';
import { DataTable } from 'primereact/datatable'; import { DataTable } from 'primereact/datatable';
@@ -13,10 +17,8 @@ import { Button } from 'primereact/button';
import { Calendar } from 'primereact/calendar'; import { Calendar } from 'primereact/calendar';
import ProperBandoLabel from '../../../../components/ProperBandoLabel'; import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import translationStrings from '../../../../translationStringsForComponents';
import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString';
import getTimeFromISOstring from '../../../../helpers/getTimeFromISOstring';
import translationStrings from '../../../../translationStringsForComponents';
const LatestBandiTable = () => { const LatestBandiTable = () => {
const [items, setItems] = useState(null); const [items, setItems] = useState(null);

View File

@@ -4,8 +4,12 @@ import { Link } from 'react-router-dom';
import translationStrings from '../../../../translationStringsForComponents'; import translationStrings from '../../../../translationStringsForComponents';
// store
import { storeGet, useStoreValue } from '../../../../store';
// api // api
import BandoService from '../../../../service/bando-service'; import BandoService from '../../../../service/bando-service';
import PreferredBandoService from '../../../../service/preferred-bando-service';
// tools // tools
import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString'; import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString';
@@ -14,6 +18,7 @@ import getFormattedDateString from '../../../../helpers/getFormattedDateString';
import getBandoLabel from '../../../../helpers/getBandoLabel'; import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity'; import getBandoSeverity from '../../../../helpers/getBandoSeverity';
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint'; import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
// components // components
import { DataTable } from 'primereact/datatable'; import { DataTable } from 'primereact/datatable';
@@ -24,11 +29,9 @@ import { Dropdown } from 'primereact/dropdown';
import { Tag } from 'primereact/tag'; import { Tag } from 'primereact/tag';
import { Calendar } from 'primereact/calendar'; import { Calendar } from 'primereact/calendar';
import { isNil } from 'ramda'; import { isNil } from 'ramda';
import { storeGet } from '../../../../store';
import PreferredBandoService from '../../../../service/preferred-bando-service';
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
const LatestBandiBeneficiarioTableAsync = () => { const LatestBandiBeneficiarioTableAsync = () => {
const chosenCompanyId = useStoreValue('chosenCompanyId');
const [localAsyncRequest, setLocalAsyncRequest] = useState(false); const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [items, setItems] = useState(null); const [items, setItems] = useState(null);
const [totalRecordsNum, setTotalRecordsNum] = useState(0); const [totalRecordsNum, setTotalRecordsNum] = useState(0);
@@ -197,9 +200,19 @@ const LatestBandiBeneficiarioTableAsync = () => {
useEffect(() => { useEffect(() => {
setLocalAsyncRequest(true); setLocalAsyncRequest(true);
const paginationQuery = getPaginationQuery(); const paginationQuery = getPaginationQuery();
const role = storeGet('getRole');
BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks); if (role === 'ROLE_CONFIDI') {
}, [lazyState]); BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks, [
['companyId', chosenCompanyId],
['onlyConfidiCall', true]
]);
} else {
BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks, [
['companyId', chosenCompanyId]
]);
}
}, [lazyState, chosenCompanyId]);
return ( return (
<div className="appPageSection__table"> <div className="appPageSection__table">

View File

@@ -33,7 +33,7 @@ const LatestBandiTable = () => {
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
const role = storeGet('getRole') const role = storeGet('getRole');
if (role === 'ROLE_CONFIDI') { if (role === 'ROLE_CONFIDI') {
BandoService.getBandi(getCallback, errGetCallbacks, [ BandoService.getBandi(getCallback, errGetCallbacks, [

View File

@@ -31,7 +31,6 @@ import { ProgressBar } from 'primereact/progressbar';
const MyLatestSubmissionsTableAsync = () => { const MyLatestSubmissionsTableAsync = () => {
const chosenCompanyId = useStoreValue('chosenCompanyId'); const chosenCompanyId = useStoreValue('chosenCompanyId');
const companies = useStoreValue('companies');
const [localAsyncRequest, setLocalAsyncRequest] = useState(false); const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [items, setItems] = useState(null); const [items, setItems] = useState(null);
const [totalRecordsNum, setTotalRecordsNum] = useState(0); const [totalRecordsNum, setTotalRecordsNum] = useState(0);
@@ -194,13 +193,15 @@ const MyLatestSubmissionsTableAsync = () => {
}; };
useEffect(() => { useEffect(() => {
if (chosenCompanyId && chosenCompanyId !== 0) {
setLocalAsyncRequest(true); setLocalAsyncRequest(true);
const paginationQuery = getPaginationQuery(); const paginationQuery = getPaginationQuery();
ApplicationService.getApplicationsPaginated(paginationQuery, getCallback, errGetCallbacks, [ ApplicationService.getApplicationsPaginated(paginationQuery, getCallback, errGetCallbacks, [
['companyId', chosenCompanyId] ['companyId', chosenCompanyId]
]); ]);
}, [lazyState, chosenCompanyId, companies]); }
}, [lazyState, chosenCompanyId]);
return ( return (
<div className="appPageSection__table"> <div className="appPageSection__table">

View File

@@ -14,7 +14,7 @@ import DashboardService from '../../service/dashboard-service';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import ErrorBoundary from '../../components/ErrorBoundary'; import ErrorBoundary from '../../components/ErrorBoundary';
import MyLatestSubmissionsTableAsync from '../DashboardBeneficiario/components/MyLatestSubmissionsTableAsync'; import MyLatestSubmissionsTableAsync from '../DashboardBeneficiario/components/MyLatestSubmissionsTableAsync';
import LatestBandiTable from '../DashboardBeneficiario/components/LatestBandiTable'; import LatestBandiBeneficiarioTableAsync from '../DashboardBeneficiario/components/LatestBandiBeneficiarioTableAsync';
const DashboardBeneficiarioConfidi = () => { const DashboardBeneficiarioConfidi = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -108,7 +108,7 @@ const DashboardBeneficiarioConfidi = () => {
<div className="appPageSection"> <div className="appPageSection">
<h2>{__('Bandi disponibili', 'gepafin')}</h2> <h2>{__('Bandi disponibili', 'gepafin')}</h2>
<ErrorBoundary><LatestBandiTable/></ErrorBoundary> <ErrorBoundary><LatestBandiBeneficiarioTableAsync/></ErrorBoundary>
</div> </div>
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>

View File

@@ -26,6 +26,7 @@ import { Calendar } from 'primereact/calendar';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint'; import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
const APP_HUB_ID = process.env.REACT_APP_HUB_ID; const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
@@ -73,7 +74,8 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
const getCallback = (resp) => { const getCallback = (resp) => {
if (resp.status === 'SUCCESS') { if (resp.status === 'SUCCESS') {
const { body, totalRecords, const {
body, totalRecords,
//currentPage, totalPages, pageSize //currentPage, totalPages, pageSize
} = resp.data; } = resp.data;
setTotalRecordsNum(totalRecords); setTotalRecordsNum(totalRecords);
@@ -114,7 +116,8 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
} }
setLazyState({ ...lazyState, filters, first: 0 }); setLazyState({ ...lazyState, filters, first: 0 });
}} }}
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter"/>; itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel}
className="p-column-filter"/>;
}; };
const dateFilterTemplate = (options) => { const dateFilterTemplate = (options) => {
@@ -130,22 +133,43 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
return getFormattedDateString(rowData.evaluationEndDate); return getFormattedDateString(rowData.evaluationEndDate);
}; };
const updateRowData = useCallback((id, updateResponse) => {
const newItems = items.map((o) => {
if (o.id === id) {
o.emailSendResponse = updateResponse;
}
return o;
})
setItems(newItems);
}, [items]);
const actionsBodyTemplate = (rowData) => { const actionsBodyTemplate = (rowData) => {
if (rowData.status === 'AWAITING') { if (rowData.status === 'AWAITING') {
return <Button return <div className="appPageSection__tableActions lessGap">
<Button
severity="info" severity="info"
onClick={() => handleInitiateEvaluation(rowData.id)} onClick={() => handleInitiateEvaluation(rowData.id)}
label={__('Valuta', 'gepafin')} label={__('Valuta', 'gepafin')}
icon="pi pi-eye" icon="pi pi-eye"
size="small" size="small"
iconPos="right"/> iconPos="right"/>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
} else { } else {
const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId
? __('Valuta', 'gepafin') ? __('Valuta', 'gepafin')
: __('Mostra', 'gepafin'); : __('Mostra', 'gepafin');
return <Link to={`/mie-domande/${rowData.applicationId}`}> return <div className="appPageSection__tableActions lessGap">
<Link
to={`/mie-domande/${rowData.applicationId}`}>
<Button severity="info" label={label} icon="pi pi-eye" size="small" iconPos="right"/> <Button severity="info" label={label} icon="pi pi-eye" size="small" iconPos="right"/>
</Link> </Link>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
} }
} }
@@ -192,7 +216,8 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
const renderHeader = () => { const renderHeader = () => {
return ( return (
<div className="flex justify-content-between"> <div className="flex justify-content-between">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined onClick={clearFilter} /> <Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
onClick={clearFilter}/>
</div> </div>
); );
}; };
@@ -268,7 +293,7 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
style={{ minWidth: '7rem' }} body={statusBodyTemplate} style={{ minWidth: '7rem' }} body={statusBodyTemplate}
filter filter
filterMatchModeOptions={translationStrings.statusFilterOptions} filterMatchModeOptions={translationStrings.statusFilterOptions}
filterElement={statusFilterTemplate} /> filterElement={statusFilterTemplate}/>
<Column header={__('Azioni', 'gepafin')} <Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/> body={actionsBodyTemplate}/>
</DataTable> </DataTable>

View File

@@ -15,6 +15,7 @@ import { useStoreValue } from '../../../../store';
import getFormattedDateString from '../../../../helpers/getFormattedDateString'; import getFormattedDateString from '../../../../helpers/getFormattedDateString';
import getBandoLabel from '../../../../helpers/getBandoLabel'; import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity'; import getBandoSeverity from '../../../../helpers/getBandoSeverity';
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
// components // components
import { DataTable } from 'primereact/datatable'; import { DataTable } from 'primereact/datatable';
@@ -25,7 +26,7 @@ import { Tag } from 'primereact/tag';
import { Calendar } from 'primereact/calendar'; import { Calendar } from 'primereact/calendar';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint'; import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
const APP_HUB_ID = process.env.REACT_APP_HUB_ID; const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
@@ -73,7 +74,8 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
const getCallback = (resp) => { const getCallback = (resp) => {
if (resp.status === 'SUCCESS') { if (resp.status === 'SUCCESS') {
const { body, totalRecords, const {
body, totalRecords,
//currentPage, totalPages, pageSize //currentPage, totalPages, pageSize
} = resp.data; } = resp.data;
setTotalRecordsNum(totalRecords); setTotalRecordsNum(totalRecords);
@@ -114,7 +116,8 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
} }
setLazyState({ ...lazyState, filters, first: 0 }); setLazyState({ ...lazyState, filters, first: 0 });
}} }}
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter"/>; itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel}
className="p-column-filter"/>;
}; };
const dateFilterTemplate = (options) => { const dateFilterTemplate = (options) => {
@@ -130,22 +133,48 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
return getFormattedDateString(rowData.evaluationEndDate); return getFormattedDateString(rowData.evaluationEndDate);
}; };
const updateRowData = useCallback((id, updateResponse) => {
const newItems = items.map((o) => {
if (o.id === id) {
o.emailSendResponse = updateResponse;
}
return o;
})
setItems(newItems);
}, [items]);
const actionsBodyTemplate = (rowData) => { const actionsBodyTemplate = (rowData) => {
if (rowData.status === 'AWAITING') { if (rowData.status === 'AWAITING') {
return <Button return <div className="appPageSection__tableActions lessGap">
<Button
severity="info" severity="info"
onClick={() => handleInitiateEvaluation(rowData.id)} onClick={() => handleInitiateEvaluation(rowData.id)}
label={__('Valuta', 'gepafin')} label={__('Valuta', 'gepafin')}
icon="pi pi-eye" icon="pi pi-eye"
size="small" size="small"
iconPos="right"/> iconPos="right"/>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
} else { } else {
const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId
? __('Valuta', 'gepafin') ? __('Valuta', 'gepafin')
: __('Mostra', 'gepafin'); : __('Mostra', 'gepafin');
return <Link to={`/domande/${rowData.applicationId}`}> return (
<div className="appPageSection__tableActions lessGap">
<Link to={`/domande/${rowData.applicationId}`}>
<Button severity="info" label={label} icon="pi pi-eye" size="small" iconPos="right"/> <Button severity="info" label={label} icon="pi pi-eye" size="small" iconPos="right"/>
</Link> </Link>
<Link to={`/domande/${rowData.applicationId}/preview`}>
<Button severity="info" label={__('Anteprima', 'gepafin')} icon="pi pi-eye" size="small"
iconPos="right"/>
</Link>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
)
} }
} }
@@ -192,7 +221,8 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
const renderHeader = () => { const renderHeader = () => {
return ( return (
<div className="flex justify-content-between"> <div className="flex justify-content-between">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined onClick={clearFilter} /> <Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
onClick={clearFilter}/>
</div> </div>
); );
}; };
@@ -268,7 +298,7 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
style={{ minWidth: '7rem' }} body={statusBodyTemplate} style={{ minWidth: '7rem' }} body={statusBodyTemplate}
filter filter
filterMatchModeOptions={translationStrings.statusFilterOptions} filterMatchModeOptions={translationStrings.statusFilterOptions}
filterElement={statusFilterTemplate} /> filterElement={statusFilterTemplate}/>
<Column header={__('Azioni', 'gepafin')} <Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/> body={actionsBodyTemplate}/>
</DataTable> </DataTable>

View File

@@ -52,6 +52,8 @@ import RepeaterFields from '../DomandaEditPreInstructor/components/RepeaterField
import ApplicationInfo from '../DomandaEditPreInstructor/components/ApplicationInfo'; import ApplicationInfo from '../DomandaEditPreInstructor/components/ApplicationInfo';
import ApplicationDownloadFiles from '../DomandaEditPreInstructor/components/ApplicationDownloadFiles'; import ApplicationDownloadFiles from '../DomandaEditPreInstructor/components/ApplicationDownloadFiles';
import FormField from '../../components/FormField'; import FormField from '../../components/FormField';
import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
import EvaluationReAdmit from '../DomandaEditPreInstructor/components/EvaluationReAdmit';
const APP_EVALUATION_FLOW_ID = process.env.REACT_APP_EVALUATION_FLOW_ID; const APP_EVALUATION_FLOW_ID = process.env.REACT_APP_EVALUATION_FLOW_ID;
const APP_HUB_ID = process.env.REACT_APP_HUB_ID; const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
@@ -85,6 +87,7 @@ const DomandaEditInstructorManager = () => {
const [formData, setFormData] = useState([]); const [formData, setFormData] = useState([]);
const [formId, setFormId] = useState(0); const [formId, setFormId] = useState(0);
const [formInitialData, setFormInitialData] = useState(null); const [formInitialData, setFormInitialData] = useState(null);
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
const { const {
control, control,
handleSubmit, handleSubmit,
@@ -785,6 +788,11 @@ const DomandaEditInstructorManager = () => {
} }
} }
const updateStatusOfAppl = useCallback((newStatus) => {
const newData = wrap(data).set(['applicationStatus'], newStatus).value();
setData(newData);
}, [data]);
const actionBtns = () => { const actionBtns = () => {
return <div className="appPageSection__actions"> return <div className="appPageSection__actions">
{['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus) {['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus)
@@ -873,6 +881,29 @@ const DomandaEditInstructorManager = () => {
</div> </div>
} }
const auxActions = () => {
return <div className="appPageSection__actions">
<SoccorsoResendEmails
emailsData={emailSendResponse}
setDataEmailsSoccorso={updateEmailSendResponses}/>
<EvaluationReAdmit id={data.applicationId} status={data.applicationStatus} statusUpdateFn={updateStatusOfAppl}/>
</div>
}
const updateEmailSendResponses = useCallback((newEmailData) => {
const newData = wrap(data).set(['emailSendResponse'], newEmailData).value();
setData(newData);
}, [data]);
const motivationMsg = () => {
return data.motivation && !isEmpty(data.motivation) && ['REJECTED', 'APPROVED'].includes(data.applicationStatus)
? <div className="appPageSection__message info">
<i className="pi pi-info-circle"></i>
<span className="summary">{__('Motivazione:', 'gepafin')}</span>
<span>{renderHtmlContent(data.motivation)}</span>
</div> : null
}
useEffect(() => { useEffect(() => {
let updatedFormValues = klona(formValues); let updatedFormValues = klona(formValues);
let context = {}; let context = {};
@@ -964,14 +995,21 @@ const DomandaEditInstructorManager = () => {
? <div className="appPage__content"> ? <div className="appPage__content">
<ApplicationInfo data={data}/> <ApplicationInfo data={data}/>
<div className="appPageSection">
{motivationMsg()}
</div>
<div className="appPageSection__hr"> <div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span> <span>{__('Azioni rapide', 'gepafin')}</span>
</div> </div>
<div className="appPageSection"> <div className="appPageSection">
{actionBtns()} {actionBtns()}
{auxActions()}
</div> </div>
<div className="appPageSection__hr"></div>
<ApplicationDownloadFiles id={id}/> <ApplicationDownloadFiles id={id}/>
<div className="appPageSection"> <div className="appPageSection">
@@ -996,6 +1034,23 @@ const DomandaEditInstructorManager = () => {
sourceName="evaluation"/> sourceName="evaluation"/>
</div> </div>
<div className="appPageSection">
<h2>{__('Note', 'gepafin')}</h2>
<div translate="no" style={{ 'width': '100%' }}>
<Editor
value={data.note}
readOnly={shouldDisableField('note') || evaluationBlockedForUser(data)}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateEvaluationValue(
e.htmlValue,
['note']
)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</div>
{data.evaluationVersion === 'V2' {data.evaluationVersion === 'V2'
? <div className="appPageSection"> ? <div className="appPageSection">
<h2>{__('Documenti allegati', 'gepafin')}</h2> <h2>{__('Documenti allegati', 'gepafin')}</h2>
@@ -1108,21 +1163,6 @@ const DomandaEditInstructorManager = () => {
</div>)} </div>)}
</div> </div>
</div> </div>
<h3>{__('Note', 'gepafin')}</h3>
<div translate="no">
<Editor
value={data.note}
readOnly={shouldDisableField('note') || evaluationBlockedForUser(data)}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateEvaluationValue(
e.htmlValue,
['note']
)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</div> </div>
<div> <div>
<h3>{__('Documenti allegati', 'gepafin')}</h3> <h3>{__('Documenti allegati', 'gepafin')}</h3>
@@ -1241,6 +1281,7 @@ const DomandaEditInstructorManager = () => {
<div className="appPageSection"> <div className="appPageSection">
{actionBtns()} {actionBtns()}
{auxActions()}
</div> </div>
<Dialog <Dialog

View File

@@ -0,0 +1,105 @@
import React, { useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
// tools
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
// api
import ApplicationService from '../../../../service/application-service';
// store
import { useStoreValue } from '../../../../store';
import { Toast } from 'primereact/toast';
import { Button } from 'primereact/button';
import { Dialog } from 'primereact/dialog';
const EvaluationReAdmit = ({ id, status, statusUpdateFn }) => {
const [isSendingRequest, setIsSendingRequest] = useState(false);
const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false);
const toast = useRef(null);
const role = useStoreValue('getRole');
const doReAdmit = () => {
setIsSendingRequest(true);
ApplicationService.reAdmitApplication(id, reAdmitCallback, errReAdmitCallback);
}
const reAdmitCallback = (resp) => {
if (resp.status === 'SUCCESS') {
if (toast.current && resp.message) {
toast.current.show({
severity: 'success',
summary: '',
detail: resp.message
});
}
if (statusUpdateFn) {
statusUpdateFn(resp.data.status);
}
}
setIsSendingRequest(false);
}
const errReAdmitCallback = (resp) => {
if (toast.current && resp.message) {
toast.current.show({
severity: resp.status === 'SUCCESS' ? 'info' : 'error',
summary: '',
detail: resp.message
});
}
set404FromErrorResponse(resp);
setIsSendingRequest(false);
}
const headerConfirmDialog = () => {
return <span>{__('Richiesta di conferma', 'gepafin')}</span>;
}
const hideConfirmDialog = () => {
setIsVisibleConfirmDialog(false);
}
const footerConfirmDialog = () => {
return <div>
<Button type="button" label={__('No', 'gepafin')} onClick={() => setIsVisibleConfirmDialog(false)} outlined/>
<Button
type="button"
label={__('Si', 'gepafin')} onClick={doConfirm}/>
</div>
}
const doConfirm = () => {
setIsVisibleConfirmDialog(false);
doReAdmit();
}
return (
['ROLE_SUPER_ADMIN', 'ROLE_INSTRUCTOR_MANAGER'].includes(role) && ['REJECTED'].includes(status)
? <>
<Toast ref={toast}/>
<Button
type="button"
disabled={isSendingRequest}
severity="warning"
onClick={() => setIsVisibleConfirmDialog(true)}
label={__('Riammetti', 'gepafin')}
icon="pi pi-arrow-circle-up" iconPos="right"/>
<Dialog
visible={isVisibleConfirmDialog}
modal
header={headerConfirmDialog}
footer={footerConfirmDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideConfirmDialog}>
<div className="appForm__field">
<p>{__('È autorizzato dal direttore e autorizzazione caricata su portale a seguito del quale parte l\'email?', 'gepafin')}</p>
</div>
</Dialog>
</> : null
)
}
export default EvaluationReAdmit;

View File

@@ -53,6 +53,8 @@ import RepeaterFields from './components/RepeaterFields';
import ApplicationInfo from './components/ApplicationInfo'; import ApplicationInfo from './components/ApplicationInfo';
import ApplicationDownloadFiles from './components/ApplicationDownloadFiles'; import ApplicationDownloadFiles from './components/ApplicationDownloadFiles';
import FormField from '../../components/FormField'; import FormField from '../../components/FormField';
import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
import EvaluationReAdmit from './components/EvaluationReAdmit';
const APP_EVALUATION_FLOW_ID = process.env.REACT_APP_EVALUATION_FLOW_ID; const APP_EVALUATION_FLOW_ID = process.env.REACT_APP_EVALUATION_FLOW_ID;
const APP_HUB_ID = process.env.REACT_APP_HUB_ID; const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
@@ -86,6 +88,7 @@ const DomandaEditPreInstructor = () => {
const [formData, setFormData] = useState([]); const [formData, setFormData] = useState([]);
const [formId, setFormId] = useState(0); const [formId, setFormId] = useState(0);
const [formInitialData, setFormInitialData] = useState(null); const [formInitialData, setFormInitialData] = useState(null);
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
const { const {
control, control,
handleSubmit, handleSubmit,
@@ -592,7 +595,7 @@ const DomandaEditPreInstructor = () => {
const shouldDisableField = useCallback((fieldName) => { const shouldDisableField = useCallback((fieldName) => {
return !['EVALUATION', 'ADMISSIBLE'].includes(data.applicationStatus) return !['EVALUATION', 'ADMISSIBLE'].includes(data.applicationStatus)
|| (['ADMISSIBLE'].includes(data.applicationStatus) && fieldName !== 'criteria') || (['ADMISSIBLE'].includes(data.applicationStatus) && !['criteria', 'note'].includes(fieldName))
|| (fieldName === 'files' && !isEmpty(data.amendmentDetails)) || (fieldName === 'files' && !isEmpty(data.amendmentDetails))
}, [data.applicationStatus]); }, [data.applicationStatus]);
@@ -784,6 +787,11 @@ const DomandaEditPreInstructor = () => {
} }
} }
const updateStatusOfAppl = useCallback((newStatus) => {
const newData = wrap(data).set(['applicationStatus'], newStatus).value();
setData(newData);
}, [data]);
const actionBtns = () => { const actionBtns = () => {
return <div className="appPageSection__actions"> return <div className="appPageSection__actions">
{['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus) {['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus)
@@ -874,6 +882,29 @@ const DomandaEditPreInstructor = () => {
</div> </div>
} }
const auxActions = () => {
return <div className="appPageSection__actions">
<SoccorsoResendEmails
emailsData={emailSendResponse}
setDataEmailsSoccorso={updateEmailSendResponses}/>
<EvaluationReAdmit id={data.applicationId} status={data.applicationStatus} statusUpdateFn={updateStatusOfAppl}/>
</div>
}
const updateEmailSendResponses = useCallback((newEmailData) => {
const newData = wrap(data).set(['emailSendResponse'], newEmailData).value();
setData(newData);
}, [data]);
const motivationMsg = () => {
return data.motivation && !isEmpty(data.motivation) && ['REJECTED', 'APPROVED'].includes(data.applicationStatus)
? <div className="appPageSection__message info">
<i className="pi pi-info-circle"></i>
<span className="summary">{__('Motivazione:', 'gepafin')}</span>
<span>{renderHtmlContent(data.motivation)}</span>
</div> : null
}
useEffect(() => { useEffect(() => {
let updatedFormValues = klona(formValues); let updatedFormValues = klona(formValues);
let context = {}; let context = {};
@@ -966,14 +997,21 @@ const DomandaEditPreInstructor = () => {
? <div className="appPage__content"> ? <div className="appPage__content">
<ApplicationInfo data={data}/> <ApplicationInfo data={data}/>
<div className="appPageSection">
{motivationMsg()}
</div>
<div className="appPageSection__hr"> <div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span> <span>{__('Azioni rapide', 'gepafin')}</span>
</div> </div>
<div className="appPageSection"> <div className="appPageSection">
{actionBtns()} {actionBtns()}
{auxActions()}
</div> </div>
<div className="appPageSection__hr"></div>
<ApplicationDownloadFiles id={id}/> <ApplicationDownloadFiles id={id}/>
<div className="appPageSection"> <div className="appPageSection">
@@ -998,6 +1036,23 @@ const DomandaEditPreInstructor = () => {
sourceName="evaluation"/> sourceName="evaluation"/>
</div> </div>
<div className="appPageSection">
<h2>{__('Note', 'gepafin')}</h2>
<div translate="no" style={{ 'width': '100%' }}>
<Editor
value={data.note}
readOnly={shouldDisableField('note') || evaluationBlockedForUser(data)}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateEvaluationValue(
e.htmlValue,
['note']
)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</div>
{data.evaluationVersion === 'V2' {data.evaluationVersion === 'V2'
? <div className="appPageSection"> ? <div className="appPageSection">
<h2>{__('Documenti allegati', 'gepafin')}</h2> <h2>{__('Documenti allegati', 'gepafin')}</h2>
@@ -1110,21 +1165,6 @@ const DomandaEditPreInstructor = () => {
</div>)} </div>)}
</div> </div>
</div> </div>
<h3>{__('Note', 'gepafin')}</h3>
<div translate="no">
<Editor
value={data.note}
readOnly={shouldDisableField('note') || evaluationBlockedForUser(data)}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateEvaluationValue(
e.htmlValue,
['note']
)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</div> </div>
<div> <div>
<h3>{__('Documenti allegati', 'gepafin')}</h3> <h3>{__('Documenti allegati', 'gepafin')}</h3>
@@ -1243,6 +1283,7 @@ const DomandaEditPreInstructor = () => {
<div className="appPageSection"> <div className="appPageSection">
{actionBtns()} {actionBtns()}
{auxActions()}
</div> </div>
<Dialog <Dialog

View File

@@ -41,7 +41,6 @@ const Login = () => {
} }
const validateCallback = (data) => { const validateCallback = (data) => {
//console.log('login validateCallback', data)
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
storeSet('setAuthData', { storeSet('setAuthData', {
token: data.data.token, token: data.data.token,
@@ -107,7 +106,7 @@ const Login = () => {
useEffect(() => { useEffect(() => {
const temp_token = searchParams.get('temp_token'); const temp_token = searchParams.get('temp_token');
//console.log('login temp_token', temp_token);
if (!isNil(temp_token) && !isEmpty(temp_token)) { if (!isNil(temp_token) && !isEmpty(temp_token)) {
errorMsgs.current.clear(); errorMsgs.current.clear();
AuthenticationService.validateExistingUser(temp_token, validateCallback, validateError); AuthenticationService.validateExistingUser(temp_token, validateCallback, validateError);

View File

@@ -32,7 +32,7 @@ const SoccorsoAddPreInstructor = () => {
const [data, setData] = useState({}); const [data, setData] = useState({});
const [evaluationId, setEvaluationId] = useState(0); const [evaluationId, setEvaluationId] = useState(0);
const [formData, setFormData] = useState({}); const [formData, setFormData] = useState({});
const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false) const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false);
const toast = useRef(null); const toast = useRef(null);
const goToEvaluationPage = () => { const goToEvaluationPage = () => {

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef, useMemo } from 'react'; import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { is, isEmpty } from 'ramda'; import { is, isEmpty, isNil, pathOr } from 'ramda';
import { wrap } from 'object-path-immutable'; import { wrap } from 'object-path-immutable';
import { klona } from 'klona'; import { klona } from 'klona';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
@@ -28,6 +28,7 @@ import FormField from '../../components/FormField';
import { Editor } from 'primereact/editor'; import { Editor } from 'primereact/editor';
import { InputNumber } from 'primereact/inputnumber'; import { InputNumber } from 'primereact/inputnumber';
import SoccorsoComunications from '../SoccorsoEditPreInstructor/components/SoccorsoComunications'; import SoccorsoComunications from '../SoccorsoEditPreInstructor/components/SoccorsoComunications';
import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
const SoccorsoEditInstructorManager = () => { const SoccorsoEditInstructorManager = () => {
@@ -43,6 +44,7 @@ const SoccorsoEditInstructorManager = () => {
const [internalNote, setInternalNote] = useState(''); const [internalNote, setInternalNote] = useState('');
const toast = useRef(null); const toast = useRef(null);
const [formInitialData, setFormInitialData] = useState({}); const [formInitialData, setFormInitialData] = useState({});
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
const { const {
control, control,
handleSubmit, handleSubmit,
@@ -346,6 +348,11 @@ const SoccorsoEditInstructorManager = () => {
setIsLoadingReminding(false); setIsLoadingReminding(false);
} }
const updateEmailSendResponses = useCallback((newEmailData) => {
const newData = wrap(data).set(['emailSendResponse'], newEmailData).value();
setData(newData);
}, [data]);
useEffect(() => { useEffect(() => {
if (formInitialData) { if (formInitialData) {
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k])); Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
@@ -506,7 +513,8 @@ const SoccorsoEditInstructorManager = () => {
<Button <Button
type="button" type="button"
onClick={sendReminder} onClick={sendReminder}
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)} disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)
|| (!isNil(emailSendResponse) && !isEmpty(emailSendResponse))}
outlined outlined
label={__('Invia Sollecito', 'gepafin')} label={__('Invia Sollecito', 'gepafin')}
icon="pi pi-send" icon="pi pi-send"
@@ -532,6 +540,11 @@ const SoccorsoEditInstructorManager = () => {
label={__('Chiudi Soccorso Istruttorio', 'gepafin')} label={__('Chiudi Soccorso Istruttorio', 'gepafin')}
icon="pi pi-times" iconPos="right"/> icon="pi pi-times" iconPos="right"/>
</div> </div>
<div className="appPageSection__actions">
<SoccorsoResendEmails
emailsData={emailSendResponse}
setDataEmailsSoccorso={updateEmailSendResponses}/>
</div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,85 @@
import React, { useRef, useState } from 'react';
import { pathOr } from 'ramda';
// tools
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
// api
import EmailService from '../../../../service/email-service';
import { Toast } from 'primereact/toast';
import { Button } from 'primereact/button';
import { resendEmailLabelsByType } from '../../../../configData';
import { useStoreValue } from '../../../../store';
const SoccorsoResendEmails = ({ emailsData = [], setDataEmailsSoccorso }) => {
const [isResendingRequest, setIsResendingRequest] = useState(false);
const toast = useRef(null);
const role = useStoreValue('getRole');
const filtered = emailsData.filter(o => !o.isEmailSend);
const resendEmail = (data) => {
setIsResendingRequest(true);
EmailService.emailResend(
data.userActionId,
(resp) => resendingCallback(resp, data.userActionId),
errResendingCallback
);
}
const resendingCallback = (resp, id) => {
if (resp.status === 'SUCCESS') {
if (toast.current && resp.message) {
toast.current.show({
severity: 'success',
summary: '',
detail: resp.message
});
}
setTimeout(() => {
if (setDataEmailsSoccorso) {
const newEmailSendResponse = emailsData.map((o) => {
if (o.userActionId === id) {
o.isEmailSend = true;
}
return o;
});
setDataEmailsSoccorso(newEmailSendResponse);
}
setIsResendingRequest(false);
}, 1500);
}
}
const errResendingCallback = (resp) => {
if (toast.current && resp.message) {
toast.current.show({
severity: 'error',
summary: '',
detail: resp.message
});
}
set404FromErrorResponse(resp);
setIsResendingRequest(false);
}
return (
['ROLE_SUPER_ADMIN', 'ROLE_INSTRUCTOR_MANAGER'].includes(role) && filtered.length > 0
? <>
<Toast ref={toast}/>
{filtered
.map(o => <Button
key={o.userActionId}
severity="warning"
type="button"
disabled={isResendingRequest}
onClick={() => resendEmail(o)}
label={pathOr('Re-inivia email', ['emailScenario'], resendEmailLabelsByType)}
icon="pi pi-send" iconPos="right"/>)}
</> : null
)
}
export default SoccorsoResendEmails;

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef, useMemo } from 'react'; import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { is, isEmpty } from 'ramda'; import { is, isEmpty, isNil, pathOr } from 'ramda';
import { wrap } from 'object-path-immutable'; import { wrap } from 'object-path-immutable';
import { klona } from 'klona'; import { klona } from 'klona';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
@@ -28,6 +28,7 @@ import FormField from '../../components/FormField';
import { Editor } from 'primereact/editor'; import { Editor } from 'primereact/editor';
import { InputNumber } from 'primereact/inputnumber'; import { InputNumber } from 'primereact/inputnumber';
import SoccorsoComunications from './components/SoccorsoComunications'; import SoccorsoComunications from './components/SoccorsoComunications';
import SoccorsoResendEmails from './components/SoccorsoResendEmails';
const SoccorsoEditPreInstructor = () => { const SoccorsoEditPreInstructor = () => {
@@ -43,6 +44,7 @@ const SoccorsoEditPreInstructor = () => {
const [internalNote, setInternalNote] = useState(''); const [internalNote, setInternalNote] = useState('');
const toast = useRef(null); const toast = useRef(null);
const [formInitialData, setFormInitialData] = useState({}); const [formInitialData, setFormInitialData] = useState({});
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
const { const {
control, control,
handleSubmit, handleSubmit,
@@ -346,6 +348,11 @@ const SoccorsoEditPreInstructor = () => {
setIsLoadingReminding(false); setIsLoadingReminding(false);
} }
const updateEmailSendResponses = useCallback((newEmailData) => {
const newData = wrap(data).set(['emailSendResponse'], newEmailData).value();
setData(newData);
}, [data]);
useEffect(() => { useEffect(() => {
if (formInitialData) { if (formInitialData) {
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k])); Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
@@ -506,7 +513,8 @@ const SoccorsoEditPreInstructor = () => {
<Button <Button
type="button" type="button"
onClick={sendReminder} onClick={sendReminder}
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)} disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)
|| (!isNil(emailSendResponse) && !isEmpty(emailSendResponse))}
outlined outlined
label={__('Invia Sollecito', 'gepafin')} label={__('Invia Sollecito', 'gepafin')}
icon="pi pi-send" icon="pi pi-send"
@@ -532,6 +540,11 @@ const SoccorsoEditPreInstructor = () => {
label={__('Chiudi Soccorso Istruttorio', 'gepafin')} label={__('Chiudi Soccorso Istruttorio', 'gepafin')}
icon="pi pi-times" iconPos="right"/> icon="pi pi-times" iconPos="right"/>
</div> </div>
<div className="appPageSection__actions">
<SoccorsoResendEmails
emailsData={emailSendResponse}
setDataEmailsSoccorso={updateEmailSendResponses}/>
</div>
</div> </div>
</div> </div>

View File

@@ -22,6 +22,7 @@ import { Dropdown } from 'primereact/dropdown';
import { Tag } from 'primereact/tag'; import { Tag } from 'primereact/tag';
import { Calendar } from 'primereact/calendar'; import { Calendar } from 'primereact/calendar';
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint'; import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
const SoccorsiInstructorManagerMioTableAsync = ({ userId = null }) => { const SoccorsiInstructorManagerMioTableAsync = ({ userId = null }) => {
const [localAsyncRequest, setLocalAsyncRequest] = useState(false); const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
@@ -94,10 +95,25 @@ const SoccorsiInstructorManagerMioTableAsync = ({ userId = null }) => {
}); });
}; };
const updateRowData = useCallback((id, updateResponse) => {
const newItems = items.map((o) => {
if (o.id === id) {
o.emailSendResponse = updateResponse;
}
return o;
})
setItems(newItems);
}, [items]);
const actionsBodyTemplate = (rowData) => { const actionsBodyTemplate = (rowData) => {
return <Link to={`/mie-domande/${rowData.applicationId}/soccorso/${rowData.id}`}> return <div className="appPageSection__tableActions lessGap">
<Link to={`/mie-domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/> <Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/>
</Link> </Link>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
} }
const statusBodyTemplate = (rowData) => { const statusBodyTemplate = (rowData) => {

View File

@@ -22,6 +22,7 @@ import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import { Dropdown } from 'primereact/dropdown'; import { Dropdown } from 'primereact/dropdown';
import { Tag } from 'primereact/tag'; import { Tag } from 'primereact/tag';
import { Calendar } from 'primereact/calendar'; import { Calendar } from 'primereact/calendar';
import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
const SoccorsiPreInstructorTableAsync = ({ userId = null }) => { const SoccorsiPreInstructorTableAsync = ({ userId = null }) => {
const [localAsyncRequest, setLocalAsyncRequest] = useState(false); const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
@@ -94,10 +95,25 @@ const SoccorsiPreInstructorTableAsync = ({ userId = null }) => {
}); });
}; };
const updateRowData = useCallback((id, updateResponse) => {
const newItems = items.map((o) => {
if (o.id === id) {
o.emailSendResponse = updateResponse;
}
return o;
})
setItems(newItems);
}, [items]);
const actionsBodyTemplate = (rowData) => { const actionsBodyTemplate = (rowData) => {
return <Link to={`/domande/${rowData.applicationId}/soccorso/${rowData.id}`}> return <div className="appPageSection__tableActions lessGap">
<Link to={`/domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/> <Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/>
</Link> </Link>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
} }
const statusBodyTemplate = (rowData) => { const statusBodyTemplate = (rowData) => {

View File

@@ -139,7 +139,7 @@ const routes = ({ role, chosenCompanyId }) => {
{'ROLE_INSTRUCTOR_MANAGER' === role ? <DomandeInstructorManager/> : null} {'ROLE_INSTRUCTOR_MANAGER' === role ? <DomandeInstructorManager/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/domande/:id/" element={<DefaultLayout> <Route path="/domande/:id/" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoApplicationPreview/> : null} {'ROLE_SUPER_ADMIN' === role ? <DomandaEditPreInstructor/> : null}
{'ROLE_BENEFICIARY' === role ? <SoccorsoEditBeneficiario/> : null} {'ROLE_BENEFICIARY' === role ? <SoccorsoEditBeneficiario/> : null}
{'ROLE_CONFIDI' === role ? <SoccorsoEditBeneficiario/> : null} {'ROLE_CONFIDI' === role ? <SoccorsoEditBeneficiario/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandaEditPreInstructor/> : null} {'ROLE_PRE_INSTRUCTOR' === role ? <DomandaEditPreInstructor/> : null}

View File

@@ -63,4 +63,8 @@ export default class ApplicationService {
static downloadCsvReport = (id, callback, errCallback, queryParams) => { static downloadCsvReport = (id, callback, errCallback, queryParams) => {
NetworkService.getBlob(`${API_BASE_URL}/application/call/${id}/csv`, callback, errCallback, queryParams); NetworkService.getBlob(`${API_BASE_URL}/application/call/${id}/csv`, callback, errCallback, queryParams);
}; };
static reAdmitApplication = (id, callback, errCallback) => {
NetworkService.put(`${API_BASE_URL}/application/${id}/readmit`, {}, callback, errCallback);
};
} }

View File

@@ -0,0 +1,13 @@
import { NetworkService } from './network-service';
const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class EmailService {
static emailResend = (id, callback, errCallback) => {
NetworkService.post(`${API_BASE_URL}/email/resend`, {}, callback, errCallback, [
['userActionId', id]
]);
};
}

View File

@@ -1,5 +1,6 @@
import { storeGet, storeSet } from '../store'; import { storeGet, storeSet } from '../store';
import logMsgWithSentry from '../helpers/logMsgWithSentry'; import logMsgWithSentry from '../helpers/logMsgWithSentry';
import { isEmpty } from 'ramda';
export class NetworkService { export class NetworkService {
@@ -257,6 +258,10 @@ export class NetworkService {
url = url.substring(0, url.length - 1); url = url.substring(0, url.length - 1);
} }
if (url.includes('user/me') && isEmpty(storeGet('getToken'))) {
return;
}
fetch(url, { fetch(url, {
method: 'GET', method: 'GET',
mode: 'cors', mode: 'cors',

View File

@@ -13,18 +13,12 @@ export const actionsAlpha = ({ set, get }) => ({
export const actionsBeta = ({ set, get }) => ({ export const actionsBeta = ({ set, get }) => ({
setAuthData: ({ userData, token }) => { setAuthData: ({ userData, token }) => {
set('state', (draft) => { set('token', token);
draft.userData = userData; set('userData', userData);
draft.token = token;
return draft;
});
}, },
doLogout: () => { doLogout: () => {
set('state', (draft) => { set('token', '');
draft.userData = {}; set('userData', {});
draft.token = '';
return draft;
});
}, },
removeElement: (id) => { removeElement: (id) => {
const elements = get('formElements'); const elements = get('formElements');