- added files field to evaluation rejection, ammendment creation and ammendment communication modal windows;

- fixed issue with filtering applications in dashboard for pre instructor;
This commit is contained in:
Vitalii Kiiko
2025-10-09 15:21:48 +02:00
parent 5cbfc11a50
commit 05281927ad
10 changed files with 443 additions and 79 deletions

6
.idea/copilot.data.migration.agent.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

6
.idea/copilot.data.migration.ask.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AskMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Ask2AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

6
.idea/copilot.data.migration.edit.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EditMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@@ -54,6 +54,8 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
assignedUserName: { value: null, matchMode: 'equals' } assignedUserName: { value: null, matchMode: 'equals' }
} }
}); });
const applicationStatuses = ['EVALUATION', 'SOCCORSO', 'NDG', 'APPOINTMENT', 'ADMISSIBLE',
'AWAITING_TECHNICAL_EVALUATION', 'TECHNICAL_EVALUATION'];
const getPaginationQuery = useCallback(() => getQueryParamsForPaginatedEndpoint(lazyState, statuses, 'applicationId'), [lazyState]); const getPaginationQuery = useCallback(() => getQueryParamsForPaginatedEndpoint(lazyState, statuses, 'applicationId'), [lazyState]);
@@ -106,14 +108,14 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
}; };
const statusFilterTemplate = (options) => { const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses} return <Dropdown value={options.value} options={applicationStatuses}
onChange={(e) => { onChange={(e) => {
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 });
}} }}

View File

