- merged with develop;

This commit is contained in:
Vitalii Kiiko
2025-01-03 09:43:15 +01:00
63 changed files with 2966 additions and 415 deletions

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
import { isEmpty, head } from 'ramda';
import { klona } from 'klona';
@@ -29,6 +29,7 @@ const AddCompany = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const infoMsgs = useRef(null);
const [, debouncedPivaValue, setInputPiva] = useDebounce('', 1000);
const [vatCheckResponse, setVatCheckResponse] = useState({});
const {
control,
@@ -54,14 +55,19 @@ const AddCompany = () => {
address: '',
companyName: ''
}
setVatCheckResponse({});
Object.keys(formData).map(k => setValue(k, formData[k]));
}
const onSubmit = (formData) => {
infoMsgs.current.clear();
storeSet.main.setAsyncRequest();
const submitData = {
...formData,
vatCheckResponse
}
CompanyService.createCompany(formData, updateCallback, updateError);
CompanyService.createCompany(submitData, updateCallback, updateError);
};
const updateCallback = (data) => {
@@ -109,7 +115,7 @@ const AddCompany = () => {
const checkVatCallback = (data) => {
if (data.status === 'SUCCESS') {
const resp = data.data.data;
const resp = data.data.vatCheckResponse.data;
if (!isEmpty(resp)) {
const {
cap, cf, denominazione, piva, indirizzo, comune, dettaglio: { pec }
@@ -126,6 +132,7 @@ const AddCompany = () => {
companyName: denominazione
}
Object.keys(formData).map(k => setValue(k, formData[k]));
setVatCheckResponse(data.data.vatCheckResponse);
}
//setData(getFormattedBandiData(data.data));
} else {

View File

@@ -1,6 +1,10 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
import { useNavigate } from 'react-router-dom';
import { head } from 'ramda';
// store
import { useStore } from '../../store';
// components
import MyLatestSubmissionsTable from '../DashboardBeneficiario/components/MyLatestSubmissionsTable';
@@ -9,6 +13,9 @@ import ErrorBoundary from '../../components/ErrorBoundary';
const Applications = () => {
const navigate = useNavigate();
const chosenCompanyId = useStore().main.chosenCompanyId();
const companies = useStore().main.companies();
const company = head(companies.filter(o => o.id === chosenCompanyId));
const gotToBandiDisponibili = () => {
navigate('/bandi')
@@ -18,6 +25,7 @@ const Applications = () => {
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Domande in Lavorazione', 'gepafin')}</h1>
{company ? <span className="companyName">{company.companyName}</span> : null}
</div>
<div className="appPage__spacer"></div>

View File

@@ -97,7 +97,14 @@ const AllBandiTable = () => {
};
const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses} onChange={(e) => options.filterCallback(e.value, options.index)} itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter" showClear />;
return <Dropdown
value={options.value}
options={statuses}
onChange={(e) => options.filterCallback(e.value, options.index)}
itemTemplate={statusItemTemplate}
placeholder={translationStrings.selectOneLabel}
className="p-column-filter"
showClear />;
};
const statusItemTemplate = (option) => {
@@ -139,4 +146,4 @@ const AllBandiTable = () => {
)
}
export default AllBandiTable;
export default AllBandiTable;

View File

@@ -30,6 +30,8 @@ import { Button } from 'primereact/button';
// i18n
import translationStrings from '../../../../translationStringsForComponents';
const REACT_APP_HUB_ID = process.env.REACT_APP_HUB_ID;
const AllBandiAccordion = ({ showOnlyPreferred = false }) => {
const chosenCompanyId = useStore().main.chosenCompanyId();
const isAsyncRequest = useStore().main.isAsyncRequest();
@@ -182,11 +184,13 @@ const AllBandiAccordion = ({ showOnlyPreferred = false }) => {
<div className="p-3">
{renderHtmlContent(data.descriptionShort)}
<p>{__('Scadenza', 'gepafin')}: {getDateFromISOstring(data.dates[1])}</p>
{!isEmpty(chosenCompanyId) && chosenCompanyId !== 0 && !data.confidi
{!isEmpty(chosenCompanyId) && chosenCompanyId !== 0 && (!data.confidi
|| (data.confidi && data.id === 6 && REACT_APP_HUB_ID === 'p4lk3bcx1RStqTaIVVbXs'))
? <Button onClick={() => goToBandoPage(data.id)} severity="info">
{__('Partecipa', 'gepafin')}
</Button> : null}
{!isEmpty(chosenCompanyId) && chosenCompanyId !== 0 && data.confidi
&& (data.id !== 6 || (data.id === 6 && REACT_APP_HUB_ID !== 'p4lk3bcx1RStqTaIVVbXs'))
? <Button onClick={() => goToBandoPage(data.id)} severity="info">
{__('Mostra', 'gepafin')}
</Button> : null}
@@ -229,4 +233,4 @@ const AllBandiAccordion = ({ showOnlyPreferred = false }) => {
)
}
export default AllBandiAccordion;
export default AllBandiAccordion;

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
import { Link } from 'react-router-dom';
import { isEmpty } from 'ramda';
import { head, isEmpty } from 'ramda';
// store
import { useStore } from '../../store';
@@ -12,11 +12,14 @@ import ErrorBoundary from '../../components/ErrorBoundary';
const BandiBeneficiario = () => {
const chosenCompanyId = useStore().main.chosenCompanyId();
const companies = useStore().main.companies();
const company = head(companies.filter(o => o.id === chosenCompanyId));
return (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Bandi disponibili', 'gepafin')}</h1>
{company ? <span className="companyName">{company.companyName}</span> : null}
</div>
<div className="appPage__spacer"></div>

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
import { Link } from 'react-router-dom';
import { isEmpty } from 'ramda';
import { head, isEmpty } from 'ramda';
// store
import { useStore } from '../../store';
@@ -11,11 +11,14 @@ import AllBandiAccordion from '../BandiBeneficiario/components/AllBandiAccordion
const BandiPreferredBeneficiario = () => {
const chosenCompanyId = useStore().main.chosenCompanyId();
const companies = useStore().main.companies();
const company = head(companies.filter(o => o.id === chosenCompanyId));
return (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Bandi osservati', 'gepafin')}</h1>
{company ? <span className="companyName">{company.companyName}</span> : null}
</div>
<div className="appPage__spacer"></div>

View File

@@ -86,8 +86,8 @@ const BandoApplicationPreview = () => {
ApplicationService.validateApplication(applId, {}, validateApplicationCallback, errValidateApplicationCallback);
};
const onSubmit = () => {
};
/*const onSubmit = () => {
};*/
const validateApplicationCallback = (data) => {
if (data.status === 'SUCCESS') {
@@ -155,31 +155,6 @@ const BandoApplicationPreview = () => {
user: {}
};
/*if (company) {
dynamicData = Object.keys(company).reduce((acc, cur) => {
if ([
'companyName', 'vatNumber', 'codiceFiscale', 'address', 'phoneNumber',
'city', 'province', 'cap', 'country', 'pec', 'email', 'contactName', 'contactEmail'
].includes(cur)) {
acc.company[cur] = company[cur];
}
return acc;
}, dynamicData);
}
const userData = storeGet.main.userData();
Object.keys(userData).reduce((acc, cur) => {
if ([
'email', 'firstName', 'lastName', 'phoneNumber', 'codiceFiscale'
].includes(cur)) {
acc.user[cur] = userData[cur];
}
if (['dateOfBirth'].includes(cur)) {
acc.user[cur] = new Date(userData[cur]);
}
return acc;
}, dynamicData);*/
if (data.data.applicationFormResponse.content) {
// eslint-disable-next-line array-callback-return
data.data.applicationFormResponse.content.map((o) => {
@@ -252,33 +227,25 @@ const BandoApplicationPreview = () => {
{activeStep > 1 && activeStep <= totalSteps
? <Button
type="button"
disabled={'SUBMIT' === applicationStatus}
onClick={goBackward}
label={__('Vai indietro', 'gepafin')}
icon="pi pi-arrow-left"
iconPos="left"/> : null}
<Button
type="button"
disabled={isAsyncRequest || 'SUBMIT' === applicationStatus}
disabled={isAsyncRequest}
onClick={saveDraft}
outlined
label={__('Controlla', 'gepafin')} icon="pi pi-verified" iconPos="right"/>
{activeStep < totalSteps
? <Button
type="button"
disabled={'SUBMIT' === applicationStatus}
onClick={goForward}
label={__('Vai avanti', 'gepafin')}
icon="pi pi-arrow-right"
iconPos="right"/> : null}
{/*<Button
disabled={'SUBMIT' === applicationStatus}
label={__('Convalidare', 'gepafin')}
icon="pi pi-check"
iconPos="right"/>*/}
<Button
type="button"
disabled={'SUBMIT' === applicationStatus}
onClick={onDownloadApplicationPdf}
label={__('Scarica PDF', 'gepafin')}
icon="pi pi-download"
@@ -373,7 +340,7 @@ const BandoApplicationPreview = () => {
: <FormField
key={o.id}
type={o.name}
disabled={o.name === 'fileupload'}
disabled={o.name === 'fileupload' || 'DRAFT' !== applicationStatus}
fieldName={o.id}
label={label ? label.value : ''}
placeholder={placeholder ? placeholder.value : ''}

View File

@@ -104,7 +104,7 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => {
</div>
: <div ref={ref} className="formBuilder__element" style={{ opacity }} data-handler-id={handlerId}>
<div className="meta">
<Tag value={name} severity="info"/>
<Tag value={label} severity="info"/>
<BuilderElementProperLabel id={id} defaultLabel={label}/>
</div>
<div className="actions">

View File

@@ -10,6 +10,7 @@ import { Editor } from 'primereact/editor';
import { mimeTypes } from '../../../../../../configData';
import ElementSettingTableColumns from '../ElementSettingTableColumns';
import { InputSwitch } from 'primereact/inputswitch';
const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => {
@@ -17,6 +18,7 @@ const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => {
label: __('Label', 'gepafin'),
placeholder: __('Segnaposto', 'gepafin'),
step: __('Numero Decimali', 'gepafin'),
isRequestedAmount: __('Importo richiesto', 'gepafin'),
options: __('Opzioni', 'gepafin'),
mime: __('Tipo di file', 'gepafin'),
text: __('Testo formattato', 'gepafin'),
@@ -78,6 +80,10 @@ const ElementSetting = ({ setting, changeFn, updateDataFn, bandoStatus }) => {
name={setting.name}
bandoStatus={bandoStatus}
setDataFn={updateDataFn}/>
} else if (setting.name === 'isRequestedAmount') {
return <InputSwitch
checked={setting.value}
onChange={(e) => changeFn(e.value, setting.name)}/>
} else {
return <InputText id={setting.name} aria-describedby={`${setting.name}-help`}
value={setting.value}

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import { __ } from '@wordpress/i18n';
import { wrap } from 'object-path-immutable';
import { findIndex, propEq } from 'ramda';
import { pathOr } from 'ramda';
// components
import { InputText } from 'primereact/inputtext';
@@ -14,7 +14,8 @@ import uniqid from '../../../../../../helpers/uniqid';
const ElementSettingTableColumns = ({
value,
name,
setDataFn
setDataFn,
bandoStatus
}) => {
const [stateFieldData, setStateFieldData] = useState([]);
const [rowsData, setRowsData] = useState([]);
@@ -28,18 +29,19 @@ const ElementSettingTableColumns = ({
setStateFieldData([...stateFieldData, { name: uniqid('o'), label: '', predefined: false }]);
}
const addNewRow = (index) => {
const newStateFieldData = wrap(stateFieldData)
.insert([index, 'rows'], { label: '' }, stateFieldData[index].rows.length)
.value();
setStateFieldData(newStateFieldData);
const addNewRow = () => {
const obj = stateFieldData
.filter(o => o.predefined)
.reduce((acc, cur) => {
acc[cur.name] = ''
return acc;
}, {});
setRowsData([...rowsData, obj]);
}
const removeRow = (index, indexK) => {
const newStateFieldData = wrap(stateFieldData)
.del([index, 'rows', indexK])
.value();
setStateFieldData(newStateFieldData);
const removeRow = (index) => {
const newRowsData = wrap(rowsData).del([index]).value();
setRowsData(newRowsData);
}
const onInputChange = (e, index) => {
@@ -53,24 +55,31 @@ const ElementSettingTableColumns = ({
setStateFieldData(newData);
}
const onSubInputChange = (e, index, indexK) => {
const onSubInputChange = (e, name, index) => {
const { value } = e.target;
const newStateFieldData = wrap(stateFieldData)
.set([index, 'rows', indexK, 'label'], value)
.value();
setStateFieldData(newStateFieldData);
const newRowsData = wrap(rowsData).set([index, name], value).value();
setRowsData(newRowsData);
}
const setChecked = (value, index) => {
let name = '';
const newData = stateFieldData.map((o, i) => {
if (i === index) {
o.predefined = value;
if (value === false) {
o.rows = [];
}
name = o.name;
}
return o;
})
});
let newRowsData = [];
if (value === false) {
newRowsData = rowsData.map(o => wrap(o).set([name], '').value());
} else {
newRowsData = rowsData.map(o => wrap(o).set([name], '').value());
}
setRowsData(newRowsData);
setStateFieldData(newData);
}
@@ -79,23 +88,31 @@ const ElementSettingTableColumns = ({
<InputText value={item.label} onInput={(e) => onInputChange(e, i)}/>
<div className="flex-1">
<span>{__('Predefinito?', 'gepafin')}</span>
<InputSwitch checked={item.predefined} onChange={(e) => setChecked(e.value, i)}/>
<InputSwitch
checked={item.predefined}
disabled={bandoStatus === 'PUBLISH'}
onChange={(e) => setChecked(e.value, i)}/>
</div>
</>
}
const properSubField = (item, i, k) => {
return <InputText value={item.label} onInput={(e) => onSubInputChange(e, i, k)}/>
const properSubField = (item, i, name) => {
return <InputText value={item[name]} onInput={(e) => onSubInputChange(e, name, i)}/>
}
useEffect(() => {
const storeFieldData = value ?? [];
setStateFieldData(storeFieldData);
const stateFieldData = pathOr([], ['stateFieldData'], value);
setStateFieldData(stateFieldData);
const rowsData = pathOr([], ['rowsData'], value);
setRowsData(rowsData);
}, []);
useEffect(() => {
setDataFn(name, [...stateFieldData]);
}, [stateFieldData]);
setDataFn(name, {
stateFieldData,
rowsData
});
}, [stateFieldData, rowsData]);
stateFieldData.filter(o => o.predefined)
@@ -105,10 +122,10 @@ const ElementSettingTableColumns = ({
{stateFieldData.map((o, i) => <div key={i} className="formElementSettings__repeaterItem">
<div className="p-inputgroup flex-1">
{properField(o, i)}
<Button icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
<Button icon="pi pi-times" disabled={bandoStatus === 'PUBLISH'} className="p-button-danger" onClick={() => removeItem(i)}/>
</div>
</div>)}
<Button type="button" outlined label={__('Aggiungi', 'gepafin')} onClick={addNewItem}/>
<Button type="button" disabled={bandoStatus === 'PUBLISH'} outlined label={__('Aggiungi', 'gepafin')} onClick={addNewItem}/>
</div>
{stateFieldData
.filter(o => o.predefined)
@@ -116,19 +133,22 @@ const ElementSettingTableColumns = ({
<div className="formElementSettings__repeater formElementSettings__subRepeater">
<label>{__('Righe per colonna:', 'gepafin')} <strong>{o.label}</strong></label>
<div className="formElementSettings__repeater">
{o.rows.map((c, k) => {
const properIndex = findIndex(propEq(o.name, 'name'))(stateFieldData);
{rowsData.map((c, k) => {
return <div key={k} className="formElementSettings__repeaterItem">
<div className="p-inputgroup flex-1">
{properSubField(c, properIndex, k)}
<Button icon="pi pi-times" className="p-button-danger"
onClick={() => removeRow(properIndex, k)}/>
{properSubField(c, k, o.name)}
<Button icon="pi pi-times"
disabled={bandoStatus === 'PUBLISH'}
className="p-button-danger"
onClick={() => removeRow(k)}/>
</div>
</div>
})}
<Button type="button" outlined
<Button type="button"
outlined
disabled={bandoStatus === 'PUBLISH'}
label={__('Aggiungi una riga', 'gepafin')}
onClick={() => addNewRow(findIndex(propEq(o.name, 'name'))(stateFieldData))}/>
onClick={addNewRow}/>
</div>
</div>
</div>)}

View File

@@ -5,7 +5,7 @@ import { klona } from 'klona';
import { wrap } from 'object-path-immutable';
// store
import { storeSet, useStore } from '../../../../store';
import { storeGet, storeSet, useStore } from '../../../../store';
// components
import { InputText } from 'primereact/inputtext';
@@ -128,10 +128,20 @@ const BuilderElementSettings = ({ closeSettingsFn, bandoStatus }) => {
useEffect(() => {
const chosen = head(elements.filter(o => o.id === activeElement));
const elementItems = storeGet.main.elementItems();
const chosenElementItemCfg = head(elementItems.filter(o => o.name === chosen.name));
let settings = [];
if (chosenElementItemCfg) {
settings = chosenElementItemCfg.settings.map((o) => {
const setting = head(chosen.settings.filter(s => s.name === o.name));
return setting ? klona(setting) : klona(o)
});
}
if (chosen) {
setActiveElementData(klona(chosen));
setSettings(klona(chosen.settings));
setSettings(settings);
setValidators(klona(chosen.validators));
setDynamicData(chosen.dynamicData ? chosen.dynamicData : '');
setCriteria(chosen.criteria ? chosen.criteria : []);

View File

@@ -30,6 +30,8 @@ import { Editor } from 'primereact/editor';
import { Dialog } from 'primereact/dialog';
import PreferredBandoService from '../../service/preferred-bando-service';
const REACT_APP_HUB_ID = process.env.REACT_APP_HUB_ID;
const BandoViewBeneficiario = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const chosenCompanyId = useStore().main.chosenCompanyId();
@@ -89,30 +91,52 @@ const BandoViewBeneficiario = () => {
}
const submitBtnLabel = () => {
if (applicationObj && applicationObj.id) {
return __('Vai alla domanda', 'gepafin');
} else {
if (REACT_APP_HUB_ID === 't7jh5wfg9QXylNaTZkPoE') {
return __('Presenta domanda', 'gepafin');
} else {
if (applicationObj && applicationObj.id) {
return __('Vai alla domanda', 'gepafin');
} else {
return __('Presenta domanda', 'gepafin');
}
}
}
const submitBtnIcon = () => {
if (applicationObj && applicationObj.id) {
return 'pi pi-arrow-right';
} else {
if (REACT_APP_HUB_ID === 't7jh5wfg9QXylNaTZkPoE') {
return 'pi pi-save';
} else {
if (applicationObj && applicationObj.id) {
return 'pi pi-arrow-right';
} else {
return 'pi pi-save';
}
}
}
const submitApplication = () => {
if (data.confidi) {
displayConfidiPopup();
} else {
if (applicationObj && applicationObj.id) {
navigate(`/imieibandi/${applicationObj.id}`);
if (data.id === 6 && REACT_APP_HUB_ID === 'p4lk3bcx1RStqTaIVVbXs') {
if (applicationObj && applicationObj.id) {
navigate(`/imieibandi/${applicationObj.id}`);
} else {
const bandoId = getBandoId();
ApplicationService.createApplication(bandoId, {}, createApplCallback, errCreateApplCallback, [['companyId', chosenCompanyId]]);
}
} else {
displayConfidiPopup();
}
} else {
if (REACT_APP_HUB_ID === 't7jh5wfg9QXylNaTZkPoE') {
const bandoId = getBandoId();
ApplicationService.createApplication(bandoId, {}, createApplCallback, errCreateApplCallback, [['companyId', chosenCompanyId]]);
} else {
if (applicationObj && applicationObj.id) {
navigate(`/imieibandi/${applicationObj.id}`);
} else {
const bandoId = getBandoId();
ApplicationService.createApplication(bandoId, {}, createApplCallback, errCreateApplCallback, [['companyId', chosenCompanyId]]);
}
}
}
}
@@ -195,6 +219,7 @@ const BandoViewBeneficiario = () => {
if (data.status === 'SUCCESS') {
setData(getFormattedBandiData(data.data));
}
storeSet.main.unsetAsyncRequest();
}
const errGetBandoCallback = (data) => {
@@ -206,6 +231,7 @@ const BandoViewBeneficiario = () => {
});
}
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
const getFormattedBandiData = (data) => {
@@ -291,10 +317,13 @@ const BandoViewBeneficiario = () => {
BandoService.getBando(bandoId, getBandoCallback, errGetBandoCallback, [
['companyId', chosenCompanyId]
]);
ApplicationService.getApplications(getApplCallback, errGetApplCallback, [
['callId', bandoId],
['companyId', chosenCompanyId]
]);
if (REACT_APP_HUB_ID !== 't7jh5wfg9QXylNaTZkPoE') {
storeSet.main.setAsyncRequest();
ApplicationService.getApplications(getApplCallback, errGetApplCallback, [
['callId', bandoId],
['companyId', chosenCompanyId]
]);
}
}
}, [id, chosenCompanyId]);
@@ -521,4 +550,4 @@ const BandoViewBeneficiario = () => {
}
export default BandoViewBeneficiario;
export default BandoViewBeneficiario;

View File

@@ -42,7 +42,7 @@ const DraftApplicationsTable = () => {
if (data.status === 'SUCCESS') {
if (is(Array, data.data)) {
setItems(getFormattedBandiData(data.data));
setStatuses(uniq(items.map(o => o.status)))
setStatuses(uniq(data.data.map(o => o.status)))
initFilters();
}
}

View File

@@ -57,6 +57,23 @@ const MyLatestSubmissionsTable = () => {
setLocalAsyncRequest(false);
}
const handleDeleteApplication = (id) => {
setLocalAsyncRequest(true);
ApplicationService.deleteApplication(id, (resp) => delApplCallback(resp, id), errDelApplCallback)
}
const delApplCallback = (resp, id) => {
if (resp.status === 'SUCCESS') {
const newItems = items.filter(o => o.id !== id);
setItems(newItems);
}
setLocalAsyncRequest(false);
}
const errDelApplCallback = (data) => {
setLocalAsyncRequest(false);
}
const getFormattedBandiData = (data) => {
return [...(data || [])].map((d) => {
d.callEndDate = new Date(d.callEndDate);
@@ -130,7 +147,8 @@ const MyLatestSubmissionsTable = () => {
const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses}
onChange={(e) => options.filterCallback(e.value, options.index)}
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter"
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel}
className="p-column-filter"
showClear/>;
};
@@ -143,13 +161,24 @@ const MyLatestSubmissionsTable = () => {
};
const actionsBodyTemplate = (rowData) => {
return <Link to={`/imieibandi/${rowData.id}`}>
{'DRAFT' === rowData.status
? <Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small"
return 'DRAFT' === rowData.status
? <div className="appPageSection__tableActions lessGap">
<Link to={`/imieibandi/${rowData.id}`}>
<Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small"
iconPos="right"/>
</Link>
<Button severity="danger"
onClick={() => handleDeleteApplication(rowData.id)}
label={__('Cancella', 'gepafin')}
icon="pi pi-trash"
size="small"
iconPos="right"/>
</div>
: <Link to={`/imieibandi/${rowData.id}`}>
<Button severity="info" label={__('Mostra', 'gepafin')} icon="pi pi-eye" size="small"
iconPos="right"/>
: <Button severity="info" label={__('Mostra', 'gepafin')} icon="pi pi-eye" size="small"
iconPos="right"/>}
</Link>
}
const header = renderHeader();

View File

@@ -21,6 +21,7 @@ const DashboardBeneficiario = () => {
const [mainStats, setMainStats] = useState({});
const companies = useStore().main.companies();
const chosenCompanyId = useStore().main.chosenCompanyId();
const company = head(companies.filter(o => o.id === chosenCompanyId));
const goToAllSubmissions = () => {
navigate('/bandi');
@@ -51,6 +52,7 @@ const DashboardBeneficiario = () => {
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Dashboard', 'gepafin')}</h1>
{company ? <span className="companyName">{company.companyName}</span> : null}
</div>
<div className="appPage__spacer"></div>

View File

@@ -0,0 +1,960 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { is, isEmpty, isNil, sum, pathOr, head } from 'ramda';
import { klona } from 'klona';
import { wrap } from 'object-path-immutable';
// store
import { storeGet, storeSet, useStore } from '../../store';
// api
import ApplicationEvaluationService from '../../service/application-evaluation-service';
import AmendmentsService from '../../service/amendments-service';
import AppointmentService from '../../service/appointment-service';
// tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import getBandoLabel from '../../helpers/getBandoLabel';
import getDateFromISOstring from '../../helpers/getDateFromISOstring';
// components
import { Skeleton } from 'primereact/skeleton';
import { Button } from 'primereact/button';
import { Tag } from 'primereact/tag';
import { Checkbox } from 'primereact/checkbox';
import { Editor } from 'primereact/editor';
import { InputNumber } from 'primereact/inputnumber';
import { Toast } from 'primereact/toast';
import { Dialog } from 'primereact/dialog';
import HelpIcon from '../../icons/HelpIcon';
import { classNames } from 'primereact/utils';
import { InputTextarea } from 'primereact/inputtextarea';
import { InputText } from 'primereact/inputtext';
import DownloadApplicationArchive from '../DomandaEditPreInstructor/components/DownloadApplicationArchive';
import DownloadCompanyDelegation from '../DomandaEditPreInstructor/components/DownloadCompanyDelegation';
import DownloadSignedApplication from '../DomandaEditPreInstructor/components/DownloadSignedApplication';
import ListOfFiles from '../DomandaEditPreInstructor/components/ListOfFiles';
import RepeaterFields from '../DomandaEditPreInstructor/components/RepeaterFields';
const APP_EVALUATION_FLOW_ID = process.env.REACT_APP_EVALUATION_FLOW_ID;
const DomandaEditPreInstructor = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const { id } = useParams();
const navigate = useNavigate();
const [data, setData] = useState({});
const [isVisibleCriterionData, setIsVisibleCriterionData] = useState(0);
const [criterionDataTitle, setCriterionDataTitle] = useState('');
const [criterionDataContent, setCriterionDataContent] = useState('');
const [isAdmissible, setIsAdmissible] = useState(false);
const [connectedSoccorsoId, setConnectedSoccorsoId] = useState(0);
const toast = useRef(null);
const [loading, setLoading] = useState(false);
const [isVisibleCompleteDialog, setIsVisibleCompleteDialog] = useState(false);
const [operationType, setOperationType] = useState('');
const [motivation, setMotivation] = useState('');
const [isVisibleAppointmentDialog, setIsVisibleAppointmentDialog] = useState(false);
const [allFilesRated, setAllFilesRated] = useState(false);
const [atLeastOneChecked, setAtLeastOneChecked] = useState(false);
const [allChecksChecked, setAllChecksChecked] = useState(false);
const [appointmentData, setAppointmentData] = useState({
title: '',
text: '',
duration: 0,
amount: 0
});
const goToEvaluationsPage = () => {
navigate('/domande');
}
const updateFlagsForSoccorso = (data) => {
let nonRatedFilesLength = 0;
if (data.files) {
const nonRatedFiles = data.files
.map(el => el.valid)
.filter(v => isNil(v));
nonRatedFilesLength = nonRatedFiles.length;
}
if (data.amendmentDetails) {
const nonRatedFiles = data.amendmentDetails
.map(el => el.valid)
.filter(v => isNil(v));
nonRatedFilesLength = nonRatedFiles.length;
}
setAllFilesRated(nonRatedFilesLength === 0);
if (data.checklist) {
const checkedChecklistItems = data.checklist
.map(el => el.valid)
.filter(v => v);
setAtLeastOneChecked(checkedChecklistItems.length > 0);
setAllChecksChecked(checkedChecklistItems.length === data.checklist.length)
}
}
const doNewSoccorso = () => {
if (connectedSoccorsoId !== 0) {
navigate(`/domande/${id}/soccorso/${connectedSoccorsoId}`);
} else {
doSaveDraft(`/domande/${id}/aggiungi-soccorso/`)
}
}
const getCallback = (data) => {
if (data.status === 'SUCCESS') {
setData(getFormattedData(data.data));
setMotivation(data.data.motivation);
updateFlagsForSoccorso(data.data);
}
storeSet.main.unsetAsyncRequest();
}
const errGetCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: 'error',
summary: '',
detail: data.message
});
}
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
const getFormattedData = (data) => {
data.submissionDate = is(String, data.submissionDate) ? new Date(data.submissionDate) : (data.submissionDate ? data.submissionDate : '');
data.evaluationDate = is(String, data.evaluationDate) ? new Date(data.evaluationDate) : (data.evaluationDate ? data.evaluationDate : '');
return data;
};
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();
const updateEvaluationValue = (value, path, maxValue = null) => {
let finalValue = value;
if (maxValue || maxValue === 0) {
finalValue = value > maxValue ? maxValue : value;
}
const newData = wrap(data).set(path, finalValue).value();
setData(newData);
updateFlagsForSoccorso(newData);
}
const doSaveDraft = useCallback((doRedirect = '') => {
const formData = {
criteria: klona(data.criteria),
checklist: klona(data.checklist),
files: klona(data.files),
evaluationDocument: klona(data.evaluationDocument.map(o => ({
...o,
fileValue: o.fileValue[0] ? o.fileValue[0].id : ''
})
)),
amendmentDetails: klona(data.amendmentDetails),
note: data.note
}
ApplicationEvaluationService.updateEvaluation(
data.assignedApplicationId,
formData,
(data) => updateCallback(data, doRedirect),
errUpdateCallback
);
}, [data]);
const updateCallback = (data, doRedirect = '') => {
if (data.status === 'SUCCESS') {
setData(getFormattedData(data.data));
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
if (!isEmpty(doRedirect)) {
navigate(doRedirect);
}
}
storeSet.main.unsetAsyncRequest();
}
const errUpdateCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: 'error',
summary: '',
detail: data.message
});
}
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
const doApprove = () => {
const formData = {
applicationStatus: 'APPROVED',
criteria: klona(data.criteria),
checklist: klona(data.checklist),
files: klona(data.files),
note: data.note,
motivation
}
setIsVisibleCompleteDialog(false);
ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateStatusCallback, errUpdateStatusCallback);
}
const doReject = () => {
const formData = {
applicationStatus: 'REJECTED',
criteria: klona(data.criteria),
checklist: klona(data.checklist),
files: klona(data.files),
note: data.note,
motivation
}
setIsVisibleCompleteDialog(false);
ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateStatusCallback, errUpdateStatusCallback);
}
const updateStatusCallback = (data) => {
if (data.status === 'SUCCESS') {
setData(getFormattedData(data.data));
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
}
storeSet.main.unsetAsyncRequest();
}
const errUpdateStatusCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: 'error',
summary: '',
detail: data.message
});
}
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
const displayCriterionData = (id) => {
const criterion = head(data.criteria.filter(o => o.id === id));
setCriterionDataTitle(criterion.label);
const content = <div className="criterionRelatedData">
<h3>{__('I campi correlati')}</h3>
{criterion.criteriaMappedFields ? criterion.criteriaMappedFields.map(o => criteriaDataItem(o)) : null}
</div>;
setCriterionDataContent(content);
setIsVisibleCriterionData(id);
}
const criteriaDataItem = (item) => {
let content = '';
switch (item.fieldName) {
case 'fileupload' :
content = <ul>
{item.fieldValue
? item.fieldValue.map(o => <li key={o.id}>
{o.filePath ? <a href={o.filePath}>{o.name}</a> : null}
</li>)
: null}
</ul>;
break;
case 'table' :
const th = Object.keys(item.fieldValue[0]);
content = <table>
<thead>
<tr>
{th.map(v => <th key={v}>{v}</th>)}
</tr>
</thead>
<tbody>
{item.fieldValue
? item.fieldValue.map((o, i) => <tr key={i}>
{Object.values(o).map(v => <td key={v}>{v}</td>)}
</tr>)
: null}
</tbody>
</table>;
break;
default :
content = item.fieldValue;
break;
}
return <div key={item.id} className="criterionRelatedData__item">
<strong>{item.fieldLabel}</strong>
{content}
</div>
}
const hideCriterionData = () => {
setIsVisibleCriterionData(0);
setCriterionDataTitle('');
setCriterionDataContent('');
}
const getAmendmentsCallback = (data) => {
if (data.status === 'SUCCESS') {
if (data.data.length) {
setConnectedSoccorsoId(data.data[0].id);
}
}
}
const errGetAmendmentsCallback = () => {
if (toast.current && data.message) {
toast.current.show({
severity: 'error',
summary: '',
detail: data.message
});
}
set404FromErrorResponse(data);
}
const shouldDisableField = (fieldName) => {
return !['EVALUATION'].includes(data.applicationStatus)
|| (['ADMISSIBLE'].includes(data.applicationStatus) && fieldName !== 'criteria')
}
const headerCompleteDialog = () => {
return 'approve' === operationType
? <span>{__('Confermare l\'approvazione', 'gepafin')}</span>
: <span>{__('Confermare il rifiuto', 'gepafin')}</span>;
}
const hideCompleteDialog = () => {
setIsVisibleCompleteDialog(false);
setOperationType('');
setMotivation('');
}
const footerCompleteDialog = () => {
return <div>
<Button type="button" label={__('Anulla', 'gepafin')} onClick={hideCompleteDialog} outlined/>
<Button
type="button"
disabled={loading}
label={__('Invia', 'gepafin')} onClick={'approve' === operationType ? doApprove : doReject}/>
</div>
}
const initiateApproving = () => {
setOperationType('approve');
setIsVisibleCompleteDialog(true);
}
const initiateRejecting = () => {
setOperationType('reject');
setIsVisibleCompleteDialog(true);
}
const doCheckNDG = () => {
storeSet.main.setAsyncRequest();
doSaveDraft();
setTimeout(() => {
AppointmentService.getNdg(id, getNdgCallback, errGetNdgCallback);
}, 100);
}
const getNdgCallback = (data) => {
if (data.status === 'SUCCESS') {
if (toast.current && data.message) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
}
storeSet.main.unsetAsyncRequest();
}
const errGetNdgCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: data.status === 'SUCCESS' ? 'info' : 'error',
summary: '',
detail: data.message
});
}
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
const doCreateAppointment = () => {
setAppointmentData({
title: '',
text: '',
duration: 0,
amount: 0
});
setIsVisibleAppointmentDialog(true);
}
const setValue = (name, value) => {
const newData = wrap(appointmentData).set(name, value).value();
setAppointmentData(newData);
}
const headerAppointmentDialog = () => {
return <span>{__('Crea appuntamento', 'gepafin')}</span>;
}
const hideAppointmentDialog = () => {
setIsVisibleAppointmentDialog(false);
setAppointmentData({});
}
const footerAppointmentDialog = () => {
return <div>
<Button type="button" label={__('Anulla', 'gepafin')} onClick={hideAppointmentDialog} outlined/>
<Button
type="button"
disabled={loading}
label={__('Invia', 'gepafin')} onClick={doCreateAppointmentRequest}/>
</div>
}
const doCreateAppointmentRequest = () => {
if (
!isEmpty(appointmentData.title) && !isEmpty(appointmentData.text) && !isEmpty(appointmentData.amount)
&& !isEmpty(appointmentData.duration) && appointmentData.duration !== 0 && appointmentData.amount !== 0
) {
storeSet.main.setAsyncRequest();
const submitData = {
'importoBreveTermine': appointmentData.amount,
'durataMesiFinanziamento': appointmentData.duration,
'nota': {
'titolo': appointmentData.title,
'testo': appointmentData.text
}
}
AppointmentService.createAppointment(id, submitData, getAppointemntCallback, errGetAppointemntCallback);
}
}
const getAppointemntCallback = (data) => {
if (data.status === 'SUCCESS') {
if (toast.current && data.message) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
}
setIsVisibleAppointmentDialog(false);
storeSet.main.unsetAsyncRequest();
}
const errGetAppointemntCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: data.status === 'SUCCESS' ? 'info' : 'error',
summary: '',
detail: data.message
});
}
setIsVisibleAppointmentDialog(false);
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
const doMakeAdmisible = () => {
// TODO
}
const evaluationShouldBeBlocked = (data = {}) => {
const userData = storeGet.main.userData()
return isAsyncRequest || userData.id !== data.assignedUserId;
}
useEffect(() => {
const maxScore = pathOr(0, ['minScore'], data);
const criteria = pathOr([], ['criteria'], data);
const scoreSum = sum(criteria.map(o => o.score));
setIsAdmissible(scoreSum !== 0 && scoreSum >= maxScore);
}, [data]);
useEffect(() => {
const parsed = parseInt(id)
const entityId = !isNaN(parsed) ? parsed : 0;
storeSet.main.setAsyncRequest();
ApplicationEvaluationService.getEvaluationByApplId(getCallback, errGetCallback, [
['applicationId', entityId]
]);
AmendmentsService.getSoccorsoByApplId(entityId, getAmendmentsCallback, errGetAmendmentsCallback, [
['statuses', 'AWAITING']
]);
}, [id]);
return (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Valuta domanda', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<Toast ref={toast}/>
<div className="appPageSection__row">
<Button
type="button"
outlined
onClick={goToEvaluationsPage}
label={__('Indietro', 'gepafin')}
icon="pi pi-arrow-left" iconPos="left"/>
</div>
<div className="appPage__spacer"></div>
{!isAsyncRequest && !isEmpty(data)
? <div className="appPage__content">
<div className="appPageSection__withBorder columns">
<p className="appPageSection__pMeta">
<span>{__('ID domanda', 'gepafin')}</span>
<span>{data.applicationId}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Protocollo', 'gepafin')}</span>
<span>{data.protocolNumber}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('NDG', 'gepafin')}</span>
<span>{data.ndg}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Appuntamento', 'gepafin')}</span>
<span>{data.appointmentId}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Bando', 'gepafin')}</span>
<span>{data.callName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Referente Aziendale', 'gepafin')}</span>
<span>{data.beneficiary}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Azienda Beneficiaria', 'gepafin')}</span>
<span>{data.companyName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Data ricezione', 'gepafin')}</span>
<span>{getDateFromISOstring(data.submissionDate)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Data assegnazione', 'gepafin')}</span>
<span>{getDateFromISOstring(data.assignedAt)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Scadenza Valutazione', 'gepafin')}</span>
<span>{getDateFromISOstring(data.evaluationEndDate)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Stato', 'gepafin')}</span>
<span>{getBandoLabel(data.applicationStatus)}</span>
</p>
</div>
<div className="appPageSection">
<h2>{__('Scarica documenti della domanda', 'gepafin')}</h2>
<div className="appPageSection__row autoFlow">
<DownloadApplicationArchive applicationId={id}/>
<DownloadSignedApplication applicationId={id}/>
<DownloadCompanyDelegation applicationId={id}/>
</div>
</div>
<div className="appPageSection">
<h2>{__('Documenti aggiuntivi', 'gepafin')}</h2>
<RepeaterFields
defaultValue={data.evaluationDocument ?? []}
updateFn={(data) => updateEvaluationValue(
data,
['evaluationDocument']
)}
shouldDisable={['APPROVED', 'REJECTED'].includes(data.applicationStatus) || evaluationShouldBeBlocked(data)}
sourceId={data.assignedApplicationId}
sourceName="evaluation"/>
</div>
<div className="appPageSection">
<h2>{__('Checklist Valutazione', 'gepafin')}</h2>
<div className="appPageSection columns">
<div>
<h3>{__('Lista', 'gepafin')}</h3>
<div className="appPageSection__withBorder grey" style={{ marginBottom: '20px' }}>
<div className="appPageSection__checklist">
{data.checklist.map((o, i) => <div key={o.id}>
<Checkbox
disabled={shouldDisableField('checklist') || evaluationShouldBeBlocked(data)}
inputId={`checklist_${o.id}`}
onChange={(e) => updateEvaluationValue(
e.checked,
['checklist', i, 'valid']
)}
checked={o.valid}></Checkbox>
<label htmlFor={`checklist_${o.id}`}>{o.label}</label>
</div>)}
</div>
</div>
<h3>{__('Note', 'gepafin')}</h3>
<div>
<Editor
value={data.note}
readOnly={shouldDisableField('note') || evaluationShouldBeBlocked(data)}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateEvaluationValue(
e.htmlValue,
['note']
)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</div>
<div>
<h3>{__('Documenti allegati', 'gepafin')}</h3>
<ListOfFiles
files={data.files}
updateFn={updateEvaluationValue}
shouldDisableFieldFn={(name) => shouldDisableField(name) || evaluationShouldBeBlocked(data)}
name="files"
ndg={data.ndg}
applicationId={id}/>
</div>
</div>
</div>
{!isEmpty(data.amendmentDetails)
? <div className="appPageSection">
<h2>{__('Documenti di soccorso', 'gepafin')}</h2>
<ListOfFiles
files={data.amendmentDetails}
updateFn={updateEvaluationValue}
shouldDisableFieldFn={(name) => shouldDisableField(name) || evaluationShouldBeBlocked(data)}
name="amendmentDetails"
ndg={data.ndg}
applicationId={id}/>
</div> : null}
<div className="appPageSection">
<h2>{__('Punteggi di valutazione', 'gepafin')}</h2>
{data.criteria
? <table className="myTable">
<thead className="myThead">
<tr>
<th>{__('Parametro', 'gepafin')}</th>
<th style={{ width: 200 }}>{__('Punteggio', 'gepafin')}</th>
<th style={{ width: 220 }}>{__('Stato', 'gepafin')}</th>
</tr>
</thead>
<tbody className="myTbody">
{data.criteria.map((o, i) => <tr key={o.id}>
<td>{o.label}</td>
<td>
<div className="p-inputgroup">
<InputNumber
disabled={shouldDisableField('criteria') || evaluationShouldBeBlocked(data)}
placeholder={__('Punteggio', 'gepafin')}
keyfilter="int"
value={o.score}
max={o.maxScore}
onChange={(e) => updateEvaluationValue(
e.value,
['criteria', i, 'score'],
o.criteria
)}/>
<span className="p-inputgroup-addon">
/ {o.maxScore}
</span>
</div>
</td>
<td>
<div className="appPageSection__iconActions">
{!isEmpty(o.criteriaMappedFields)
? <Button icon="pi pi-eye"
rounded outlined severity="info"
onClick={() => displayCriterionData(o.id)}
aria-label={__('Mostra', 'gepafin')}/> : null}
<Button icon="pi pi-thumbs-up" rounded outlined
disabled={shouldDisableField('criteria') || evaluationShouldBeBlocked(data)}
severity={!isNil(o.valid) && o.valid ? 'success' : 'secondary'}
onClick={() => updateEvaluationValue(
true,
['criteria', i, 'valid']
)}
aria-label={__('Su', 'gepafin')}/>
<Button icon="pi pi-thumbs-down" rounded outlined
disabled={shouldDisableField('criteria') || evaluationShouldBeBlocked(data)}
severity={!isNil(o.valid) && !o.valid ? 'danger' : 'secondary'}
onClick={() => updateEvaluationValue(
false,
['criteria', i, 'valid']
)}
aria-label={__('Giu', 'gepafin')}/>
</div>
</td>
</tr>)}
<tr>
<td>{__('Punteggio:', 'gepafin')}</td>
<td>{sum(data.criteria.map(o => o.score))}</td>
<td>
{isAdmissible
? <Tag icon="pi pi-check" severity="success"
value={__('Ammissibile')}></Tag> : null}
{!isAdmissible
? <Tag icon="pi pi-times" severity="warning"
value={__('Inammissibile')}></Tag> : null}
</td>
</tr>
</tbody>
<tfoot className="myTfoot">
<tr>
<td colSpan="3">{sprintf(__('Punteggio minimo per l\'ammissione: %d'), data.minScore)}</td>
</tr>
</tfoot>
</table> : null}
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span>
</div>
<div className="appPageSection">
<div className="appPageSection__actions">
{['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus)
? <Button
type="button"
disabled={!data.id || data.status === 'CLOSE' || (data.applicationStatus === 'EVALUATION'
&& (!allFilesRated || !atLeastOneChecked)) || evaluationShouldBeBlocked(data)}
onClick={doNewSoccorso}
outlined
label={<>
{data.applicationStatus === 'EVALUATION'
? __('Richiedi Soccorso Istruttorio', 'gepafin')
: __('Apri Soccorso Istruttorio', 'gepafin')}
<i style={{ marginLeft: 7 }}>
<HelpIcon/>
</i>
</>}
/> : null}
{data.id
? <Button
type="button"
disabled={data.status === 'CLOSE' || evaluationShouldBeBlocked(data)}
onClick={() => doSaveDraft()}
outlined
label={__('Salva bozza valutazione', 'gepafin')}
icon="pi pi-save" iconPos="right"/>
: <Button
type="button"
onClick={() => doSaveDraft()}
label={__('Crea valutazione', 'gepafin')}
icon="pi pi-save" iconPos="right"/>}
{/*{APP_EVALUATION_FLOW_ID === '1' && ['EVALUATION'].includes(data.applicationStatus)
? <Button
type="button"
disabled={!data.id || !allFilesRated || !allChecksChecked || evaluationShouldBeBlocked(data)}
onClick={doCheckNDG}
label={__('Controlla NDG', 'gepafin')}
/> : null}*/}
<Button
type="button"
disabled={true}
onClick={() => {}}
label={__('Controlla NDG', 'gepafin')}
/>
{/*{APP_EVALUATION_FLOW_ID === '1' && ['NDG'].includes(data.applicationStatus) && data.ndg
? <Button
type="button"
disabled={!data.id || evaluationShouldBeBlocked(data)}
onClick={doCreateAppointment}
label={__('Crea l\'appuntamento', 'gepafin')}
/> : null}*/}
<Button
type="button"
disabled={true}
onClick={() => {}}
label={__('Crea l\'appuntamento', 'gepafin')}
/>
{/*{APP_EVALUATION_FLOW_ID === '1' && ['APPOINTMENT'].includes(data.applicationStatus)
? <Button
type="button"
disabled={!data.id || evaluationShouldBeBlocked(data)}
onClick={doMakeAdmisible}
label={__('Ammissibile', 'gepafin')}
/> : null}*/}
<Button
type="button"
disabled={true}
onClick={() => {}}
label={__('Ammissibile', 'gepafin')}
/>
{data.id
? <Button
type="button"
disabled={!isAdmissible || ['APPROVED'].includes(data.applicationStatus) || evaluationShouldBeBlocked(data)}
/*disabled={!isAdmissible
|| (APP_EVALUATION_FLOW_ID === '1' && !['ADMISSIBLE', 'APPOINTMENT'].includes(data.applicationStatus))}*/
onClick={initiateApproving}
label={__('Approva Domanda', 'gepafin')}
icon="pi pi-check" iconPos="right"/> : null}
{/*{data.id
? <Button
type="button"
disabled={APP_EVALUATION_FLOW_ID === '1' && !['ADMISSIBLE', 'APPOINTMENT'].includes(data.applicationStatus)}
onClick={initiateRejecting}
label={__('Respingi Domanda', 'gepafin')}
icon="pi pi-times" iconPos="right"/> : null}*/}
{data.id
? <Button
type="button"
disabled={APP_EVALUATION_FLOW_ID === '1'
&& !['EVALUATION', 'ADMISSIBLE', 'APPOINTMENT'].includes(data.applicationStatus)
|| evaluationShouldBeBlocked(data)}
onClick={initiateRejecting}
label={__('Respingi Domanda', 'gepafin')}
icon="pi pi-times" iconPos="right"/> : null}
</div>
</div>
<Dialog
header={criterionDataTitle}
visible={isVisibleCriterionData !== 0}
style={{ width: '50vw' }}
onHide={hideCriterionData}>
{criterionDataContent}
</Dialog>
<Dialog
visible={isVisibleCompleteDialog}
modal
header={headerCompleteDialog}
footer={footerCompleteDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideCompleteDialog}>
<div className="appForm__field">
<label>{__('Motivazione', 'gepafin')}</label>
<Editor
value={motivation}
readOnly={loading}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => setMotivation(e.htmlValue)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</Dialog>
<Dialog
visible={isVisibleAppointmentDialog}
modal
header={headerAppointmentDialog}
footer={footerAppointmentDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideAppointmentDialog}>
<div className="appForm__field">
<label
className={classNames({ 'p-error': isEmpty(appointmentData.amount) || appointmentData.amount === 0 })}>
{__('Importo', 'gepafin')}
</label>
<InputNumber
value={appointmentData.amount}
keyfilter="int"
invalid={isEmpty(appointmentData.amount) || appointmentData.amount === 0}
onChange={(e) => setValue('amount', e.value)}/>
</div>
<div className="appForm__field">
<label
className={classNames({ 'p-error': isEmpty(appointmentData.duration) || appointmentData.duration === 0 })}>
{__('Durata', 'gepafin')}
</label>
<InputNumber
value={appointmentData.duration}
keyfilter="int"
invalid={isEmpty(appointmentData.duration) || appointmentData.duration === 0}
onChange={(e) => setValue('duration', e.value)}/>
</div>
<div className="appForm__field">
<label className={classNames({ 'p-error': isEmpty(appointmentData.title) })}>
{__('Titolo', 'gepafin')}
</label>
<InputText
value={appointmentData.title}
invalid={isEmpty(appointmentData.title)}
onChange={(e) => setValue('title', e.target.value)}/>
</div>
<div className="appForm__field">
<label className={classNames({ 'p-error': isEmpty(appointmentData.text) })}>
{__('Messaggio', 'gepafin')}
</label>
<InputTextarea
value={appointmentData.text}
invalid={isEmpty(appointmentData.text)}
onChange={(e) => setValue('text', e.target.value)}
rows={3}
cols={30}/>
</div>
</Dialog>
</div>
: <>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="2rem" className="mb-8"></Skeleton>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="4rem" className="mb-8"></Skeleton>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="2rem" className="mb-8"></Skeleton>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="4rem"></Skeleton>
</>}
</div>
)
}
export default DomandaEditPreInstructor;

View File

@@ -1,8 +1,14 @@
import React, { useEffect, useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
import { isEmpty } from 'ramda';
import { isEmpty, pathOr, isNil } from 'ramda';
import { wrap } from 'object-path-immutable';
// store
import AppointmentService from '../../../../service/appointment-service';
// tools
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
// components
import { Button } from 'primereact/button';
import { Dialog } from 'primereact/dialog';
@@ -11,14 +17,11 @@ import { Dropdown } from 'primereact/dropdown';
import { classNames } from 'primereact/utils';
import { InputSwitch } from 'primereact/inputswitch';
import { InputText } from 'primereact/inputtext';
import { classificationType, protocolType } from '../../../../configData';
import AppointmentService from '../../../../service/appointment-service';
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
import { storeSet } from '../../../../store';
import { Toast } from 'primereact/toast';
const ArchiveDocument = ({ applicationId, ndg = '', fileId = 0 }) => {
import { classificationType, protocolType } from '../../../../configData';
const ArchiveDocument = ({ applicationId, ndg = '', fileId = 0, docAttachmentId = null, updateFn = () => {} }) => {
const [loading, setLoading] = useState(false);
const [isVisibleDialog, setIsVisibleDialog] = useState(false);
const [modalData, setModalData] = useState({});
@@ -87,19 +90,29 @@ const ArchiveDocument = ({ applicationId, ndg = '', fileId = 0 }) => {
const submitCallback = (data) => {
if (data.status === 'SUCCESS') {
console.log(data.data);
if (toast.current && data.message) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
const documentAttachmentId = pathOr('fake_id', ['documentAttachmentId'], data.data);
updateFn(documentAttachmentId);
}
setIsVisibleDialog(false);
setLoading(false);
}
const errSubmitCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: 'error',
severity: data.status === 'SUCCESS' ? 'info' : 'error',
summary: '',
detail: data.message
});
}
setIsVisibleDialog(false);
set404FromErrorResponse(data);
setLoading(false);
}
@@ -119,7 +132,7 @@ const ArchiveDocument = ({ applicationId, ndg = '', fileId = 0 }) => {
setTypes(protocolType.map(o => ({ value: o.id, label: o.name })));
}, []);
return (!isEmpty(ndg)
return (!isEmpty(ndg) && !isNil(ndg) && !docAttachmentId
? <>
<Toast ref={toast}/>
<Button icon="pi pi-file-export"

View File

@@ -3,22 +3,35 @@ import { Button } from 'primereact/button';
import { __ } from '@wordpress/i18n';
import { isNil } from 'ramda';
import ArchiveDocument from '../ArchiveDocument';
import renderHtmlContent from '../../../../helpers/renderHtmlContent';
import uniqid from '../../../../helpers/uniqid';
const ListOfFiles = ({ files, updateFn, shouldDisableFieldFn, name, ndg, applicationId }) => {
return (
<ol className="appPageSection__list">
{files.map((o, i) => <li key={o.id} className="appPageSection__listItem">
{files.map((o, i) => <li key={`${o.id}_${uniqid('f')}`} className="appPageSection__listItem">
<div className="appPageSection__listItemRow">
<span>{o.label}</span>
<div>{renderHtmlContent(o.label)}</div>
<div className="appPageSection__iconActions">
{o.fileDetail && o.fileDetail.length === 1
? <Button icon="pi pi-eye" rounded
? <>
<Button icon="pi pi-eye" rounded
onClick={() => {
window.open(o.fileDetail[0].filePath, '_blank').focus()
}}
outlined severity="info"
aria-label={__('Mostra', 'gepafin')}/> : null}
aria-label={__('Mostra', 'gepafin')}/>
<ArchiveDocument
ndg={ndg}
applicationId={applicationId}
fileId={o.fileDetail[0].id}
updateFn={(val) => updateFn(
val,
[name, i, 'fileDetail', 0, 'documentAttachmentId']
)}
docAttachmentId={o.fileDetail[0].documentAttachmentId}/>
</> : null}
<Button icon="pi pi-thumbs-up" rounded outlined
disabled={shouldDisableFieldFn(name)}
severity={!isNil(o.valid) && o.valid ? 'success' : 'secondary'}
@@ -45,7 +58,7 @@ const ListOfFiles = ({ files, updateFn, shouldDisableFieldFn, name, ndg, applica
flexDirection: 'column',
gap: '10px'
}}>
{o.fileDetail.map((k) => <li key={k.id} style={{
{o.fileDetail.map((k, ind) => <li key={k.id} style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
@@ -53,7 +66,15 @@ const ListOfFiles = ({ files, updateFn, shouldDisableFieldFn, name, ndg, applica
}}>
<span>{k.name}</span>
<div className="appPageSection__iconActions">
<ArchiveDocument ndg={ndg} applicationId={applicationId} fileId={k.id}/>
<ArchiveDocument
ndg={ndg}
applicationId={applicationId}
fileId={k.id}
updateFn={(val) => updateFn(
val,
[name, i, 'fileDetail', ind, 'documentAttachmentId']
)}
docAttachmentId={k.documentAttachmentId}/>
<Button icon="pi pi-eye" rounded
onClick={() => {
window.open(k.filePath, '_blank').focus()

View File

@@ -0,0 +1,149 @@
import React, { useMemo, useState, useCallback } from 'react';
import { useForm, useFieldArray } from 'react-hook-form';
import { isEmpty, head } from 'ramda';
import { __ } from '@wordpress/i18n';
import { klona } from 'klona';
// tools
import uniqid from '../../../../helpers/uniqid';
// components
import FormField from '../../../../components/FormField';
import { Button } from 'primereact/button';
const RepeaterFields = ({
sourceId,
sourceName,
updateFn = () => {
},
updateCallbackFn = () => {
},
defaultValue = [],
shouldDisable = false
}) => {
const [chosen, setChosen] = useState('');
const {
control,
handleSubmit,
formState: { errors },
setValue,
register,
trigger,
getValues,
watch
} = useForm({
defaultValues: useMemo(() => {
return {
items: defaultValue || []
};
}, [defaultValue]), mode: 'onChange'
});
const { fields, append, remove } = useFieldArray({
control,
name: 'items'
});
const watchFields = watch('items');
const onSubmit = () => {
}
const doUpdateAfterFileUploaded = () => {
const formData = getValues();
updateFn(formData.items);
updateCallbackFn(formData.items);
}
const addNew = () => {
const uid = uniqid('f');
const newItem = {
fieldId: uid,
nameValue: '',
fileValue: []
}
append(newItem);
setChosen(newItem.fieldId);
trigger();
};
const setNewChosen = useCallback((id) => {
const chosenObj = head(fields.filter(o => id === o.fieldId));
if (chosenObj) {
setChosen(chosen === id ? '' : id);
}
}, [fields, chosen]);
const removeItem = useCallback((index) => {
const chosenObj = klona(fields[index]);
remove(index);
if (chosen === chosenObj.fieldId) {
setChosen('');
}
const formData = getValues();
updateFn(formData.items);
updateCallbackFn(formData.items);
}, [fields, chosen]);
return (
<div className="fieldsRepeater">
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
{watchFields
? watchFields.map((o, index) => <div key={o.fieldId}
className="fieldsRepeater__panel p-panel p-component">
<div className="fieldsRepeater__heading p-panel p-panel-header">
<span onClick={() => setNewChosen(o.fieldId)}>{o.nameValue}</span>
<Button icon="pi pi-times"
disabled={shouldDisable}
outlined
severity="danger"
className="actionBtn"
type="button"
aria-label={__('Cancella', 'gepafin')}
onClick={() => removeItem(index)}/>
</div>
<div className="fieldsRepeater__fields p-panel-content" data-hide={chosen !== o.fieldId}>
<FormField
type="textinput"
disabled={shouldDisable}
fieldName={`items.${index}.nameValue`}
label={__('Titolo del file', 'gepafin')}
control={control}
errors={errors}
defaultValue={o.nameValue}
config={{ required: __('È obbligatorio', 'gepafin') }}
/>
<FormField
type="fileupload"
disabled={isEmpty(o.nameValue) || shouldDisable}
setDataFn={setValue}
saveFormCallback={doUpdateAfterFileUploaded}
fieldName={`items.${index}.fileValue`}
label={__('File', 'gepafin')}
control={control}
register={register}
errors={errors}
defaultValue={o.fileValue ? o.fileValue : []}
accept={[]}
source={sourceName}
sourceId={sourceId}
multiple={false}
deleteOnBackend={false}
config={{ required: __('È obbligatorio', 'gepafin') }}
/>
</div>
</div>
) : null}
</form>
<Button
className="fieldsRepeater__addNew"
outlined
type="button"
disabled={(watchFields && watchFields.filter(o => isEmpty(o.nameValue) || isEmpty(o.fileValue)).length > 0) || shouldDisable}
onClick={addNew}
label={__('Aggiungi nuovo file', 'gepafin')}
/>
</div>
)
}
export default RepeaterFields;

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from 'react';
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { is, isEmpty, isNil, sum, pathOr, head } from 'ramda';
@@ -6,11 +6,12 @@ import { klona } from 'klona';
import { wrap } from 'object-path-immutable';
// store
import { storeSet, useStore } from '../../store';
import { storeGet, storeSet, useStore } from '../../store';
// api
import ApplicationEvaluationService from '../../service/application-evaluation-service';
import AmendmentsService from '../../service/amendments-service';
import AppointmentService from '../../service/appointment-service';
// tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
@@ -24,19 +25,18 @@ import { Tag } from 'primereact/tag';
import { Checkbox } from 'primereact/checkbox';
import { Editor } from 'primereact/editor';
import { InputNumber } from 'primereact/inputnumber';
import BlockingOverlay from '../../components/BlockingOverlay';
import { Toast } from 'primereact/toast';
import HelpIcon from '../../icons/HelpIcon';
import { Dialog } from 'primereact/dialog';
import ArchiveDocument from './components/ArchiveDocument';
import HelpIcon from '../../icons/HelpIcon';
import BlockingOverlay from '../../components/BlockingOverlay';
import { classNames } from 'primereact/utils';
import { InputTextarea } from 'primereact/inputtextarea';
import { InputText } from 'primereact/inputtext';
import DownloadApplicationArchive from './components/DownloadApplicationArchive';
import DownloadCompanyDelegation from './components/DownloadCompanyDelegation';
import DownloadSignedApplication from './components/DownloadSignedApplication';
import AppointmentService from '../../service/appointment-service';
import ListOfFiles from './components/ListOfFiles';
import RepeaterFields from './components/RepeaterFields';
const APP_EVALUATION_FLOW_ID = process.env.REACT_APP_EVALUATION_FLOW_ID;
@@ -71,12 +71,24 @@ const DomandaEditPreInstructor = () => {
}
const updateFlagsForSoccorso = (data) => {
let nonRatedFilesLength = 0;
if (data.files) {
const nonRatedFiles = data.files
.map(el => el.valid)
.filter(v => isNil(v));
setAllFilesRated(nonRatedFiles.length === 0);
nonRatedFilesLength = nonRatedFiles.length;
}
if (data.amendmentDetails) {
const nonRatedFiles = data.amendmentDetails
.map(el => el.valid)
.filter(v => isNil(v));
nonRatedFilesLength = nonRatedFiles.length;
}
setAllFilesRated(nonRatedFilesLength === 0);
if (data.checklist) {
const checkedChecklistItems = data.checklist
.map(el => el.valid)
@@ -88,7 +100,7 @@ const DomandaEditPreInstructor = () => {
const doNewSoccorso = () => {
if (connectedSoccorsoId !== 0) {
doSaveDraft(`/domande/${id}/soccorso/${connectedSoccorsoId}`)
navigate(`/domande/${id}/soccorso/${connectedSoccorsoId}`);
} else {
doSaveDraft(`/domande/${id}/aggiungi-soccorso/`)
}
@@ -153,11 +165,17 @@ const DomandaEditPreInstructor = () => {
updateFlagsForSoccorso(newData);
}
const doSaveDraft = (doRedirect = '') => {
const doSaveDraft = useCallback((doRedirect = '') => {
const formData = {
criteria: klona(data.criteria),
checklist: klona(data.checklist),
files: klona(data.files),
evaluationDocument: klona(data.evaluationDocument.map(o => ({
...o,
fileValue: o.fileValue[0] ? o.fileValue[0].id : ''
})
)),
amendmentDetails: klona(data.amendmentDetails),
note: data.note
}
@@ -167,7 +185,7 @@ const DomandaEditPreInstructor = () => {
(data) => updateCallback(data, doRedirect),
errUpdateCallback
);
}
}, [data]);
const updateCallback = (data, doRedirect = '') => {
if (data.status === 'SUCCESS') {
@@ -208,7 +226,7 @@ const DomandaEditPreInstructor = () => {
motivation
}
setLoading(true);
setIsVisibleCompleteDialog(false);
ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateStatusCallback, errUpdateStatusCallback);
}
@@ -222,12 +240,13 @@ const DomandaEditPreInstructor = () => {
motivation
}
setLoading(true);
setIsVisibleCompleteDialog(false);
ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateStatusCallback, errUpdateStatusCallback);
}
const updateStatusCallback = (data) => {
if (data.status === 'SUCCESS') {
setData(getFormattedData(data.data));
if (toast.current) {
toast.current.show({
severity: 'success',
@@ -236,7 +255,7 @@ const DomandaEditPreInstructor = () => {
});
}
}
setLoading(false);
storeSet.main.unsetAsyncRequest();
}
const errUpdateStatusCallback = (data) => {
@@ -248,7 +267,7 @@ const DomandaEditPreInstructor = () => {
});
}
set404FromErrorResponse(data);
setLoading(false);
storeSet.main.unsetAsyncRequest();
}
const displayCriterionData = (id) => {
@@ -376,7 +395,13 @@ const DomandaEditPreInstructor = () => {
const getNdgCallback = (data) => {
if (data.status === 'SUCCESS') {
console.log('data', data.data);
if (toast.current && data.message) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
}
storeSet.main.unsetAsyncRequest();
}
@@ -384,7 +409,7 @@ const DomandaEditPreInstructor = () => {
const errGetNdgCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: 'error',
severity: data.status === 'SUCCESS' ? 'info' : 'error',
summary: '',
detail: data.message
});
@@ -432,15 +457,56 @@ const DomandaEditPreInstructor = () => {
!isEmpty(appointmentData.title) && !isEmpty(appointmentData.text) && !isEmpty(appointmentData.amount)
&& !isEmpty(appointmentData.duration) && appointmentData.duration !== 0 && appointmentData.amount !== 0
) {
console.log(appointmentData);
//setLoading(true);
storeSet.main.setAsyncRequest();
const submitData = {
'importoBreveTermine': appointmentData.amount,
'durataMesiFinanziamento': appointmentData.duration,
'nota': {
'titolo': appointmentData.title,
'testo': appointmentData.text
}
}
AppointmentService.createAppointment(id, submitData, getAppointemntCallback, errGetAppointemntCallback);
}
}
const getAppointemntCallback = (data) => {
if (data.status === 'SUCCESS') {
if (toast.current && data.message) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
}
setIsVisibleAppointmentDialog(false);
storeSet.main.unsetAsyncRequest();
}
const errGetAppointemntCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: data.status === 'SUCCESS' ? 'info' : 'error',
summary: '',
detail: data.message
});
}
setIsVisibleAppointmentDialog(false);
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
const doMakeAdmisible = () => {
// TODO
}
const evaluationShouldBeBlocked = (data = {}) => {
const userData = storeGet.main.userData()
return isAsyncRequest || userData.id !== data.assignedUserId;
}
useEffect(() => {
const maxScore = pathOr(0, ['minScore'], data);
const criteria = pathOr([], ['criteria'], data);
@@ -507,11 +573,11 @@ const DomandaEditPreInstructor = () => {
<span>{data.callName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Beneficiario', 'gepafin')}</span>
<span>{__('Referente Aziendale', 'gepafin')}</span>
<span>{data.beneficiary}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Azienda', 'gepafin')}</span>
<span>{__('Azienda Beneficiaria', 'gepafin')}</span>
<span>{data.companyName}</span>
</p>
<p className="appPageSection__pMeta">
@@ -541,17 +607,29 @@ const DomandaEditPreInstructor = () => {
</div>
</div>
<div className="appPageSection">
<h2>{__('Documenti aggiuntivi', 'gepafin')}</h2>
<RepeaterFields
defaultValue={data.evaluationDocument ?? []}
updateFn={(data) => updateEvaluationValue(
data,
['evaluationDocument']
)}
shouldDisable={['APPROVED', 'REJECTED'].includes(data.applicationStatus) || evaluationShouldBeBlocked(data)}
sourceId={data.assignedApplicationId}
sourceName="evaluation"/>
</div>
<div className="appPageSection">
<h2>{__('Checklist Valutazione', 'gepafin')}</h2>
<div className="appPageSection columns">
<div>
<h3>{__('Lista', 'gepafin')}</h3>
<div className="appPageSection__withBorder grey" style={{ marginBottom: '20px' }}>
<div className="appPageSection__checklist">
{data.checklist.map((o, i) => <div key={o.id}>
<Checkbox
disabled={shouldDisableField('checklist')}
disabled={shouldDisableField('checklist') || evaluationShouldBeBlocked(data)}
inputId={`checklist_${o.id}`}
onChange={(e) => updateEvaluationValue(
e.checked,
@@ -567,7 +645,7 @@ const DomandaEditPreInstructor = () => {
<div>
<Editor
value={data.note}
readOnly={shouldDisableField('note')}
readOnly={shouldDisableField('note') || evaluationShouldBeBlocked(data)}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateEvaluationValue(
@@ -583,7 +661,7 @@ const DomandaEditPreInstructor = () => {
<ListOfFiles
files={data.files}
updateFn={updateEvaluationValue}
shouldDisableFieldFn={shouldDisableField}
shouldDisableFieldFn={(name) => shouldDisableField(name) || evaluationShouldBeBlocked(data)}
name="files"
ndg={data.ndg}
applicationId={id}/>
@@ -591,6 +669,18 @@ const DomandaEditPreInstructor = () => {
</div>
</div>
{!isEmpty(data.amendmentDetails)
? <div className="appPageSection">
<h2>{__('Documenti di soccorso', 'gepafin')}</h2>
<ListOfFiles
files={data.amendmentDetails}
updateFn={updateEvaluationValue}
shouldDisableFieldFn={(name) => shouldDisableField(name) || evaluationShouldBeBlocked(data)}
name="amendmentDetails"
ndg={data.ndg}
applicationId={id}/>
</div> : null}
<div className="appPageSection">
<h2>{__('Punteggi di valutazione', 'gepafin')}</h2>
{data.criteria
@@ -608,7 +698,7 @@ const DomandaEditPreInstructor = () => {
<td>
<div className="p-inputgroup">
<InputNumber
disabled={shouldDisableField('criteria')}
disabled={shouldDisableField('criteria') || evaluationShouldBeBlocked(data)}
placeholder={__('Punteggio', 'gepafin')}
keyfilter="int"
value={o.score}
@@ -631,7 +721,7 @@ const DomandaEditPreInstructor = () => {
onClick={() => displayCriterionData(o.id)}
aria-label={__('Mostra', 'gepafin')}/> : null}
<Button icon="pi pi-thumbs-up" rounded outlined
disabled={shouldDisableField('criteria')}
disabled={shouldDisableField('criteria') || evaluationShouldBeBlocked(data)}
severity={!isNil(o.valid) && o.valid ? 'success' : 'secondary'}
onClick={() => updateEvaluationValue(
true,
@@ -639,7 +729,7 @@ const DomandaEditPreInstructor = () => {
)}
aria-label={__('Su', 'gepafin')}/>
<Button icon="pi pi-thumbs-down" rounded outlined
disabled={shouldDisableField('criteria')}
disabled={shouldDisableField('criteria') || evaluationShouldBeBlocked(data)}
severity={!isNil(o.valid) && !o.valid ? 'danger' : 'secondary'}
onClick={() => updateEvaluationValue(
false,
@@ -681,7 +771,8 @@ const DomandaEditPreInstructor = () => {
{['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus)
? <Button
type="button"
disabled={!data.id || data.status === 'CLOSE' || !allFilesRated || !atLeastOneChecked}
disabled={!data.id || data.status === 'CLOSE' || (data.applicationStatus === 'EVALUATION'
&& (!allFilesRated || !atLeastOneChecked)) || evaluationShouldBeBlocked(data)}
onClick={doNewSoccorso}
outlined
label={<>
@@ -696,7 +787,7 @@ const DomandaEditPreInstructor = () => {
{data.id
? <Button
type="button"
disabled={data.status === 'CLOSE'}
disabled={data.status === 'CLOSE' || evaluationShouldBeBlocked(data)}
onClick={() => doSaveDraft()}
outlined
label={__('Salva bozza valutazione', 'gepafin')}
@@ -706,41 +797,72 @@ const DomandaEditPreInstructor = () => {
onClick={() => doSaveDraft()}
label={__('Crea valutazione', 'gepafin')}
icon="pi pi-save" iconPos="right"/>}
{APP_EVALUATION_FLOW_ID === '1' && ['EVALUATION'].includes(data.applicationStatus)
{/*{APP_EVALUATION_FLOW_ID === '1' && ['EVALUATION'].includes(data.applicationStatus)
? <Button
type="button"
disabled={!data.id || !allFilesRated || !allChecksChecked}
disabled={!data.id || !allFilesRated || !allChecksChecked || evaluationShouldBeBlocked(data)}
onClick={doCheckNDG}
label={__('Controlla NDG', 'gepafin')}
/> : null}
{APP_EVALUATION_FLOW_ID === '1' && ['EVALUATION'].includes(data.applicationStatus) && data.ndg
/> : null}*/}
<Button
type="button"
disabled={true}
onClick={() => {
}}
label={__('Controlla NDG', 'gepafin')}
/>
{/*{APP_EVALUATION_FLOW_ID === '1' && ['NDG'].includes(data.applicationStatus) && data.ndg
? <Button
type="button"
disabled={!data.id}
disabled={!data.id || evaluationShouldBeBlocked(data)}
onClick={doCreateAppointment}
label={__('Crea l\'appuntamento', 'gepafin')}
/> : null}
{APP_EVALUATION_FLOW_ID === '1' && ['APPOINTMENT'].includes(data.applicationStatus)
/> : null}*/}
<Button
type="button"
disabled={true}
onClick={() => {
}}
label={__('Crea l\'appuntamento', 'gepafin')}
/>
{/*{APP_EVALUATION_FLOW_ID === '1' && ['APPOINTMENT'].includes(data.applicationStatus)
? <Button
type="button"
disabled={!data.id}
disabled={!data.id || evaluationShouldBeBlocked(data)}
onClick={doMakeAdmisible}
label={__('Ammissibile', 'gepafin')}
/> : null}
/> : null}*/}
<Button
type="button"
disabled={true}
onClick={() => {
}}
label={__('Ammissibile', 'gepafin')}
/>
{data.id
? <Button
type="button"
disabled={!isAdmissible
|| (APP_EVALUATION_FLOW_ID === '1' && !['ADMISSIBLE', 'APPOINTMENT'].includes(data.applicationStatus))}
disabled={!isAdmissible || ['APPROVED'].includes(data.applicationStatus) || evaluationShouldBeBlocked(data)}
/*disabled={!isAdmissible
|| (APP_EVALUATION_FLOW_ID === '1' && !['ADMISSIBLE', 'APPOINTMENT'].includes(data.applicationStatus))}*/
onClick={initiateApproving}
label={__('Approva Domanda', 'gepafin')}
icon="pi pi-check" iconPos="right"/> : null}
{data.id
{/*{data.id
? <Button
type="button"
disabled={APP_EVALUATION_FLOW_ID === '1' && !['ADMISSIBLE', 'APPOINTMENT'].includes(data.applicationStatus)}
onClick={initiateRejecting}
label={__('Respingi Domanda', 'gepafin')}
icon="pi pi-times" iconPos="right"/> : null}*/}
{data.id
? <Button
type="button"
disabled={APP_EVALUATION_FLOW_ID === '1'
&& !['EVALUATION', 'ADMISSIBLE', 'APPOINTMENT'].includes(data.applicationStatus)
|| evaluationShouldBeBlocked(data)}
onClick={initiateRejecting}
label={__('Respingi Domanda', 'gepafin')}
icon="pi pi-times" iconPos="right"/> : null}
</div>
</div>
@@ -840,4 +962,4 @@ const DomandaEditPreInstructor = () => {
}
export default DomandaEditPreInstructor;
export default DomandaEditPreInstructor;

View File

@@ -1,11 +1,18 @@
import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n';
import { is, uniq } from 'ramda';
import { Link } from 'react-router-dom';
import { Link, useLocation } from 'react-router-dom';
// api
import ApplicationService from '../../../../service/application-service';
// tools
import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
// translation
import translationStrings from '../../../../translationStringsForComponents';
// components
import { FilterMatchMode, FilterOperator } from 'primereact/api';
import { DataTable } from 'primereact/datatable';
@@ -13,14 +20,16 @@ import { Column } from 'primereact/column';
import { Button } from 'primereact/button';
import { Calendar } from 'primereact/calendar';
import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import translationStrings from '../../../../translationStringsForComponents';
import { Dropdown } from 'primereact/dropdown';
import { Tag } from 'primereact/tag';
const AllDomandeTable = ({ openDialogFn, updaterString = '' }) => {
const [items, setItems] = useState(null);
const [filters, setFilters] = useState(null);
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [, setStatuses] = useState([]);
const [statuses, setStatuses] = useState([]);
const location = useLocation();
useEffect(() => {
setLocalAsyncRequest(true);
@@ -81,7 +90,8 @@ const AllDomandeTable = ({ openDialogFn, updaterString = '' }) => {
callEndDate: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
}
},
status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] },
});
};
@@ -98,9 +108,12 @@ const AllDomandeTable = ({ openDialogFn, updaterString = '' }) => {
return formatDate(rowData.submissionDate);
};
/*const dateEndBodyTemplate = (rowData) => {
return formatDate(rowData.callEndDate);
};*/
const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses}
onChange={(e) => options.filterCallback(e.value, options.index)}
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter"
showClear/>;
};
const dateFilterTemplate = (options) => {
return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)}
@@ -111,17 +124,22 @@ const AllDomandeTable = ({ openDialogFn, updaterString = '' }) => {
return <ProperBandoLabel status={rowData.status}/>;
};
const statusItemTemplate = (option) => {
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)}/>;
};
const actionsBodyTemplate = (rowData) => {
return <div className="appPageSection__tableActions lessGap">
{openDialogFn
{openDialogFn && ['SUBMIT', 'EVALUATION', 'SOCCORSO'].includes(rowData.status)
? <Button severity="info"
onClick={() => openDialogFn(rowData.id)}
label={__('Assegnare', 'gepafin')}
icon="pi pi-pencil" size="small" iconPos="right"/>
: <Link to={'/domande'}>
<Button severity="info" label={__('Gestire', 'gepafin')} size="small"/>
</Link>}
<Link to={`/domande/${rowData.id}`}>
: location.pathname !== '/domande'
? <Link to={'/domande'}>
<Button severity="info" label={__('Gestire', 'gepafin')} size="small"/>
</Link> : null}
<Link to={`/domande/${rowData.id}/preview`}>
<Button severity="info" label={__('Anteprima', 'gepafin')} icon="pi pi-eye" size="small"
iconPos="right"/>
</Link>
@@ -147,7 +165,7 @@ const AllDomandeTable = ({ openDialogFn, updaterString = '' }) => {
filter sortable
filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '10rem' }}/>
<Column field="companyName" header={__('Azienda', 'gepafin')}
<Column field="companyName" header={__('Azienda Beneficiaria', 'gepafin')}
filter sortable
filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '8rem' }}/>
@@ -155,12 +173,14 @@ const AllDomandeTable = ({ openDialogFn, updaterString = '' }) => {
filterField="submissionDate" dataType="date"
style={{ minWidth: '8rem' }}
body={dateAppliedBodyTemplate} filter filterElement={dateFilterTemplate}/>
{/*<Column header={__('Scadenza', 'gepafin')}
filterField="callEndDate" dataType="date"
style={{ minWidth: '8rem' }}
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>*/}
<Column field="assignedUserName" header={__('Assegnato', 'gepafin')}
filter sortable
filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column field="status" header={__('Stato', 'gepafin')}
style={{ minWidth: '8rem' }} body={statusBodyTemplate}/>
style={{ minWidth: '8rem' }} body={statusBodyTemplate}
filter
filterElement={statusFilterTemplate}/>
<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/>
</DataTable>
@@ -168,4 +188,4 @@ const AllDomandeTable = ({ openDialogFn, updaterString = '' }) => {
)
}
export default AllDomandeTable;
export default AllDomandeTable;

View File

@@ -24,7 +24,7 @@ import DraftApplicationsTable from '../Dashboard/components/DraftApplicationsTab
const Domande = () => {
const [loading, setLoading] = useState(false);
const [isVisibleEditDialog, setIsVisibleEditDialog] = useState(false);
const [roleId, setRoleId] = useState(0);
const [roleIds, setRoleIds] = useState([]);
const [users, setUsers] = useState([]);
const [chosenUser, setChosenUser] = useState(0);
const [chosenApplication, setChosenApplication] = useState(0);
@@ -34,10 +34,8 @@ const Domande = () => {
const getRolesCallback = (data) => {
if (data.status === 'SUCCESS') {
const roles = data.data
.filter(o => ['ROLE_PRE_INSTRUCTOR'].includes(o.roleType));
if (roles.length) {
setRoleId(roles[0].id);
}
.filter(o => ['ROLE_PRE_INSTRUCTOR', 'ROLE_INSTRUCTOR_MANAGER'].includes(o.roleType));
setRoleIds(roles.map(o => o.id));
}
setLoading(false);
}
@@ -65,7 +63,7 @@ const Domande = () => {
}
const headerEditDialog = () => {
return <span>{__('Assign application', 'gepafin')}</span>
return <span>{__('Assegni la domanda', 'gepafin')}</span>
}
const hideEditDialog = () => {
@@ -126,11 +124,11 @@ const Domande = () => {
}
useEffect(() => {
if (roleId !== 0) {
if (roleIds.length > 0) {
setLoading(true);
UserService.getUsers(getUsersCallback, errGetUsersCallback, [['roleId', roleId]])
UserService.getUsers(getUsersCallback, errGetUsersCallback, [['roleIds', roleIds]])
}
}, [roleId]);
}, [roleIds]);
useEffect(() => {
if (isVisibleEditDialog) {

View File

@@ -33,7 +33,7 @@ const BeneficiarioDomandeTable = () => {
const [statuses, setStatuses] = useState([]);
useEffect(() => {
if (!isEmpty(chosenCompanyId) && chosenCompanyId !== 0) {
if (!isEmpty(chosenCompanyId) && chosenCompanyId !== 0 && !localAsyncRequest) {
setLocalAsyncRequest(true);
ApplicationService.getApplications(getApplCallback, errGetApplCallback, [
['companyId', chosenCompanyId],
@@ -126,7 +126,8 @@ const BeneficiarioDomandeTable = () => {
const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses}
onChange={(e) => options.filterCallback(e.value, options.index)}
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter"
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel}
className="p-column-filter"
showClear/>;
};
@@ -139,11 +140,17 @@ const BeneficiarioDomandeTable = () => {
};
const actionsBodyTemplate = (rowData) => {
return rowData.status === 'SOCCORSO'
? <Link to={`/domande/${rowData.id}`}>
<Button severity="info" label={__('Dettagli', 'gepafin')} icon="pi pi-eye" size="small"
return <>
{rowData.status === 'SOCCORSO'
? <Link to={`/domande/${rowData.id}`}>
<Button severity="info" label={__('Dettagli', 'gepafin')} icon="pi pi-eye" size="small"
iconPos="right"/>
</Link> : null}
<Link to={`/domande/${rowData.id}/preview`}>
<Button severity="info" label={__('Anteprima', 'gepafin')} icon="pi pi-eye" size="small"
iconPos="right"/>
</Link> : null;
</Link>
</>
}
const header = renderHeader();
@@ -165,7 +172,7 @@ const BeneficiarioDomandeTable = () => {
filter sortable
filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column field="companyName" header={__('Azienda', 'gepafin')}
<Column field="companyName" header={__('Azienda Beneficiaria', 'gepafin')}
filter sortable
filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '8rem' }}/>
@@ -184,4 +191,4 @@ const BeneficiarioDomandeTable = () => {
)
}
export default BeneficiarioDomandeTable;
export default BeneficiarioDomandeTable;

View File

@@ -1,14 +1,23 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
import { head } from 'ramda';
// store
import { useStore } from '../../store';
// components
import BeneficiarioDomandeTable from './components/BeneficiarioDomandeTable';
const DomandePreInstructor = () => {
const chosenCompanyId = useStore().main.chosenCompanyId();
const companies = useStore().main.companies();
const company = head(companies.filter(o => o.id === chosenCompanyId));
return(
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Archivio domande', 'gepafin')}</h1>
{company ? <span className="companyName">{company.companyName}</span> : null}
</div>
<div className="appPage__spacer"></div>

View File

@@ -0,0 +1,184 @@
import React, { useEffect, useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
import { isEmpty } from 'ramda';
// store
import { storeSet } from '../../store';
// tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import uniqid from '../../helpers/uniqid';
// api
import AssignedApplicationService from '../../service/assigned-application-service';
import UserService from '../../service/user-service';
// components
import PreInstructorDomandeTable from '../DashboardPreInstructor/components/PreInstructorDomandeTable';
import AllDomandeTable from '../Domande/components/AllDomandeTable';
import { classNames } from 'primereact/utils';
import { Dropdown } from 'primereact/dropdown';
import { Dialog } from 'primereact/dialog';
import { Button } from 'primereact/button';
const DomandeInstructorManager = () => {
const [loading, setLoading] = useState(false);
const [isVisibleEditDialog, setIsVisibleEditDialog] = useState(false);
const [roleIds, setRoleIds] = useState(0);
const [users, setUsers] = useState([]);
const [chosenUser, setChosenUser] = useState(0);
const [chosenApplication, setChosenApplication] = useState(0);
const [updaterString, setUpdaterString] = useState('');
const toast = useRef(null);
const getRolesCallback = (data) => {
if (data.status === 'SUCCESS') {
const roles = data.data
.filter(o => ['ROLE_PRE_INSTRUCTOR', 'ROLE_INSTRUCTOR_MANAGER'].includes(o.roleType));
setRoleIds(roles.map(o => o.id));
}
setLoading(false);
}
const errGetRolesCallback = (data) => {
set404FromErrorResponse(data);
setLoading(false);
}
const getUsersCallback = (data) => {
if (data.status === 'SUCCESS') {
const users = data.data
.map(o => ({
name: `${o.firstName} ${o.lastName} (${o.email})`,
value: o.id
}));
setUsers(users);
}
setLoading(false);
}
const errGetUsersCallback = (data) => {
set404FromErrorResponse(data);
setLoading(false);
}
const headerEditDialog = () => {
return <span>{__('Assegni la domanda', 'gepafin')}</span>
}
const hideEditDialog = () => {
setIsVisibleEditDialog(false);
setChosenUser(0);
setChosenApplication(0);
}
const footerEditDialog = () => {
return <div>
<Button type="button" label={__('Anulla', 'gepafin')} onClick={hideEditDialog} outlined/>
<Button
type="button"
disabled={loading}
label={__('Invia', 'gepafin')} onClick={saveEditDialog}/>
</div>
}
const openAssignDialog = (applId) => {
setChosenApplication(applId)
setIsVisibleEditDialog(true);
}
const saveEditDialog = () => {
if (chosenUser !== 0 && chosenApplication !== 0) {
storeSet.main.setAsyncRequest();
AssignedApplicationService.assignApplication(chosenApplication, assignApplCallback, errAssignApplCallback, [
['userId', chosenUser]
]);
hideEditDialog();
}
}
const assignApplCallback = (data) => {
if (data.status === 'SUCCESS') {
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
setUpdaterString(uniqid());
}
storeSet.main.unsetAsyncRequest();
}
const errAssignApplCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: 'error',
summary: '',
detail: data.message
});
}
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
useEffect(() => {
if (roleIds.length > 0) {
setLoading(true);
UserService.getUsers(getUsersCallback, errGetUsersCallback, [['roleIds', roleIds]])
}
}, [roleIds]);
useEffect(() => {
if (isVisibleEditDialog) {
setLoading(true);
UserService.getRoles(getRolesCallback, errGetRolesCallback)
}
}, [isVisibleEditDialog]);
return(
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Domande da valutare', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<PreInstructorDomandeTable/>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<h2>{__('Domande pubblicate', 'gepafin')}</h2>
<AllDomandeTable openDialogFn={openAssignDialog} updaterString={updaterString}/>
</div>
<Dialog
visible={isVisibleEditDialog}
modal
header={headerEditDialog}
footer={footerEditDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideEditDialog}>
<div className="appForm__field">
<label
className={classNames({ 'p-error': isEmpty(chosenUser) || chosenUser === 0 || chosenApplication === 0 })}>
{__('Istruttore', 'gepafin')}*
</label>
<Dropdown
value={chosenUser}
invalid={isEmpty(chosenUser) || chosenUser === 0 || chosenApplication === 0}
onChange={(e) => setChosenUser(e.value)}
options={users}
optionLabel="name"
optionValue="value"/>
</div>
</Dialog>
</div>
)
}
export default DomandeInstructorManager;

View File

@@ -8,7 +8,7 @@ const DomandePreInstructor = () => {
return(
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Archivio domande', 'gepafin')}</h1>
<h1>{__('Domande da valutare', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>

View File

@@ -185,7 +185,7 @@ const SoccorsoAddPreInstructor = () => {
<span>{data.callName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Beneficiario', 'gepafin')}</span>
<span>{__('Referente Aziendale', 'gepafin')}</span>
<span>{data.beneficiaryName}</span>
</p>
</div>
@@ -193,7 +193,7 @@ const SoccorsoAddPreInstructor = () => {
<div className="appPageSection">
<div className="appPageSection columns">
<div>
<h3>{__('Note', 'gepafin')}</h3>
<h3>{__('Pec/Email', 'gepafin')}</h3>
<div style={{marginBottom: '30px'}}>
<Editor
value={formData.note}
@@ -281,6 +281,7 @@ const SoccorsoAddPreInstructor = () => {
<Button
type="button"
outlined
onClick={goToEvaluationPage}
label={__('Anulla', 'gepafin')}
icon="pi pi-times" iconPos="right"/>
<Button
@@ -307,4 +308,4 @@ const SoccorsoAddPreInstructor = () => {
}
export default SoccorsoAddPreInstructor;
export default SoccorsoAddPreInstructor;

View File

@@ -4,6 +4,7 @@ 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';
@@ -16,7 +17,6 @@ import ApplicationService from '../../service/application-service';
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';
@@ -25,8 +25,10 @@ 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';
import getEmailTemplateForSoccorso from '../../helpers/getStrippedHtmlBodyTags';
const DomandaBeneficiario = () => {
const SoccorsoEditBeneficiario = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const { id } = useParams();
const navigate = useNavigate();
@@ -67,12 +69,16 @@ const DomandaBeneficiario = () => {
if (data.data.length) {
const amendmentObj = data.data[0];
setData(getFormattedData(amendmentObj));
const formDataInitial = amendmentObj.applicationFormFields.reduce((acc, cur) => {
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 {
@@ -101,7 +107,7 @@ const DomandaBeneficiario = () => {
}
const getFormattedData = (data) => {
data.submissionDate = is(String, data.submissionDate) ? new Date(data.submissionDate) : (data.submissionDate ? data.submissionDate : '');
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;
@@ -110,10 +116,11 @@ const DomandaBeneficiario = () => {
const onSubmit = () => {
};
const doUpdateAmendment = () => {
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];
@@ -126,32 +133,59 @@ const DomandaBeneficiario = () => {
});
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, updateAmendmentCallback, errUpdateAmendmentCallback);
AmendmentsService.updateSoccorso(
amendmentId,
submitData,
(resp) => updateAmendmentCallback(resp, doClose),
errUpdateAmendmentCallback
);
}
const updateAmendmentCallback = (data) => {
const doUpdateAmendmentAndCompleteTask = () => {
doUpdateAmendment(true);
}
const updateAmendmentCallback = (data, doClose = false) => {
if (data.status === 'SUCCESS') {
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
const newFormDataInitial = data.data.applicationFormFields.reduce((acc, cur) => {
if (cur.fieldValue) {
acc[cur.fieldId] = cur.fieldValue;
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
});
}
return acc;
}, formInitialData);
setFormInitialData(newFormDataInitial);
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();
}
@@ -168,6 +202,31 @@ const DomandaBeneficiario = () => {
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();
@@ -182,7 +241,7 @@ const DomandaBeneficiario = () => {
{data.id
? <h1>
{sprintf(__('Soccorso Istruttorio: richiesta integrazione documenti per domanda #%s', 'gepafin'), id)}
</h1> : null}
</h1> : null}
{dataAppl.id
? <h1>
{sprintf(__('Dettagli: domanda #%s', 'gepafin'), dataAppl.id)}
@@ -216,11 +275,11 @@ const DomandaBeneficiario = () => {
<span>{data.callName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Beneficiario', 'gepafin')}</span>
<span>{__('Referente Aziendale', 'gepafin')}</span>
<span>{data.beneficiaryName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Azienda', 'gepafin')}</span>
<span>{__('Azienda Beneficiaria', 'gepafin')}</span>
<span></span>
</p>
<p className="appPageSection__pMeta">
@@ -239,7 +298,7 @@ const DomandaBeneficiario = () => {
{dataAppl.id
? <div className="appPageSection__withBorder columns">
<p className="appPageSection__pMeta">
<span>{__('ID domanda', 'gepafin')}</span>
<span>{__('ID domanda', 'gepafin')}</span>
<span>{dataAppl.id}</span>
</p>
<p className="appPageSection__pMeta">
@@ -247,7 +306,7 @@ const DomandaBeneficiario = () => {
<span>{dataAppl.callTitle}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Azienda', 'gepafin')}</span>
<span>{__('Azienda Beneficiaria', 'gepafin')}</span>
<span>{dataAppl.companyName}</span>
</p>
<p className="appPageSection__pMeta">
@@ -268,10 +327,13 @@ const DomandaBeneficiario = () => {
? <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>{getEmailTemplateForSoccorso(data.emailTemplate, 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
@@ -282,7 +344,7 @@ const DomandaBeneficiario = () => {
? data.formFields.map((o, i) => {
return <FormField
key={o.fieldId}
disabled={data.status === 'CLOSE'}
disabled={data.status !== 'AWAITING'}
type="fileupload"
setDataFn={setValue}
saveFormCallback={doUpdateAmendment}
@@ -294,58 +356,100 @@ const DomandaBeneficiario = () => {
defaultValue={formInitialData[o.fieldId] ? formInitialData[o.fieldId] : []}
accept={[]}
source="AMENDMENT"
sourceId={data.applicationId}
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>*/}
<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>{__('Comunicazioni', 'gepafin')}</h2>
<SoccorsoComunications amendmentId={data.id} soccorsoStatus={data.status}/>
</div> : null}
<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 lintegrazione non può essere ritenuta valida.', 'gepafin')}</span>
</div> : null}
<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 lintegrazione non può essere ritenuta valida.', 'gepafin')}</span>
</div> : null}
<div className="appPageSection">
<div className="appPageSection__actions">
{data.id
? <Button
type="button"
disabled={isAsyncRequest}
onClick={() => setIsVisibleEmailDialog(true)}
label={__('Invia documenti via PEC', 'gepafin')}
icon="pi pi-envelope" iconPos="right"/> : null}
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}
disabled={isAsyncRequest}
label={__('Chiudi', 'gepafin')}
icon="pi pi-times" iconPos="right"/>
label={__('Indietro', 'gepafin')}
icon="pi pi-arrow-left" iconPos="left"/>
</div>
</div>
</div>
<Dialog
header={__('Invia documenti via PEC', 'gepafin')}
header={__('Invia documenti', 'gepafin')}
visible={isVisibleEmailDialog}
style={{ width: '50vw' }}
onHide={() => {if (!isVisibleEmailDialog) return; setIsVisibleEmailDialog(false); }}>
onHide={() => {
if (!isVisibleEmailDialog) return;
setIsVisibleEmailDialog(false);
}}>
<p className="m-0">
{data.callEmail}
</p>
@@ -355,4 +459,4 @@ const DomandaBeneficiario = () => {
}
export default DomandaBeneficiario;
export default SoccorsoEditBeneficiario;

View File

@@ -16,7 +16,7 @@ import AmendmentsService from '../../service/amendments-service';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import getBandoLabel from '../../helpers/getBandoLabel';
import getDateFromISOstring from '../../helpers/getDateFromISOstring';
import renderHtmlContent from '../../helpers/renderHtmlContent';
import getEmailTemplateForSoccorso from '../../helpers/getStrippedHtmlBodyTags';
// components
import { Button } from 'primereact/button';
@@ -35,10 +35,12 @@ const SoccorsoEditPreInstructor = () => {
const { id, amendmentId } = useParams();
const navigate = useNavigate();
const [data, setData] = useState({});
const [isVisibleCloseAmendDialog, setIsVisibleCloseAmendDialog] = useState(false);
const [isVisibleExtendTimeDialog, setIsVisibleExtendTimeDialog] = useState(false);
const [extendedTime, setExtendedTime] = useState(3);
const [isLoadingExtendingTime, setIsLoadingExtendingTime] = useState(false);
const [isLoadingReminding, setIsLoadingReminding] = useState(false);
const [internalNote, setInternalNote] = useState('');
const toast = useRef(null);
const [formInitialData, setFormInitialData] = useState({});
const {
@@ -62,12 +64,16 @@ const SoccorsoEditPreInstructor = () => {
const getCallback = (data) => {
if (data.status === 'SUCCESS') {
setData(getFormattedData(data.data));
const formDataInitial = data.data.applicationFormFields.reduce((acc, cur) => {
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();
@@ -112,17 +118,18 @@ const SoccorsoEditPreInstructor = () => {
const header = renderHeader();
const updateNewAmendmentData = (value, path) => {
const newData = wrap(data).set(path.split('.'), value).value();
const newData = wrap(data).set(path, value).value();
setData(newData);
}
const onSubmit = () => {
};
const doUpdateAmendment = () => {
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];
@@ -135,30 +142,55 @@ const SoccorsoEditPreInstructor = () => {
});
return acc;
}, []);
const newAmendDocs = formValues.amendmentDocuments
? formValues.amendmentDocuments.map(o => o.id).join(',')
: '';
const submitData = {
applicationFormFields: newFormValues
applicationFormFields: newFormValues,
amendmentDocuments: newAmendDocs,
amendmentNotes: data.amendmentNotes
}
storeSet.main.setAsyncRequest();
AmendmentsService.updateSoccorso(amendmentId, submitData, updateAmendmentCallback, errUpdateAmendmentCallback);
AmendmentsService.updateSoccorso(
amendmentId,
submitData,
(resp) => updateAmendmentCallback(resp, doClose),
errUpdateAmendmentCallback
);
}
const updateAmendmentCallback = (data) => {
const updateAmendmentCallback = (data, doClose = false) => {
if (data.status === 'SUCCESS') {
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
const newFormDataInitial = data.data.applicationFormFields.reduce((acc, cur) => {
if (cur.fieldValue) {
acc[cur.fieldId] = cur.fieldValue;
setData(getFormattedData(data.data));
console.log('internalNote', internalNote)
if (doClose) {
const submitData = {
internalNote
}
return acc;
}, formInitialData);
setFormInitialData(newFormDataInitial);
storeSet.main.setAsyncRequest();
AmendmentsService.closeSoccorso(amendmentId, submitData, closeAmendmentCallback, errCloseAmendmentCallback);
} 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;
}, formInitialData);
formDataInitial = {
...formDataInitial,
amendmentDocuments: data.data.amendmentDocuments
}
setFormInitialData(formDataInitial);
}
}
storeSet.main.unsetAsyncRequest();
}
@@ -175,12 +207,30 @@ const SoccorsoEditPreInstructor = () => {
storeSet.main.unsetAsyncRequest();
}
const openCloseAmendmentDialog = () => {
setIsVisibleCloseAmendDialog(true);
}
const headerCloseAmendDialog = () => {
return <span>{__('Chiudi Soccorso Istruttorio', 'gepafin')}</span>
}
const hideCloseAmendDialog = () => {
setIsVisibleCloseAmendDialog(false);
}
const footerCloseAmendDialog = () => {
return <div>
<Button type="button" label={__('Anulla', 'gepafin')} onClick={hideCloseAmendDialog} outlined/>
<Button
type="button"
disabled={isAsyncRequest || isEmpty(data.internalNotes)}
label={__('Invia', 'gepafin')} onClick={doCloseAmendment}/>
</div>
}
const doCloseAmendment = () => {
const submitData = {
internalNote: data.internalNote
}
storeSet.main.setAsyncRequest();
AmendmentsService.closeSoccorso(amendmentId, submitData, closeAmendmentCallback, errCloseAmendmentCallback);
doUpdateAmendment(true);
}
const closeAmendmentCallback = (data) => {
@@ -193,7 +243,8 @@ const SoccorsoEditPreInstructor = () => {
});
}
if (data.data.status) {
updateNewAmendmentData(data.data.status, 'status')
updateNewAmendmentData(data.data.status, ['status']);
setIsVisibleCloseAmendDialog(false);
}
}
storeSet.main.unsetAsyncRequest();
@@ -297,7 +348,6 @@ const SoccorsoEditPreInstructor = () => {
useEffect(() => {
if (formInitialData) {
//reset();
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
trigger();
}
@@ -342,7 +392,7 @@ const SoccorsoEditPreInstructor = () => {
<span>{data.callName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Beneficiario', 'gepafin')}</span>
<span>{__('Referente Aziendale', 'gepafin')}</span>
<span>{data.beneficiaryName}</span>
</p>
<p className="appPageSection__pMeta">
@@ -361,26 +411,19 @@ const SoccorsoEditPreInstructor = () => {
<div className="appPageSection">
<h2>{__('Dettagli Richiesta', 'gepafin')}</h2>
<div className="appPageSection columns">
<div>
<h3>{__('Documenti Richiesti', 'gepafin')}</h3>
<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>
<div>
<h3>{__('Note e spiegazioni', 'gepafin')}</h3>
<div className="appPageSection__withBorder grey ql-editor"
style={{ minHeight: '200px' }}>
{renderHtmlContent(data.note)}
</div>
</div>
</div>
<h3>{__('Note e spiegazioni', 'gepafin')}</h3>
<div
className="appPageSection__emailTemplate">{getEmailTemplateForSoccorso(data.emailTemplate, data.note)}</div>
</div>
<div className="appPageSection">
<h3>{__('Documenti Richiesti', 'gepafin')}</h3>
<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>
<div className="appPageSection">
@@ -388,15 +431,15 @@ const SoccorsoEditPreInstructor = () => {
<SoccorsoComunications amendmentId={amendmentId} soccorsoStatus={data.status}/>
</div>
<div className="appPageSection">
<h2>{__('Documenti Ricevuti', 'gepafin')}</h2>
{data.formFields && !isEmpty(data.formFields)
? <div className="appPageSection">
<h2>{__('Documenti Ricevuti', 'gepafin')}</h2>
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
{data.formFields
? data.formFields.map((o, i) => {
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
{data.formFields.map((o, i) => {
return <FormField
key={o.fieldId}
disabled={data.status === 'CLOSE'}
disabled={['CLOSE', 'AWAITING', 'EXPIRED'].includes(data.status)}
type="fileupload"
setDataFn={setValue}
saveFormCallback={doUpdateAmendment}
@@ -408,27 +451,46 @@ const SoccorsoEditPreInstructor = () => {
defaultValue={formInitialData[o.fieldId] ? formInitialData[o.fieldId] : []}
accept={[]}
source="AMENDMENT"
sourceId={data.applicationId}
sourceId={amendmentId}
multiple={true}
/>
}) : null}
</form>
</div>
})}
</form>
</div> : null}
<div className="appForm__field">
<label>{__('Motivazioni / Note Interne', 'gepafin')}</label>
<div style={{ position: 'relative' }}>
<BlockingOverlay shouldDisplay={data.status === 'CLOSE'}/>
<Editor
value={data.internalNote}
readOnly={data.status === 'CLOSE'}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateNewAmendmentData(
e.htmlValue,
'internalNote'
)}
style={{ height: 80 * 3, width: '100%' }}
<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={['CLOSE', 'AWAITING', 'EXPIRED'].includes(data.status)}/>
<Editor
value={data.amendmentNotes}
readOnly={['CLOSE', 'AWAITING', 'EXPIRED'].includes(data.status)}
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={['CLOSE', 'AWAITING', 'EXPIRED'].includes(data.status)}
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={amendmentId}
multiple={true}
/>
</div>
</div>
@@ -444,7 +506,7 @@ const SoccorsoEditPreInstructor = () => {
<Button
type="button"
onClick={sendReminder}
disabled={isLoadingReminding || data.status === 'CLOSE'}
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)}
outlined
label={__('Invia Sollecito', 'gepafin')}
icon="pi pi-send"
@@ -452,21 +514,21 @@ const SoccorsoEditPreInstructor = () => {
<Button
type="button"
onClick={openExtendResponseTimeDialog}
disabled={isLoadingExtendingTime || data.status === 'CLOSE'}
disabled={isLoadingExtendingTime || ['CLOSE', 'EXPIRED'].includes(data.status)}
outlined
label={__('Estendi Scadenza', 'gepafin')}
icon="pi pi-stopwatch"
/>
{/*<Button
type="button"
onClick={doUpdateAmendment}
disabled={isAsyncRequest || data.status === 'CLOSE'}
label={__('Salva bozza', 'gepafin')}
icon="pi pi-save" iconPos="right"/>*/}
<Button
type="button"
onClick={doCloseAmendment}
disabled={isAsyncRequest || data.status === 'CLOSE'}
onClick={() => doUpdateAmendment()}
disabled={isAsyncRequest || ['CLOSE', 'AWAITING', 'EXPIRED'].includes(data.status)}
label={__('Salva bozza', 'gepafin')}
icon="pi pi-save" iconPos="right"/>
<Button
type="button"
onClick={openCloseAmendmentDialog}
disabled={isAsyncRequest || ['CLOSE', 'EXPIRED'].includes(data.status)}
label={__('Chiudi Soccorso Istruttorio', 'gepafin')}
icon="pi pi-times" iconPos="right"/>
</div>
@@ -488,15 +550,38 @@ const SoccorsoEditPreInstructor = () => {
</label>
<InputNumber
keyfilter="int"
disabled={data.status === 'CLOSE'}
disabled={['CLOSE', 'EXPIRED'].includes(data.status)}
value={extendedTime}
showButtons
onChange={(e) => setExtendedTime(e.value)}/>
</div>
</Dialog>
<Dialog
visible={isVisibleCloseAmendDialog}
modal
header={headerCloseAmendDialog}
footer={footerCloseAmendDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideCloseAmendDialog}>
<div className="appForm__field">
<label>{__('Motivazioni', 'gepafin')}</label>
<div style={{ position: 'relative' }}>
<BlockingOverlay shouldDisplay={data.status === 'CLOSE'}/>
<Editor
value={internalNote}
readOnly={['CLOSE', 'EXPIRED'].includes(data.status)}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => setInternalNote(e.htmlValue)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</div>
</Dialog>
</div>
)
}
export default SoccorsoEditPreInstructor;
export default SoccorsoEditPreInstructor;

View File

@@ -78,7 +78,7 @@ const PreInstructorSoccorsiTable = ({ openDialogFn }) => {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }]
},
beneficiaryName: {
companyName: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }]
},
@@ -149,7 +149,7 @@ const PreInstructorSoccorsiTable = ({ openDialogFn }) => {
<Column field="callName" header={__('Bando', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column field="beneficiaryName" header={__('Beneficiario', 'gepafin')}
<Column field="companyName" header={__('Azienda Beneficiaria', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column header={__('Data Richiesta', 'gepafin')}
@@ -170,4 +170,4 @@ const PreInstructorSoccorsiTable = ({ openDialogFn }) => {
)
}
export default PreInstructorSoccorsiTable;
export default PreInstructorSoccorsiTable;

View File

@@ -0,0 +1,192 @@
import React, { useState, useEffect, useRef } from 'react';
import { __ } from '@wordpress/i18n';
import { isEmpty, pathOr } from 'ramda';
import { useNavigate, useParams } from 'react-router-dom';
import NumberFlow from '@number-flow/react';
// service
import UserService from '../../service/user-service';
// tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import getDateFromISOstring from '../../helpers/getDateFromISOstring';
// components
import { Button } from 'primereact/button';
import { Toast } from 'primereact/toast';
import { Dropdown } from 'primereact/dropdown';
const UserActivity = () => {
const [loading, setLoading] = useState(false);
const toast = useRef(null);
const navigate = useNavigate();
const { id } = useParams();
const [user, setUser] = useState({});
const [roles, setRoles] = useState([]);
const [chosenRole, setChosenRole] = useState(0);
const goBack = () => {
navigate(`/utenti`);
}
const getUserCallback = (resp) => {
if (resp.status === 'SUCCESS') {
setUser(resp.data)
setChosenRole(resp.data.role?.id);
}
setLoading(false);
}
const errGetUserCallback = (resp) => {
set404FromErrorResponse(resp);
setLoading(false);
}
const getRolesCallback = (resp) => {
if (resp.status === 'SUCCESS') {
setRoles(resp.data)
}
setLoading(false);
}
const errGetRolesCallback = (resp) => {
set404FromErrorResponse(resp);
setLoading(false);
}
const getStatValue = (key, fallback = 0) => {
return pathOr(fallback, [key], {});
}
const handleRoleUpdate = () => {
if (user.role?.id !== chosenRole) {
setLoading(true);
UserService.updateUser(user.id, {roleId: chosenRole}, updateRoleCallback, errUpdateRoleCallback)
}
}
const updateRoleCallback = (resp) => {
if (resp.status === 'SUCCESS') {
setUser(resp.data)
}
setLoading(false);
}
const errUpdateRoleCallback = (resp) => {
set404FromErrorResponse(resp);
setLoading(false);
}
useEffect(() => {
if (id && !isEmpty(id)) {
setLoading(true);
UserService.getUser(id, getUserCallback, errGetUserCallback);
UserService.getRoles(getRolesCallback, errGetRolesCallback);
}
}, [id])
return (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Controllo attività utenti', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<Toast ref={toast}/>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button
onClick={goBack}
outlined
label={__('Indietro', 'gepafin')} icon="pi pi-arrow-left" iconPos="left"/>
</div>
</div>
<div className="appPageSection__withBorder columns">
<p className="appPageSection__pMeta">
<span>{__('Nome utente', 'gepafin')}</span>
<span>{`${user.firstName} ${user.lastName}`}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Email', 'gepafin')}</span>
<span>{user.email}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Ruolo', 'gepafin')}</span>
<span>{user.role?.roleName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Data registrazione', 'gepafin')}</span>
<span>{getDateFromISOstring(user.createdDate)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Ultimo accesso', 'gepafin')}</span>
<span>{getDateFromISOstring(user.lastLogin)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Stato account', 'gepafin')}</span>
<span>{user.status}</span>
</p>
</div>
{['ROLE_PRE_INSTRUCTOR', 'ROLE_INSTRUCTOR_MANAGER'].includes(user.role?.roleType)
? <>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<h3>{__('Cambia ruolo', 'gepafin')}</h3>
<div className="row">
<Dropdown
id="form"
disabled={isEmpty(roles) || loading}
value={chosenRole}
onChange={(e) => setChosenRole(e.value)}
options={roles.filter(o => [3, 5].includes(o.id)).map(o => ({ label: o.roleName, value: o.id }))}
optionLabel="label"
placeholder={__('Seleziona ruolo', 'gepafin')}/>
<Button
type="button"
disabled={loading}
outlined
onClick={handleRoleUpdate}
label={__('Modifica', 'gepafin')}
icon="pi pi-cog" iconPos="right"/>
</div>
</div>
</> : null}
<div className="appPage__spacer"></div>
{/*<div className="appPageSection">
<h2>{__('Statistiche attività', 'gepafin')}</h2>
<div className="statsBigBadges__grid grid-small">
<div className="statsBigBadges__gridItem">
<span>{__('Login totali', 'gepafin')}</span>
<span>{<NumberFlow
value={getStatValue('numberOfActiveCalls', 0)}
format={{ notation: 'compact' }}
locales="it-IT"/>}</span>
</div>
<div className="statsBigBadges__gridItem">
<span>{__('Bandi gestiti', 'gepafin')}</span>
<span>{<NumberFlow
value={getStatValue('numberOfActiveCalls', 0)}
format={{ notation: 'compact' }}
locales="it-IT"/>}</span>
</div>
<div className="statsBigBadges__gridItem">
<span>{__('Domande processate', 'gepafin')}</span>
<span>{<NumberFlow
value={getStatValue('numberOfActiveCalls', 0)}
format={{ notation: 'compact' }}
locales="it-IT"/>}</span>
</div>
</div>
</div>*/}
</div>
)
}
export default UserActivity;

View File

@@ -24,6 +24,7 @@ import { Calendar } from 'primereact/calendar';
import { Tag } from 'primereact/tag';
import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import translationStrings from '../../../../translationStringsForComponents';
import { Link } from 'react-router-dom';
const AllUsersTable = () => {
const users = useStore().main.users();
@@ -53,7 +54,7 @@ const AllUsersTable = () => {
const getFormattedData = (data) => {
return data
.filter(o => ['ROLE_SUPER_ADMIN', 'ROLE_PRE_INSTRUCTOR'].includes(o.role.roleType));
.filter(o => ['ROLE_SUPER_ADMIN', 'ROLE_PRE_INSTRUCTOR', 'ROLE_INSTRUCTOR_MANAGER'].includes(o.role.roleType));
};
const clearFilter = () => {
@@ -125,12 +126,16 @@ const AllUsersTable = () => {
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)}/>;
};
/*const actionsBodyTemplate = (rowData) => {
/!*return <Link to={`/utenti/${rowData.id}`}>
<Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small" iconPos="right"/>
</Link>*!/
return null;
}*/
const actionsBodyTemplate = (rowData) => {
return <Link to={`/utenti/${rowData.id}`}>
<Button
severity="info"
label={__('Attività', 'gepafin')}
icon="pi pi-eye"
size="small"
iconPos="right"/>
</Link>
}
const header = renderHeader();
@@ -149,22 +154,22 @@ const AllUsersTable = () => {
filter sortable
field="email"
filterPlaceholder={__('Cerca per email', 'gepafin')}
style={{ minWidth: '12rem' }}/>
style={{ minWidth: '10rem' }}/>
<Column body={roleBodyTemplate} header={__('Ruolo', 'gepafin')}
style={{ minWidth: '12rem' }}/>
style={{ minWidth: '8rem' }}/>
<Column field="status" header={__('Stato', 'gepafin')}
filterMenuStyle={{ width: '14rem' }}
style={{ width: '120px' }} body={statusBodyTemplate}
filterElement={statusFilterTemplate}/>
<Column header={__('Ultimo accesso', 'gepafin')}
filterField="lastLogin" dataType="date"
style={{ minWidth: '10rem' }}
style={{ minWidth: '7rem' }}
body={dateLastAccessBodyTemplate} filter filterElement={dateFilterTemplate}/>
{/*<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/>*/}
<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/>
</DataTable>
</div>
)
}
export default AllUsersTable;
export default AllUsersTable;

View File

@@ -121,7 +121,7 @@ const Users = () => {
const getRolesCallback = (data) => {
if (data.status === 'SUCCESS') {
const roles = data.data
.filter(o => ['ROLE_SUPER_ADMIN', 'ROLE_PRE_INSTRUCTOR'].includes(o.roleType))
.filter(o => ['ROLE_SUPER_ADMIN', 'ROLE_PRE_INSTRUCTOR', 'ROLE_INSTRUCTOR_MANAGER'].includes(o.roleType))
.map(o => ({
name: o.roleName,
value: o.id