Merge pull request #7 from Kitzanos/feature/67
Feature: preview application by ID by admin
This commit is contained in:
@@ -30,6 +30,10 @@
|
|||||||
line-height: normal;
|
line-height: normal;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.p-error {
|
||||||
|
color: var(--message-error-color)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
small {
|
small {
|
||||||
|
|||||||
@@ -312,7 +312,7 @@
|
|||||||
#f8d282 20px
|
#f8d282 20px
|
||||||
);
|
);
|
||||||
|
|
||||||
.p-button {
|
.p-button-outlined {
|
||||||
background: white;
|
background: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,17 @@ img {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blockingOverlay {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 999;
|
||||||
|
inset: 0;
|
||||||
|
background-color: rgba(255,255,255,0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
button[disabled] {
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 800px) {
|
@media (max-width: 800px) {
|
||||||
.inner {
|
.inner {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -106,19 +106,18 @@
|
|||||||
padding: 1rem 1.5rem;
|
padding: 1rem 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blockingOverlay {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 999;
|
|
||||||
inset: 0;
|
|
||||||
background-color: rgba(255,255,255,0.3)
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-accordion-header-text, .p-accordion-content {
|
.p-accordion-header-text, .p-accordion-content {
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.p-disabled, .p-disabled * {
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: auto;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
.p-inputgroup.flex-1 {
|
.p-inputgroup.flex-1 {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -606,8 +606,8 @@ const BandoApplication = () => {
|
|||||||
//console.log('validations', validations, o.name)
|
//console.log('validations', validations, o.name)
|
||||||
|
|
||||||
return ['paragraph'].includes(o.name) && text
|
return ['paragraph'].includes(o.name) && text
|
||||||
? <div>
|
? <div key={o.id}>
|
||||||
<div className="ql-editor" key={o.id}>
|
<div className="ql-editor">
|
||||||
{renderHtmlContent(text.value)}
|
{renderHtmlContent(text.value)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
412
src/pages/BandoApplicationPreview/index.js
Normal file
412
src/pages/BandoApplicationPreview/index.js
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
import React, { useState, useEffect, useRef, useMemo } from 'react';
|
||||||
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { head, isEmpty, pathOr } from 'ramda';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import 'quill/dist/quill.core.css';
|
||||||
|
|
||||||
|
// store
|
||||||
|
import { storeSet, useStore } from '../../store';
|
||||||
|
|
||||||
|
// api
|
||||||
|
import ApplicationService from '../../service/application-service';
|
||||||
|
|
||||||
|
// tools
|
||||||
|
import {
|
||||||
|
isPIVA,
|
||||||
|
isCodiceFiscale,
|
||||||
|
isCAP,
|
||||||
|
isIBAN,
|
||||||
|
isEmail,
|
||||||
|
isEmailPEC,
|
||||||
|
isUrl,
|
||||||
|
isMarcaDaBollo, minChecks, maxChecks, nonEmptyTables
|
||||||
|
} from '../../helpers/validators';
|
||||||
|
import renderHtmlContent from '../../helpers/renderHtmlContent';
|
||||||
|
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
||||||
|
|
||||||
|
// components
|
||||||
|
import { Skeleton } from 'primereact/skeleton';
|
||||||
|
import { Button } from 'primereact/button';
|
||||||
|
import FormField from '../../components/FormField';
|
||||||
|
import { Toast } from 'primereact/toast';
|
||||||
|
import { Messages } from 'primereact/messages';
|
||||||
|
import ApplicationSteps from '../BandoApplication/ApplicationSteps';
|
||||||
|
import BlockingOverlay from '../../components/BlockingOverlay';
|
||||||
|
|
||||||
|
const BandoApplicationPreview = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const [formData, setFormData] = useState([]);
|
||||||
|
const [formInitialData, setFormInitialData] = useState(null);
|
||||||
|
const [bandoTitle, setBandoTitle] = useState('');
|
||||||
|
const [bandoId, setBandoId] = useState(0);
|
||||||
|
const [formId, setFormId] = useState('');
|
||||||
|
const [totalSteps, setTotalSteps] = useState(0);
|
||||||
|
const [applicationStatus, setApplicationStatus] = useState('');
|
||||||
|
const [activeStep, setActiveStep] = useState(1);
|
||||||
|
const isAsyncRequest = useStore().main.isAsyncRequest();
|
||||||
|
const toast = useRef(null);
|
||||||
|
const formMsgs = useRef(null);
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
setValue,
|
||||||
|
trigger,
|
||||||
|
register,
|
||||||
|
getValues,
|
||||||
|
reset
|
||||||
|
} = useForm({
|
||||||
|
defaultValues: useMemo(() => {
|
||||||
|
return formInitialData ? formInitialData : {}
|
||||||
|
}, [formInitialData]),
|
||||||
|
mode: 'onChange'
|
||||||
|
});
|
||||||
|
const validationFns = {
|
||||||
|
isPIVA,
|
||||||
|
isCodiceFiscale,
|
||||||
|
isCAP,
|
||||||
|
isIBAN,
|
||||||
|
isEmail,
|
||||||
|
isEmailPEC,
|
||||||
|
isUrl,
|
||||||
|
isMarcaDaBollo,
|
||||||
|
minChecks,
|
||||||
|
maxChecks,
|
||||||
|
nonEmptyTables
|
||||||
|
}
|
||||||
|
const activeStepIndex = activeStep - 1;
|
||||||
|
const values = getValues();
|
||||||
|
|
||||||
|
const onValidate = () => {
|
||||||
|
const applId = getApplicationId();
|
||||||
|
storeSet.main.setAsyncRequest();
|
||||||
|
formMsgs.current.clear();
|
||||||
|
|
||||||
|
ApplicationService.validateApplication(applId, {}, validateApplicationCallback, errValidateApplicationCallback);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateApplicationCallback = (data) => {
|
||||||
|
if (data.status === 'SUCCESS') {
|
||||||
|
toast.current.show({
|
||||||
|
severity: 'success',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
storeSet.main.unsetAsyncRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
const errValidateApplicationCallback = (data) => {
|
||||||
|
if (toast.current) {
|
||||||
|
toast.current.show({
|
||||||
|
severity: 'error',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
storeSet.main.unsetAsyncRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveDraft = (saveAndMove = '') => {
|
||||||
|
trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
const getApplicationId = () => {
|
||||||
|
const parsed = parseInt(id)
|
||||||
|
return !isNaN(parsed) ? parsed : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const goBackward = () => {
|
||||||
|
storeSet.main.setAsyncRequest();
|
||||||
|
ApplicationService.getApplicationForm(id, getApplFormCallback, errGetApplFormCallbacks, [
|
||||||
|
['formId', formId],
|
||||||
|
['action', 'PREVIOUS']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const goForward = () => {
|
||||||
|
storeSet.main.setAsyncRequest();
|
||||||
|
ApplicationService.getApplicationForm(id, getApplFormCallback, errGetApplFormCallbacks, [
|
||||||
|
['formId', formId],
|
||||||
|
['action', 'NEXT']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getApplFormCallback = (data) => {
|
||||||
|
if (data.status === 'SUCCESS') {
|
||||||
|
setBandoTitle(data.data.callTitle);
|
||||||
|
setBandoId(data.data.callId);
|
||||||
|
setFormData(data.data.applicationFormResponse.content);
|
||||||
|
setFormId(data.data.formId);
|
||||||
|
setTotalSteps(data.data.totalFormSteps);
|
||||||
|
setApplicationStatus(data.data.applicationStatus)
|
||||||
|
setActiveStep(data.data.currentStep);
|
||||||
|
|
||||||
|
/*const chosenCompanyId = storeGet.main.chosenCompanyId();
|
||||||
|
const companies = storeGet.main.companies();
|
||||||
|
const company = head(companies.filter(o => o.id === chosenCompanyId));*/
|
||||||
|
let formDataInitial = {};
|
||||||
|
let dynamicData = {
|
||||||
|
company: {},
|
||||||
|
user: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*if (company) {
|
||||||
|
dynamicData = Object.keys(company).reduce((acc, cur) => {
|
||||||
|
if ([
|
||||||
|
'companyName', 'vatNumber', 'codiceFiscale', 'address', 'phoneNumber',
|
||||||
|
'city', 'province', 'cap', 'country', 'pec', 'email', 'contactName', 'contactEmail'
|
||||||
|
].includes(cur)) {
|
||||||
|
acc.company[cur] = company[cur];
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, dynamicData);
|
||||||
|
}
|
||||||
|
|
||||||
|
const userData = storeGet.main.userData();
|
||||||
|
Object.keys(userData).reduce((acc, cur) => {
|
||||||
|
if ([
|
||||||
|
'email', 'firstName', 'lastName', 'phoneNumber', 'codiceFiscale'
|
||||||
|
].includes(cur)) {
|
||||||
|
acc.user[cur] = userData[cur];
|
||||||
|
}
|
||||||
|
if (['dateOfBirth'].includes(cur)) {
|
||||||
|
acc.user[cur] = new Date(userData[cur]);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, dynamicData);*/
|
||||||
|
|
||||||
|
if (data.data.applicationFormResponse.content) {
|
||||||
|
// eslint-disable-next-line array-callback-return
|
||||||
|
data.data.applicationFormResponse.content.map((o) => {
|
||||||
|
if (o.dynamicData && !isEmpty(o.dynamicData)) {
|
||||||
|
formDataInitial[o.id] = pathOr('', o.dynamicData.split('.'), dynamicData);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.data.applicationFormResponse.formFields) {
|
||||||
|
const submitData = data.data.applicationFormResponse.formFields.map((o) => ({
|
||||||
|
fieldId: o.fieldId,
|
||||||
|
fieldValue: o.fieldValue
|
||||||
|
}));
|
||||||
|
formDataInitial = submitData.reduce((acc, cur) => {
|
||||||
|
if (cur.fieldValue) {
|
||||||
|
acc[cur.fieldId] = cur.fieldValue;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, formDataInitial);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
setFormInitialData(formDataInitial);
|
||||||
|
}
|
||||||
|
storeSet.main.unsetAsyncRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
const errGetApplFormCallbacks = (data) => {
|
||||||
|
storeSet.main.unsetAsyncRequest();
|
||||||
|
if (data.status === 'VALIDATION_ERROR') {
|
||||||
|
if (toast.current) {
|
||||||
|
toast.current.show({
|
||||||
|
severity: 'error',
|
||||||
|
summary: '',
|
||||||
|
detail: data.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set404FromErrorResponse(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDownloadApplicationPdf = () => {
|
||||||
|
const applId = getApplicationId();
|
||||||
|
storeSet.main.setAsyncRequest();
|
||||||
|
|
||||||
|
ApplicationService.downloadApplicationPdf(applId, {}, getPdfCallback, errPdfCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPdfCallback = (data) => {
|
||||||
|
const applId = getApplicationId();
|
||||||
|
const pdfFile = new Blob([data], { type: 'application/octet-stream' })
|
||||||
|
const url = window.URL.createObjectURL(pdfFile);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.setAttribute('download', `application-${applId}.pdf`);
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
storeSet.main.unsetAsyncRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
const errPdfCallback = (data) => {
|
||||||
|
set404FromErrorResponse(data);
|
||||||
|
storeSet.main.unsetAsyncRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionBtns = <div className="appPageSection__actions">
|
||||||
|
{activeStep > 1 && activeStep <= totalSteps
|
||||||
|
? <Button
|
||||||
|
type="button"
|
||||||
|
disabled={'SUBMIT' === applicationStatus}
|
||||||
|
onClick={goBackward}
|
||||||
|
label={__('Vai indietro', 'gepafin')}
|
||||||
|
icon="pi pi-arrow-left"
|
||||||
|
iconPos="left"/> : null}
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={isAsyncRequest || 'SUBMIT' === applicationStatus}
|
||||||
|
onClick={saveDraft}
|
||||||
|
outlined
|
||||||
|
label={__('Controlla', 'gepafin')} icon="pi pi-verified" iconPos="right"/>
|
||||||
|
{activeStep < totalSteps
|
||||||
|
? <Button
|
||||||
|
type="button"
|
||||||
|
disabled={'SUBMIT' === applicationStatus}
|
||||||
|
onClick={goForward}
|
||||||
|
label={__('Vai avanti', 'gepafin')}
|
||||||
|
icon="pi pi-arrow-right"
|
||||||
|
iconPos="right"/> : null}
|
||||||
|
{/*<Button
|
||||||
|
disabled={'SUBMIT' === applicationStatus}
|
||||||
|
label={__('Convalidare', 'gepafin')}
|
||||||
|
icon="pi pi-check"
|
||||||
|
iconPos="right"/>*/}
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={'SUBMIT' === applicationStatus}
|
||||||
|
onClick={onDownloadApplicationPdf}
|
||||||
|
label={__('Scarica PDF', 'gepafin')}
|
||||||
|
icon="pi pi-download"
|
||||||
|
iconPos="right"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (formInitialData) {
|
||||||
|
//reset();
|
||||||
|
Object.keys(formInitialData).map(k => setValue(k, formInitialData[k]));
|
||||||
|
trigger();
|
||||||
|
}
|
||||||
|
}, [formInitialData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const applId = getApplicationId();
|
||||||
|
|
||||||
|
if (applId) {
|
||||||
|
storeSet.main.setAsyncRequest();
|
||||||
|
ApplicationService.getApplicationForm(applId, getApplFormCallback, errGetApplFormCallbacks);
|
||||||
|
}
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="appPage">
|
||||||
|
{!isAsyncRequest
|
||||||
|
? <div className="appPage__pageHeader">
|
||||||
|
<h1>{sprintf(__('Domanda per il Bando: %s', 'gepafin'), bandoTitle)}</h1>
|
||||||
|
</div>
|
||||||
|
: <>
|
||||||
|
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
|
||||||
|
<Skeleton width="100%" height="2rem" className="mb-8"></Skeleton>
|
||||||
|
</>}
|
||||||
|
|
||||||
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
|
<ApplicationSteps totalSteps={totalSteps} activeStepIndex={activeStepIndex}/>
|
||||||
|
|
||||||
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
|
<Messages ref={formMsgs}/>
|
||||||
|
<Toast ref={toast}/>
|
||||||
|
|
||||||
|
<div className="appPage__content">
|
||||||
|
<BlockingOverlay shouldDisplay={isAsyncRequest}/>
|
||||||
|
<form className="appForm" onSubmit={handleSubmit(onValidate)}>
|
||||||
|
<div className="appPageSection__preview">
|
||||||
|
{actionBtns}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{formData.map(o => {
|
||||||
|
const label = head(o.settings.filter(o => o.name === 'label'));
|
||||||
|
const text = head(o.settings.filter(o => o.name === 'text'));
|
||||||
|
const placeholder = head(o.settings.filter(o => o.name === 'placeholder'));
|
||||||
|
const options = head(o.settings.filter(o => o.name === 'options'));
|
||||||
|
const tableColumns = head(o.settings.filter(o => o.name === 'table_columns'));
|
||||||
|
const step = head(o.settings.filter(o => o.name === 'step'));
|
||||||
|
const mime = head(o.settings.filter(o => o.name === 'mime'));
|
||||||
|
let mimeValue = '';
|
||||||
|
|
||||||
|
if (mime) {
|
||||||
|
mimeValue = mime.value.map(o => o.code ? o.code : o.ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validations = Object.keys(o.validators).reduce((acc, cur) => {
|
||||||
|
if (o.validators[cur]) {
|
||||||
|
if (['min', 'max', 'minLength', 'maxLength', 'maxSize'].includes(cur)) {
|
||||||
|
acc[cur] = parseInt(o.validators[cur]);
|
||||||
|
} else if ('pattern' === cur) {
|
||||||
|
acc[cur] = new RegExp(o.validators[cur]);
|
||||||
|
} else if ('isRequired' === cur) {
|
||||||
|
//acc[cur] = o.validators[cur];
|
||||||
|
acc['required'] = true;
|
||||||
|
} else if ('custom' === cur && validationFns[o.validators[cur]]) {
|
||||||
|
if (!acc.validate) {
|
||||||
|
acc.validate = {};
|
||||||
|
}
|
||||||
|
acc.validate[o.validators[cur]] = validationFns[o.validators[cur]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
//console.log('validations', validations, o.name)
|
||||||
|
|
||||||
|
return ['paragraph'].includes(o.name) && text
|
||||||
|
? <div key={o.id}>
|
||||||
|
<div className="ql-editor">
|
||||||
|
{renderHtmlContent(text.value)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: <FormField
|
||||||
|
key={o.id}
|
||||||
|
type={o.name}
|
||||||
|
disabled={o.name === 'fileupload'}
|
||||||
|
fieldName={o.id}
|
||||||
|
label={label ? label.value : ''}
|
||||||
|
placeholder={placeholder ? placeholder.value : ''}
|
||||||
|
control={control}
|
||||||
|
register={register}
|
||||||
|
errors={errors}
|
||||||
|
defaultValue={values[o.id] ? values[o.id] : ''}
|
||||||
|
maxFractionDigits={step ? step.value : 0}
|
||||||
|
accept={mimeValue}
|
||||||
|
config={validations}
|
||||||
|
options={options ? options.value : []}
|
||||||
|
setDataFn={setValue}
|
||||||
|
saveFormCallback={saveDraft}
|
||||||
|
sourceId={getApplicationId()}
|
||||||
|
useGrouping={false}
|
||||||
|
tableColumns={tableColumns ? tableColumns.value : {}}
|
||||||
|
/>
|
||||||
|
})}
|
||||||
|
|
||||||
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
|
<div className="appPageSection__hr">
|
||||||
|
<span>{__('Azioni rapide', 'gepafin')}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="appPageSection__preview">
|
||||||
|
{actionBtns}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BandoApplicationPreview;
|
||||||
199
src/pages/Dashboard/components/DraftApplicationsTable/index.js
Normal file
199
src/pages/Dashboard/components/DraftApplicationsTable/index.js
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { uniq, is } from 'ramda';
|
||||||
|
|
||||||
|
// tools
|
||||||
|
import getBandoLabel from '../../../../helpers/getBandoLabel';
|
||||||
|
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
|
||||||
|
|
||||||
|
// store
|
||||||
|
import { useStore } from '../../../../store';
|
||||||
|
|
||||||
|
// api
|
||||||
|
import ApplicationService from '../../../../service/application-service';
|
||||||
|
|
||||||
|
// components
|
||||||
|
import { FilterMatchMode, FilterOperator } from 'primereact/api';
|
||||||
|
import { DataTable } from 'primereact/datatable';
|
||||||
|
import { Column } from 'primereact/column';
|
||||||
|
import { InputText } from 'primereact/inputtext';
|
||||||
|
import { IconField } from 'primereact/iconfield';
|
||||||
|
import { InputIcon } from 'primereact/inputicon';
|
||||||
|
import { Dropdown } from 'primereact/dropdown';
|
||||||
|
import { ProgressBar } from 'primereact/progressbar';
|
||||||
|
import { Button } from 'primereact/button';
|
||||||
|
//import { Calendar } from 'primereact/calendar';
|
||||||
|
import { Tag } from 'primereact/tag';
|
||||||
|
import ProperBandoLabel from '../../../../components/ProperBandoLabel';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
const DraftApplicationsTable = () => {
|
||||||
|
const chosenCompanyId = useStore().main.chosenCompanyId();
|
||||||
|
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
|
||||||
|
const [items, setItems] = useState(null);
|
||||||
|
const [filters, setFilters] = useState(null);
|
||||||
|
const [globalFilterValue, setGlobalFilterValue] = useState('');
|
||||||
|
const [statuses, setStatuses] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalAsyncRequest(true);
|
||||||
|
ApplicationService.getApplications(getApplCallback, errGetApplCallback, [
|
||||||
|
['statuses', ['DRAFT', 'AWAITING', 'READY']]
|
||||||
|
])
|
||||||
|
}, [chosenCompanyId]);
|
||||||
|
|
||||||
|
const getApplCallback = (data) => {
|
||||||
|
if (data.status === 'SUCCESS') {
|
||||||
|
if (is(Array, data.data)) {
|
||||||
|
setItems(getFormattedBandiData(data.data));
|
||||||
|
setStatuses(uniq(items.map(o => o.status)))
|
||||||
|
initFilters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setLocalAsyncRequest(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const errGetApplCallback = (data) => {
|
||||||
|
setLocalAsyncRequest(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFormattedBandiData = (data) => {
|
||||||
|
return [...(data || [])].map((d) => {
|
||||||
|
d.callEndDate = new Date(d.callEndDate);
|
||||||
|
d.modifiedDate = new Date(d.modifiedDate);
|
||||||
|
|
||||||
|
return d;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*const formatDate = (value) => {
|
||||||
|
return value.toLocaleDateString('it-IT', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
year: 'numeric'
|
||||||
|
});
|
||||||
|
};*/
|
||||||
|
|
||||||
|
const clearFilter = () => {
|
||||||
|
initFilters();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onGlobalFilterChange = (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
let _filters = { ...filters };
|
||||||
|
|
||||||
|
_filters['global'].value = value;
|
||||||
|
|
||||||
|
setFilters(_filters);
|
||||||
|
setGlobalFilterValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initFilters = () => {
|
||||||
|
setFilters({
|
||||||
|
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
|
||||||
|
callTitle: {
|
||||||
|
operator: FilterOperator.AND,
|
||||||
|
constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }]
|
||||||
|
},
|
||||||
|
modifiedDate: {
|
||||||
|
operator: FilterOperator.AND,
|
||||||
|
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
|
||||||
|
},
|
||||||
|
callEndDate: {
|
||||||
|
operator: FilterOperator.AND,
|
||||||
|
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
|
||||||
|
},
|
||||||
|
status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] },
|
||||||
|
});
|
||||||
|
setGlobalFilterValue('');
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderHeader = () => {
|
||||||
|
return (
|
||||||
|
<div className="appTableHeader">
|
||||||
|
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
|
||||||
|
onClick={clearFilter}/>
|
||||||
|
<IconField iconPosition="left">
|
||||||
|
<InputIcon className="pi pi-search"/>
|
||||||
|
<InputText value={globalFilterValue} onChange={onGlobalFilterChange}
|
||||||
|
placeholder={__('Cerca', 'gepafin')}/>
|
||||||
|
</IconField>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*const dateModifyBodyTemplate = (rowData) => {
|
||||||
|
return formatDate(rowData.modifiedDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
const dateEndBodyTemplate = (rowData) => {
|
||||||
|
return formatDate(rowData.callEndDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
const dateFilterTemplate = (options) => {
|
||||||
|
return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)}
|
||||||
|
dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999"/>;
|
||||||
|
};*/
|
||||||
|
|
||||||
|
const statusBodyTemplate = (rowData) => {
|
||||||
|
return <ProperBandoLabel status={rowData.status}/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusFilterTemplate = (options) => {
|
||||||
|
return <Dropdown value={options.value} options={statuses}
|
||||||
|
onChange={(e) => options.filterCallback(e.value, options.index)}
|
||||||
|
itemTemplate={statusItemTemplate} placeholder="Select One" className="p-column-filter"
|
||||||
|
showClear/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const progressBodyTemplate = (options) => {
|
||||||
|
return <ProgressBar value={options.progress} color={'#64748B'}></ProgressBar>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusItemTemplate = (option) => {
|
||||||
|
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)}/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const actionsBodyTemplate = (rowData) => {
|
||||||
|
return <Link to={`/domande/${rowData.id}`}>
|
||||||
|
<Button severity="info" label={__('Anteprima', 'gepafin')} icon="pi pi-eye" size="small"
|
||||||
|
iconPos="right"/>
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = renderHeader();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="appPageSection__table">
|
||||||
|
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
|
||||||
|
filters={filters}
|
||||||
|
globalFilterFields={['name', 'status']}
|
||||||
|
header={header}
|
||||||
|
emptyMessage={__('Nessun dato disponibile', 'gepafin')}
|
||||||
|
onFilter={(e) => setFilters(e.filters)}>
|
||||||
|
<Column field="id" header={__('ID domanda', 'gepafin')}
|
||||||
|
filter filterPlaceholder={__('Cerca', 'gepafin')}
|
||||||
|
style={{ minWidth: '8rem' }}/>
|
||||||
|
<Column field="callTitle" header={__('Bando', 'gepafin')} filter
|
||||||
|
filterPlaceholder={__('Cerca il nome', 'gepafin')}
|
||||||
|
style={{ minWidth: '8rem' }}/>
|
||||||
|
{/*<Column header={__('Scadenza', 'gepafin')} filterField="callEndDate" dataType="date"
|
||||||
|
style={{ minWidth: '8rem' }}
|
||||||
|
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>
|
||||||
|
<Column header={__('Ultima modifica', 'gepafin')} filterField="modifiedDate" dataType="date"
|
||||||
|
style={{ minWidth: '8rem' }}
|
||||||
|
body={dateModifyBodyTemplate} filter filterElement={dateFilterTemplate}/>*/}
|
||||||
|
<Column field="status" header={__('Stato', 'gepafin')} filterMenuStyle={{ width: '14rem' }}
|
||||||
|
style={{ minWidth: '7rem' }} body={statusBodyTemplate} filter
|
||||||
|
filterElement={statusFilterTemplate}/>
|
||||||
|
<Column header={__('Progressi', 'gepafin')}
|
||||||
|
style={{ minWidth: '10rem' }} field="progress" body={progressBodyTemplate}/>
|
||||||
|
<Column header={__('Azioni', 'gepafin')}
|
||||||
|
style={{ minWidth: '10rem' }}
|
||||||
|
body={actionsBodyTemplate}/>
|
||||||
|
</DataTable>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DraftApplicationsTable;
|
||||||
@@ -16,6 +16,7 @@ import LatestBandiTable from './components/LatestBandiTable';
|
|||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
//import MyEvaluationsTable from '../DashboardPreInstructor/components/PreInstructorDomandeTable';
|
//import MyEvaluationsTable from '../DashboardPreInstructor/components/PreInstructorDomandeTable';
|
||||||
import AllDomandeTable from '../Domande/components/AllDomandeTable';
|
import AllDomandeTable from '../Domande/components/AllDomandeTable';
|
||||||
|
import DraftApplicationsTable from './components/DraftApplicationsTable';
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -69,35 +70,35 @@ const Dashboard = () => {
|
|||||||
<span><NumberFlow
|
<span><NumberFlow
|
||||||
value={getStatValue('numberOfActiveCalls', 0)}
|
value={getStatValue('numberOfActiveCalls', 0)}
|
||||||
format={{ notation: 'compact' }}
|
format={{ notation: 'compact' }}
|
||||||
locales="it-IT" /></span>
|
locales="it-IT"/></span>
|
||||||
</div>
|
</div>
|
||||||
<div className="statsBigBadges__gridItem">
|
<div className="statsBigBadges__gridItem">
|
||||||
<span>{__('Utenti registrati', 'gepafin')}</span>
|
<span>{__('Utenti registrati', 'gepafin')}</span>
|
||||||
<span><NumberFlow
|
<span><NumberFlow
|
||||||
value={getStatValue('numberOfResgisteredUsers', 0)}
|
value={getStatValue('numberOfResgisteredUsers', 0)}
|
||||||
format={{ notation: 'compact' }}
|
format={{ notation: 'compact' }}
|
||||||
locales="it-IT" /></span>
|
locales="it-IT"/></span>
|
||||||
</div>
|
</div>
|
||||||
<div className="statsBigBadges__gridItem">
|
<div className="statsBigBadges__gridItem">
|
||||||
<span>{__('Domande in pre-istruttoria', 'gepafin')}</span>
|
<span>{__('Domande in pre-istruttoria', 'gepafin')}</span>
|
||||||
<span><NumberFlow
|
<span><NumberFlow
|
||||||
value={getStatValue('numberOfSubmittedApplications', 0)}
|
value={getStatValue('numberOfSubmittedApplications', 0)}
|
||||||
format={{ notation: 'compact' }}
|
format={{ notation: 'compact' }}
|
||||||
locales="it-IT" /></span>
|
locales="it-IT"/></span>
|
||||||
</div>
|
</div>
|
||||||
<div className="statsBigBadges__gridItem">
|
<div className="statsBigBadges__gridItem">
|
||||||
<span>{__('Domande in bozza', 'gepafin')}</span>
|
<span>{__('Domande in bozza', 'gepafin')}</span>
|
||||||
<span><NumberFlow
|
<span><NumberFlow
|
||||||
value={getStatValue('numberOfDraftApplications', 0)}
|
value={getStatValue('numberOfDraftApplications', 0)}
|
||||||
format={{ notation: 'compact' }}
|
format={{ notation: 'compact' }}
|
||||||
locales="it-IT" /></span>
|
locales="it-IT"/></span>
|
||||||
</div>
|
</div>
|
||||||
<div className="statsBigBadges__gridItem">
|
<div className="statsBigBadges__gridItem">
|
||||||
<span>{__('Aziende', 'gepafin')}</span>
|
<span>{__('Aziende', 'gepafin')}</span>
|
||||||
<span><NumberFlow
|
<span><NumberFlow
|
||||||
value={getStatValue('numberOfCompany', 0)}
|
value={getStatValue('numberOfCompany', 0)}
|
||||||
format={{ notation: 'compact' }}
|
format={{ notation: 'compact' }}
|
||||||
locales="it-IT" /></span>
|
locales="it-IT"/></span>
|
||||||
</div>
|
</div>
|
||||||
<div className="statsBigBadges__gridItem">
|
<div className="statsBigBadges__gridItem">
|
||||||
<span>{__('Totale finanziamenti attivi', 'gepafin')}</span>
|
<span>{__('Totale finanziamenti attivi', 'gepafin')}</span>
|
||||||
@@ -111,7 +112,7 @@ const Dashboard = () => {
|
|||||||
currency: 'EUR',
|
currency: 'EUR',
|
||||||
currencyDisplay: 'symbol'
|
currencyDisplay: 'symbol'
|
||||||
}}
|
}}
|
||||||
locales="en-US" /></span>
|
locales="en-US"/></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,6 +131,13 @@ const Dashboard = () => {
|
|||||||
<AllDomandeTable/>
|
<AllDomandeTable/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
|
<div className="appPageSection">
|
||||||
|
<h2>{__('Domande in bozza', 'gepafin')}</h2>
|
||||||
|
<DraftApplicationsTable/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/*<div className="appPage__spacer"></div>
|
{/*<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const MyLatestSubmissionsTable = () => {
|
|||||||
setLocalAsyncRequest(true);
|
setLocalAsyncRequest(true);
|
||||||
ApplicationService.getApplications(getApplCallback, errGetApplCallback, [
|
ApplicationService.getApplications(getApplCallback, errGetApplCallback, [
|
||||||
['companyId', chosenCompanyId],
|
['companyId', chosenCompanyId],
|
||||||
['statuses', ['DRAFT', 'SUBMIT', 'AWAITING', 'READY', 'DISCARD']]
|
['statuses', ['DRAFT', 'AWAITING', 'READY']]
|
||||||
])
|
])
|
||||||
}, [chosenCompanyId]);
|
}, [chosenCompanyId]);
|
||||||
|
|
||||||
|
|||||||
@@ -148,14 +148,22 @@ const DomandaEditPreInstructor = () => {
|
|||||||
|
|
||||||
const doApprove = () => {
|
const doApprove = () => {
|
||||||
const formData = {
|
const formData = {
|
||||||
status: 'APPROVED'
|
applicationStatus: 'APPROVED',
|
||||||
|
criteria: klona(data.criteria),
|
||||||
|
checklist: klona(data.checklist),
|
||||||
|
files: klona(data.files),
|
||||||
|
note: data.note
|
||||||
}
|
}
|
||||||
ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateStatusCallback, errUpdateStatusCallback);
|
ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateStatusCallback, errUpdateStatusCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
const doReject = () => {
|
const doReject = () => {
|
||||||
const formData = {
|
const formData = {
|
||||||
status: 'REJECTED'
|
applicationStatus: 'REJECTED',
|
||||||
|
criteria: klona(data.criteria),
|
||||||
|
checklist: klona(data.checklist),
|
||||||
|
files: klona(data.files),
|
||||||
|
note: data.note
|
||||||
}
|
}
|
||||||
ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateStatusCallback, errUpdateStatusCallback);
|
ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateStatusCallback, errUpdateStatusCallback);
|
||||||
}
|
}
|
||||||
@@ -537,14 +545,14 @@ const DomandaEditPreInstructor = () => {
|
|||||||
{data.id
|
{data.id
|
||||||
? <Button
|
? <Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!isAdmissible || data.status === 'CLOSE'}
|
disabled={!isAdmissible || ['CLOSE', 'SOCCORSO'].includes(data.status)}
|
||||||
onClick={doApprove}
|
onClick={doApprove}
|
||||||
label={__('Approva Domanda', 'gepafin')}
|
label={__('Approva Domanda', 'gepafin')}
|
||||||
icon="pi pi-check" iconPos="right"/> : null}
|
icon="pi pi-check" iconPos="right"/> : null}
|
||||||
{data.id
|
{data.id
|
||||||
? <Button
|
? <Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={data.status === 'CLOSE'}
|
disabled={['CLOSE', 'SOCCORSO'].includes(data.status)}
|
||||||
onClick={doReject}
|
onClick={doReject}
|
||||||
label={__('Respingi Domanda', 'gepafin')}
|
label={__('Respingi Domanda', 'gepafin')}
|
||||||
icon="pi pi-times" iconPos="right"/> : null}
|
icon="pi pi-times" iconPos="right"/> : null}
|
||||||
|
|||||||
@@ -149,20 +149,20 @@ const AllDomandeTable = ({ openDialogFn, updaterString = '' }) => {
|
|||||||
onFilter={(e) => setFilters(e.filters)}>
|
onFilter={(e) => setFilters(e.filters)}>
|
||||||
<Column field="id" header={__('ID domanda', 'gepafin')}
|
<Column field="id" header={__('ID domanda', 'gepafin')}
|
||||||
filter filterPlaceholder={__('Cerca', 'gepafin')}
|
filter filterPlaceholder={__('Cerca', 'gepafin')}
|
||||||
style={{ minWidth: '12rem' }}/>
|
style={{ minWidth: '8rem' }}/>
|
||||||
<Column field="callTitle" header={__('Bando', 'gepafin')}
|
<Column field="callTitle" header={__('Bando', 'gepafin')}
|
||||||
filter filterPlaceholder={__('Cerca', 'gepafin')}
|
filter filterPlaceholder={__('Cerca', 'gepafin')}
|
||||||
style={{ minWidth: '12rem' }}/>
|
style={{ minWidth: '10rem' }}/>
|
||||||
<Column header={__('Data Ricezione', 'gepafin')}
|
<Column header={__('Data Ricezione', 'gepafin')}
|
||||||
filterField="submissionDate" dataType="date"
|
filterField="submissionDate" dataType="date"
|
||||||
style={{ minWidth: '10rem' }}
|
style={{ minWidth: '8rem' }}
|
||||||
body={dateAppliedBodyTemplate} filter filterElement={dateFilterTemplate}/>
|
body={dateAppliedBodyTemplate} filter filterElement={dateFilterTemplate}/>
|
||||||
<Column header={__('Scadenza', 'gepafin')}
|
<Column header={__('Scadenza', 'gepafin')}
|
||||||
filterField="callEndDate" dataType="date"
|
filterField="callEndDate" dataType="date"
|
||||||
style={{ minWidth: '10rem' }}
|
style={{ minWidth: '8rem' }}
|
||||||
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>
|
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>
|
||||||
<Column field="status" header={__('Stato', 'gepafin')}
|
<Column field="status" header={__('Stato', 'gepafin')}
|
||||||
style={{ width: '120px' }} body={statusBodyTemplate} />
|
style={{ minWidth: '8rem' }} body={statusBodyTemplate} />
|
||||||
<Column header={__('Azioni', 'gepafin')}
|
<Column header={__('Azioni', 'gepafin')}
|
||||||
body={actionsBodyTemplate}/>
|
body={actionsBodyTemplate}/>
|
||||||
</DataTable>
|
</DataTable>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { Dialog } from 'primereact/dialog';
|
|||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import { classNames } from 'primereact/utils';
|
import { classNames } from 'primereact/utils';
|
||||||
import { Dropdown } from 'primereact/dropdown';
|
import { Dropdown } from 'primereact/dropdown';
|
||||||
|
import DraftApplicationsTable from '../Dashboard/components/DraftApplicationsTable';
|
||||||
|
|
||||||
const Domande = () => {
|
const Domande = () => {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -147,9 +148,17 @@ const Domande = () => {
|
|||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
|
<h2>{__('Domande pubblicate', 'gepafin')}</h2>
|
||||||
<AllDomandeTable openDialogFn={openAssignDialog} updaterString={updaterString}/>
|
<AllDomandeTable openDialogFn={openAssignDialog} updaterString={updaterString}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
|
<div className="appPageSection">
|
||||||
|
<h2>{__('Domande in bozza', 'gepafin')}</h2>
|
||||||
|
<DraftApplicationsTable/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
visible={isVisibleEditDialog}
|
visible={isVisibleEditDialog}
|
||||||
modal
|
modal
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import SoccorsoEditPreInstructor from './pages/SoccorsoEditPreInstructor';
|
|||||||
import SoccorsoAddPreInstructor from './pages/SoccorsoAddPreInstructor';
|
import SoccorsoAddPreInstructor from './pages/SoccorsoAddPreInstructor';
|
||||||
import DomandeBeneficiario from './pages/DomandeBeneficiario';
|
import DomandeBeneficiario from './pages/DomandeBeneficiario';
|
||||||
import DomandaBeneficiario from './pages/DomandaBeneficiario';
|
import DomandaBeneficiario from './pages/DomandaBeneficiario';
|
||||||
|
import BandoApplicationPreview from './pages/BandoApplicationPreview';
|
||||||
|
|
||||||
const routes = ({ role, chosenCompanyId }) => {
|
const routes = ({ role, chosenCompanyId }) => {
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ const routes = ({ role, chosenCompanyId }) => {
|
|||||||
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandePreInstructor/> : null}
|
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandePreInstructor/> : null}
|
||||||
</DefaultLayout>}/>
|
</DefaultLayout>}/>
|
||||||
<Route path="/domande/:id/" element={<DefaultLayout>
|
<Route path="/domande/:id/" element={<DefaultLayout>
|
||||||
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
|
{'ROLE_SUPER_ADMIN' === role ? <BandoApplicationPreview/> : null}
|
||||||
{'ROLE_BENEFICIARY' === role ? <DomandaBeneficiario/> : null}
|
{'ROLE_BENEFICIARY' === role ? <DomandaBeneficiario/> : null}
|
||||||
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandaEditPreInstructor/> : null}
|
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandaEditPreInstructor/> : null}
|
||||||
</DefaultLayout>}/>
|
</DefaultLayout>}/>
|
||||||
|
|||||||
Reference in New Issue
Block a user