Merge branch 'master' of github.com:Kitzanos/GEPAFIN-FE
This commit is contained in:
175
package.json
175
package.json
@@ -1,90 +1,91 @@
|
|||||||
{
|
{
|
||||||
"name": "bflows-gepafin",
|
"name": "bflows-gepafin",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "7.21.11",
|
||||||
"@babel/preset-react": "7.26.3",
|
"@babel/preset-react": "7.26.3",
|
||||||
"@date-fns/tz": "1.2.0",
|
"@date-fns/tz": "1.2.0",
|
||||||
"@emailjs/browser": "4.4.1",
|
"@emailjs/browser": "4.4.1",
|
||||||
"@number-flow/react": "0.5.9",
|
"@number-flow/react": "0.5.9",
|
||||||
"@sentry/browser": "9.11.0",
|
"@sentry/browser": "9.11.0",
|
||||||
"@stomp/stompjs": "7.1.1",
|
"@stomp/stompjs": "7.1.1",
|
||||||
"@tanstack/react-table": "8.21.2",
|
"@tanstack/react-table": "8.21.2",
|
||||||
"@wordpress/i18n": "5.21.0",
|
"@wordpress/i18n": "5.21.0",
|
||||||
"@wordpress/react-i18n": "4.21.0",
|
"@wordpress/react-i18n": "4.21.0",
|
||||||
"codice-fiscale-js": "2.3.22",
|
"codice-fiscale-js": "2.3.22",
|
||||||
"copy-to-clipboard": "3.3.3",
|
"copy-to-clipboard": "3.3.3",
|
||||||
"deep-object-diff": "1.1.9",
|
"deep-object-diff": "1.1.9",
|
||||||
"dompurify": "3.2.5",
|
"dompurify": "3.2.5",
|
||||||
"expression-language": "1.2.0",
|
"expression-language": "1.2.0",
|
||||||
"fast-deep-equal": "3.1.3",
|
"fast-deep-equal": "3.1.3",
|
||||||
"hotkeys-js": "3.13.9",
|
"hotkeys-js": "3.13.9",
|
||||||
"html-react-parser": "5.2.3",
|
"html-react-parser": "5.2.3",
|
||||||
"jwt-decode": "4.0.0",
|
"jwt-decode": "4.0.0",
|
||||||
"klona": "2.0.6",
|
"klona": "2.0.6",
|
||||||
"leader-line-new": "1.1.9",
|
"leader-line-new": "1.1.9",
|
||||||
"luxon": "3.6.1",
|
"luxon": "3.6.1",
|
||||||
"mathjs": "14.4.0",
|
"mathjs": "14.4.0",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"object-path-immutable": "4.1.2",
|
"object-path-immutable": "4.1.2",
|
||||||
"primeicons": "7.0.0",
|
"primeicons": "7.0.0",
|
||||||
"primereact": "10.9.4",
|
"primereact": "10.9.4",
|
||||||
"quill": "2.0.3",
|
"quill": "2.0.3",
|
||||||
"ramda": "0.30.1",
|
"ramda": "0.30.1",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dnd": "16.0.1",
|
"react-dnd": "16.0.1",
|
||||||
"react-dnd-html5-backend": "16.0.1",
|
"react-dnd-html5-backend": "16.0.1",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-hook-form": "7.55.0",
|
"react-hook-form": "7.55.0",
|
||||||
"react-router-dom": "7.5.0",
|
"react-router-dom": "7.5.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"recharts": "2.15.2",
|
"recharts": "2.15.2",
|
||||||
"sockjs-client": "1.6.1",
|
"sockjs-client": "1.6.1",
|
||||||
"validate.js": "0.13.1",
|
"validate.js": "0.13.1",
|
||||||
"zustand": "5.0.3",
|
"zustand": "5.0.3",
|
||||||
"zustand-x": "6.1.0"
|
"zustand-x": "6.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.27.0",
|
"@babel/cli": "7.27.0",
|
||||||
"@babel/core": "7.26.10",
|
"@babel/core": "7.26.10",
|
||||||
"@babel/plugin-syntax-jsx": "7.25.9",
|
"@babel/plugin-syntax-jsx": "7.25.9",
|
||||||
"@wordpress/babel-plugin-makepot": "6.21.0",
|
"@wordpress/babel-plugin-makepot": "6.21.0",
|
||||||
"babel-plugin-macros": "3.1.0",
|
"babel-plugin-macros": "3.1.0",
|
||||||
"node-wp-i18n": "1.2.7",
|
"node-wp-i18n": "1.2.7",
|
||||||
"sass": "1.86.3",
|
"sass": "1.86.3",
|
||||||
"sass-loader": "16.0.5"
|
"sass-loader": "16.0.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "GENERATE_SOURCEMAP=false react-scripts start",
|
"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",
|
"start2": "react-scripts start",
|
||||||
"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",
|
"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",
|
||||||
"build": "react-scripts build",
|
"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: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": "react-scripts build",
|
||||||
"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",
|
"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",
|
||||||
"test": "react-scripts test",
|
"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",
|
||||||
"eject": "react-scripts eject",
|
"test": "react-scripts test",
|
||||||
"make-pot": "wpi18n makepot --domain-path=languages --domain=gepafin"
|
"eject": "react-scripts eject",
|
||||||
},
|
"make-pot": "wpi18n makepot --domain-path=languages --domain=gepafin"
|
||||||
"eslintConfig": {
|
},
|
||||||
"extends": [
|
"eslintConfig": {
|
||||||
"react-app",
|
"extends": [
|
||||||
"react-app/jest"
|
"react-app",
|
||||||
],
|
"react-app/jest"
|
||||||
"rules": {
|
],
|
||||||
"react-hooks/exhaustive-deps": "off"
|
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -295,8 +295,9 @@
|
|||||||
.appPageSection__message {
|
.appPageSection__message {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
background: rgba(255, 242, 226, 0.7);
|
background: rgba(242, 242, 242, 0.7);
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 0 0 0 6px;
|
border-width: 0 0 0 6px;
|
||||||
padding: 1.25rem 1.75rem;
|
padding: 1.25rem 1.75rem;
|
||||||
@@ -409,10 +410,18 @@
|
|||||||
.appPageSection__actions {
|
.appPageSection__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
padding: 24px 0 48px;
|
padding: 24px 0 0;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.appPageSection .appPageSection__actions:last-of-type {
|
||||||
|
padding-bottom: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appPageSection__actions:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.appPageSection__tableActions {
|
.appPageSection__tableActions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import getPropeMimeLabels from '../../../../helpers/getPropeMimeLabels';
|
|||||||
import { FileUpload } from 'primereact/fileupload';
|
import { FileUpload } from 'primereact/fileupload';
|
||||||
import { Tag } from 'primereact/tag';
|
import { Tag } from 'primereact/tag';
|
||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
|
import { Messages } from 'primereact/messages';
|
||||||
|
|
||||||
import { defaultMaxFileSize, mimeTypes } from '../../../../configData';
|
import { defaultMaxFileSize, mimeTypes } from '../../../../configData';
|
||||||
import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText';
|
import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText';
|
||||||
@@ -47,24 +48,48 @@ const Fileupload = ({
|
|||||||
const [acceptFormats, setAcceptFormats] = useState('');
|
const [acceptFormats, setAcceptFormats] = useState('');
|
||||||
const [formatsForInput, setFormatsForInput] = useState('');
|
const [formatsForInput, setFormatsForInput] = useState('');
|
||||||
const inputRef = useRef();
|
const inputRef = useRef();
|
||||||
|
const messagesRef = useRef(null);
|
||||||
|
|
||||||
const customBase64Uploader = (event) => {
|
const customBase64Uploader = (event) => {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
|
const filesToUpload = [];
|
||||||
|
const uploadedFiles = inputRef.current ? inputRef.current.getUploadedFiles() : [];
|
||||||
|
const currentFiles = stateFieldData;
|
||||||
|
|
||||||
for (const file of event.files) {
|
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') {
|
if (data.status === 'SUCCESS') {
|
||||||
setStateFieldData(data.data);
|
setStateFieldData(prevState => [...prevState, ...data.data]);
|
||||||
const uploadedFiles = inputRef.current.getUploadedFiles();
|
const currentUploadedFiles = inputRef.current.getUploadedFiles() || [];
|
||||||
setDataFn(fieldName, [...uploadedFiles, ...data.data], { shouldValidate: true });
|
|
||||||
inputRef.current.setFiles([]);
|
inputRef.current.setFiles([]);
|
||||||
|
|
||||||
|
setDataFn(fieldName, [...currentUploadedFiles, ...data.data], { shouldValidate: true });
|
||||||
|
|
||||||
saveFormCallback();
|
saveFormCallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,6 +229,7 @@ const Fileupload = ({
|
|||||||
return (
|
return (
|
||||||
sourceId || sourceId === 0
|
sourceId || sourceId === 0
|
||||||
? <>
|
? <>
|
||||||
|
<Messages ref={messagesRef} />
|
||||||
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
|
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
|
||||||
{label}{config.required || config.isRequired ?
|
{label}{config.required || config.isRequired ?
|
||||||
<span className="appForm__field--required">*</span> : null}
|
<span className="appForm__field--required">*</span> : null}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import { FileUpload } from 'primereact/fileupload';
|
|||||||
import { Tag } from 'primereact/tag';
|
import { Tag } from 'primereact/tag';
|
||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import { head, isEmpty } from 'ramda';
|
import { head, isEmpty } from 'ramda';
|
||||||
|
import { Messages } from 'primereact/messages';
|
||||||
|
|
||||||
|
|
||||||
import { defaultMaxFileSize, mimeTypes } from '../../../../configData';
|
import { defaultMaxFileSize, mimeTypes } from '../../../../configData';
|
||||||
import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText';
|
import getFormatedFileSizeText from '../../../../helpers/getFormatedFileSizeText';
|
||||||
@@ -42,24 +44,50 @@ const FileuploadAsync = ({
|
|||||||
const [acceptFormats, setAcceptFormats] = useState('');
|
const [acceptFormats, setAcceptFormats] = useState('');
|
||||||
const [formatsForInput, setFormatsForInput] = useState('');
|
const [formatsForInput, setFormatsForInput] = useState('');
|
||||||
const inputRef = useRef();
|
const inputRef = useRef();
|
||||||
|
const messagesRef = useRef(null);
|
||||||
|
|
||||||
const customBase64Uploader = (event) => {
|
const customBase64Uploader = (event) => {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
|
const filesToUpload = [];
|
||||||
|
const uploadedFiles = inputRef.current ? inputRef.current.getUploadedFiles() : [];
|
||||||
|
const currentFiles = stateFieldData;
|
||||||
|
|
||||||
for (const file of event.files) {
|
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') {
|
if (data.status === 'SUCCESS') {
|
||||||
setStateFieldData(data.data);
|
setStateFieldData(prevState => [...prevState, ...data.data]);
|
||||||
const uploadedFiles = inputRef.current.getUploadedFiles();
|
const currentUploadedFiles = inputRef.current.getUploadedFiles() || [];
|
||||||
setDataFn(fieldName, [...uploadedFiles, ...data.data], { shouldValidate: true });
|
|
||||||
|
inputRef.current.setUploadedFiles([...currentUploadedFiles, ...data.data]);
|
||||||
inputRef.current.setFiles([]);
|
inputRef.current.setFiles([]);
|
||||||
|
|
||||||
|
setDataFn(fieldName, [...currentUploadedFiles, ...data.data], { shouldValidate: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +214,7 @@ const FileuploadAsync = ({
|
|||||||
return (
|
return (
|
||||||
sourceId && sourceId !== 0
|
sourceId && sourceId !== 0
|
||||||
? <>
|
? <>
|
||||||
|
<Messages ref={messagesRef} />
|
||||||
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
|
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
|
||||||
{label}{config.required ? '*' : null}
|
{label}{config.required ? '*' : null}
|
||||||
{acceptFormats ? ' (' + getPropeMimeLabels(accept) + ')' : null}
|
{acceptFormats ? ' (' + getPropeMimeLabels(accept) + ')' : null}
|
||||||
|
|||||||
@@ -263,3 +263,10 @@ export const classificationType = [
|
|||||||
'idTipoprotocollo': 3
|
'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)'
|
||||||
|
}
|
||||||
@@ -115,23 +115,43 @@ const AddCompany = () => {
|
|||||||
|
|
||||||
const checkVatCallback = (data) => {
|
const checkVatCallback = (data) => {
|
||||||
if (data.status === 'SUCCESS') {
|
if (data.status === 'SUCCESS') {
|
||||||
|
const version = data.data.version;
|
||||||
const resp = data.data.vatCheckResponse.data;
|
const resp = data.data.vatCheckResponse.data;
|
||||||
if (!isEmpty(resp)) {
|
if (!isEmpty(resp)) {
|
||||||
const firstItem = resp[0];
|
let formData = {};
|
||||||
const {
|
|
||||||
taxCode, vatCode, address, companyName, pec
|
|
||||||
} = firstItem;
|
|
||||||
const { streetName, zipCode, town } = address?.registeredOffice;
|
|
||||||
|
|
||||||
const formData = {
|
if (version === 'V2') {
|
||||||
cap: zipCode,
|
const firstItem = resp[0];
|
||||||
pec,
|
const {
|
||||||
email: pec,
|
taxCode, vatCode, address, companyName, pec
|
||||||
city: town,
|
} = firstItem;
|
||||||
codiceFiscale: taxCode ? taxCode : vatCode,
|
const { streetName, zipCode, town } = address?.registeredOffice;
|
||||||
address: streetName,
|
|
||||||
vatNumber: vatCode,
|
formData = {
|
||||||
companyName
|
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]));
|
Object.keys(formData).map(k => setValue(k, formData[k]));
|
||||||
setVatCheckResponse(data.data.vatCheckResponse);
|
setVatCheckResponse(data.data.vatCheckResponse);
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import { uniq } from 'ramda';
|
|||||||
// api
|
// api
|
||||||
import BandoService from '../../../../service/bando-service';
|
import BandoService from '../../../../service/bando-service';
|
||||||
|
|
||||||
|
// tools
|
||||||
|
import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString';
|
||||||
|
import getTimeFromISOstring from '../../../../helpers/getTimeFromISOstring';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { FilterMatchMode, FilterOperator } from 'primereact/api';
|
import { FilterMatchMode, FilterOperator } from 'primereact/api';
|
||||||
import { DataTable } from 'primereact/datatable';
|
import { DataTable } from 'primereact/datatable';
|
||||||
@@ -13,10 +17,8 @@ import { Button } from 'primereact/button';
|
|||||||
import { Calendar } from 'primereact/calendar';
|
import { Calendar } from 'primereact/calendar';
|
||||||
import ProperBandoLabel from '../../../../components/ProperBandoLabel';
|
import ProperBandoLabel from '../../../../components/ProperBandoLabel';
|
||||||
import { Link } from 'react-router-dom';
|
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 LatestBandiTable = () => {
|
||||||
const [items, setItems] = useState(null);
|
const [items, setItems] = useState(null);
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ import { Link } from 'react-router-dom';
|
|||||||
|
|
||||||
import translationStrings from '../../../../translationStringsForComponents';
|
import translationStrings from '../../../../translationStringsForComponents';
|
||||||
|
|
||||||
|
// store
|
||||||
|
import { storeGet, useStoreValue } from '../../../../store';
|
||||||
|
|
||||||
// api
|
// api
|
||||||
import BandoService from '../../../../service/bando-service';
|
import BandoService from '../../../../service/bando-service';
|
||||||
|
import PreferredBandoService from '../../../../service/preferred-bando-service';
|
||||||
|
|
||||||
// tools
|
// tools
|
||||||
import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString';
|
import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString';
|
||||||
@@ -14,6 +18,7 @@ import getFormattedDateString from '../../../../helpers/getFormattedDateString';
|
|||||||
import getBandoLabel from '../../../../helpers/getBandoLabel';
|
import getBandoLabel from '../../../../helpers/getBandoLabel';
|
||||||
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
|
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
|
||||||
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
|
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
|
||||||
|
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { DataTable } from 'primereact/datatable';
|
import { DataTable } from 'primereact/datatable';
|
||||||
@@ -24,11 +29,9 @@ import { Dropdown } from 'primereact/dropdown';
|
|||||||
import { Tag } from 'primereact/tag';
|
import { Tag } from 'primereact/tag';
|
||||||
import { Calendar } from 'primereact/calendar';
|
import { Calendar } from 'primereact/calendar';
|
||||||
import { isNil } from 'ramda';
|
import { isNil } from 'ramda';
|
||||||
import { storeGet } from '../../../../store';
|
|
||||||
import PreferredBandoService from '../../../../service/preferred-bando-service';
|
|
||||||
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
|
|
||||||
|
|
||||||
const LatestBandiBeneficiarioTableAsync = () => {
|
const LatestBandiBeneficiarioTableAsync = () => {
|
||||||
|
const chosenCompanyId = useStoreValue('chosenCompanyId');
|
||||||
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
|
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
|
||||||
const [items, setItems] = useState(null);
|
const [items, setItems] = useState(null);
|
||||||
const [totalRecordsNum, setTotalRecordsNum] = useState(0);
|
const [totalRecordsNum, setTotalRecordsNum] = useState(0);
|
||||||
@@ -197,9 +200,19 @@ const LatestBandiBeneficiarioTableAsync = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLocalAsyncRequest(true);
|
setLocalAsyncRequest(true);
|
||||||
const paginationQuery = getPaginationQuery();
|
const paginationQuery = getPaginationQuery();
|
||||||
|
const role = storeGet('getRole');
|
||||||
|
|
||||||
BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks);
|
if (role === 'ROLE_CONFIDI') {
|
||||||
}, [lazyState]);
|
BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks, [
|
||||||
|
['companyId', chosenCompanyId],
|
||||||
|
['onlyConfidiCall', true]
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks, [
|
||||||
|
['companyId', chosenCompanyId]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}, [lazyState, chosenCompanyId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="appPageSection__table">
|
<div className="appPageSection__table">
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const LatestBandiTable = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const role = storeGet('getRole')
|
const role = storeGet('getRole');
|
||||||
|
|
||||||
if (role === 'ROLE_CONFIDI') {
|
if (role === 'ROLE_CONFIDI') {
|
||||||
BandoService.getBandi(getCallback, errGetCallbacks, [
|
BandoService.getBandi(getCallback, errGetCallbacks, [
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import { ProgressBar } from 'primereact/progressbar';
|
|||||||
|
|
||||||
const MyLatestSubmissionsTableAsync = () => {
|
const MyLatestSubmissionsTableAsync = () => {
|
||||||
const chosenCompanyId = useStoreValue('chosenCompanyId');
|
const chosenCompanyId = useStoreValue('chosenCompanyId');
|
||||||
const companies = useStoreValue('companies');
|
|
||||||
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
|
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
|
||||||
const [items, setItems] = useState(null);
|
const [items, setItems] = useState(null);
|
||||||
const [totalRecordsNum, setTotalRecordsNum] = useState(0);
|
const [totalRecordsNum, setTotalRecordsNum] = useState(0);
|
||||||
@@ -194,13 +193,15 @@ const MyLatestSubmissionsTableAsync = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLocalAsyncRequest(true);
|
if (chosenCompanyId && chosenCompanyId !== 0) {
|
||||||
const paginationQuery = getPaginationQuery();
|
setLocalAsyncRequest(true);
|
||||||
|
const paginationQuery = getPaginationQuery();
|
||||||
|
|
||||||
ApplicationService.getApplicationsPaginated(paginationQuery, getCallback, errGetCallbacks, [
|
ApplicationService.getApplicationsPaginated(paginationQuery, getCallback, errGetCallbacks, [
|
||||||
['companyId', chosenCompanyId]
|
['companyId', chosenCompanyId]
|
||||||
]);
|
]);
|
||||||
}, [lazyState, chosenCompanyId, companies]);
|
}
|
||||||
|
}, [lazyState, chosenCompanyId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="appPageSection__table">
|
<div className="appPageSection__table">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import DashboardService from '../../service/dashboard-service';
|
|||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import ErrorBoundary from '../../components/ErrorBoundary';
|
import ErrorBoundary from '../../components/ErrorBoundary';
|
||||||
import MyLatestSubmissionsTableAsync from '../DashboardBeneficiario/components/MyLatestSubmissionsTableAsync';
|
import MyLatestSubmissionsTableAsync from '../DashboardBeneficiario/components/MyLatestSubmissionsTableAsync';
|
||||||
import LatestBandiTable from '../DashboardBeneficiario/components/LatestBandiTable';
|
import LatestBandiBeneficiarioTableAsync from '../DashboardBeneficiario/components/LatestBandiBeneficiarioTableAsync';
|
||||||
|
|
||||||
const DashboardBeneficiarioConfidi = () => {
|
const DashboardBeneficiarioConfidi = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -108,7 +108,7 @@ const DashboardBeneficiarioConfidi = () => {
|
|||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
<h2>{__('Bandi disponibili', 'gepafin')}</h2>
|
<h2>{__('Bandi disponibili', 'gepafin')}</h2>
|
||||||
<ErrorBoundary><LatestBandiTable/></ErrorBoundary>
|
<ErrorBoundary><LatestBandiBeneficiarioTableAsync/></ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { Calendar } from 'primereact/calendar';
|
|||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
|
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
|
||||||
|
import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
|
||||||
|
|
||||||
|
|
||||||
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
||||||
@@ -73,7 +74,8 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
|
|||||||
|
|
||||||
const getCallback = (resp) => {
|
const getCallback = (resp) => {
|
||||||
if (resp.status === 'SUCCESS') {
|
if (resp.status === 'SUCCESS') {
|
||||||
const { body, totalRecords,
|
const {
|
||||||
|
body, totalRecords,
|
||||||
//currentPage, totalPages, pageSize
|
//currentPage, totalPages, pageSize
|
||||||
} = resp.data;
|
} = resp.data;
|
||||||
setTotalRecordsNum(totalRecords);
|
setTotalRecordsNum(totalRecords);
|
||||||
@@ -114,7 +116,8 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
|
|||||||
}
|
}
|
||||||
setLazyState({ ...lazyState, filters, first: 0 });
|
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) => {
|
const dateFilterTemplate = (options) => {
|
||||||
@@ -130,22 +133,43 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
|
|||||||
return getFormattedDateString(rowData.evaluationEndDate);
|
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) => {
|
const actionsBodyTemplate = (rowData) => {
|
||||||
if (rowData.status === 'AWAITING') {
|
if (rowData.status === 'AWAITING') {
|
||||||
return <Button
|
return <div className="appPageSection__tableActions lessGap">
|
||||||
severity="info"
|
<Button
|
||||||
onClick={() => handleInitiateEvaluation(rowData.id)}
|
severity="info"
|
||||||
label={__('Valuta', 'gepafin')}
|
onClick={() => handleInitiateEvaluation(rowData.id)}
|
||||||
icon="pi pi-eye"
|
label={__('Valuta', 'gepafin')}
|
||||||
size="small"
|
icon="pi pi-eye"
|
||||||
iconPos="right"/>
|
size="small"
|
||||||
|
iconPos="right"/>
|
||||||
|
<SoccorsoResendEmails
|
||||||
|
emailsData={rowData.emailSendResponse}
|
||||||
|
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
|
||||||
|
</div>
|
||||||
} else {
|
} else {
|
||||||
const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId
|
const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId
|
||||||
? __('Valuta', 'gepafin')
|
? __('Valuta', 'gepafin')
|
||||||
: __('Mostra', 'gepafin');
|
: __('Mostra', 'gepafin');
|
||||||
return <Link to={`/mie-domande/${rowData.applicationId}`}>
|
return <div className="appPageSection__tableActions lessGap">
|
||||||
<Button severity="info" label={label} icon="pi pi-eye" size="small" iconPos="right"/>
|
<Link
|
||||||
</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 = () => {
|
const renderHeader = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-content-between">
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -268,7 +293,7 @@ const MieDomandeTableInstructorManagerAsync = ({ userId = null, statuses = [] })
|
|||||||
style={{ minWidth: '7rem' }} body={statusBodyTemplate}
|
style={{ minWidth: '7rem' }} body={statusBodyTemplate}
|
||||||
filter
|
filter
|
||||||
filterMatchModeOptions={translationStrings.statusFilterOptions}
|
filterMatchModeOptions={translationStrings.statusFilterOptions}
|
||||||
filterElement={statusFilterTemplate} />
|
filterElement={statusFilterTemplate}/>
|
||||||
<Column header={__('Azioni', 'gepafin')}
|
<Column header={__('Azioni', 'gepafin')}
|
||||||
body={actionsBodyTemplate}/>
|
body={actionsBodyTemplate}/>
|
||||||
</DataTable>
|
</DataTable>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { useStoreValue } from '../../../../store';
|
|||||||
import getFormattedDateString from '../../../../helpers/getFormattedDateString';
|
import getFormattedDateString from '../../../../helpers/getFormattedDateString';
|
||||||
import getBandoLabel from '../../../../helpers/getBandoLabel';
|
import getBandoLabel from '../../../../helpers/getBandoLabel';
|
||||||
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
|
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
|
||||||
|
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { DataTable } from 'primereact/datatable';
|
import { DataTable } from 'primereact/datatable';
|
||||||
@@ -25,7 +26,7 @@ import { Tag } from 'primereact/tag';
|
|||||||
import { Calendar } from 'primereact/calendar';
|
import { Calendar } from 'primereact/calendar';
|
||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
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;
|
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
||||||
@@ -73,7 +74,8 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
|
|||||||
|
|
||||||
const getCallback = (resp) => {
|
const getCallback = (resp) => {
|
||||||
if (resp.status === 'SUCCESS') {
|
if (resp.status === 'SUCCESS') {
|
||||||
const { body, totalRecords,
|
const {
|
||||||
|
body, totalRecords,
|
||||||
//currentPage, totalPages, pageSize
|
//currentPage, totalPages, pageSize
|
||||||
} = resp.data;
|
} = resp.data;
|
||||||
setTotalRecordsNum(totalRecords);
|
setTotalRecordsNum(totalRecords);
|
||||||
@@ -114,7 +116,8 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
|
|||||||
}
|
}
|
||||||
setLazyState({ ...lazyState, filters, first: 0 });
|
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) => {
|
const dateFilterTemplate = (options) => {
|
||||||
@@ -130,22 +133,48 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
|
|||||||
return getFormattedDateString(rowData.evaluationEndDate);
|
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) => {
|
const actionsBodyTemplate = (rowData) => {
|
||||||
if (rowData.status === 'AWAITING') {
|
if (rowData.status === 'AWAITING') {
|
||||||
return <Button
|
return <div className="appPageSection__tableActions lessGap">
|
||||||
severity="info"
|
<Button
|
||||||
onClick={() => handleInitiateEvaluation(rowData.id)}
|
severity="info"
|
||||||
label={__('Valuta', 'gepafin')}
|
onClick={() => handleInitiateEvaluation(rowData.id)}
|
||||||
icon="pi pi-eye"
|
label={__('Valuta', 'gepafin')}
|
||||||
size="small"
|
icon="pi pi-eye"
|
||||||
iconPos="right"/>
|
size="small"
|
||||||
|
iconPos="right"/>
|
||||||
|
<SoccorsoResendEmails
|
||||||
|
emailsData={rowData.emailSendResponse}
|
||||||
|
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
|
||||||
|
</div>
|
||||||
} else {
|
} else {
|
||||||
const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId
|
const label = ['OPEN', 'SOCCORSO'].includes(rowData.status) && userData.id === rowData.userId
|
||||||
? __('Valuta', 'gepafin')
|
? __('Valuta', 'gepafin')
|
||||||
: __('Mostra', 'gepafin');
|
: __('Mostra', 'gepafin');
|
||||||
return <Link to={`/domande/${rowData.applicationId}`}>
|
return (
|
||||||
<Button severity="info" label={label} icon="pi pi-eye" size="small" iconPos="right"/>
|
<div className="appPageSection__tableActions lessGap">
|
||||||
</Link>
|
<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 = () => {
|
const renderHeader = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-content-between">
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -268,7 +298,7 @@ const DomandeTablePreInstructorAsync = ({ userId = null, statuses = [] }) => {
|
|||||||
style={{ minWidth: '7rem' }} body={statusBodyTemplate}
|
style={{ minWidth: '7rem' }} body={statusBodyTemplate}
|
||||||
filter
|
filter
|
||||||
filterMatchModeOptions={translationStrings.statusFilterOptions}
|
filterMatchModeOptions={translationStrings.statusFilterOptions}
|
||||||
filterElement={statusFilterTemplate} />
|
filterElement={statusFilterTemplate}/>
|
||||||
<Column header={__('Azioni', 'gepafin')}
|
<Column header={__('Azioni', 'gepafin')}
|
||||||
body={actionsBodyTemplate}/>
|
body={actionsBodyTemplate}/>
|
||||||
</DataTable>
|
</DataTable>
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ import RepeaterFields from '../DomandaEditPreInstructor/components/RepeaterField
|
|||||||
import ApplicationInfo from '../DomandaEditPreInstructor/components/ApplicationInfo';
|
import ApplicationInfo from '../DomandaEditPreInstructor/components/ApplicationInfo';
|
||||||
import ApplicationDownloadFiles from '../DomandaEditPreInstructor/components/ApplicationDownloadFiles';
|
import ApplicationDownloadFiles from '../DomandaEditPreInstructor/components/ApplicationDownloadFiles';
|
||||||
import FormField from '../../components/FormField';
|
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_EVALUATION_FLOW_ID = process.env.REACT_APP_EVALUATION_FLOW_ID;
|
||||||
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
||||||
@@ -85,6 +87,7 @@ const DomandaEditInstructorManager = () => {
|
|||||||
const [formData, setFormData] = useState([]);
|
const [formData, setFormData] = useState([]);
|
||||||
const [formId, setFormId] = useState(0);
|
const [formId, setFormId] = useState(0);
|
||||||
const [formInitialData, setFormInitialData] = useState(null);
|
const [formInitialData, setFormInitialData] = useState(null);
|
||||||
|
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -785,6 +788,11 @@ const DomandaEditInstructorManager = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateStatusOfAppl = useCallback((newStatus) => {
|
||||||
|
const newData = wrap(data).set(['applicationStatus'], newStatus).value();
|
||||||
|
setData(newData);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
const actionBtns = () => {
|
const actionBtns = () => {
|
||||||
return <div className="appPageSection__actions">
|
return <div className="appPageSection__actions">
|
||||||
{['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus)
|
{['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus)
|
||||||
@@ -873,6 +881,29 @@ const DomandaEditInstructorManager = () => {
|
|||||||
</div>
|
</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(() => {
|
useEffect(() => {
|
||||||
let updatedFormValues = klona(formValues);
|
let updatedFormValues = klona(formValues);
|
||||||
let context = {};
|
let context = {};
|
||||||
@@ -964,14 +995,21 @@ const DomandaEditInstructorManager = () => {
|
|||||||
? <div className="appPage__content">
|
? <div className="appPage__content">
|
||||||
<ApplicationInfo data={data}/>
|
<ApplicationInfo data={data}/>
|
||||||
|
|
||||||
|
<div className="appPageSection">
|
||||||
|
{motivationMsg()}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="appPageSection__hr">
|
<div className="appPageSection__hr">
|
||||||
<span>{__('Azioni rapide', 'gepafin')}</span>
|
<span>{__('Azioni rapide', 'gepafin')}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
{actionBtns()}
|
{actionBtns()}
|
||||||
|
{auxActions()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="appPageSection__hr"></div>
|
||||||
|
|
||||||
<ApplicationDownloadFiles id={id}/>
|
<ApplicationDownloadFiles id={id}/>
|
||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
@@ -996,6 +1034,23 @@ const DomandaEditInstructorManager = () => {
|
|||||||
sourceName="evaluation"/>
|
sourceName="evaluation"/>
|
||||||
</div>
|
</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'
|
{data.evaluationVersion === 'V2'
|
||||||
? <div className="appPageSection">
|
? <div className="appPageSection">
|
||||||
<h2>{__('Documenti allegati', 'gepafin')}</h2>
|
<h2>{__('Documenti allegati', 'gepafin')}</h2>
|
||||||
@@ -1108,21 +1163,6 @@ const DomandaEditInstructorManager = () => {
|
|||||||
</div>)}
|
</div>)}
|
||||||
</div>
|
</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>
|
||||||
<div>
|
<div>
|
||||||
<h3>{__('Documenti allegati', 'gepafin')}</h3>
|
<h3>{__('Documenti allegati', 'gepafin')}</h3>
|
||||||
@@ -1241,6 +1281,7 @@ const DomandaEditInstructorManager = () => {
|
|||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
{actionBtns()}
|
{actionBtns()}
|
||||||
|
{auxActions()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -53,6 +53,8 @@ import RepeaterFields from './components/RepeaterFields';
|
|||||||
import ApplicationInfo from './components/ApplicationInfo';
|
import ApplicationInfo from './components/ApplicationInfo';
|
||||||
import ApplicationDownloadFiles from './components/ApplicationDownloadFiles';
|
import ApplicationDownloadFiles from './components/ApplicationDownloadFiles';
|
||||||
import FormField from '../../components/FormField';
|
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_EVALUATION_FLOW_ID = process.env.REACT_APP_EVALUATION_FLOW_ID;
|
||||||
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
|
||||||
@@ -86,6 +88,7 @@ const DomandaEditPreInstructor = () => {
|
|||||||
const [formData, setFormData] = useState([]);
|
const [formData, setFormData] = useState([]);
|
||||||
const [formId, setFormId] = useState(0);
|
const [formId, setFormId] = useState(0);
|
||||||
const [formInitialData, setFormInitialData] = useState(null);
|
const [formInitialData, setFormInitialData] = useState(null);
|
||||||
|
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -592,7 +595,7 @@ const DomandaEditPreInstructor = () => {
|
|||||||
|
|
||||||
const shouldDisableField = useCallback((fieldName) => {
|
const shouldDisableField = useCallback((fieldName) => {
|
||||||
return !['EVALUATION', 'ADMISSIBLE'].includes(data.applicationStatus)
|
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))
|
|| (fieldName === 'files' && !isEmpty(data.amendmentDetails))
|
||||||
}, [data.applicationStatus]);
|
}, [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 = () => {
|
const actionBtns = () => {
|
||||||
return <div className="appPageSection__actions">
|
return <div className="appPageSection__actions">
|
||||||
{['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus)
|
{['EVALUATION', 'SOCCORSO', 'CLOSE'].includes(data.applicationStatus)
|
||||||
@@ -874,6 +882,29 @@ const DomandaEditPreInstructor = () => {
|
|||||||
</div>
|
</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(() => {
|
useEffect(() => {
|
||||||
let updatedFormValues = klona(formValues);
|
let updatedFormValues = klona(formValues);
|
||||||
let context = {};
|
let context = {};
|
||||||
@@ -966,14 +997,21 @@ const DomandaEditPreInstructor = () => {
|
|||||||
? <div className="appPage__content">
|
? <div className="appPage__content">
|
||||||
<ApplicationInfo data={data}/>
|
<ApplicationInfo data={data}/>
|
||||||
|
|
||||||
|
<div className="appPageSection">
|
||||||
|
{motivationMsg()}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="appPageSection__hr">
|
<div className="appPageSection__hr">
|
||||||
<span>{__('Azioni rapide', 'gepafin')}</span>
|
<span>{__('Azioni rapide', 'gepafin')}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
{actionBtns()}
|
{actionBtns()}
|
||||||
|
{auxActions()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="appPageSection__hr"></div>
|
||||||
|
|
||||||
<ApplicationDownloadFiles id={id}/>
|
<ApplicationDownloadFiles id={id}/>
|
||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
@@ -998,6 +1036,23 @@ const DomandaEditPreInstructor = () => {
|
|||||||
sourceName="evaluation"/>
|
sourceName="evaluation"/>
|
||||||
</div>
|
</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'
|
{data.evaluationVersion === 'V2'
|
||||||
? <div className="appPageSection">
|
? <div className="appPageSection">
|
||||||
<h2>{__('Documenti allegati', 'gepafin')}</h2>
|
<h2>{__('Documenti allegati', 'gepafin')}</h2>
|
||||||
@@ -1110,21 +1165,6 @@ const DomandaEditPreInstructor = () => {
|
|||||||
</div>)}
|
</div>)}
|
||||||
</div>
|
</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>
|
||||||
<div>
|
<div>
|
||||||
<h3>{__('Documenti allegati', 'gepafin')}</h3>
|
<h3>{__('Documenti allegati', 'gepafin')}</h3>
|
||||||
@@ -1243,6 +1283,7 @@ const DomandaEditPreInstructor = () => {
|
|||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
{actionBtns()}
|
{actionBtns()}
|
||||||
|
{auxActions()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ const Login = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const validateCallback = (data) => {
|
const validateCallback = (data) => {
|
||||||
//console.log('login validateCallback', data)
|
|
||||||
if (data.status === 'SUCCESS') {
|
if (data.status === 'SUCCESS') {
|
||||||
storeSet('setAuthData', {
|
storeSet('setAuthData', {
|
||||||
token: data.data.token,
|
token: data.data.token,
|
||||||
@@ -107,7 +106,7 @@ const Login = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const temp_token = searchParams.get('temp_token');
|
const temp_token = searchParams.get('temp_token');
|
||||||
//console.log('login temp_token', temp_token);
|
|
||||||
if (!isNil(temp_token) && !isEmpty(temp_token)) {
|
if (!isNil(temp_token) && !isEmpty(temp_token)) {
|
||||||
errorMsgs.current.clear();
|
errorMsgs.current.clear();
|
||||||
AuthenticationService.validateExistingUser(temp_token, validateCallback, validateError);
|
AuthenticationService.validateExistingUser(temp_token, validateCallback, validateError);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const SoccorsoAddPreInstructor = () => {
|
|||||||
const [data, setData] = useState({});
|
const [data, setData] = useState({});
|
||||||
const [evaluationId, setEvaluationId] = useState(0);
|
const [evaluationId, setEvaluationId] = useState(0);
|
||||||
const [formData, setFormData] = useState({});
|
const [formData, setFormData] = useState({});
|
||||||
const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false)
|
const [isVisibleConfirmDialog, setIsVisibleConfirmDialog] = useState(false);
|
||||||
const toast = useRef(null);
|
const toast = useRef(null);
|
||||||
|
|
||||||
const goToEvaluationPage = () => {
|
const goToEvaluationPage = () => {
|
||||||
|
|||||||
@@ -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 { __ } from '@wordpress/i18n';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
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 { wrap } from 'object-path-immutable';
|
||||||
import { klona } from 'klona';
|
import { klona } from 'klona';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
@@ -28,6 +28,7 @@ import FormField from '../../components/FormField';
|
|||||||
import { Editor } from 'primereact/editor';
|
import { Editor } from 'primereact/editor';
|
||||||
import { InputNumber } from 'primereact/inputnumber';
|
import { InputNumber } from 'primereact/inputnumber';
|
||||||
import SoccorsoComunications from '../SoccorsoEditPreInstructor/components/SoccorsoComunications';
|
import SoccorsoComunications from '../SoccorsoEditPreInstructor/components/SoccorsoComunications';
|
||||||
|
import SoccorsoResendEmails from '../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
|
||||||
|
|
||||||
|
|
||||||
const SoccorsoEditInstructorManager = () => {
|
const SoccorsoEditInstructorManager = () => {
|
||||||
@@ -43,6 +44,7 @@ const SoccorsoEditInstructorManager = () => {
|
|||||||
const [internalNote, setInternalNote] = useState('');
|
const [internalNote, setInternalNote] = useState('');
|
||||||
const toast = useRef(null);
|
const toast = useRef(null);
|
||||||
const [formInitialData, setFormInitialData] = useState({});
|
const [formInitialData, setFormInitialData] = useState({});
|
||||||
|
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -346,6 +348,11 @@ const SoccorsoEditInstructorManager = () => {
|
|||||||
setIsLoadingReminding(false);
|
setIsLoadingReminding(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateEmailSendResponses = useCallback((newEmailData) => {
|
||||||
|
const newData = wrap(data).set(['emailSendResponse'], newEmailData).value();
|
||||||
|
setData(newData);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (formInitialData) {
|
if (formInitialData) {
|
||||||
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
|
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
|
||||||
@@ -506,7 +513,8 @@ const SoccorsoEditInstructorManager = () => {
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={sendReminder}
|
onClick={sendReminder}
|
||||||
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)}
|
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)
|
||||||
|
|| (!isNil(emailSendResponse) && !isEmpty(emailSendResponse))}
|
||||||
outlined
|
outlined
|
||||||
label={__('Invia Sollecito', 'gepafin')}
|
label={__('Invia Sollecito', 'gepafin')}
|
||||||
icon="pi pi-send"
|
icon="pi pi-send"
|
||||||
@@ -532,6 +540,11 @@ const SoccorsoEditInstructorManager = () => {
|
|||||||
label={__('Chiudi Soccorso Istruttorio', 'gepafin')}
|
label={__('Chiudi Soccorso Istruttorio', 'gepafin')}
|
||||||
icon="pi pi-times" iconPos="right"/>
|
icon="pi pi-times" iconPos="right"/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="appPageSection__actions">
|
||||||
|
<SoccorsoResendEmails
|
||||||
|
emailsData={emailSendResponse}
|
||||||
|
setDataEmailsSoccorso={updateEmailSendResponses}/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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 { __ } from '@wordpress/i18n';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
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 { wrap } from 'object-path-immutable';
|
||||||
import { klona } from 'klona';
|
import { klona } from 'klona';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
@@ -28,6 +28,7 @@ import FormField from '../../components/FormField';
|
|||||||
import { Editor } from 'primereact/editor';
|
import { Editor } from 'primereact/editor';
|
||||||
import { InputNumber } from 'primereact/inputnumber';
|
import { InputNumber } from 'primereact/inputnumber';
|
||||||
import SoccorsoComunications from './components/SoccorsoComunications';
|
import SoccorsoComunications from './components/SoccorsoComunications';
|
||||||
|
import SoccorsoResendEmails from './components/SoccorsoResendEmails';
|
||||||
|
|
||||||
|
|
||||||
const SoccorsoEditPreInstructor = () => {
|
const SoccorsoEditPreInstructor = () => {
|
||||||
@@ -43,6 +44,7 @@ const SoccorsoEditPreInstructor = () => {
|
|||||||
const [internalNote, setInternalNote] = useState('');
|
const [internalNote, setInternalNote] = useState('');
|
||||||
const toast = useRef(null);
|
const toast = useRef(null);
|
||||||
const [formInitialData, setFormInitialData] = useState({});
|
const [formInitialData, setFormInitialData] = useState({});
|
||||||
|
const emailSendResponse = pathOr([], ['emailSendResponse'], data);
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -346,6 +348,11 @@ const SoccorsoEditPreInstructor = () => {
|
|||||||
setIsLoadingReminding(false);
|
setIsLoadingReminding(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateEmailSendResponses = useCallback((newEmailData) => {
|
||||||
|
const newData = wrap(data).set(['emailSendResponse'], newEmailData).value();
|
||||||
|
setData(newData);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (formInitialData) {
|
if (formInitialData) {
|
||||||
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
|
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
|
||||||
@@ -506,7 +513,8 @@ const SoccorsoEditPreInstructor = () => {
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={sendReminder}
|
onClick={sendReminder}
|
||||||
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)}
|
disabled={isLoadingReminding || ['CLOSE', 'EXPIRED'].includes(data.status)
|
||||||
|
|| (!isNil(emailSendResponse) && !isEmpty(emailSendResponse))}
|
||||||
outlined
|
outlined
|
||||||
label={__('Invia Sollecito', 'gepafin')}
|
label={__('Invia Sollecito', 'gepafin')}
|
||||||
icon="pi pi-send"
|
icon="pi pi-send"
|
||||||
@@ -532,6 +540,11 @@ const SoccorsoEditPreInstructor = () => {
|
|||||||
label={__('Chiudi Soccorso Istruttorio', 'gepafin')}
|
label={__('Chiudi Soccorso Istruttorio', 'gepafin')}
|
||||||
icon="pi pi-times" iconPos="right"/>
|
icon="pi pi-times" iconPos="right"/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="appPageSection__actions">
|
||||||
|
<SoccorsoResendEmails
|
||||||
|
emailsData={emailSendResponse}
|
||||||
|
setDataEmailsSoccorso={updateEmailSendResponses}/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { Dropdown } from 'primereact/dropdown';
|
|||||||
import { Tag } from 'primereact/tag';
|
import { Tag } from 'primereact/tag';
|
||||||
import { Calendar } from 'primereact/calendar';
|
import { Calendar } from 'primereact/calendar';
|
||||||
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
|
import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint';
|
||||||
|
import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
|
||||||
|
|
||||||
const SoccorsiInstructorManagerMioTableAsync = ({ userId = null }) => {
|
const SoccorsiInstructorManagerMioTableAsync = ({ userId = null }) => {
|
||||||
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
|
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) => {
|
const actionsBodyTemplate = (rowData) => {
|
||||||
return <Link to={`/mie-domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
|
return <div className="appPageSection__tableActions lessGap">
|
||||||
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/>
|
<Link to={`/mie-domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
|
||||||
</Link>
|
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/>
|
||||||
|
</Link>
|
||||||
|
<SoccorsoResendEmails
|
||||||
|
emailsData={rowData.emailSendResponse}
|
||||||
|
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusBodyTemplate = (rowData) => {
|
const statusBodyTemplate = (rowData) => {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import ProperBandoLabel from '../../../../components/ProperBandoLabel';
|
|||||||
import { Dropdown } from 'primereact/dropdown';
|
import { Dropdown } from 'primereact/dropdown';
|
||||||
import { Tag } from 'primereact/tag';
|
import { Tag } from 'primereact/tag';
|
||||||
import { Calendar } from 'primereact/calendar';
|
import { Calendar } from 'primereact/calendar';
|
||||||
|
import SoccorsoResendEmails from '../../../SoccorsoEditPreInstructor/components/SoccorsoResendEmails';
|
||||||
|
|
||||||
const SoccorsiPreInstructorTableAsync = ({ userId = null }) => {
|
const SoccorsiPreInstructorTableAsync = ({ userId = null }) => {
|
||||||
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
|
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) => {
|
const actionsBodyTemplate = (rowData) => {
|
||||||
return <Link to={`/domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
|
return <div className="appPageSection__tableActions lessGap">
|
||||||
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/>
|
<Link to={`/domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
|
||||||
</Link>
|
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small"/>
|
||||||
|
</Link>
|
||||||
|
<SoccorsoResendEmails
|
||||||
|
emailsData={rowData.emailSendResponse}
|
||||||
|
setDataEmailsSoccorso={(updateResponse) => updateRowData(rowData.id, updateResponse)}/>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusBodyTemplate = (rowData) => {
|
const statusBodyTemplate = (rowData) => {
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ const routes = ({ role, chosenCompanyId }) => {
|
|||||||
{'ROLE_INSTRUCTOR_MANAGER' === role ? <DomandeInstructorManager/> : null}
|
{'ROLE_INSTRUCTOR_MANAGER' === role ? <DomandeInstructorManager/> : null}
|
||||||
</DefaultLayout>}/>
|
</DefaultLayout>}/>
|
||||||
<Route path="/domande/:id/" element={<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_BENEFICIARY' === role ? <SoccorsoEditBeneficiario/> : null}
|
||||||
{'ROLE_CONFIDI' === role ? <SoccorsoEditBeneficiario/> : null}
|
{'ROLE_CONFIDI' === role ? <SoccorsoEditBeneficiario/> : null}
|
||||||
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandaEditPreInstructor/> : null}
|
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandaEditPreInstructor/> : null}
|
||||||
|
|||||||
@@ -63,4 +63,8 @@ export default class ApplicationService {
|
|||||||
static downloadCsvReport = (id, callback, errCallback, queryParams) => {
|
static downloadCsvReport = (id, callback, errCallback, queryParams) => {
|
||||||
NetworkService.getBlob(`${API_BASE_URL}/application/call/${id}/csv`, 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);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/service/email-service.js
Normal file
13
src/service/email-service.js
Normal 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]
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { storeGet, storeSet } from '../store';
|
import { storeGet, storeSet } from '../store';
|
||||||
import logMsgWithSentry from '../helpers/logMsgWithSentry';
|
import logMsgWithSentry from '../helpers/logMsgWithSentry';
|
||||||
|
import { isEmpty } from 'ramda';
|
||||||
|
|
||||||
export class NetworkService {
|
export class NetworkService {
|
||||||
|
|
||||||
@@ -257,6 +258,10 @@ export class NetworkService {
|
|||||||
url = url.substring(0, url.length - 1);
|
url = url.substring(0, url.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (url.includes('user/me') && isEmpty(storeGet('getToken'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
|
|||||||
@@ -13,18 +13,12 @@ export const actionsAlpha = ({ set, get }) => ({
|
|||||||
|
|
||||||
export const actionsBeta = ({ set, get }) => ({
|
export const actionsBeta = ({ set, get }) => ({
|
||||||
setAuthData: ({ userData, token }) => {
|
setAuthData: ({ userData, token }) => {
|
||||||
set('state', (draft) => {
|
set('token', token);
|
||||||
draft.userData = userData;
|
set('userData', userData);
|
||||||
draft.token = token;
|
|
||||||
return draft;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
doLogout: () => {
|
doLogout: () => {
|
||||||
set('state', (draft) => {
|
set('token', '');
|
||||||
draft.userData = {};
|
set('userData', {});
|
||||||
draft.token = '';
|
|
||||||
return draft;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
removeElement: (id) => {
|
removeElement: (id) => {
|
||||||
const elements = get('formElements');
|
const elements = get('formElements');
|
||||||
|
|||||||
Reference in New Issue
Block a user