- save progress;

This commit is contained in:
Vitalii Kiiko
2024-10-24 15:41:34 +02:00
11 changed files with 114 additions and 115 deletions

View File

@@ -14,7 +14,7 @@
"@xyflow/react": "12.3.1", "@xyflow/react": "12.3.1",
"codice-fiscale-js": "2.3.22", "codice-fiscale-js": "2.3.22",
"deep-object-diff": "^1.1.9", "deep-object-diff": "^1.1.9",
"dompurify": "3.1.7", "dompurify": "^3.1.7",
"fast-deep-equal": "3.1.3", "fast-deep-equal": "3.1.3",
"html-react-parser": "5.1.16", "html-react-parser": "5.1.16",
"jwt-decode": "4.0.0", "jwt-decode": "4.0.0",

View File

@@ -38,6 +38,10 @@
cursor: pointer; cursor: pointer;
} }
&[disabled] {
background: var(--message-info-background);
}
span { span {
color: #FFF; color: #FFF;
font-size: 17.25px; font-size: 17.25px;

View File

@@ -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 { isNil, isEmpty } from 'ramda'; import { isNil, isEmpty, is } from 'ramda';
// components // components
import { InputSwitch } from 'primereact/inputswitch'; import { InputSwitch } from 'primereact/inputswitch';
@@ -28,7 +28,7 @@ const Switch = ({
render={({ field, fieldState }) => ( render={({ field, fieldState }) => (
<InputSwitch <InputSwitch
inputId={fieldName} inputId={fieldName}
checked={field.value} checked={is(String, field.value) ? 'true' === field.value : field.value}
disabled={disabled} disabled={disabled}
onChange={(e) => field.onChange(e.value)} onChange={(e) => field.onChange(e.value)}
className={classNames({ 'p-invalid': fieldState.invalid })}/> className={classNames({ 'p-invalid': fieldState.invalid })}/>

View File

@@ -1,6 +1,10 @@
import parse from 'html-react-parser'; import parse from 'html-react-parser';
import { isNil } from 'ramda'; import { isNil } from 'ramda';
import DOMPurify from 'dompurify';
const renderHtmlContent = (content = '') => !isNil(content) ? parse(content) : ''; const renderHtmlContent = (content = '') => {
const clean = DOMPurify.sanitize(content);
return !isNil(clean) ? parse(clean) : '';
}
export default renderHtmlContent; export default renderHtmlContent;

View File

@@ -25,6 +25,7 @@ import {
} from '../../helpers/validators'; } from '../../helpers/validators';
import renderHtmlContent from '../../helpers/renderHtmlContent'; import renderHtmlContent from '../../helpers/renderHtmlContent';
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';
@@ -36,7 +37,7 @@ import ApplicationSteps from './ApplicationSteps';
import BlockingOverlay from '../../components/BlockingOverlay'; import BlockingOverlay from '../../components/BlockingOverlay';
import { Dialog } from 'primereact/dialog'; import { Dialog } from 'primereact/dialog';
import FileuploadApplicationSignedPdf from '../../components/FileuploadApplicationSignedPdf'; import FileuploadApplicationSignedPdf from '../../components/FileuploadApplicationSignedPdf';
import getFormatedFileSizeText from '../../helpers/getFormatedFileSizeText';
import { defaultMaxFileSize } from '../../configData'; import { defaultMaxFileSize } from '../../configData';
const BandoApplication = () => { const BandoApplication = () => {
@@ -47,7 +48,6 @@ const BandoApplication = () => {
const [bandoTitle, setBandoTitle] = useState(''); const [bandoTitle, setBandoTitle] = useState('');
const [formId, setFormId] = useState(''); const [formId, setFormId] = useState('');
const [totalSteps, setTotalSteps] = useState(0); const [totalSteps, setTotalSteps] = useState(0);
//const [completedSteps, setCompletedSteps] = useState(0);
const [visibleConfirmation, setVisibleConfirmation] = useState(false); const [visibleConfirmation, setVisibleConfirmation] = useState(false);
const [applicationStatus, setApplicationStatus] = useState(''); const [applicationStatus, setApplicationStatus] = useState('');
const [activeStep, setActiveStep] = useState(1); const [activeStep, setActiveStep] = useState(1);
@@ -91,68 +91,9 @@ const BandoApplication = () => {
storeSet.main.setAsyncRequest(); storeSet.main.setAsyncRequest();
formMsgs.current.clear(); formMsgs.current.clear();
/*ApplicationService.updateStatusApplication(applId, {}, submitApplicationCallback, errSubmitApplicationCallback, [
['status', 'SUBMIT']
]);*/
ApplicationService.validateApplication(applId, {}, validateApplicationCallback, errValidateApplicationCallback); ApplicationService.validateApplication(applId, {}, validateApplicationCallback, errValidateApplicationCallback);
}; };
/*const submitApplicationCallback = (data) => {
if (data.status === 'SUCCESS') {
if (data.data.status) {
setApplicationStatus(data.data.status); // ask why not 'applicationStatus'?
}
}
storeSet.main.unsetAsyncRequest();
}
const errSubmitApplicationCallback = (data) => {
storeSet.main.unsetAsyncRequest();
if (data.status === 'VALIDATION_ERROR') {
if (formMsgs.current) {
formMsgs.current.show([
{
id: '99',
sticky: true, severity: 'error', summary: '',
detail: data.data.join(', '),
closable: true
}
]);
}
} else if (data.status === 'EXCEPTION_ERROR') {
if (formMsgs.current) {
formMsgs.current.show([
{
id: '99',
sticky: true, severity: 'error', summary: '',
detail: data.message,
closable: true
}
]);
}
} else if (data.status === 'BAD_REQUEST') {
if (formMsgs.current) {
formMsgs.current.show([
{
id: '99',
sticky: true, severity: 'error', summary: '',
detail: data.message,
closable: true
}
]);
}
if (toast.current) {
toast.current.show({
severity: 'error',
summary: '',
detail: data.message
});
}
} else {
set404FromErrorResponse(data);
}
}*/
const validateApplicationCallback = (data) => { const validateApplicationCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
if (data.data.status) { if (data.data.status) {
@@ -461,7 +402,8 @@ const BandoApplication = () => {
useEffect(() => { useEffect(() => {
if (formInitialData) { if (formInitialData) {
//reset(); //reset();
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k])) Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
trigger();
} }
}, [formInitialData]); }, [formInitialData]);
@@ -487,7 +429,7 @@ const BandoApplication = () => {
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>
{'SUBMIT' !== applicationStatus {'DRAFT' === applicationStatus
? <ApplicationSteps totalSteps={totalSteps} activeStepIndex={activeStepIndex}/> ? <ApplicationSteps totalSteps={totalSteps} activeStepIndex={activeStepIndex}/>
: null} : null}
@@ -510,12 +452,12 @@ const BandoApplication = () => {
<div className="appPage__content"> <div className="appPage__content">
<BlockingOverlay shouldDisplay={isAsyncRequest}/> <BlockingOverlay shouldDisplay={isAsyncRequest}/>
<form className="appForm" onSubmit={handleSubmit(onSubmit)}> <form className="appForm" onSubmit={handleSubmit(onSubmit)}>
{'SUBMIT' !== applicationStatus {'DRAFT' === applicationStatus
? <div className="appPageSection"> ? <div className="appPageSection">
{actionBtns} {actionBtns}
</div> : null} </div> : null}
{'SUBMIT' !== applicationStatus {'DRAFT' === applicationStatus
? formData.map(o => { ? formData.map(o => {
const label = head(o.settings.filter(o => o.name === 'label')); const label = head(o.settings.filter(o => o.name === 'label'));
const text = head(o.settings.filter(o => o.name === 'text')); const text = head(o.settings.filter(o => o.name === 'text'));
@@ -576,7 +518,7 @@ const BandoApplication = () => {
}) })
: null} : null}
{'SUBMIT' === applicationStatus {'AWAIT' === applicationStatus
? <div className="appPageSection"> ? <div className="appPageSection">
<div className="appForm__field"> <div className="appForm__field">
<label> <label>
@@ -585,14 +527,14 @@ const BandoApplication = () => {
</div> </div>
<Button <Button
type="button" type="button"
disabled={'SUBMIT' !== applicationStatus} disabled={'SUBMIT' === applicationStatus}
onClick={onDownloadApplicationPdf} onClick={onDownloadApplicationPdf}
label={__('Scarica PDF', 'gepafin')} label={__('Scarica PDF', 'gepafin')}
icon="pi pi-download" icon="pi pi-download"
iconPos="right"/> iconPos="right"/>
</div> : null} </div> : null}
{'SUBMIT' === applicationStatus {'AWAIT' === applicationStatus
? <div className="appPageSection"> ? <div className="appPageSection">
<div className="appForm__field"> <div className="appForm__field">
<label htmlFor="signedPdfFile"> <label htmlFor="signedPdfFile">
@@ -617,12 +559,12 @@ const BandoApplication = () => {
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>
{'SUBMIT' !== applicationStatus {'DRAFT' === applicationStatus
? <div className="appPageSection__hr"> ? <div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span> <span>{__('Azioni rapide', 'gepafin')}</span>
</div> : null} </div> : null}
{'SUBMIT' !== applicationStatus {'DRAFT' === applicationStatus
? <div className="appPageSection"> ? <div className="appPageSection">
{actionBtns} {actionBtns}
</div> : null} </div> : null}

View File

@@ -148,6 +148,10 @@ const BandoEdit = () => {
detail: __('Pubblicato!', 'gepafin') detail: __('Pubblicato!', 'gepafin')
}); });
} }
if (data.data.docs) {
data.data.docs = data.data.docs
.filter(o => o.source === 'CALL' && o.type === 'DOCUMENT');
}
setData(data.data); setData(data.data);
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
@@ -191,7 +195,10 @@ const BandoEdit = () => {
]); ]);
} }
} }
if (data.data.docs) {
data.data.docs = data.data.docs
.filter(o => o.source === 'CALL' && o.type === 'DOCUMENT');
}
setData(data.data); setData(data.data);
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
@@ -248,7 +255,8 @@ const BandoEdit = () => {
} }
} else { } else {
BandoService.getBando(id, getCallback, errGetCallback); BandoService.getBando(id, getCallback, errGetCallback);
FormsService.getFormsForCall(id, getFormsCallback, () => {}); FormsService.getFormsForCall(id, getFormsCallback, () => {
});
} }
}, [id]); }, [id]);

