465 lines
20 KiB
JavaScript
465 lines
20 KiB
JavaScript
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
||
import { __, sprintf } from '@wordpress/i18n';
|
||
import { useNavigate, useParams } from 'react-router-dom';
|
||
import { is, isEmpty } from 'ramda';
|
||
import { useForm } from 'react-hook-form';
|
||
import { klona } from 'klona';
|
||
import { wrap } from 'object-path-immutable';
|
||
|
||
// store
|
||
import { storeSet, useStore } from '../../store';
|
||
|
||
// api
|
||
import AmendmentsService from '../../service/amendments-service';
|
||
import ApplicationService from '../../service/application-service';
|
||
|
||
// tools
|
||
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
||
import getBandoLabel from '../../helpers/getBandoLabel';
|
||
import getDateFromISOstring from '../../helpers/getDateFromISOstring';
|
||
import renderHtmlContent from '../../helpers/renderHtmlContent';
|
||
|
||
// components
|
||
import { Button } from 'primereact/button';
|
||
import BlockingOverlay from '../../components/BlockingOverlay';
|
||
import { Toast } from 'primereact/toast';
|
||
import { Dialog } from 'primereact/dialog';
|
||
import FormField from '../../components/FormField';
|
||
import SoccorsoComunications from '../SoccorsoEditPreInstructor/components/SoccorsoComunications';
|
||
import { Editor } from 'primereact/editor';
|
||
|
||
const SoccorsoEditBeneficiario = () => {
|
||
const isAsyncRequest = useStore().main.isAsyncRequest();
|
||
const { id } = useParams();
|
||
const navigate = useNavigate();
|
||
const [data, setData] = useState({});
|
||
const [dataAppl, setDataAppl] = useState({});
|
||
const [isVisibleEmailDialog, setIsVisibleEmailDialog] = useState(false);
|
||
const toast = useRef(null);
|
||
const [formInitialData, setFormInitialData] = useState({});
|
||
const {
|
||
control,
|
||
handleSubmit,
|
||
formState: { errors },
|
||
setValue,
|
||
register,
|
||
trigger,
|
||
getValues
|
||
} = useForm({
|
||
defaultValues: useMemo(() => {
|
||
return formInitialData;
|
||
}, [formInitialData]), mode: 'onChange'
|
||
});
|
||
|
||
const goToArchivePage = () => {
|
||
navigate(`/domande`);
|
||
}
|
||
|
||
useEffect(() => {
|
||
const parsedId = parseInt(id);
|
||
const entityId = !isNaN(parsedId) ? parsedId : 0;
|
||
|
||
AmendmentsService.getSoccorsoByApplId(entityId, getCallback, errGetCallback, [
|
||
['statuses', 'AWAITING']
|
||
]);
|
||
}, [id]);
|
||
|
||
const getCallback = (data) => {
|
||
if (data.status === 'SUCCESS') {
|
||
if (data.data.length) {
|
||
const amendmentObj = data.data[0];
|
||
setData(getFormattedData(amendmentObj));
|
||
let formDataInitial = amendmentObj.applicationFormFields.reduce((acc, cur) => {
|
||
if (cur.fieldValue) {
|
||
acc[cur.fieldId] = cur.fieldValue;
|
||
}
|
||
return acc;
|
||
}, {});
|
||
formDataInitial = {
|
||
...formDataInitial,
|
||
amendmentDocuments: amendmentObj.amendmentDocuments
|
||
}
|
||
setFormInitialData(formDataInitial);
|
||
storeSet.main.unsetAsyncRequest();
|
||
} else {
|
||
ApplicationService.getApplication(id, getApplCallback, errGetCallback)
|
||
}
|
||
}
|
||
}
|
||
|
||
const errGetCallback = (data) => {
|
||
if (toast.current && data.message) {
|
||
toast.current.show({
|
||
severity: 'error',
|
||
summary: '',
|
||
detail: data.message
|
||
});
|
||
}
|
||
set404FromErrorResponse(data);
|
||
storeSet.main.unsetAsyncRequest();
|
||
}
|
||
|
||
const getApplCallback = (data) => {
|
||
if (data.status === 'SUCCESS') {
|
||
setDataAppl(getFormattedData(data.data));
|
||
}
|
||
storeSet.main.unsetAsyncRequest();
|
||
}
|
||
|
||
const getFormattedData = (data) => {
|
||
data.evaluationEndDate = is(String, data.evaluationEndDate) ? new Date(data.evaluationEndDate) : (data.evaluationEndDate ? data.evaluationEndDate : '');
|
||
data.startDate = is(String, data.startDate) ? new Date(data.startDate) : (data.startDate ? data.startDate : '');
|
||
data.expirationDate = is(String, data.expirationDate) ? new Date(data.expirationDate) : (data.expirationDate ? data.expirationDate : '');
|
||
return data;
|
||
};
|
||
|
||
const onSubmit = () => {
|
||
};
|
||
|
||
const doUpdateAmendment = (doClose = false) => {
|
||
trigger();
|
||
let formValues = klona(getValues());
|
||
const newFormValues = Object.keys(formValues)
|
||
.filter(v => v !== 'amendmentDocuments')
|
||
.reduce((acc, cur) => {
|
||
let fieldVal = formValues[cur];
|
||
|
||
fieldVal = isEmpty(fieldVal) ? null : fieldVal;
|
||
fieldVal = is(Array, fieldVal) ? fieldVal.map(o => o.id).join(',') : null;
|
||
|
||
acc.push({
|
||
'fieldId': cur,
|
||
'fieldValue': fieldVal
|
||
});
|
||
return acc;
|
||
}, []);
|
||
const newAmendDocs = formValues.amendmentDocuments
|
||
? formValues.amendmentDocuments.map(o => o.id).join(',')
|
||
: '';
|
||
|
||
const submitData = {
|
||
applicationFormFields: newFormValues,
|
||
amendmentDocuments: newAmendDocs,
|
||
amendmentNotes: data.amendmentNotes
|
||
}
|
||
const amendmentId = data.id;
|
||
|
||
storeSet.main.setAsyncRequest();
|
||
|
||
AmendmentsService.updateSoccorso(
|
||
amendmentId,
|
||
submitData,
|
||
(resp) => updateAmendmentCallback(resp, doClose),
|
||
errUpdateAmendmentCallback
|
||
);
|
||
}
|
||
|
||
const doUpdateAmendmentAndCompleteTask = () => {
|
||
doUpdateAmendment(true);
|
||
}
|
||
|
||
const updateAmendmentCallback = (data, doClose = false) => {
|
||
if (data.status === 'SUCCESS') {
|
||
setData(getFormattedData(data.data));
|
||
|
||
if (doClose) {
|
||
AmendmentsService.updateStatusSoccorso(data.data.id, updateAmendmentCallback, errUpdateAmendmentCallback, [
|
||
['status', 'RESPONSE_RECEIVED']
|
||
]);
|
||
} else {
|
||
if (toast.current) {
|
||
toast.current.show({
|
||
severity: 'success',
|
||
summary: '',
|
||
detail: data.message
|
||
});
|
||
}
|
||
let formDataInitial = data.data.applicationFormFields.reduce((acc, cur) => {
|
||
if (cur.fieldValue) {
|
||
acc[cur.fieldId] = cur.fieldValue;
|
||
}
|
||
return acc;
|
||
}, {});
|
||
formDataInitial = {
|
||
...formDataInitial,
|
||
amendmentDocuments: data.data.amendmentDocuments
|
||
}
|
||
setFormInitialData(formDataInitial);
|
||
}
|
||
}
|
||
storeSet.main.unsetAsyncRequest();
|
||
}
|
||
|
||
const errUpdateAmendmentCallback = (data) => {
|
||
if (toast.current && data.message) {
|
||
toast.current.show({
|
||
severity: 'error',
|
||
summary: '',
|
||
detail: data.message
|
||
});
|
||
}
|
||
//set404FromErrorResponse(data);
|
||
storeSet.main.unsetAsyncRequest();
|
||
}
|
||
|
||
const updateNewAmendmentData = (value, path) => {
|
||
const newData = wrap(data).set(path, value).value();
|
||
setData(newData);
|
||
}
|
||
|
||
const renderHeader = () => {
|
||
return (
|
||
<span className="ql-formats">
|
||
<button className="ql-bold" aria-label="Bold"></button>
|
||
<button className="ql-italic" aria-label="Italic"></button>
|
||
<button className="ql-underline" aria-label="Underline"></button>
|
||
<button className="ql-link" aria-label="Link"></button>
|
||
<button className="ql-list" value="ordered"></button>
|
||
<button className="ql-header" value="2"></button>
|
||
<button className="ql-header" value="3"></button>
|
||
<button className="ql-blockquote"></button>
|
||
<button className="ql-list" value="bullet"></button>
|
||
<button className="ql-indent" value="-1"></button>
|
||
<button className="ql-indent" value="+1"></button>
|
||
</span>
|
||
);
|
||
};
|
||
|
||
const header = renderHeader();
|
||
|
||
useEffect(() => {
|
||
if (formInitialData) {
|
||
//reset();
|
||
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
|
||
trigger();
|
||
}
|
||
}, [formInitialData]);
|
||
|
||
return (
|
||
<div className="appPage">
|
||
<div className="appPage__pageHeader">
|
||
{data.id
|
||
? <h1>
|
||
{sprintf(__('Soccorso Istruttorio: richiesta integrazione documenti per domanda #%s', 'gepafin'), id)}
|
||
</h1> : null}
|
||
{dataAppl.id
|
||
? <h1>
|
||
{sprintf(__('Dettagli: domanda #%s', 'gepafin'), dataAppl.id)}
|
||
</h1> : null}
|
||
</div>
|
||
|
||
<div className="appPage__spacer"></div>
|
||
<Toast ref={toast}/>
|
||
<BlockingOverlay shouldDisplay={isAsyncRequest}/>
|
||
|
||
<div className="appPageSection__row">
|
||
<Button
|
||
type="button"
|
||
outlined
|
||
onClick={goToArchivePage}
|
||
label={__('Indietro', 'gepafin')}
|
||
icon="pi pi-arrow-left" iconPos="left"/>
|
||
</div>
|
||
|
||
<div className="appPage__spacer"></div>
|
||
|
||
<div className="appPage__content">
|
||
{data.id
|
||
? <div className="appPageSection__withBorder columns">
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('ID domanda', 'gepafin')}</span>
|
||
<span>{data.applicationId}</span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Bando', 'gepafin')}</span>
|
||
<span>{data.callName}</span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Beneficiario', 'gepafin')}</span>
|
||
<span>{data.beneficiaryName}</span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Azienda', 'gepafin')}</span>
|
||
<span></span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Inizio', 'gepafin')}</span>
|
||
<span>{getDateFromISOstring(data.startDate)}</span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Scadenza', 'gepafin')}</span>
|
||
<span>{getDateFromISOstring(data.expirationDate)}</span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Stato', 'gepafin')}</span>
|
||
<span>{getBandoLabel(data.status)}</span>
|
||
</p>
|
||
</div> : null}
|
||
{dataAppl.id
|
||
? <div className="appPageSection__withBorder columns">
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('ID domanda', 'gepafin')}</span>
|
||
<span>{dataAppl.id}</span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Bando', 'gepafin')}</span>
|
||
<span>{dataAppl.callTitle}</span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Azienda', 'gepafin')}</span>
|
||
<span>{dataAppl.companyName}</span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Inviato', 'gepafin')}</span>
|
||
<span>{getDateFromISOstring(dataAppl.submissionDate)}</span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Protocolo', 'gepafin')}</span>
|
||
<span>{dataAppl.protocolNumber}</span>
|
||
</p>
|
||
<p className="appPageSection__pMeta">
|
||
<span>{__('Stato', 'gepafin')}</span>
|
||
<span>{getBandoLabel(dataAppl.status)}</span>
|
||
</p>
|
||
</div> : null}
|
||
|
||
{data.id
|
||
? <div className="appPageSection">
|
||
<h2>{__('Dettagli Richiesta', 'gepafin')}</h2>
|
||
<h3>{__('Note e spiegazioni', 'gepafin')}</h3>
|
||
<div className="appPageSection__withBorder grey ql-editor"
|
||
style={{ minHeight: '100px' }}>
|
||
{renderHtmlContent(data.note)}
|
||
</div>
|
||
</div> : null}
|
||
|
||
{data.id
|
||
? <div className="appPageSection">
|
||
<h2>{__('Comunicazioni', 'gepafin')}</h2>
|
||
<SoccorsoComunications amendmentId={data.id} soccorsoStatus={data.status}/>
|
||
</div> : null}
|
||
|
||
{data.id
|
||
? <div className="appPageSection">
|
||
<h3>{__('Documenti Richiesti', 'gepafin')}</h3>
|
||
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
|
||
{data.formFields
|
||
? data.formFields.map((o, i) => {
|
||
return <FormField
|
||
key={o.fieldId}
|
||
disabled={data.status !== 'AWAITING'}
|
||
type="fileupload"
|
||
setDataFn={setValue}
|
||
saveFormCallback={doUpdateAmendment}
|
||
fieldName={o.fieldId}
|
||
label={o.label}
|
||
control={control}
|
||
register={register}
|
||
errors={errors}
|
||
defaultValue={formInitialData[o.fieldId] ? formInitialData[o.fieldId] : []}
|
||
accept={[]}
|
||
source="AMENDMENT"
|
||
sourceId={data.id}
|
||
multiple={true}
|
||
/>
|
||
}) : null}
|
||
</form>
|
||
<ol className="appPageSection__list">
|
||
{data.formFields
|
||
? data.formFields.map((o, i) => <li key={o.fieldId}
|
||
style={{ flexDirection: 'row' }}>
|
||
<span>{o.label}</span>
|
||
</li>) : null}
|
||
</ol>
|
||
</div> : null}
|
||
|
||
{data.id
|
||
? <div className="appPageSection">
|
||
<h2>{__('Documenti aggiuntivi', 'gepafin')}</h2>
|
||
<div className="appPageSection">
|
||
<h3>{__('Notes', 'gepafin')}</h3>
|
||
<div style={{ marginBottom: '30px', width: '100%', position: 'relative' }}>
|
||
<BlockingOverlay shouldDisplay={data.status !== 'AWAITING'}/>
|
||
<Editor
|
||
value={data.amendmentNotes}
|
||
readOnly={data.status !== 'AWAITING'}
|
||
placeholder={__('Digita qui il messagio', 'gepafin')}
|
||
headerTemplate={header}
|
||
onTextChange={(e) => updateNewAmendmentData(
|
||
e.htmlValue,
|
||
'amendmentNotes'
|
||
)}
|
||
style={{ height: 80 * 3, width: '100%' }}
|
||
/>
|
||
</div>
|
||
<FormField
|
||
type="fileupload"
|
||
disabled={data.status !== 'AWAITING'}
|
||
setDataFn={setValue}
|
||
saveFormCallback={doUpdateAmendment}
|
||
fieldName="amendmentDocuments"
|
||
label={__('I file', 'gepafin')}
|
||
control={control}
|
||
register={register}
|
||
errors={errors}
|
||
defaultValue={formInitialData.amendmentDocuments ? formInitialData.amendmentDocuments : []}
|
||
accept={[]}
|
||
source="amendment"
|
||
sourceId={data.id}
|
||
multiple={true}
|
||
/>
|
||
</div>
|
||
</div> : null}
|
||
|
||
{data.id
|
||
? <div className="appPageSection__message warning">
|
||
<i className="pi pi-exclamation-triangle"></i>
|
||
<span className="summary">{__('Attenzione', 'gepafin')}</span>
|
||
<span>{__('Inviare la documentazione richiesta completa delle integrazioni esclusivamente via PEC. In caso contarrio l’integrazione non può essere ritenuta valida.', 'gepafin')}</span>
|
||
</div> : null}
|
||
|
||
<div className="appPageSection">
|
||
<div className="appPageSection__actions">
|
||
{data.id
|
||
? <Button
|
||
type="button"
|
||
disabled={isAsyncRequest || data.status !== 'AWAITING'}
|
||
onClick={doUpdateAmendmentAndCompleteTask}
|
||
label={__('Invia documenti', 'gepafin')}
|
||
icon="pi pi-save" iconPos="right"/> : null}
|
||
{data.id
|
||
? <Button
|
||
type="button"
|
||
outlined
|
||
disabled={isAsyncRequest || data.status !== 'AWAITING'}
|
||
onClick={() => doUpdateAmendment()}
|
||
label={__('Salva bozza', 'gepafin')}
|
||
icon="pi pi-save" iconPos="right"/> : null}
|
||
<Button
|
||
type="button"
|
||
outlined
|
||
onClick={goToArchivePage}
|
||
label={__('Indietro', 'gepafin')}
|
||
icon="pi pi-times" iconPos="right"/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<Dialog
|
||
header={__('Invia documenti', 'gepafin')}
|
||
visible={isVisibleEmailDialog}
|
||
style={{ width: '50vw' }}
|
||
onHide={() => {
|
||
if (!isVisibleEmailDialog) return;
|
||
setIsVisibleEmailDialog(false);
|
||
}}>
|
||
<p className="m-0">
|
||
{data.callEmail}
|
||
</p>
|
||
</Dialog>
|
||
</div>
|
||
)
|
||
|
||
}
|
||
|
||
export default SoccorsoEditBeneficiario; |