From 6bfe2f972875739474557aeacf1e2a780191bf24 Mon Sep 17 00:00:00 2001 From: Vitalii Kiiko Date: Sat, 12 Oct 2024 13:49:40 +0200 Subject: [PATCH] - added download application pdf button for submitted applications; - fixed adding files for call; --- package.json | 4 +- src/assets/scss/components/appPage.scss | 2 +- .../FileuploadApplicationSignedPdf/index.js | 194 ++++++++++ src/components/FileuploadDelega/index.js | 22 +- .../FormField/components/Checkboxes/index.js | 8 +- .../FormField/components/Fileupload/index.js | 6 +- .../components/FileuploadAsync/index.js | 26 +- .../UnsavedChangesDetector/index.js | 4 +- src/pages/AddCompany/index.js | 14 +- src/pages/BandoApplication/index.js | 352 +++++++----------- .../components/BandoEditFormStep2/index.js | 2 + src/pages/BandoViewBeneficiario/index.js | 4 +- .../MyLatestSubmissionsTable/index.js | 10 +- src/service/application-service.js | 16 + 14 files changed, 400 insertions(+), 264 deletions(-) create mode 100644 src/components/FileuploadApplicationSignedPdf/index.js diff --git a/package.json b/package.json index 50ce58f..9b1eeda 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "@wordpress/react-i18n": "4.8.0", "@xyflow/react": "12.3.1", "codice-fiscale-js": "2.3.22", - "deep-object-diff": "1.1.9", + "deep-object-diff": "^1.1.9", "dompurify": "3.1.7", "fast-deep-equal": "3.1.3", "html-react-parser": "5.1.16", @@ -75,4 +75,4 @@ "last 1 safari version" ] } -} \ No newline at end of file +} diff --git a/src/assets/scss/components/appPage.scss b/src/assets/scss/components/appPage.scss index 91d5da9..feb7d93 100644 --- a/src/assets/scss/components/appPage.scss +++ b/src/assets/scss/components/appPage.scss @@ -170,7 +170,7 @@ .appPageSection__hero { display: flex; - height: 172px; + height: 250px; width: 100%; img { diff --git a/src/components/FileuploadApplicationSignedPdf/index.js b/src/components/FileuploadApplicationSignedPdf/index.js new file mode 100644 index 0000000..3cbccec --- /dev/null +++ b/src/components/FileuploadApplicationSignedPdf/index.js @@ -0,0 +1,194 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { __ } from '@wordpress/i18n'; +import { head, isEmpty } from 'ramda'; + +import { FileUpload } from 'primereact/fileupload'; +import { Tag } from 'primereact/tag'; +import { Button } from 'primereact/button'; + +// api +import ApplicationService from '../../service/application-service'; + +import { mimeTypes } from '../../configData'; + +const FileuploadApplicationSignedPdf = ({ + fieldName, + setDataFn, + defaultValue = [], + accept = [], + maxSize = 100000000, + emptyText = __('Trascina qui il tuo file', 'gepafin'), + chooseLabel = __('Aggiungi documento', 'gepafin'), + uploadLabel = __('Salva documento', 'gepafin'), + cancelLabel = __('Cancella documento', 'gepafin'), + multiple = false, + applicationId = 0, + disabled = false + }) => { + const [stateFieldData, setStateFieldData] = useState([]); + const [acceptFormats, setAcceptFormats] = useState(''); + const [formatsForInput, setFormatsForInput] = useState(''); + const inputRef = useRef(); + + const customBase64Uploader = (event) => { + const formData = new FormData() + for (const file of event.files) { + formData.append('file', file) + } + ApplicationService.uploadApplicationSignedPdf(applicationId, formData, callback, errorCallback); + }; + + const callback = (data) => { + if (data.status === 'SUCCESS') { + setStateFieldData([data.data]); + inputRef.current.setFiles([]); + } + } + + const errorCallback = (err) => { + console.log('err', err); + } + + const itemTemplate = (file) => { + let fileName = file.fileName ? file.fileName : file.name; + return ( +
+
+ + {fileName} + +
+
+ {file.id ? : null} + {!file.id ? : null} +
+
+
+
+ ); + }; + + const onTemplateRemove = (file) => { + if (file.id) { + ApplicationService.deleteApplicationSignedPdf( + applicationId, + (data) => dCallback(data, file.id), + dErrorCallback + ); + } else { + const files = inputRef.current.getFiles() + const newFiles = files.filter(o => o.lastModified !== file.lastModified && o.name !== file.name); + inputRef.current.setFiles(newFiles); + } + } + + const dCallback = (data, id) => { + if (data.status === 'SUCCESS') { + setStateFieldData(prevState => { + const newFiles = prevState.filter(o => o.id !== id); + inputRef.current.setUploadedFiles(newFiles); + console.log('dCallback - newFiles', newFiles) + return newFiles; + }); + } + } + + const dErrorCallback = (err) => { + console.log('err', err); + } + + const onBeforeDrop = (e) => { + return !isEmpty(e.dataTransfer.files) ? validateFileInputType(e.dataTransfer.files) : false; + } + + const onBeforeSelect = (e) => { + const files = inputRef.current.getFiles(); + const uploadedfiles = inputRef.current.getUploadedFiles(); + + if (!multiple && (uploadedfiles.length > 0 || files.length > 0)) { + return false; + } + + if (e.originalEvent.target.files) { + return !isEmpty(e.originalEvent.target.files) + ? validateFileInputType(e.originalEvent.target.files) + : false; + } + } + + const validateFileInputType = (files) => { + const MIMEtype = new RegExp(acceptFormats); + + return Array.prototype.every.call(files, function passesAcceptedFormat(file) { + const fileExtension = `.${file.name.split('.').pop().toLowerCase()}`; + const fileType = file.type; + return MIMEtype.test(fileType) || MIMEtype.test(fileExtension); + }); + } + + useEffect(() => { + setStateFieldData(defaultValue); + }, []); + + useEffect(() => { + if (inputRef.current) { + inputRef.current.setUploadedFiles(defaultValue); + } + }, [defaultValue]); + + useEffect(() => { + const properMime = accept + .map(v => { + const found = head(mimeTypes.filter(o => o.code.includes(v))); + let res = v; + + if (found) { + res = found.code; + } + + return res; + }) + // eslint-disable-next-line no-useless-escape + setAcceptFormats(properMime.join(',').replace(/\*/g, '.\*').replace(/,/g, '|')); + setFormatsForInput(properMime.join(',')) + }, [accept]); + + useEffect(() => { + if (inputRef.current) { + inputRef.current.setUploadedFiles(stateFieldData); + } + setDataFn(fieldName, stateFieldData, { shouldValidate: true }); + }, [stateFieldData]) + + return ( + applicationId && applicationId !== 0 + ? {emptyText}

} + chooseLabel={chooseLabel} + cancelLabel={cancelLabel} + uploadLabel={uploadLabel} + itemTemplate={itemTemplate} + customUpload + onBeforeDrop={onBeforeDrop} + onBeforeSelect={onBeforeSelect} + uploadHandler={customBase64Uploader}/> + : null + ) +} + +export default FileuploadApplicationSignedPdf; \ No newline at end of file diff --git a/src/components/FileuploadDelega/index.js b/src/components/FileuploadDelega/index.js index 3b0f2dd..e53be51 100644 --- a/src/components/FileuploadDelega/index.js +++ b/src/components/FileuploadDelega/index.js @@ -11,19 +11,19 @@ import CompanyService from '../../service/company-service'; import { mimeTypes } from '../../configData'; const FileuploadDelega = ({ - fieldName, - setDataFn, - defaultValue = [], - accept = [], - maxSize = 100000000, - emptyText = __('Trascina qui il tuo file', 'gepafin'), - chooseLabel = __('Aggiungi delega', 'gepafin'), + fieldName, + setDataFn, + defaultValue = [], + accept = [], + maxSize = 100000000, + emptyText = __('Trascina qui il tuo file', 'gepafin'), + chooseLabel = __('Aggiungi delega', 'gepafin'), uploadLabel = __('Salva documento', 'gepafin'), cancelLabel = __('Cancella documento', 'gepafin'), - multiple = false, - companyId = 0, - disabled = false - }) => { + multiple = false, + companyId = 0, + disabled = false + }) => { const [stateFieldData, setStateFieldData] = useState([]); const [acceptFormats, setAcceptFormats] = useState(''); const [formatsForInput, setFormatsForInput] = useState(''); diff --git a/src/components/FormField/components/Checkboxes/index.js b/src/components/FormField/components/Checkboxes/index.js index 2e08031..ef4794b 100644 --- a/src/components/FormField/components/Checkboxes/index.js +++ b/src/components/FormField/components/Checkboxes/index.js @@ -35,11 +35,11 @@ const Checkboxes = ({ const properConfig = (config) => { let newConfig = klona(config); - if (config.minLength) { - newConfig = wrap(newConfig).set(['validate', 'minChecks'], (v) => minChecks(v, config.minLength)).value(); + if (config.min) { + newConfig = wrap(newConfig).set(['validate', 'minChecks'], (v) => minChecks(v, config.min)).value(); } - if (config.maxLength) { - newConfig = wrap(newConfig).set(['validate', 'maxChecks'], (v) => maxChecks(v, config.maxLength)).value(); + if (config.max) { + newConfig = wrap(newConfig).set(['validate', 'maxChecks'], (v) => maxChecks(v, config.max)).value(); } return newConfig; diff --git a/src/components/FormField/components/Fileupload/index.js b/src/components/FormField/components/Fileupload/index.js index 05cf58c..49a035e 100644 --- a/src/components/FormField/components/Fileupload/index.js +++ b/src/components/FormField/components/Fileupload/index.js @@ -60,9 +60,8 @@ const Fileupload = ({ const callback = (data) => { if (data.status === 'SUCCESS') { setStateFieldData(data.data); - const files = inputRef.current.getFiles(); - inputRef.current.setUploadedFiles(files); - setDataFn(fieldName, data.data, { shouldValidate: true }); + const uploadedFiles = inputRef.current.getUploadedFiles(); + setDataFn(fieldName, [...uploadedFiles, ...data.data], { shouldValidate: true }); inputRef.current.setFiles([]); saveFormCallback(); } @@ -116,7 +115,6 @@ const Fileupload = ({ if (data.status === 'SUCCESS') { setStateFieldData(prevState => { const newFiles = prevState.filter(o => o.id !== id); - inputRef.current.setUploadedFiles(newFiles); setDataFn(fieldName, newFiles, { shouldValidate: true }); saveFormCallback(); return newFiles; diff --git a/src/components/FormField/components/FileuploadAsync/index.js b/src/components/FormField/components/FileuploadAsync/index.js index 305b226..56ecc77 100644 --- a/src/components/FormField/components/FileuploadAsync/index.js +++ b/src/components/FormField/components/FileuploadAsync/index.js @@ -54,8 +54,8 @@ const FileuploadAsync = ({ const callback = (data) => { if (data.status === 'SUCCESS') { setStateFieldData(data.data); - const files = inputRef.current.getFiles(); - inputRef.current.setUploadedFiles(files); + const uploadedFiles = inputRef.current.getUploadedFiles(); + setDataFn(fieldName, [...uploadedFiles, ...data.data], { shouldValidate: true }); inputRef.current.setFiles([]); } } @@ -108,7 +108,7 @@ const FileuploadAsync = ({ if (data.status === 'SUCCESS') { setStateFieldData(prevState => { const newFiles = prevState.filter(o => o.id !== id); - inputRef.current.setUploadedFiles(newFiles); + setDataFn(fieldName, newFiles, { shouldValidate: true }); return newFiles; }); } @@ -123,6 +123,13 @@ const FileuploadAsync = ({ } const onBeforeSelect = (e) => { + const files = inputRef.current.getFiles(); + const uploadedfiles = inputRef.current.getUploadedFiles(); + + if (!multiple && (uploadedfiles.length > 0 || files.length > 0)) { + return false; + } + if (e.originalEvent.target.files) { return !isEmpty(e.originalEvent.target.files) ? validateFileInputType(e.originalEvent.target.files) @@ -147,11 +154,9 @@ const FileuploadAsync = ({ useEffect(() => { if (inputRef.current) { - const properValue = multiple - ? defaultValue - : !isEmpty(defaultValue) ? [defaultValue] : []; - inputRef.current.setUploadedFiles(properValue); + inputRef.current.setUploadedFiles(defaultValue); } + setStateFieldData(defaultValue); }, [defaultValue]); useEffect(() => { @@ -171,13 +176,6 @@ const FileuploadAsync = ({ setFormatsForInput(properMime.join(',')) }, [accept]); - useEffect(() => { - if (inputRef.current) { - inputRef.current.setUploadedFiles(stateFieldData); - } - setDataFn(fieldName, [...stateFieldData], { shouldValidate: true }); - }, [stateFieldData]) - return ( sourceId && sourceId !== 0 ? <> diff --git a/src/components/UnsavedChangesDetector/index.js b/src/components/UnsavedChangesDetector/index.js index 41aa83d..428a0a9 100644 --- a/src/components/UnsavedChangesDetector/index.js +++ b/src/components/UnsavedChangesDetector/index.js @@ -1,6 +1,7 @@ import { useEffect } from 'react'; import { __ } from '@wordpress/i18n'; import equal from 'fast-deep-equal'; +import { diff } from 'deep-object-diff'; // store import { storeGet } from '../../store'; @@ -10,7 +11,8 @@ const UnsavedChangesDetector = ({ getValuesFn }) => { const formData = getValuesFn(); const initial = storeGet.main.formInitialData(); const isEqual = equal(initial, formData); - + // TODO + //console.log('isEqual', isEqual, initial, formData, diff(initial, formData)) if (!isEqual) { event.returnValue = __('You have unsaved changes. If you proceed, they will be lost.', 'gepafin'); } diff --git a/src/pages/AddCompany/index.js b/src/pages/AddCompany/index.js index 09e3b01..ac29844 100644 --- a/src/pages/AddCompany/index.js +++ b/src/pages/AddCompany/index.js @@ -33,15 +33,12 @@ const AddCompany = () => { handleSubmit, formState: { errors }, setValue, - getValues, watch } = useForm({ defaultValues: {}, mode: 'onChange' }); - const values = getValues(); - const emptyValues = Object.values(values).filter(v => isEmpty(v) || isNil(v)).length; - const isPiva = watch('vatNumber') + const isPiva = watch('vatNumber'); const setEmptyValues = () => { const formData = { @@ -75,7 +72,6 @@ const AddCompany = () => { } else { newCompanies = [...companies, company]; storeSet.main.chosenCompanyId(company.id); - console.log('set company 1', company.id) } storeSet.main.companies(newCompanies); @@ -210,7 +206,7 @@ const AddCompany = () => { config={{ required: __('È obbligatorio', 'gepafin'), validate: { - isEmailPEC + isEmailPEC: isEmailPEC } }} /> @@ -225,7 +221,7 @@ const AddCompany = () => { config={{ required: __('È obbligatorio', 'gepafin'), validate: { - isEmail + isEmail: isEmail } }} /> @@ -291,7 +287,7 @@ const AddCompany = () => { config={{ required: __('È obbligatorio', 'gepafin'), validate: { - isEmail + isEmail: isEmail } }} /> @@ -321,7 +317,7 @@ const AddCompany = () => {
diff --git a/src/pages/BandoApplication/index.js b/src/pages/BandoApplication/index.js index 79ca21e..cee9bfd 100644 --- a/src/pages/BandoApplication/index.js +++ b/src/pages/BandoApplication/index.js @@ -34,6 +34,7 @@ import { Messages } from 'primereact/messages'; import ApplicationSteps from './ApplicationSteps'; import BlockingOverlay from '../../components/BlockingOverlay'; import { Dialog } from 'primereact/dialog'; +//import FileuploadApplicationSignedPdf from '../../components/FileuploadApplicationSignedPdf'; const BandoApplication = () => { const { id } = useParams(); @@ -46,6 +47,8 @@ const BandoApplication = () => { const [visibleConfirmation, setVisibleConfirmation] = useState(false); const [applicationStatus, setApplicationStatus] = useState(''); const [activeStep, setActiveStep] = useState(1); + // TODO + //const [signedPdfFile, setSignedPdfFile] = useState([]); const isAsyncRequest = useStore().main.isAsyncRequest(); const toast = useRef(null); const formMsgs = useRef(null); @@ -337,6 +340,30 @@ const BandoApplication = () => { iconPos="right"/> + 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); + } + useEffect(() => { if (formInitialData) { reset(); @@ -366,7 +393,9 @@ const BandoApplication = () => {
- + {'SUBMIT' !== applicationStatus + ? + : null}
@@ -375,7 +404,10 @@ const BandoApplication = () => { {if (!visibleConfirmation) return; setVisibleConfirmation(false); }}> + onHide={() => { + if (!visibleConfirmation) return; + setVisibleConfirmation(false); + }}>

{__('Grazie, la tua domanda è stata inviata correttamente. Entro 24 ore riceverai una pec con data, ora e numero di protocollo.', 'gepafin')}

@@ -384,227 +416,123 @@ const BandoApplication = () => {
-
- {actionBtns} -
+ {'SUBMIT' !== applicationStatus + ?
+ {actionBtns} +
: null} - {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 = ''; + {'SUBMIT' !== applicationStatus + ? 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]]; - } + if (mime) { + mimeValue = mime.value.map(o => o.code ? o.code : o.ext); } - return acc; - }, {}); - //console.log('validations', validations, o.name) + 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 ['paragraph'].includes(o.name) && text - ?
{renderHtmlContent(text.value)}
- : - })} + return acc; + }, {}); + //console.log('validations', validations, o.name) + + return ['paragraph'].includes(o.name) && text + ?
{renderHtmlContent(text.value)}
+ : + }) + : null} + + {'SUBMIT' === applicationStatus + ?
+
+ +
+
: null} + + {/*{'SUBMIT' === applicationStatus + ?
+
+ + +
+
+ : null}*/}
-
- {__('Azioni rapide', 'gepafin')} -
+ {'SUBMIT' !== applicationStatus + ?
+ {__('Azioni rapide', 'gepafin')} +
: null} -
- {actionBtns} -
+ {'SUBMIT' !== applicationStatus + ?
+ {actionBtns} +
: null}
- - {/*{!isAsyncRequest - ?
-
-
-
- {activeStep > 1 && activeStep <= totalSteps - ?
-
- - {formInitialData - ? 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 - ?
{renderHtmlContent(text.value)}
- : - }) : null} - -
- -
- {__('Azioni rapide', 'gepafin')} -
- -
-
- {activeStep > 1 && activeStep <= totalSteps - ?
-
- -
- : <> - - - - - - - - - }*/} ) diff --git a/src/pages/BandoEdit/components/BandoEditFormStep2/index.js b/src/pages/BandoEdit/components/BandoEditFormStep2/index.js index 515a716..2bd04bf 100644 --- a/src/pages/BandoEdit/components/BandoEditFormStep2/index.js +++ b/src/pages/BandoEdit/components/BandoEditFormStep2/index.js @@ -109,6 +109,7 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors, st }); const newFormData = {...formInitialData, ...data.data}; setFormInitialData(newFormData); + storeSet.main.formInitialData(newFormData); } } @@ -264,6 +265,7 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors, st register={register} sourceId={values.id} source="call" + multiple={false} /> { storeSet.main.unsetAsyncRequest(); } - const saveToFavourites = () => { + /*const saveToFavourites = () => { - } + }*/ const submitNewQuestion = () => { if (newQuestion && chosenCompanyId && 0 !== chosenCompanyId) { diff --git a/src/pages/DashboardBeneficiario/components/MyLatestSubmissionsTable/index.js b/src/pages/DashboardBeneficiario/components/MyLatestSubmissionsTable/index.js index edd663e..0fda671 100644 --- a/src/pages/DashboardBeneficiario/components/MyLatestSubmissionsTable/index.js +++ b/src/pages/DashboardBeneficiario/components/MyLatestSubmissionsTable/index.js @@ -151,11 +151,13 @@ const MyLatestSubmissionsTable = () => { }; const actionsBodyTemplate = (rowData) => { - return 'DRAFT' === rowData.status - ? -