diff --git a/src/App.js b/src/App.js index 7d3a901..2c0deea 100644 --- a/src/App.js +++ b/src/App.js @@ -4,7 +4,8 @@ import Routes from './routes'; import { createI18n, setLocaleData } from '@wordpress/i18n'; import { I18nProvider } from '@wordpress/react-i18n'; import './assets/scss/theme.scss'; -import { isEmpty, head } from 'ramda' +import { isEmpty, head } from 'ramda'; +import { addLocale, PrimeReactProvider } from 'primereact/api'; // store import { useStore, storeSet, storeGet } from './store'; @@ -18,6 +19,9 @@ function App() { const role = useStore().main.getRole(); const chosenCompanyId = useStore().main.chosenCompanyId(); const isRedirectedOnceNoCompany = useStore().main.isRedirectedOnceNoCompany(); + const value = { + locale: 'it', + }; const callback = (data) => { if (data.status === 'SUCCESS') { @@ -59,6 +63,51 @@ function App() { storeSet.main.setAsyncRequest(); AuthenticationService.me(callback, errCallback); + addLocale('it', { + startsWith: 'Inizia con', + contains: 'Contiene', + notContains: 'Non contiene', + endsWith: 'Finisce con', + equals: 'Uguale a', + notEquals: 'Diverso da', + noFilter: 'Nessun filtro', + lt: 'Minore di', + lte: 'Minore o uguale a', + gt: 'Maggiore di', + gte: 'Maggiore o uguale a', + dateIs: 'Data uguale a', + dateIsNot: 'Data diversa da', + dateBefore: 'Data prima di', + dateAfter: 'Data dopo', + custom: 'Personalizzato', + clear: 'Cancella', + apply: 'Applica', + matchAll: 'Tutte le condizioni', + matchAny: 'Qualsiasi condizione', + addRule: 'Aggiungi regola', + removeRule: 'Rimuovi regola', + accept: 'Sì', + reject: 'No', + choose: 'Scegli', + upload: 'Carica', + cancel: 'Annulla', + dayNames: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'], + dayNamesShort: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'], + dayNamesMin: ['D', 'L', 'M', 'M', 'G', 'V', 'S'], + monthNames: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'], + monthNamesShort: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'], + today: 'Oggi', + weekHeader: 'Sm', + firstDayOfWeek: 1, + dateFormat: 'dd/mm/yy', + weak: 'Debole', + medium: 'Medio', + strong: 'Forte', + passwordPrompt: 'Inserisci una password', + emptyMessage: 'Nessun risultato trovato', + emptyFilterMessage: 'Nessun risultato trovato' + }); + fetch('/languages/en_US.json') .then((res) => res.json()) .then(res => { @@ -69,7 +118,9 @@ function App() { return ( - + + + ); diff --git a/src/assets/scss/components/appForm.scss b/src/assets/scss/components/appForm.scss index dad056e..c6bef3f 100644 --- a/src/assets/scss/components/appForm.scss +++ b/src/assets/scss/components/appForm.scss @@ -109,6 +109,51 @@ background-color: var(--table-border-color) } } + + &.fileselect { + .fileselectInner { + flex-direction: row; + display: flex; + gap: 1rem; + + > div { + flex: 1 1 50%; + } + } + + .fileselectInner__selectionBox { + display: flex; + flex-direction: column; + gap: 10px; + } + + .fileselectInner__selectedFiles { + display: flex; + flex-direction: column; + gap: 10px; + + p { + margin: 0; + font-weight: bold; + font-size: 14px; + } + + ul { + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 7px; + + li { + display: flex; + gap: 1rem; + justify-content: space-between; + align-items: center; + } + } + } + } } .appForm__field--required.appForm__field--required { diff --git a/src/assets/scss/components/appPage.scss b/src/assets/scss/components/appPage.scss index a07df79..a608ea4 100644 --- a/src/assets/scss/components/appPage.scss +++ b/src/assets/scss/components/appPage.scss @@ -113,6 +113,10 @@ font-weight: 600; line-height: normal; margin: 0 0 24px; + display: flex; + gap: 1rem; + flex-wrap: wrap; + align-items: center; } h3 { diff --git a/src/assets/scss/components/misc.scss b/src/assets/scss/components/misc.scss index 030a350..03bd8f8 100644 --- a/src/assets/scss/components/misc.scss +++ b/src/assets/scss/components/misc.scss @@ -167,6 +167,13 @@ } } +.p-column-filter-overlay .p-column-filter-row-items .p-column-filter-row-item.p-highlight { + color: white; +} +.p-listbox .p-listbox-list .p-listbox-item.p-highlight { + color: white; +} + .p-inputnumber-input[readonly] { background-color: #e1e1e1; } diff --git a/src/components/DataTableAsync/DataTableAsync.js b/src/components/DataTableAsync/DataTableAsync.js new file mode 100644 index 0000000..8e6bb34 --- /dev/null +++ b/src/components/DataTableAsync/DataTableAsync.js @@ -0,0 +1,105 @@ +import React, { useEffect, useState } from 'react'; +import { __ } from '@wordpress/i18n'; + +import translationStrings from '../../translationStringsForComponents'; + +// components +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import { Button } from 'primereact/button'; +import BandoService from '../../service/bando-service'; + +const DataTableAsync = () => { + const [localAsyncRequest, setLocalAsyncRequest] = useState(false); + const [items, setItems] = useState(null); + const [totalRecordsNum, setTotalRecordsNum] = useState(0); + const [lazyState, setLazyState] = useState({ + first: 0, + rows: 5, + page: 1, + sortField: null, + sortOrder: null, + filters: { + name: { value: '', matchMode: 'contains' } + } + }); + + const getPaginationQuery = () => { + return { + "globalFilters": { + "page": 0, + "limit": lazyState.rows, + "sortBy": { + "columnName": "ID", + "sortDesc": true + } + } + } + } + + const onPage = (event) => { + setLazyState(event); + }; + + const onSort = (event) => { + setLazyState(event); + }; + + const onFilter = (event) => { + event['first'] = 0; + setLazyState(event); + }; + + const getCallback = (data) => { + if (data.status === 'SUCCESS') { + const { body, totalRecords, currentPage, totalPages, pageSize } = data.data; + setTotalRecordsNum(totalRecords); + const newItems = body.filter(o => o.status === 'PUBLISH'); + setItems(getFormattedBandiData(newItems)); + } + setLocalAsyncRequest(false); + } + + const errGetCallbacks = (data) => { + setLocalAsyncRequest(false); + } + + const getFormattedBandiData = (data) => { + return [...(data || [])].map((d) => { + d.start_date = new Date(d.dates[0]); + d.end_date = new Date(d.dates[1]); + + return d; + }); + }; + + useEffect(() => { + console.log(lazyState) + }, [lazyState]); + + useEffect(() => { + if (!localAsyncRequest) { + setLocalAsyncRequest(true); + const paginationQuery = getPaginationQuery(); + BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks); + } + }, []); + + return ( +
+ + + +
+ ) +} + +export default DataTableAsync; \ No newline at end of file diff --git a/src/components/FormField/components/FileSelect/index.js b/src/components/FormField/components/FileSelect/index.js new file mode 100644 index 0000000..64f70ac --- /dev/null +++ b/src/components/FormField/components/FileSelect/index.js @@ -0,0 +1,153 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { classNames } from 'primereact/utils'; +import { __ } from '@wordpress/i18n'; +import { isEmpty, pluck } from 'ramda'; + +// components +import { ListBox } from 'primereact/listbox'; +import { Button } from 'primereact/button'; +import CompanyDocumentsService from '../../../../service/company-documents-service'; +import { Link } from 'react-router-dom'; + +const FileSelect = ({ + fieldName, + label, + setDataFn, + register, + errors, + defaultValue, + config = {}, + infoText = null, + disabled = false, + options = [], + sourceId = 0, + source = 'DOCUMENT', + saveFormCallback + }) => { + //const [stateFieldData, setStateFieldData] = useState([]); + const stateFieldData = useRef([]); + const [selectedUnconfirmed, setSelectedUnconfirmed] = useState([]); + const [optionsTransformed, setOptionsTransformed] = useState([]); + const [loading, setLoading] = useState(false); + + const attachSelectedFiles = useCallback(() => { + const existingIds = pluck('id', stateFieldData.current); + const selectedToBeAdded = selectedUnconfirmed.filter(o => !existingIds.includes(o.id)); + setSelectedUnconfirmed([]); + + setLoading(true); + // eslint-disable-next-line array-callback-return + selectedToBeAdded.map(o => { + CompanyDocumentsService.attachCompanyDocumentToAppl(o.id, callback, errCallback, [ + ['applicationId', sourceId], + ['documentType', source] + ]) + }); + }, [selectedUnconfirmed]); + + const removeAttached = (id) => { + console.log('stateFieldData', stateFieldData, id) + } + + const callback = (resp) => { + if (resp.status === 'SUCCESS') { + stateFieldData.current = [...stateFieldData.current, resp.data]; + setDataFn(fieldName, stateFieldData.current, { shouldValidate: true }); + saveFormCallback(); + } + setLoading(false); + } + + const errCallback = () => { + setLoading(false); + } + + useEffect(() => { + console.log('selectedUnconfirmed', selectedUnconfirmed) + }, [selectedUnconfirmed]); + + useEffect(() => { + stateFieldData.current = defaultValue; + register(fieldName, config) + }, []); + + useEffect(() => { + if (!isEmpty(options)) { + const optionsDefault = [ + { + label: __('Documenti dell\'Azienda', 'gepafin'), + code: 'COMPANY_DOCUMENT', + items: options.filter(o => o.type === 'COMPANY_DOCUMENT') + }, + { + label: __('Documenti del Rappresentante Legale', 'gepafin'), + code: 'PERSONAL_DOCUMENT', + items: options.filter(o => o.type === 'PERSONAL_DOCUMENT') + } + ]; + setOptionsTransformed(optionsDefault); + } + }, [options]); + + useEffect(() => { + stateFieldData.current = defaultValue; + }, [defaultValue]); + + return ( + <> + +
+
+ setSelectedUnconfirmed(e.value)} + options={optionsTransformed} + optionLabel="name" + optionGroupLabel="label" + optionGroupChildren="items" + className="w-full md:w-14rem" + listStyle={{ maxHeight: '130px' }}/> + {!isEmpty(optionsTransformed) + ?
+ {!isEmpty(optionsTransformed) + ?
+

{__('I file selezionati')}

+ +
: null} +
+ {isEmpty(optionsTransformed) + ?
+ + {__('I file caricati sulla pagina Documenti saranno disponibili qui. ', 'gepafin')} + + {__('Vai alla pagina Documenti', 'gepafin')} + + +
+ : null} + {infoText ? {infoText} : null} + ) +} + +export default FileSelect; \ No newline at end of file diff --git a/src/components/FormField/index.js b/src/components/FormField/index.js index 6a60658..e9d7fbd 100644 --- a/src/components/FormField/index.js +++ b/src/components/FormField/index.js @@ -18,6 +18,7 @@ import Fileupload from './components/Fileupload'; import Table from './components/Table'; import PasswordField from './components/PasswordField'; import CriteriaTable from './components/CriteriaTable'; +import FileSelect from './components/FileSelect'; const FormField = (props) => { const fields = { @@ -35,7 +36,8 @@ const FormField = (props) => { checkboxes: Checkboxes, table: Table, criteria_table: CriteriaTable, - password: PasswordField + password: PasswordField, + fileselect: FileSelect, } const Comp = !isNil(fields[props.type]) ? fields[props.type] : null; diff --git a/src/helpers/getBandoLabel.js b/src/helpers/getBandoLabel.js index 7cbf3bf..9a13e36 100644 --- a/src/helpers/getBandoLabel.js +++ b/src/helpers/getBandoLabel.js @@ -14,6 +14,9 @@ const getBandoLabel = (status) => { case 'APPROVED': return __('Approvato', 'gepafin'); + case 'VALID': + return __('Valido', 'gepafin'); + case 'READY_TO_PUBLISH': return __('Pronto', 'gepafin'); @@ -29,6 +32,9 @@ const getBandoLabel = (status) => { case 'ADMISSIBLE': return __('Ammisibile', 'gepafin'); + case 'DUE': + return __('In scadenza', 'gepafin'); + case 'RESPONSE_RECEIVED': return __('Riposta ricevuta', 'gepafin'); diff --git a/src/helpers/getBandoSeverity.js b/src/helpers/getBandoSeverity.js index 03b8704..cd74015 100644 --- a/src/helpers/getBandoSeverity.js +++ b/src/helpers/getBandoSeverity.js @@ -12,6 +12,9 @@ const getBandoSeverity = (status) => { case 'APPROVED': return 'success'; + case 'VALID': + return 'success'; + case 'READY_TO_PUBLISH': return 'info'; @@ -27,6 +30,9 @@ const getBandoSeverity = (status) => { case 'ADMISSIBLE': return 'info'; + case 'DUE': + return 'warning'; + case 'RESPONSE_RECEIVED': return 'warning'; diff --git a/src/layouts/DefaultLayout/components/AppSidebar/index.js b/src/layouts/DefaultLayout/components/AppSidebar/index.js index c449022..2fbbdbb 100644 --- a/src/layouts/DefaultLayout/components/AppSidebar/index.js +++ b/src/layouts/DefaultLayout/components/AppSidebar/index.js @@ -138,6 +138,13 @@ const AppSidebar = () => { id: 15, enable: intersection(permissions, ['APPLY_CALLS']).length }, + { + label: __('Documenti', 'gepafin'), + icon: 'pi pi-folder-open', + href: '/documenti', + id: 16, + enable: intersection(permissions, ['APPLY_CALLS']).length + }, { label: __('Log di Sistema', 'gepafin'), icon: 'pi pi-receipt', diff --git a/src/pages/BandoApplication/index.js b/src/pages/BandoApplication/index.js index cdf52c3..187325e 100644 --- a/src/pages/BandoApplication/index.js +++ b/src/pages/BandoApplication/index.js @@ -1,7 +1,7 @@ import React, { useState, useEffect, useRef, useMemo } from 'react'; import { __, sprintf } from '@wordpress/i18n'; import { useParams } from 'react-router-dom'; -import { head, is, pluck, isEmpty, pathOr, isNil } from 'ramda'; +import { head, is, pluck, isEmpty, pathOr, isNil, uniqBy } from 'ramda'; import { useForm } from 'react-hook-form'; import 'quill/dist/quill.core.css'; import { wrap } from 'object-path-immutable'; @@ -47,6 +47,7 @@ import FileuploadApplicationSignedPdf from '../../components/FileuploadApplicati import { defaultMaxFileSize } from '../../configData'; import parseCommaDecimal from '../../helpers/parseCommaDecimal'; +import CompanyDocumentsService from '../../service/company-documents-service'; const BandoApplication = () => { const chosenCompanyId = useStore().main.chosenCompanyId(); @@ -62,6 +63,8 @@ const BandoApplication = () => { const [applicationStatus, setApplicationStatus] = useState(''); const [activeStep, setActiveStep] = useState(1); const [signedPdfFile, setSignedPdfFile] = useState([]); + const [companyDocs, setCompanyDocs] = useState([]); + const [personalDocs, setPersonalDocs] = useState([]); const [isRequestForApplData, setIsRequestForApplData] = useState(false); const isAsyncRequest = useStore().main.isAsyncRequest(); const previousStatus = useRef(''); @@ -217,7 +220,7 @@ const BandoApplication = () => { } fieldVal = isEmpty(fieldVal) ? null : fieldVal; - if (formField && formField.name === 'fileupload') { + if (formField && ['fileupload', 'fileselect'].includes(formField.name)) { fieldVal = is(Array, fieldVal) ? fieldVal.map(o => o.id).join(',') : null; } acc.push({ @@ -260,7 +263,7 @@ const BandoApplication = () => { }); } if (!isEmpty(saveAndMove) && is(String, saveAndMove)) { - if (['NEXT','PREVIOUS'].includes(saveAndMove)) { + if (['NEXT', 'PREVIOUS'].includes(saveAndMove)) { storeSet.main.setAsyncRequest(); ApplicationService.getApplicationForm(data.data.id, getApplFormCallback, errGetApplFormCallbacks, [ ['formId', formId], @@ -540,6 +543,26 @@ const BandoApplication = () => { ? ['.p7m', '.pdf'] : ['.p7m']; + const getDocsCallback = (resp, type) => { + if (resp.status === 'SUCCESS') { + const filteredPersonal = getFormattedDocsData(resp.data.filter(o => o.type === 'PERSONAL_DOCUMENT')); + setPersonalDocs(uniqBy((o) => o.id, filteredPersonal)); + const filteredCompany = getFormattedDocsData(resp.data.filter(o => o.type === 'COMPANY_DOCUMENT')); + setCompanyDocs(uniqBy((o) => o.id, filteredCompany)); + } + } + + + const errDocsGetCallbacks = () => { + } + + const getFormattedDocsData = (data) => { + return data.map((d) => { + d.callEndDate = is(String, d.callEndDate) ? new Date(d.callEndDate) : (d.callEndDate ? d.callEndDate : ''); + return d; + }); + }; + useEffect(() => { let updatedFormValues = klona(formValues); let context = {}; @@ -628,6 +651,13 @@ const BandoApplication = () => { ApplicationService.getApplicationForm(applId, getApplFormCallback, errGetApplFormCallbacks, [ ['companyId', chosenCompanyId] ]); + CompanyDocumentsService.getCompanyDocuments(chosenCompanyId, getDocsCallback, errDocsGetCallbacks); + /*CompanyDocumentsService.getCompanyDocuments(chosenCompanyId, (resp) => getDocsCallback(resp, 'COMPANY_DOCUMENT'), errDocsGetCallbacks, [ + ['documentType', 'COMPANY_DOCUMENT'] + ]); + CompanyDocumentsService.getCompanyDocuments(chosenCompanyId, (resp) => getDocsCallback(resp, 'PERSONAL_DOCUMENT'), errDocsGetCallbacks, [ + ['documentType', 'PERSONAL_DOCUMENT'] + ]);*/ } }, [id, chosenCompanyId]); @@ -684,7 +714,9 @@ const BandoApplication = () => { 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 options = ['fileselect'].includes(o.name) + ? { value: [...companyDocs, ...personalDocs] } + : head(o.settings.filter(o => o.name === 'options')); let tableColumns = head(o.settings.filter(o => o.name === 'table_columns')); if (!tableColumns) { tableColumns = head(o.settings.filter(o => o.name === 'criteria_table_columns')); @@ -718,9 +750,9 @@ const BandoApplication = () => { return acc; }, {}); - /*if (o.name === 'table') { - console.log('value:', values[o.id] ? values[o.id] : '') - }*/ + if (o.name === 'fileselect') { + console.log('options::', options) + } return ['paragraph'].includes(o.name) && text ?
diff --git a/src/pages/BandoEdit/components/BandoEditFormStep3/index.js b/src/pages/BandoEdit/components/BandoEditFormStep3/index.js index b67b93f..e9db65c 100644 --- a/src/pages/BandoEdit/components/BandoEditFormStep3/index.js +++ b/src/pages/BandoEdit/components/BandoEditFormStep3/index.js @@ -78,7 +78,11 @@ const BandoEditFormStep3 = forwardRef(function () { const getElementItemsCallback = (data) => { if (data.status === 'SUCCESS') { //storeSet.main.elementItems(elementItems.sort((a, b) => a.sortOrder - b.sortOrder)); - storeSet.main.elementItems(data.data.sort((a, b) => a.sortOrder - b.sortOrder)); + storeSet.main.elementItems( + data.data + .filter(o => !['fileselect'].includes(o.name)) + .sort((a, b) => a.sortOrder - b.sortOrder) + ); } storeSet.main.unsetAsyncRequest(); } diff --git a/src/pages/BandoFormsEdit/components/BuilderElement/index.js b/src/pages/BandoFormsEdit/components/BuilderElement/index.js index d0acc67..6fda4b4 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElement/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElement/index.js @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react' import { useDrag, useDrop } from 'react-dnd' import { ItemTypes } from '../ItemTypes'; import { __ } from '@wordpress/i18n'; -import { head, isEmpty } from 'ramda'; +import { head, isEmpty, pathOr } from 'ramda'; import { klona } from 'klona'; // store @@ -22,6 +22,7 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => { const ref = useRef(null); const elements = useStore().main.formElements(); const element = head(elements.filter(o => o.id === id)); + const elementSettings = pathOr([], ['settings'], element); const [isVariable, setIsVariable] = useState('secondary'); const [isFormula, setIsFormula] = useState('secondary'); const [variableName, setVariableName] = useState('secondary'); @@ -134,10 +135,10 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => { drag(drop(ref)); useEffect(() => { - const variable = head(element.settings.filter(o => o.name === 'variable')); - const formula = head(element.settings.filter(o => o.name === 'formula')); - const isRequestedAmount = head(element.settings.filter(o => o.name === 'isRequestedAmount')); - const isDelegation = head(element.settings.filter(o => o.name === 'isDelegation')); + const variable = head(elementSettings.filter(o => o.name === 'variable')); + const formula = head(elementSettings.filter(o => o.name === 'formula')); + const isRequestedAmount = head(elementSettings.filter(o => o.name === 'isRequestedAmount')); + const isDelegation = head(elementSettings.filter(o => o.name === 'isDelegation')); if (variable && !isEmpty(variable.value)) { setIsVariable('warning'); @@ -151,12 +152,16 @@ const BuilderElement = ({ id, name, label, index, bandoStatus }) => { if (isRequestedAmount && !isEmpty(isRequestedAmount.value) && isRequestedAmount.value) { setIsRequestedAmount('tertiary'); + } else { + setIsRequestedAmount(false); } if (isDelegation && !isEmpty(isDelegation.value) && isDelegation.value) { setIsDelegation('tertiary'); + } else { + setIsDelegation(false); } - }, [elements]); + }, [elementSettings]); return ( draggingElementId === id diff --git a/src/pages/BandoFormsEdit/index.js b/src/pages/BandoFormsEdit/index.js index e739c89..2c55a2b 100644 --- a/src/pages/BandoFormsEdit/index.js +++ b/src/pages/BandoFormsEdit/index.js @@ -24,7 +24,7 @@ import set404FromErrorResponse from '../../helpers/set404FromErrorResponse'; import BandoService from '../../service/bando-service'; // TODO temp data -//import { elementItems } from '../../tempData'; +import { elementItems } from '../../tempData'; const BandoFormsEdit = () => { const { id, formId } = useParams(); @@ -215,10 +215,10 @@ const BandoFormsEdit = () => { const getElementItemsCallback = (data) => { if (data.status === 'SUCCESS') { - //storeSet.main.elementItems(elementItems.sort((a, b) => a.sortOrder - b.sortOrder)); - storeSet.main.elementItems(data.data + storeSet.main.elementItems(elementItems.sort((a, b) => a.sortOrder - b.sortOrder)); + /*storeSet.main.elementItems(data.data .filter(o => o.id !== 22) - .sort((a, b) => a.sortOrder - b.sortOrder)); + .sort((a, b) => a.sortOrder - b.sortOrder));*/ } storeSet.main.unsetAsyncRequest(); } diff --git a/src/pages/Dashboard/components/LatestBandiTableAsync/index.js b/src/pages/Dashboard/components/LatestBandiTableAsync/index.js new file mode 100644 index 0000000..71421ff --- /dev/null +++ b/src/pages/Dashboard/components/LatestBandiTableAsync/index.js @@ -0,0 +1,194 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import { __ } from '@wordpress/i18n'; + +import translationStrings from '../../../../translationStringsForComponents'; + +// api +import BandoService from '../../../../service/bando-service'; + +// components +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import { Link } from 'react-router-dom'; +import { Button } from 'primereact/button'; +import ProperBandoLabel from '../../../../components/ProperBandoLabel'; +import { Dropdown } from 'primereact/dropdown'; +import { Tag } from 'primereact/tag'; +import getBandoLabel from '../../../../helpers/getBandoLabel'; +import getBandoSeverity from '../../../../helpers/getBandoSeverity'; + +const LatestBandiTableAsync = () => { + const [localAsyncRequest, setLocalAsyncRequest] = useState(false); + const [items, setItems] = useState(null); + const [totalRecordsNum, setTotalRecordsNum] = useState(0); + const [lazyState, setLazyState] = useState({ + first: 0, + rows: 5, + page: 0, + sortField: null, + sortOrder: null, + filters: { + name: { value: '', matchMode: 'contains' }, + status: { value: '', matchMode: 'contains' } + } + }); + const statuses = ['PUBLISH']; + + const getPaginationQuery = useCallback(() => { + let sortBy = { + "columnName": "ID", + "sortDesc": true + }; + + if (lazyState.sortField) { + sortBy = { + "columnName": lazyState.sortField, + "sortDesc": lazyState.sortOrder === -1 + } + } + return { + "globalFilters": { + "page": lazyState.page ? lazyState.page + 1 : 1, + "limit": lazyState.rows, + sortBy, + status: statuses + } + } + }, [lazyState]); + + const onPage = (event) => { + console.log('onPage', event); + setLazyState(event); + }; + + const onSort = (event) => { + console.log('onSort', event); + setLazyState(event); + }; + + const onFilter = useCallback((event) => { + console.log('onFilter', event); + event['first'] = 0; + setLazyState(event); + }, [lazyState]); + + const getCallback = (data) => { + if (data.status === 'SUCCESS') { + const { body, totalRecords, currentPage, totalPages, pageSize } = data.data; + setTotalRecordsNum(totalRecords); + //const newItems = body.filter(o => o.status === 'PUBLISH'); + setItems(getFormattedBandiData(body)); + } + setLocalAsyncRequest(false); + } + + const errGetCallbacks = (data) => { + setLocalAsyncRequest(false); + } + + const getFormattedBandiData = (data) => { + return [...(data || [])].map((d) => { + d.start_date = new Date(d.dates[0]); + d.end_date = new Date(d.dates[1]); + + return d; + }); + }; + + const actionsBodyTemplate = (rowData) => { + return +
+ ); + }; + + const dateCreatedBodyTemplate = (rowData) => { + return getDateFromISOstring(rowData.createdDate); + }; + const dateExpirationBodyTemplate = (rowData) => { + return getDateFromISOstring(rowData.expirationDate); + }; + + const dateFilterTemplate = (options) => { + return options.filterCallback(e.value, options.index)} + dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999"/>; + }; + + const catBodyTemplate = (rowData) => { + return rowData.category.categoryName; + }; + + const statusBodyTemplate = (rowData) => { + return ; + }; + + const statusFilterTemplate = (options) => { + return options.filterCallback(e.value, options.index)} + itemTemplate={statusItemTemplate} placeholder={__('Scegli uno', 'gepafin')} + className="p-column-filter" + showClear/>; + }; + + const statusItemTemplate = (option) => { + return ; + }; + + const handleDeleteFile = (id) => { + setLoading(true); + CompanyDocumentsService.deleteCompanyDocument(id,(resp) => deleteCallback(resp, id), errDeleteCallback) + } + + const deleteCallback = (resp, deletedId) => { + if (resp.status === 'SUCCESS') { + setDocs(docs.filter(o => o.id !== deletedId)); + } + setLoading(false); + } + + const errDeleteCallback = () => { + setLoading(false); + } + + const actionsBodyTemplate = (rowData) => { + return
+
+ } + + const confirmDelete = (event, id) => { + confirmPopup({ + target: event.currentTarget, + message: __('Sei sicuro di voler rimuovere il file?', 'gepafin'), + acceptLabel: __('Si', 'gepafin'), + icon: 'pi pi-info-circle', + defaultFocus: 'reject', + acceptClassName: 'p-button-danger', + accept: () => { + handleDeleteFile(id); + }, + reject: () => { + } + }); + }; + + const header = renderHeader(); + + return ( +
+ setFilters(e.filters)}> + + + + + + + +
+ ) +} + +export default DocumentsTable; diff --git a/src/pages/DocumentsBeneficiary/index.js b/src/pages/DocumentsBeneficiary/index.js new file mode 100644 index 0000000..7f99c92 --- /dev/null +++ b/src/pages/DocumentsBeneficiary/index.js @@ -0,0 +1,230 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { __ } from '@wordpress/i18n'; +import { classNames } from 'primereact/utils'; +import { wrap } from 'object-path-immutable'; +import { isEmpty, isNil } from 'ramda'; + +// api +import DocumentCategoryService from '../../service/document-category-service'; + +// components +import DocumentsTable from './components/DocumentsTable'; +import { Dialog } from 'primereact/dialog'; +import { Button } from 'primereact/button'; +import { Dropdown } from 'primereact/dropdown'; +import { InputText } from 'primereact/inputtext'; +import { useStore } from '../../store'; +import { Calendar } from 'primereact/calendar'; +import { FileUpload } from 'primereact/fileupload'; +import formatDateString from '../../helpers/formatDateString'; +import CompanyDocumentsService from '../../service/company-documents-service'; + + +const DocumentsBeneficiary = () => { + const [loading, setLoading] = useState(false); + const chosenCompanyId = useStore().main.chosenCompanyId(); + const [isVisibleAddNewDialog, setIsVisibleAddNewDialog] = useState(false); + const [categories, setCategories] = useState(false); + const [newFileData, setNewFileData] = useState({}); + const [fileAttached, setFileAttached] = useState([]); + const [reloadHash, setReloadHash] = useState(0); + const today = new Date(); + const tomorrow = new Date(today); + tomorrow.setDate(today.getDate() + 1); + + const onCreateNew = useCallback((type) => { + const newData = wrap({}) + .set(['documentType'], type) + .set(['documentCategoryId'], 0) + .set(['expirationDate'], '') + .set(['name'], '') + .value(); + + setNewFileData(newData); + setFileAttached([]); + setIsVisibleAddNewDialog(true); + }, [newFileData, chosenCompanyId]); + + const hideAddNewDialog = () => { + setIsVisibleAddNewDialog(false); + const newData = wrap({}) + .set(['documentType'], '') + .set(['documentCategoryId'], 0) + .set(['expirationDate'], '') + .set(['name'], '') + .value(); + setNewFileData(newData); + setFileAttached([]); + } + + const headerAddNewDialog = () => { + return {__('Aggiungi file', 'gepafin')} + } + + const footerAddNewDialog = () => { + return
+
+ } + + const isValidForm = useCallback(() => { + return !isEmpty(fileAttached) && !Object.keys(newFileData).filter(k => isInvalidField(newFileData, k) + || newFileData[k] === 0).length + }, [fileAttached, newFileData]); + + const doAddNew = useCallback(() => { + const submitData = { + ...newFileData, + expirationDate: formatDateString(newFileData.expirationDate) + } + const queryParams = Object.keys(submitData).map(k => [ + k, submitData[k] + ]); + + if (!isEmpty(fileAttached)) { + const formData = new FormData() + for (const file of fileAttached) { + formData.append('file', file) + } + setLoading(true); + CompanyDocumentsService.uploadCompanyDocument(chosenCompanyId, formData, uploadDoc, errUploadDoc, queryParams); + } + }, [fileAttached, newFileData, chosenCompanyId]); + + const uploadDoc = (resp) => { + if (resp.status === 'SUCCESS') { + hideAddNewDialog(); + setReloadHash(new Date().getTime()) + } + setLoading(false); + } + + const errUploadDoc = () => { + setLoading(false); + } + + const isInvalidField = (data, key) => isEmpty(data[key]) || isNil(data[key]); + + const onUpdateFieldValue = useCallback((value, name) => { + const newData = wrap(newFileData).set([name], value).value(); + setNewFileData(newData); + }, [newFileData]); + + const onFileSelect = (file) => { + setFileAttached(file.files); + } + + const getCategories = (resp) => { + if (resp.status === 'SUCCESS') { + setCategories(resp.data.map(o => ({value: o.id, label: o.description}))); + } + setLoading(false); + } + + const errGetCategories = () => { + setLoading(false); + } + + useEffect(() => { + setLoading(true); + DocumentCategoryService.getCategories(getCategories, errGetCategories) + }, []); + + return( +
+
+

{__('Gestione documenti', 'gepafin')}

+
+ +
+ +
+

+ {__('Documenti del rappresentante legale', 'gepafin')} +

+ +
+ +
+ +
+

+ {__('Documenti dell\'azienda', 'gepafin')} +

+ +
+ + +
+
+ + onUpdateFieldValue(e.target.value, 'name')}/> +
+
+ + onUpdateFieldValue(e.value, 'documentCategoryId')} + options={categories} + optionLabel="label" + optionValue="value"/> +
+
+
+
+ + onUpdateFieldValue(e.value, 'expirationDate')}/> +
+
+
+
+ + +
+
+
+
+ ) +} + +export default DocumentsBeneficiary; \ No newline at end of file diff --git a/src/routes.js b/src/routes.js index f5555e3..75a1caa 100644 --- a/src/routes.js +++ b/src/routes.js @@ -51,6 +51,7 @@ import SoccorsoEditInstructorManager from './pages/SoccorsoEditInstructorManager import SoccorsoIstruttorioInstructorManager from './pages/SoccorsoIstruttorioInstructorManager'; import SoccorsoIstruttorioMioInstructorManager from './pages/SoccorsoIstruttorioMioInstructorManager'; import StatsBeneficiary from './pages/StatsBeneficiary'; +import DocumentsBeneficiary from './pages/DocumentsBeneficiary'; const routes = ({ role, chosenCompanyId }) => { @@ -243,6 +244,12 @@ const routes = ({ role, chosenCompanyId }) => { {'ROLE_PRE_INSTRUCTOR' === role ? : null} {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} }/> + + {'ROLE_SUPER_ADMIN' === role ? : null} + {'ROLE_BENEFICIARY' === role ? : null} + {'ROLE_PRE_INSTRUCTOR' === role ? : null} + {'ROLE_INSTRUCTOR_MANAGER' === role ? : null} + }/> }/> }/> diff --git a/src/service/company-documents-service.js b/src/service/company-documents-service.js new file mode 100644 index 0000000..3d38b71 --- /dev/null +++ b/src/service/company-documents-service.js @@ -0,0 +1,24 @@ +import { NetworkService } from './network-service'; + +const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS; + +export default class CompanyDocumentsService { + + static getCompanyDocuments = (companyId, callback, errCallback, queryParams) => { + NetworkService.get(`${API_BASE_URL}/companyDocument/company/${companyId}`, callback, errCallback, queryParams); + }; + + static uploadCompanyDocument = (id, body, callback, errCallback, queryParams) => { + NetworkService.postMultiPart(`${API_BASE_URL}/companyDocument/company/${id}/upload`, body, callback, errCallback, queryParams); + }; + + static attachCompanyDocumentToAppl = (id, callback, errCallback, queryParams) => { + NetworkService.put(`${API_BASE_URL}/companyDocument/${id}/document/upload`, {}, callback, errCallback, queryParams); + }; + + static deleteCompanyDocument = (id, callback, errCallback) => { + NetworkService.delete(`${API_BASE_URL}/companyDocument`, {}, callback, errCallback, [ + ['id', id] + ]); + }; +} diff --git a/src/service/document-category-service.js b/src/service/document-category-service.js new file mode 100644 index 0000000..1cdb30b --- /dev/null +++ b/src/service/document-category-service.js @@ -0,0 +1,10 @@ +import { NetworkService } from './network-service'; + +const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS; + +export default class DocumentCategoryService { + + static getCategories = (callback, errCallback) => { + NetworkService.get(`${API_BASE_URL}/documentCategory`, callback, errCallback); + }; +} diff --git a/src/tempData.js b/src/tempData.js index 431f555..5e7812f 100644 --- a/src/tempData.js +++ b/src/tempData.js @@ -483,5 +483,25 @@ export const elementItems = [ validators: { isRequired: false } + }, + { + id: 23, + sortOrder: 23, + name: 'fileselect', + label: 'Seleziona File', + description: "Per selezionare di documenti o immagini", + settings: [ + { + name: "label", + value: "Seleziona File" + }, + { + name: "isDelegation", + value: false + } + ], + validators: { + isRequired: false + } } ]