Feature: preview application by ID by admin
- added ApplicationPreview page for admin; - added a new table to admin dashboard with applications in draft;
This commit is contained in:
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;
|
||||
Reference in New Issue
Block a user