Merge branch 'master' of github.com:Kitzanos/GEPAFIN-FE

This commit is contained in:
Vitalii Kiiko
2025-05-22 11:24:05 +02:00
28 changed files with 736 additions and 228 deletions

View File

@@ -1,90 +1,91 @@
{
"name": "bflows-gepafin",
"version": "1.0.0",
"private": true,
"dependencies": {
"@babel/plugin-proposal-private-property-in-object": "7.21.11",
"@babel/preset-react": "7.26.3",
"@date-fns/tz": "1.2.0",
"@emailjs/browser": "4.4.1",
"@number-flow/react": "0.5.9",
"@sentry/browser": "9.11.0",
"@stomp/stompjs": "7.1.1",
"@tanstack/react-table": "8.21.2",
"@wordpress/i18n": "5.21.0",
"@wordpress/react-i18n": "4.21.0",
"codice-fiscale-js": "2.3.22",
"copy-to-clipboard": "3.3.3",
"deep-object-diff": "1.1.9",
"dompurify": "3.2.5",
"expression-language": "1.2.0",
"fast-deep-equal": "3.1.3",
"hotkeys-js": "3.13.9",
"html-react-parser": "5.2.3",
"jwt-decode": "4.0.0",
"klona": "2.0.6",
"leader-line-new": "1.1.9",
"luxon": "3.6.1",
"mathjs": "14.4.0",
"mustache": "4.2.0",
"object-path-immutable": "4.1.2",
"primeicons": "7.0.0",
"primereact": "10.9.4",
"quill": "2.0.3",
"ramda": "0.30.1",
"react": "19.1.0",
"react-dnd": "16.0.1",
"react-dnd-html5-backend": "16.0.1",
"react-dom": "19.1.0",
"react-hook-form": "7.55.0",
"react-router-dom": "7.5.0",
"react-scripts": "5.0.1",
"recharts": "2.15.2",
"sockjs-client": "1.6.1",
"validate.js": "0.13.1",
"zustand": "5.0.3",
"zustand-x": "6.1.0"
},
"devDependencies": {
"@babel/cli": "7.27.0",
"@babel/core": "7.26.10",
"@babel/plugin-syntax-jsx": "7.25.9",
"@wordpress/babel-plugin-makepot": "6.21.0",
"babel-plugin-macros": "3.1.0",
"node-wp-i18n": "1.2.7",
"sass": "1.86.3",
"sass-loader": "16.0.5"
},
"scripts": {
"start": "GENERATE_SOURCEMAP=false react-scripts start",
"start:dev": "cp environments/dev/* public/loaded-files && rm public/loaded-files/dev.env && cp environments/dev/dev.env .env && PORT=8000 react-scripts start --mode development",
"start:prod": "cp environments/prod/* public/loaded-files && rm public/loaded-files/prod.env && cp environments/prod/prod.env .env && react-scripts start --mode production",
"build": "react-scripts build",
"build:dev": "cp environments/dev/* public/loaded-files && rm public/loaded-files/dev.env && cp environments/dev/dev.env .env && react-scripts build --mode development",
"build:prod": "cp environments/prod/* public/loaded-files && rm public/loaded-files/prod.env && cp environments/prod/prod.env .env && react-scripts build --mode production",
"test": "react-scripts test",
"eject": "react-scripts eject",
"make-pot": "wpi18n makepot --domain-path=languages --domain=gepafin"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
],
"rules": {
"react-hooks/exhaustive-deps": "off"
"name": "bflows-gepafin",
"version": "1.0.0",
"private": true,
"dependencies": {
"@babel/plugin-proposal-private-property-in-object": "7.21.11",
"@babel/preset-react": "7.26.3",
"@date-fns/tz": "1.2.0",
"@emailjs/browser": "4.4.1",
"@number-flow/react": "0.5.9",
"@sentry/browser": "9.11.0",
"@stomp/stompjs": "7.1.1",
"@tanstack/react-table": "8.21.2",
"@wordpress/i18n": "5.21.0",
"@wordpress/react-i18n": "4.21.0",
"codice-fiscale-js": "2.3.22",
"copy-to-clipboard": "3.3.3",
"deep-object-diff": "1.1.9",
"dompurify": "3.2.5",
"expression-language": "1.2.0",
"fast-deep-equal": "3.1.3",
"hotkeys-js": "3.13.9",
"html-react-parser": "5.2.3",
"jwt-decode": "4.0.0",
"klona": "2.0.6",
"leader-line-new": "1.1.9",
"luxon": "3.6.1",
"mathjs": "14.4.0",
"mustache": "4.2.0",
"object-path-immutable": "4.1.2",
"primeicons": "7.0.0",
"primereact": "10.9.4",
"quill": "2.0.3",
"ramda": "0.30.1",
"react": "19.1.0",
"react-dnd": "16.0.1",
"react-dnd-html5-backend": "16.0.1",
"react-dom": "19.1.0",
"react-hook-form": "7.55.0",
"react-router-dom": "7.5.0",
"react-scripts": "5.0.1",
"recharts": "2.15.2",
"sockjs-client": "1.6.1",
"validate.js": "0.13.1",
"zustand": "5.0.3",
"zustand-x": "6.1.0"
},
"devDependencies": {
"@babel/cli": "7.27.0",
"@babel/core": "7.26.10",
"@babel/plugin-syntax-jsx": "7.25.9",
"@wordpress/babel-plugin-makepot": "6.21.0",
"babel-plugin-macros": "3.1.0",
"node-wp-i18n": "1.2.7",
"sass": "1.86.3",
"sass-loader": "16.0.5"
},
"scripts": {
"start": "GENERATE_SOURCEMAP=false react-scripts start",
"start2": "react-scripts start",
"start:dev": "cp environments/dev/* public/loaded-files && rm public/loaded-files/dev.env && cp environments/dev/dev.env .env && PORT=8000 react-scripts start --mode development",
"start:prod": "cp environments/prod/* public/loaded-files && rm public/loaded-files/prod.env && cp environments/prod/prod.env .env && react-scripts start --mode production",
"build": "react-scripts build",
"build:dev": "cp environments/dev/* public/loaded-files && rm public/loaded-files/dev.env && cp environments/dev/dev.env .env && react-scripts build --mode development",
"build:prod": "cp environments/prod/* public/loaded-files && rm public/loaded-files/prod.env && cp environments/prod/prod.env .env && react-scripts build --mode production",
"test": "react-scripts test",
"eject": "react-scripts eject",
"make-pot": "wpi18n makepot --domain-path=languages --domain=gepafin"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
],
"rules": {
"react-hooks/exhaustive-deps": "off"
}
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
}

View File

@@ -295,8 +295,9 @@
.appPageSection__message {
display: flex;
align-items: center;
width: 100%;
gap: 20px;
background: rgba(255, 242, 226, 0.7);
background: rgba(242, 242, 242, 0.7);
border-style: solid;
border-width: 0 0 0 6px;
padding: 1.25rem 1.75rem;
@@ -409,10 +410,18 @@
.appPageSection__actions {
display: flex;
gap: 24px;
padding: 24px 0 48px;
padding: 24px 0 0;
flex-wrap: wrap;
}
.appPageSection .appPageSection__actions:last-of-type {
padding-bottom: 48px;
}
.appPageSection__actions:empty {
display: none;
}
.appPageSection__tableActions {
display: flex;
gap: 10px;

View File

@@ -13,6 +13,7 @@ import getPropeMimeLabels from '../../../../helpers/getPropeMimeLabels';
import { FileUpload } from 'primereact/fileupload';
import { Tag } from 'primereact/tag';
import { Button } from 'primereact/button';
import { Messages } from 'primereact/messages';
import { defaultMaxFileSize, mimeTypes } from '../../../../configData';
import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText';
@@ -47,24 +48,48 @@ const Fileupload = ({
const [acceptFormats, setAcceptFormats] = useState('');
const [formatsForInput, setFormatsForInput] = useState('');
const inputRef = useRef();
const messagesRef = useRef(null);
const customBase64Uploader = (event) => {
const formData = new FormData()
const filesToUpload = [];
const uploadedFiles = inputRef.current ? inputRef.current.getUploadedFiles() : [];
const currentFiles = stateFieldData;
for (const file of event.files) {
formData.append('file', file)
const isDuplicate = [...uploadedFiles, ...currentFiles].some(
(uploadedFile)=>uploadedFile.name === file.name
);
if(isDuplicate){
messagesRef.current.show({
severity: 'error',
summary: __('Attenzione', 'gepafin'),
detail: `Il file con nome "${file.name}" è già stato caricato.`,
life: 10000
});
} else {
formData.append('file', file)
filesToUpload.push(file);
}
}
if(filesToUpload.length > 0 ){
FileUploadService.uploadFile(sourceId, formData, callback, errorCallback, [
['documentType', doctype.toUpperCase()],
['sourceType', source.toUpperCase()]
]);
}
FileUploadService.uploadFile(sourceId, formData, callback, errorCallback, [
['documentType', doctype.toUpperCase()],
['sourceType', source.toUpperCase()]
]);
};
const callback = (data) => {
const callback = (data, uploadedFiles) => {
if (data.status === 'SUCCESS') {
setStateFieldData(data.data);
const uploadedFiles = inputRef.current.getUploadedFiles();
setDataFn(fieldName, [...uploadedFiles, ...data.data], { shouldValidate: true });
setStateFieldData(prevState => [...prevState, ...data.data]);
const currentUploadedFiles = inputRef.current.getUploadedFiles() || [];
inputRef.current.setFiles([]);
setDataFn(fieldName, [...currentUploadedFiles, ...data.data], { shouldValidate: true });
saveFormCallback();
}
}
@@ -204,6 +229,7 @@ const Fileupload = ({
return (
sourceId || sourceId === 0
? <>
<Messages ref={messagesRef} />
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ?
<span className="appForm__field--required">*</span> : null}

View File

@@ -13,6 +13,8 @@ import { FileUpload } from 'primereact/fileupload';
import { Tag } from 'primereact/tag';
import { Button } from 'primereact/button';
import { head, isEmpty } from 'ramda';
import { Messages } from 'primereact/messages';
import { defaultMaxFileSize, mimeTypes } from '../../../../configData';
import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText';
@@ -42,24 +44,50 @@ const FileuploadAsync = ({
const [acceptFormats, setAcceptFormats] = useState('');
const [formatsForInput, setFormatsForInput] = useState('');
const inputRef = useRef();
const messagesRef = useRef(null);
const customBase64Uploader = (event) => {
const formData = new FormData()
const filesToUpload = [];
const uploadedFiles = inputRef.current ? inputRef.current.getUploadedFiles() : [];
const currentFiles = stateFieldData;
for (const file of event.files) {
formData.append('file', file)
const isDuplicate = [...uploadedFiles, ...currentFiles].some(
(uploadedFile)=>uploadedFile.name === file.name
);
if(isDuplicate){
messagesRef.current.show({
severity: 'error',
summary: __('Attenzione', 'gepafin'),
detail: `Il file con nome "${file.name}" è già stato caricato.`,
life: 10000
});
} else {
formData.append('file', file)
filesToUpload.push(file);
}
}
FileUploadService.uploadFile(sourceId, formData, callback, errorCallback, [
['documentType', doctype.toUpperCase()],
['sourceType', source.toUpperCase()]
]);
if(filesToUpload.length > 0){
FileUploadService.uploadFile(sourceId, formData, callback, errorCallback, [
['documentType', doctype.toUpperCase()],
['sourceType', source.toUpperCase()]
]);
}
};
const callback = (data) => {
const callback = (data, uploadedFiles) => {
if (data.status === 'SUCCESS') {
setStateFieldData(data.data);
const uploadedFiles = inputRef.current.getUploadedFiles();
setDataFn(fieldName, [...uploadedFiles, ...data.data], { shouldValidate: true });
setStateFieldData(prevState => [...prevState, ...data.data]);
const currentUploadedFiles = inputRef.current.getUploadedFiles() || [];
inputRef.current.setUploadedFiles([...currentUploadedFiles, ...data.data]);
inputRef.current.setFiles([]);
setDataFn(fieldName, [...currentUploadedFiles, ...data.data], { shouldValidate: true });
}
}
@@ -160,7 +188,7 @@ const FileuploadAsync = ({
useEffect(() => {
if (inputRef.current) {
inputRef.current.setUploadedFiles(defaultValue);
inputRef.current.setUploadedFiles(defaultValue);
}
setStateFieldData(defaultValue);
}, [defaultValue]);
@@ -186,6 +214,7 @@ const FileuploadAsync = ({
return (
sourceId && sourceId !== 0
? <>
<Messages ref={messagesRef} />
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
{acceptFormats ? ' (' + getPropeMimeLabels(accept) + ')' : null}

View File

@@ -262,4 +262,11 @@ export const classificationType = [
'name': 'GENERICO',
'idTipoprotocollo': 3
}
];
];
export const resendEmailLabelsByType = {
APPLICATION_AMENDMENT_REQUESTED: 'Invia email (nuovo soccorso)',
APPLICATION_AMENDMENT_REMINDER: 'Invia email (sollecito)',
APPLICATION_ADMISSIBLE: 'Invia email (ammisibile)',
APPLICATION_REJECTED: 'Invia email (respinto)'
}

View File

@@ -115,23 +115,43 @@ const AddCompany = () => {
const checkVatCallback = (data) => {
if (data.status === 'SUCCESS') {
const version = data.data.version;
const resp = data.data.vatCheckResponse.data;
if (!isEmpty(resp)) {
const firstItem = resp[0];
const {
taxCode, vatCode, address, companyName, pec
} = firstItem;
const { streetName, zipCode, town } = address?.registeredOffice;
let formData = {};
const formData = {
cap: zipCode,
pec,
email: pec,
city: town,
codiceFiscale: taxCode ? taxCode : vatCode,
address: streetName,
vatNumber: vatCode,
companyName
if (version === 'V2') {
const firstItem = resp[0];
const {
taxCode, vatCode, address, companyName, pec
} = firstItem;
const { streetName, zipCode, town } = address?.registeredOffice;
formData = {
cap: zipCode,
pec,
email: pec,
city: town,
codiceFiscale: taxCode ? taxCode : vatCode,
address: streetName,
vatNumber: vatCode,
companyName
}
} else {
const {
cap, cf, denominazione, piva, indirizzo, comune, dettaglio: { pec }
} = resp;
formData = {
cap,
pec,
email: pec,
city: comune,
codiceFiscale: cf ? cf : piva,
address: indirizzo,
vatNumber: piva,
companyName: denominazione
}
}
Object.keys(formData).map(k => setValue(k, formData[k]));
setVatCheckResponse(data.data.vatCheckResponse);

View File

@@ -5,6 +5,10 @@ import { uniq } from 'ramda';
// api
import BandoService from '../../../../service/bando-service';
// tools
import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString';
import getTimeFromISOstring from '../../../../helpers/getTimeFromISOstring';
// components
import { FilterMatchMode, FilterOperator } from 'primereact/api';
import { DataTable } from 'primereact/datatable';
@@ -13,10 +17,8 @@ import { Button } from 'primereact/button';
import { Calendar } from 'primereact/calendar';
import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import { Link } from 'react-router-dom';
import translationStrings from '../../../../translationStringsForComponents';
import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString';
import getTimeFromISOstring from '../../../../helpers/getTimeFromISOstring';
import translationStrings from '../../../../translationStringsForComponents';
const LatestBandiTable = () => {
const [items, setItems] = useState(null);

View File

@@ -4,8 +4,12 @@ import { Link } from 'react-router-dom';
import translationStrings from '../../../../translationStringsForComponents';
// store
import { storeGet, useStoreValue } from '../../../../store';
// api
import BandoService from '../../../../service/bando-service';
import PreferredBandoService from '../../../../service/preferred-bando-service';
// tools
import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString';
@@ -14,6 +18,7 @@ import getFormattedDateString from '../../../../helpers/getFormattedDateString';
import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
// components
import { DataTable } from 'primereact/datatable';
@@ -24,11 +29,9 @@ import { Dropdown } from 'primereact/dropdown';
import { Tag } from 'primereact/tag';
import { Calendar } from 'primereact/calendar';
import { isNil } from 'ramda';
import { storeGet } from '../../../../store';
import PreferredBandoService from '../../../../service/preferred-bando-service';
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
const LatestBandiBeneficiarioTableAsync = () => {
const chosenCompanyId = useStoreValue('chosenCompanyId');
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [items, setItems] = useState(null);
const [totalRecordsNum, setTotalRecordsNum] = useState(0);
@@ -197,9 +200,19 @@ const LatestBandiBeneficiarioTableAsync = () => {
useEffect(() => {
setLocalAsyncRequest(true);
const paginationQuery = getPaginationQuery();
const role = storeGet('getRole');
BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks);
}, [lazyState]);
if (role === 'ROLE_CONFIDI') {
BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks, [
['companyId', chosenCompanyId],
['onlyConfidiCall', true]
]);
} else {
BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks, [
['companyId', chosenCompanyId]
]);
}
}, [lazyState, chosenCompanyId]);
return (
<div className="appPageSection__table">

View File

@@ -33,7 +33,7 @@ const LatestBandiTable = () => {
useEffect(() => {
setLoading(true);
const role = storeGet('getRole')
const role = storeGet('getRole');
if (role === 'ROLE_CONFIDI') {
BandoService.getBandi(getCallback, errGetCallbacks, [

View File

@@ -31,7 +31,6 @@ import { ProgressBar } from 'primereact/progressbar';
const MyLatestSubmissionsTableAsync = () => {
const chosenCompanyId = useStoreValue('chosenCompanyId');
const companies = useStoreValue('companies');
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [items, setItems] = useState(null);
const [totalRecordsNum, setTotalRecordsNum] = useState(0);
@@ -194,13 +193,15 @@ const MyLatestSubmissionsTableAsync = () => {
};
useEffect(() => {
setLocalAsyncRequest(true);
const paginationQuery = getPaginationQuery();
if (chosenCompanyId && chosenCompanyId !== 0) {
setLocalAsyncRequest(true);
const paginationQuery = getPaginationQuery();
ApplicationService.getApplicationsPaginated(paginationQuery, getCallback, errGetCallbacks, [
['companyId', chosenCompanyId]
]);
}, [lazyState, chosenCompanyId, companies]);
ApplicationService.getApplicationsPaginated(paginationQuery, getCallback, errGetCallbacks, [
['companyId', chosenCompanyId]
]);
}
}, [lazyState, chosenCompanyId]);
return (
<div className="appPageSection__table">

View File

@@ -14,7 +14,7 @@ import DashboardService from '../../service/dashboard-service';
import { Button } from 'primereact/button';
import ErrorBoundary from '../../components/ErrorBoundary';
import MyLatestSubmissionsTableAsync from '../DashboardBeneficiario/components/MyLatestSubmissionsTableAsync';
import LatestBandiTable from '../DashboardBeneficiario/components/LatestBandiTable';
import LatestBandiBeneficiarioTableAsync from '../DashboardBeneficiario/components/LatestBandiBeneficiarioTableAsync';
const DashboardBeneficiarioConfidi = () => {
const navigate = useNavigate();
@@ -108,7 +108,7 @@ const DashboardBeneficiarioConfidi = () => {
<div className="appPageSection">
<h2>{__('Bandi disponibili', 'gepafin')}</h2>
<ErrorBoundary><LatestBandiTable/></ErrorBoundary>
<ErrorBoundary><LatestBandiBeneficiarioTableAsync/></ErrorBoundary>
</div>
<div className="appPage__spacer"></div>

View File

@@ -26,6 +26,7 @@ import { Calendar } from 'primereact/calendar';
import { Button } from 'primereact/button';
import { Link, useNavigate } from 'react-router-dom';
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
@@ -73,7 +74,8 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
const getCallback = (resp) => {
if (resp.status === 'SUCCESS') {
const { body, totalRecords,
const {
body, totalRecords,
//currentPage, totalPages, pageSize
} = resp.data;
setTotalRecordsNum(totalRecords);
@@ -114,7 +116,8 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
}
setLazyState({ ...lazyState, filters, first: 0 });
}}
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter"/>;
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel}
className="p-column-filter"/>;
};
const dateFilterTemplate = (options) => {
@@ -130,22 +133,43 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
return getFormattedDateString(rowData.evaluationEndDate);
};
const updateRowData = useCallback((id, updateResponse) => {
const newItems = items.map((o) => {
if (o.id === id) {
o.emailSendResponse = updateResponse;
}
return o;
})
setItems(newItems);
}, [items]);
const actionsBodyTemplate = (rowData) => {
if (rowData.status === 'AWAITING') {
return <Button
severity="info"
onClick={() => handleInitiateEvaluation(rowData.id)}
label={__('Valuta', 'gepafin')}
icon="pi pi-eye"
size="small"
iconPos="right"/>
return <div className="appPageSection__tableActions lessGap">
<Button
severity="info"
onClick={() => handleInitiateEvaluation(rowData.id)}
label={__('Valuta', 'gepafin')}
icon="pi pi-eye"
size="small"
iconPos="right"/>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
} else {
const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId
? __('Valuta', 'gepafin')
: __('Mostra', 'gepafin');
return <Link to={`/mie-domande/${rowData.applicationId}`}>
<Button severity="info" label={label} icon="pi pi-eye" size="small" iconPos="right"/>
</Link>
return <div className="appPageSection__tableActions lessGap">
<Link
to={`/mie-domande/${rowData.applicationId}`}>
<Button severity="info" label={label} icon="pi pi-eye" size="small" iconPos="right"/>
</Link>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
}
}
@@ -192,7 +216,8 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
const renderHeader = () => {
return (
<div className="flex justify-content-between">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined onClick={clearFilter} />
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
onClick={clearFilter}/>
</div>
);
};
@@ -268,7 +293,7 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
style={{ minWidth: '7rem' }} body={statusBodyTemplate}
filter
filterMatchModeOptions={translationStrings.statusFilterOptions}
filterElement={statusFilterTemplate} />
filterElement={statusFilterTemplate}/>
<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/>
</DataTable>

View File

@@ -15,6 +15,7 @@ import { useStoreValue } from '../../../../store';
import getFormattedDateString from '../../../../helpers/getFormattedDateString';
import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
// components
import { DataTable } from 'primereact/datatable';
@@ -25,7 +26,7 @@ import { Tag } from 'primereact/tag';
import { Calendar } from 'primereact/calendar';
import { Button } from 'primereact/button';
import { Link, useNavigate } from 'react-router-dom';
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
@@ -73,7 +74,8 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
const getCallback = (resp) => {
if (resp.status === 'SUCCESS') {
const { body, totalRecords,
const {
body, totalRecords,
//currentPage, totalPages, pageSize
} = resp.data;
setTotalRecordsNum(totalRecords);
@@ -114,7 +116,8 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
}
setLazyState({ ...lazyState, filters, first: 0 });
}}
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter"/>;
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel}
className="p-column-filter"/>;
};
const dateFilterTemplate = (options) => {
@@ -130,22 +133,48 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
return getFormattedDateString(rowData.evaluationEndDate);
};
const updateRowData = useCallback((id, updateResponse) => {
const newItems = items.map((o) => {
if (o.id === id) {
o.emailSendResponse = updateResponse;
}
return o;
})
setItems(newItems);
}, [items]);
const actionsBodyTemplate = (rowData) => {
if (rowData.status === 'AWAITING') {
return <Button
severity="info"
onClick={() => handleInitiateEvaluation(rowData.id)}
label={__('Valuta', 'gepafin')}
icon="pi pi-eye"
size="small"
iconPos="right"/>
return <div className="appPageSection__tableActions lessGap">
<Button
severity="info"
onClick={() => handleInitiateEvaluation(rowData.id)}
label={__('Valuta', 'gepafin')}
icon="pi pi-eye"
size="small"
iconPos="right"/>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
} else {
const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId
? __('Valuta', 'gepafin')
: __('Mostra', 'gepafin');
return <Link to={`/domande/${rowData.applicationId}`}>
<Button severity="info" label={label} icon="pi pi-eye" size="small" iconPos="right"/>
</Link>
return (
<div className="appPageSection__tableActions lessGap">
<Link to={`/domande/${rowData.applicationId}`}>
<Button severity="info" label={label} icon="pi pi-eye" size="small" iconPos="right"/>
</Link>
<Link to={`/domande/${rowData.applicationId}/preview`}>
<Button severity="info" label={__('Anteprima', 'gepafin')} icon="pi pi-eye" size="small"
iconPos="right"/>
</Link>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
)
}
}
@@ -192,7 +221,8 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
const renderHeader = () => {
return (
<div className="flex justify-content-between">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined onClick={clearFilter} />
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
onClick={clearFilter}/>
</div>
);
};
@@ -268,7 +298,7 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
style={{ minWidth: '7rem' }} body={statusBodyTemplate}
filter
filterMatchModeOptions={translationStrings.statusFilterOptions}
filterElement={statusFilterTemplate} />
filterElement={statusFilterTemplate}/>
<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/>
</DataTable>

View File

@@ -52,6 +52,8 @@ import RepeaterFields from '../DomandaEditPreInstructor/components/RepeaterField
import ApplicationInfo from '../DomandaEditPreInstructor/components/ApplicationInfo';
import ApplicationDownloadFiles from '../DomandaEditPreInstructor/components/ApplicationDownloadFiles';
import FormField from '../../components/FormField';
import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
import EvaluationReAdmit from '../DomandaEditPreInstructor/components/EvaluationReAdmit';
const APP_EVALUATION_FLOW_ID = process.env.REACT_APP_EVALUATION_FLOW_ID;
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
@@ -85,6 +87,7 @@ const DomandaEditInstructorManager = () => {
const [formData, setFormData] = useState([]);
const [formId, setFormId] = useState(0);
const [formInitialData, setFormInitialData] = useState(null);
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
const {
control,
handleSubmit,
@@ -785,6 +788,11 @@ const DomandaEditInstructorManager = () => {
}
}
const updateStatusOfAppl = useCallback((newStatus) => {
const newData = wrap(data).set(['applicationStatus'], newStatus).value();
setData(newData);
}, [data]);
const actionBtns = () => {
return <div className="appPageSection__actions">
{['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus)
@@ -873,6 +881,29 @@ const DomandaEditInstructorManager = () => {
</div>
}
const auxActions = () => {
return <div className="appPageSection__actions">
<SoccorsoResendEmails
emailsData={emailSendResponse}
setDataEmailsSoccorso={updateEmailSendResponses}/>
<EvaluationReAdmit id={data.applicationId} status={data.applicationStatus} statusUpdateFn={updateStatusOfAppl}/>
</div>
}
const updateEmailSendResponses = useCallback((newEmailData) => {
const newData = wrap(data).set(['emailSendResponse'], newEmailData).value();
setData(newData);
}, [data]);
const motivationMsg = () => {
return data.motivation && !isEmpty(data.motivation) && ['REJECTED', 'APPROVED'].includes(data.applicationStatus)
? <div className="appPageSection__message info">
<i className="pi pi-info-circle"></i>
<span className="summary">{__('Motivazione:', 'gepafin')}</span>
<span>{renderHtmlContent(data.motivation)}</span>
</div> : null
}
useEffect(() => {
let updatedFormValues = klona(formValues);
let context = {};
@@ -964,14 +995,21 @@ const DomandaEditInstructorManager = () => {
? <div className="appPage__content">
<ApplicationInfo data={data}/>
<div className="appPageSection">
{motivationMsg()}
</div>
<div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span>
</div>
<div className="appPageSection">
{actionBtns()}
{auxActions()}
</div>
<div className="appPageSection__hr"></div>
<ApplicationDownloadFiles id={id}/>
<div className="appPageSection">
@@ -996,6 +1034,23 @@ const DomandaEditInstructorManager = () => {
sourceName="evaluation"/>
</div>
<div className="appPageSection">
<h2>{__('Note', 'gepafin')}</h2>
<div translate="no" style={{ 'width': '100%' }}>
<Editor
value={data.note}
readOnly={shouldDisableField('note') || evaluationBlockedForUser(data)}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateEvaluationValue(
e.htmlValue,
['note']
)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</div>
{data.evaluationVersion === 'V2'
? <div className="appPageSection">
<h2>{__('Documenti allegati', 'gepafin')}</h2>
@@ -1108,21 +1163,6 @@ const DomandaEditInstructorManager = () => {
</div>)}
</div>
</div>
<h3>{__('Note', 'gepafin')}</h3>
<div translate="no">
<Editor
value={data.note}
readOnly={shouldDisableField('note') || evaluationBlockedForUser(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>
@@ -1241,6 +1281,7 @@ const DomandaEditInstructorManager = () => {
<div className="appPageSection">
{actionBtns()}
{auxActions()}
</div>
<Dialog

View File

@@ -0,0 +1,105 @@
import React, { useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
// tools
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
// api
import ApplicationService from '../../../../service/application-service';
// store
import { useStoreValue } from '../../../../store';
import { Toast } from 'primereact/toast';
import { Button } from 'primereact/button';
import { Dialog } from 'primereact/dialog';
const EvaluationReAdmit = ({ id, status, statusUpdateFn }) => {
const [isSendingRequest, setIsSendingRequest] = useState(false);
const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false);
const toast = useRef(null);
const role = useStoreValue('getRole');
const doReAdmit = () => {
setIsSendingRequest(true);
ApplicationService.reAdmitApplication(id, reAdmitCallback, errReAdmitCallback);
}
const reAdmitCallback = (resp) => {
if (resp.status === 'SUCCESS') {
if (toast.current && resp.message) {
toast.current.show({
severity: 'success',
summary: '',
detail: resp.message
});
}
if (statusUpdateFn) {
statusUpdateFn(resp.data.status);
}
}
setIsSendingRequest(false);
}
const errReAdmitCallback = (resp) => {
if (toast.current && resp.message) {
toast.current.show({
severity: resp.status === 'SUCCESS' ? 'info' : 'error',
summary: '',
detail: resp.message
});
}
set404FromErrorResponse(resp);
setIsSendingRequest(false);
}
const headerConfirmDialog = () => {
return <span>{__('Richiesta di conferma', 'gepafin')}</span>;
}
const hideConfirmDialog = () => {
setIsVisibleConfirmDialog(false);
}
const footerConfirmDialog = () => {
return <div>
<Button type="button" label={__('No', 'gepafin')} onClick={() => setIsVisibleConfirmDialog(false)} outlined/>
<Button
type="button"
label={__('Si', 'gepafin')} onClick={doConfirm}/>
</div>
}
const doConfirm = () => {
setIsVisibleConfirmDialog(false);
doReAdmit();
}
return (
['ROLE_SUPER_ADMIN', 'ROLE_INSTRUCTOR_MANAGER'].includes(role) && ['REJECTED'].includes(status)
? <>
<Toast ref={toast}/>
<Button
type="button"
disabled={isSendingRequest}
severity="warning"
onClick={() => setIsVisibleConfirmDialog(true)}
label={__('Riammetti', 'gepafin')}
icon="pi pi-arrow-circle-up" iconPos="right"/>
<Dialog
visible={isVisibleConfirmDialog}
modal
header={headerConfirmDialog}
footer={footerConfirmDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideConfirmDialog}>
<div className="appForm__field">
<p>{__('È autorizzato dal direttore e autorizzazione caricata su portale a seguito del quale parte l\'email?', 'gepafin')}</p>
</div>
</Dialog>
</> : null
)
}
export default EvaluationReAdmit;

View File

@@ -53,6 +53,8 @@ import RepeaterFields from './components/RepeaterFields';
import ApplicationInfo from './components/ApplicationInfo';
import ApplicationDownloadFiles from './components/ApplicationDownloadFiles';
import FormField from '../../components/FormField';
import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
import EvaluationReAdmit from './components/EvaluationReAdmit';
const APP_EVALUATION_FLOW_ID = process.env.REACT_APP_EVALUATION_FLOW_ID;
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
@@ -86,6 +88,7 @@ const DomandaEditPreInstructor = () => {
const [formData, setFormData] = useState([]);
const [formId, setFormId] = useState(0);
const [formInitialData, setFormInitialData] = useState(null);
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
const {
control,
handleSubmit,
@@ -592,7 +595,7 @@ const DomandaEditPreInstructor = () => {
const shouldDisableField = useCallback((fieldName) => {
return !['EVALUATION', 'ADMISSIBLE'].includes(data.applicationStatus)
|| (['ADMISSIBLE'].includes(data.applicationStatus) && fieldName !== 'criteria')
|| (['ADMISSIBLE'].includes(data.applicationStatus) && !['criteria', 'note'].includes(fieldName))
|| (fieldName === 'files' && !isEmpty(data.amendmentDetails))
}, [data.applicationStatus]);
@@ -784,6 +787,11 @@ const DomandaEditPreInstructor = () => {
}
}
const updateStatusOfAppl = useCallback((newStatus) => {
const newData = wrap(data).set(['applicationStatus'], newStatus).value();
setData(newData);
}, [data]);
const actionBtns = () => {
return <div className="appPageSection__actions">
{['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus)
@@ -874,6 +882,29 @@ const DomandaEditPreInstructor = () => {
</div>
}
const auxActions = () => {
return <div className="appPageSection__actions">
<SoccorsoResendEmails
emailsData={emailSendResponse}
setDataEmailsSoccorso={updateEmailSendResponses}/>
<EvaluationReAdmit id={data.applicationId} status={data.applicationStatus} statusUpdateFn={updateStatusOfAppl}/>
</div>
}
const updateEmailSendResponses = useCallback((newEmailData) => {
const newData = wrap(data).set(['emailSendResponse'], newEmailData).value();
setData(newData);
}, [data]);
const motivationMsg = () => {
return data.motivation && !isEmpty(data.motivation) && ['REJECTED', 'APPROVED'].includes(data.applicationStatus)
? <div className="appPageSection__message info">
<i className="pi pi-info-circle"></i>
<span className="summary">{__('Motivazione:', 'gepafin')}</span>
<span>{renderHtmlContent(data.motivation)}</span>
</div> : null
}
useEffect(() => {
let updatedFormValues = klona(formValues);
let context = {};
@@ -966,14 +997,21 @@ const DomandaEditPreInstructor = () => {
? <div className="appPage__content">
<ApplicationInfo data={data}/>
<div className="appPageSection">
{motivationMsg()}
</div>
<div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span>
</div>
<div className="appPageSection">
{actionBtns()}
{auxActions()}
</div>
<div className="appPageSection__hr"></div>
<ApplicationDownloadFiles id={id}/>
<div className="appPageSection">
@@ -998,6 +1036,23 @@ const DomandaEditPreInstructor = () => {
sourceName="evaluation"/>
</div>
<div className="appPageSection">
<h2>{__('Note', 'gepafin')}</h2>
<div translate="no" style={{ 'width': '100%' }}>
<Editor
value={data.note}
readOnly={shouldDisableField('note') || evaluationBlockedForUser(data)}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateEvaluationValue(
e.htmlValue,
['note']
)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</div>
{data.evaluationVersion === 'V2'
? <div className="appPageSection">
<h2>{__('Documenti allegati', 'gepafin')}</h2>
@@ -1110,21 +1165,6 @@ const DomandaEditPreInstructor = () => {
</div>)}
</div>
</div>
<h3>{__('Note', 'gepafin')}</h3>
<div translate="no">
<Editor
value={data.note}
readOnly={shouldDisableField('note') || evaluationBlockedForUser(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>
@@ -1243,6 +1283,7 @@ const DomandaEditPreInstructor = () => {
<div className="appPageSection">
{actionBtns()}
{auxActions()}
</div>
<Dialog

View File

@@ -41,7 +41,6 @@ const Login = () => {
}
const validateCallback = (data) => {
//console.log('login validateCallback', data)
if (data.status === 'SUCCESS') {
storeSet('setAuthData', {
token: data.data.token,
@@ -107,7 +106,7 @@ const Login = () => {
useEffect(() => {
const temp_token = searchParams.get('temp_token');
//console.log('login temp_token', temp_token);
if (!isNil(temp_token) && !isEmpty(temp_token)) {
errorMsgs.current.clear();
AuthenticationService.validateExistingUser(temp_token, validateCallback, validateError);

View File

@@ -32,7 +32,7 @@ const SoccorsoAddPreInstructor = () => {
const [data, setData] = useState({});
const [evaluationId, setEvaluationId] = useState(0);
const [formData, setFormData] = useState({});
const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false)
const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false);
const toast = useRef(null);
const goToEvaluationPage = () => {

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef, useMemo } from 'react';
import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
import { __ } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { is, isEmpty } from 'ramda';
import { is, isEmpty, isNil, pathOr } from 'ramda';
import { wrap } from 'object-path-immutable';
import { klona } from 'klona';
import { useForm } from 'react-hook-form';
@@ -28,6 +28,7 @@ import FormField from '../../components/FormField';
import { Editor } from 'primereact/editor';
import { InputNumber } from 'primereact/inputnumber';
import SoccorsoComunications from '../SoccorsoEditPreInstructor/components/SoccorsoComunications';
import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
const SoccorsoEditInstructorManager = () => {
@@ -43,6 +44,7 @@ const SoccorsoEditInstructorManager = () => {
const [internalNote, setInternalNote] = useState('');
const toast = useRef(null);
const [formInitialData, setFormInitialData] = useState({});
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
const {
control,
handleSubmit,
@@ -346,6 +348,11 @@ const SoccorsoEditInstructorManager = () => {
setIsLoadingReminding(false);
}
const updateEmailSendResponses = useCallback((newEmailData) => {
const newData = wrap(data).set(['emailSendResponse'], newEmailData).value();
setData(newData);
}, [data]);
useEffect(() => {
if (formInitialData) {
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
@@ -506,7 +513,8 @@ const SoccorsoEditInstructorManager = () => {
<Button
type="button"
onClick={sendReminder}
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)}
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)
|| (!isNil(emailSendResponse) && !isEmpty(emailSendResponse))}
outlined
label={__('Invia Sollecito', 'gepafin')}
icon="pi pi-send"
@@ -532,6 +540,11 @@ const SoccorsoEditInstructorManager = () => {
label={__('Chiudi Soccorso Istruttorio', 'gepafin')}
icon="pi pi-times" iconPos="right"/>
</div>
<div className="appPageSection__actions">
<SoccorsoResendEmails
emailsData={emailSendResponse}
setDataEmailsSoccorso={updateEmailSendResponses}/>
</div>
</div>
</div>

View File

@@ -0,0 +1,85 @@
import React, { useRef, useState } from 'react';
import { pathOr } from 'ramda';
// tools
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
// api
import EmailService from '../../../../service/email-service';
import { Toast } from 'primereact/toast';
import { Button } from 'primereact/button';
import { resendEmailLabelsByType } from '../../../../configData';
import { useStoreValue } from '../../../../store';
const SoccorsoResendEmails = ({ emailsData = [], setDataEmailsSoccorso }) => {
const [isResendingRequest, setIsResendingRequest] = useState(false);
const toast = useRef(null);
const role = useStoreValue('getRole');
const filtered = emailsData.filter(o => !o.isEmailSend);
const resendEmail = (data) => {
setIsResendingRequest(true);
EmailService.emailResend(
data.userActionId,
(resp) => resendingCallback(resp, data.userActionId),
errResendingCallback
);
}
const resendingCallback = (resp, id) => {
if (resp.status === 'SUCCESS') {
if (toast.current && resp.message) {
toast.current.show({
severity: 'success',
summary: '',
detail: resp.message
});
}
setTimeout(() => {
if (setDataEmailsSoccorso) {
const newEmailSendResponse = emailsData.map((o) => {
if (o.userActionId === id) {
o.isEmailSend = true;
}
return o;
});
setDataEmailsSoccorso(newEmailSendResponse);
}
setIsResendingRequest(false);
}, 1500);
}
}
const errResendingCallback = (resp) => {
if (toast.current && resp.message) {
toast.current.show({
severity: 'error',
summary: '',
detail: resp.message
});
}
set404FromErrorResponse(resp);
setIsResendingRequest(false);
}
return (
['ROLE_SUPER_ADMIN', 'ROLE_INSTRUCTOR_MANAGER'].includes(role) && filtered.length > 0
? <>
<Toast ref={toast}/>
{filtered
.map(o => <Button
key={o.userActionId}
severity="warning"
type="button"
disabled={isResendingRequest}
onClick={() => resendEmail(o)}
label={pathOr('Re-inivia email', ['emailScenario'], resendEmailLabelsByType)}
icon="pi pi-send" iconPos="right"/>)}
</> : null
)
}
export default SoccorsoResendEmails;

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef, useMemo } from 'react';
import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
import { __ } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { is, isEmpty } from 'ramda';
import { is, isEmpty, isNil, pathOr } from 'ramda';
import { wrap } from 'object-path-immutable';
import { klona } from 'klona';
import { useForm } from 'react-hook-form';
@@ -28,6 +28,7 @@ import FormField from '../../components/FormField';
import { Editor } from 'primereact/editor';
import { InputNumber } from 'primereact/inputnumber';
import SoccorsoComunications from './components/SoccorsoComunications';
import SoccorsoResendEmails from './components/SoccorsoResendEmails';
const SoccorsoEditPreInstructor = () => {
@@ -43,6 +44,7 @@ const SoccorsoEditPreInstructor = () => {
const [internalNote, setInternalNote] = useState('');
const toast = useRef(null);
const [formInitialData, setFormInitialData] = useState({});
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
const {
control,
handleSubmit,
@@ -346,6 +348,11 @@ const SoccorsoEditPreInstructor = () => {
setIsLoadingReminding(false);
}
const updateEmailSendResponses = useCallback((newEmailData) => {
const newData = wrap(data).set(['emailSendResponse'], newEmailData).value();
setData(newData);
}, [data]);
useEffect(() => {
if (formInitialData) {
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
@@ -506,7 +513,8 @@ const SoccorsoEditPreInstructor = () => {
<Button
type="button"
onClick={sendReminder}
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)}
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)
|| (!isNil(emailSendResponse) && !isEmpty(emailSendResponse))}
outlined
label={__('Invia Sollecito', 'gepafin')}
icon="pi pi-send"
@@ -532,6 +540,11 @@ const SoccorsoEditPreInstructor = () => {
label={__('Chiudi Soccorso Istruttorio', 'gepafin')}
icon="pi pi-times" iconPos="right"/>
</div>
<div className="appPageSection__actions">
<SoccorsoResendEmails
emailsData={emailSendResponse}
setDataEmailsSoccorso={updateEmailSendResponses}/>
</div>
</div>
</div>

View File

@@ -22,6 +22,7 @@ import { Dropdown } from 'primereact/dropdown';
import { Tag } from 'primereact/tag';
import { Calendar } from 'primereact/calendar';
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
const SoccorsiInstructorManagerMioTableAsync = ({ userId = null }) => {
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
@@ -94,10 +95,25 @@ const SoccorsiInstructorManagerMioTableAsync = ({ userId = null }) => {
});
};
const updateRowData = useCallback((id, updateResponse) => {
const newItems = items.map((o) => {
if (o.id === id) {
o.emailSendResponse = updateResponse;
}
return o;
})
setItems(newItems);
}, [items]);
const actionsBodyTemplate = (rowData) => {
return <Link to={`/mie-domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/>
</Link>
return <div className="appPageSection__tableActions lessGap">
<Link to={`/mie-domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/>
</Link>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
}
const statusBodyTemplate = (rowData) => {

View File

@@ -22,6 +22,7 @@ import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import { Dropdown } from 'primereact/dropdown';
import { Tag } from 'primereact/tag';
import { Calendar } from 'primereact/calendar';
import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
const SoccorsiPreInstructorTableAsync = ({ userId = null }) => {
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
@@ -94,10 +95,25 @@ const SoccorsiPreInstructorTableAsync = ({ userId = null }) => {
});
};
const updateRowData = useCallback((id, updateResponse) => {
const newItems = items.map((o) => {
if (o.id === id) {
o.emailSendResponse = updateResponse;
}
return o;
})
setItems(newItems);
}, [items]);
const actionsBodyTemplate = (rowData) => {
return <Link to={`/domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/>
</Link>
return <div className="appPageSection__tableActions lessGap">
<Link to={`/domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/>
</Link>
<SoccorsoResendEmails
emailsData={rowData.emailSendResponse}
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
</div>
}
const statusBodyTemplate = (rowData) => {

View File

@@ -139,7 +139,7 @@ const routes = ({ role, chosenCompanyId }) => {
{'ROLE_INSTRUCTOR_MANAGER' === role ? <DomandeInstructorManager/> : null}
</DefaultLayout>}/>
<Route path="/domande/:id/" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoApplicationPreview/> : null}
{'ROLE_SUPER_ADMIN' === role ? <DomandaEditPreInstructor/> : null}
{'ROLE_BENEFICIARY' === role ? <SoccorsoEditBeneficiario/> : null}
{'ROLE_CONFIDI' === role ? <SoccorsoEditBeneficiario/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandaEditPreInstructor/> : null}

View File

@@ -63,4 +63,8 @@ export default class ApplicationService {
static downloadCsvReport = (id, callback, errCallback, queryParams) => {
NetworkService.getBlob(`${API_BASE_URL}/application/call/${id}/csv`, callback, errCallback, queryParams);
};
static reAdmitApplication = (id, callback, errCallback) => {
NetworkService.put(`${API_BASE_URL}/application/${id}/readmit`, {}, callback, errCallback);
};
}

View File

@@ -0,0 +1,13 @@
import { NetworkService } from './network-service';
const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class EmailService {
static emailResend = (id, callback, errCallback) => {
NetworkService.post(`${API_BASE_URL}/email/resend`, {}, callback, errCallback, [
['userActionId', id]
]);
};
}

View File

@@ -1,5 +1,6 @@
import { storeGet, storeSet } from '../store';
import logMsgWithSentry from '../helpers/logMsgWithSentry';
import { isEmpty } from 'ramda';
export class NetworkService {
@@ -257,6 +258,10 @@ export class NetworkService {
url = url.substring(0, url.length - 1);
}
if (url.includes('user/me') && isEmpty(storeGet('getToken'))) {
return;
}
fetch(url, {
method: 'GET',
mode: 'cors',

View File

@@ -13,18 +13,12 @@ export const actionsAlpha = ({ set, get }) => ({
export const actionsBeta = ({ set, get }) => ({
setAuthData: ({ userData, token }) => {
set('state', (draft) => {
draft.userData = userData;
draft.token = token;
return draft;
});
set('token', token);
set('userData', userData);
},
doLogout: () => {
set('state', (draft) => {
draft.userData = {};
draft.token = '';
return draft;
});
set('token', '');
set('userData', {});
},
removeElement: (id) => {
const elements = get('formElements');