@@ -56,6 +56,9 @@ import FormField from '../../components/FormField';
import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails'; import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
import EvaluationReAdmit from '../DomandaEditPreInstructor/components/EvaluationReAdmit'; import EvaluationReAdmit from '../DomandaEditPreInstructor/components/EvaluationReAdmit';
import { SplitButton } from 'primereact/splitbutton'; import { SplitButton } from 'primereact/splitbutton';
import { FileUpload } from 'primereact/fileupload';
import { defaultMaxFileSize, mimeTypes } from '../../configData';
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
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;
@@ -75,8 +78,10 @@ const DomandaEditInstructorManager = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [isVisibleCompleteDialog, setIsVisibleCompleteDialog] = useState(false); const [isVisibleCompleteDialog, setIsVisibleCompleteDialog] = useState(false);
const [operationType, setOperationType] = useState(''); const [operationType, setOperationType] = useState('');
const [motivation, setMotivation] = useState(''); const [finalDialogData, setFinalDialogData] = useState({
const [amountAccepted, setAmountAccepted] = useState(0); motivation: ''
});
const finalDialogFilesRef = useRef(null);
const [isVisibleAppointmentDialog, setIsVisibleAppointmentDialog] = useState(false); const [isVisibleAppointmentDialog, setIsVisibleAppointmentDialog] = useState(false);
const [isVisiblePreTecEvalDialog, setIsVisiblePreTecEvalDialog] = useState(false); const [isVisiblePreTecEvalDialog, setIsVisiblePreTecEvalDialog] = useState(false);
const [preTecEvalData, setPreTecEvalData] = useState({ const [preTecEvalData, setPreTecEvalData] = useState({
@@ -219,7 +224,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));
setMotivation(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') {
@@ -445,8 +450,8 @@ const DomandaEditInstructorManager = () => {
checklist: klona(data.checklist), checklist: klona(data.checklist),
files: klona(data.files), files: klona(data.files),
note: data.note, note: data.note,
motivation, motivation: finalDialogData.motivation,
amountAccepted amountAccepted: finalDialogData.amount
} }
setLoading(true); setLoading(true);
@@ -470,8 +475,8 @@ const DomandaEditInstructorManager = () => {
)), )),
amendmentDetails: klona(data.amendmentDetails), amendmentDetails: klona(data.amendmentDetails),
note: data.note, note: data.note,
motivation, motivation: finalDialogData.motivation,
amountAccepted amountAccepted: finalDialogData.amount
} }
setLoading(true); setLoading(true);
@@ -484,7 +489,7 @@ const DomandaEditInstructorManager = () => {
errApproveRejectCallback errApproveRejectCallback
); );
} }
}, [data, motivation]); }, [data, finalDialogData]);
const doReject = useCallback((newStatus) => { const doReject = useCallback((newStatus) => {
if (data.evaluationVersion === 'V1') { if (data.evaluationVersion === 'V1') {
@@ -494,7 +499,7 @@ const DomandaEditInstructorManager = () => {
checklist: klona(data.checklist), checklist: klona(data.checklist),
files: klona(data.files), files: klona(data.files),
note: data.note, note: data.note,
motivation motivation: finalDialogData.motivation
} }
setLoading(true); setLoading(true);
@@ -518,7 +523,7 @@ const DomandaEditInstructorManager = () => {
)), )),
amendmentDetails: klona(data.amendmentDetails), amendmentDetails: klona(data.amendmentDetails),
note: data.note, note: data.note,
motivation motivation: finalDialogData.motivation
} }
setLoading(true); setLoading(true);
@@ -531,7 +536,7 @@ const DomandaEditInstructorManager = () => {
errApproveRejectCallback errApproveRejectCallback
); );
} }
}, [data, motivation]); }, [data, finalDialogData]);
const approveRejectCallback = (data) => { const approveRejectCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
@@ -653,7 +658,7 @@ const DomandaEditInstructorManager = () => {
const hideCompleteDialog = () => { const hideCompleteDialog = () => {
setIsVisibleCompleteDialog(false); setIsVisibleCompleteDialog(false);
setOperationType(''); setOperationType('');
setMotivation(''); setFinalDialogData({});
} }
const footerCompleteDialog = useCallback(() => { const footerCompleteDialog = useCallback(() => {
@@ -662,11 +667,12 @@ const DomandaEditInstructorManager = () => {
if (operationType === 'approve') { if (operationType === 'approve') {
onSubmitAction = doApprove; onSubmitAction = doApprove;
isDisabled = isDisabled || !amountAccepted || isEmpty(amountAccepted) || amountAccepted === 0; isDisabled = isDisabled || !finalDialogData.amount || isEmpty(finalDialogData.amount) || finalDialogData.amount === 0;
} else if (operationType === 'tf_reject') { } else if (operationType === 'tf_reject') {
onSubmitAction = () => doReject('TECHNICAL_EVALUATION_REJECTED'); onSubmitAction = () => doReject('TECHNICAL_EVALUATION_REJECTED');
} else { } else {
onSubmitAction = () => doReject('REJECTED'); onSubmitAction = () => doReject('REJECTED');
isDisabled = isDisabled || !finalDialogData.subject || isEmpty(finalDialogData.subject) || !finalDialogData.motivation || isEmpty(finalDialogData.motivation)
} }
return <div> return <div>
@@ -676,7 +682,12 @@ const DomandaEditInstructorManager = () => {
disabled={isDisabled} disabled={isDisabled}
label={__('Invia', 'gepafin')} onClick={onSubmitAction}/> label={__('Invia', 'gepafin')} onClick={onSubmitAction}/>
</div> </div>
}, [amountAccepted, data, motivation]); }, [finalDialogData, data]);
const updateFinalDialogData = (value, path) => {
const newData = wrap(finalDialogData).set(path.split('.'), value).value();
setFinalDialogData(newData);
};
const initiateApproving = () => { const initiateApproving = () => {
setOperationType('approve'); setOperationType('approve');
@@ -945,7 +956,7 @@ const DomandaEditInstructorManager = () => {
? <Button ? <Button
type="button" type="button"
disabled={!data.id || data.status === 'CLOSE' disabled={!data.id || data.status === 'CLOSE'
|| (data.applicationStatus === 'EVALUATION' && shouldDisableNewSoccorso()) || (data.applicationStatus !== 'SOCCORSO' && shouldDisableNewSoccorso())
|| evaluationBlockedForUser(data)} || evaluationBlockedForUser(data)}
onClick={doNewSoccorso} onClick={doNewSoccorso}
outlined outlined
@@ -1505,28 +1516,95 @@ const DomandaEditInstructorManager = () => {
{operationType === 'approve' {operationType === 'approve'
? <div className="appForm__field"> ? <div className="appForm__field">
<label <label
className={classNames({ 'p-error': !amountAccepted || isEmpty(amountAccepted) || amountAccepted === 0 })}> className={classNames({ 'p-error': !finalDialogData.amount || isEmpty(finalDialogData.amount) || finalDialogData.amount === 0 })}>
{__('Importo approvato', 'gepafin')} {__('Importo approvato', 'gepafin')}
</label> </label>
<InputNumber <InputNumber
value={amountAccepted} value={finalDialogData.amount}
keyfilter="int" keyfilter="int"
invalid={!amountAccepted || isEmpty(amountAccepted) || amountAccepted === 0} invalid={!finalDialogData.amount || isEmpty(finalDialogData.amount) || finalDialogData.amount === 0}
onChange={(e) => setAmountAccepted(e.value)}/> onChange={(e) => updateFinalDialogData(e.value, 'amount')}/>
</div> : null}
{operationType === 'reject'
? <div className="appForm__field">
<label className={classNames({ 'p-error': !finalDialogData.subject || isEmpty(finalDialogData.subject) })}>
{__('Soggetto', 'gepafin')}
</label>
<InputText
value={finalDialogData.subject}
invalid={!finalDialogData.subject || isEmpty(finalDialogData.subject)}
onChange={(e) => updateFinalDialogData(e.target.value, 'subject')}/>
</div> : null} </div> : null}
<div className="appForm__field"> <div className="appForm__field">
<label>{__('Motivazione', 'gepafin')}</label> <label className={classNames({ 'p-error': !finalDialogData.motivation || isEmpty(finalDialogData.motivation) })}>
{__('Motivazione', 'gepafin')}
</label>
<div translate="no"> <div translate="no">
<Editor <Editor
value={motivation} value={finalDialogData.motivation}
readOnly={loading} readOnly={loading}
placeholder={__('Digita qui il messagio', 'gepafin')} placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header} headerTemplate={header}
onTextChange={(e) => setMotivation(e.htmlValue)} onTextChange={(e) => updateFinalDialogData(e.htmlValue, 'motivation')}
style={{ height: 80 * 3, width: '100%' }} style={{ height: 80 * 3, width: '100%' }}
/> />
</div> </div>
</div> </div>
{operationType === 'reject'
? <div className="appForm__field">
<label>
{__('Files', 'gepafin')}
</label>
<FileUpload
ref={finalDialogFilesRef}
name="files[]"
multiple
accept={mimeTypes.map(o => o.code).join(',')}
maxFileSize={defaultMaxFileSize}
auto={false}
customUpload={true}
onSelect={(e) => {
updateFinalDialogData(e.files, 'files');
}}
onRemove={(e) => {
const updatedFiles = finalDialogFilesRef.current.getFiles();
updateFinalDialogData(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> : null}
</Dialog> </Dialog>
<Dialog <Dialog

View File

@@ -56,6 +56,9 @@ import FormField from '../../components/FormField';
import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails'; import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
import EvaluationReAdmit from './components/EvaluationReAdmit'; import EvaluationReAdmit from './components/EvaluationReAdmit';
import { SplitButton } from 'primereact/splitbutton'; import { SplitButton } from 'primereact/splitbutton';
import { FileUpload } from 'primereact/fileupload';
import { defaultMaxFileSize, mimeTypes } from '../../configData';
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
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;
@@ -75,8 +78,10 @@ const DomandaEditPreInstructor = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [isVisibleCompleteDialog, setIsVisibleCompleteDialog] = useState(false); const [isVisibleCompleteDialog, setIsVisibleCompleteDialog] = useState(false);
const [operationType, setOperationType] = useState(''); const [operationType, setOperationType] = useState('');
const [motivation, setMotivation] = useState(''); const [finalDialogData, setFinalDialogData] = useState({
const [amountAccepted, setAmountAccepted] = useState(0); motivation: ''
});
const finalDialogFilesRef = useRef(null);
const [isVisibleAppointmentDialog, setIsVisibleAppointmentDialog] = useState(false); const [isVisibleAppointmentDialog, setIsVisibleAppointmentDialog] = useState(false);
const [isVisiblePreTecEvalDialog, setIsVisiblePreTecEvalDialog] = useState(false); const [isVisiblePreTecEvalDialog, setIsVisiblePreTecEvalDialog] = useState(false);
const [preTecEvalData, setPreTecEvalData] = useState({ const [preTecEvalData, setPreTecEvalData] = useState({
@@ -219,7 +224,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));
setMotivation(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') {
@@ -445,8 +450,8 @@ const DomandaEditPreInstructor = () => {
checklist: klona(data.checklist), checklist: klona(data.checklist),
files: klona(data.files), files: klona(data.files),
note: data.note, note: data.note,
motivation, motivation: finalDialogData.motivation,
amountAccepted amountAccepted: finalDialogData.amount
} }
setLoading(true); setLoading(true);
@@ -470,8 +475,8 @@ const DomandaEditPreInstructor = () => {
)), )),
amendmentDetails: klona(data.amendmentDetails), amendmentDetails: klona(data.amendmentDetails),
note: data.note, note: data.note,
motivation, motivation: finalDialogData.motivation,
amountAccepted amountAccepted: finalDialogData.amount
} }
setLoading(true); setLoading(true);
@@ -484,7 +489,7 @@ const DomandaEditPreInstructor = () => {
errApproveRejectCallback errApproveRejectCallback
); );
} }
}, [data, motivation]); }, [data, finalDialogData]);
const doReject = useCallback((newStatus) => { const doReject = useCallback((newStatus) => {
if (data.evaluationVersion === 'V1') { if (data.evaluationVersion === 'V1') {
@@ -494,7 +499,7 @@ const DomandaEditPreInstructor = () => {
checklist: klona(data.checklist), checklist: klona(data.checklist),
files: klona(data.files), files: klona(data.files),
note: data.note, note: data.note,
motivation motivation: finalDialogData.motivation
} }
setLoading(true); setLoading(true);
@@ -518,7 +523,7 @@ const DomandaEditPreInstructor = () => {
)), )),
amendmentDetails: klona(data.amendmentDetails), amendmentDetails: klona(data.amendmentDetails),
note: data.note, note: data.note,
motivation motivation: finalDialogData.motivation
} }
setLoading(true); setLoading(true);
@@ -531,7 +536,7 @@ const DomandaEditPreInstructor = () => {
errApproveRejectCallback errApproveRejectCallback
); );
} }
}, [data, motivation]); }, [data, finalDialogData]);
const approveRejectCallback = (data) => { const approveRejectCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
@@ -653,7 +658,7 @@ const DomandaEditPreInstructor = () => {
const hideCompleteDialog = () => { const hideCompleteDialog = () => {
setIsVisibleCompleteDialog(false); setIsVisibleCompleteDialog(false);
setOperationType(''); setOperationType('');
setMotivation(''); setFinalDialogData({});
} }
const footerCompleteDialog = useCallback(() => { const footerCompleteDialog = useCallback(() => {
@@ -662,11 +667,12 @@ const DomandaEditPreInstructor = () => {
if (operationType === 'approve') { if (operationType === 'approve') {
onSubmitAction = doApprove; onSubmitAction = doApprove;
isDisabled = isDisabled || !amountAccepted || isEmpty(amountAccepted) || amountAccepted === 0; isDisabled = isDisabled || !finalDialogData.amount || isEmpty(finalDialogData.amount) || finalDialogData.amount === 0;
} else if (operationType === 'tf_reject') { } else if (operationType === 'tf_reject') {
onSubmitAction = () => doReject('TECHNICAL_EVALUATION_REJECTED'); onSubmitAction = () => doReject('TECHNICAL_EVALUATION_REJECTED');
} else { } else {
onSubmitAction = () => doReject('REJECTED'); onSubmitAction = () => doReject('REJECTED');
isDisabled = isDisabled || !finalDialogData.subject || isEmpty(finalDialogData.subject) || !finalDialogData.motivation || isEmpty(finalDialogData.motivation)
} }
return <div> return <div>
@@ -676,7 +682,12 @@ const DomandaEditPreInstructor = () => {
disabled={isDisabled} disabled={isDisabled}
label={__('Invia', 'gepafin')} onClick={onSubmitAction}/> label={__('Invia', 'gepafin')} onClick={onSubmitAction}/>
</div> </div>
}, [amountAccepted, data, motivation]); }, [finalDialogData, data]);
const updateFinalDialogData = (value, path) => {
const newData = wrap(finalDialogData).set(path.split('.'), value).value();
setFinalDialogData(newData);
};
const initiateApproving = () => { const initiateApproving = () => {
setOperationType('approve'); setOperationType('approve');
@@ -945,7 +956,7 @@ const DomandaEditPreInstructor = () => {
? <Button ? <Button
type="button" type="button"
disabled={!data.id || data.status === 'CLOSE' disabled={!data.id || data.status === 'CLOSE'
|| (data.applicationStatus === 'EVALUATION' && shouldDisableNewSoccorso()) || (data.applicationStatus !== 'SOCCORSO' && shouldDisableNewSoccorso())
|| evaluationBlockedForUser(data)} || evaluationBlockedForUser(data)}
onClick={doNewSoccorso} onClick={doNewSoccorso}
outlined outlined
@@ -1505,28 +1516,95 @@ const DomandaEditPreInstructor = () => {
{operationType === 'approve' {operationType === 'approve'
? <div className="appForm__field"> ? <div className="appForm__field">
<label <label
className={classNames({ 'p-error': !amountAccepted || isEmpty(amountAccepted) || amountAccepted === 0 })}> className={classNames({ 'p-error': !finalDialogData.amount || isEmpty(finalDialogData.amount) || finalDialogData.amount === 0 })}>
{__('Importo approvato', 'gepafin')} {__('Importo approvato', 'gepafin')}
</label> </label>
<InputNumber <InputNumber
value={amountAccepted} value={finalDialogData.amount}
keyfilter="int" keyfilter="int"
invalid={!amountAccepted || isEmpty(amountAccepted) || amountAccepted === 0} invalid={!finalDialogData.amount || isEmpty(finalDialogData.amount) || finalDialogData.amount === 0}
onChange={(e) => setAmountAccepted(e.value)}/> onChange={(e) => updateFinalDialogData(e.value, 'amount')}/>
</div> : null}
{operationType === 'reject'
? <div className="appForm__field">
<label className={classNames({ 'p-error': !finalDialogData.subject || isEmpty(finalDialogData.subject) })}>
{__('Soggetto', 'gepafin')}
</label>
<InputText
value={finalDialogData.subject}
invalid={!finalDialogData.subject || isEmpty(finalDialogData.subject)}
onChange={(e) => updateFinalDialogData(e.target.value, 'subject')}/>
</div> : null} </div> : null}
<div className="appForm__field"> <div className="appForm__field">
<label>{__('Motivazione', 'gepafin')}</label> <label className={classNames({ 'p-error': !finalDialogData.motivation || isEmpty(finalDialogData.motivation) })}>
{__('Motivazione', 'gepafin')}
</label>
<div translate="no"> <div translate="no">
<Editor <Editor
value={motivation} value={finalDialogData.motivation}
readOnly={loading} readOnly={loading}
placeholder={__('Digita qui il messagio', 'gepafin')} placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header} headerTemplate={header}
onTextChange={(e) => setMotivation(e.htmlValue)} onTextChange={(e) => updateFinalDialogData(e.htmlValue, 'motivation')}
style={{ height: 80 * 3, width: '100%' }} style={{ height: 80 * 3, width: '100%' }}
/> />
</div> </div>
</div> </div>
{operationType === 'reject'
? <div className="appForm__field">
<label>
{__('Files', 'gepafin')}
</label>
<FileUpload
ref={finalDialogFilesRef}
name="files[]"
multiple
accept={mimeTypes.map(o => o.code).join(',')}
maxFileSize={defaultMaxFileSize}
auto={false}
customUpload={true}
onSelect={(e) => {
updateFinalDialogData(e.files, 'files');
}}
onRemove={(e) => {
const updatedFiles = finalDialogFilesRef.current.getFiles();
updateFinalDialogData(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> : null}
</Dialog> </Dialog>
<Dialog <Dialog

View File

@@ -12,6 +12,7 @@ import AmendmentsService from '../../service/amendments-service';
// tools // tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse'; import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
// components // components
import { Skeleton } from 'primereact/skeleton'; import { Skeleton } from 'primereact/skeleton';
@@ -24,6 +25,8 @@ import { Toast } from 'primereact/toast';
import { InputSwitch } from 'primereact/inputswitch'; import { InputSwitch } from 'primereact/inputswitch';
import ApplicationEvaluationService from '../../service/application-evaluation-service'; import ApplicationEvaluationService from '../../service/application-evaluation-service';
import { Dialog } from 'primereact/dialog'; import { Dialog } from 'primereact/dialog';
import { FileUpload } from 'primereact/fileupload';
import { defaultMaxFileSize, mimeTypes } from '../../configData';
const SoccorsoAddInstructorManager = () => { const SoccorsoAddInstructorManager = () => {
const isAsyncRequest = useStoreValue('isAsyncRequest'); const isAsyncRequest = useStoreValue('isAsyncRequest');
@@ -32,8 +35,9 @@ const SoccorsoAddInstructorManager = () => {
const [data, setData] = useState({}); const [data, setData] = useState({});
const [evaluationId, setEvaluationId] = useState(0); const [evaluationId, setEvaluationId] = useState(0);
const [formData, setFormData] = useState({}); const [formData, setFormData] = useState({});
const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false) const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false);
const toast = useRef(null); const toast = useRef(null);
const ammendmentFilesRef = useRef(null);
const goToEvaluationPage = () => { const goToEvaluationPage = () => {
navigate(`/mie-domande/${id}`); navigate(`/mie-domande/${id}`);
@@ -222,7 +226,7 @@ const SoccorsoAddInstructorManager = () => {
<div className="appPageSection columns"> <div className="appPageSection columns">
<div> <div>
<h3>{__('Pec/Email', 'gepafin')}</h3> <h3>{__('Pec/Email', 'gepafin')}</h3>
<div style={{ marginBottom: '30px' }} translate="no"> <div style={{marginBottom: '30px'}} translate="no">
<Editor <Editor
value={formData.note} value={formData.note}
placeholder={__('Digita qui il messagio', 'gepafin')} placeholder={__('Digita qui il messagio', 'gepafin')}
@@ -235,8 +239,65 @@ const SoccorsoAddInstructorManager = () => {
/> />
</div> </div>
<div style={{marginBottom: '30px'}}>
<div className="appForm__field">
<label>
{__('Files', 'gepafin')}
</label>
<FileUpload
ref={ammendmentFilesRef}
name="files[]"
multiple
accept={mimeTypes.map(o => o.code).join(',')}
maxFileSize={defaultMaxFileSize}
auto={false}
customUpload={true}
onSelect={(e) => {
updateEvaluationValue(e.files, 'files');
}}
onRemove={(e) => {
const updatedFiles = ammendmentFilesRef.current.getFiles();
updateEvaluationValue(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>
</div>
<h3>{__('Tempo per la Risposta (giorni)', 'gepafin')}</h3> <h3>{__('Tempo per la Risposta (giorni)', 'gepafin')}</h3>
<div style={{ marginBottom: '30px' }}> <div style={{marginBottom: '30px'}}>
<InputNumber <InputNumber
keyfilter="int" keyfilter="int"
value={formData.responseDays} value={formData.responseDays}

View File

@@ -12,6 +12,7 @@ import AmendmentsService from '../../service/amendments-service';
// tools // tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse'; import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
// components // components
import { Skeleton } from 'primereact/skeleton'; import { Skeleton } from 'primereact/skeleton';
@@ -24,6 +25,8 @@ import { Toast } from 'primereact/toast';
import { InputSwitch } from 'primereact/inputswitch'; import { InputSwitch } from 'primereact/inputswitch';
import ApplicationEvaluationService from '../../service/application-evaluation-service'; import ApplicationEvaluationService from '../../service/application-evaluation-service';
import { Dialog } from 'primereact/dialog'; import { Dialog } from 'primereact/dialog';
import { FileUpload } from 'primereact/fileupload';
import { defaultMaxFileSize, mimeTypes } from '../../configData';
const SoccorsoAddPreInstructor = () => { const SoccorsoAddPreInstructor = () => {
const isAsyncRequest = useStoreValue('isAsyncRequest'); const isAsyncRequest = useStoreValue('isAsyncRequest');
@@ -34,6 +37,7 @@ const SoccorsoAddPreInstructor = () => {
const [formData, setFormData] = useState({}); const [formData, setFormData] = useState({});
const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false); const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false);
const toast = useRef(null); const toast = useRef(null);
const ammendmentFilesRef = useRef(null);
const goToEvaluationPage = () => { const goToEvaluationPage = () => {
navigate(`/domande/${id}`); navigate(`/domande/${id}`);
@@ -235,6 +239,63 @@ const SoccorsoAddPreInstructor = () => {
/> />
</div> </div>
<div style={{marginBottom: '30px'}}>
<div className="appForm__field">
<label>
{__('Files', 'gepafin')}
</label>
<FileUpload
ref={ammendmentFilesRef}
name="files[]"
multiple
accept={mimeTypes.map(o => o.code).join(',')}
maxFileSize={defaultMaxFileSize}
auto={false}
customUpload={true}
onSelect={(e) => {
updateEvaluationValue(e.files, 'files');
}}
onRemove={(e) => {
const updatedFiles = ammendmentFilesRef.current.getFiles();
updateEvaluationValue(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>
</div>
<h3>{__('Tempo per la Risposta (giorni)', 'gepafin')}</h3> <h3>{__('Tempo per la Risposta (giorni)', 'gepafin')}</h3>
<div style={{marginBottom: '30px'}}> <div style={{marginBottom: '30px'}}>
<InputNumber <InputNumber
@@ -297,7 +358,7 @@ const SoccorsoAddPreInstructor = () => {
<div className="appPageSection__message warning"> <div className="appPageSection__message warning">
<i className="pi pi-exclamation-triangle"></i> <i className="pi pi-exclamation-triangle"></i>
<span className="summary">{__('Attenzione', 'gepafin')}</span> <span className="summary">{__('Attenzione', 'gepafin')}</span>
<span>{__("L'invio della richiesta di integrazione sospenderà il termine di valutazione della domanda.", 'gepafin')}</span> <span>{__('L\'invio della richiesta di integrazione sospenderà il termine di valutazione della domanda.', 'gepafin')}</span>
</div> </div>
<div className="appPageSection__hr"> <div className="appPageSection__hr">

View File

@@ -9,7 +9,7 @@ import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse
import uniqid from '../../../../helpers/uniqid'; import uniqid from '../../../../helpers/uniqid';
// store // store
import { storeGet } from '../../../../store'; import { storeGet, useStoreValue } from '../../../../store';
// api // api
import CommunicationService from '../../../../service/communication-service'; import CommunicationService from '../../../../service/communication-service';
@@ -21,13 +21,18 @@ import { InputText } from 'primereact/inputtext';
import { InputTextarea } from 'primereact/inputtextarea'; import { InputTextarea } from 'primereact/inputtextarea';
import { Dialog } from 'primereact/dialog'; import { Dialog } from 'primereact/dialog';
import { Toast } from 'primereact/toast'; import { Toast } from 'primereact/toast';
import { FileUpload } from 'primereact/fileupload';
import { defaultMaxFileSize, mimeTypes } from '../../../../configData';
import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText';
const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => { const SoccorsoComunications = ({amendmentId, soccorsoStatus}) => {
const [comms, setComms] = useState([]); const [comms, setComms] = useState([]);
const [isVisibleNewCommDialog, setIsVisibleNewCommDialog] = useState(false); const [isVisibleNewCommDialog, setIsVisibleNewCommDialog] = useState(false);
const [newCommData, setNewCommData] = useState({}); const [newCommData, setNewCommData] = useState({});
const [isLoadingCommunication, setIsLoadingCommunication] = useState(false); const [isLoadingCommunication, setIsLoadingCommunication] = useState(false);
const toast = useRef(null); const toast = useRef(null);
const commDialogFilesRef = useRef(null);
const role = useStoreValue('getRole');
useEffect(() => { useEffect(() => {
if (amendmentId && amendmentId !== 0) { if (amendmentId && amendmentId !== 0) {
@@ -41,7 +46,7 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
setComms(data.data.commentsList.map(o => getFormattedCommsData(o))); setComms(data.data.commentsList.map(o => getFormattedCommsData(o)));
} }
setIsLoadingCommunication(false); setIsLoadingCommunication(false);
} };
const errGetCommsCallback = (data) => { const errGetCommsCallback = (data) => {
if (toast.current && data.message) { if (toast.current && data.message) {
@@ -52,8 +57,8 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
}); });
} }
setIsLoadingCommunication(false); setIsLoadingCommunication(false);
} };
const getFormattedCommsData = (data) => { const getFormattedCommsData = (data) => {
data.id = isNil(data.id) ? uniqid('id') : data.id; data.id = isNil(data.id) ? uniqid('id') : data.id;
data.commentedDate = is(String, data.commentedDate) ? new Date(data.commentedDate) : (data.commentedDate ? data.commentedDate : ''); data.commentedDate = is(String, data.commentedDate) ? new Date(data.commentedDate) : (data.commentedDate ? data.commentedDate : '');
@@ -63,8 +68,8 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
}; };
const headerNewComDialog = () => { const headerNewComDialog = () => {
return <span>{__('Aggiungi comunicazione', 'gepafin')}</span> return <span>{__('Aggiungi comunicazione', 'gepafin')}</span>;
} };
const hideNewComDialog = () => { const hideNewComDialog = () => {
setIsVisibleNewCommDialog(false); setIsVisibleNewCommDialog(false);
@@ -72,7 +77,7 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
title: '', title: '',
comment: '' comment: ''
}); });
} };
const footerNewComDialog = () => { const footerNewComDialog = () => {
return <div> return <div>
@@ -81,8 +86,8 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
type="button" type="button"
disabled={isLoadingCommunication || isEmpty(newCommData.title) || isEmpty(newCommData.comment)} disabled={isLoadingCommunication || isEmpty(newCommData.title) || isEmpty(newCommData.comment)}
label={__('Invia', 'gepafin')} onClick={createCommunication}/> label={__('Invia', 'gepafin')} onClick={createCommunication}/>
</div> </div>;
} };
const openNewCommDialog = () => { const openNewCommDialog = () => {
setIsVisibleNewCommDialog(true); setIsVisibleNewCommDialog(true);
@@ -90,17 +95,17 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
title: '', title: '',
comment: '' comment: ''
}); });
} };
const updateNewCommData = (value, path) => { const updateNewCommData = (value, path) => {
const newData = wrap(newCommData).set(path.split('.'), value).value(); const newData = wrap(newCommData).set(path.split('.'), value).value();
setNewCommData(newData); setNewCommData(newData);
} };
const createCommunication = () => { const createCommunication = () => {
setIsLoadingCommunication(true); setIsLoadingCommunication(true);
CommunicationService.createCommunication(amendmentId, newCommData, createCommunicationCallback, errCreateCommunicationCallback); CommunicationService.createCommunication(amendmentId, newCommData, createCommunicationCallback, errCreateCommunicationCallback);
} };
const createCommunicationCallback = (data) => { const createCommunicationCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
@@ -111,11 +116,11 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
detail: data.message detail: data.message
}); });
} }
setComms([...comms, getFormattedCommsData(data.data)]) setComms([...comms, getFormattedCommsData(data.data)]);
setIsVisibleNewCommDialog(false); setIsVisibleNewCommDialog(false);
} }
setIsLoadingCommunication(false); setIsLoadingCommunication(false);
} };
const errCreateCommunicationCallback = (data) => { const errCreateCommunicationCallback = (data) => {
if (toast.current && data.message) { if (toast.current && data.message) {
@@ -127,29 +132,29 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
} }
set404FromErrorResponse(data); set404FromErrorResponse(data);
setIsLoadingCommunication(false); setIsLoadingCommunication(false);
} };
const displayCommIcon = (comm) => { const displayCommIcon = (comm) => {
const userData = storeGet('userData'); const userData = storeGet('userData');
return userData.id === comm.senderUserId return userData.id === comm.senderUserId
? <i className="pi pi-upload"></i> ? <i className="pi pi-upload"></i>
: <i className="pi pi-download"></i>; : <i className="pi pi-download"></i>;
} };
const getCommRowClass = (comm) => { const getCommRowClass = (comm) => {
const userData = storeGet('userData'); const userData = storeGet('userData');
return userData.id === comm.senderUserId ? 'outgoing' : 'incoming'; return userData.id === comm.senderUserId ? 'outgoing' : 'incoming';
} };
return ( return (
<> <>
<Toast ref={toast}/> <Toast ref={toast}/>
<table className="myTable"> <table className="myTable">
<thead className="myThead"> <thead className="myThead">
<tr> <tr>
<th style={{ width: 50 }}></th> <th style={{width: 50}}></th>
<th style={{ width: 250 }}>{__('Data', 'gepafin')}</th> <th style={{width: 250}}>{__('Data', 'gepafin')}</th>
<th style={{ width: '100%' }}>{__('Comunicazione', 'gepafin')}</th> <th style={{width: '100%'}}>{__('Comunicazione', 'gepafin')}</th>
</tr> </tr>
</thead> </thead>
<tbody className="myTbody"> <tbody className="myTbody">
@@ -175,7 +180,7 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
</table> </table>
<Button <Button
style={{ marginTop: 30 }} style={{marginTop: 30}}
onClick={openNewCommDialog} onClick={openNewCommDialog}
disabled={soccorsoStatus === 'CLOSE'} disabled={soccorsoStatus === 'CLOSE'}
type="button" type="button"
@@ -187,11 +192,11 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
modal modal
header={headerNewComDialog} header={headerNewComDialog}
footer={footerNewComDialog} footer={footerNewComDialog}
style={{ maxWidth: '600px', width: '100%' }} style={{maxWidth: '600px', width: '100%'}}
onHide={hideNewComDialog}> onHide={hideNewComDialog}>
<div className="appForm__field"> <div className="appForm__field">
<label <label
className={classNames({ 'p-error': isEmpty(newCommData.title) })}> className={classNames({'p-error': isEmpty(newCommData.title)})}>
{__('Titolo', 'gepafin')}* {__('Titolo', 'gepafin')}*
</label> </label>
<InputText value={newCommData.title} <InputText value={newCommData.title}
@@ -200,7 +205,7 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
onChange={(e) => updateNewCommData(e.target.value, 'title')}/> onChange={(e) => updateNewCommData(e.target.value, 'title')}/>
<label <label
className={classNames({ 'p-error': isEmpty(newCommData.comment) })}> className={classNames({'p-error': isEmpty(newCommData.comment)})}>
{__('Contenuto', 'gepafin')}* {__('Contenuto', 'gepafin')}*
</label> </label>
<InputTextarea <InputTextarea
@@ -209,10 +214,65 @@ const SoccorsoComunications = ({ amendmentId, soccorsoStatus }) => {
rows={5} cols={30} rows={5} cols={30}
invalid={isEmpty(newCommData.comment)} invalid={isEmpty(newCommData.comment)}
onChange={(e) => updateNewCommData(e.target.value, 'comment')}/> onChange={(e) => updateNewCommData(e.target.value, 'comment')}/>
{!['ROLE_CONFIDI', 'ROLE_BENEFICIARY'].includes(role)
? <div className="appForm__field">
<label>
{__('Files', 'gepafin')}
</label>
<FileUpload
ref={commDialogFilesRef}
name="files[]"
multiple
accept={mimeTypes.map(o => o.code).join(',')}
maxFileSize={defaultMaxFileSize}
auto={false}
customUpload={true}
onSelect={(e) => {
updateNewCommData(e.files, 'files');
}}
onRemove={(e) => {
const updatedFiles = commDialogFilesRef.current.getFiles();
updateNewCommData(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> : null}
</div> </div>
</Dialog> </Dialog>
</> </>
) );
} };
export default SoccorsoComunications; export default SoccorsoComunications;