Merge branch 'develop' into new-role-director
This commit is contained in:
@@ -320,6 +320,10 @@
|
|||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul li {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@container section_with_border (max-width: 600px) {
|
@container section_with_border (max-width: 600px) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { classNames } from 'primereact/utils';
|
import { classNames } from 'primereact/utils';
|
||||||
import { Controller } from 'react-hook-form';
|
import { Controller } from 'react-hook-form';
|
||||||
import { is, isEmpty } from 'ramda';
|
import { is, isEmpty, isNil } from 'ramda';
|
||||||
|
|
||||||
import { InputNumber } from 'primereact/inputnumber';
|
import { InputNumber } from 'primereact/inputnumber';
|
||||||
import { isNaN } from 'mathjs';
|
import { isNaN } from 'mathjs';
|
||||||
@@ -18,7 +18,7 @@ const NumberInput = ({
|
|||||||
icon = null,
|
icon = null,
|
||||||
locale = 'it-IT',
|
locale = 'it-IT',
|
||||||
minFractionDigits = 0,
|
minFractionDigits = 0,
|
||||||
maxFractionDigits = 1,
|
maxFractionDigits = 0,
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
@@ -27,6 +27,7 @@ const NumberInput = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const minAttr = config.min ? config.min : min;
|
const minAttr = config.min ? config.min : min;
|
||||||
const maxAttr = config.max ? config.max : max;
|
const maxAttr = config.max ? config.max : max;
|
||||||
|
|
||||||
const input = <Controller
|
const input = <Controller
|
||||||
name={fieldName}
|
name={fieldName}
|
||||||
control={control}
|
control={control}
|
||||||
@@ -40,9 +41,10 @@ const NumberInput = ({
|
|||||||
onValueChange={(e) => field.onChange(e.value)}
|
onValueChange={(e) => field.onChange(e.value)}
|
||||||
min={minAttr}
|
min={minAttr}
|
||||||
max={maxAttr}
|
max={maxAttr}
|
||||||
|
mode="decimal"
|
||||||
locale={locale}
|
locale={locale}
|
||||||
showButtons
|
showButtons
|
||||||
useGrouping={useGrouping}
|
useGrouping={!isNil(maxFractionDigits) && parseInt(maxFractionDigits) !== 0}
|
||||||
maxFractionDigits={!isNaN(parseInt(maxFractionDigits)) ? parseInt(maxFractionDigits) : 0}
|
maxFractionDigits={!isNaN(parseInt(maxFractionDigits)) ? parseInt(maxFractionDigits) : 0}
|
||||||
minFractionDigits={!isNaN(parseInt(minFractionDigits)) ? parseInt(minFractionDigits) : 0}
|
minFractionDigits={!isNaN(parseInt(minFractionDigits)) ? parseInt(minFractionDigits) : 0}
|
||||||
className={classNames({ 'p-invalid': fieldState.invalid })}/>
|
className={classNames({ 'p-invalid': fieldState.invalid })}/>
|
||||||
|
|||||||
@@ -237,6 +237,11 @@ export const classificationType = [
|
|||||||
'name': 'FATTURA',
|
'name': 'FATTURA',
|
||||||
'idTipoprotocollo': 2
|
'idTipoprotocollo': 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'idClassificazione': 200,
|
||||||
|
'name': 'LETTERA ACCETTAZIONE DELIBERA',
|
||||||
|
'idTipoprotocollo': 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'idClassificazione': 201,
|
'idClassificazione': 201,
|
||||||
'name': 'LETTERA ESITO DELIBERA',
|
'name': 'LETTERA ESITO DELIBERA',
|
||||||
@@ -247,6 +252,11 @@ export const classificationType = [
|
|||||||
'name': 'LETTERA TRASPARENZA',
|
'name': 'LETTERA TRASPARENZA',
|
||||||
'idTipoprotocollo': 2
|
'idTipoprotocollo': 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'idClassificazione': 212,
|
||||||
|
'name': 'LETTERA ACCETTAZIONE DELIBERA PROTOCOLLO',
|
||||||
|
'idTipoprotocollo': 2
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'idClassificazione': 205,
|
'idClassificazione': 205,
|
||||||
'name': 'CONTRATTO',
|
'name': 'CONTRATTO',
|
||||||
@@ -288,21 +298,25 @@ export const rejectionReasons = [
|
|||||||
|
|
||||||
export const amendmentRequestedDocs = {
|
export const amendmentRequestedDocs = {
|
||||||
NESSUNA_GARANZIA: [
|
NESSUNA_GARANZIA: [
|
||||||
'Lettera di accettazione firmata'
|
'Lettera di accettazione firmata',
|
||||||
|
'Visura Centrale Rischi'
|
||||||
],
|
],
|
||||||
GARANZIA_MCC: [
|
GARANZIA_MCC: [
|
||||||
'Lettera di accettazione firmata',
|
'Lettera di accettazione firmata',
|
||||||
'Modulo di domanda della agevolazione (ex allegato 4)'
|
'Modulo di domanda della agevolazione (ex allegato 4)',
|
||||||
|
'Visura Centrale Rischi'
|
||||||
],
|
],
|
||||||
MCC_START_UP: [
|
MCC_START_UP: [
|
||||||
'Lettera di accettazione firmata',
|
'Lettera di accettazione firmata',
|
||||||
'Modulo di domanda della agevolazione (ex allegato 4)',
|
'Modulo di domanda della agevolazione (ex allegato 4)',
|
||||||
'Modello di valutazione bilanci previsionali',
|
'Modello di valutazione bilanci previsionali',
|
||||||
'Modello valutazione start up'
|
'Modello valutazione start up',
|
||||||
|
'Visura Centrale Rischi'
|
||||||
],
|
],
|
||||||
ALTRE_GARANZIE: [
|
ALTRE_GARANZIE: [
|
||||||
'Lettera di accettazione firmata',
|
'Lettera di accettazione firmata',
|
||||||
'Modello privacy',
|
'Modello privacy',
|
||||||
'Autocertificazione e altri eventuali in zip/p7m'
|
'Autocertificazione e altri eventuali in zip/p7m',
|
||||||
|
'Visura Centrale Rischi'
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -33,10 +33,10 @@ const getBandoLabel = (status) => {
|
|||||||
return __('Ammisibile', 'gepafin');
|
return __('Ammisibile', 'gepafin');
|
||||||
|
|
||||||
case 'TECHNICAL_EVALUATION':
|
case 'TECHNICAL_EVALUATION':
|
||||||
return __('Valutazione technico-finanziaria', 'gepafin');
|
return __('Valutazione tecnico-finanziaria', 'gepafin');
|
||||||
|
|
||||||
case 'AWAITING_TECHNICAL_EVALUATION':
|
case 'AWAITING_TECHNICAL_EVALUATION':
|
||||||
return __('Pre valutazione technico-finanziaria', 'gepafin');
|
return __('Post valutazione tecnico-finanziaria', 'gepafin');
|
||||||
|
|
||||||
case 'DUE':
|
case 'DUE':
|
||||||
return __('In scadenza', 'gepafin');
|
return __('In scadenza', 'gepafin');
|
||||||
@@ -77,6 +77,12 @@ const getBandoLabel = (status) => {
|
|||||||
case 'TECHNICAL_EVALUATION_REJECTED':
|
case 'TECHNICAL_EVALUATION_REJECTED':
|
||||||
return __('Respinto Tec-Fin', 'gepafin');
|
return __('Respinto Tec-Fin', 'gepafin');
|
||||||
|
|
||||||
|
case 'AWAITING_CONTRACT':
|
||||||
|
return __('In attesa di contratto', 'gepafin');
|
||||||
|
|
||||||
|
case 'CONTRACT_SIGNED':
|
||||||
|
return __('Contratto firmato', 'gepafin');
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,12 @@ const getBandoSeverity = (status) => {
|
|||||||
case 'TECHNICAL_EVALUATION_REJECTED':
|
case 'TECHNICAL_EVALUATION_REJECTED':
|
||||||
return 'danger';
|
return 'danger';
|
||||||
|
|
||||||
|
case 'AWAITING_CONTRACT':
|
||||||
|
return 'warning';
|
||||||
|
|
||||||
|
case 'CONTRACT_SIGNED':
|
||||||
|
return 'success';
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 'info';
|
return 'info';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,49 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import { head, isEmpty, pathOr } from 'ramda';
|
import { head, isEmpty, pathOr } from 'ramda';
|
||||||
import NumberFlow from '@number-flow/react';
|
import NumberFlow from '@number-flow/react';
|
||||||
|
import { wrap } from 'object-path-immutable';
|
||||||
|
|
||||||
// store
|
// store
|
||||||
import { useStoreValue } from '../../store';
|
import { storeGet, storeSet, useStoreValue } from '../../store';
|
||||||
|
|
||||||
// api
|
// api
|
||||||
import DashboardService from '../../service/dashboard-service';
|
import DashboardService from '../../service/dashboard-service';
|
||||||
|
import ApplicationContractService from '../../service/application-contract-service';
|
||||||
|
|
||||||
|
// tools
|
||||||
|
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
||||||
|
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import ErrorBoundary from '../../components/ErrorBoundary';
|
import ErrorBoundary from '../../components/ErrorBoundary';
|
||||||
import LatestBandiBeneficiarioTableAsync from './components/LatestBandiBeneficiarioTableAsync';
|
import LatestBandiBeneficiarioTableAsync from './components/LatestBandiBeneficiarioTableAsync';
|
||||||
import MyLatestSubmissionsTableAsync from './components/MyLatestSubmissionsTableAsync';
|
import MyLatestSubmissionsTableAsync from './components/MyLatestSubmissionsTableAsync';
|
||||||
|
import { classNames } from 'primereact/utils';
|
||||||
|
import { FileUpload } from 'primereact/fileupload';
|
||||||
|
import { defaultMaxFileSize, mimeTypes } from '../../configData';
|
||||||
|
import { Dialog } from 'primereact/dialog';
|
||||||
|
import { Toast } from 'primereact/toast';
|
||||||
|
|
||||||
const REACT_APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
const REACT_APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
||||||
|
|
||||||
const DashboardBeneficiario = () => {
|
const DashboardBeneficiario = () => {
|
||||||
|
const isAsyncRequest = useStoreValue('isAsyncRequest');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [mainStats, setMainStats] = useState({});
|
const [mainStats, setMainStats] = useState({});
|
||||||
|
const [contractsData, setContractsData] = useState([]);
|
||||||
const companies = useStoreValue('companies');
|
const companies = useStoreValue('companies');
|
||||||
const chosenCompanyId = useStoreValue('chosenCompanyId');
|
const chosenCompanyId = useStoreValue('chosenCompanyId');
|
||||||
const company = head(companies.filter(o => o.id === chosenCompanyId));
|
const company = head(companies.filter(o => o.id === chosenCompanyId));
|
||||||
|
const [isVisibleContractForm, setIsVisibleContractForm] = useState(false);
|
||||||
|
const [contractFormData, setContractFormData] = useState({
|
||||||
|
subject: '',
|
||||||
|
text: ''
|
||||||
|
});
|
||||||
|
const contractFormFilesRef = useRef(null);
|
||||||
|
const toast = useRef(null);
|
||||||
|
|
||||||
const goToAllSubmissions = () => {
|
const goToAllSubmissions = () => {
|
||||||
navigate('/bandi');
|
navigate('/bandi');
|
||||||
@@ -42,11 +62,112 @@ const DashboardBeneficiario = () => {
|
|||||||
const errGetStats = () => {
|
const errGetStats = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getContracts = (data) => {
|
||||||
|
if (data.status === 'SUCCESS') {
|
||||||
|
setContractsData(data.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const errGetContracts = () => {
|
||||||
|
}
|
||||||
|
|
||||||
|
const openSendContractForm = useCallback((id) => {
|
||||||
|
const contract = head(contractsData.filter(o => o.id === id));
|
||||||
|
|
||||||
|
if (contract) {
|
||||||
|
setContractFormData(contract)
|
||||||
|
setIsVisibleContractForm(true);
|
||||||
|
}
|
||||||
|
}, [contractsData]);
|
||||||
|
|
||||||
|
const headerContractDialog = () => {
|
||||||
|
return <span>{__('Invia il contratto', 'gepafin')}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideContractDialog = () => {
|
||||||
|
setIsVisibleContractForm(false);
|
||||||
|
setContractFormData({
|
||||||
|
subject: '',
|
||||||
|
text: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const footerContractDialog = useCallback(() => {
|
||||||
|
let isDisabled = !contractFormData.files || isEmpty(contractFormData.files) || isAsyncRequest;
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<Button type="button" label={__('Annulla', 'gepafin')} onClick={hideContractDialog} outlined/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={isDisabled}
|
||||||
|
label={__('Invia', 'gepafin')} onClick={doSendContract}/>
|
||||||
|
</div>
|
||||||
|
}, [contractFormData]);
|
||||||
|
|
||||||
|
const updateContractFormData = (value, path) => {
|
||||||
|
const newData = wrap(contractFormData).set(path.split('.'), value).value();
|
||||||
|
setContractFormData(newData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const doSendContract = useCallback(() => {
|
||||||
|
if (contractFormData.files && !isEmpty(contractFormData.files) && !isAsyncRequest) {
|
||||||
|
const formDataToSend = new FormData();
|
||||||
|
|
||||||
|
if (contractFormData.files && contractFormData.files.length > 0) {
|
||||||
|
contractFormData.files.forEach((file) => {
|
||||||
|
formDataToSend.append('beneficiaryContractDocuments', file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
storeSet('setAsyncRequest');
|
||||||
|
|
||||||
|
ApplicationContractService.updateApplicationContract(
|
||||||
|
contractFormData.id,
|
||||||
|
formDataToSend,
|
||||||
|
getUploadApplicationContractCallback,
|
||||||
|
errGetUploadApplicationContractCallback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [contractFormData]);
|
||||||
|
|
||||||
|
const getUploadApplicationContractCallback = (data) => {
|
||||||
|
if (data.status === 'SUCCESS') {
|
||||||
|
setContractsData(null);
|
||||||
|
if (toast.current && data.message) {
|
||||||
|
toast.current.show({
|
||||||
|
severity: 'success',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hideContractDialog();
|
||||||
|
storeSet('unsetAsyncRequest');
|
||||||
|
}
|
||||||
|
|
||||||
|
const errGetUploadApplicationContractCallback = (data) => {
|
||||||
|
if (toast.current && data.message) {
|
||||||
|
toast.current.show({
|
||||||
|
severity: data.status === 'SUCCESS' ? 'info' : 'error',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
hideContractDialog();
|
||||||
|
set404FromErrorResponse(data);
|
||||||
|
storeSet('unsetAsyncRequest');
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const existingCompany = head(companies.filter(o => o.id === chosenCompanyId));
|
const existingCompany = head(companies.filter(o => o.id === chosenCompanyId));
|
||||||
|
|
||||||
if (existingCompany) {
|
if (existingCompany) {
|
||||||
DashboardService.getBeneficiaryStatsForCompany(existingCompany.id, getStats, errGetStats);
|
DashboardService.getBeneficiaryStatsForCompany(existingCompany.id, getStats, errGetStats);
|
||||||
|
const userData = storeGet('userData');
|
||||||
|
|
||||||
|
ApplicationContractService.getContractByUserId(getContracts, errGetContracts, [
|
||||||
|
['userId', userData.id]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}, [companies, chosenCompanyId]);
|
}, [companies, chosenCompanyId]);
|
||||||
|
|
||||||
@@ -57,7 +178,28 @@ const DashboardBeneficiario = () => {
|
|||||||
{company ? <span className="companyName">{company.companyName}</span> : null}
|
{company ? <span className="companyName">{company.companyName}</span> : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{contractsData && !isEmpty(contractsData)
|
||||||
|
? <>
|
||||||
|
<div className="appPage__spacer"></div>
|
||||||
|
<div className="appPageSection__message warning">
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, width: '100%' }}>
|
||||||
|
<div style={{ display: 'flex', gap: 10 }}>
|
||||||
|
<i className="pi pi-info-circle"></i>
|
||||||
|
<span className="summary">{__('Contratti in attesa:', 'gepafin')}</span>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
{contractsData.map(o => <li key={o.id}>
|
||||||
|
<a href="#" onClick={() => openSendContractForm(o.id)}>
|
||||||
|
{o.callName} - Domanda #{o.applicationId}
|
||||||
|
</a>
|
||||||
|
</li>)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</> : null}
|
||||||
|
|
||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
|
<Toast ref={toast}/>
|
||||||
|
|
||||||
<div className="appPageSection statsBigBadges">
|
<div className="appPageSection statsBigBadges">
|
||||||
<h2>{__('Panoramica di Sistema', 'gepafin')}</h2>
|
<h2>{__('Panoramica di Sistema', 'gepafin')}</h2>
|
||||||
@@ -162,6 +304,88 @@ const DashboardBeneficiario = () => {
|
|||||||
label={__('Contatta assistenza', 'gepafin')} icon="pi pi-envelope" iconPos="right"/>*/}
|
label={__('Contatta assistenza', 'gepafin')} icon="pi pi-envelope" iconPos="right"/>*/}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
visible={isVisibleContractForm}
|
||||||
|
modal
|
||||||
|
header={headerContractDialog}
|
||||||
|
footer={footerContractDialog}
|
||||||
|
style={{ maxWidth: '600px', width: '100%' }}
|
||||||
|
onHide={hideContractDialog}>
|
||||||
|
<div className="appForm__field">
|
||||||
|
<p>Scarica il contratto:</p>
|
||||||
|
<ul>
|
||||||
|
{contractFormData?.instructorDocuments
|
||||||
|
? contractFormData.instructorDocuments.map(o => <li key={o.id}>
|
||||||
|
<a href={o.filePath} target="_blank" rel="noreferrer">{o.name}</a>
|
||||||
|
</li>)
|
||||||
|
: null}
|
||||||
|
</ul>
|
||||||
|
<p>Firmalo digitalmente e ricaricalo</p>
|
||||||
|
</div>
|
||||||
|
<div className="appForm__field">
|
||||||
|
<label
|
||||||
|
className={classNames({ 'p-error': !contractFormData.files || isEmpty(contractFormData.files) })}>
|
||||||
|
{__('Files', 'gepafin')}* (p7m)
|
||||||
|
</label>
|
||||||
|
<FileUpload
|
||||||
|
ref={contractFormFilesRef}
|
||||||
|
name="files[]"
|
||||||
|
multiple
|
||||||
|
accept='.p7m,application/pkcs7-mime,application/x-pkcs7-mime'
|
||||||
|
maxFileSize={defaultMaxFileSize}
|
||||||
|
auto={false}
|
||||||
|
customUpload={true}
|
||||||
|
className={classNames({ 'p-invalid': !contractFormData.files || isEmpty(contractFormData.files) })}
|
||||||
|
onSelect={(e) => {
|
||||||
|
updateContractFormData(e.files, 'files');
|
||||||
|
}}
|
||||||
|
onRemove={(e) => {
|
||||||
|
const updatedFiles = contractFormFilesRef.current.getFiles();
|
||||||
|
updateContractFormData(updatedFiles, 'files');
|
||||||
|
}}
|
||||||
|
headerTemplate={(options) => {
|
||||||
|
const { chooseButton } = options;
|
||||||
|
return (
|
||||||
|
<div className="p-fileupload-buttonbar" data-pc-section="buttonbar">
|
||||||
|
{chooseButton}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
chooseOptions={{
|
||||||
|
label: __('Aggiungi i file', 'gepafin'),
|
||||||
|
icon: 'pi pi-plus'
|
||||||
|
}}
|
||||||
|
itemTemplate={(file, props) => {
|
||||||
|
return (
|
||||||
|
<div className="p-fileupload-row" data-pc-section="file">
|
||||||
|
<div data-pc-section="details" style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '10px',
|
||||||
|
textAlign: 'left'
|
||||||
|
}}>
|
||||||
|
<div className="p-fileupload-filename" data-pc-section="filename">
|
||||||
|
{file.name}
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
data-pc-section="filesize">{getFormatedFileSizeText(file.size)}</span>
|
||||||
|
</div>
|
||||||
|
<div data-pc-section="actions">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
icon="pi pi-times"
|
||||||
|
className="p-button-rounded p-button-danger p-button-text"
|
||||||
|
onClick={() => props.onRemove()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
emptyTemplate={<p className="m-0">{__('Trascina i file qua')}</p>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,49 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import { head, isEmpty, pathOr } from 'ramda';
|
import { head, isEmpty, pathOr } from 'ramda';
|
||||||
import NumberFlow from '@number-flow/react';
|
import NumberFlow from '@number-flow/react';
|
||||||
|
import { wrap } from 'object-path-immutable';
|
||||||
|
|
||||||
// store
|
// store
|
||||||
import { useStoreValue } from '../../store';
|
import { storeGet, storeSet, useStoreValue } from '../../store';
|
||||||
|
|
||||||
// api
|
// api
|
||||||
import DashboardService from '../../service/dashboard-service';
|
import DashboardService from '../../service/dashboard-service';
|
||||||
|
import ApplicationContractService from '../../service/application-contract-service';
|
||||||
|
|
||||||
|
// tools
|
||||||
|
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
|
||||||
|
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
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 LatestBandiBeneficiarioTableAsync from '../DashboardBeneficiario/components/LatestBandiBeneficiarioTableAsync';
|
import LatestBandiBeneficiarioTableAsync from '../DashboardBeneficiario/components/LatestBandiBeneficiarioTableAsync';
|
||||||
|
import { classNames } from 'primereact/utils';
|
||||||
|
import { FileUpload } from 'primereact/fileupload';
|
||||||
|
import { defaultMaxFileSize } from '../../configData';
|
||||||
|
import { Dialog } from 'primereact/dialog';
|
||||||
|
import { Toast } from 'primereact/toast';
|
||||||
|
|
||||||
const REACT_APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
const REACT_APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
||||||
|
|
||||||
const DashboardBeneficiarioConfidi = () => {
|
const DashboardBeneficiarioConfidi = () => {
|
||||||
|
const isAsyncRequest = useStoreValue('isAsyncRequest');
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [mainStats, setMainStats] = useState({});
|
const [mainStats, setMainStats] = useState({});
|
||||||
|
const [contractsData, setContractsData] = useState([]);
|
||||||
const companies = useStoreValue('companies');
|
const companies = useStoreValue('companies');
|
||||||
const chosenCompanyId = useStoreValue('chosenCompanyId');
|
const chosenCompanyId = useStoreValue('chosenCompanyId');
|
||||||
const company = head(companies.filter(o => o.id === chosenCompanyId));
|
const company = head(companies.filter(o => o.id === chosenCompanyId));
|
||||||
|
const [isVisibleContractForm, setIsVisibleContractForm] = useState(false);
|
||||||
|
const [contractFormData, setContractFormData] = useState({
|
||||||
|
subject: '',
|
||||||
|
text: ''
|
||||||
|
});
|
||||||
|
const contractFormFilesRef = useRef(null);
|
||||||
|
const toast = useRef(null);
|
||||||
|
|
||||||
const goToAllSubmissions = () => {
|
const goToAllSubmissions = () => {
|
||||||
navigate('/bandi');
|
navigate('/bandi');
|
||||||
@@ -42,11 +62,112 @@ const DashboardBeneficiarioConfidi = () => {
|
|||||||
const errGetStats = () => {
|
const errGetStats = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getContracts = (data) => {
|
||||||
|
if (data.status === 'SUCCESS') {
|
||||||
|
setContractsData(data.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const errGetContracts = () => {
|
||||||
|
}
|
||||||
|
|
||||||
|
const openSendContractForm = useCallback((id) => {
|
||||||
|
const contract = head(contractsData.filter(o => o.id === id));
|
||||||
|
|
||||||
|
if (contract) {
|
||||||
|
setContractFormData(contract)
|
||||||
|
setIsVisibleContractForm(true);
|
||||||
|
}
|
||||||
|
}, [contractsData]);
|
||||||
|
|
||||||
|
const headerContractDialog = () => {
|
||||||
|
return <span>{__('Invia il contratto', 'gepafin')}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideContractDialog = () => {
|
||||||
|
setIsVisibleContractForm(false);
|
||||||
|
setContractFormData({
|
||||||
|
subject: '',
|
||||||
|
text: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const footerContractDialog = useCallback(() => {
|
||||||
|
let isDisabled = !contractFormData.files || isEmpty(contractFormData.files) || isAsyncRequest;
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<Button type="button" label={__('Annulla', 'gepafin')} onClick={hideContractDialog} outlined/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={isDisabled}
|
||||||
|
label={__('Invia', 'gepafin')} onClick={doSendContract}/>
|
||||||
|
</div>
|
||||||
|
}, [contractFormData]);
|
||||||
|
|
||||||
|
const updateContractFormData = (value, path) => {
|
||||||
|
const newData = wrap(contractFormData).set(path.split('.'), value).value();
|
||||||
|
setContractFormData(newData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const doSendContract = useCallback(() => {
|
||||||
|
if (contractFormData.files && !isEmpty(contractFormData.files) && !isAsyncRequest) {
|
||||||
|
const formDataToSend = new FormData();
|
||||||
|
|
||||||
|
if (contractFormData.files && contractFormData.files.length > 0) {
|
||||||
|
contractFormData.files.forEach((file) => {
|
||||||
|
formDataToSend.append('beneficiaryContractDocuments', file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
storeSet('setAsyncRequest');
|
||||||
|
|
||||||
|
ApplicationContractService.updateApplicationContract(
|
||||||
|
contractFormData.id,
|
||||||
|
formDataToSend,
|
||||||
|
getUploadApplicationContractCallback,
|
||||||
|
errGetUploadApplicationContractCallback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [contractFormData]);
|
||||||
|
|
||||||
|
const getUploadApplicationContractCallback = (data) => {
|
||||||
|
if (data.status === 'SUCCESS') {
|
||||||
|
//setData(getFormattedData(data.data));
|
||||||
|
if (toast.current && data.message) {
|
||||||
|
toast.current.show({
|
||||||
|
severity: 'success',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hideContractDialog();
|
||||||
|
storeSet('unsetAsyncRequest');
|
||||||
|
}
|
||||||
|
|
||||||
|
const errGetUploadApplicationContractCallback = (data) => {
|
||||||
|
if (toast.current && data.message) {
|
||||||
|
toast.current.show({
|
||||||
|
severity: data.status === 'SUCCESS' ? 'info' : 'error',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
hideContractDialog();
|
||||||
|
set404FromErrorResponse(data);
|
||||||
|
storeSet('unsetAsyncRequest');
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const existingCompany = head(companies.filter(o => o.id === chosenCompanyId));
|
const existingCompany = head(companies.filter(o => o.id === chosenCompanyId));
|
||||||
|
|
||||||
if (existingCompany) {
|
if (existingCompany) {
|
||||||
DashboardService.getBeneficiaryStatsForCompany(existingCompany.id, getStats, errGetStats);
|
DashboardService.getBeneficiaryStatsForCompany(existingCompany.id, getStats, errGetStats);
|
||||||
|
const userData = storeGet('userData');
|
||||||
|
|
||||||
|
ApplicationContractService.getContractByUserId(getContracts, errGetContracts, [
|
||||||
|
['userId', userData.id]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}, [companies, chosenCompanyId]);
|
}, [companies, chosenCompanyId]);
|
||||||
|
|
||||||
@@ -57,7 +178,28 @@ const DashboardBeneficiarioConfidi = () => {
|
|||||||
{company ? <span className="companyName">{company.companyName}</span> : null}
|
{company ? <span className="companyName">{company.companyName}</span> : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{contractsData && !isEmpty(contractsData)
|
||||||
|
? <>
|
||||||
|
<div className="appPage__spacer"></div>
|
||||||
|
<div className="appPageSection__message warning">
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, width: '100%' }}>
|
||||||
|
<div style={{ display: 'flex', gap: 10 }}>
|
||||||
|
<i className="pi pi-info-circle"></i>
|
||||||
|
<span className="summary">{__('Contratti in attesa:', 'gepafin')}</span>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
{contractsData.map(o => <li key={o.id}>
|
||||||
|
<a href="#" onClick={() => openSendContractForm(o.id)}>
|
||||||
|
{o.callName} - Domanda #{o.applicationId}
|
||||||
|
</a>
|
||||||
|
</li>)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</> : null}
|
||||||
|
|
||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
|
<Toast ref={toast}/>
|
||||||
|
|
||||||
<div className="appPageSection statsBigBadges">
|
<div className="appPageSection statsBigBadges">
|
||||||
<h2>{__('Panoramica di Sistema', 'gepafin')}</h2>
|
<h2>{__('Panoramica di Sistema', 'gepafin')}</h2>
|
||||||
@@ -102,7 +244,7 @@ const DashboardBeneficiarioConfidi = () => {
|
|||||||
<span className="summary">{__('Attenzione', 'gepafin')}</span>
|
<span className="summary">{__('Attenzione', 'gepafin')}</span>
|
||||||
<span>
|
<span>
|
||||||
{__('Per applicare ai bandi devi Registare un Azienda clicca', 'gepafin')}
|
{__('Per applicare ai bandi devi Registare un Azienda clicca', 'gepafin')}
|
||||||
<Link to={`/agguingi-azienda`} style={{marginLeft: '0.5ch'}}>{__('qua', 'gepafin')}</Link>
|
<Link to={`/agguingi-azienda`} style={{ marginLeft: '0.5ch' }}>{__('qua', 'gepafin')}</Link>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
@@ -148,8 +290,102 @@ const DashboardBeneficiarioConfidi = () => {
|
|||||||
<Button
|
<Button
|
||||||
onClick={goToAllSubmissions}
|
onClick={goToAllSubmissions}
|
||||||
label={__('Nuova domanda', 'gepafin')} icon="pi pi-plus" iconPos="right"/>
|
label={__('Nuova domanda', 'gepafin')} icon="pi pi-plus" iconPos="right"/>
|
||||||
|
{/*<Button
|
||||||
|
disabled={true}
|
||||||
|
outlined
|
||||||
|
onClick={() => {
|
||||||
|
}}
|
||||||
|
label={__('Carica documento', 'gepafin')} icon="pi pi-upload" iconPos="right"/>
|
||||||
|
<Button
|
||||||
|
disabled={true}
|
||||||
|
outlined
|
||||||
|
onClick={() => {
|
||||||
|
}}
|
||||||
|
label={__('Contatta assistenza', 'gepafin')} icon="pi pi-envelope" iconPos="right"/>*/}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
visible={isVisibleContractForm}
|
||||||
|
modal
|
||||||
|
header={headerContractDialog}
|
||||||
|
footer={footerContractDialog}
|
||||||
|
style={{ maxWidth: '600px', width: '100%' }}
|
||||||
|
onHide={hideContractDialog}>
|
||||||
|
<div className="appForm__field">
|
||||||
|
<p>Scarica il contratto:</p>
|
||||||
|
<ul>
|
||||||
|
{contractFormData?.instructorDocuments
|
||||||
|
? contractFormData.instructorDocuments.map(o => <li key={o.id}>
|
||||||
|
<a href={o.filePath} target="_blank" rel="noreferrer">{o.name}</a>
|
||||||
|
</li>)
|
||||||
|
: null}
|
||||||
|
</ul>
|
||||||
|
<p>Firmalo digitalmente e ricaricalo</p>
|
||||||
|
</div>
|
||||||
|
<div className="appForm__field">
|
||||||
|
<label
|
||||||
|
className={classNames({ 'p-error': !contractFormData.files || isEmpty(contractFormData.files) })}>
|
||||||
|
{__('Files', 'gepafin')}* (p7m)
|
||||||
|
</label>
|
||||||
|
<FileUpload
|
||||||
|
ref={contractFormFilesRef}
|
||||||
|
name="files[]"
|
||||||
|
multiple
|
||||||
|
accept='.p7m,application/pkcs7-mime,application/x-pkcs7-mime'
|
||||||
|
maxFileSize={defaultMaxFileSize}
|
||||||
|
auto={false}
|
||||||
|
customUpload={true}
|
||||||
|
className={classNames({ 'p-invalid': !contractFormData.files || isEmpty(contractFormData.files) })}
|
||||||
|
onSelect={(e) => {
|
||||||
|
updateContractFormData(e.files, 'files');
|
||||||
|
}}
|
||||||
|
onRemove={(e) => {
|
||||||
|
const updatedFiles = contractFormFilesRef.current.getFiles();
|
||||||
|
updateContractFormData(updatedFiles, 'files');
|
||||||
|
}}
|
||||||
|
headerTemplate={(options) => {
|
||||||
|
const { chooseButton } = options;
|
||||||
|
return (
|
||||||
|
<div className="p-fileupload-buttonbar" data-pc-section="buttonbar">
|
||||||
|
{chooseButton}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
chooseOptions={{
|
||||||
|
label: __('Aggiungi i file', 'gepafin'),
|
||||||
|
icon: 'pi pi-plus'
|
||||||
|
}}
|
||||||
|
itemTemplate={(file, props) => {
|
||||||
|
return (
|
||||||
|
<div className="p-fileupload-row" data-pc-section="file">
|
||||||
|
<div data-pc-section="details" style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '10px',
|
||||||
|
textAlign: 'left'
|
||||||
|
}}>
|
||||||
|
<div className="p-fileupload-filename" data-pc-section="filename">
|
||||||
|
{file.name}
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
data-pc-section="filesize">{getFormatedFileSizeText(file.size)}</span>
|
||||||
|
</div>
|
||||||
|
<div data-pc-section="actions">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
icon="pi pi-times"
|
||||||
|
className="p-button-rounded p-button-danger p-button-text"
|
||||||
|
onClick={() => props.onRemove()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
emptyTemplate={<p className="m-0">{__('Trascina i file qua')}</p>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,15 @@ import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/
|
|||||||
|
|
||||||
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
||||||
|
|
||||||
const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [],
|
const DomandeTablePreInstructorAsync = ({
|
||||||
applicationStatuses = ['EVALUATION', 'SOCCORSO', 'NDG', 'APPOINTMENT', 'ADMISSIBLE',
|
userId = null,
|
||||||
'AWAITING_TECHNICAL_EVALUATION', 'TECHNICAL_EVALUATION']}) => {
|
statuses = [],
|
||||||
|
applicationStatuses = [
|
||||||
|
'EVALUATION', 'SOCCORSO', 'NDG', 'APPOINTMENT', 'ADMISSIBLE',
|
||||||
|
'AWAITING_TECHNICAL_EVALUATION', 'TECHNICAL_EVALUATION',
|
||||||
|
'AWAITING_CONTRACT', 'CONTRACT_SIGNED'
|
||||||
|
]
|
||||||
|
}) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const userData = useStoreValue('userData');
|
const userData = useStoreValue('userData');
|
||||||
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
|
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
|
||||||
|
|||||||
@@ -110,7 +110,9 @@ const DashboardPreInstructor = () => {
|
|||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
<h2>{__('Coda di lavoro', 'gepafin')}</h2>
|
<h2>{__('Coda di lavoro', 'gepafin')}</h2>
|
||||||
<DomandeTablePreInstructorAsync statuses={['OPEN', 'SOCCORSO']} userId={userData.id}/>
|
<DomandeTablePreInstructorAsync
|
||||||
|
statuses={['OPEN', 'SOCCORSO', 'AWAITING_CONTRACT', 'CONTRACT_SIGNED']}
|
||||||
|
userId={userData.id}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { storeGet, storeSet, useStoreValue } from '../../store';
|
|||||||
import ApplicationEvaluationService from '../../service/application-evaluation-service';
|
import ApplicationEvaluationService from '../../service/application-evaluation-service';
|
||||||
import AmendmentsService from '../../service/amendments-service';
|
import AmendmentsService from '../../service/amendments-service';
|
||||||
import AppointmentService from '../../service/appointment-service';
|
import AppointmentService from '../../service/appointment-service';
|
||||||
|
import ApplicationContractService from '../../service/application-contract-service';
|
||||||
|
|
||||||
// tools
|
// tools
|
||||||
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
||||||
@@ -60,6 +61,7 @@ import { SplitButton } from 'primereact/splitbutton';
|
|||||||
import { FileUpload } from 'primereact/fileupload';
|
import { FileUpload } from 'primereact/fileupload';
|
||||||
import { defaultMaxFileSize, mimeTypes, rejectionReasons } from '../../configData';
|
import { defaultMaxFileSize, mimeTypes, rejectionReasons } from '../../configData';
|
||||||
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
|
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
|
||||||
|
import ArchiveDocument from '../DomandaEditPreInstructor/components/ArchiveDocument';
|
||||||
|
|
||||||
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;
|
||||||
@@ -98,6 +100,12 @@ const DomandaEditInstructorManager = () => {
|
|||||||
duration: 0,
|
duration: 0,
|
||||||
amount: 0
|
amount: 0
|
||||||
});
|
});
|
||||||
|
const [isVisibleContractForm, setIsVisibleContractForm] = useState(false);
|
||||||
|
const [contractFormData, setContractFormData] = useState({
|
||||||
|
subject: '',
|
||||||
|
text: ''
|
||||||
|
});
|
||||||
|
const contractFormFilesRef = useRef(null);
|
||||||
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);
|
||||||
@@ -225,7 +233,7 @@ const DomandaEditInstructorManager = () => {
|
|||||||
const getCallback = (resp) => {
|
const getCallback = (resp) => {
|
||||||
if (resp.status === 'SUCCESS') {
|
if (resp.status === 'SUCCESS') {
|
||||||
setData(getFormattedData(resp.data));
|
setData(getFormattedData(resp.data));
|
||||||
setFinalDialogData((prev) => ({...prev, motivation: resp.data.motivation}));
|
setFinalDialogData((prev) => ({ ...prev, motivation: resp.data.motivation }));
|
||||||
updateFlagsForSoccorso(resp.data);
|
updateFlagsForSoccorso(resp.data);
|
||||||
|
|
||||||
if (resp.data.evaluationVersion === 'V2') {
|
if (resp.data.evaluationVersion === 'V2') {
|
||||||
@@ -290,7 +298,7 @@ const DomandaEditInstructorManager = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const header = renderHeader();
|
const header = renderHeader();
|
||||||
const technicalEvalItems = [
|
const tecnicalEvalItems = [
|
||||||
{
|
{
|
||||||
label: __('Nessuna garanzia', 'gepafin'),
|
label: __('Nessuna garanzia', 'gepafin'),
|
||||||
icon: 'pi pi-pen-to-square',
|
icon: 'pi pi-pen-to-square',
|
||||||
@@ -697,9 +705,9 @@ const DomandaEditInstructorManager = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const shouldDisableField = useCallback((fieldName) => {
|
const shouldDisableField = useCallback((fieldName) => {
|
||||||
return !['EVALUATION', 'ADMISSIBLE'].includes(data.applicationStatus)
|
return ['EXPIRED', 'CLOSE'].includes(data.status)
|
||||||
|| (['ADMISSIBLE', 'TECHNICAL_EVALUATION'].includes(data.applicationStatus) && !['criteria', 'note'].includes(fieldName))
|
|| (['ADMISSIBLE', 'TECHNICAL_EVALUATION', 'TECHNICAL_EVALUATION', 'AWAITING_TECHNICAL_EVALUATION'].includes(data.applicationStatus)
|
||||||
|| (fieldName === 'files' && !isEmpty(data.amendmentDetails))
|
&& !['criteria', 'note', 'files', 'amendmentDetails'].includes(fieldName))
|
||||||
}, [data.applicationStatus]);
|
}, [data.applicationStatus]);
|
||||||
|
|
||||||
const headerCompleteDialog = () => {
|
const headerCompleteDialog = () => {
|
||||||
@@ -932,6 +940,7 @@ const DomandaEditInstructorManager = () => {
|
|||||||
|
|
||||||
const getAmendmentSpecialCallback = (data) => {
|
const getAmendmentSpecialCallback = (data) => {
|
||||||
if (data.status === 'SUCCESS') {
|
if (data.status === 'SUCCESS') {
|
||||||
|
setData(getFormattedData(data.data));
|
||||||
if (toast.current && data.message) {
|
if (toast.current && data.message) {
|
||||||
toast.current.show({
|
toast.current.show({
|
||||||
severity: 'success',
|
severity: 'success',
|
||||||
@@ -999,6 +1008,106 @@ const DomandaEditInstructorManager = () => {
|
|||||||
setData(newData);
|
setData(newData);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
|
const openSendContractForm = () => {
|
||||||
|
setIsVisibleContractForm(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerContractDialog = () => {
|
||||||
|
return <span>{__('Invia il contratto', 'gepafin')}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideContractDialog = () => {
|
||||||
|
setIsVisibleContractForm(false);
|
||||||
|
setContractFormData({
|
||||||
|
subject: '',
|
||||||
|
text: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const footerContractDialog = useCallback(() => {
|
||||||
|
let isDisabled = !contractFormData.subject || isEmpty(contractFormData.subject)
|
||||||
|
|| !contractFormData.text || isEmpty(contractFormData.text)
|
||||||
|
|| !contractFormData.files || isEmpty(contractFormData.files) || isAsyncRequest;
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<Button type="button" label={__('Annulla', 'gepafin')} onClick={hideContractDialog} outlined/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={isDisabled}
|
||||||
|
label={__('Invia', 'gepafin')} onClick={doSendContract}/>
|
||||||
|
</div>
|
||||||
|
}, [contractFormData]);
|
||||||
|
|
||||||
|
const updateContractFormData = (value, path) => {
|
||||||
|
const newData = wrap(contractFormData).set(path.split('.'), value).value();
|
||||||
|
setContractFormData(newData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const doSendContract = useCallback(() => {
|
||||||
|
if (
|
||||||
|
contractFormData.subject && !isEmpty(contractFormData.subject)
|
||||||
|
&& contractFormData.text && !isEmpty(contractFormData.text)
|
||||||
|
&& contractFormData.files && !isEmpty(contractFormData.files)
|
||||||
|
) {
|
||||||
|
const formDataToSend = new FormData();
|
||||||
|
const contractFormDataTemp = {
|
||||||
|
...contractFormData
|
||||||
|
};
|
||||||
|
delete contractFormDataTemp.files;
|
||||||
|
const jsonBlob = new Blob([JSON.stringify(contractFormDataTemp)], {
|
||||||
|
type: 'application/json'
|
||||||
|
});
|
||||||
|
formDataToSend.append('applicationContractRequest', jsonBlob);
|
||||||
|
|
||||||
|
if (contractFormData.files && contractFormData.files.length > 0) {
|
||||||
|
contractFormData.files.forEach((file) => {
|
||||||
|
formDataToSend.append('contractDocuments', file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
storeSet('setAsyncRequest');
|
||||||
|
|
||||||
|
ApplicationContractService.uploadApplicationContract(
|
||||||
|
data.applicationId,
|
||||||
|
formDataToSend,
|
||||||
|
getUploadApplicationContractCallback,
|
||||||
|
errGetUploadApplicationContractCallback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [contractFormData]);
|
||||||
|
|
||||||
|
const getUploadApplicationContractCallback = (data) => {
|
||||||
|
if (data.status === 'SUCCESS') {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
applicationStatus: 'AWAITING_CONTRACT',
|
||||||
|
contract: data.data
|
||||||
|
}));
|
||||||
|
if (toast.current && data.message) {
|
||||||
|
toast.current.show({
|
||||||
|
severity: 'success',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hideContractDialog();
|
||||||
|
storeSet('unsetAsyncRequest');
|
||||||
|
}
|
||||||
|
|
||||||
|
const errGetUploadApplicationContractCallback = (data) => {
|
||||||
|
if (toast.current && data.message) {
|
||||||
|
toast.current.show({
|
||||||
|
severity: data.status === 'SUCCESS' ? 'info' : 'error',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
hideContractDialog();
|
||||||
|
set404FromErrorResponse(data);
|
||||||
|
storeSet('unsetAsyncRequest');
|
||||||
|
}
|
||||||
|
|
||||||
const actionBtns = () => {
|
const actionBtns = () => {
|
||||||
return <div className="appPageSection__actions">
|
return <div className="appPageSection__actions">
|
||||||
{(['SOCCORSO', 'CLOSE', 'EVALUATION', 'NDG', 'APPOINTMENT', 'ADMISSIBLE',
|
{(['SOCCORSO', 'CLOSE', 'EVALUATION', 'NDG', 'APPOINTMENT', 'ADMISSIBLE',
|
||||||
@@ -1035,8 +1144,7 @@ const DomandaEditInstructorManager = () => {
|
|||||||
onClick={() => doSaveDraft()}
|
onClick={() => doSaveDraft()}
|
||||||
label={__('Crea valutazione', 'gepafin')}
|
label={__('Crea valutazione', 'gepafin')}
|
||||||
icon="pi pi-save" iconPos="right"/>}
|
icon="pi pi-save" iconPos="right"/>}
|
||||||
{APP_EVALUATION_FLOW_ID === '1' && ['EVALUATION'].includes(data.applicationStatus)
|
{APP_EVALUATION_FLOW_ID === '1' && APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
|
||||||
&& APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
|
|
||||||
? <Button
|
? <Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!data.id
|
disabled={!data.id
|
||||||
@@ -1103,7 +1211,7 @@ const DomandaEditInstructorManager = () => {
|
|||||||
? __('Punteggio sufficiente per passaggio alla valutazione tecnica ed economico finanziaria', 'gepafin')
|
? __('Punteggio sufficiente per passaggio alla valutazione tecnica ed economico finanziaria', 'gepafin')
|
||||||
: __('Punteggio non sufficiente per passaggio alla valutazione tecnica ed economico finanziaria', 'gepafin')}
|
: __('Punteggio non sufficiente per passaggio alla valutazione tecnica ed economico finanziaria', 'gepafin')}
|
||||||
severity={isAdmissible ? 'success' : 'warning'}
|
severity={isAdmissible ? 'success' : 'warning'}
|
||||||
model={technicalEvalItems}/> : null}
|
model={tecnicalEvalItems}/> : null}
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!data.id || !['TECHNICAL_EVALUATION'].includes(data.applicationStatus) || evaluationBlockedForUser(data)}
|
disabled={!data.id || !['TECHNICAL_EVALUATION'].includes(data.applicationStatus) || evaluationBlockedForUser(data)}
|
||||||
@@ -1135,6 +1243,13 @@ const DomandaEditInstructorManager = () => {
|
|||||||
onClick={initiateRejecting}
|
onClick={initiateRejecting}
|
||||||
label={__('Respingi domanda', 'gepafin')}
|
label={__('Respingi domanda', 'gepafin')}
|
||||||
icon="pi pi-times" iconPos="right"/> : null}
|
icon="pi pi-times" iconPos="right"/> : null}
|
||||||
|
{APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE' && data.applicationStatus === 'APPROVED'
|
||||||
|
? <Button
|
||||||
|
type="button"
|
||||||
|
disabled={!data.id}
|
||||||
|
onClick={openSendContractForm}
|
||||||
|
label={__('Invia il contratto', 'gepafin')}
|
||||||
|
/> : null}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1167,7 +1282,7 @@ const DomandaEditInstructorManager = () => {
|
|||||||
? <div className="appPageSection__message info">
|
? <div className="appPageSection__message info">
|
||||||
<i className="pi pi-info-circle"></i>
|
<i className="pi pi-info-circle"></i>
|
||||||
<span className="summary">{__('Motivazione:', 'gepafin')}</span>
|
<span className="summary">{__('Motivazione:', 'gepafin')}</span>
|
||||||
<div style={{display: 'flex', flexDirection: 'column', gap: 10}}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||||||
<span>{renderHtmlContent(data.motivation)}</span>
|
<span>{renderHtmlContent(data.motivation)}</span>
|
||||||
{rejectionFiles()}
|
{rejectionFiles()}
|
||||||
</div>
|
</div>
|
||||||
@@ -1329,6 +1444,76 @@ const DomandaEditInstructorManager = () => {
|
|||||||
|
|
||||||
{data.evaluationVersion === 'V2'
|
{data.evaluationVersion === 'V2'
|
||||||
? <div className="appPageSection">
|
? <div className="appPageSection">
|
||||||
|
<h3>{__('Contratto', 'gepafin')}</h3>
|
||||||
|
{data.contract && data.contract?.beneficiaryDocuments && !isEmpty(data.contract?.beneficiaryDocuments)
|
||||||
|
? <ol className="appPageSection__list">
|
||||||
|
{data.contract?.beneficiaryDocuments.map(o => <li key={o.id} className="appPageSection__listItem">
|
||||||
|
<div className="appPageSection__listItemRow">
|
||||||
|
<div>{renderHtmlContent(o.name)}</div>
|
||||||
|
<div className="appPageSection__iconActions">
|
||||||
|
<Button icon="pi pi-eye" rounded
|
||||||
|
onClick={() => {
|
||||||
|
window.open(o.fileDetail[0].filePath, '_blank').focus()
|
||||||
|
}}
|
||||||
|
outlined severity="info"
|
||||||
|
aria-label={__('Mostra', 'gepafin')}/>
|
||||||
|
<ArchiveDocument
|
||||||
|
ndg={data.ndg}
|
||||||
|
applicationId={id}
|
||||||
|
fileDescr={renderHtmlContent(o.name)}
|
||||||
|
fileId={o.id}
|
||||||
|
updateFn={(attachId) => {
|
||||||
|
setData((prev) => {
|
||||||
|
const newDocuments = data.contract.beneficiaryDocuments
|
||||||
|
.map(ob => {
|
||||||
|
return ob.id === o.id
|
||||||
|
? {...o, documentAttachmentId: attachId}
|
||||||
|
: o;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
contract: {
|
||||||
|
...prev.contract,
|
||||||
|
beneficiaryDocuments: newDocuments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
isSignedDocument={true}
|
||||||
|
docAttachmentId={o.documentAttachmentId}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>)}
|
||||||
|
</ol>
|
||||||
|
: <p>{__('Nessun documento firmato', 'gepafin')}</p>}
|
||||||
|
<h2>{__('Domanda PDF firmato', 'gepafin')}</h2>
|
||||||
|
{data.signedDocument && !isEmpty(data.signedDocument)
|
||||||
|
? <ol className="appPageSection__list">
|
||||||
|
<li className="appPageSection__listItem">
|
||||||
|
<div className="appPageSection__listItemRow">
|
||||||
|
<div>{renderHtmlContent(data.signedDocument.fileName)}</div>
|
||||||
|
<div className="appPageSection__iconActions">
|
||||||
|
<ArchiveDocument
|
||||||
|
ndg={data.ndg}
|
||||||
|
applicationId={id}
|
||||||
|
fileDescr={renderHtmlContent(data.signedDocument.fileName)}
|
||||||
|
fileId={data.signedDocument.id}
|
||||||
|
updateFn={(attachId) => {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
signedDocument: {
|
||||||
|
...prev.signedDocument,
|
||||||
|
documentAttachmentId: attachId
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
isSignedDocument={true}
|
||||||
|
docAttachmentId={data.signedDocument.documentAttachmentId}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
: <p>{__('Nessun documento firmato', 'gepafin')}</p>}
|
||||||
<h2>{__('Documenti allegati', 'gepafin')}</h2>
|
<h2>{__('Documenti allegati', 'gepafin')}</h2>
|
||||||
{!isEmpty(data.files)
|
{!isEmpty(data.files)
|
||||||
? <ListOfFiles
|
? <ListOfFiles
|
||||||
@@ -1441,6 +1626,82 @@ const DomandaEditInstructorManager = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<h3>{__('Contratto', 'gepafin')}</h3>
|
||||||
|
{data.contract && data.contract?.beneficiaryDocuments && !isEmpty(data.contract?.beneficiaryDocuments)
|
||||||
|
? <ol className="appPageSection__list">
|
||||||
|
{data.contract?.beneficiaryDocuments.map(o => <li key={o.id} className="appPageSection__listItem">
|
||||||
|
<div className="appPageSection__listItemRow">
|
||||||
|
<div>{renderHtmlContent(o.name)}</div>
|
||||||
|
<div className="appPageSection__iconActions">
|
||||||
|
<Button icon="pi pi-eye" rounded
|
||||||
|
onClick={() => {
|
||||||
|
window.open(o.fileDetail[0].filePath, '_blank').focus()
|
||||||
|
}}
|
||||||
|
outlined severity="info"
|
||||||
|
aria-label={__('Mostra', 'gepafin')}/>
|
||||||
|
<ArchiveDocument
|
||||||
|
ndg={data.ndg}
|
||||||
|
applicationId={id}
|
||||||
|
fileDescr={renderHtmlContent(o.name)}
|
||||||
|
fileId={o.id}
|
||||||
|
updateFn={(attachId) => {
|
||||||
|
setData((prev) => {
|
||||||
|
const newDocuments = data.contract.beneficiaryDocuments
|
||||||
|
.map(ob => {
|
||||||
|
return ob.id === o.id
|
||||||
|
? {...o, documentAttachmentId: attachId}
|
||||||
|
: o;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
contract: {
|
||||||
|
...prev.contract,
|
||||||
|
beneficiaryDocuments: newDocuments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
isSignedDocument={true}
|
||||||
|
docAttachmentId={o.documentAttachmentId}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>)}
|
||||||
|
</ol>
|
||||||
|
: <p>{__('Nessun documento firmato', 'gepafin')}</p>}
|
||||||
|
<h3>{__('Domanda PDF firmato', 'gepafin')}</h3>
|
||||||
|
{data.signedDocument && !isEmpty(data.signedDocument)
|
||||||
|
? <ol className="appPageSection__list">
|
||||||
|
<li className="appPageSection__listItem">
|
||||||
|
<div className="appPageSection__listItemRow">
|
||||||
|
<div>{renderHtmlContent(data.signedDocument.fileName)}</div>
|
||||||
|
<div className="appPageSection__iconActions">
|
||||||
|
<Button icon="pi pi-eye" rounded
|
||||||
|
onClick={() => {
|
||||||
|
window.open(data.signedDocument.filePath, '_blank').focus()
|
||||||
|
}}
|
||||||
|
outlined severity="info"
|
||||||
|
aria-label={__('Mostra', 'gepafin')}/>
|
||||||
|
<ArchiveDocument
|
||||||
|
ndg={data.ndg}
|
||||||
|
applicationId={id}
|
||||||
|
fileDescr={renderHtmlContent(data.signedDocument.fileName)}
|
||||||
|
fileId={data.signedDocument.id}
|
||||||
|
updateFn={(attachId) => {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
signedDocument: {
|
||||||
|
...prev.signedDocument,
|
||||||
|
documentAttachmentId: attachId
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
isSignedDocument={true}
|
||||||
|
docAttachmentId={data.signedDocument.documentAttachmentId}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
: <p>{__('Nessun documento firmato', 'gepafin')}</p>}
|
||||||
<h3>{__('Documenti allegati', 'gepafin')}</h3>
|
<h3>{__('Documenti allegati', 'gepafin')}</h3>
|
||||||
{!isEmpty(data.files)
|
{!isEmpty(data.files)
|
||||||
? <ListOfFiles
|
? <ListOfFiles
|
||||||
@@ -1592,7 +1853,8 @@ const DomandaEditInstructorManager = () => {
|
|||||||
</div> : null}
|
</div> : null}
|
||||||
{operationType === 'reject' && APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
|
{operationType === 'reject' && APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
|
||||||
? <div className="appForm__field">
|
? <div className="appForm__field">
|
||||||
<label className={classNames({ 'p-error': !finalDialogData.reason || isEmpty(finalDialogData.reason) })}>
|
<label
|
||||||
|
className={classNames({ 'p-error': !finalDialogData.reason || isEmpty(finalDialogData.reason) })}>
|
||||||
{__('Oggetto', 'gepafin')}
|
{__('Oggetto', 'gepafin')}
|
||||||
</label>
|
</label>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
@@ -1619,7 +1881,8 @@ const DomandaEditInstructorManager = () => {
|
|||||||
style={{ width: '100%' }}/>
|
style={{ width: '100%' }}/>
|
||||||
</div> : null}
|
</div> : null}
|
||||||
<div className="appForm__field">
|
<div className="appForm__field">
|
||||||
<label className={classNames({ 'p-error': !finalDialogData.motivation || isEmpty(finalDialogData.motivation) })}>
|
<label
|
||||||
|
className={classNames({ 'p-error': !finalDialogData.motivation || isEmpty(finalDialogData.motivation) })}>
|
||||||
{__('Motivazione', 'gepafin')}
|
{__('Motivazione', 'gepafin')}
|
||||||
</label>
|
</label>
|
||||||
<div translate="no">
|
<div translate="no">
|
||||||
@@ -1666,13 +1929,19 @@ const DomandaEditInstructorManager = () => {
|
|||||||
icon: 'pi pi-plus'
|
icon: 'pi pi-plus'
|
||||||
}}
|
}}
|
||||||
itemTemplate={(file, props) => {
|
itemTemplate={(file, props) => {
|
||||||
return(
|
return (
|
||||||
<div className="p-fileupload-row" data-pc-section="file">
|
<div className="p-fileupload-row" data-pc-section="file">
|
||||||
<div data-pc-section="details" style={{display: 'flex', flexDirection: 'column', gap: '10px', textAlign: 'left'}}>
|
<div data-pc-section="details" style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '10px',
|
||||||
|
textAlign: 'left'
|
||||||
|
}}>
|
||||||
<div className="p-fileupload-filename" data-pc-section="filename">
|
<div className="p-fileupload-filename" data-pc-section="filename">
|
||||||
{file.name}
|
{file.name}
|
||||||
</div>
|
</div>
|
||||||
<span data-pc-section="filesize">{getFormatedFileSizeText(file.size)}</span>
|
<span
|
||||||
|
data-pc-section="filesize">{getFormatedFileSizeText(file.size)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div data-pc-section="actions">
|
<div data-pc-section="actions">
|
||||||
<Button
|
<Button
|
||||||
@@ -1751,8 +2020,10 @@ const DomandaEditInstructorManager = () => {
|
|||||||
onHide={hidePreTecEvalDialog}>
|
onHide={hidePreTecEvalDialog}>
|
||||||
<div className="appForm__field">
|
<div className="appForm__field">
|
||||||
<label
|
<label
|
||||||
className={classNames({ 'p-error': isEmpty(preTecEvalData.amount)
|
className={classNames({
|
||||||
|| isNaN(parseFloat(preTecEvalData.amount)) || parseFloat(preTecEvalData.amount) <= 0 })}>
|
'p-error': isEmpty(preTecEvalData.amount)
|
||||||
|
|| isNaN(parseFloat(preTecEvalData.amount)) || parseFloat(preTecEvalData.amount) <= 0
|
||||||
|
})}>
|
||||||
{__('Importo', 'gepafin')}
|
{__('Importo', 'gepafin')}
|
||||||
</label>
|
</label>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
@@ -1764,6 +2035,102 @@ const DomandaEditInstructorManager = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
visible={isVisibleContractForm}
|
||||||
|
modal
|
||||||
|
header={headerContractDialog}
|
||||||
|
footer={footerContractDialog}
|
||||||
|
style={{ maxWidth: '600px', width: '100%' }}
|
||||||
|
onHide={hideContractDialog}>
|
||||||
|
<div className="appForm__field">
|
||||||
|
<label
|
||||||
|
className={classNames({ 'p-error': !contractFormData.subject || isEmpty(contractFormData.subject) })}>
|
||||||
|
{__('Oggetto', 'gepafin')}*
|
||||||
|
</label>
|
||||||
|
<InputText
|
||||||
|
value={contractFormData.subject}
|
||||||
|
invalid={isEmpty(contractFormData.subject)}
|
||||||
|
onChange={(e) => updateContractFormData(e.target.value, 'subject')}/>
|
||||||
|
</div>
|
||||||
|
<div className="appForm__field">
|
||||||
|
<label
|
||||||
|
className={classNames({ 'p-error': !contractFormData.text || isEmpty(contractFormData.text) })}>
|
||||||
|
{__('Testo', 'gepafin')}*
|
||||||
|
</label>
|
||||||
|
<div translate="no">
|
||||||
|
<Editor
|
||||||
|
value={contractFormData.text}
|
||||||
|
readOnly={loading}
|
||||||
|
placeholder={__('Digita qui il messagio', 'gepafin')}
|
||||||
|
headerTemplate={header}
|
||||||
|
onTextChange={(e) => updateContractFormData(e.htmlValue, 'text')}
|
||||||
|
style={{ height: 80 * 3, width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="appForm__field">
|
||||||
|
<label
|
||||||
|
className={classNames({ 'p-error': !contractFormData.files || isEmpty(contractFormData.files) })}>
|
||||||
|
{__('Files', 'gepafin')}*
|
||||||
|
</label>
|
||||||
|
<FileUpload
|
||||||
|
ref={contractFormFilesRef}
|
||||||
|
name="files[]"
|
||||||
|
multiple
|
||||||
|
accept={mimeTypes.map(o => o.code).join(',')}
|
||||||
|
maxFileSize={defaultMaxFileSize}
|
||||||
|
auto={false}
|
||||||
|
customUpload={true}
|
||||||
|
className={classNames({ 'p-invalid': !contractFormData.files || isEmpty(contractFormData.files) })}
|
||||||
|
onSelect={(e) => {
|
||||||
|
updateContractFormData(e.files, 'files');
|
||||||
|
}}
|
||||||
|
onRemove={(e) => {
|
||||||
|
const updatedFiles = contractFormFilesRef.current.getFiles();
|
||||||
|
updateContractFormData(updatedFiles, 'files');
|
||||||
|
}}
|
||||||
|
headerTemplate={(options) => {
|
||||||
|
const { chooseButton } = options;
|
||||||
|
return (
|
||||||
|
<div className="p-fileupload-buttonbar" data-pc-section="buttonbar">
|
||||||
|
{chooseButton}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
chooseOptions={{
|
||||||
|
label: __('Aggiungi i file', 'gepafin'),
|
||||||
|
icon: 'pi pi-plus'
|
||||||
|
}}
|
||||||
|
itemTemplate={(file, props) => {
|
||||||
|
return (
|
||||||
|
<div className="p-fileupload-row" data-pc-section="file">
|
||||||
|
<div data-pc-section="details" style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '10px',
|
||||||
|
textAlign: 'left'
|
||||||
|
}}>
|
||||||
|
<div className="p-fileupload-filename" data-pc-section="filename">
|
||||||
|
{file.name}
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
data-pc-section="filesize">{getFormatedFileSizeText(file.size)}</span>
|
||||||
|
</div>
|
||||||
|
<div data-pc-section="actions">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
icon="pi pi-times"
|
||||||
|
className="p-button-rounded p-button-danger p-button-text"
|
||||||
|
onClick={() => props.onRemove()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
emptyTemplate={<p className="m-0">{__('Trascina i file qua')}</p>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
: <>
|
: <>
|
||||||
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
|
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ const ArchiveDocument = ({
|
|||||||
fileId = 0,
|
fileId = 0,
|
||||||
fileDescr = '',
|
fileDescr = '',
|
||||||
docAttachmentId = null,
|
docAttachmentId = null,
|
||||||
updateFn = () => {}
|
updateFn = () => {},
|
||||||
|
isSignedDocument = false
|
||||||
}) => {
|
}) => {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [isVisibleDialog, setIsVisibleDialog] = useState(false);
|
const [isVisibleDialog, setIsVisibleDialog] = useState(false);
|
||||||
@@ -93,7 +94,9 @@ const ArchiveDocument = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AppointmentService.archiveDocument(applicationId, fileId, submitData, submitCallback, errSubmitCallback)
|
AppointmentService.archiveDocument(applicationId, fileId, submitData, submitCallback, errSubmitCallback, [
|
||||||
|
['isSignedDocument', isSignedDocument]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { storeGet, storeSet, useStoreValue } from '../../store';
|
|||||||
import ApplicationEvaluationService from '../../service/application-evaluation-service';
|
import ApplicationEvaluationService from '../../service/application-evaluation-service';
|
||||||
import AmendmentsService from '../../service/amendments-service';
|
import AmendmentsService from '../../service/amendments-service';
|
||||||
import AppointmentService from '../../service/appointment-service';
|
import AppointmentService from '../../service/appointment-service';
|
||||||
|
import ApplicationContractService from '../../service/application-contract-service';
|
||||||
|
|
||||||
// tools
|
// tools
|
||||||
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
||||||
@@ -60,6 +61,7 @@ import { SplitButton } from 'primereact/splitbutton';
|
|||||||
import { FileUpload } from 'primereact/fileupload';
|
import { FileUpload } from 'primereact/fileupload';
|
||||||
import { defaultMaxFileSize, mimeTypes, rejectionReasons } from '../../configData';
|
import { defaultMaxFileSize, mimeTypes, rejectionReasons } from '../../configData';
|
||||||
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
|
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
|
||||||
|
import ArchiveDocument from './components/ArchiveDocument';
|
||||||
|
|
||||||
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;
|
||||||
@@ -98,6 +100,12 @@ const DomandaEditPreInstructor = () => {
|
|||||||
duration: 0,
|
duration: 0,
|
||||||
amount: 0
|
amount: 0
|
||||||
});
|
});
|
||||||
|
const [isVisibleContractForm, setIsVisibleContractForm] = useState(false);
|
||||||
|
const [contractFormData, setContractFormData] = useState({
|
||||||
|
subject: '',
|
||||||
|
text: ''
|
||||||
|
});
|
||||||
|
const contractFormFilesRef = useRef(null);
|
||||||
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);
|
||||||
@@ -225,7 +233,7 @@ const DomandaEditPreInstructor = () => {
|
|||||||
const getCallback = (resp) => {
|
const getCallback = (resp) => {
|
||||||
if (resp.status === 'SUCCESS') {
|
if (resp.status === 'SUCCESS') {
|
||||||
setData(getFormattedData(resp.data));
|
setData(getFormattedData(resp.data));
|
||||||
setFinalDialogData((prev) => ({...prev, motivation: resp.data.motivation}));
|
setFinalDialogData((prev) => ({ ...prev, motivation: resp.data.motivation }));
|
||||||
updateFlagsForSoccorso(resp.data);
|
updateFlagsForSoccorso(resp.data);
|
||||||
|
|
||||||
if (resp.data.evaluationVersion === 'V2') {
|
if (resp.data.evaluationVersion === 'V2') {
|
||||||
@@ -290,7 +298,7 @@ const DomandaEditPreInstructor = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const header = renderHeader();
|
const header = renderHeader();
|
||||||
const technicalEvalItems = [
|
const tecnicalEvalItems = [
|
||||||
{
|
{
|
||||||
label: __('Nessuna garanzia', 'gepafin'),
|
label: __('Nessuna garanzia', 'gepafin'),
|
||||||
icon: 'pi pi-pen-to-square',
|
icon: 'pi pi-pen-to-square',
|
||||||
@@ -697,9 +705,9 @@ const DomandaEditPreInstructor = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const shouldDisableField = useCallback((fieldName) => {
|
const shouldDisableField = useCallback((fieldName) => {
|
||||||
return !['EVALUATION', 'ADMISSIBLE'].includes(data.applicationStatus)
|
return ['EXPIRED', 'CLOSE'].includes(data.status)
|
||||||
|| (['ADMISSIBLE', 'TECHNICAL_EVALUATION'].includes(data.applicationStatus) && !['criteria', 'note'].includes(fieldName))
|
|| (['ADMISSIBLE', 'TECHNICAL_EVALUATION', 'TECHNICAL_EVALUATION', 'AWAITING_TECHNICAL_EVALUATION'].includes(data.applicationStatus)
|
||||||
|| (fieldName === 'files' && !isEmpty(data.amendmentDetails))
|
&& !['criteria', 'note', 'files', 'amendmentDetails'].includes(fieldName))
|
||||||
}, [data.applicationStatus]);
|
}, [data.applicationStatus]);
|
||||||
|
|
||||||
const headerCompleteDialog = () => {
|
const headerCompleteDialog = () => {
|
||||||
@@ -932,6 +940,7 @@ const DomandaEditPreInstructor = () => {
|
|||||||
|
|
||||||
const getAmendmentSpecialCallback = (data) => {
|
const getAmendmentSpecialCallback = (data) => {
|
||||||
if (data.status === 'SUCCESS') {
|
if (data.status === 'SUCCESS') {
|
||||||
|
setData(getFormattedData(data.data));
|
||||||
if (toast.current && data.message) {
|
if (toast.current && data.message) {
|
||||||
toast.current.show({
|
toast.current.show({
|
||||||
severity: 'success',
|
severity: 'success',
|
||||||
@@ -999,6 +1008,106 @@ const DomandaEditPreInstructor = () => {
|
|||||||
setData(newData);
|
setData(newData);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
|
const openSendContractForm = () => {
|
||||||
|
setIsVisibleContractForm(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerContractDialog = () => {
|
||||||
|
return <span>{__('Invia il contratto', 'gepafin')}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hideContractDialog = () => {
|
||||||
|
setIsVisibleContractForm(false);
|
||||||
|
setContractFormData({
|
||||||
|
subject: '',
|
||||||
|
text: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const footerContractDialog = useCallback(() => {
|
||||||
|
let isDisabled = !contractFormData.subject || isEmpty(contractFormData.subject)
|
||||||
|
|| !contractFormData.text || isEmpty(contractFormData.text)
|
||||||
|
|| !contractFormData.files || isEmpty(contractFormData.files) || isAsyncRequest;
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<Button type="button" label={__('Annulla', 'gepafin')} onClick={hideContractDialog} outlined/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={isDisabled}
|
||||||
|
label={__('Invia', 'gepafin')} onClick={doSendContract}/>
|
||||||
|
</div>
|
||||||
|
}, [contractFormData]);
|
||||||
|
|
||||||
|
const updateContractFormData = (value, path) => {
|
||||||
|
const newData = wrap(contractFormData).set(path.split('.'), value).value();
|
||||||
|
setContractFormData(newData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const doSendContract = useCallback(() => {
|
||||||
|
if (
|
||||||
|
contractFormData.subject && !isEmpty(contractFormData.subject)
|
||||||
|
&& contractFormData.text && !isEmpty(contractFormData.text)
|
||||||
|
&& contractFormData.files && !isEmpty(contractFormData.files)
|
||||||
|
) {
|
||||||
|
const formDataToSend = new FormData();
|
||||||
|
const contractFormDataTemp = {
|
||||||
|
...contractFormData
|
||||||
|
};
|
||||||
|
delete contractFormDataTemp.files;
|
||||||
|
const jsonBlob = new Blob([JSON.stringify(contractFormDataTemp)], {
|
||||||
|
type: 'application/json'
|
||||||
|
});
|
||||||
|
formDataToSend.append('applicationContractRequest', jsonBlob);
|
||||||
|
|
||||||
|
if (contractFormData.files && contractFormData.files.length > 0) {
|
||||||
|
contractFormData.files.forEach((file) => {
|
||||||
|
formDataToSend.append('contractDocuments', file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
storeSet('setAsyncRequest');
|
||||||
|
|
||||||
|
ApplicationContractService.uploadApplicationContract(
|
||||||
|
data.applicationId,
|
||||||
|
formDataToSend,
|
||||||
|
getUploadApplicationContractCallback,
|
||||||
|
errGetUploadApplicationContractCallback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [contractFormData]);
|
||||||
|
|
||||||
|
const getUploadApplicationContractCallback = (data) => {
|
||||||
|
if (data.status === 'SUCCESS') {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
applicationStatus: 'AWAITING_CONTRACT',
|
||||||
|
contract: data.data
|
||||||
|
}));
|
||||||
|
if (toast.current && data.message) {
|
||||||
|
toast.current.show({
|
||||||
|
severity: 'success',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hideContractDialog();
|
||||||
|
storeSet('unsetAsyncRequest');
|
||||||
|
}
|
||||||
|
|
||||||
|
const errGetUploadApplicationContractCallback = (data) => {
|
||||||
|
if (toast.current && data.message) {
|
||||||
|
toast.current.show({
|
||||||
|
severity: data.status === 'SUCCESS' ? 'info' : 'error',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
hideContractDialog();
|
||||||
|
set404FromErrorResponse(data);
|
||||||
|
storeSet('unsetAsyncRequest');
|
||||||
|
}
|
||||||
|
|
||||||
const actionBtns = () => {
|
const actionBtns = () => {
|
||||||
return <div className="appPageSection__actions">
|
return <div className="appPageSection__actions">
|
||||||
{(['SOCCORSO', 'CLOSE', 'EVALUATION', 'NDG', 'APPOINTMENT', 'ADMISSIBLE',
|
{(['SOCCORSO', 'CLOSE', 'EVALUATION', 'NDG', 'APPOINTMENT', 'ADMISSIBLE',
|
||||||
@@ -1035,8 +1144,7 @@ const DomandaEditPreInstructor = () => {
|
|||||||
onClick={() => doSaveDraft()}
|
onClick={() => doSaveDraft()}
|
||||||
label={__('Crea valutazione', 'gepafin')}
|
label={__('Crea valutazione', 'gepafin')}
|
||||||
icon="pi pi-save" iconPos="right"/>}
|
icon="pi pi-save" iconPos="right"/>}
|
||||||
{APP_EVALUATION_FLOW_ID === '1' && ['EVALUATION'].includes(data.applicationStatus)
|
{APP_EVALUATION_FLOW_ID === '1' && APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
|
||||||
&& APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
|
|
||||||
? <Button
|
? <Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!data.id
|
disabled={!data.id
|
||||||
@@ -1103,7 +1211,7 @@ const DomandaEditPreInstructor = () => {
|
|||||||
? __('Punteggio sufficiente per passaggio alla valutazione tecnica ed economico finanziaria', 'gepafin')
|
? __('Punteggio sufficiente per passaggio alla valutazione tecnica ed economico finanziaria', 'gepafin')
|
||||||
: __('Punteggio non sufficiente per passaggio alla valutazione tecnica ed economico finanziaria', 'gepafin')}
|
: __('Punteggio non sufficiente per passaggio alla valutazione tecnica ed economico finanziaria', 'gepafin')}
|
||||||
severity={isAdmissible ? 'success' : 'warning'}
|
severity={isAdmissible ? 'success' : 'warning'}
|
||||||
model={technicalEvalItems}/> : null}
|
model={tecnicalEvalItems}/> : null}
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!data.id || !['TECHNICAL_EVALUATION'].includes(data.applicationStatus) || evaluationBlockedForUser(data)}
|
disabled={!data.id || !['TECHNICAL_EVALUATION'].includes(data.applicationStatus) || evaluationBlockedForUser(data)}
|
||||||
@@ -1135,6 +1243,13 @@ const DomandaEditPreInstructor = () => {
|
|||||||
onClick={initiateRejecting}
|
onClick={initiateRejecting}
|
||||||
label={__('Respingi domanda', 'gepafin')}
|
label={__('Respingi domanda', 'gepafin')}
|
||||||
icon="pi pi-times" iconPos="right"/> : null}
|
icon="pi pi-times" iconPos="right"/> : null}
|
||||||
|
{APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE' && data.applicationStatus === 'APPROVED'
|
||||||
|
? <Button
|
||||||
|
type="button"
|
||||||
|
disabled={!data.id}
|
||||||
|
onClick={openSendContractForm}
|
||||||
|
label={__('Invia il contratto', 'gepafin')}
|
||||||
|
/> : null}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1167,7 +1282,7 @@ const DomandaEditPreInstructor = () => {
|
|||||||
? <div className="appPageSection__message info">
|
? <div className="appPageSection__message info">
|
||||||
<i className="pi pi-info-circle"></i>
|
<i className="pi pi-info-circle"></i>
|
||||||
<span className="summary">{__('Motivazione:', 'gepafin')}</span>
|
<span className="summary">{__('Motivazione:', 'gepafin')}</span>
|
||||||
<div style={{display: 'flex', flexDirection: 'column', gap: 10}}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||||||
<span>{renderHtmlContent(data.motivation)}</span>
|
<span>{renderHtmlContent(data.motivation)}</span>
|
||||||
{rejectionFiles()}
|
{rejectionFiles()}
|
||||||
</div>
|
</div>
|
||||||
@@ -1329,6 +1444,76 @@ const DomandaEditPreInstructor = () => {
|
|||||||
|
|
||||||
{data.evaluationVersion === 'V2'
|
{data.evaluationVersion === 'V2'
|
||||||
? <div className="appPageSection">
|
? <div className="appPageSection">
|
||||||
|
<h3>{__('Contratto', 'gepafin')}</h3>
|
||||||
|
{data.contract && data.contract?.beneficiaryDocuments && !isEmpty(data.contract?.beneficiaryDocuments)
|
||||||
|
? <ol className="appPageSection__list">
|
||||||
|
{data.contract?.beneficiaryDocuments.map(o => <li key={o.id} className="appPageSection__listItem">
|
||||||
|
<div className="appPageSection__listItemRow">
|
||||||
|
<div>{renderHtmlContent(o.name)}</div>
|
||||||
|
<div className="appPageSection__iconActions">
|
||||||
|
<Button icon="pi pi-eye" rounded
|
||||||
|
onClick={() => {
|
||||||
|
window.open(o.fileDetail[0].filePath, '_blank').focus()
|
||||||
|
}}
|
||||||
|
outlined severity="info"
|
||||||
|
aria-label={__('Mostra', 'gepafin')}/>
|
||||||
|
<ArchiveDocument
|
||||||
|
ndg={data.ndg}
|
||||||
|
applicationId={id}
|
||||||
|
fileDescr={renderHtmlContent(o.name)}
|
||||||
|
fileId={o.id}
|
||||||
|
updateFn={(attachId) => {
|
||||||
|
setData((prev) => {
|
||||||
|
const newDocuments = data.contract.beneficiaryDocuments
|
||||||
|
.map(ob => {
|
||||||
|
return ob.id === o.id
|
||||||
|
? {...o, documentAttachmentId: attachId}
|
||||||
|
: o;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
contract: {
|
||||||
|
...prev.contract,
|
||||||
|
beneficiaryDocuments: newDocuments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
isSignedDocument={true}
|
||||||
|
docAttachmentId={o.documentAttachmentId}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>)}
|
||||||
|
</ol>
|
||||||
|
: <p>{__('Nessun documento firmato', 'gepafin')}</p>}
|
||||||
|
<h2>{__('Domanda PDF firmato', 'gepafin')}</h2>
|
||||||
|
{data.signedDocument && !isEmpty(data.signedDocument)
|
||||||
|
? <ol className="appPageSection__list">
|
||||||
|
<li className="appPageSection__listItem">
|
||||||
|
<div className="appPageSection__listItemRow">
|
||||||
|
<div>{renderHtmlContent(data.signedDocument.fileName)}</div>
|
||||||
|
<div className="appPageSection__iconActions">
|
||||||
|
<ArchiveDocument
|
||||||
|
ndg={data.ndg}
|
||||||
|
applicationId={id}
|
||||||
|
fileDescr={renderHtmlContent(data.signedDocument.fileName)}
|
||||||
|
fileId={data.signedDocument.id}
|
||||||
|
updateFn={(attachId) => {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
signedDocument: {
|
||||||
|
...prev.signedDocument,
|
||||||
|
documentAttachmentId: attachId
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
isSignedDocument={true}
|
||||||
|
docAttachmentId={data.signedDocument.documentAttachmentId}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
: <p>{__('Nessun documento firmato', 'gepafin')}</p>}
|
||||||
<h2>{__('Documenti allegati', 'gepafin')}</h2>
|
<h2>{__('Documenti allegati', 'gepafin')}</h2>
|
||||||
{!isEmpty(data.files)
|
{!isEmpty(data.files)
|
||||||
? <ListOfFiles
|
? <ListOfFiles
|
||||||
@@ -1441,6 +1626,82 @@ const DomandaEditPreInstructor = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<h3>{__('Contratto', 'gepafin')}</h3>
|
||||||
|
{data.contract && data.contract?.beneficiaryDocuments && !isEmpty(data.contract?.beneficiaryDocuments)
|
||||||
|
? <ol className="appPageSection__list">
|
||||||
|
{data.contract?.beneficiaryDocuments.map(o => <li key={o.id} className="appPageSection__listItem">
|
||||||
|
<div className="appPageSection__listItemRow">
|
||||||
|
<div>{renderHtmlContent(o.name)}</div>
|
||||||
|
<div className="appPageSection__iconActions">
|
||||||
|
<Button icon="pi pi-eye" rounded
|
||||||
|
onClick={() => {
|
||||||
|
window.open(o.fileDetail[0].filePath, '_blank').focus()
|
||||||
|
}}
|
||||||
|
outlined severity="info"
|
||||||
|
aria-label={__('Mostra', 'gepafin')}/>
|
||||||
|
<ArchiveDocument
|
||||||
|
ndg={data.ndg}
|
||||||
|
applicationId={id}
|
||||||
|
fileDescr={renderHtmlContent(o.name)}
|
||||||
|
fileId={o.id}
|
||||||
|
updateFn={(attachId) => {
|
||||||
|
setData((prev) => {
|
||||||
|
const newDocuments = data.contract.beneficiaryDocuments
|
||||||
|
.map(ob => {
|
||||||
|
return ob.id === o.id
|
||||||
|
? {...o, documentAttachmentId: attachId}
|
||||||
|
: o;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
contract: {
|
||||||
|
...prev.contract,
|
||||||
|
beneficiaryDocuments: newDocuments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
isSignedDocument={true}
|
||||||
|
docAttachmentId={o.documentAttachmentId}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>)}
|
||||||
|
</ol>
|
||||||
|
: <p>{__('Nessun documento firmato', 'gepafin')}</p>}
|
||||||
|
<h3>{__('Domanda PDF firmato', 'gepafin')}</h3>
|
||||||
|
{data.signedDocument && !isEmpty(data.signedDocument)
|
||||||
|
? <ol className="appPageSection__list">
|
||||||
|
<li className="appPageSection__listItem">
|
||||||
|
<div className="appPageSection__listItemRow">
|
||||||
|
<div>{renderHtmlContent(data.signedDocument.fileName)}</div>
|
||||||
|
<div className="appPageSection__iconActions">
|
||||||
|
<Button icon="pi pi-eye" rounded
|
||||||
|
onClick={() => {
|
||||||
|
window.open(data.signedDocument.filePath, '_blank').focus()
|
||||||
|
}}
|
||||||
|
outlined severity="info"
|
||||||
|
aria-label={__('Mostra', 'gepafin')}/>
|
||||||
|
<ArchiveDocument
|
||||||
|
ndg={data.ndg}
|
||||||
|
applicationId={id}
|
||||||
|
fileDescr={renderHtmlContent(data.signedDocument.fileName)}
|
||||||
|
fileId={data.signedDocument.id}
|
||||||
|
updateFn={(attachId) => {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
signedDocument: {
|
||||||
|
...prev.signedDocument,
|
||||||
|
documentAttachmentId: attachId
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
isSignedDocument={true}
|
||||||
|
docAttachmentId={data.signedDocument.documentAttachmentId}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
: <p>{__('Nessun documento firmato', 'gepafin')}</p>}
|
||||||
<h3>{__('Documenti allegati', 'gepafin')}</h3>
|
<h3>{__('Documenti allegati', 'gepafin')}</h3>
|
||||||
{!isEmpty(data.files)
|
{!isEmpty(data.files)
|
||||||
? <ListOfFiles
|
? <ListOfFiles
|
||||||
@@ -1592,7 +1853,8 @@ const DomandaEditPreInstructor = () => {
|
|||||||
</div> : null}
|
</div> : null}
|
||||||
{operationType === 'reject' && APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
|
{operationType === 'reject' && APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE'
|
||||||
? <div className="appForm__field">
|
? <div className="appForm__field">
|
||||||
<label className={classNames({ 'p-error': !finalDialogData.reason || isEmpty(finalDialogData.reason) })}>
|
<label
|
||||||
|
className={classNames({ 'p-error': !finalDialogData.reason || isEmpty(finalDialogData.reason) })}>
|
||||||
{__('Oggetto', 'gepafin')}
|
{__('Oggetto', 'gepafin')}
|
||||||
</label>
|
</label>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
@@ -1619,7 +1881,8 @@ const DomandaEditPreInstructor = () => {
|
|||||||
style={{ width: '100%' }}/>
|
style={{ width: '100%' }}/>
|
||||||
</div> : null}
|
</div> : null}
|
||||||
<div className="appForm__field">
|
<div className="appForm__field">
|
||||||
<label className={classNames({ 'p-error': !finalDialogData.motivation || isEmpty(finalDialogData.motivation) })}>
|
<label
|
||||||
|
className={classNames({ 'p-error': !finalDialogData.motivation || isEmpty(finalDialogData.motivation) })}>
|
||||||
{__('Motivazione', 'gepafin')}
|
{__('Motivazione', 'gepafin')}
|
||||||
</label>
|
</label>
|
||||||
<div translate="no">
|
<div translate="no">
|
||||||
@@ -1666,13 +1929,19 @@ const DomandaEditPreInstructor = () => {
|
|||||||
icon: 'pi pi-plus'
|
icon: 'pi pi-plus'
|
||||||
}}
|
}}
|
||||||
itemTemplate={(file, props) => {
|
itemTemplate={(file, props) => {
|
||||||
return(
|
return (
|
||||||
<div className="p-fileupload-row" data-pc-section="file">
|
<div className="p-fileupload-row" data-pc-section="file">
|
||||||
<div data-pc-section="details" style={{display: 'flex', flexDirection: 'column', gap: '10px', textAlign: 'left'}}>
|
<div data-pc-section="details" style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '10px',
|
||||||
|
textAlign: 'left'
|
||||||
|
}}>
|
||||||
<div className="p-fileupload-filename" data-pc-section="filename">
|
<div className="p-fileupload-filename" data-pc-section="filename">
|
||||||
{file.name}
|
{file.name}
|
||||||
</div>
|
</div>
|
||||||
<span data-pc-section="filesize">{getFormatedFileSizeText(file.size)}</span>
|
<span
|
||||||
|
data-pc-section="filesize">{getFormatedFileSizeText(file.size)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div data-pc-section="actions">
|
<div data-pc-section="actions">
|
||||||
<Button
|
<Button
|
||||||
@@ -1751,8 +2020,10 @@ const DomandaEditPreInstructor = () => {
|
|||||||
onHide={hidePreTecEvalDialog}>
|
onHide={hidePreTecEvalDialog}>
|
||||||
<div className="appForm__field">
|
<div className="appForm__field">
|
||||||
<label
|
<label
|
||||||
className={classNames({ 'p-error': isEmpty(preTecEvalData.amount)
|
className={classNames({
|
||||||
|| isNaN(parseFloat(preTecEvalData.amount)) || parseFloat(preTecEvalData.amount) <= 0 })}>
|
'p-error': isEmpty(preTecEvalData.amount)
|
||||||
|
|| isNaN(parseFloat(preTecEvalData.amount)) || parseFloat(preTecEvalData.amount) <= 0
|
||||||
|
})}>
|
||||||
{__('Importo', 'gepafin')}
|
{__('Importo', 'gepafin')}
|
||||||
</label>
|
</label>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
@@ -1764,6 +2035,102 @@ const DomandaEditPreInstructor = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
visible={isVisibleContractForm}
|
||||||
|
modal
|
||||||
|
header={headerContractDialog}
|
||||||
|
footer={footerContractDialog}
|
||||||
|
style={{ maxWidth: '600px', width: '100%' }}
|
||||||
|
onHide={hideContractDialog}>
|
||||||
|
<div className="appForm__field">
|
||||||
|
<label
|
||||||
|
className={classNames({ 'p-error': !contractFormData.subject || isEmpty(contractFormData.subject) })}>
|
||||||
|
{__('Oggetto', 'gepafin')}*
|
||||||
|
</label>
|
||||||
|
<InputText
|
||||||
|
value={contractFormData.subject}
|
||||||
|
invalid={isEmpty(contractFormData.subject)}
|
||||||
|
onChange={(e) => updateContractFormData(e.target.value, 'subject')}/>
|
||||||
|
</div>
|
||||||
|
<div className="appForm__field">
|
||||||
|
<label
|
||||||
|
className={classNames({ 'p-error': !contractFormData.text || isEmpty(contractFormData.text) })}>
|
||||||
|
{__('Testo', 'gepafin')}*
|
||||||
|
</label>
|
||||||
|
<div translate="no">
|
||||||
|
<Editor
|
||||||
|
value={contractFormData.text}
|
||||||
|
readOnly={loading}
|
||||||
|
placeholder={__('Digita qui il messagio', 'gepafin')}
|
||||||
|
headerTemplate={header}
|
||||||
|
onTextChange={(e) => updateContractFormData(e.htmlValue, 'text')}
|
||||||
|
style={{ height: 80 * 3, width: '100%' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="appForm__field">
|
||||||
|
<label
|
||||||
|
className={classNames({ 'p-error': !contractFormData.files || isEmpty(contractFormData.files) })}>
|
||||||
|
{__('Files', 'gepafin')}*
|
||||||
|
</label>
|
||||||
|
<FileUpload
|
||||||
|
ref={contractFormFilesRef}
|
||||||
|
name="files[]"
|
||||||
|
multiple
|
||||||
|
accept={mimeTypes.map(o => o.code).join(',')}
|
||||||
|
maxFileSize={defaultMaxFileSize}
|
||||||
|
auto={false}
|
||||||
|
customUpload={true}
|
||||||
|
className={classNames({ 'p-invalid': !contractFormData.files || isEmpty(contractFormData.files) })}
|
||||||
|
onSelect={(e) => {
|
||||||
|
updateContractFormData(e.files, 'files');
|
||||||
|
}}
|
||||||
|
onRemove={(e) => {
|
||||||
|
const updatedFiles = contractFormFilesRef.current.getFiles();
|
||||||
|
updateContractFormData(updatedFiles, 'files');
|
||||||
|
}}
|
||||||
|
headerTemplate={(options) => {
|
||||||
|
const { chooseButton } = options;
|
||||||
|
return (
|
||||||
|
<div className="p-fileupload-buttonbar" data-pc-section="buttonbar">
|
||||||
|
{chooseButton}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
chooseOptions={{
|
||||||
|
label: __('Aggiungi i file', 'gepafin'),
|
||||||
|
icon: 'pi pi-plus'
|
||||||
|
}}
|
||||||
|
itemTemplate={(file, props) => {
|
||||||
|
return (
|
||||||
|
<div className="p-fileupload-row" data-pc-section="file">
|
||||||
|
<div data-pc-section="details" style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '10px',
|
||||||
|
textAlign: 'left'
|
||||||
|
}}>
|
||||||
|
<div className="p-fileupload-filename" data-pc-section="filename">
|
||||||
|
{file.name}
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
data-pc-section="filesize">{getFormatedFileSizeText(file.size)}</span>
|
||||||
|
</div>
|
||||||
|
<div data-pc-section="actions">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
icon="pi pi-times"
|
||||||
|
className="p-button-rounded p-button-danger p-button-text"
|
||||||
|
onClick={() => props.onRemove()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
emptyTemplate={<p className="m-0">{__('Trascina i file qua')}</p>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
: <>
|
: <>
|
||||||
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
|
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ const DomandeArchive = () => {
|
|||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
<h2>{__('Domande completate', 'gepafin')}</h2>
|
<h2>{__('Domande completate', 'gepafin')}</h2>
|
||||||
<DomandeTablePreInstructorAsync statuses={['CLOSE']} applicationStatuses={['APPROVED', 'REJECTED', 'TECHNICAL_EVALUATION_REJECTED']} userId={0}/>
|
<DomandeTablePreInstructorAsync
|
||||||
|
statuses={['CLOSE', 'AWAITING_CONTRACT', 'CONTRACT_SIGNED']}
|
||||||
|
applicationStatuses={['APPROVED', 'REJECTED', 'TECHNICAL_EVALUATION_REJECTED', 'AWAITING_CONTRACT', 'CONTRACT_SIGNED']}
|
||||||
|
userId={0}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ const DomandeArchivePreInstructor = () => {
|
|||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
<h2>{__('Domande completate', 'gepafin')}</h2>
|
<h2>{__('Domande completate', 'gepafin')}</h2>
|
||||||
<DomandeTablePreInstructorAsync statuses={['CLOSE']} applicationStatuses={['APPROVED', 'REJECTED', 'TECHNICAL_EVALUATION_REJECTED']} userId={userData.id}/>
|
<DomandeTablePreInstructorAsync
|
||||||
|
statuses={['CLOSE', 'AWAITING_CONTRACT', 'CONTRACT_SIGNED']}
|
||||||
|
applicationStatuses={['APPROVED', 'REJECTED', 'TECHNICAL_EVALUATION_REJECTED', 'AWAITING_CONTRACT', 'CONTRACT_SIGNED']}
|
||||||
|
userId={userData.id}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const AllDomandeBeneficiarioTableAsync = ({ statuses }) => {
|
|||||||
companyName: { value: null, matchMode: 'contains' },
|
companyName: { value: null, matchMode: 'contains' },
|
||||||
submissionDate: { value: null, matchMode: 'dateIs' },
|
submissionDate: { value: null, matchMode: 'dateIs' },
|
||||||
assignedUserName: { value: null, matchMode: 'equals' },
|
assignedUserName: { value: null, matchMode: 'equals' },
|
||||||
status: { value: null, matchMode: 'equals' }
|
applicationStatus: { value: null, matchMode: 'equals' }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -122,13 +122,14 @@ const AllDomandeBeneficiarioTableAsync = ({ statuses }) => {
|
|||||||
options.filterCallback(e.value, options.index)
|
options.filterCallback(e.value, options.index)
|
||||||
const filters = { ...lazyState.filters };
|
const filters = { ...lazyState.filters };
|
||||||
if (e.value) {
|
if (e.value) {
|
||||||
filters['status'] = { value: e.value, matchMode: 'equals' };
|
filters['applicationStatus'] = { value: e.value, matchMode: 'equals' };
|
||||||
} else {
|
} else {
|
||||||
delete filters['status'];
|
delete filters['applicationStatus'];
|
||||||
}
|
}
|
||||||
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) => {
|
||||||
@@ -182,7 +183,7 @@ const AllDomandeBeneficiarioTableAsync = ({ statuses }) => {
|
|||||||
filterMatchModeOptions={translationStrings.dateFilterOptions}
|
filterMatchModeOptions={translationStrings.dateFilterOptions}
|
||||||
style={{ minWidth: '8rem' }}
|
style={{ minWidth: '8rem' }}
|
||||||
body={dateAppliedBodyTemplate}/>
|
body={dateAppliedBodyTemplate}/>
|
||||||
<Column field="status" header={__('Stato', 'gepafin')}
|
<Column field="applicationStatus" header={__('Stato', 'gepafin')}
|
||||||
filterElement={statusFilterTemplate} filter
|
filterElement={statusFilterTemplate} filter
|
||||||
filterMatchModeOptions={translationStrings.statusFilterOptions}
|
filterMatchModeOptions={translationStrings.statusFilterOptions}
|
||||||
style={{ minWidth: '8rem' }}
|
style={{ minWidth: '8rem' }}
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ const DomandeBeneficiario = () => {
|
|||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
<AllDomandeBeneficiarioTableAsync statuses={['SOCCORSO', 'APPROVED', 'REJECTED', 'EVALUATION', 'SUBMIT',
|
<AllDomandeBeneficiarioTableAsync
|
||||||
'APPOINTMENT', 'NDG', 'ADMISSIBLE', 'AWAITING_TECHNICAL_EVALUATION', 'TECHNICAL_EVALUATION']}/>
|
statuses={['SOCCORSO', 'APPROVED', 'REJECTED', 'EVALUATION', 'SUBMIT',
|
||||||
|
'APPOINTMENT', 'NDG', 'ADMISSIBLE', 'AWAITING_TECHNICAL_EVALUATION', 'TECHNICAL_EVALUATION',
|
||||||
|
'AWAITING_CONTRACT', 'CONTRACT_SIGNED']}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ const SoccorsoEditBeneficiario = () => {
|
|||||||
const goToArchivePage = () => {
|
const goToArchivePage = () => {
|
||||||
navigate(`/domande`);
|
navigate(`/domande`);
|
||||||
}
|
}
|
||||||
|
console.log('data', data.amendmentType)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const parsedId = parseInt(id);
|
const parsedId = parseInt(id);
|
||||||
const entityId = !isNaN(parsedId) ? parsedId : 0;
|
const entityId = !isNaN(parsedId) ? parsedId : 0;
|
||||||
@@ -115,6 +115,15 @@ const SoccorsoEditBeneficiario = () => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isSpecialAmendmentValidationFailed = () => {
|
||||||
|
if (data.amendmentType === 'SPECIAL') {
|
||||||
|
const formValues = getValues();
|
||||||
|
const amendmentDocuments = formValues.amendmentDocuments;
|
||||||
|
return !amendmentDocuments || amendmentDocuments.length === 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -413,6 +422,9 @@ const SoccorsoEditBeneficiario = () => {
|
|||||||
errors={errors}
|
errors={errors}
|
||||||
defaultValue={formInitialData.amendmentDocuments ? formInitialData.amendmentDocuments : []}
|
defaultValue={formInitialData.amendmentDocuments ? formInitialData.amendmentDocuments : []}
|
||||||
accept={[]}
|
accept={[]}
|
||||||
|
config={{
|
||||||
|
required: true
|
||||||
|
}}
|
||||||
source="amendment"
|
source="amendment"
|
||||||
sourceId={data.id}
|
sourceId={data.id}
|
||||||
multiple={true}
|
multiple={true}
|
||||||
@@ -432,7 +444,7 @@ const SoccorsoEditBeneficiario = () => {
|
|||||||
{data.id
|
{data.id
|
||||||
? <Button
|
? <Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={isAsyncRequest || data.status !== 'AWAITING'}
|
disabled={isAsyncRequest || data.status !== 'AWAITING' || isSpecialAmendmentValidationFailed()}
|
||||||
onClick={doUpdateAmendmentAndCompleteTask}
|
onClick={doUpdateAmendmentAndCompleteTask}
|
||||||
label={__('Invia documenti', 'gepafin')}
|
label={__('Invia documenti', 'gepafin')}
|
||||||
icon="pi pi-save" iconPos="right"/> : null}
|
icon="pi pi-save" iconPos="right"/> : null}
|
||||||
|
|||||||
18
src/service/application-contract-service.js
Normal file
18
src/service/application-contract-service.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { NetworkService } from './network-service';
|
||||||
|
|
||||||
|
const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
|
||||||
|
|
||||||
|
export default class ApplicationContractService {
|
||||||
|
|
||||||
|
static getContractByUserId = (callback, errCallback, queryParams) => {
|
||||||
|
NetworkService.get(`${API_BASE_URL}/applicationContract/user`, callback, errCallback, queryParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
static uploadApplicationContract = (applicationId, body, callback, errCallback, queryParams) => {
|
||||||
|
NetworkService.postMultiPart(`${API_BASE_URL}/applicationContract/application/${applicationId}`, body, callback, errCallback, queryParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
static updateApplicationContract = (contractId, body, callback, errCallback, queryParams) => {
|
||||||
|
NetworkService.putMultiPart(`${API_BASE_URL}/applicationContract/${contractId}`, body, callback, errCallback, queryParams);
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user