diff --git a/package.json b/package.json index b1eef76..c998aa0 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "@date-fns/tz": "1.1.2", "@emailjs/browser": "^4.4.1", "@emotion/styled": "11.13.0", - "@tanstack/react-table": "8.20.5", + "@number-flow/react": "0.2.0", + "@tanstack/react-table": "^8.20.5", "@wordpress/i18n": "5.8.0", "@wordpress/react-i18n": "4.8.0", "@xyflow/react": "12.3.1", diff --git a/src/assets/scss/components/appForm.scss b/src/assets/scss/components/appForm.scss index db6b970..bb2c482 100644 --- a/src/assets/scss/components/appForm.scss +++ b/src/assets/scss/components/appForm.scss @@ -2,6 +2,7 @@ display: flex; flex-direction: column; gap: 24px; + width: 100%; } .appForm__field { position: relative; @@ -10,6 +11,11 @@ gap: 14px; padding: 5px 0; width: 100%; + + &.row { + flex-direction: row; + align-items: center; + } label { font-size: 14px; diff --git a/src/assets/scss/components/appPage.scss b/src/assets/scss/components/appPage.scss index 522398d..fab7e1e 100644 --- a/src/assets/scss/components/appPage.scss +++ b/src/assets/scss/components/appPage.scss @@ -88,6 +88,18 @@ display: flex; flex-direction: column; align-items: flex-start; + width: 100%; + + &.columns { + display: grid; + gap: 1rem; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + width: 100%; + + /*> div { + max-width: 50%; + }*/ + } h2 { color: var(--global-textColor); @@ -98,11 +110,27 @@ margin: 0 0 24px; } + h3 { + color: var(--global-textColor); + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: normal; + margin: 0 0 16px; + } + .row { display: flex; gap: 1rem; align-items: center; padding: 5px 0; + width: 100%; + } + + .col { + display: flex; + flex-direction: column; + gap: 1rem; } } @@ -117,6 +145,10 @@ container-name: section_with_border; container-type: inline-size; + &.grey { + border-color: var(--table-border-color) + } + &.disabled { filter: grayscale(1); opacity: 0.8; @@ -149,14 +181,101 @@ flex: 1 1 100%; } - ul { + ul, ol { padding-left: 1rem; + + li { + color: var(--global-textColor); + } + } + + a { + color: var(--global-textColor); + text-decoration: underline; + + &:hover { + text-decoration: none; + } } button { flex: 0 0 auto; } } + + &.columns { + gap: 2em; + column-count: 2; + column-width: 4em; + display: block; + padding-bottom: 0; + + .appPageSection__pMeta { + margin-bottom: 1em; + } + } +} + +.appPageSection__list { + display: flex; + flex-direction: column; + padding: 0; + width: 100%; + + > li { + padding: 15px; + border-bottom: 1px solid var(--button-secondary-borderColor); + display: flex; + justify-content: space-between; + align-items: center; + flex-direction: column; + gap: 1rem; + + &.row { + flex-direction: row; + } + } +} + +.appPageSection__checklist { + display: flex; + flex-direction: column; + gap: 1rem; + + div { + display: flex; + gap: 0.5rem; + } +} + +.appPageSection__iconActions { + display: flex; + gap: 0.5em; +} + +.appPageSection__message { + display: flex; + align-items: center; + gap: 20px; + background: rgba(255, 242, 226, 0.7); + border-style: solid; + border-width: 0 0 0 6px; + padding: 1.25rem 1.75rem; + border-radius: 6px; + + &.warning { + color: var(--message-warning-color); + border-color: var(--message-warning-color); + } + + &.info { + color: var(--message-info-color); + border-color: var(--message-info-color); + } + + .summary { + font-weight: bold; + } } @container section_with_border (max-width: 600px) { @@ -215,6 +334,10 @@ font-weight: 600; line-height: normal; } + + span:nth-of-type(2) { + font-weight: 400; + } } .appPageSection__table { @@ -269,4 +392,12 @@ &[disabled] { background-color: var(--message-info-background); } +} + +@media (max-width: 700px) { + .appPageSection { + &.columns { + grid-template-columns: 1fr; + } + } } \ No newline at end of file diff --git a/src/assets/scss/components/evaluation.scss b/src/assets/scss/components/evaluation.scss new file mode 100644 index 0000000..1498d2f --- /dev/null +++ b/src/assets/scss/components/evaluation.scss @@ -0,0 +1,41 @@ +.criterionRelatedData { + display: flex; + flex-direction: column; + gap: 15px; + max-height: 400px; + overflow-y: auto; + padding: 0 0 10px 0; + + > h3 { + margin: 0; + } +} + +.criterionRelatedData__item { + display: flex; + flex-direction: column; + gap: 7px; + padding: 10px; + background-color: #f5f5f5; + font-size: 14px; + + ul { + margin: 0; + padding-left: 15px; + + li a { + text-decoration: underline; + color: var(--global-textColor); + } + } + + table, th, td { + border: 1px solid var(--table-border-color); + border-collapse: collapse; + text-align: left; + } + + th, td { + padding: 3px; + } +} \ No newline at end of file diff --git a/src/assets/scss/components/layout.scss b/src/assets/scss/components/layout.scss index 1792b9d..d4e3572 100644 --- a/src/assets/scss/components/layout.scss +++ b/src/assets/scss/components/layout.scss @@ -12,9 +12,19 @@ body { margin: 0; font-family: "Montserrat", sans-serif; - p, span:not(.p-button-label, .p-button-icon, .p-badge, .p-message-detail, .p-highlight, .p-inline-message-text), - input, label:not(.p-error), textarea, a, li, h1, h2, h3, h4, h5, h6, div:not(.p-inline-message, .p-toast-detail), th, td { + /*p, span:not(.p-button-label, .p-button-icon, .p-badge, .p-message-detail, .p-message-summary, .p-highlight, .p-inline-message-text, .p-tag, .p-tag-icon), + input:not(.p-checkbox-input), + label:not(.p-error), textarea, a, li, h1, h2, h3, h4, h5, h6, div:not(.p-inline-message, .p-toast-detail, .p-checkbox-box), th, td, + :not(svg, path, .p-button, .p-button > span, .statsBigBadges__gridItem > span, number-flow, .p-paginator-page, .p-badge) { color: var(--global-textColor); + }*/ + + h2, h3, p, label, .appPageSection__hr, li { + color: var(--global-textColor); + } + + textarea { + max-width: 100%; } } @@ -87,6 +97,12 @@ img { color: white; } + svg { + path { + fill: white; + } + } + span { color: var(--menuitem-active-color); } diff --git a/src/assets/scss/components/misc.scss b/src/assets/scss/components/misc.scss index b2c5847..8d5a701 100644 --- a/src/assets/scss/components/misc.scss +++ b/src/assets/scss/components/misc.scss @@ -113,16 +113,15 @@ background-color: rgba(255,255,255,0.3) } -.p-inputgroup { - align-items: center; -} - .p-accordion-header-text, .p-accordion-content { p { margin: 0; } } +.p-inputgroup.flex-1 { + align-items: center; +} .flex-1 { display: flex; align-items: center; diff --git a/src/assets/scss/components/myTable.scss b/src/assets/scss/components/myTable.scss new file mode 100644 index 0000000..bbfbb2a --- /dev/null +++ b/src/assets/scss/components/myTable.scss @@ -0,0 +1,39 @@ +.myTable { + border-spacing: 0px; + width: 100%; +} + +.myThead { + th { + text-align: left; + padding: 1rem 1rem; + border: 1px solid #e5e7eb; + border-width: 0 0 1px 0; + font-weight: 700; + color: #374151; + background: #f9fafb; + transition: box-shadow 0.2s; + } +} + +.myTbody { + td { + text-align: left; + border: 1px solid #e5e7eb; + border-width: 0 0 1px 0; + padding: 1rem 1rem; + } +} + +.myTfoot { + td { + text-align: left; + padding: 1rem 1rem; + border: 1px solid #e5e7eb; + border-width: 0 0 1px 0; + font-weight: 700; + color: #374151; + background: #f9fafb; + transition: box-shadow 0.2s; + } +} \ No newline at end of file diff --git a/src/assets/scss/theme.scss b/src/assets/scss/theme.scss index 08279a3..2bd1bad 100644 --- a/src/assets/scss/theme.scss +++ b/src/assets/scss/theme.scss @@ -18,6 +18,7 @@ --table-border-color: #B7B7B7B2; --message-error-background: #ffdbdb; --message-error-color: #C2504D; + --message-warning-color: #cc8925; --message-info-background: rgba(183, 183, 183, 0.7); --message-info-color: #3B82F6; @@ -40,4 +41,6 @@ @import "./components/misc.scss"; @import "./components/login.scss"; @import "./components/flowBuilder.scss"; -@import "./components/error404.scss"; \ No newline at end of file +@import "./components/error404.scss"; +@import "./components/myTable.scss"; +@import "./components/evaluation.scss"; \ No newline at end of file diff --git a/src/components/TopBarProfileMenu/index.js b/src/components/TopBarProfileMenu/index.js index 32bfac6..4288572 100644 --- a/src/components/TopBarProfileMenu/index.js +++ b/src/components/TopBarProfileMenu/index.js @@ -55,7 +55,7 @@ const TopBarProfileMenu = ({ menuLeftRef }) => { command: () => { navigate('/profilo-aziendale') }, - enable: !intersection(permissions, ['MANAGE_TENDERS']).length && companies.length > 0 + enable: intersection(permissions, ['APPLY_CALLS']).length && companies.length > 0 }, { label: __('Seleziona azienda', 'gepafin'), @@ -67,7 +67,7 @@ const TopBarProfileMenu = ({ menuLeftRef }) => { command: () => { navigate('/agguingi-azienda') }, - enable: !intersection(permissions, ['MANAGE_TENDERS']).length + enable: intersection(permissions, ['APPLY_CALLS']).length }, { separator: true, @@ -86,7 +86,7 @@ const TopBarProfileMenu = ({ menuLeftRef }) => { const switchCompany = (id) => { if (chosenCompanyId !== id) { storeSet.main.chosenCompanyId(id); - console.log('set company 2', id) + if (toast.current) { toast.current.show({ severity: 'success', diff --git a/src/components/UnsavedChangesDetector/index.js b/src/components/UnsavedChangesDetector/index.js index 71c967e..86c21fd 100644 --- a/src/components/UnsavedChangesDetector/index.js +++ b/src/components/UnsavedChangesDetector/index.js @@ -2,17 +2,62 @@ import { useEffect } from 'react'; import { __ } from '@wordpress/i18n'; import equal from 'fast-deep-equal'; //import { diff } from 'deep-object-diff'; +import { klona } from 'klona'; +import { TZDate } from '@date-fns/tz'; +import { wrap } from 'object-path-immutable'; +import { is, isNil } from 'ramda'; // store import { storeGet } from '../../store'; const UnsavedChangesDetector = ({ getValuesFn }) => { const warnIfUnsavedChanges = (event) => { - const formData = getValuesFn(); + let formData = klona(getValuesFn()); + formData.dates = []; + + if (formData.startDate) { + let starDate; + + if (is(String, formData.startDate)) { + starDate = formData.startDate; + } else { + const tzAwareDate = new TZDate(formData.startDate, 'Europe/Berlin'); + starDate = tzAwareDate.toISOString().substring(0, 19); + } + + formData = wrap(formData).insert(['dates'], starDate, 0).value(); + } + if (formData.endDate) { + let endDate; + + if (is(String, formData.endDate)) { + endDate = formData.endDate; + } else { + const tzAwareDate = new TZDate(formData.endDate, 'Europe/Berlin'); + endDate = tzAwareDate.toISOString().substring(0, 19); + } + + formData = wrap(formData).insert(['dates'], endDate, 1).value(); + } + if (!isNil(formData.startTime)) { + if (!is(String, formData.startTime)) { + const tzAwareDate = new TZDate(formData.startTime, 'Europe/Berlin'); + formData.startTime = tzAwareDate.toISOString().substring(11, 16); + } + } + if (!isNil(formData.endTime)) { + if (!is(String, formData.endTime)) { + const tzAwareDate = new TZDate(formData.endTime, 'Europe/Berlin'); + formData.endTime = tzAwareDate.toISOString().substring(11, 16); + } + } const initial = storeGet.main.formInitialData(); + const isEqual = equal(initial, formData); // TODO - //console.log('isEqual', isEqual, initial, formData, diff(initial, formData)) + /*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/helpers/getBandoLabel.js b/src/helpers/getBandoLabel.js index 35a850e..43abcd3 100644 --- a/src/helpers/getBandoLabel.js +++ b/src/helpers/getBandoLabel.js @@ -17,6 +17,9 @@ const getBandoLabel = (status) => { case 'READY': return __('Pronto', 'gepafin'); + case 'SOCCORSO': + return __('Soccorso', 'gepafin'); + case 'DRAFT': return __('Bozza', 'gepafin'); @@ -38,6 +41,9 @@ const getBandoLabel = (status) => { case 'EXPIRED': return __('Scaduto', 'gepafin'); + case 'CLOSE': + return __('Chiuso', 'gepafin'); + default: return ''; } diff --git a/src/helpers/getBandoSeverity.js b/src/helpers/getBandoSeverity.js index d090c80..631cfe6 100644 --- a/src/helpers/getBandoSeverity.js +++ b/src/helpers/getBandoSeverity.js @@ -15,6 +15,9 @@ const getBandoSeverity = (status) => { case 'READY': return 'info'; + case 'SOCCORSO': + return 'warning'; + case 'DRAFT': return 'warning'; @@ -36,6 +39,9 @@ const getBandoSeverity = (status) => { case 'EXPIRED': return 'closed'; + case 'CLOSE': + return 'closed'; + default: return 'info'; } diff --git a/src/helpers/renderHtmlContent.js b/src/helpers/renderHtmlContent.js index 672546a..2da65aa 100644 --- a/src/helpers/renderHtmlContent.js +++ b/src/helpers/renderHtmlContent.js @@ -1,6 +1,10 @@ import parse from 'html-react-parser'; import { isNil } from 'ramda'; +import DOMPurify from 'dompurify'; -const renderHtmlContent = (content = '') => !isNil(content) ? parse(content) : ''; +const renderHtmlContent = (content = '') => { + const clean = DOMPurify.sanitize(content); + return !isNil(clean) ? parse(clean) : ''; +} export default renderHtmlContent; \ No newline at end of file diff --git a/src/icons/HelpIcon/index.js b/src/icons/HelpIcon/index.js new file mode 100644 index 0000000..26dfcd7 --- /dev/null +++ b/src/icons/HelpIcon/index.js @@ -0,0 +1,20 @@ +import React from 'react'; + +const HelpIcon = () => { + return + + + + + + + + + +} + +export default HelpIcon; \ No newline at end of file diff --git a/src/layouts/DefaultLayout/components/AppSidebar/index.js b/src/layouts/DefaultLayout/components/AppSidebar/index.js index 61a4484..5155f78 100644 --- a/src/layouts/DefaultLayout/components/AppSidebar/index.js +++ b/src/layouts/DefaultLayout/components/AppSidebar/index.js @@ -1,12 +1,13 @@ import React from 'react'; import { __ } from '@wordpress/i18n'; -import { intersection } from 'ramda'; +import { intersection, is } from 'ramda'; // store import { useStore } from '../../../../store'; // components import { NavLink } from 'react-router-dom'; +import HelpIcon from '../../../../icons/HelpIcon'; const AppSidebar = () => { const permissions = useStore().main.getPermissions(); @@ -41,32 +42,59 @@ const AppSidebar = () => { enable: intersection(permissions, ['VIEW_CALLS']).length }, { - label: __('Gestione Utenti', 'gepafin'), + label: __('Gestione domande', 'gepafin'), + icon: 'pi pi-file', + href: '/domande', + id: 5, + enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length + }, + { + label: __('Domande da valutare', 'gepafin'), + icon: 'pi pi-calendar-clock', + href: '/domande', + id: 6, + enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length + }, + { + label: __('Archivio domande', 'gepafin'), + icon: 'pi pi-file', + href: '/domande', + id: 7, + enable: intersection(permissions, ['APPLY_CALLS']).length + }, + { + label: __('Soccorso istruttorio', 'gepafin'), + icon: , + href: '/soccorso-istruttorio', + id: 8, + enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length + }, + { + label: __('Gestione utenti', 'gepafin'), icon: 'pi pi-users', href: '/utenti', - id: 5, - enable: false - //enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length + id: 9, + enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length }, { label: __('Configurazione', 'gepafin'), icon: 'pi pi-cog', //href: '/configurazione', - id: 6, + id: 10, enable: false }, { label: __('Report e Analisi', 'gepafin'), icon: 'pi pi-chart-bar', //href: '/stats', - id: 7, + id: 11, enable: false }, { label: __('Log di Sistema', 'gepafin'), icon: 'pi pi-receipt', clickFn: () => {}, - id: 8, + id: 12, enable: false } ] @@ -78,11 +106,15 @@ const AppSidebar = () => { .map(o =>
  • {o.href ? - + {is(String, o.icon) + ? + : o.icon} {o.label} : }
  • )} diff --git a/src/layouts/DefaultLayout/components/AppTopbar/index.js b/src/layouts/DefaultLayout/components/AppTopbar/index.js index 3627539..af1e9a8 100644 --- a/src/layouts/DefaultLayout/components/AppTopbar/index.js +++ b/src/layouts/DefaultLayout/components/AppTopbar/index.js @@ -27,9 +27,10 @@ const AppTopbar = () => { - + + {/* - + */} - } + }*/ const rowExpansionTemplate = (data) => { return ( diff --git a/src/pages/BandoApplication/ApplicationSteps/index.js b/src/pages/BandoApplication/ApplicationSteps/index.js index 46df2c5..82c0f1d 100644 --- a/src/pages/BandoApplication/ApplicationSteps/index.js +++ b/src/pages/BandoApplication/ApplicationSteps/index.js @@ -1,20 +1,13 @@ import React from 'react'; import { range } from 'ramda'; -//import { __ } from '@wordpress/i18n'; +import { __ } from '@wordpress/i18n'; // components import { Steps } from 'primereact/steps'; const ApplicationSteps = ({ totalSteps = 0, activeStepIndex }) => { const rangeArr = range(1, totalSteps + 1); - const items = rangeArr.map(() => ({ label: 'Passo' })); - - /*// TODO update to using Steps after primereact is updated - return( - 0 !== totalSteps - ? {__('Passo', 'gepafin')}: {activeStepIndex + 1} - : null - )*/ + const items = rangeArr.map(() => ({ label: __('Passo', 'gepafin') })); return( 0 !== totalSteps diff --git a/src/pages/BandoApplication/index.js b/src/pages/BandoApplication/index.js index 1c05f1f..cbc7d22 100644 --- a/src/pages/BandoApplication/index.js +++ b/src/pages/BandoApplication/index.js @@ -326,10 +326,14 @@ const BandoApplication = () => { ].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); diff --git a/src/pages/BandoEdit/components/BandoEditFormStep1/index.js b/src/pages/BandoEdit/components/BandoEditFormStep1/index.js index 9cbb1d5..4117123 100644 --- a/src/pages/BandoEdit/components/BandoEditFormStep1/index.js +++ b/src/pages/BandoEdit/components/BandoEditFormStep1/index.js @@ -5,6 +5,7 @@ import { useForm } from 'react-hook-form'; import { isEmpty, isNil, is } from 'ramda'; import { klona } from 'klona'; import { TZDate } from '@date-fns/tz'; +import { wrap } from 'object-path-immutable'; // components import FormField from '../../../../components/FormField'; @@ -48,38 +49,36 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st const values = getValues(); const toast = useRef(null); - const onSubmit = (formData) => { - /*if (!isNil(formData.dates) && formData.dates.length) { - formData.dates = formData.dates.map(v => { - if (is(String, v)) { - return v; - } else { - const tzAwareDate = new TZDate(v, 'Europe/Berlin'); - return tzAwareDate.toISOString().substring(0, 19); - } - }); - } - - storeSet.main.setAsyncRequest(); - if (!formData.id) { - BandoService.createBando(formData, createCallback, errCreateCallback); - } else { - BandoService.updateBandoStep1(formData.id, formData, createCallback, errCreateCallback); - }*/ - }; + const onSubmit = () => {}; const onSaveDraft = () => { trigger(); - const formData = getValues(); - if (!isNil(formData.dates) && formData.dates.length) { - formData.dates = formData.dates.map(v => { - if (is(String, v)) { - return v; - } else { - const tzAwareDate = new TZDate(v, 'Europe/Berlin'); - return tzAwareDate.toISOString().substring(0, 19); - } - }); + let formData = klona(getValues()); + formData.dates = []; + + if (formData.startDate) { + let starDate; + + if (is(String, formData.startDate)) { + starDate = formData.startDate; + } else { + const tzAwareDate = new TZDate(formData.startDate, 'Europe/Berlin'); + starDate = tzAwareDate.toISOString().substring(0, 19); + } + + formData = wrap(formData).insert(['dates'], starDate, 0).value(); + } + if (formData.endDate) { + let endDate; + + if (is(String, formData.endDate)) { + endDate = formData.endDate; + } else { + const tzAwareDate = new TZDate(formData.endDate, 'Europe/Berlin'); + endDate = tzAwareDate.toISOString().substring(0, 19); + } + + formData = wrap(formData).insert(['dates'], endDate, 1).value(); } if (!isNil(formData.startTime)) { if (!is(String, formData.startTime)) { @@ -95,6 +94,9 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st } } + delete formData.startDate; + delete formData.endDate; + storeSet.main.setAsyncRequest(); if (!formData.id) { BandoService.createBando(formData, createCallback, errCreateCallback); @@ -106,11 +108,13 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st const createCallback = (data) => { storeSet.main.unsetAsyncRequest(); if (data.status === 'SUCCESS') { - toast.current.show({ - severity: 'success', - summary: '', - detail: __('Il bando è stato aggiornato corretamente!', 'gepafin') - }); + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: __('Il bando è stato aggiornato corretamente!', 'gepafin') + }); + } const values = getValues(); if (!values.id && data.data.id) { navigate(`/bandi/${data.data.id}`); @@ -121,6 +125,13 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st } const errCreateCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } set404FromErrorResponse(data); storeSet.main.unsetAsyncRequest(); } @@ -193,7 +204,12 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st useEffect(() => { const newFormData = klona(formInitialData); if (!isNil(formInitialData.dates) && formInitialData.dates.length) { - newFormData.dates = formInitialData.dates.map(v => is(String, v) ? new Date(v) : (v ? v : '')); + if (newFormData.dates[0]) { + newFormData.startDate = is(String, newFormData.dates[0]) ? new Date(newFormData.dates[0]) : (newFormData.dates[0] ? newFormData.dates[0] : ''); + } + if (newFormData.dates[1]) { + newFormData.endDate = is(String, newFormData.dates[1]) ? new Date(newFormData.dates[1]) : (newFormData.dates[1] ? newFormData.dates[1] : ''); + } } if (!isNil(formInitialData.startTime) && !isEmpty(formInitialData.startTime)) { newFormData.startTime = is(String, formInitialData.startTime) @@ -305,7 +321,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st }} /> - + />*/} +
    + + + +
    { - /*if (!isNil(formData.dates) && formData.dates.length) { - formData.dates = formData.dates.map(v => { - if (is(String, v)) { - return v; - } else { - const tzAwareDate = new TZDate(v, 'Europe/Berlin'); - return tzAwareDate.toISOString().substring(0, 19); - } - }); - } - - const forSubmit = Object.keys(formData).reduce((acc, cur) => { - if (step2Props.includes(cur)) { - acc[cur] = formData[cur]; - } - return acc; - }, {}); - - storeSet.main.setAsyncRequest(); - BandoService.updateBandoStep2(formData.id, forSubmit, createCallback, errCreateCallback);*/ - }; + const onSubmit = () => {}; const onSaveDraft = () => { trigger(); - const formData = getValues(); - if (!isNil(formData.dates) && formData.dates.length) { - formData.dates = formData.dates.map(v => { - if (is(String, v)) { - return v; - } else { - const tzAwareDate = new TZDate(v, 'Europe/Berlin'); - return tzAwareDate.toISOString().substring(0, 19); - } - }); + let formData = klona(getValues()); + formData.dates = []; + + if (formData.startDate) { + let starDate; + + if (is(String, formData.startDate)) { + starDate = formData.startDate; + } else { + const tzAwareDate = new TZDate(formData.startDate, 'Europe/Berlin'); + starDate = tzAwareDate.toISOString().substring(0, 19); + } + + formData = wrap(formData).insert(['dates'], starDate, 0).value(); + } + if (formData.endDate) { + let endDate; + + if (is(String, formData.endDate)) { + endDate = formData.endDate; + } else { + const tzAwareDate = new TZDate(formData.endDate, 'Europe/Berlin'); + endDate = tzAwareDate.toISOString().substring(0, 19); + } + + formData = wrap(formData).insert(['dates'], endDate, 1).value(); } if (!isNil(formData.startTime)) { if (!is(String, formData.startTime)) { @@ -88,7 +84,6 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors, st formData.startTime = tzAwareDate.toISOString().substring(11, 16); } } - if (!isNil(formData.endTime)) { if (!is(String, formData.endTime)) { const tzAwareDate = new TZDate(formData.endTime, 'Europe/Berlin'); @@ -96,6 +91,9 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors, st } } + delete formData.startDate; + delete formData.endDate; + storeSet.main.setAsyncRequest(); BandoService.updateBandoStep2(formData.id, formData, createCallback, errCreateCallback); } diff --git a/src/pages/BandoEdit/index.js b/src/pages/BandoEdit/index.js index 68b2620..b7f3ee6 100644 --- a/src/pages/BandoEdit/index.js +++ b/src/pages/BandoEdit/index.js @@ -76,9 +76,9 @@ const BandoEdit = () => { BandoService.validateBando(id, validateCallback, errValidateCallback); } - const validateCallback = (data) => { - if (data.status === 'SUCCESS') { - setData({ ...data, status: data.data.status }); + const validateCallback = (resp) => { + if (resp.status === 'SUCCESS') { + setData(resp.data); if (bandoMsgs.current) { bandoMsgs.current.show([ { @@ -100,19 +100,26 @@ const BandoEdit = () => { storeSet.main.unsetAsyncRequest(); } - const errValidateCallback = (data) => { - if (data.status === 'VALIDATION_ERROR') { + const errValidateCallback = (resp) => { + if (resp.status === 'VALIDATION_ERROR') { storeSet.main.unsetAsyncRequest(); if (bandoMsgs.current) { - bandoMsgs.current.show(data.data.map((v, i) => ({ + bandoMsgs.current.show(resp.data.map((v, i) => ({ id: i, sticky: true, severity: 'error', summary: '', detail: v, closable: false }))); } + if (toast.current) { + toast.current.show(resp.data.map((v, i) => ({ + severity: 'error', + summary: '', + detail: v + }))); + } } else { - standardErrCallback(data); + standardErrCallback(resp); } } @@ -122,8 +129,8 @@ const BandoEdit = () => { BandoService.updateBandoStatus(id, publishCallback, errPublishCallback, [['status', 'PUBLISH']]); } - const publishCallback = (data) => { - if (data.status === 'SUCCESS') { + const publishCallback = (resp) => { + if (resp.status === 'SUCCESS') { if (bandoMsgs.current) { bandoMsgs.current.show([ { @@ -141,26 +148,31 @@ const BandoEdit = () => { detail: __('Pubblicato!', 'gepafin') }); } - if (data.data.docs) { - data.data.docs = data.data.docs + if (resp.data.docs) { + resp.data.docs = resp.data.docs .filter(o => o.source === 'CALL' && o.type === 'DOCUMENT'); } - setData(data.data); + setData(resp.data); } storeSet.main.unsetAsyncRequest(); } - const errPublishCallback = (data) => { - standardErrCallback(data); + const errPublishCallback = (resp) => { + standardErrCallback(resp); } - const getCallback = (data) => { - if (data.status === 'SUCCESS') { - if (!isNil(data.data.dates) && data.data.dates.length) { - data.data.dates = data.data.dates.map(v => is(String, v) ? new Date(v) : v); + const getCallback = (resp) => { + if (resp.status === 'SUCCESS') { + if (!isNil(resp.data.dates) && resp.data.dates.length) { + if (resp.data.dates[0]) { + resp.data.startDate = is(String, resp.data.dates[0]) ? new Date(resp.data.dates[0]) : (resp.data.dates[0] ? resp.data.dates[0] : ''); + } + if (resp.data.dates[1]) { + resp.data.endDate = is(String, resp.data.dates[1]) ? new Date(resp.data.dates[1]) : (resp.data.dates[1] ? resp.data.dates[1] : ''); + } } - if (data.data.status === 'READY_TO_PUBLISH') { + if (resp.data.status === 'READY_TO_PUBLISH') { bandoMsgs.current.clear(); bandoMsgs.current.show([ { @@ -170,7 +182,7 @@ const BandoEdit = () => { closable: false } ]); - } else if (data.data.status === 'DRAFT') { + } else if (resp.data.status === 'DRAFT') { if (bandoMsgs.current) { bandoMsgs.current.clear(); bandoMsgs.current.show([ @@ -183,26 +195,26 @@ const BandoEdit = () => { ]); } } - if (data.data.docs) { - data.data.docs = data.data.docs + if (resp.data.docs) { + resp.data.docs = resp.data.docs .filter(o => o.source === 'CALL' && o.type === 'DOCUMENT'); } - setData(data.data); + setData(resp.data); } storeSet.main.unsetAsyncRequest(); } - const errGetCallback = (data) => { - set404FromErrorResponse(data); + const errGetCallback = (resp) => { + set404FromErrorResponse(resp); storeSet.main.unsetAsyncRequest(); } - const standardErrCallback = (data) => { - if (bandoMsgs.current && data.message) { + const standardErrCallback = (resp) => { + if (bandoMsgs.current && resp.message) { bandoMsgs.current.show([ { sticky: true, severity: 'error', summary: '', - detail: data.message, + detail: resp.message, closable: true } ]); @@ -210,9 +222,9 @@ const BandoEdit = () => { storeSet.main.unsetAsyncRequest(); } - const getFormsCallback = (data) => { - if (data.status === 'SUCCESS') { - setForms(data.data); + const getFormsCallback = (resp) => { + if (resp.status === 'SUCCESS') { + setForms(resp.data); } storeSet.main.unsetAsyncRequest(); } diff --git a/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js b/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js index ea18c3f..5cb2f49 100644 --- a/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js +++ b/src/pages/BandoFormsEdit/components/BuilderElementSettings/index.js @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import { head, isEmpty, isNil } from 'ramda'; import { __, sprintf } from '@wordpress/i18n'; import { klona } from 'klona'; +import { wrap } from 'object-path-immutable'; // store import { storeSet, useStore } from '../../../../store'; @@ -14,13 +15,17 @@ import { TabView, TabPanel } from 'primereact/tabview'; import { InputSwitch } from 'primereact/inputswitch'; import ElementSetting from './components/ElementSetting'; import { Dropdown } from 'primereact/dropdown'; +import { MultiSelect } from 'primereact/multiselect'; -const BuilderElementSettings = ({ closeSettings }) => { +const BuilderElementSettings = ({ closeSettingsFn }) => { const elements = useStore().main.formElements(); const activeElement = useStore().main.activeElement(); + const criteriaOptions = useStore().main.bandoCriteria(); const [activeElementData, setActiveElementData] = useState({}); const [settings, setSettings] = useState([]); const [validators, setValidators] = useState({}); + const [dynamicData, setDynamicData] = useState(''); + const [criteria, setCriteria] = useState([]); const numberBasedValidatorFields = ['min', 'max', 'minLength', 'maxLength']; const customValidationOptions = [ { value: 'isPIVA', label: 'isPIVA' }, @@ -63,11 +68,14 @@ const BuilderElementSettings = ({ closeSettings }) => { } const saveSettings = () => { - activeElementData.settings = settings; - activeElementData.validators = validators; - const newElements = elements.map(o => o.id === activeElementData.id ? activeElementData : o); + let newActiveElementData = klona(activeElementData); + newActiveElementData = wrap(newActiveElementData).set(['settings'], settings).value(); + newActiveElementData = wrap(newActiveElementData).set(['validators'], validators).value(); + newActiveElementData = wrap(newActiveElementData).set(['dynamicData'], dynamicData).value(); + newActiveElementData = wrap(newActiveElementData).set(['criteria'], criteria).value(); + const newElements = elements.map(o => o.id === newActiveElementData.id ? newActiveElementData : o); storeSet.main.formElements(newElements); - closeSettings(); + closeSettingsFn(); } const showField = (value, key) => { @@ -92,17 +100,56 @@ const BuilderElementSettings = ({ closeSettings }) => { setValidators(newValidators); } + const onChangeCriteriaData = (value) => { + setCriteria(value); + } + + + const getDynamicDataOptions = (type) => { + switch (type) { + case 'datepicker' : + return [ + { label: 'user dateOfBirth', value: 'user.dateOfBirth' } + ] + default : + return [ + { label: 'company name', value: 'company.companyName' }, + { label: 'company piva', value: 'company.vatNumber' }, + { label: 'company codiceFiscale', value: 'company.codiceFiscale' }, + { label: 'company address', value: 'company.address' }, + { label: 'company phoneNumber', value: 'company.phoneNumber' }, + { label: 'company city', value: 'company.city' }, + { label: 'company province', value: 'company.province' }, + { label: 'company cap', value: 'company.cap' }, + { label: 'company country', value: 'company.country' }, + { label: 'company pec', value: 'company.pec' }, + { label: 'company email', value: 'company.email' }, + { label: 'company contactName', value: 'company.contactName' }, + { label: 'company contactEmail', value: 'company.contactEmail' }, + { label: 'user email', value: 'user.email' }, + { label: 'user firstName', value: 'user.firstName' }, + { label: 'user lastName', value: 'user.lastName' }, + { label: 'user phoneNumber', value: 'user.phoneNumber' }, + { label: 'user codiceFiscale', value: 'user.codiceFiscale' } + ] + } + } + useEffect(() => { const chosen = head(elements.filter(o => o.id === activeElement)); if (chosen) { setActiveElementData(klona(chosen)); setSettings(klona(chosen.settings)); - setValidators(klona(chosen.validators)) + setValidators(klona(chosen.validators)); + setDynamicData(chosen.dynamicData ? chosen.dynamicData : ''); + setCriteria(chosen.criteria ? chosen.criteria : []); } else { setActiveElementData({}); setSettings([]); - setValidators({}) + setValidators({}); + setDynamicData(''); + setCriteria([]); } }, [activeElement]); @@ -118,6 +165,18 @@ const BuilderElementSettings = ({ closeSettings }) => { changeFn={onChange} updateDataFn={onUpdateOptions}/>) : null} + {['textinput', 'datepicker'].includes(activeElementData.name) + ?
    + + setDynamicData(e.value)} + options={getDynamicDataOptions(activeElementData.name)} + optionLabel="label" + optionValue="value" + placeholder={__('Scegli', 'gepafin')}/> +
    : null} {!isEmpty(validators) ? @@ -172,6 +231,20 @@ const BuilderElementSettings = ({ closeSettings }) => {
    : null} ) : null} : null} + +
    + + onChangeCriteriaData(e.value)} + options={criteriaOptions} + optionLabel="label" + optionValue="value" + display="chip" + placeholder={__('Scegli', 'gepafin')}/> +
    +
    + + + + + + + + + + + + ); + }; + + const header = renderHeader(); + + const updateEvaluationValue = (value, path, maxValue) => { + let finalValue = value; + + if (maxValue) { + finalValue = value > maxValue ? maxValue : value; + } + + const newData = wrap(data).set(path.split('.'), finalValue).value(); + setData(newData); + } + + const doSaveDraft = () => { + const formData = { + criteria: klona(data.criteria), + checklist: klona(data.checklist), + files: klona(data.files), + note: data.note + } + ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateCallback, errUpdateCallback); + } + + const updateCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + } + storeSet.main.unsetAsyncRequest(); + } + + const errUpdateCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + const doApprove = () => { + const formData = { + status: 'APPROVED' + } + ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateStatusCallback, errUpdateStatusCallback); + } + + const doReject = () => { + const formData = { + status: 'REJECTED' + } + ApplicationEvaluationService.updateEvaluation(data.assignedApplicationId, formData, updateStatusCallback, errUpdateStatusCallback); + } + + const updateStatusCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + } + storeSet.main.unsetAsyncRequest(); + } + + const errUpdateStatusCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + const displayCriterionData = (id) => { + const criterion = head(data.criteria.filter(o => o.id === id)); + setCriterionDataTitle(criterion.label); + const content =
    +

    {__('I campi correlati')}

    + {criterion.criteriaMappedFields ? criterion.criteriaMappedFields.map(o => criteriaDataItem(o)) : null} +
    ; + setCriterionDataContent(content); + setIsVisibleCriterionData(id); + } + + const criteriaDataItem = (item) => { + let content = ''; + + if (is(String, item.fieldValue)) { + content = item.fieldValue; + } else if (item.fieldValue && item.fieldValue.length && !isNil(item.fieldValue[0].filePath)) { + content = ; + } else if (item.fieldValue && item.fieldValue.length && isNil(item.fieldValue[0].filePath)) { + const th = Object.keys(item.fieldValue[0]); + content = + + + {th.map(v => )} + + + + {item.fieldValue.map((o, i) => + {Object.values(o).map(v => )} + )} + +
    {v}
    {v}
    ; + } + + return
    + {item.fieldLabel} + {content} +
    + } + + const hideCriterionData = () => { + setIsVisibleCriterionData(0); + setCriterionDataTitle(''); + setCriterionDataContent(''); + } + + const getAmendmentsCallback = (data) => { + if (data.status === 'SUCCESS') { + if (data.data.length) { + setConnectedSoccorsoId(data.data[0].id); + } + } + } + + const errGetAmendmentsCallback = () => { + } + + useEffect(() => { + const maxScore = pathOr(0, ['minScore'], data); + const criteria = pathOr([], ['criteria'], data); + const scoreSum = sum(criteria.map(o => o.score)); + + setIsAdmissible(scoreSum !== 0 && scoreSum >= maxScore); + }, [data]); + + useEffect(() => { + const parsed = parseInt(id) + const entityId = !isNaN(parsed) ? parsed : 0; + + storeSet.main.setAsyncRequest(); + ApplicationEvaluationService.getEvaluationByApplId(getCallback, errGetCallback, [ + ['applicationId', entityId] + ]); + AmendmentsService.getSoccorsoByApplId(entityId, getAmendmentsCallback, errGetAmendmentsCallback, [ + ['statuses', 'AWAITING'] + ]); + }, [id]); + + return ( +
    +
    +

    {__('Valuta domanda', 'gepafin')}

    +
    + +
    + + + +
    +
    + +
    + + {!isAsyncRequest && !isEmpty(data) + ?
    +
    +

    + {__('ID domanda', 'gepafin')} + {data.applicationId} +

    +

    + {__('Bando', 'gepafin')} + {data.callName} +

    +

    + {__('Beneficiario', 'gepafin')} + {data.beneficiary} +

    +

    + {__('Data ricezione', 'gepafin')} + {getDateFromISOstring(data.submissionDate)} +

    +

    + {__('Scadenza Valutazione', 'gepafin')} + {getDateFromISOstring(data.callEndDate)} +

    +

    + {__('Stato', 'gepafin')} + {getBandoLabel(data.applicationStatus)} +

    +
    + +
    +

    {__('Punteggi di valutazione', 'gepafin')}

    + {data.criteria + ? + + + + + + + + + {data.criteria.map((o, i) => + + + + )} + + + + + + + + + + + +
    {__('Parametro', 'gepafin')}{__('Punteggio', 'gepafin')}{__('Stato', 'gepafin')}
    {o.label} +
    + updateEvaluationValue( + e.value, + `criteria.${i}.score`, + o.maxScore + )}/> + + / {o.maxScore} + +
    +
    +
    + {!isEmpty(o.criteriaMappedFields) + ?
    +
    {__('Punteggio:', 'gepafin')}{sum(data.criteria.map(o => o.score))} + {isAdmissible + ? : null} + {!isAdmissible + ? : null} +
    {sprintf(__('Punteggio minimo per l\'ammissione: %d'), data.minScore)}
    : null} +
    + +
    +

    {__('Checklist Valutazione', 'gepafin')}

    +
    +
    + +

    {__('Lista', 'gepafin')}

    +
    +
    + {data.checklist.map((o, i) =>
    + updateEvaluationValue( + e.checked, + `checklist.${i}.valid` + )} + checked={o.valid}> + +
    )} +
    +
    + +

    {__('Note', 'gepafin')}

    +
    + updateEvaluationValue( + e.htmlValue, + 'note' + )} + style={{ height: 80 * 3, width: '100%' }} + /> +
    +
    +
    +

    {__('Documenti allegati', 'gepafin')}

    +
      + {data.files.map((o, i) =>
    1. +
      + {o.label} +
      + {o.fileDetail && o.fileDetail.length === 1 + ?
      +
      + {o.fileDetail && o.fileDetail.length > 1 + ?
        + {o.fileDetail.map((k, i) =>
      • + {k.name} +
        +
        +
      • )} +
      + : null} +
    2. )} +
    +
    +
    +
    + +
    + +
    + {__('Azioni rapide', 'gepafin')} +
    + +
    +
    +
    +
    + + + {criterionDataContent} + + +
    + : <> + + + + + + + + + } +
    + ) + +} + +export default DomandaEditPreInstructor; \ No newline at end of file diff --git a/src/pages/Domande/components/AllDomandeTable/index.js b/src/pages/Domande/components/AllDomandeTable/index.js new file mode 100644 index 0000000..a613e29 --- /dev/null +++ b/src/pages/Domande/components/AllDomandeTable/index.js @@ -0,0 +1,173 @@ +import React, { useState, useEffect} from 'react'; +import { __ } from '@wordpress/i18n'; +import { is, uniq } from 'ramda'; +import { Link } from 'react-router-dom'; + +// store +//import { storeSet, storeGet } 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 { Button } from 'primereact/button'; +import { Calendar } from 'primereact/calendar'; +import ProperBandoLabel from '../../../../components/ProperBandoLabel'; + + +const AllDomandeTable = ({ openDialogFn, updaterString = '' }) => { + const [items, setItems] = useState(null); + const [filters, setFilters] = useState(null); + const [localAsyncRequest, setLocalAsyncRequest] = useState(false); + const [globalFilterValue, setGlobalFilterValue] = useState(''); + const [statuses, setStatuses] = useState([]); + + useEffect(() => { + setLocalAsyncRequest(true); + ApplicationService.getApplications(getCallback, errGetCallbacks, [ + ['statuses', 'SUBMIT'] + ]); + }, [updaterString]); + + const getCallback = (data) => { + if (data.status === 'SUCCESS') { + setItems(getFormattedData(data.data)); + setStatuses(uniq(data.data.map(o => o.status))) + initFilters(); + } + setLocalAsyncRequest(false); + } + + const errGetCallbacks = (data) => { + setLocalAsyncRequest(false); + } + + const getFormattedData = (data) => { + return data.map((d) => { + d.callEndDate = is(String, d.callEndDate) ? new Date(d.callEndDate) : (d.callEndDate ? d.callEndDate : ''); + d.modifiedDate = is(String, d.modifiedDate) ? new Date(d.modifiedDate) : (d.modifiedDate ? d.modifiedDate : ''); + d.submissionDate = is(String, d.submissionDate) ? new Date(d.submissionDate) : (d.submissionDate ? d.submissionDate : ''); + 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 }] + }, + submissionDate: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] + }, + callEndDate: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] + } + }); + setGlobalFilterValue(''); + }; + + const renderHeader = () => { + return ( +
    +
    + ); + }; + + const dateAppliedBodyTemplate = (rowData) => { + return formatDate(rowData.submissionDate); + }; + + const dateEndBodyTemplate = (rowData) => { + return formatDate(rowData.callEndDate); + }; + + const dateFilterTemplate = (options) => { + return options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />; + }; + + const statusBodyTemplate = (rowData) => { + return ; + }; + + const actionsBodyTemplate = (rowData) => { + return openDialogFn + ? + + + + + + + + + + + + ); + }; + + const header = renderHeader(); + + const updateEvaluationValue = (value, path, maxValue) => { + let finalValue = value; + + if (maxValue) { + finalValue = value > maxValue ? maxValue : value; + } + + const newData = wrap(formData).set(path.split('.'), finalValue).value(); + + setFormData(newData); + } + + const doCreate = () => { + storeSet.main.setAsyncRequest(); + + AmendmentsService.createSoccorso(formData, createCallback, errCreateCallback, [ + ['applicationEvaluationId', evaluationId] + ]); + } + + const createCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + setTimeout(() => { + navigate(`/domande/${id}/soccorso/${data.data.id}`); + }, 1000) + } + storeSet.main.unsetAsyncRequest(); + } + + const errCreateCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + useEffect(() => { + const parsed = parseInt(evaluationId) + const entityId = !isNaN(parsed) ? parsed : 0; + + storeSet.main.setAsyncRequest(); + AmendmentsService.getSoccorsoByApplEvalId(entityId, getCallback, errGetCallback); + }, [evaluationId]); + + return ( +
    +
    +

    {__('Richiesta Integrazione Documentale', 'gepafin')}

    +
    + +
    + + + +
    +
    + +
    + + {!isAsyncRequest && !isEmpty(data) + ?
    +
    +

    + {__('ID domanda', 'gepafin')} + {data.applicationId} +

    +

    + {__('Bando', 'gepafin')} + {data.callName} +

    +

    + {__('Beneficiario', 'gepafin')} + {data.beneficiaryName} +

    +
    + +
    +
    +
    +

    {__('Note', 'gepafin')}

    +
    + updateEvaluationValue( + e.htmlValue, + 'note' + )} + style={{ height: 80 * 3, width: '100%' }} + /> +
    + +

    {__('Tempo per la Risposta (giorni)', 'gepafin')}

    +
    + updateEvaluationValue( + e.value, + 'responseDays', + 9999 + )}/> +
    + +

    {__('Notifica', 'gepafin')}

    +
    +
    + updateEvaluationValue( + e.value, + 'isSendEmail' + )}/> + +
    +
    + updateEvaluationValue( + e.value, + 'isSendNotification' + )}/> + +
    +
    +
    + {formData.formFields + ?
    +

    {__('Documenti da Integrare', 'gepafin')}

    +
    +
    + {formData.formFields.map((o, i) =>
    + updateEvaluationValue( + e.checked, + `formFields.${i}.selected` + )} + checked={o.selected}> + +
    )} +
    +
    +
    + : null} +
    +
    + +
    + + {__('Attenzione', 'gepafin')} + {__("L'invio della richiesta di integrazione sospenderà il termine di valutazione della domanda.", 'gepafin')} +
    + +
    + {__('Azioni', 'gepafin')} +
    + +
    +
    +
    +
    + +
    + : <> + + + + + + + + + } +
    + ) + +} + +export default SoccorsoEditPreInstructor; \ No newline at end of file diff --git a/src/pages/SoccorsoEditPreInstructor/index.js b/src/pages/SoccorsoEditPreInstructor/index.js new file mode 100644 index 0000000..573a43e --- /dev/null +++ b/src/pages/SoccorsoEditPreInstructor/index.js @@ -0,0 +1,653 @@ +import React, { useState, useEffect, useRef, useMemo } from 'react'; +import { __ } from '@wordpress/i18n'; +import { useNavigate, useParams } from 'react-router-dom'; +import { is, isEmpty, isNil } from 'ramda'; +import { wrap } from 'object-path-immutable'; +import { klona } from 'klona'; +import { useForm } from 'react-hook-form'; + +// store +import { storeSet, useStore } from '../../store'; + +// api +import AmendmentsService from '../../service/amendments-service'; +import CommunicationService from '../../service/communication-service'; + +// tools +import set404FromErrorResponse from '../../helpers/set404FromErrorResponse'; +import getBandoLabel from '../../helpers/getBandoLabel'; +import getDateFromISOstring from '../../helpers/getDateFromISOstring'; +import renderHtmlContent from '../../helpers/renderHtmlContent'; +import uniqid from '../../helpers/uniqid'; + +// components +import { Button } from 'primereact/button'; +import BlockingOverlay from '../../components/BlockingOverlay'; +import { Toast } from 'primereact/toast'; +import { classNames } from 'primereact/utils'; +import { Dialog } from 'primereact/dialog'; +import { InputText } from 'primereact/inputtext'; +import { InputTextarea } from 'primereact/inputtextarea'; +import FormField from '../../components/FormField'; +import { Editor } from 'primereact/editor'; +import { InputNumber } from 'primereact/inputnumber'; + +const SoccorsoEditPreInstructor = () => { + const isAsyncRequest = useStore().main.isAsyncRequest(); + const { id, amendmentId } = useParams(); + const navigate = useNavigate(); + const [data, setData] = useState({}); + const [comms, setComms] = useState([]); + const [isVisibleNewCommDialog, setIsVisibleNewCommDialog] = useState(false); + const [newCommData, setNewCommData] = useState({}); + const [isLoadingCommunication, setIsLoadingCommunication] = useState(false); + const [isVisibleExtendTimeDialog, setIsVisibleExtendTimeDialog] = useState(false); + const [extendedTime, setExtendedTime] = useState(3); + const [isLoadingExtendingTime, setIsLoadingExtendingTime] = useState(false); + const [isLoadingReminding, setIsLoadingReminding] = useState(false); + const toast = useRef(null); + const [formInitialData, setFormInitialData] = useState({}); + const { + control, + handleSubmit, + formState: { errors }, + setValue, + register, + trigger, + getValues + } = useForm({ + defaultValues: useMemo(() => { + return formInitialData; + }, [formInitialData]), mode: 'onChange' + }); + + const goToEvaluationPage = () => { + navigate(`/domande/${id}`); + } + + const getCallback = (data) => { + if (data.status === 'SUCCESS') { + setData(getFormattedData(data.data)); + const formDataInitial = data.data.applicationFormFields.reduce((acc, cur) => { + if (cur.fieldValue) { + acc[cur.fieldId] = cur.fieldValue; + } + return acc; + }, {}); + setFormInitialData(formDataInitial); + CommunicationService.getCommsByAmendmentId(data.data.id, getCommsCallback, errGetCommsCallback); + } + //storeSet.main.unsetAsyncRequest(); + } + + const errGetCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + const getCommsCallback = (data) => { + if (data.status === 'SUCCESS') { + setComms(data.data.commentsList.map(o => getFormattedCommsData(o))); + } + storeSet.main.unsetAsyncRequest(); + } + + const errGetCommsCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + const getFormattedData = (data) => { + data.startDate = is(String, data.startDate) ? new Date(data.startDate) : (data.startDate ? data.startDate : ''); + data.expirationDate = is(String, data.expirationDate) ? new Date(data.expirationDate) : (data.expirationDate ? data.expirationDate : ''); + return data; + }; + + const getFormattedCommsData = (data) => { + data.id = isNil(data.id) ? uniqid('id') : data.id; + data.commentedDate = is(String, data.commentedDate) ? new Date(data.commentedDate) : (data.commentedDate ? data.commentedDate : ''); + data.createdDate = is(String, data.createdDate) ? new Date(data.createdDate) : (data.createdDate ? data.createdDate : ''); + data.updatedDate = is(String, data.updatedDate) ? new Date(data.updatedDate) : (data.updatedDate ? data.updatedDate : ''); + return data; + }; + + const headerNewComDialog = () => { + return {__('Aggiungi comunicazione', 'gepafin')} + } + + const hideNewComDialog = () => { + setIsVisibleNewCommDialog(false); + setNewCommData({ + title: '', + comment: '' + }); + } + + const footerNewComDialog = () => { + return
    +
    + } + + const openNewCommDialog = () => { + setIsVisibleNewCommDialog(true); + setNewCommData({ + title: '', + comment: '' + }); + } + + const updateNewCommData = (value, path) => { + const newData = wrap(newCommData).set(path.split('.'), value).value(); + setNewCommData(newData); + } + + const renderHeader = () => { + return ( + + + + + + + + + + + + + + ); + }; + + const header = renderHeader(); + + const updateNewAmendmentData = (value, path) => { + const newData = wrap(data).set(path.split('.'), value).value(); + setData(newData); + } + + const createCommunication = () => { + setIsLoadingCommunication(true); + CommunicationService.createCommunication(amendmentId, newCommData, createCommunicationCallback, errCreateCommunicationCallback); + } + + const createCommunicationCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + setComms([...comms, getFormattedCommsData(data.data)]) + setIsVisibleNewCommDialog(false); + } + setIsLoadingCommunication(false); + } + + const errCreateCommunicationCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + setIsLoadingCommunication(false); + } + + const onSubmit = () => { + }; + + const doUpdateAmendment = () => { + trigger(); + let formValues = klona(getValues()); + const newFormValues = Object.keys(formValues) + .reduce((acc, cur) => { + let fieldVal = formValues[cur]; + + fieldVal = isEmpty(fieldVal) ? null : fieldVal; + fieldVal = is(Array, fieldVal) ? fieldVal.map(o => o.id).join(',') : null; + + acc.push({ + 'fieldId': cur, + 'fieldValue': fieldVal + }); + return acc; + }, []); + + const submitData = { + updatedFormFields: newFormValues, + } + + storeSet.main.setAsyncRequest(); + AmendmentsService.updateSoccorso(amendmentId, submitData, updateAmendmentCallback, errUpdateAmendmentCallback); + } + + const updateAmendmentCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + } + storeSet.main.unsetAsyncRequest(); + } + + const errUpdateAmendmentCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + const doCloseAmendment = () => { + const submitData = { + internalNote: data.internalNote + } + storeSet.main.setAsyncRequest(); + AmendmentsService.closeSoccorso(amendmentId, submitData, closeAmendmentCallback, errCloseAmendmentCallback); + } + + const closeAmendmentCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + if (data.data.status) { + updateNewAmendmentData(data.data.status, 'status') + } + } + storeSet.main.unsetAsyncRequest(); + } + + const errCloseAmendmentCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + storeSet.main.unsetAsyncRequest(); + } + + const headerExtendRespDialog = () => { + return {__('Estendi scadenza', 'gepafin')} + } + + const hideExtendRespDialog = () => { + setIsVisibleExtendTimeDialog(false); + } + + const footerExtendRespDialog = () => { + return
    +
    + } + + const openExtendResponseTimeDialog = () => { + setIsVisibleExtendTimeDialog(true); + setExtendedTime(3); + } + + const doExtendTimeResponse = () => { + setIsLoadingExtendingTime(true); + AmendmentsService.extendSoccorso(amendmentId, extendedTime, extendCallback, errExtendCallback); + } + + const extendCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + setIsVisibleExtendTimeDialog(false); + } + setIsLoadingExtendingTime(false); + } + + const errExtendCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + setIsLoadingExtendingTime(false); + } + + const sendReminder = () => { + setIsLoadingReminding(true); + AmendmentsService.sendReminderForSoccorso(amendmentId, reminderCallback, errReminderCallback) + } + + const reminderCallback = (data) => { + if (data.status === 'SUCCESS') { + if (toast.current) { + toast.current.show({ + severity: 'success', + summary: '', + detail: data.message + }); + } + } + setIsLoadingReminding(false); + } + + const errReminderCallback = (data) => { + if (toast.current && data.message) { + toast.current.show({ + severity: 'error', + summary: '', + detail: data.message + }); + } + set404FromErrorResponse(data); + setIsLoadingReminding(false); + } + + useEffect(() => { + const parsedSoccorsoId = parseInt(amendmentId); + const soccorsoEntityId = !isNaN(parsedSoccorsoId) ? parsedSoccorsoId : 0; + + AmendmentsService.getSoccorsoById(getCallback, errGetCallback, [['id', soccorsoEntityId]]); + }, [amendmentId]); + + return ( +
    +
    +

    {__('Soccorso Istruttorio - Dettagli', 'gepafin')}

    +
    + +
    + + + +
    +
    + +
    + +
    +
    +

    + {__('ID domanda', 'gepafin')} + {data.applicationId} +

    +

    + {__('Bando', 'gepafin')} + {data.callName} +

    +

    + {__('Beneficiario', 'gepafin')} + {data.beneficiaryName} +

    +

    + {__('Inizio', 'gepafin')} + {getDateFromISOstring(data.startDate)} +

    +

    + {__('Scadenza', 'gepafin')} + {getDateFromISOstring(data.expirationDate)} +

    +

    + {__('Stato', 'gepafin')} + {getBandoLabel(data.status)} +

    +
    + +
    +

    {__('Dettagli Richiesta', 'gepafin')}

    +
    +
    +

    {__('Documenti Richiesti', 'gepafin')}

    +
      + {data.formFields + ? data.formFields.map((o, i) =>
    1. + {o.label} +
    2. ) : null} +
    +
    +
    +

    {__('Note e spiegazioni', 'gepafin')}

    +
    + {renderHtmlContent(data.note)} +
    +
    + +
    +
    + +
    +

    {__('Comunicazioni', 'gepafin')}

    + + + + + + + + + {!isNil(comms) && !isEmpty(comms) + ? comms.map((o, i) => + + + ) + : + + + } + +
    {__('Data', 'gepafin')}{__('Comunicazione', 'gepafin')}
    + {getDateFromISOstring(o.commentedDate)} + +

    {o.title}

    +

    {o.comment}

    +
    --
    + +
    + +
    +

    {__('Documenti Ricevuti', 'gepafin')}

    + +
    + {data.formFields + ? data.formFields.map((o, i) => { + /*const thisField = head(test.updatedFormFields.filter(j => j.fieldId === o.fieldId)); + const value = pathOr({}, ['fieldValue'], thisField); + console.log('value', value, o.fieldId);*/ + return + }) : null} + + +
    + +
    + +
    + {__('Azioni', 'gepafin')} +
    + +
    +
    +
    +
    + +
    + +
    + + updateNewAmendmentData( + e.htmlValue, + 'internalNote' + )} + style={{ height: 80 * 3, width: '100%' }} + /> +
    +
    + +
    + + +
    + + updateNewCommData(e.target.value, 'title')}/> + + + updateNewCommData(e.target.value, 'comment')}/> +
    +
    + + +
    + + setExtendedTime(e.value)}/> +
    +
    +
    + ) + +} + +export default SoccorsoEditPreInstructor; \ No newline at end of file diff --git a/src/pages/SoccorsoIstruttorioPreInstructor/components/PreInstructorSoccorsiTable/index.js b/src/pages/SoccorsoIstruttorioPreInstructor/components/PreInstructorSoccorsiTable/index.js new file mode 100644 index 0000000..31a6a70 --- /dev/null +++ b/src/pages/SoccorsoIstruttorioPreInstructor/components/PreInstructorSoccorsiTable/index.js @@ -0,0 +1,174 @@ +import React, { useState, useEffect} from 'react'; +import { __ } from '@wordpress/i18n'; +import { is, uniq } from 'ramda'; +import { Link } from 'react-router-dom'; + +// store +import { storeGet } from '../../../../store'; + +// api + + +// 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 { Button } from 'primereact/button'; +import { Calendar } from 'primereact/calendar'; +import ProperBandoLabel from '../../../../components/ProperBandoLabel'; +import AmendmentsService from '../../../../service/amendments-service'; + + +const PreInstructorSoccorsiTable = ({ openDialogFn }) => { + const [items, setItems] = useState(null); + const [filters, setFilters] = useState(null); + const [localAsyncRequest, setLocalAsyncRequest] = useState(false); + const [globalFilterValue, setGlobalFilterValue] = useState(''); + const [statuses, setStatuses] = useState([]); + + useEffect(() => { + const userData = storeGet.main.userData() + setLocalAsyncRequest(true); + AmendmentsService.getSoccorsoByPreInstructorId(userData.id, getCallback, errGetCallbacks); + }, []); + + const getCallback = (data) => { + if (data.status === 'SUCCESS') { + setItems(getFormattedData(data.data)); + setStatuses(uniq(data.data.map(o => o.status))) + initFilters(); + } + setLocalAsyncRequest(false); + } + + const errGetCallbacks = (data) => { + setLocalAsyncRequest(false); + } + + const getFormattedData = (data) => { + return data.map((d) => { + d.startDate = is(String, d.startDate) ? new Date(d.startDate) : (d.startDate ? d.startDate : ''); + d.expirationDate = is(String, d.expirationDate) ? new Date(d.expirationDate) : (d.expirationDate ? d.expirationDate : ''); + 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 }, + callName: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] + }, + beneficiaryName: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] + }, + startDate: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] + }, + expirationDate: { + operator: FilterOperator.AND, + constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] + } + }); + setGlobalFilterValue(''); + }; + + const renderHeader = () => { + return ( +
    +
    + ); + }; + + const dateStartBodyTemplate = (rowData) => { + return formatDate(rowData.startDate); + }; + + const dateExpirationBodyTemplate = (rowData) => { + return formatDate(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 statusBodyTemplate = (rowData) => { + return ; + }; + + const actionsBodyTemplate = (rowData) => { + return +