View File

@@ -157,7 +157,7 @@ const BandoView = () => {
<div className="appPageSection__withBorder"> <div className="appPageSection__withBorder">
<h2>{__('Documentazione Richiesta', 'gepafin')}</h2> <h2>{__('Documentazione Richiesta', 'gepafin')}</h2>
<div className="row rowContent"> <div className="row rowContent">
<p>{renderHtmlContent(data.documentationRequested)}</p> {renderHtmlContent(data.documentationRequested)}
</div> </div>
</div> </div>
@@ -177,7 +177,7 @@ const BandoView = () => {
<div className="row rowContent"> <div className="row rowContent">
<ul> <ul>
{data.docs {data.docs
.filter(o => o.source === 'CALL') .filter(o => o.source === 'CALL' && o.type === 'DOCUMENT')
.map((o, i) => <li key={i}> .map((o, i) => <li key={i}>
<a href={o.filePath} target="_blank" rel="noreferrer">{o.name}</a> <a href={o.filePath} target="_blank" rel="noreferrer">{o.name}</a>
</li>)} </li>)}

View File

@@ -26,6 +26,7 @@ import { Messages } from 'primereact/messages';
import { Message } from 'primereact/message'; import { Message } from 'primereact/message';
import { Toast } from 'primereact/toast'; import { Toast } from 'primereact/toast';
import { Editor } from 'primereact/editor'; import { Editor } from 'primereact/editor';
import { Dialog } from 'primereact/dialog';
const BandoViewBeneficiario = () => { const BandoViewBeneficiario = () => {
const isAsyncRequest = useStore().main.isAsyncRequest(); const isAsyncRequest = useStore().main.isAsyncRequest();
@@ -35,17 +36,26 @@ const BandoViewBeneficiario = () => {
const [data, setData] = useState({}); const [data, setData] = useState({});
const [newQuestion, setNewQuestion] = useState(''); const [newQuestion, setNewQuestion] = useState('');
const [applicationObj, setApplicationObj] = useState(true); const [applicationObj, setApplicationObj] = useState(true);
const [isVisibleConfidiPopup, setIsVisibleConfidiPopup] = useState(false);
const bandoMsgs = useRef(null); const bandoMsgs = useRef(null);
const toast = useRef(null); const toast = useRef(null);
/*const scaricaBando = () => { const displayConfidiPopup = () => {
setIsVisibleConfidiPopup(true);
}
}*/ const hideConfidiPopup = () => {
setIsVisibleConfidiPopup(false)
}
const scaricaModulistica = () => { const scaricaModulistica = () => {
if (data.confidi) {
displayConfidiPopup();
} else {
const bandoId = getBandoId(); const bandoId = getBandoId();
BandoService.getBandoPdf(bandoId, getCallPdfCallback, errCallPdfCallback); BandoService.getBandoPdf(bandoId, getCallPdfCallback, errCallPdfCallback);
} }
}
const getCallPdfCallback = (data) => { const getCallPdfCallback = (data) => {
const bandoId = getBandoId(); const bandoId = getBandoId();
@@ -77,6 +87,9 @@ const BandoViewBeneficiario = () => {
} }
const submitApplication = () => { const submitApplication = () => {
if (data.confidi) {
displayConfidiPopup();
} else {
if (applicationObj && applicationObj.id) { if (applicationObj && applicationObj.id) {
navigate(`/imieibandi/${applicationObj.id}`); navigate(`/imieibandi/${applicationObj.id}`);
} else { } else {
@@ -84,6 +97,7 @@ const BandoViewBeneficiario = () => {
ApplicationService.createApplication(bandoId, {}, createApplCallback, errCreateApplCallback, [['companyId', chosenCompanyId]]); ApplicationService.createApplication(bandoId, {}, createApplCallback, errCreateApplCallback, [['companyId', chosenCompanyId]]);
} }
} }
}
const createApplCallback = (data) => { const createApplCallback = (data) => {
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
@@ -308,7 +322,7 @@ const BandoViewBeneficiario = () => {
<div className="appPageSection__withBorder"> <div className="appPageSection__withBorder">
<h2>{__('Documentazione Richiesta', 'gepafin')}</h2> <h2>{__('Documentazione Richiesta', 'gepafin')}</h2>
<div className="row rowContent"> <div className="row rowContent">
<p>{renderHtmlContent(data.documentationRequested)}</p> {renderHtmlContent(data.documentationRequested)}
</div> </div>
</div> </div>
@@ -328,7 +342,7 @@ const BandoViewBeneficiario = () => {
<div className="row rowContent"> <div className="row rowContent">
<ul> <ul>
{data.docs {data.docs
.filter(o => o.source === 'CALL') .filter(o => o.source === 'CALL' && o.type === 'DOCUMENT')
.map((o, i) => <li key={i}> .map((o, i) => <li key={i}>
<a href={o.filePath} target="_blank" rel="noreferrer">{o.name}</a> <a href={o.filePath} target="_blank" rel="noreferrer">{o.name}</a>
</li>)} </li>)}
@@ -380,10 +394,14 @@ const BandoViewBeneficiario = () => {
: null} : null}
{data.confidi {data.confidi
? <> ? <Dialog header={data.name}
<Message severity="error" visible={isVisibleConfidiPopup}
text={__('Non sei abilitato a partecipare a questo Bando', 'gepafin')}/> style={{ width: '50vw' }}
</> onHide={hideConfidiPopup}>
<p>
{__('Non risultano convenzioni attive', 'gepafin')}
</p>
</Dialog>
: null} : null}
<div className="appPageSection"> <div className="appPageSection">
@@ -398,14 +416,13 @@ const BandoViewBeneficiario = () => {
icon="pi pi-download" iconPos="right"/>*/} icon="pi pi-download" iconPos="right"/>*/}
<Button <Button
type="button" type="button"
disabled={data.confidi}
outlined outlined
onClick={scaricaModulistica} onClick={scaricaModulistica}
label={__('Scarica Bando Completo e Modulistica', 'gepafin')} label={__('Scarica Bando Completo e Modulistica', 'gepafin')}
icon="pi pi-download" iconPos="right"/> icon="pi pi-download" iconPos="right"/>
<Button <Button
type="button" type="button"
disabled={isAsyncRequest || chosenCompanyId === 0 || data.confidi} disabled={isAsyncRequest || chosenCompanyId === 0}
onClick={submitApplication} onClick={submitApplication}
label={__('Presenta Domanda', 'gepafin')} label={__('Presenta Domanda', 'gepafin')}
icon="pi pi-save" iconPos="right"/> icon="pi pi-save" iconPos="right"/>

View File

@@ -15,6 +15,7 @@ import LatestBandiTable from './components/LatestBandiTable';
//import LatestUsersActivityTable from './components/LatestUsersActivityTable'; //import LatestUsersActivityTable from './components/LatestUsersActivityTable';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import MyEvaluationsTable from '../DashboardInstructor/components/MyEvaluationsTable'; import MyEvaluationsTable from '../DashboardInstructor/components/MyEvaluationsTable';
import AllDomandeTable from '../Domande/components/AllDomandeTable';
const Dashboard = () => { const Dashboard = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -130,7 +131,7 @@ const Dashboard = () => {
<div className="appPageSection"> <div className="appPageSection">
<h2>{__('Ultime domande pubblicate', 'gepafin')}</h2> <h2>{__('Ultime domande pubblicate', 'gepafin')}</h2>
<MyEvaluationsTable/> <AllDomandeTable/>
</div> </div>
{/*<div className="appPage__spacer"></div> {/*<div className="appPage__spacer"></div>

View File

@@ -125,10 +125,14 @@ const AllDomandeTable = ({ openDialogFn }) => {
}; };
const actionsBodyTemplate = (rowData) => { const actionsBodyTemplate = (rowData) => {
return <Button severity="info" return openDialogFn
? <Button severity="info"
onClick={() => openDialogFn(rowData.id)} onClick={() => openDialogFn(rowData.id)}
label={__('Assegnare', 'gepafin')} label={__('Assegnare', 'gepafin')}
icon="pi pi-pencil" size="small" iconPos="right" /> icon="pi pi-pencil" size="small" iconPos="right" />
: <Link to={'/domande'}>
<Button severity="info" label={__('Gestire', 'gepafin')} size="small" />
</Link>
} }
const header = renderHeader(); const header = renderHeader();

View File

@@ -25,11 +25,12 @@ const Login = () => {
const errorMsgs = useRef(null); const errorMsgs = useRef(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [visibleCacheFaq, setVisibleCacheFaq] = useState(false); const [visibleCacheFaq, setVisibleCacheFaq] = useState(false);
const [isMaintenance] = useState(false);
let [searchParams] = useSearchParams(); let [searchParams] = useSearchParams();
const { origin } = window.location; const { origin } = window.location;
const loginWithSpid = () => { const loginWithSpid = () => {
if (!loading) { if (!loading && !isMaintenance) {
if (APP_HUB_ID) { if (APP_HUB_ID) {
window.location.replace(`${API_BASE_URL}/saml2/authenticate/loginumbria?hubId=${APP_HUB_ID}`); window.location.replace(`${API_BASE_URL}/saml2/authenticate/loginumbria?hubId=${APP_HUB_ID}`);
} else { } else {
@@ -46,6 +47,7 @@ const Login = () => {
userData: data.data.user userData: data.data.user
}); });
} else { } else {
if (errorMsgs.current) {
errorMsgs.current.show([ errorMsgs.current.show([
{ {
sticky: true, severity: 'error', summary: '', sticky: true, severity: 'error', summary: '',
@@ -54,10 +56,12 @@ const Login = () => {
} }
]); ]);
} }
}
setLoading(false); setLoading(false);
} }
const validateError = (err) => { const validateError = (err) => {
if (errorMsgs.current) {
errorMsgs.current.show([ errorMsgs.current.show([
{ {
sticky: true, severity: 'error', summary: '', sticky: true, severity: 'error', summary: '',
@@ -65,6 +69,7 @@ const Login = () => {
closable: true closable: true
} }
]); ]);
}
setLoading(false); setLoading(false);
} }
@@ -77,6 +82,20 @@ const Login = () => {
setVisibleCacheFaq(false); setVisibleCacheFaq(false);
} }
useEffect(() => {
if (isMaintenance) {
if (errorMsgs.current) {
errorMsgs.current.show([
{
sticky: true, severity: 'info', summary: '',
detail: __('Piattaforma in manutenzione', 'gepafin'),
closable: false
}
]);
}
}
}, [isMaintenance])
useEffect(() => { useEffect(() => {
if (!isEmpty(token)) { if (!isEmpty(token)) {
window.location.replace('/') window.location.replace('/')
@@ -103,7 +122,7 @@ const Login = () => {
<Messages ref={errorMsgs}/> <Messages ref={errorMsgs}/>
<button className="appPageLogin__spidBtn" onClick={loginWithSpid}> <button className="appPageLogin__spidBtn" onClick={loginWithSpid} disabled={isMaintenance}>
<svg width="31" height="31" viewBox="0 0 31 31" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="31" height="31" viewBox="0 0 31 31" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_1156_13356)"> <g clipPath="url(#clip0_1156_13356)">
<path <path