diff --git a/package.json b/package.json index 05d514e..a463d6b 100644 --- a/package.json +++ b/package.json @@ -7,54 +7,53 @@ "@babel/preset-react": "7.26.3", "@date-fns/tz": "1.2.0", "@emailjs/browser": "4.4.1", - "@emotion/styled": "11.14.0", - "@number-flow/react": "0.5.5", - "@sentry/browser": "8.51.0", - "@stomp/stompjs": "7.0.0", - "@tanstack/react-table": "8.20.6", - "@wordpress/i18n": "5.16.0", - "@wordpress/react-i18n": "4.16.0", + "@number-flow/react": "0.5.9", + "@sentry/browser": "9.11.0", + "@stomp/stompjs": "7.1.1", + "@tanstack/react-table": "8.21.2", + "@wordpress/i18n": "5.21.0", + "@wordpress/react-i18n": "4.21.0", "codice-fiscale-js": "2.3.22", "copy-to-clipboard": "3.3.3", "deep-object-diff": "1.1.9", - "dompurify": "3.2.3", + "dompurify": "3.2.5", "expression-language": "1.2.0", "fast-deep-equal": "3.1.3", "hotkeys-js": "3.13.9", - "html-react-parser": "5.2.2", + "html-react-parser": "5.2.3", "jwt-decode": "4.0.0", "klona": "2.0.6", "leader-line-new": "1.1.9", - "luxon": "3.5.0", - "mathjs": "14.0.1", + "luxon": "3.6.1", + "mathjs": "14.4.0", "mustache": "4.2.0", "object-path-immutable": "4.1.2", "primeicons": "7.0.0", - "primereact": "10.9.2", + "primereact": "10.9.4", "quill": "2.0.3", "ramda": "0.30.1", - "react": "18.3.1", + "react": "19.1.0", "react-dnd": "16.0.1", "react-dnd-html5-backend": "16.0.1", - "react-dom": "18.3.1", - "react-hook-form": "7.54.2", - "react-router-dom": "7.1.3", + "react-dom": "19.1.0", + "react-hook-form": "7.55.0", + "react-router-dom": "7.5.0", "react-scripts": "5.0.1", - "recharts": "2.15.0", + "recharts": "2.15.2", "sockjs-client": "1.6.1", "validate.js": "0.13.1", - "zustand": "4.5.4", - "zustand-x": "3.0.4" + "zustand": "5.0.3", + "zustand-x": "6.1.0" }, "devDependencies": { - "@babel/cli": "7.26.4", - "@babel/core": "7.26.7", + "@babel/cli": "7.27.0", + "@babel/core": "7.26.10", "@babel/plugin-syntax-jsx": "7.25.9", - "@wordpress/babel-plugin-makepot": "6.16.0", + "@wordpress/babel-plugin-makepot": "6.21.0", "babel-plugin-macros": "3.1.0", "node-wp-i18n": "1.2.7", - "sass": "1.83.4", - "sass-loader": "16.0.4" + "sass": "1.86.3", + "sass-loader": "16.0.5" }, "scripts": { "start": "GENERATE_SOURCEMAP=false react-scripts start", diff --git a/src/App.js b/src/App.js index ad0f6ef..d233d43 100644 --- a/src/App.js +++ b/src/App.js @@ -1,58 +1,60 @@ import { useEffect } from 'react'; import { BrowserRouter } from 'react-router-dom'; import Routes from './routes'; -import { createI18n, setLocaleData } from '@wordpress/i18n'; -import { I18nProvider } from '@wordpress/react-i18n'; +import { + //createI18n, + setLocaleData } from '@wordpress/i18n'; +//import { I18nProvider } from '@wordpress/react-i18n'; import './assets/scss/theme.scss'; import { isEmpty, head } from 'ramda'; import { addLocale, PrimeReactProvider } from 'primereact/api'; // store -import { useStore, storeSet, storeGet } from './store'; +import { useStoreValue, storeSet, storeGet } from './store'; // api import AuthenticationService from './service/authentication-service'; -const i18n = createI18n({}, 'gepafin'); +//const i18n = createI18n({}, 'gepafin'); function App() { - const role = useStore().main.getRole(); - const chosenCompanyId = useStore().main.chosenCompanyId(); - const isRedirectedOnceNoCompany = useStore().main.isRedirectedOnceNoCompany(); + const role = useStoreValue('getRole'); + const chosenCompanyId = useStoreValue('chosenCompanyId'); + const isRedirectedOnceNoCompany = useStoreValue('isRedirectedOnceNoCompany'); const value = { locale: 'it', }; - const callback = (data) => { - if (data.status === 'SUCCESS') { - storeSet.main.userData(data.data); + const callback = (resp) => { + if (resp.status === 'SUCCESS') { + storeSet('userData', resp.data); } else { - storeSet.main.doLogout(); + storeSet('doLogout'); } - storeSet.main.unsetAsyncRequest(); + storeSet('unsetAsyncRequest'); } - const errCallback = (data) => { - storeSet.main.unsetAsyncRequest(); + const errCallback = () => { + storeSet('unsetAsyncRequest'); } useEffect(() => { if (['ROLE_BENEFICIARY', 'ROLE_CONFIDI'].includes(role)) { - const userData = storeGet.main.userData(); + const userData = storeGet('userData'); if (userData.companies && !isEmpty(userData.companies)) { - storeSet.main.companies(userData.companies); + storeSet('companies', userData.companies); const company = head(userData.companies.filter(o => o.id === chosenCompanyId)); if (!company) { - storeSet.main.chosenCompanyId(userData.companies[0].id); + storeSet('chosenCompanyId', userData.companies[0].id); } } else { - storeSet.main.chosenCompanyId(0); + storeSet('chosenCompanyId', 0); const { origin, href } = window.location; const url = `${origin}/agguingi-azienda`; if (!isRedirectedOnceNoCompany && url !== href) { - storeSet.main.isRedirectedOnceNoCompany(true); + storeSet('isRedirectedOnceNoCompany', true); window.location.replace('/agguingi-azienda') } } @@ -60,7 +62,7 @@ function App() { }, [role]); useEffect(() => { - storeSet.main.setAsyncRequest(); + storeSet('setAsyncRequest'); AuthenticationService.me(callback, errCallback); addLocale('it', { @@ -116,14 +118,22 @@ function App() { }, []); return ( - + + + + + + ); +} + +export default App; + +/* + - ); -} - -export default App; + */ \ No newline at end of file diff --git a/src/assets/scss/components/formBuilder.scss b/src/assets/scss/components/formBuilder.scss index 00db384..23fcb37 100644 --- a/src/assets/scss/components/formBuilder.scss +++ b/src/assets/scss/components/formBuilder.scss @@ -256,6 +256,10 @@ grid-template-columns: 4.5fr 2.4fr 1fr 1.4fr 0.7fr; } + &.tableRowCsv { + grid-template-columns: 3fr 5fr 1fr; + } + > div { display: flex; align-items: center; diff --git a/src/components/FileuploadApplicationSignedPdf/index.js b/src/components/FileuploadApplicationSignedPdf/index.js index 286d08f..3490c50 100644 --- a/src/components/FileuploadApplicationSignedPdf/index.js +++ b/src/components/FileuploadApplicationSignedPdf/index.js @@ -68,7 +68,7 @@ const FileuploadApplicationSignedPdf = ({ disabled={disabled} icon="pi pi-times" severity="danger" - aria-label={__('Anulla', 'gepafin')} + aria-label={__('Annulla', 'gepafin')} onClick={() => onTemplateRemove(file)}/> diff --git a/src/components/FileuploadDelega/index.js b/src/components/FileuploadDelega/index.js index afa70a4..6457853 100644 --- a/src/components/FileuploadDelega/index.js +++ b/src/components/FileuploadDelega/index.js @@ -67,7 +67,7 @@ const FileuploadDelega = ({ disabled={disabled} icon="pi pi-times" severity="danger" - aria-label={__('Anulla', 'gepafin')} + aria-label={__('Annulla', 'gepafin')} onClick={() => onTemplateRemove(file)}/> diff --git a/src/components/FormField/components/Fileupload/index.js b/src/components/FormField/components/Fileupload/index.js index 9c21022..a8218a7 100644 --- a/src/components/FormField/components/Fileupload/index.js +++ b/src/components/FormField/components/Fileupload/index.js @@ -104,7 +104,7 @@ const Fileupload = ({ severity="danger" type="button" disabled={disabled} - aria-label={__('Anulla', 'gepafin')} + aria-label={__('Annulla', 'gepafin')} onClick={(e) => confirmDelete(e, file)}/> diff --git a/src/components/FormField/components/FileuploadAsync/index.js b/src/components/FormField/components/FileuploadAsync/index.js index d28c704..b422c78 100644 --- a/src/components/FormField/components/FileuploadAsync/index.js +++ b/src/components/FormField/components/FileuploadAsync/index.js @@ -85,7 +85,7 @@ const FileuploadAsync = ({ disabled={disabled} icon="pi pi-times" severity="danger" - aria-label={__('Anulla', 'gepafin')} + aria-label={__('Annulla', 'gepafin')} onClick={() => onTemplateRemove(file)}/> diff --git a/src/components/FormFieldRepeaterFaq/index.js b/src/components/FormFieldRepeaterFaq/index.js index f2223f5..4662f20 100644 --- a/src/components/FormFieldRepeaterFaq/index.js +++ b/src/components/FormFieldRepeaterFaq/index.js @@ -109,7 +109,7 @@ const FormFieldRepeaterFaq = ({ const footerEditDialog = () => { return
-
+ } + + const exportToCSV = (applicationId) => { + setLocalAsyncRequest(true); + ApplicationService.downloadCsvReport( + applicationId, + (resp) => getCsvReportback(resp, applicationId), + errCsvReportCallback + ) + } + + const getCsvReportback = (resp, applicationId) => { + const file = new Blob([resp], { type: 'text/csv' }); + const url = window.URL.createObjectURL(file); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', `call-${applicationId}-applications-report.csv`); + document.body.appendChild(link); + link.click(); + link.remove(); + setLocalAsyncRequest(false); + } + + const errCsvReportCallback = (resp) => { + set404FromErrorResponse(resp); + setLocalAsyncRequest(false); + } + + const statusBodyTemplate = (rowData) => { + return ; + }; + + const statusItemTemplate = (option) => { + return ; + }; + + const statusFilterTemplate = (options) => { + return { + options.filterCallback(e.value, options.index) + const filters = { ...lazyState.filters }; + if (e.value) { + filters['status'] = { value: e.value, matchMode: 'equals' }; + } else { + delete filters['status']; + } + setLazyState({ ...lazyState, filters, first: 0 }); + }} + itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} + className="p-column-filter"/>; + }; + + const dateFilterTemplate = (options) => { + return options.filterCallback(e.value, options.index)} + dateFormat="dd/mm/yy" placeholder="dd/mm/yyyy" mask="99/99/9999"/>; + }; + + const dateStartBodyTemplate = (rowData) => { + const startTimeObj = getTimeParsedFromString(rowData.startTime); + return getFormattedDateString(rowData.startDate) + ' ' + getTimeFromISOstring(startTimeObj); + }; + + const dateEndBodyTemplate = (rowData) => { + const endTimeObg = getTimeParsedFromString(rowData.endTime); + return getFormattedDateString(rowData.endDate) + ' ' + getTimeFromISOstring(endTimeObg); + }; + + useEffect(() => { + setLocalAsyncRequest(true); + const paginationQuery = getPaginationQuery(); + + BandoService.getBandiPaginated(paginationQuery, getCallback, errGetCallbacks); + }, [lazyState]); + + return ( +
+ + + + + + + +
+ ) +} + +export default AllBandiTableAsync; \ No newline at end of file diff --git a/src/pages/Bandi/index.js b/src/pages/Bandi/index.js index 756d6fb..406fe6e 100644 --- a/src/pages/Bandi/index.js +++ b/src/pages/Bandi/index.js @@ -3,8 +3,8 @@ import { __ } from '@wordpress/i18n'; import { useNavigate } from 'react-router-dom'; // components -import AllBandiTable from './components/AllBandiTable'; import { Button } from 'primereact/button'; +import AllBandiTableAsync from './components/AllBandiTableAsync'; const Bandi = () => { const navigate = useNavigate(); @@ -28,7 +28,7 @@ const Bandi = () => { label={__('Crea nuovo bando')} icon="pi pi-plus" iconPos="right"/> - + ) diff --git a/src/pages/BandiBeneficiario/components/AllBandiAccordion/index.js b/src/pages/BandiBeneficiario/components/AllBandiAccordion/index.js index 8fd5d57..d65d2a3 100644 --- a/src/pages/BandiBeneficiario/components/AllBandiAccordion/index.js +++ b/src/pages/BandiBeneficiario/components/AllBandiAccordion/index.js @@ -5,7 +5,7 @@ import { wrap } from 'object-path-immutable'; import { useNavigate } from 'react-router-dom'; // store -import { storeGet, storeSet, useStore } from '../../../../store'; +import { storeGet, storeSet, useStoreValue } from '../../../../store'; // tools import getBandoSeverity from '../../../../helpers/getBandoSeverity'; @@ -36,9 +36,9 @@ import { Badge } from 'primereact/badge'; const REACT_APP_HUB_ID = process.env.REACT_APP_HUB_ID; const AllBandiAccordion = ({ showOnlyPreferred = false }) => { - const chosenCompanyId = useStore().main.chosenCompanyId(); - const companies = useStore().main.companies(); - const isAsyncRequest = useStore().main.isAsyncRequest(); + const chosenCompanyId = useStoreValue('chosenCompanyId'); + const companies = useStoreValue('companies'); + const isAsyncRequest = useStoreValue('isAsyncRequest'); const [items, setItems] = useState(null); const [filters, setFilters] = useState(null); const [expandedRows, setExpandedRows] = useState(null); @@ -49,7 +49,7 @@ const AllBandiAccordion = ({ showOnlyPreferred = false }) => { const existingCompany = head(companies.filter(o => o.id === chosenCompanyId)); if (existingCompany && !isAsyncRequest) { - storeSet.main.setAsyncRequest(); + storeSet('setAsyncRequest'); BandoService.getBandi(getCallback, errGetCallbacks, [ ['companyId', chosenCompanyId], ['onlyPreferredCall', showOnlyPreferred] @@ -62,12 +62,12 @@ const AllBandiAccordion = ({ showOnlyPreferred = false }) => { setItems(getFormattedBandiData(data.data)); setStatuses(uniq(data.data.map(o => o.status))) } - storeSet.main.unsetAsyncRequest(); + storeSet('unsetAsyncRequest'); } const errGetCallbacks = (data) => { set404FromErrorResponse(data); - storeSet.main.unsetAsyncRequest(); + storeSet('unsetAsyncRequest'); } const getFormattedBandiData = (data) => { @@ -116,8 +116,7 @@ const AllBandiAccordion = ({ showOnlyPreferred = false }) => { const statusFilterTemplate = (options) => { return options.filterCallback(e.value, options.index)} - itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter" - showClear/>; + itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter"/>; }; const statusItemTemplate = (option) => { @@ -125,7 +124,7 @@ const AllBandiAccordion = ({ showOnlyPreferred = false }) => { }; const addToFavourites = (id, preferredId) => { - const companyId = storeGet.main.chosenCompanyId() + const companyId = storeGet('chosenCompanyId') const data = { companyId, callId: id diff --git a/src/pages/BandiBeneficiario/index.js b/src/pages/BandiBeneficiario/index.js index ec107f7..8047d2e 100644 --- a/src/pages/BandiBeneficiario/index.js +++ b/src/pages/BandiBeneficiario/index.js @@ -4,15 +4,15 @@ import { Link } from 'react-router-dom'; import { head, isEmpty } from 'ramda'; // store -import { useStore } from '../../store'; +import { useStoreValue } from '../../store'; // components import AllBandiAccordion from './components/AllBandiAccordion'; import ErrorBoundary from '../../components/ErrorBoundary'; const BandiBeneficiario = () => { - const chosenCompanyId = useStore().main.chosenCompanyId(); - const companies = useStore().main.companies(); + const chosenCompanyId = useStoreValue('chosenCompanyId'); + const companies = useStoreValue('companies'); const company = head(companies.filter(o => o.id === chosenCompanyId)); return ( diff --git a/src/pages/BandiPreInstructor/components/AllBandiPreInstructorTableAsync/index.js b/src/pages/BandiPreInstructor/components/AllBandiPreInstructorTableAsync/index.js new file mode 100644 index 0000000..4f5b400 --- /dev/null +++ b/src/pages/BandiPreInstructor/components/AllBandiPreInstructorTableAsync/index.js @@ -0,0 +1,180 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import { __ } from '@wordpress/i18n'; +import { Link } from 'react-router-dom'; + +import translationStrings from '../../../../translationStringsForComponents'; + +// api +import BandoService from '../../../../service/bando-service'; + +// tools +import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString'; +import getTimeFromISOstring from '../../../../helpers/getTimeFromISOstring'; +import getFormattedDateString from '../../../../helpers/getFormattedDateString'; +import getBandoLabel from '../../../../helpers/getBandoLabel'; +import getBandoSeverity from '../../../../helpers/getBandoSeverity'; +import getQueryParamsForPaginatedEndpoint from '../../../../helpers/getQueryParamsForPaginatedEndpoint'; + +// components +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import { Button } from 'primereact/button'; +import ProperBandoLabel from '../../../../components/ProperBandoLabel'; +import { Dropdown } from 'primereact/dropdown'; +import { Tag } from 'primereact/tag'; +import { Calendar } from 'primereact/calendar'; + +const AllBandiPreInstructorTableAsync = () => { + 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: null, matchMode: 'contains' }, + startDate: { value: null, matchMode: 'dateIs' }, + endDate: { value: null, matchMode: 'dateIs' }, + status: { value: null, matchMode: 'equals' } + } + }); + const statuses = ['DRAFT','PUBLISH','EXPIRED']; + + const getPaginationQuery = useCallback(() => getQueryParamsForPaginatedEndpoint(lazyState, statuses, 'id'), [lazyState]); + + const onPage = (event) => { + setLazyState(event); + }; + + const onSort = (event) => { + event['first'] = 0; + event['page'] = 0; + setLazyState(event); + }; + + const onFilter = useCallback((event) => { + event['first'] = 0; + event['page'] = 0; + setLazyState(event); + }, [lazyState]); + + const getCallback = (resp) => { + if (resp.status === 'SUCCESS') { + const { body, totalRecords, + //currentPage, totalPages, pageSize + } = resp.data; + setTotalRecordsNum(totalRecords); + setItems(getFormattedData(body)); + } + setLocalAsyncRequest(false); + } + + const errGetCallbacks = () => { + setLocalAsyncRequest(false); + } + + const getFormattedData = (data) => { + return [...(data || [])].map((d) => { + d.startDate = new Date(d.dates[0]); + d.endDate = new Date(d.dates[1]); + + return d; + }); + }; + + const actionsBodyTemplate = (rowData) => { + return + + +