Merge branch 'master' of github.com:Kitzanos/GEPAFIN-FE

This commit is contained in:
Vitalii Kiiko
2024-11-12 11:26:56 +01:00
60 changed files with 4316 additions and 504 deletions

View File

@@ -8,7 +8,8 @@
"@date-fns/tz": "1.1.2", "@date-fns/tz": "1.1.2",
"@emailjs/browser": "^4.4.1", "@emailjs/browser": "^4.4.1",
"@emotion/styled": "11.13.0", "@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/i18n": "5.8.0",
"@wordpress/react-i18n": "4.8.0", "@wordpress/react-i18n": "4.8.0",
"@xyflow/react": "12.3.1", "@xyflow/react": "12.3.1",

View File

@@ -2,6 +2,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 24px; gap: 24px;
width: 100%;
} }
.appForm__field { .appForm__field {
position: relative; position: relative;
@@ -10,6 +11,11 @@
gap: 14px; gap: 14px;
padding: 5px 0; padding: 5px 0;
width: 100%; width: 100%;
&.row {
flex-direction: row;
align-items: center;
}
label { label {
font-size: 14px; font-size: 14px;

View File

@@ -88,6 +88,18 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; 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 { h2 {
color: var(--global-textColor); color: var(--global-textColor);
@@ -98,11 +110,27 @@
margin: 0 0 24px; 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 { .row {
display: flex; display: flex;
gap: 1rem; gap: 1rem;
align-items: center; align-items: center;
padding: 5px 0; padding: 5px 0;
width: 100%;
}
.col {
display: flex;
flex-direction: column;
gap: 1rem;
} }
} }
@@ -117,6 +145,10 @@
container-name: section_with_border; container-name: section_with_border;
container-type: inline-size; container-type: inline-size;
&.grey {
border-color: var(--table-border-color)
}
&.disabled { &.disabled {
filter: grayscale(1); filter: grayscale(1);
opacity: 0.8; opacity: 0.8;
@@ -149,14 +181,101 @@
flex: 1 1 100%; flex: 1 1 100%;
} }
ul { ul, ol {
padding-left: 1rem; padding-left: 1rem;
li {
color: var(--global-textColor);
}
}
a {
color: var(--global-textColor);
text-decoration: underline;
&:hover {
text-decoration: none;
}
} }
button { button {
flex: 0 0 auto; 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) { @container section_with_border (max-width: 600px) {
@@ -215,6 +334,10 @@
font-weight: 600; font-weight: 600;
line-height: normal; line-height: normal;
} }
span:nth-of-type(2) {
font-weight: 400;
}
} }
.appPageSection__table { .appPageSection__table {
@@ -269,4 +392,12 @@
&[disabled] { &[disabled] {
background-color: var(--message-info-background); background-color: var(--message-info-background);
} }
}
@media (max-width: 700px) {
.appPageSection {
&.columns {
grid-template-columns: 1fr;
}
}
} }

View File

@@ -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;
}
}

View File

@@ -12,9 +12,19 @@ body {
margin: 0; margin: 0;
font-family: "Montserrat", sans-serif; 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), /*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, label:not(.p-error), textarea, a, li, h1, h2, h3, h4, h5, h6, div:not(.p-inline-message, .p-toast-detail), th, td { 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); 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; color: white;
} }
svg {
path {
fill: white;
}
}
span { span {
color: var(--menuitem-active-color); color: var(--menuitem-active-color);
} }

View File

@@ -113,16 +113,15 @@
background-color: rgba(255,255,255,0.3) background-color: rgba(255,255,255,0.3)
} }
.p-inputgroup {
align-items: center;
}
.p-accordion-header-text, .p-accordion-content { .p-accordion-header-text, .p-accordion-content {
p { p {
margin: 0; margin: 0;
} }
} }
.p-inputgroup.flex-1 {
align-items: center;
}
.flex-1 { .flex-1 {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -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;
}
}

View File

@@ -18,6 +18,7 @@
--table-border-color: #B7B7B7B2; --table-border-color: #B7B7B7B2;
--message-error-background: #ffdbdb; --message-error-background: #ffdbdb;
--message-error-color: #C2504D; --message-error-color: #C2504D;
--message-warning-color: #cc8925;
--message-info-background: rgba(183, 183, 183, 0.7); --message-info-background: rgba(183, 183, 183, 0.7);
--message-info-color: #3B82F6; --message-info-color: #3B82F6;
@@ -40,4 +41,6 @@
@import "./components/misc.scss"; @import "./components/misc.scss";
@import "./components/login.scss"; @import "./components/login.scss";
@import "./components/flowBuilder.scss"; @import "./components/flowBuilder.scss";
@import "./components/error404.scss"; @import "./components/error404.scss";
@import "./components/myTable.scss";
@import "./components/evaluation.scss";

View File

@@ -55,7 +55,7 @@ const TopBarProfileMenu = ({ menuLeftRef }) => {
command: () => { command: () => {
navigate('/profilo-aziendale') 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'), label: __('Seleziona azienda', 'gepafin'),
@@ -67,7 +67,7 @@ const TopBarProfileMenu = ({ menuLeftRef }) => {
command: () => { command: () => {
navigate('/agguingi-azienda') navigate('/agguingi-azienda')
}, },
enable: !intersection(permissions, ['MANAGE_TENDERS']).length enable: intersection(permissions, ['APPLY_CALLS']).length
}, },
{ {
separator: true, separator: true,
@@ -86,7 +86,7 @@ const TopBarProfileMenu = ({ menuLeftRef }) => {
const switchCompany = (id) => { const switchCompany = (id) => {
if (chosenCompanyId !== id) { if (chosenCompanyId !== id) {
storeSet.main.chosenCompanyId(id); storeSet.main.chosenCompanyId(id);
console.log('set company 2', id)
if (toast.current) { if (toast.current) {
toast.current.show({ toast.current.show({
severity: 'success', severity: 'success',

View File

@@ -2,17 +2,62 @@ import { useEffect } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import equal from 'fast-deep-equal'; import equal from 'fast-deep-equal';
//import { diff } from 'deep-object-diff'; //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 // store
import { storeGet } from '../../store'; import { storeGet } from '../../store';
const UnsavedChangesDetector = ({ getValuesFn }) => { const UnsavedChangesDetector = ({ getValuesFn }) => {
const warnIfUnsavedChanges = (event) => { 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 initial = storeGet.main.formInitialData();
const isEqual = equal(initial, formData); const isEqual = equal(initial, formData);
// TODO // TODO
//console.log('isEqual', isEqual, initial, formData, diff(initial, formData)) /*console.log('isEqual', isEqual,
initial, formData,
diff(initial, formData))*/
if (!isEqual) { if (!isEqual) {
event.returnValue = __('You have unsaved changes. If you proceed, they will be lost.', 'gepafin'); event.returnValue = __('You have unsaved changes. If you proceed, they will be lost.', 'gepafin');
} }

View File

@@ -17,6 +17,9 @@ const getBandoLabel = (status) => {
case 'READY': case 'READY':
return __('Pronto', 'gepafin'); return __('Pronto', 'gepafin');
case 'SOCCORSO':
return __('Soccorso', 'gepafin');
case 'DRAFT': case 'DRAFT':
return __('Bozza', 'gepafin'); return __('Bozza', 'gepafin');
@@ -38,6 +41,9 @@ const getBandoLabel = (status) => {
case 'EXPIRED': case 'EXPIRED':
return __('Scaduto', 'gepafin'); return __('Scaduto', 'gepafin');
case 'CLOSE':
return __('Chiuso', 'gepafin');
default: default:
return ''; return '';
} }

View File

@@ -15,6 +15,9 @@ const getBandoSeverity = (status) => {
case 'READY': case 'READY':
return 'info'; return 'info';
case 'SOCCORSO':
return 'warning';
case 'DRAFT': case 'DRAFT':
return 'warning'; return 'warning';
@@ -36,6 +39,9 @@ const getBandoSeverity = (status) => {
case 'EXPIRED': case 'EXPIRED':
return 'closed'; return 'closed';
case 'CLOSE':
return 'closed';
default: default:
return 'info'; return 'info';
} }

View File

@@ -1,6 +1,10 @@
import parse from 'html-react-parser'; import parse from 'html-react-parser';
import { isNil } from 'ramda'; 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; export default renderHtmlContent;

View File

@@ -0,0 +1,20 @@
import React from 'react';
const HelpIcon = () => {
return <svg width="15" height="14" viewBox="0 0 15 14" fill="none"
xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_1665_1656)">
<path
d="M7.50129 14C6.53308 14 5.62302 13.8163 4.7711 13.4488C3.91919 13.0814 3.17818 12.5827 2.54805 11.9529C1.91793 11.323 1.41903 10.5823 1.05134 9.73074C0.683781 8.87919 0.5 7.96938 0.5 7.00129C0.5 6.03308 0.683719 5.12302 1.05116 4.2711C1.4186 3.41919 1.91725 2.67818 2.54713 2.04805C3.17701 1.41793 3.91772 0.919026 4.76926 0.551342C5.62081 0.183781 6.53062 0 7.49871 0C8.46692 0 9.37698 0.183719 10.2289 0.551158C11.0808 0.918596 11.8218 1.41725 12.4519 2.04713C13.0821 2.67701 13.581 3.41772 13.9487 4.26926C14.3162 5.12081 14.5 6.03062 14.5 6.99871C14.5 7.96692 14.3163 8.87698 13.9488 9.72889C13.5814 10.5808 13.0827 11.3218 12.4529 11.9519C11.823 12.5821 11.0823 13.081 10.2307 13.4487C9.37919 13.8162 8.46938 14 7.50129 14ZM5.36316 12.4895L6.44576 10.0238C5.98585 9.86147 5.58888 9.60977 5.25484 9.26874C4.92081 8.92758 4.66598 8.52747 4.49037 8.06842L1.99634 9.1C2.28359 9.88596 2.72195 10.5737 3.31142 11.1632C3.90089 11.7526 4.58481 12.1947 5.36316 12.4895ZM4.49037 5.93158C4.65665 5.47253 4.90816 5.07555 5.24489 4.74066C5.58163 4.40576 5.97719 4.15567 6.43158 3.99037L5.4 1.49634C4.60937 1.79587 3.91932 2.24037 3.32984 2.82984C2.74037 3.41932 2.29587 4.10937 1.99634 4.9L4.49037 5.93158ZM7.49834 9.09724C8.08045 9.09724 8.57579 8.8935 8.98437 8.48603C9.39295 8.07855 9.59724 7.58376 9.59724 7.00166C9.59724 6.41955 9.3935 5.92421 8.98603 5.51563C8.57855 5.10705 8.08376 4.90276 7.50166 4.90276C6.91955 4.90276 6.42421 5.1065 6.01563 5.51397C5.60705 5.92145 5.40276 6.41624 5.40276 6.99834C5.40276 7.58045 5.6065 8.07579 6.01397 8.48437C6.42145 8.89295 6.91624 9.09724 7.49834 9.09724ZM9.63684 12.4895C10.4105 12.1947 11.089 11.7557 11.6724 11.1724C12.2557 10.589 12.6947 9.91053 12.9895 9.13684L10.5238 8.05424C10.368 8.50862 10.1209 8.90302 9.78274 9.23742C9.44453 9.57182 9.05203 9.82923 8.60526 10.0096L9.63684 12.4895ZM10.5096 5.89474L12.9895 4.86316C12.6947 4.08947 12.2557 3.41096 11.6724 2.82763C11.089 2.2443 10.4105 1.80526 9.63684 1.51053L8.60526 4.01303C9.04258 4.18299 9.4242 4.43051 9.75013 4.75558C10.0761 5.08053 10.3292 5.46025 10.5096 5.89474Z"
fill="currentColor"/>
</g>
<defs>
<clipPath id="clip0_1665_1656">
<rect width="14" height="14" fill="white"
transform="translate(0.5)"/>
</clipPath>
</defs>
</svg>
}
export default HelpIcon;

View File

@@ -1,12 +1,13 @@
import React from 'react'; import React from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { intersection } from 'ramda'; import { intersection, is } from 'ramda';
// store // store
import { useStore } from '../../../../store'; import { useStore } from '../../../../store';
// components // components
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import HelpIcon from '../../../../icons/HelpIcon';
const AppSidebar = () => { const AppSidebar = () => {
const permissions = useStore().main.getPermissions(); const permissions = useStore().main.getPermissions();
@@ -41,32 +42,59 @@ const AppSidebar = () => {
enable: intersection(permissions, ['VIEW_CALLS']).length 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: <HelpIcon/>,
href: '/soccorso-istruttorio',
id: 8,
enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length
},
{
label: __('Gestione utenti', 'gepafin'),
icon: 'pi pi-users', icon: 'pi pi-users',
href: '/utenti', href: '/utenti',
id: 5, id: 9,
enable: false enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length
//enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length
}, },
{ {
label: __('Configurazione', 'gepafin'), label: __('Configurazione', 'gepafin'),
icon: 'pi pi-cog', icon: 'pi pi-cog',
//href: '/configurazione', //href: '/configurazione',
id: 6, id: 10,
enable: false enable: false
}, },
{ {
label: __('Report e Analisi', 'gepafin'), label: __('Report e Analisi', 'gepafin'),
icon: 'pi pi-chart-bar', icon: 'pi pi-chart-bar',
//href: '/stats', //href: '/stats',
id: 7, id: 11,
enable: false enable: false
}, },
{ {
label: __('Log di Sistema', 'gepafin'), label: __('Log di Sistema', 'gepafin'),
icon: 'pi pi-receipt', icon: 'pi pi-receipt',
clickFn: () => {}, clickFn: () => {},
id: 8, id: 12,
enable: false enable: false
} }
] ]
@@ -78,11 +106,15 @@ const AppSidebar = () => {
.map(o => <li key={o.id}> .map(o => <li key={o.id}>
{o.href {o.href
? <NavLink to={o.href}> ? <NavLink to={o.href}>
<i className={o.icon}></i> {is(String, o.icon)
? <i className={o.icon}></i>
: o.icon}
<span>{o.label}</span> <span>{o.label}</span>
</NavLink> </NavLink>
: <button onClick={() => {}}> : <button onClick={() => {}}>
<i className={o.icon}></i> {is(String, o.icon)
? <i className={o.icon}></i>
: o.icon}
<span>{o.label}</span> <span>{o.label}</span>
</button>} </button>}
</li>)} </li>)}

View File

@@ -27,9 +27,10 @@ const AppTopbar = () => {
<i className="pi pi-bell p-overlay-badge topBar__icon"> <i className="pi pi-bell p-overlay-badge topBar__icon">
<Badge value="0"></Badge> <Badge value="0"></Badge>
</i> </i>
<i className="pi pi-envelope p-overlay-badge topBar__icon"> <i className="pi pi-envelope topBar__icon"></i>
{/*<i className="pi pi-envelope p-overlay-badge topBar__icon">
<Badge severity="danger"></Badge> <Badge severity="danger"></Badge>
</i> </i>*/}
<Button <Button
className="topBar__profileBtn" className="topBar__profileBtn"
outlined outlined
@@ -40,7 +41,7 @@ const AppTopbar = () => {
<TopBarProfileMenu menuLeftRef={menuLeft}/> <TopBarProfileMenu menuLeftRef={menuLeft}/>
</div> </div>
return( return (
<Toolbar start={startContent} end={endContent} className="topBar"/> <Toolbar start={startContent} end={endContent} className="topBar"/>
) )
} }

View File

@@ -2,9 +2,6 @@ import React, { useState, useEffect} from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { is, uniq } from 'ramda'; import { is, uniq } from 'ramda';
// store
import { storeSet, storeGet } from '../../../../store';
// tools // tools
import getBandoSeverity from '../../../../helpers/getBandoSeverity'; import getBandoSeverity from '../../../../helpers/getBandoSeverity';
import getBandoLabel from '../../../../helpers/getBandoLabel'; import getBandoLabel from '../../../../helpers/getBandoLabel';
@@ -31,12 +28,12 @@ import { Link } from 'react-router-dom';
const AllBandiTable = () => { const AllBandiTable = () => {
const [items, setItems] = useState(null); const [items, setItems] = useState(null);
const [filters, setFilters] = useState(null); const [filters, setFilters] = useState(null);
const [loading, setLoading] = useState(false); const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [globalFilterValue, setGlobalFilterValue] = useState(''); const [globalFilterValue, setGlobalFilterValue] = useState('');
const [statuses, setStatuses] = useState([]); const [statuses, setStatuses] = useState([]);
useEffect(() => { useEffect(() => {
storeSet.main.setAsyncRequest(); setLocalAsyncRequest(true);
BandoService.getBandi(getCallback, errGetCallbacks); BandoService.getBandi(getCallback, errGetCallbacks);
}, []); }, []);
@@ -46,12 +43,11 @@ const AllBandiTable = () => {
setStatuses(uniq(data.data.map(o => o.status))) setStatuses(uniq(data.data.map(o => o.status)))
initFilters(); initFilters();
} }
storeSet.main.unsetAsyncRequest(); setLocalAsyncRequest(false);
} }
const errGetCallbacks = (data) => { const errGetCallbacks = (data) => {
console.log('errGetCallbacks', data) setLocalAsyncRequest(false);
storeSet.main.unsetAsyncRequest();
} }
const getFormattedBandiData = (data) => { const getFormattedBandiData = (data) => {
@@ -98,9 +94,9 @@ const AllBandiTable = () => {
); );
}; };
const nameBodyTemplate = (rowData) => { /*const nameBodyTemplate = (rowData) => {
return <span>{rowData.name}</span> return <span>{rowData.name}</span>
} }*/
const dateStartBodyTemplate = (rowData) => { const dateStartBodyTemplate = (rowData) => {
return getDateFromISOstring(rowData.dates[0]); return getDateFromISOstring(rowData.dates[0]);
@@ -136,7 +132,7 @@ const AllBandiTable = () => {
return( return(
<div className="appPageSection__table"> <div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={loading} dataKey="id" <DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
filters={filters} filters={filters}
globalFilterFields={['name', 'status']} globalFilterFields={['name', 'status']}
header={header} header={header}
@@ -144,15 +140,15 @@ const AllBandiTable = () => {
onFilter={(e) => setFilters(e.filters)}> onFilter={(e) => setFilters(e.filters)}>
<Column field="name" header={__('Nome Bando', 'gepafin')} <Column field="name" header={__('Nome Bando', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')} filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '12rem' }}/> style={{ minWidth: '8rem' }}/>
<Column header={__('Data Pubblicazione', 'gepafin')} filterField="start_date" dataType="date" <Column header={__('Data Pubblicazione', 'gepafin')} filterField="start_date" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '8rem' }}
body={dateStartBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateStartBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column header={__('Data Scadenza', 'gepafin')} filterField="end_date" dataType="date" <Column header={__('Data Scadenza', 'gepafin')} filterField="end_date" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '8rem' }}
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="status" header={__('Stato', 'gepafin')} filterMenuStyle={{ width: '14rem' }} <Column field="status" header={__('Stato', 'gepafin')} filterMenuStyle={{ width: '14rem' }}
style={{ width: '120px' }} body={statusBodyTemplate} filter style={{ minWidth: '7rem' }} body={statusBodyTemplate} filter
filterElement={statusFilterTemplate}/> filterElement={statusFilterTemplate}/>
<Column header={__('Azioni', 'gepafin')} <Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/> body={actionsBodyTemplate}/>

View File

@@ -92,21 +92,21 @@ const AllBandiAccordion = () => {
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)} />; return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)} />;
}; };
const addToFavourites = () => { /*const addToFavourites = () => {
console.log('addToFavourites'); console.log('addToFavourites');
} }*/
const goToBandoPage = (id) => { const goToBandoPage = (id) => {
navigate(`/bandi/${id}`) navigate(`/bandi/${id}`)
} }
const actionsBodyTemplate = (rowData) => { /*const actionsBodyTemplate = (rowData) => {
return <div className="appPageSection__tableActions"> return <div className="appPageSection__tableActions">
<button type="button" className="appPageSection__addToFavourites" onClick={addToFavourites} disabled={true}> <button type="button" className="appPageSection__addToFavourites" onClick={addToFavourites} disabled={true}>
<i className="pi pi-heart" style={{ fontSize: '1rem' }}></i> <i className="pi pi-heart" style={{ fontSize: '1rem' }}></i>
</button> </button>
</div> </div>
} }*/
const rowExpansionTemplate = (data) => { const rowExpansionTemplate = (data) => {
return ( return (

View File

@@ -1,20 +1,13 @@
import React from 'react'; import React from 'react';
import { range } from 'ramda'; import { range } from 'ramda';
//import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
// components // components
import { Steps } from 'primereact/steps'; import { Steps } from 'primereact/steps';
const ApplicationSteps = ({ totalSteps = 0, activeStepIndex }) => { const ApplicationSteps = ({ totalSteps = 0, activeStepIndex }) => {
const rangeArr = range(1, totalSteps + 1); const rangeArr = range(1, totalSteps + 1);
const items = rangeArr.map(() => ({ label: 'Passo' })); const items = rangeArr.map(() => ({ label: __('Passo', 'gepafin') }));
/*// TODO update to using Steps after primereact is updated
return(
0 !== totalSteps
? <span>{__('Passo', 'gepafin')}: {activeStepIndex + 1}</span>
: null
)*/
return( return(
0 !== totalSteps 0 !== totalSteps

View File

@@ -326,10 +326,14 @@ const BandoApplication = () => {
].includes(cur)) { ].includes(cur)) {
acc.user[cur] = userData[cur]; acc.user[cur] = userData[cur];
} }
if (['dateOfBirth'].includes(cur)) {
acc.user[cur] = new Date(userData[cur]);
}
return acc; return acc;
}, dynamicData); }, dynamicData);
if (data.data.applicationFormResponse.content) { if (data.data.applicationFormResponse.content) {
// eslint-disable-next-line array-callback-return
data.data.applicationFormResponse.content.map((o) => { data.data.applicationFormResponse.content.map((o) => {
if (o.dynamicData && !isEmpty(o.dynamicData)) { if (o.dynamicData && !isEmpty(o.dynamicData)) {
formDataInitial[o.id] = pathOr('', o.dynamicData.split('.'), dynamicData); formDataInitial[o.id] = pathOr('', o.dynamicData.split('.'), dynamicData);

View File

@@ -5,6 +5,7 @@ import { useForm } from 'react-hook-form';
import { isEmpty, isNil, is } from 'ramda'; import { isEmpty, isNil, is } from 'ramda';
import { klona } from 'klona'; import { klona } from 'klona';
import { TZDate } from '@date-fns/tz'; import { TZDate } from '@date-fns/tz';
import { wrap } from 'object-path-immutable';
// components // components
import FormField from '../../../../components/FormField'; import FormField from '../../../../components/FormField';
@@ -48,38 +49,36 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st
const values = getValues(); const values = getValues();
const toast = useRef(null); const toast = useRef(null);
const onSubmit = (formData) => { const onSubmit = () => {};
/*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 onSaveDraft = () => { const onSaveDraft = () => {
trigger(); trigger();
const formData = getValues(); let formData = klona(getValues());
if (!isNil(formData.dates) && formData.dates.length) { formData.dates = [];
formData.dates = formData.dates.map(v => {
if (is(String, v)) { if (formData.startDate) {
return v; let starDate;
} else {
const tzAwareDate = new TZDate(v, 'Europe/Berlin'); if (is(String, formData.startDate)) {
return tzAwareDate.toISOString().substring(0, 19); 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 (!isNil(formData.startTime)) {
if (!is(String, 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(); storeSet.main.setAsyncRequest();
if (!formData.id) { if (!formData.id) {
BandoService.createBando(formData, createCallback, errCreateCallback); BandoService.createBando(formData, createCallback, errCreateCallback);
@@ -106,11 +108,13 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st
const createCallback = (data) => { const createCallback = (data) => {
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
toast.current.show({ if (toast.current) {
severity: 'success', toast.current.show({
summary: '', severity: 'success',
detail: __('Il bando è stato aggiornato corretamente!', 'gepafin') summary: '',
}); detail: __('Il bando è stato aggiornato corretamente!', 'gepafin')
});
}
const values = getValues(); const values = getValues();
if (!values.id && data.data.id) { if (!values.id && data.data.id) {
navigate(`/bandi/${data.data.id}`); navigate(`/bandi/${data.data.id}`);
@@ -121,6 +125,13 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st
} }
const errCreateCallback = (data) => { const errCreateCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: 'error',
summary: '',
detail: data.message
});
}
set404FromErrorResponse(data); set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
@@ -193,7 +204,12 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st
useEffect(() => { useEffect(() => {
const newFormData = klona(formInitialData); const newFormData = klona(formInitialData);
if (!isNil(formInitialData.dates) && formInitialData.dates.length) { 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)) { if (!isNil(formInitialData.startTime) && !isEmpty(formInitialData.startTime)) {
newFormData.startTime = is(String, formInitialData.startTime) newFormData.startTime = is(String, formInitialData.startTime)
@@ -305,7 +321,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st
}} }}
/> />
<FormField {/*<FormField
type="datepickerrange" type="datepickerrange"
disabled={shouldDisableField()} disabled={shouldDisableField()}
fieldName="dates" fieldName="dates"
@@ -314,7 +330,30 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st
errors={errors} errors={errors}
defaultValue={values['dates']} defaultValue={values['dates']}
config={{ required: __('È obbligatorio', 'gepafin') }} config={{ required: __('È obbligatorio', 'gepafin') }}
/> />*/}
<div className="appForm__cols">
<FormField
type="datepicker"
disabled={shouldDisableField()}
fieldName="startDate"
label={__('Data di inizio', 'gepafin')}
control={control}
errors={errors}
defaultValue={values['startDate']}
config={{ required: __('È obbligatorio', 'gepafin') }}
/>
<FormField
type="datepicker"
disabled={shouldDisableField()}
fieldName="endDate"
label={__('Data di fine', 'gepafin')}
control={control}
errors={errors}
defaultValue={values['endDate']}
config={{ required: __('È obbligatorio', 'gepafin') }}
/>
</div>
<div className="appForm__cols"> <div className="appForm__cols">
<FormField <FormField

View File

@@ -22,6 +22,7 @@ import LookupdataService from '../../../../service/lookupdata-service';
import { storeSet } from '../../../../store'; import { storeSet } from '../../../../store';
import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString'; import getTimeParsedFromString from '../../../../helpers/getTimeParsedFromString';
import { mimeTypes } from '../../../../configData'; import { mimeTypes } from '../../../../configData';
import { wrap } from 'object-path-immutable';
const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors, status }, ref) { const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors, status }, ref) {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -46,41 +47,36 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors, st
//const step2Props = ['threshold', 'criteria', 'checkList', 'docs', 'images']; //const step2Props = ['threshold', 'criteria', 'checkList', 'docs', 'images'];
const toast = useRef(null); const toast = useRef(null);
const onSubmit = (formData) => { const onSubmit = () => {};
/*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 onSaveDraft = () => { const onSaveDraft = () => {
trigger(); trigger();
const formData = getValues(); let formData = klona(getValues());
if (!isNil(formData.dates) && formData.dates.length) { formData.dates = [];
formData.dates = formData.dates.map(v => {
if (is(String, v)) { if (formData.startDate) {
return v; let starDate;
} else {
const tzAwareDate = new TZDate(v, 'Europe/Berlin'); if (is(String, formData.startDate)) {
return tzAwareDate.toISOString().substring(0, 19); 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 (!isNil(formData.startTime)) {
if (!is(String, 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); formData.startTime = tzAwareDate.toISOString().substring(11, 16);
} }
} }
if (!isNil(formData.endTime)) { if (!isNil(formData.endTime)) {
if (!is(String, formData.endTime)) { if (!is(String, formData.endTime)) {
const tzAwareDate = new TZDate(formData.endTime, 'Europe/Berlin'); 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(); storeSet.main.setAsyncRequest();
BandoService.updateBandoStep2(formData.id, formData, createCallback, errCreateCallback); BandoService.updateBandoStep2(formData.id, formData, createCallback, errCreateCallback);
} }

View File

@@ -76,9 +76,9 @@ const BandoEdit = () => {
BandoService.validateBando(id, validateCallback, errValidateCallback); BandoService.validateBando(id, validateCallback, errValidateCallback);
} }
const validateCallback = (data) => { const validateCallback = (resp) => {
if (data.status === 'SUCCESS') { if (resp.status === 'SUCCESS') {
setData({ ...data, status: data.data.status }); setData(resp.data);
if (bandoMsgs.current) { if (bandoMsgs.current) {
bandoMsgs.current.show([ bandoMsgs.current.show([
{ {
@@ -100,19 +100,26 @@ const BandoEdit = () => {
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const errValidateCallback = (data) => { const errValidateCallback = (resp) => {
if (data.status === 'VALIDATION_ERROR') { if (resp.status === 'VALIDATION_ERROR') {
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
if (bandoMsgs.current) { if (bandoMsgs.current) {
bandoMsgs.current.show(data.data.map((v, i) => ({ bandoMsgs.current.show(resp.data.map((v, i) => ({
id: i, id: i,
sticky: true, severity: 'error', summary: '', sticky: true, severity: 'error', summary: '',
detail: v, detail: v,
closable: false closable: false
}))); })));
} }
if (toast.current) {
toast.current.show(resp.data.map((v, i) => ({
severity: 'error',
summary: '',
detail: v
})));
}
} else { } else {
standardErrCallback(data); standardErrCallback(resp);
} }
} }
@@ -122,8 +129,8 @@ const BandoEdit = () => {
BandoService.updateBandoStatus(id, publishCallback, errPublishCallback, [['status', 'PUBLISH']]); BandoService.updateBandoStatus(id, publishCallback, errPublishCallback, [['status', 'PUBLISH']]);
} }
const publishCallback = (data) => { const publishCallback = (resp) => {
if (data.status === 'SUCCESS') { if (resp.status === 'SUCCESS') {
if (bandoMsgs.current) { if (bandoMsgs.current) {
bandoMsgs.current.show([ bandoMsgs.current.show([
{ {
@@ -141,26 +148,31 @@ const BandoEdit = () => {
detail: __('Pubblicato!', 'gepafin') detail: __('Pubblicato!', 'gepafin')
}); });
} }
if (data.data.docs) { if (resp.data.docs) {
data.data.docs = data.data.docs resp.data.docs = resp.data.docs
.filter(o => o.source === 'CALL' && o.type === 'DOCUMENT'); .filter(o => o.source === 'CALL' && o.type === 'DOCUMENT');
} }
setData(data.data); setData(resp.data);
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const errPublishCallback = (data) => { const errPublishCallback = (resp) => {
standardErrCallback(data); standardErrCallback(resp);
} }
const getCallback = (data) => { const getCallback = (resp) => {
if (data.status === 'SUCCESS') { if (resp.status === 'SUCCESS') {
if (!isNil(data.data.dates) && data.data.dates.length) { if (!isNil(resp.data.dates) && resp.data.dates.length) {
data.data.dates = data.data.dates.map(v => is(String, v) ? new Date(v) : v); 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.clear();
bandoMsgs.current.show([ bandoMsgs.current.show([
{ {
@@ -170,7 +182,7 @@ const BandoEdit = () => {
closable: false closable: false
} }
]); ]);
} else if (data.data.status === 'DRAFT') { } else if (resp.data.status === 'DRAFT') {
if (bandoMsgs.current) { if (bandoMsgs.current) {
bandoMsgs.current.clear(); bandoMsgs.current.clear();
bandoMsgs.current.show([ bandoMsgs.current.show([
@@ -183,26 +195,26 @@ const BandoEdit = () => {
]); ]);
} }
} }
if (data.data.docs) { if (resp.data.docs) {
data.data.docs = data.data.docs resp.data.docs = resp.data.docs
.filter(o => o.source === 'CALL' && o.type === 'DOCUMENT'); .filter(o => o.source === 'CALL' && o.type === 'DOCUMENT');
} }
setData(data.data); setData(resp.data);
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const errGetCallback = (data) => { const errGetCallback = (resp) => {
set404FromErrorResponse(data); set404FromErrorResponse(resp);
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const standardErrCallback = (data) => { const standardErrCallback = (resp) => {
if (bandoMsgs.current && data.message) { if (bandoMsgs.current && resp.message) {
bandoMsgs.current.show([ bandoMsgs.current.show([
{ {
sticky: true, severity: 'error', summary: '', sticky: true, severity: 'error', summary: '',
detail: data.message, detail: resp.message,
closable: true closable: true
} }
]); ]);
@@ -210,9 +222,9 @@ const BandoEdit = () => {
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const getFormsCallback = (data) => { const getFormsCallback = (resp) => {
if (data.status === 'SUCCESS') { if (resp.status === 'SUCCESS') {
setForms(data.data); setForms(resp.data);
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }

View File

@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
import { head, isEmpty, isNil } from 'ramda'; import { head, isEmpty, isNil } from 'ramda';
import { __, sprintf } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import { klona } from 'klona'; import { klona } from 'klona';
import { wrap } from 'object-path-immutable';
// store // store
import { storeSet, useStore } from '../../../../store'; import { storeSet, useStore } from '../../../../store';
@@ -14,13 +15,17 @@ import { TabView, TabPanel } from 'primereact/tabview';
import { InputSwitch } from 'primereact/inputswitch'; import { InputSwitch } from 'primereact/inputswitch';
import ElementSetting from './components/ElementSetting'; import ElementSetting from './components/ElementSetting';
import { Dropdown } from 'primereact/dropdown'; import { Dropdown } from 'primereact/dropdown';
import { MultiSelect } from 'primereact/multiselect';
const BuilderElementSettings = ({ closeSettings }) => { const BuilderElementSettings = ({ closeSettingsFn }) => {
const elements = useStore().main.formElements(); const elements = useStore().main.formElements();
const activeElement = useStore().main.activeElement(); const activeElement = useStore().main.activeElement();
const criteriaOptions = useStore().main.bandoCriteria();
const [activeElementData, setActiveElementData] = useState({}); const [activeElementData, setActiveElementData] = useState({});
const [settings, setSettings] = useState([]); const [settings, setSettings] = useState([]);
const [validators, setValidators] = useState({}); const [validators, setValidators] = useState({});
const [dynamicData, setDynamicData] = useState('');
const [criteria, setCriteria] = useState([]);
const numberBasedValidatorFields = ['min', 'max', 'minLength', 'maxLength']; const numberBasedValidatorFields = ['min', 'max', 'minLength', 'maxLength'];
const customValidationOptions = [ const customValidationOptions = [
{ value: 'isPIVA', label: 'isPIVA' }, { value: 'isPIVA', label: 'isPIVA' },
@@ -63,11 +68,14 @@ const BuilderElementSettings = ({ closeSettings }) => {
} }
const saveSettings = () => { const saveSettings = () => {
activeElementData.settings = settings; let newActiveElementData = klona(activeElementData);
activeElementData.validators = validators; newActiveElementData = wrap(newActiveElementData).set(['settings'], settings).value();
const newElements = elements.map(o => o.id === activeElementData.id ? activeElementData : o); 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); storeSet.main.formElements(newElements);
closeSettings(); closeSettingsFn();
} }
const showField = (value, key) => { const showField = (value, key) => {
@@ -92,17 +100,56 @@ const BuilderElementSettings = ({ closeSettings }) => {
setValidators(newValidators); 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(() => { useEffect(() => {
const chosen = head(elements.filter(o => o.id === activeElement)); const chosen = head(elements.filter(o => o.id === activeElement));
if (chosen) { if (chosen) {
setActiveElementData(klona(chosen)); setActiveElementData(klona(chosen));
setSettings(klona(chosen.settings)); setSettings(klona(chosen.settings));
setValidators(klona(chosen.validators)) setValidators(klona(chosen.validators));
setDynamicData(chosen.dynamicData ? chosen.dynamicData : '');
setCriteria(chosen.criteria ? chosen.criteria : []);
} else { } else {
setActiveElementData({}); setActiveElementData({});
setSettings([]); setSettings([]);
setValidators({}) setValidators({});
setDynamicData('');
setCriteria([]);
} }
}, [activeElement]); }, [activeElement]);
@@ -118,6 +165,18 @@ const BuilderElementSettings = ({ closeSettings }) => {
changeFn={onChange} changeFn={onChange}
updateDataFn={onUpdateOptions}/>) updateDataFn={onUpdateOptions}/>)
: null} : null}
{['textinput', 'datepicker'].includes(activeElementData.name)
? <div className="formElementSettings__field">
<label htmlFor="dynamicData">{__('Dati dinamici', 'gepafin')}</label>
<Dropdown
id="dynamicData"
value={dynamicData}
onChange={(e) => setDynamicData(e.value)}
options={getDynamicDataOptions(activeElementData.name)}
optionLabel="label"
optionValue="value"
placeholder={__('Scegli', 'gepafin')}/>
</div> : null}
</TabPanel> </TabPanel>
{!isEmpty(validators) {!isEmpty(validators)
? <TabPanel header={__('Validation', 'gepafin')}> ? <TabPanel header={__('Validation', 'gepafin')}>
@@ -172,6 +231,20 @@ const BuilderElementSettings = ({ closeSettings }) => {
</div> : null} </div> : null}
</div>) : null} </div>) : null}
</TabPanel> : null} </TabPanel> : null}
<TabPanel header={__('Criteri', 'gepafin')}>
<div className="formElementSettings__field">
<label htmlFor="criteria">{__('Criteri di valutazione', 'gepafin')}</label>
<MultiSelect
id="criteria"
value={criteria}
onChange={(e) => onChangeCriteriaData(e.value)}
options={criteriaOptions}
optionLabel="label"
optionValue="value"
display="chip"
placeholder={__('Scegli', 'gepafin')}/>
</div>
</TabPanel>
</TabView> </TabView>
<Button label={__('Salva', 'gepafin')} onClick={saveSettings}/> <Button label={__('Salva', 'gepafin')} onClick={saveSettings}/>

View File

@@ -57,7 +57,7 @@ const FormBuilder = () => {
<> <>
<Sidebar visible={!isEmpty(activeElement)} onHide={closeSettings} className="formBuilder__elementSettings"> <Sidebar visible={!isEmpty(activeElement)} onHide={closeSettings} className="formBuilder__elementSettings">
<h2>{__('Impostazioni del campo modulo', 'gepafin')}</h2> <h2>{__('Impostazioni del campo modulo', 'gepafin')}</h2>
{!isEmpty(activeElement) ? <BuilderElementSettings closeSettings={closeSettings}/> : null} {!isEmpty(activeElement) ? <BuilderElementSettings closeSettingsFn={closeSettings}/> : null}
</Sidebar> </Sidebar>
<div className="formBuilder"> <div className="formBuilder">
<div className="formBuilder__main"> <div className="formBuilder__main">

View File

@@ -4,7 +4,7 @@ import { useNavigate, useParams } from 'react-router-dom';
import { DndProvider } from 'react-dnd'; import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend'; import { HTML5Backend } from 'react-dnd-html5-backend';
import { klona } from 'klona'; import { klona } from 'klona';
import { isEmpty } from 'ramda'; import { isEmpty, pathOr } from 'ramda';
// store // store
import { storeSet, storeGet, useStore } from '../../store'; import { storeSet, storeGet, useStore } from '../../store';
@@ -21,6 +21,7 @@ import { Messages } from 'primereact/messages';
// api // api
import FormsService from '../../service/forms-service'; import FormsService from '../../service/forms-service';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse'; import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import BandoService from '../../service/bando-service';
// TODO temp data // TODO temp data
//import { elementItems } from '../../tempData'; //import { elementItems } from '../../tempData';
@@ -243,7 +244,21 @@ const BandoFormsEdit = () => {
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const getBandoCallback = (data) => {
if (data.status === 'SUCCESS') {
const criteria = pathOr([], ['data', 'criteria'], data);
const criteriaOptions = criteria.map(o => ({value: o.id, label: o.value}));
console.log('criteriaOptions', criteriaOptions);
storeSet.main.bandoCriteria(criteriaOptions);
}
}
const errGetBandoCallback = (data) => {
set404FromErrorResponse(data);
}
useEffect(() => { useEffect(() => {
const bandoId = getBandoId();
const parsedFormId = parseInt(formId) const parsedFormId = parseInt(formId)
const bandoFormId = !isNaN(parsedFormId) ? parsedFormId : 0; const bandoFormId = !isNaN(parsedFormId) ? parsedFormId : 0;
@@ -254,11 +269,15 @@ const BandoFormsEdit = () => {
storeSet.main.setAsyncRequest(); storeSet.main.setAsyncRequest();
FormsService.getFormById(bandoFormId, getFormCallback, errGetFormCallbacks); FormsService.getFormById(bandoFormId, getFormCallback, errGetFormCallbacks);
} }
if (bandoId) {
BandoService.getBando(bandoId, getBandoCallback, errGetBandoCallback);
}
return () => { return () => {
storeSet.main.formId(0); storeSet.main.formId(0);
storeSet.main.formLabel(''); storeSet.main.formLabel('');
storeSet.main.formElements([]); storeSet.main.formElements([]);
storeSet.main.bandoCriteria([]);
} }
}, [id, formId]); }, [id, formId]);

View File

@@ -145,11 +145,11 @@ const BandoViewBeneficiario = () => {
'isVisible': false 'isVisible': false
} }
storeSet.main.setAsyncRequest(); storeSet.main.setAsyncRequest();
FaqItemService.addQuestion(id, obj, createCallBack, errCreateCallback, [['companyId', chosenCompanyId]]) FaqItemService.addQuestion(id, obj, createQuestionBack, errCreateQuestionCallback, [['companyId', chosenCompanyId]])
} }
} }
const createCallBack = (data) => { const createQuestionBack = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
setNewQuestion(''); setNewQuestion('');
if (toast.current) { if (toast.current) {
@@ -163,7 +163,7 @@ const BandoViewBeneficiario = () => {
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const errCreateCallback = (data) => { const errCreateQuestionCallback = (data) => {
if (toast.current && data.message) { if (toast.current && data.message) {
toast.current.show({ toast.current.show({
severity: 'error', severity: 'error',
@@ -262,10 +262,10 @@ const BandoViewBeneficiario = () => {
{!isAsyncRequest && !isEmpty(data) {!isAsyncRequest && !isEmpty(data)
? <div className="appPage__content"> ? <div className="appPage__content">
{!isEmpty(data.images) {!isEmpty(data.images)
? <div><picture className="appPageSection__hero"> ? <picture className="appPageSection__hero">
<source srcSet={data.images[0] ? data.images[0].filePath : ''}/> <source srcSet={data.images[0] ? data.images[0].filePath : ''}/>
<img src={data.images[0] ? data.images[0].filePath : ''} alt={data.name}/> <img src={data.images[0] ? data.images[0].filePath : ''} alt={data.name}/>
</picture></div> : null} </picture> : null}
<div className="appPageSection__withBorder"> <div className="appPageSection__withBorder">
<h2>{__('Descrizione breve', 'gepafin')}</h2> <h2>{__('Descrizione breve', 'gepafin')}</h2>
@@ -408,7 +408,7 @@ const BandoViewBeneficiario = () => {
<div className="appPageSection"> <div className="appPageSection">
<h2>{__('Download Documenti', 'gepafin')}</h2> <h2>{__('Download Documenti', 'gepafin')}</h2>
<div className="appPageSection__actions"> <div className="appPageSection__actions">
{/* <Button {/* <Button
type="button" type="button"
disabled={true} disabled={true}
outlined outlined

View File

@@ -2,13 +2,6 @@ import React, { useState, useEffect} from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { uniq } from 'ramda'; import { uniq } from 'ramda';
// tools
import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
// store
import { storeSet } from '../../../../store';
// api // api
import BandoService from '../../../../service/bando-service'; import BandoService from '../../../../service/bando-service';
@@ -19,11 +12,8 @@ import { Column } from 'primereact/column';
import { InputText } from 'primereact/inputtext'; import { InputText } from 'primereact/inputtext';
import { IconField } from 'primereact/iconfield'; import { IconField } from 'primereact/iconfield';
import { InputIcon } from 'primereact/inputicon'; import { InputIcon } from 'primereact/inputicon';
import { Dropdown } from 'primereact/dropdown';
import { InputNumber } from 'primereact/inputnumber';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { Calendar } from 'primereact/calendar'; import { Calendar } from 'primereact/calendar';
import { Tag } from 'primereact/tag';
import ProperBandoLabel from '../../../../components/ProperBandoLabel'; import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@@ -31,44 +21,12 @@ import { Link } from 'react-router-dom';
const LatestBandiTable = () => { const LatestBandiTable = () => {
const [items, setItems] = useState(null); const [items, setItems] = useState(null);
const [filters, setFilters] = useState(null); const [filters, setFilters] = useState(null);
const [loading, setLoading] = useState(false); const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [globalFilterValue, setGlobalFilterValue] = useState(''); const [globalFilterValue, setGlobalFilterValue] = useState('');
const [statuses, setStatuses] = useState([]); const [statuses, setStatuses] = useState([]);
useEffect(() => { useEffect(() => {
// TODO setLocalAsyncRequest(true);
/*const items = [
{
name: 'Bando Innovazione 2024',
start_date: '2024-08-08T00:00:00+00:00',
end_date: '2024-08-30T00:00:00+00:00',
submissions: 24,
status: 'PUBLISH',
id: 11
},
{
name: 'Bando Sostenibilità 2024',
start_date: '2024-07-28T00:00:00+00:00',
end_date: '2024-08-15T00:00:00+00:00',
submissions: 35,
status: 'PUBLISH',
id: 9
},
{
name: 'Bando A',
start_date: '2024-06-28T00:00:00+00:00',
end_date: '2024-06-15T00:00:00+00:00',
submissions: 2,
status: 'EXPIRED',
id: 2
}
]
setItems(getFormattedBandiData(items));
setStatuses(uniq(items.map(o => o.status)))
setLoading(false);
initFilters();*/
storeSet.main.setAsyncRequest();
BandoService.getBandi(getCallback, errGetCallbacks); BandoService.getBandi(getCallback, errGetCallbacks);
}, []); }, []);
@@ -79,12 +37,11 @@ const LatestBandiTable = () => {
setStatuses(uniq(data.data.map(o => o.status))) setStatuses(uniq(data.data.map(o => o.status)))
initFilters(); initFilters();
} }
storeSet.main.unsetAsyncRequest(); setLocalAsyncRequest(false);
} }
const errGetCallbacks = (data) => { const errGetCallbacks = (data) => {
console.log('errGetCallbacks', data) setLocalAsyncRequest(false);
storeSet.main.unsetAsyncRequest();
} }
const getFormattedBandiData = (data) => { const getFormattedBandiData = (data) => {
@@ -153,22 +110,10 @@ const LatestBandiTable = () => {
return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />; return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />;
}; };
const balanceFilterTemplate = (options) => {
return <InputNumber value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} />;
};
const statusBodyTemplate = (rowData) => { const statusBodyTemplate = (rowData) => {
return <ProperBandoLabel status={rowData.status}/>; return <ProperBandoLabel status={rowData.status}/>;
}; };
const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses} onChange={(e) => options.filterCallback(e.value, options.index)} itemTemplate={statusItemTemplate} placeholder="Select One" className="p-column-filter" showClear />;
};
const statusItemTemplate = (option) => {
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)} />;
};
const actionsBodyTemplate = (rowData) => { const actionsBodyTemplate = (rowData) => {
return <Link to={`/bandi/${rowData.id}`}> return <Link to={`/bandi/${rowData.id}`}>
<Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small" iconPos="right" /> <Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small" iconPos="right" />
@@ -179,22 +124,22 @@ const LatestBandiTable = () => {
return( return(
<div className="appPageSection__table"> <div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={loading} dataKey="id" <DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
filters={filters} filters={filters}
globalFilterFields={['name', 'status']} globalFilterFields={['name', 'status']}
header={header} header={header}
emptyMessage={__('Nessun dato disponibile', 'gepafin')} emptyMessage={__('Nessun dato disponibile', 'gepafin')}
onFilter={(e) => setFilters(e.filters)}> onFilter={(e) => setFilters(e.filters)}>
<Column field="name" header={__('Nome Bando', 'gepafin')} filter filterPlaceholder="Search by name" <Column field="name" header={__('Nome Bando', 'gepafin')} filter filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '12rem' }}/> style={{ minWidth: '8rem' }}/>
<Column header={__('Data Pubblicazione', 'gepafin')} filterField="start_date" dataType="date" <Column header={__('Data Pubblicazione', 'gepafin')} filterField="start_date" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '8rem' }}
body={dateStartBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateStartBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column header={__('Data Scadenza', 'gepafin')} filterField="end_date" dataType="date" <Column header={__('Data Scadenza', 'gepafin')} filterField="end_date" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '8rem' }}
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="status" header={__('Stato', 'gepafin')} <Column field="status" header={__('Stato', 'gepafin')}
style={{ width: '120px' }} body={statusBodyTemplate} /> style={{ minWidth: '7rem' }} body={statusBodyTemplate} />
<Column header={__('Azioni', 'gepafin')} <Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/> body={actionsBodyTemplate}/>
</DataTable> </DataTable>

View File

@@ -110,10 +110,10 @@ const LatestUsersActivityTable = () => {
emptyMessage={__('Nessun dato disponibile', 'gepafin')} emptyMessage={__('Nessun dato disponibile', 'gepafin')}
onFilter={(e) => setFilters(e.filters)}> onFilter={(e) => setFilters(e.filters)}>
<Column header={__('Timestamp', 'gepafin')} filterField="date" dataType="date" <Column header={__('Timestamp', 'gepafin')} filterField="date" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '8rem' }}
body={dateBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="email" header={__('Utente', 'gepafin')} filter filterPlaceholder="Search by email" <Column field="email" header={__('Utente', 'gepafin')} filter filterPlaceholder="Search by email"
style={{ minWidth: '12rem' }}/> style={{ minWidth: '8rem' }}/>
<Column field="action" header={__('Azione', 'gepafin')}/> <Column field="action" header={__('Azione', 'gepafin')}/>
<Column field="dettails" header={__('Dettagli', 'gepafin')}/> <Column field="dettails" header={__('Dettagli', 'gepafin')}/>
</DataTable> </DataTable>

View File

@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { pathOr } from 'ramda'; import { pathOr } from 'ramda';
import NumberFlow from '@number-flow/react';
// store // store
//import { storeSet } from '../../store'; //import { storeSet } from '../../store';
@@ -13,6 +14,8 @@ import DashboardService from '../../service/dashboard-service';
import LatestBandiTable from './components/LatestBandiTable'; import LatestBandiTable from './components/LatestBandiTable';
//import LatestUsersActivityTable from './components/LatestUsersActivityTable'; //import LatestUsersActivityTable from './components/LatestUsersActivityTable';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
//import MyEvaluationsTable from '../DashboardPreInstructor/components/PreInstructorDomandeTable';
import AllDomandeTable from '../Domande/components/AllDomandeTable';
const Dashboard = () => { const Dashboard = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -34,10 +37,6 @@ const Dashboard = () => {
console.log('onGoToSettings') console.log('onGoToSettings')
}*/ }*/
function formatToMillions(num) {
return Math.abs(num) > 999 ? Math.sign(num)*((Math.abs(num)/1000000).toFixed(1)) + 'M' : Math.sign(num)*Math.abs(num)
}
const getStatValue = (key, fallback = '') => { const getStatValue = (key, fallback = '') => {
return pathOr(fallback, [key], mainStats); return pathOr(fallback, [key], mainStats);
} }
@@ -67,27 +66,52 @@ const Dashboard = () => {
<div className="statsBigBadges__grid"> <div className="statsBigBadges__grid">
<div className="statsBigBadges__gridItem"> <div className="statsBigBadges__gridItem">
<span>{__('Bandi attivi', 'gepafin')}</span> <span>{__('Bandi attivi', 'gepafin')}</span>
<span>{getStatValue('numberOfActiveCalls', 0)}</span> <span><NumberFlow
value={getStatValue('numberOfActiveCalls', 0)}
format={{ notation: 'compact' }}
locales="it-IT" /></span>
</div> </div>
<div className="statsBigBadges__gridItem"> <div className="statsBigBadges__gridItem">
<span>{__('Utenti registrati', 'gepafin')}</span> <span>{__('Utenti registrati', 'gepafin')}</span>
<span>{getStatValue('numberOfResgisteredUsers', 0)}</span> <span><NumberFlow
value={getStatValue('numberOfResgisteredUsers', 0)}
format={{ notation: 'compact' }}
locales="it-IT" /></span>
</div> </div>
<div className="statsBigBadges__gridItem"> <div className="statsBigBadges__gridItem">
<span>{__('Domande in pre-istruttoria', 'gepafin')}</span> <span>{__('Domande in pre-istruttoria', 'gepafin')}</span>
<span>{getStatValue('numberOfSubmittedApplications', 0)}</span> <span><NumberFlow
value={getStatValue('numberOfSubmittedApplications', 0)}
format={{ notation: 'compact' }}
locales="it-IT" /></span>
</div> </div>
<div className="statsBigBadges__gridItem"> <div className="statsBigBadges__gridItem">
<span>{__('Domande in bozza', 'gepafin')}</span> <span>{__('Domande in bozza', 'gepafin')}</span>
<span>{getStatValue('numberOfDraftApplications', 0)}</span> <span><NumberFlow
value={getStatValue('numberOfDraftApplications', 0)}
format={{ notation: 'compact' }}
locales="it-IT" /></span>
</div> </div>
<div className="statsBigBadges__gridItem"> <div className="statsBigBadges__gridItem">
<span>{__('Aziende', 'gepafin')}</span> <span>{__('Aziende', 'gepafin')}</span>
<span>{getStatValue('numberOfCompany', 0)}</span> <span><NumberFlow
value={getStatValue('numberOfCompany', 0)}
format={{ notation: 'compact' }}
locales="it-IT" /></span>
</div> </div>
<div className="statsBigBadges__gridItem"> <div className="statsBigBadges__gridItem">
<span>{__('Totale finanziamenti attivi', 'gepafin')}</span> <span>{__('Totale finanziamenti attivi', 'gepafin')}</span>
<span>{formatToMillions(getStatValue('totalActiveFinancing', 0))}</span> <span><NumberFlow
value={getStatValue('totalActiveFinancing', 0)}
format={{
notation: 'compact',
compactDisplay: 'short',
roundingMode: 'trunc',
style: 'currency',
currency: 'EUR',
currencyDisplay: 'symbol'
}}
locales="en-US" /></span>
</div> </div>
</div> </div>
</div> </div>
@@ -95,10 +119,17 @@ const Dashboard = () => {
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>
<div className="appPageSection"> <div className="appPageSection">
<h2>{__('Ultimi Bandi Pubblicati', 'gepafin')}</h2> <h2>{__('Ultimi bandi pubblicati', 'gepafin')}</h2>
<LatestBandiTable/> <LatestBandiTable/>
</div> </div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<h2>{__('Ultime domande pubblicate', 'gepafin')}</h2>
<AllDomandeTable/>
</div>
{/*<div className="appPage__spacer"></div> {/*<div className="appPage__spacer"></div>
<div className="appPageSection"> <div className="appPageSection">

View File

@@ -1,10 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { uniq } from 'ramda'; import { uniq } from 'ramda';
import { Link } from 'react-router-dom';
// tools
import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
// store // store
import { storeSet } from '../../../../store'; import { storeSet } from '../../../../store';
@@ -19,13 +16,9 @@ import { Column } from 'primereact/column';
import { InputText } from 'primereact/inputtext'; import { InputText } from 'primereact/inputtext';
import { IconField } from 'primereact/iconfield'; import { IconField } from 'primereact/iconfield';
import { InputIcon } from 'primereact/inputicon'; import { InputIcon } from 'primereact/inputicon';
import { Dropdown } from 'primereact/dropdown';
import { InputNumber } from 'primereact/inputnumber';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { Calendar } from 'primereact/calendar'; import { Calendar } from 'primereact/calendar';
import { Tag } from 'primereact/tag';
import ProperBandoLabel from '../../../../components/ProperBandoLabel'; import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import { Link } from 'react-router-dom';
const LatestBandiTable = () => { const LatestBandiTable = () => {
@@ -43,7 +36,7 @@ const LatestBandiTable = () => {
const getCallback = (data) => { const getCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
const newItems = data.data.filter(o => o.status === 'PUBLISH'); const newItems = data.data.filter(o => o.status === 'PUBLISH');
setItems(getFormattedBandiData(newItems)); setItems(getFormattedData(newItems));
setStatuses(uniq(data.data.map(o => o.status))) setStatuses(uniq(data.data.map(o => o.status)))
initFilters(); initFilters();
} }
@@ -55,7 +48,7 @@ const LatestBandiTable = () => {
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const getFormattedBandiData = (data) => { const getFormattedData = (data) => {
return [...(data || [])].map((d) => { return [...(data || [])].map((d) => {
d.start_date = new Date(d.dates[0]); d.start_date = new Date(d.dates[0]);
d.end_date = new Date(d.dates[1]); d.end_date = new Date(d.dates[1]);
@@ -89,18 +82,9 @@ const LatestBandiTable = () => {
const initFilters = () => { const initFilters = () => {
setFilters({ setFilters({
global: { value: null, matchMode: FilterMatchMode.CONTAINS }, global: { value: null, matchMode: FilterMatchMode.CONTAINS },
name: { name: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] },
operator: FilterOperator.AND, start_date: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] },
constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] end_date: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] },
},
start_date: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
},
end_date: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
},
status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] } status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] }
}); });
setGlobalFilterValue(''); setGlobalFilterValue('');
@@ -109,12 +93,10 @@ const LatestBandiTable = () => {
const renderHeader = () => { const renderHeader = () => {
return ( return (
<div className="appTableHeader"> <div className="appTableHeader">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined <Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined onClick={clearFilter} />
onClick={clearFilter}/>
<IconField iconPosition="left"> <IconField iconPosition="left">
<InputIcon className="pi pi-search"/> <InputIcon className="pi pi-search" />
<InputText value={globalFilterValue} onChange={onGlobalFilterChange} <InputText value={globalFilterValue} onChange={onGlobalFilterChange} placeholder={__('Cerca', 'gepafin')} />
placeholder={__('Cerca', 'gepafin')}/>
</IconField> </IconField>
</div> </div>
); );
@@ -129,29 +111,13 @@ const LatestBandiTable = () => {
}; };
const dateFilterTemplate = (options) => { const dateFilterTemplate = (options) => {
return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />;
dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999"/>;
};
const balanceFilterTemplate = (options) => {
return <InputNumber value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)}/>;
}; };
const statusBodyTemplate = (rowData) => { const statusBodyTemplate = (rowData) => {
return <ProperBandoLabel status={rowData.status}/>; return <ProperBandoLabel status={rowData.status}/>;
}; };
const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses}
onChange={(e) => options.filterCallback(e.value, options.index)}
itemTemplate={statusItemTemplate} placeholder="Select One" className="p-column-filter"
showClear/>;
};
const statusItemTemplate = (option) => {
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)}/>;
};
const actionsBodyTemplate = (rowData) => { const actionsBodyTemplate = (rowData) => {
return rowData.confidi return rowData.confidi
? <Link to={`/bandi/${rowData.id}`}> ? <Link to={`/bandi/${rowData.id}`}>
@@ -173,16 +139,16 @@ const LatestBandiTable = () => {
header={header} header={header}
emptyMessage={__('Nessun dato disponibile', 'gepafin')} emptyMessage={__('Nessun dato disponibile', 'gepafin')}
onFilter={(e) => setFilters(e.filters)}> onFilter={(e) => setFilters(e.filters)}>
<Column field="name" header={__('Nome Bando', 'gepafin')} filter filterPlaceholder="Search by name" <Column field="name" header={__('Nome Bando', 'gepafin')} filter filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '12rem' }}/> style={{ minWidth: '8rem' }}/>
<Column header={__('Data Pubblicazione', 'gepafin')} filterField="start_date" dataType="date" <Column header={__('Data Pubblicazione', 'gepafin')} filterField="start_date" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '8rem' }}
body={dateStartBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateStartBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column header={__('Data Scadenza', 'gepafin')} filterField="end_date" dataType="date" <Column header={__('Data Scadenza', 'gepafin')} filterField="end_date" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '8rem' }}
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="status" header={__('Stato', 'gepafin')} <Column field="status" header={__('Stato', 'gepafin')}
style={{ width: '120px' }} body={statusBodyTemplate}/> style={{ minWidth: '7rem' }} body={statusBodyTemplate} />
<Column header={__('Azioni', 'gepafin')} <Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/> body={actionsBodyTemplate}/>
</DataTable> </DataTable>

View File

@@ -1,11 +1,17 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { uniq } from 'ramda'; import { uniq, is } from 'ramda';
// tools // tools
import getBandoLabel from '../../../../helpers/getBandoLabel'; import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity'; import getBandoSeverity from '../../../../helpers/getBandoSeverity';
// store
import { useStore } from '../../../../store';
// api
import ApplicationService from '../../../../service/application-service';
// components // components
import { FilterMatchMode, FilterOperator } from 'primereact/api'; import { FilterMatchMode, FilterOperator } from 'primereact/api';
import { DataTable } from 'primereact/datatable'; import { DataTable } from 'primereact/datatable';
@@ -20,38 +26,36 @@ import { Calendar } from 'primereact/calendar';
import { Tag } from 'primereact/tag'; import { Tag } from 'primereact/tag';
import ProperBandoLabel from '../../../../components/ProperBandoLabel'; import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import ApplicationService from '../../../../service/application-service';
import { storeSet, useStore } from '../../../../store';
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
const MyLatestSubmissionsTable = () => { const MyLatestSubmissionsTable = () => {
const chosenCompanyId = useStore().main.chosenCompanyId(); const chosenCompanyId = useStore().main.chosenCompanyId();
const isAsyncRequest = useStore().main.isAsyncRequest(); const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
const [items, setItems] = useState(null); const [items, setItems] = useState(null);
const [filters, setFilters] = useState(null); const [filters, setFilters] = useState(null);
const [globalFilterValue, setGlobalFilterValue] = useState(''); const [globalFilterValue, setGlobalFilterValue] = useState('');
const [statuses, setStatuses] = useState([]); const [statuses, setStatuses] = useState([]);
useEffect(() => { useEffect(() => {
storeSet.main.setAsyncRequest(); setLocalAsyncRequest(true);
ApplicationService.getApplications(getApplCallback, errGetApplCallback, [['companyId', chosenCompanyId]]) ApplicationService.getApplications(getApplCallback, errGetApplCallback, [
['companyId', chosenCompanyId],
['statuses', ['DRAFT', 'SUBMIT', 'AWAITING', 'READY', 'DISCARD']]
])
}, [chosenCompanyId]); }, [chosenCompanyId]);
const getApplCallback = (data) => { const getApplCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
if (data.data.length) { if (is(Array, data.data)) {
setItems(getFormattedBandiData(data.data)); setItems(getFormattedBandiData(data.data));
setStatuses(uniq(items.map(o => o.status))) setStatuses(uniq(items.map(o => o.status)))
initFilters(); initFilters();
} }
} }
storeSet.main.unsetAsyncRequest(); setLocalAsyncRequest(false);
} }
const errGetApplCallback = (data) => { const errGetApplCallback = (data) => {
set404FromErrorResponse(data); setLocalAsyncRequest(false);
storeSet.main.unsetAsyncRequest();
} }
const getFormattedBandiData = (data) => { const getFormattedBandiData = (data) => {
@@ -165,26 +169,27 @@ const MyLatestSubmissionsTable = () => {
return ( return (
<div className="appPageSection__table"> <div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={isAsyncRequest} dataKey="id" <DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
filters={filters} filters={filters}
globalFilterFields={['name', 'status']} globalFilterFields={['name', 'status']}
header={header} header={header}
emptyMessage={__('Nessun dato disponibile', 'gepafin')} emptyMessage={__('Nessun dato disponibile', 'gepafin')}
onFilter={(e) => setFilters(e.filters)}> onFilter={(e) => setFilters(e.filters)}>
<Column field="callTitle" header={__('Bando', 'gepafin')} filter filterPlaceholder="Search by name" <Column field="callTitle" header={__('Bando', 'gepafin')} filter filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '12rem' }}/> style={{ minWidth: '8rem' }}/>
<Column header={__('Scadenza', 'gepafin')} filterField="callEndDate" dataType="date" <Column header={__('Scadenza', 'gepafin')} filterField="callEndDate" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '8rem' }}
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column header={__('Ultima modifica', 'gepafin')} filterField="modifiedDate" dataType="date" <Column header={__('Ultima modifica', 'gepafin')} filterField="modifiedDate" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '8rem' }}
body={dateModifyBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateModifyBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="status" header={__('Stato', 'gepafin')} filterMenuStyle={{ width: '14rem' }} <Column field="status" header={__('Stato', 'gepafin')} filterMenuStyle={{ width: '14rem' }}
style={{ width: '120px' }} body={statusBodyTemplate} filter style={{ minWidth: '7rem' }} body={statusBodyTemplate} filter
filterElement={statusFilterTemplate}/> filterElement={statusFilterTemplate}/>
<Column header={__('Progressi', 'gepafin')} <Column header={__('Progressi', 'gepafin')}
style={{ minWidth: '10rem' }} field="progress" body={progressBodyTemplate}/> style={{ minWidth: '10rem' }} field="progress" body={progressBodyTemplate}/>
<Column header={__('Azioni', 'gepafin')} <Column header={__('Azioni', 'gepafin')}
style={{ minWidth: '10rem' }}
body={actionsBodyTemplate}/> body={actionsBodyTemplate}/>
</DataTable> </DataTable>
</div> </div>

View File

@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { head, pathOr } from 'ramda'; import { head, pathOr } from 'ramda';
import NumberFlow from '@number-flow/react';
// store // store
import { useStore } from '../../store'; import { useStore } from '../../store';
@@ -57,15 +58,24 @@ const DashboardBeneficiario = () => {
<div className="statsBigBadges__grid"> <div className="statsBigBadges__grid">
<div className="statsBigBadges__gridItem"> <div className="statsBigBadges__gridItem">
<span>{__('Domande attive', 'gepafin')}</span> <span>{__('Domande attive', 'gepafin')}</span>
<span>{getStatValue('numberOfApplications', 0)}</span> <span><NumberFlow
value={getStatValue('numberOfApplications', 0)}
format={{ notation: 'compact' }}
locales="it-IT" /></span>
</div> </div>
<div className="statsBigBadges__gridItem"> <div className="statsBigBadges__gridItem">
<span>{__('Bandi osservati', 'gepafin')}</span> <span>{__('Bandi osservati', 'gepafin')}</span>
<span>{getStatValue('numberOfCalls', 0)}</span> <span><NumberFlow
value={getStatValue('numberOfCalls', 0)}
format={{ notation: 'compact' }}
locales="it-IT" /></span>
</div> </div>
<div className="statsBigBadges__gridItem"> <div className="statsBigBadges__gridItem">
<span>{__('Documenti da integrare', 'gepafin')}</span> <span>{__('Documenti da integrare', 'gepafin')}</span>
<span>{getStatValue('numberOfIntegratedDocuments', 0)}</span> <span><NumberFlow
value={getStatValue('numberOfIntegratedDocuments', 0)}
format={{ notation: 'compact' }}
locales="it-IT" /></span>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,166 @@
import React, { useState, useEffect} from 'react';
import { __ } from '@wordpress/i18n';
import { is, uniq } from 'ramda';
import { Link } from 'react-router-dom';
// store
import { useStore } from '../../../../store';
// api
import AssignedApplicationService from '../../../../service/assigned-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 PreInstructorDomandeTable = () => {
const userData = useStore().main.userData();
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);
AssignedApplicationService.getAssignedApplications(getCallback, errGetCallbacks, [
['userId', userData.id]
]);
}, []);
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.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 },
callName: {
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 (
<div className="appTableHeader">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined onClick={clearFilter} />
<IconField iconPosition="left">
<InputIcon className="pi pi-search" />
<InputText value={globalFilterValue} onChange={onGlobalFilterChange} placeholder={__('Cerca', 'gepafin')} />
</IconField>
</div>
);
};
const dateAppliedBodyTemplate = (rowData) => {
return formatDate(rowData.submissionDate);
};
const dateEndBodyTemplate = (rowData) => {
return formatDate(rowData.callEndDate);
};
const dateFilterTemplate = (options) => {
return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />;
};
const statusBodyTemplate = (rowData) => {
return <ProperBandoLabel status={rowData.status}/>;
};
const actionsBodyTemplate = (rowData) => {
return <Link to={`/domande/${rowData.applicationId}`}>
<Button severity="info" label={__('Valuta', 'gepafin')} icon="pi pi-eye" size="small" iconPos="right"/>
</Link>
}
const header = renderHeader();
return(
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
filters={filters}
globalFilterFields={['name', 'status']}
header={header}
emptyMessage={__('Nessun dato disponibile', 'gepafin')}
onFilter={(e) => setFilters(e.filters)}>
<Column field="applicationId" header={__('ID domanda', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '6rem' }}/>
<Column field="callName" header={__('Bando', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column header={__('Data ricezione', 'gepafin')} filterField="submissionDate" dataType="date"
style={{ minWidth: '8rem' }}
body={dateAppliedBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column header={__('Scadenza', 'gepafin')} filterField="callEndDate" dataType="date"
style={{ minWidth: '8rem' }}
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="status" header={__('Stato', 'gepafin')}
style={{ minWidth: '7rem' }} body={statusBodyTemplate} />
<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/>
</DataTable>
</div>
)
}
export default PreInstructorDomandeTable;

View File

@@ -0,0 +1,75 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
import { useNavigate } from 'react-router-dom';
// store
//import { useStore } from '../../store';
// api
//import DashboardService from '../../service/dashboard-service';
// components
//import LatestBandiTable from './components/LatestBandiTable';
//import MyLatestSubmissionsTable from './components/MyLatestSubmissionsTable';
import { Button } from 'primereact/button';
import PreInstructorDomandeTable from './components/PreInstructorDomandeTable';
const DashboardPreInstructor = () => {
const navigate = useNavigate();
//const [mainStats, setMainStats] = useState({});
const goToAllEvaluations = () => {
navigate('/domande');
}
return(
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Dashboard', 'gepafin')}</h1>
</div>
{/*<div className="appPage__spacer"></div>
<div className="appPageSection statsBigBadges">
<h2>{__('Panoramica di Sistema', 'gepafin')}</h2>
<div className="statsBigBadges__grid">
<div className="statsBigBadges__gridItem">
<span>{__('Domande attive', 'gepafin')}</span>
<span>{getStatValue('numberOfApplications', 0)}</span>
</div>
<div className="statsBigBadges__gridItem">
<span>{__('Bandi osservati', 'gepafin')}</span>
<span>{getStatValue('numberOfCalls', 0)}</span>
</div>
<div className="statsBigBadges__gridItem">
<span>{__('Documenti da integrare', 'gepafin')}</span>
<span>{getStatValue('numberOfIntegratedDocuments', 0)}</span>
</div>
</div>
</div>*/}
<div className="appPage__spacer"></div>
<div className="appPageSection">
<h2>{__('Coda di lavoro', 'gepafin')}</h2>
<PreInstructorDomandeTable/>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span>
</div>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button
onClick={goToAllEvaluations}
label={__('Tutte le domande', 'gepafin')} icon="pi pi-arrow-right" iconPos="right"/>
</div>
</div>
</div>
)
}
export default DashboardPreInstructor;

View File

@@ -0,0 +1,353 @@
import React, { useState, useEffect, useRef } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { is, isEmpty, isNil } from 'ramda';
import { wrap } from 'object-path-immutable';
// 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';
const DomandaBeneficiario = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const { id } = 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 [isVisibleEmailDialog, setIsVisibleEmailDialog] = useState(false);
const toast = useRef(null);
const goToArchivePage = () => {
navigate(`/domande`);
}
const getCallback = (data) => {
if (data.status === 'SUCCESS') {
if (data.data.length) {
setData(getFormattedData(data.data[0]));
CommunicationService.getCommsByAmendmentId(data.data[0].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 <span>{__('Aggiungi comunicazione', 'gepafin')}</span>
}
const hideNewComDialog = () => {
setIsVisibleNewCommDialog(false);
setNewCommData({
title: '',
comment: ''
});
}
const footerNewComDialog = () => {
return <div>
<Button type="button" label={__('Anulla', 'gepafin')} onClick={hideNewComDialog} outlined/>
<Button
type="button"
disabled={isLoadingCommunication || isEmpty(newCommData.title) || isEmpty(newCommData.comment)}
label={__('Invia', 'gepafin')} onClick={createCommunication}/>
</div>
}
const openNewCommDialog = () => {
setIsVisibleNewCommDialog(true);
setNewCommData({
title: '',
comment: ''
});
}
const updateNewCommData = (value, path) => {
const newData = wrap(newCommData).set(path.split('.'), value).value();
setNewCommData(newData);
}
const createCommunication = () => {
setIsLoadingCommunication(true);
CommunicationService.createCommunication(data.id, 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);
}
useEffect(() => {
const parsedId = parseInt(id);
const entityId = !isNaN(parsedId) ? parsedId : 0;
AmendmentsService.getSoccorsoByApplId(entityId, getCallback, errGetCallback, [
['statuses', 'AWAITING']
]);
}, [id]);
return (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{sprintf(__('Soccorso Istruttorio: richiesta integrazione documenti per domanda #%s', 'gepafin'), id)}</h1>
</div>
<div className="appPage__spacer"></div>
<Toast ref={toast}/>
<BlockingOverlay shouldDisplay={isAsyncRequest}/>
<div className="appPageSection__row">
<Button
type="button"
outlined
onClick={goToArchivePage}
label={__('Indietro', 'gepafin')}
icon="pi pi-arrow-left" iconPos="left"/>
</div>
<div className="appPage__spacer"></div>
<div className="appPage__content">
<div className="appPageSection__withBorder columns">
<p className="appPageSection__pMeta">
<span>{__('ID domanda', 'gepafin')}</span>
<span>{data.applicationId}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Bando', 'gepafin')}</span>
<span>{data.callName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Beneficiario', 'gepafin')}</span>
<span>{data.beneficiaryName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Inizio', 'gepafin')}</span>
<span>{getDateFromISOstring(data.startDate)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Scadenza', 'gepafin')}</span>
<span>{getDateFromISOstring(data.expirationDate)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Stato', 'gepafin')}</span>
<span>{getBandoLabel(data.status)}</span>
</p>
</div>
<div className="appPageSection">
<h2>{__('Dettagli Richiesta', 'gepafin')}</h2>
<h3>{__('Documenti Richiesti', 'gepafin')}</h3>
<ol className="appPageSection__list">
{data.formFields
? data.formFields.map((o, i) => <li key={o.fieldId}
style={{ flexDirection: 'row' }}>
<span>{o.label}</span>
</li>) : null}
</ol>
</div>
<div className="appPageSection">
<h3>{__('Note e spiegazioni', 'gepafin')}</h3>
<div className="appPageSection__withBorder grey ql-editor"
style={{ minHeight: '200px' }}>
{renderHtmlContent(data.note)}
</div>
</div>
<div className="appPageSection">
<h2>{__('Comunicazioni', 'gepafin')}</h2>
<table className="myTable">
<thead className="myThead">
<tr>
<th style={{ width: 250 }}>{__('Data', 'gepafin')}</th>
<th>{__('Comunicazione', 'gepafin')}</th>
</tr>
</thead>
<tbody className="myTbody">
{!isNil(comms) && !isEmpty(comms)
? comms.map((o, i) => <tr key={o.id}>
<td valign="top">
{getDateFromISOstring(o.commentedDate)}
</td>
<td>
<h3>{o.title}</h3>
<p>{o.comment}</p>
</td>
</tr>)
: <tr>
<td>-</td>
<td>-</td>
</tr>}
</tbody>
</table>
<Button
style={{ marginTop: 30 }}
onClick={openNewCommDialog}
disabled={data.status === 'CLOSE'}
type="button"
label={__('Aggiungi Comunicazione', 'gepafin')}
icon="pi pi-plus" iconPos="right"/>
</div>
<div className="appPageSection__message warning">
<i className="pi pi-exclamation-triangle"></i>
<span className="summary">{__('Attenzione', 'gepafin')}</span>
<span>{__('Inviare la documentazione richiesta completa delle integrazioni esclusivamente via PEC. In caso contarrio lintegrazione non può essere ritenuta valida.', 'gepafin')}</span>
</div>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button
type="button"
disabled={isAsyncRequest}
onClick={() => setIsVisibleEmailDialog(true)}
label={__('Invia documenti via PEC', 'gepafin')}
icon="pi pi-envelope" iconPos="right"/>
<Button
type="button"
outlined
onClick={goToArchivePage}
disabled={isAsyncRequest}
label={__('Chiudi', 'gepafin')}
icon="pi pi-times" iconPos="right"/>
</div>
</div>
</div>
<Dialog
visible={isVisibleNewCommDialog}
modal
header={headerNewComDialog}
footer={footerNewComDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideNewComDialog}>
<div className="appForm__field">
<label
className={classNames({ 'p-error': isEmpty(newCommData.title) })}>
{__('Titolo', 'gepafin')}*
</label>
<InputText value={newCommData.title}
disabled={data.status === 'CLOSE'}
invalid={isEmpty(newCommData.title)}
onChange={(e) => updateNewCommData(e.target.value, 'title')}/>
<label
className={classNames({ 'p-error': isEmpty(newCommData.comment) })}>
{__('Contenuto', 'gepafin')}*
</label>
<InputTextarea
value={newCommData.comment}
disabled={data.status === 'CLOSE'}
rows={5} cols={30}
invalid={isEmpty(newCommData.comment)}
onChange={(e) => updateNewCommData(e.target.value, 'comment')}/>
</div>
</Dialog>
<Dialog
header={__('Invia documenti via PEC', 'gepafin')}
visible={isVisibleEmailDialog}
style={{ width: '50vw' }}
onHide={() => {if (!isVisibleEmailDialog) return; setIsVisibleEmailDialog(false); }}>
<p className="m-0">
{data.callEmail}
</p>
</Dialog>
</div>
)
}
export default DomandaBeneficiario;

View File

@@ -0,0 +1,564 @@
import React, { useState, useEffect, useRef } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { is, isEmpty, isNil, sum, pathOr, head } from 'ramda';
import { klona } from 'klona';
import { wrap } from 'object-path-immutable';
// store
import { storeSet, useStore } from '../../store';
// api
import ApplicationEvaluationService from '../../service/application-evaluation-service';
import AmendmentsService from '../../service/amendments-service';
// tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import getBandoLabel from '../../helpers/getBandoLabel';
import getDateFromISOstring from '../../helpers/getDateFromISOstring';
// components
import { Skeleton } from 'primereact/skeleton';
import { Button } from 'primereact/button';
import { Tag } from 'primereact/tag';
import { Checkbox } from 'primereact/checkbox';
import { Editor } from 'primereact/editor';
import { InputNumber } from 'primereact/inputnumber';
import BlockingOverlay from '../../components/BlockingOverlay';
import { Toast } from 'primereact/toast';
import HelpIcon from '../../icons/HelpIcon';
import { Dialog } from 'primereact/dialog';
const DomandaEditPreInstructor = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const { id } = useParams();
const navigate = useNavigate();
const [data, setData] = useState({});
const [isVisibleCriterionData, setIsVisibleCriterionData] = useState(0);
const [criterionDataTitle, setCriterionDataTitle] = useState('');
const [criterionDataContent, setCriterionDataContent] = useState('');
const [isAdmissible, setIsAdmissible] = useState(false);
const [connectedSoccorsoId, setConnectedSoccorsoId] = useState(0);
const toast = useRef(null);
const goToEvaluationsPage = () => {
navigate('/domande');
}
const doNewSoccorso = () => {
if (connectedSoccorsoId !== 0) {
navigate(`/domande/${id}/soccorso/${connectedSoccorsoId}`);
} else {
navigate(`/domande/${id}/aggiungi-soccorso/`);
}
}
const getCallback = (data) => {
if (data.status === 'SUCCESS') {
setData(getFormattedData(data.data));
}
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 getFormattedData = (data) => {
data.submissionDate = is(String, data.submissionDate) ? new Date(data.submissionDate) : (data.submissionDate ? data.submissionDate : '');
data.evaluationDate = is(String, data.evaluationDate) ? new Date(data.evaluationDate) : (data.evaluationDate ? data.evaluationDate : '');
return data;
};
const renderHeader = () => {
return (
<span className="ql-formats">
<button className="ql-bold" aria-label="Bold"></button>
<button className="ql-italic" aria-label="Italic"></button>
<button className="ql-underline" aria-label="Underline"></button>
<button className="ql-link" aria-label="Link"></button>
<button className="ql-list" value="ordered"></button>
<button className="ql-header" value="2"></button>
<button className="ql-header" value="3"></button>
<button className="ql-blockquote"></button>
<button className="ql-list" value="bullet"></button>
<button className="ql-indent" value="-1"></button>
<button className="ql-indent" value="+1"></button>
</span>
);
};
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 = <div className="criterionRelatedData">
<h3>{__('I campi correlati')}</h3>
{criterion.criteriaMappedFields ? criterion.criteriaMappedFields.map(o => criteriaDataItem(o)) : null}
</div>;
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 = <ul>
{item.fieldValue.map(o => <li key={o.id}>
{o.filePath ? <a href={o.filePath}>{o.name}</a> : null}
</li>)}
</ul>;
} else if (item.fieldValue && item.fieldValue.length && isNil(item.fieldValue[0].filePath)) {
const th = Object.keys(item.fieldValue[0]);
content = <table>
<thead>
<tr>
{th.map(v => <th key={v}>{v}</th>)}
</tr>
</thead>
<tbody>
{item.fieldValue.map((o, i) => <tr key={i}>
{Object.values(o).map(v => <td key={v}>{v}</td>)}
</tr>)}
</tbody>
</table>;
}
return <div key={item.id} className="criterionRelatedData__item">
<strong>{item.fieldLabel}</strong>
{content}
</div>
}
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 (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Valuta domanda', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<Toast ref={toast}/>
<BlockingOverlay shouldDisplay={isAsyncRequest}/>
<div className="appPageSection__row">
<Button
type="button"
outlined
onClick={goToEvaluationsPage}
label={__('Indietro', 'gepafin')}
icon="pi pi-arrow-left" iconPos="left"/>
</div>
<div className="appPage__spacer"></div>
{!isAsyncRequest && !isEmpty(data)
? <div className="appPage__content">
<div className="appPageSection__withBorder columns">
<p className="appPageSection__pMeta">
<span>{__('ID domanda', 'gepafin')}</span>
<span>{data.applicationId}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Bando', 'gepafin')}</span>
<span>{data.callName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Beneficiario', 'gepafin')}</span>
<span>{data.beneficiary}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Data ricezione', 'gepafin')}</span>
<span>{getDateFromISOstring(data.submissionDate)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Scadenza Valutazione', 'gepafin')}</span>
<span>{getDateFromISOstring(data.callEndDate)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Stato', 'gepafin')}</span>
<span>{getBandoLabel(data.applicationStatus)}</span>
</p>
</div>
<div className="appPageSection">
<h2>{__('Punteggi di valutazione', 'gepafin')}</h2>
{data.criteria
? <table className="myTable">
<thead className="myThead">
<tr>
<th>{__('Parametro', 'gepafin')}</th>
<th style={{ width: 200 }}>{__('Punteggio', 'gepafin')}</th>
<th style={{ width: 220 }}>{__('Stato', 'gepafin')}</th>
</tr>
</thead>
<tbody className="myTbody">
{data.criteria.map((o, i) => <tr key={o.id}>
<td>{o.label}</td>
<td>
<div className="p-inputgroup">
<InputNumber
placeholder={__('Punteggio', 'gepafin')}
keyfilter="int"
value={o.score}
max={o.maxScore}
onChange={(e) => updateEvaluationValue(
e.value,
`criteria.${i}.score`,
o.maxScore
)}/>
<span className="p-inputgroup-addon">
/ {o.maxScore}
</span>
</div>
</td>
<td>
<div className="appPageSection__iconActions">
{!isEmpty(o.criteriaMappedFields)
? <Button icon="pi pi-eye"
rounded outlined severity="info"
onClick={() => displayCriterionData(o.id)}
aria-label={__('Mostra', 'gepafin')}/> : null}
<Button icon="pi pi-thumbs-up" rounded outlined
severity={!isNil(o.valid) && o.valid ? 'success' : 'secondary'}
onClick={() => updateEvaluationValue(
true,
`criteria.${i}.valid`
)}
aria-label={__('Su', 'gepafin')}/>
<Button icon="pi pi-thumbs-down" rounded outlined
severity={!isNil(o.valid) && !o.valid ? 'danger' : 'secondary'}
onClick={() => updateEvaluationValue(
false,
`criteria.${i}.valid`
)}
aria-label={__('Giu', 'gepafin')}/>
</div>
</td>
</tr>)}
<tr>
<td>{__('Punteggio:', 'gepafin')}</td>
<td>{sum(data.criteria.map(o => o.score))}</td>
<td>
{isAdmissible
? <Tag icon="pi pi-check" severity="success"
value={__('Ammissibile')}></Tag> : null}
{!isAdmissible
? <Tag icon="pi pi-times" severity="warning"
value={__('Inammissibile')}></Tag> : null}
</td>
</tr>
</tbody>
<tfoot className="myTfoot">
<tr>
<td colSpan="3">{sprintf(__('Punteggio minimo per l\'ammissione: %d'), data.minScore)}</td>
</tr>
</tfoot>
</table> : null}
</div>
<div className="appPageSection">
<h2>{__('Checklist Valutazione', 'gepafin')}</h2>
<div className="appPageSection columns">
<div>
<h3>{__('Lista', 'gepafin')}</h3>
<div className="appPageSection__withBorder grey" style={{ marginBottom: '20px' }}>
<div className="appPageSection__checklist">
{data.checklist.map((o, i) => <div key={o.id}>
<Checkbox
inputId={`checklist_${o.id}`}
onChange={(e) => updateEvaluationValue(
e.checked,
`checklist.${i}.valid`
)}
checked={o.valid}></Checkbox>
<label htmlFor={`checklist_${o.id}`}>{o.label}</label>
</div>)}
</div>
</div>
<h3>{__('Note', 'gepafin')}</h3>
<div>
<Editor
value={data.note}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateEvaluationValue(
e.htmlValue,
'note'
)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</div>
<div>
<h3>{__('Documenti allegati', 'gepafin')}</h3>
<ol className="appPageSection__list">
{data.files.map((o, i) => <li key={o.id}>
<div className="appPageSection" style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between'
}}>
<span>{o.label}</span>
<div className="appPageSection__iconActions">
{o.fileDetail && o.fileDetail.length === 1
? <Button icon="pi pi-eye" rounded
onClick={() => window.open(o.fileDetail[0].filePath, '_blank').focus()}
outlined severity="info"
aria-label={__('Mostra', 'gepafin')}/> : null}
<Button icon="pi pi-thumbs-up" rounded outlined
severity={!isNil(o.valid) && o.valid ? 'success' : 'secondary'}
onClick={() => updateEvaluationValue(
true,
`files.${i}.valid`
)}
aria-label={__('Su', 'gepafin')}/>
<Button icon="pi pi-thumbs-down" rounded outlined
severity={!isNil(o.valid) && !o.valid ? 'danger' : 'secondary'}
onClick={() => updateEvaluationValue(
false,
`files.${i}.valid`
)}
aria-label={__('Giu', 'gepafin')}/>
</div>
</div>
{o.fileDetail && o.fileDetail.length > 1
? <ul style={{
width: '100%',
paddingLeft: '15px',
display: 'flex',
flexDirection: 'column',
gap: '10px'
}}>
{o.fileDetail.map((k, i) => <li key={k.id} style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between'
}}>
<span>{k.name}</span>
<div className="appPageSection__iconActions">
<Button icon="pi pi-eye" rounded
onClick={() => window.open(k.filePath, '_blank').focus()}
outlined severity="info"
aria-label={__('Mostra', 'gepafin')}/>
</div>
</li>)}
</ul>
: null}
</li>)}
</ol>
</div>
</div>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span>
</div>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button
type="button"
disabled={!data.id}
onClick={doNewSoccorso}
outlined
label={<>
{__('Richiedi Soccorso Istruttorio', 'gepafin')}
<i style={{ marginLeft: 7 }}>
<HelpIcon/>
</i>
</>}
/>
{data.id
? <Button
type="button"
onClick={doSaveDraft}
outlined
label={__('Salva bozza valutazione', 'gepafin')}
icon="pi pi-save" iconPos="right"/>
: <Button
type="button"
onClick={doSaveDraft}
label={__('Crea valutazione', 'gepafin')}
icon="pi pi-save" iconPos="right"/>}
{data.id
? <Button
type="button"
disabled={!isAdmissible}
onClick={doApprove}
label={__('Approva Domanda', 'gepafin')}
icon="pi pi-check" iconPos="right"/> : null}
{data.id
? <Button
type="button"
onClick={doReject}
label={__('Respingi Domanda', 'gepafin')}
icon="pi pi-times" iconPos="right"/> : null}
</div>
</div>
<Dialog
header={criterionDataTitle}
visible={isVisibleCriterionData !== 0}
style={{ width: '50vw' }}
onHide={hideCriterionData}>
{criterionDataContent}
</Dialog>
</div>
: <>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="2rem" className="mb-8"></Skeleton>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="4rem" className="mb-8"></Skeleton>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="2rem" className="mb-8"></Skeleton>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="4rem"></Skeleton>
</>}
</div>
)
}
export default DomandaEditPreInstructor;

View File

@@ -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 (
<div className="appTableHeader">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined onClick={clearFilter} />
<IconField iconPosition="left">
<InputIcon className="pi pi-search" />
<InputText value={globalFilterValue} onChange={onGlobalFilterChange} placeholder={__('Cerca', 'gepafin')} />
</IconField>
</div>
);
};
const dateAppliedBodyTemplate = (rowData) => {
return formatDate(rowData.submissionDate);
};
const dateEndBodyTemplate = (rowData) => {
return formatDate(rowData.callEndDate);
};
const dateFilterTemplate = (options) => {
return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />;
};
const statusBodyTemplate = (rowData) => {
return <ProperBandoLabel status={rowData.status}/>;
};
const actionsBodyTemplate = (rowData) => {
return openDialogFn
? <Button severity="info"
onClick={() => openDialogFn(rowData.id)}
label={__('Assegnare', 'gepafin')}
icon="pi pi-pencil" size="small" iconPos="right" />
: <Link to={'/domande'}>
<Button severity="info" label={__('Gestire', 'gepafin')} size="small" />
</Link>
}
const header = renderHeader();
return(
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
filters={filters}
globalFilterFields={['name', 'status']}
header={header}
emptyMessage={__('Nessun dato disponibile', 'gepafin')}
onFilter={(e) => setFilters(e.filters)}>
<Column field="id" header={__('ID domanda', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '12rem' }}/>
<Column field="callTitle" header={__('Bando', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '12rem' }}/>
<Column header={__('Data Ricezione', 'gepafin')}
filterField="submissionDate" dataType="date"
style={{ minWidth: '10rem' }}
body={dateAppliedBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column header={__('Scadenza', 'gepafin')}
filterField="callEndDate" dataType="date"
style={{ minWidth: '10rem' }}
body={dateEndBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="status" header={__('Stato', 'gepafin')}
style={{ width: '120px' }} body={statusBodyTemplate} />
<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/>
</DataTable>
</div>
)
}
export default AllDomandeTable;

178
src/pages/Domande/index.js Normal file
View File

@@ -0,0 +1,178 @@
import React, { useEffect, useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
import { isEmpty } from 'ramda';
// api
import UserService from '../../service/user-service';
import AssignedApplicationService from '../../service/assigned-application-service';
// store
import { storeSet } from '../../store';
// tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import uniqid from '../../helpers/uniqid';
// components
import AllDomandeTable from './components/AllDomandeTable';
import { Dialog } from 'primereact/dialog';
import { Button } from 'primereact/button';
import { classNames } from 'primereact/utils';
import { Dropdown } from 'primereact/dropdown';
const Domande = () => {
const [loading, setLoading] = useState(false);
const [isVisibleEditDialog, setIsVisibleEditDialog] = useState(false);
const [roleId, setRoleId] = useState(0);
const [users, setUsers] = useState([]);
const [chosenUser, setChosenUser] = useState(0);
const [chosenApplication, setChosenApplication] = useState(0);
const [updaterString, setUpdaterString] = useState('');
const toast = useRef(null);
const getRolesCallback = (data) => {
if (data.status === 'SUCCESS') {
const roles = data.data
.filter(o => ['ROLE_PRE_INSTRUCTOR'].includes(o.roleType));
if (roles.length) {
setRoleId(roles[0].id);
}
}
setLoading(false);
}
const errGetRolesCallback = (data) => {
set404FromErrorResponse(data);
setLoading(false);
}
const getUsersCallback = (data) => {
if (data.status === 'SUCCESS') {
const users = data.data
.map(o => ({
name: `${o.firstName} ${o.lastName} (${o.email})`,
value: o.id
}));
setUsers(users);
}
setLoading(false);
}
const errGetUsersCallback = (data) => {
set404FromErrorResponse(data);
setLoading(false);
}
const headerEditDialog = () => {
return <span>{__('Assign application', 'gepafin')}</span>
}
const hideEditDialog = () => {
setIsVisibleEditDialog(false);
setChosenUser(0);
setChosenApplication(0);
}
const footerEditDialog = () => {
return <div>
<Button type="button" label={__('Anulla', 'gepafin')} onClick={hideEditDialog} outlined/>
<Button
type="button"
disabled={loading}
label={__('Invia', 'gepafin')} onClick={saveEditDialog}/>
</div>
}
const openAssignDialog = (applId) => {
setChosenApplication(applId)
setIsVisibleEditDialog(true);
}
const saveEditDialog = () => {
if (chosenUser !== 0 && chosenApplication !== 0) {
storeSet.main.setAsyncRequest();
AssignedApplicationService.assignApplication(chosenApplication, assignApplCallback, errAssignApplCallback, [
['userId', chosenUser]
]);
hideEditDialog();
}
}
const assignApplCallback = (data) => {
if (data.status === 'SUCCESS') {
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
setUpdaterString(uniqid());
}
storeSet.main.unsetAsyncRequest();
}
const errAssignApplCallback = (data) => {
if (toast.current && data.message) {
toast.current.show({
severity: 'error',
summary: '',
detail: data.message
});
}
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
useEffect(() => {
if (roleId !== 0) {
setLoading(true);
UserService.getUsers(getUsersCallback, errGetUsersCallback, [['roleId', roleId]])
}
}, [roleId]);
useEffect(() => {
if (isVisibleEditDialog) {
setLoading(true);
UserService.getRoles(getRolesCallback, errGetRolesCallback)
}
}, [isVisibleEditDialog]);
return (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Gestione domande', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<AllDomandeTable openDialogFn={openAssignDialog} updaterString={updaterString}/>
</div>
<Dialog
visible={isVisibleEditDialog}
modal
header={headerEditDialog}
footer={footerEditDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideEditDialog}>
<div className="appForm__field">
<label
className={classNames({ 'p-error': isEmpty(chosenUser) || chosenUser === 0 || chosenApplication === 0 })}>
{__('Pre-istruttore', 'gepafin')}*
</label>
<Dropdown
value={chosenUser}
invalid={isEmpty(chosenUser) || chosenUser === 0 || chosenApplication === 0}
onChange={(e) => setChosenUser(e.value)}
options={users}
optionLabel="name"
optionValue="value"/>
</div>
</Dialog>
</div>
)
}
export default Domande;

View File

@@ -0,0 +1,193 @@
import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n';
import { is, uniq } from 'ramda';
import { Link } from 'react-router-dom';
// store
import { useStore } from '../../../../store';
// api
import ApplicationService from '../../../../service/application-service';
// tools
import getBandoLabel from '../../../../helpers/getBandoLabel';
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
// 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 { Dropdown } from 'primereact/dropdown';
import { Tag } from 'primereact/tag';
const BeneficiarioDomandeTable = () => {
const chosenCompanyId = useStore().main.chosenCompanyId();
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(getApplCallback, errGetApplCallback, [
['companyId', chosenCompanyId],
['statuses', ['SOCCORSO', 'APPROVED', 'REJECTED', 'EVALUATION']]
])
}, [chosenCompanyId]);
const getApplCallback = (data) => {
if (data.status === 'SUCCESS') {
if (is(Array, data.data)) {
setItems(getFormattedBandiData(data.data));
setStatuses(uniq(items.map(o => o.status)))
initFilters();
}
}
setLocalAsyncRequest(false);
}
const errGetApplCallback = (data) => {
setLocalAsyncRequest(false);
}
const getFormattedBandiData = (data) => {
return [...(data || [])].map((d) => {
d.callEndDate = new Date(d.callEndDate);
d.modifiedDate = new Date(d.modifiedDate);
d.submissionDate = new Date(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 }]
},
modifiedDate: {
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 (
<div className="appTableHeader">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
onClick={clearFilter}/>
<IconField iconPosition="left">
<InputIcon className="pi pi-search"/>
<InputText value={globalFilterValue} onChange={onGlobalFilterChange}
placeholder={__('Cerca', 'gepafin')}/>
</IconField>
</div>
);
};
const dateSubmissionBodyTemplate = (rowData) => {
return formatDate(rowData.submissionDate);
};
const dateFilterTemplate = (options) => {
return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)}
dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999"/>;
};
const statusBodyTemplate = (rowData) => {
return <ProperBandoLabel status={rowData.status}/>;
};
const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses}
onChange={(e) => options.filterCallback(e.value, options.index)}
itemTemplate={statusItemTemplate} placeholder="Select One" className="p-column-filter"
showClear/>;
};
const progressBodyTemplate = (options) => {
return '-';
};
const statusItemTemplate = (option) => {
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)}/>;
};
const actionsBodyTemplate = (rowData) => {
return rowData.status === 'SOCCORSO'
? <Link to={`/domande/${rowData.id}`}>
<Button severity="info" label={__('Dettagli', 'gepafin')} icon="pi pi-eye" size="small"
iconPos="right"/>
</Link> : null;
}
const header = renderHeader();
return (
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
filters={filters}
globalFilterFields={['name', 'status']}
header={header}
emptyMessage={__('Nessun dato disponibile', 'gepafin')}
onFilter={(e) => setFilters(e.filters)}>
<Column field="id" header={__('ID domanda', 'gepafin')} filter
filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '6rem' }}/>
<Column field="callTitle" header={__('Titolo bando', 'gepafin')} filter
filterPlaceholder={__('Cerca il nome', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column header={__('Data di invio', 'gepafin')} filterField="submissionDate" dataType="date"
style={{ minWidth: '8rem' }}
body={dateSubmissionBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="status" header={__('Stato', 'gepafin')} filterMenuStyle={{ width: '14rem' }}
style={{ minWidth: '7rem' }} body={statusBodyTemplate} filter
filterElement={statusFilterTemplate}/>
<Column header={__('Esito', 'gepafin')}
style={{ minWidth: '7rem' }} field="progress" body={progressBodyTemplate}/>
<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/>
</DataTable>
</div>
)
}
export default BeneficiarioDomandeTable;

View File

@@ -0,0 +1,23 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
// components
import BeneficiarioDomandeTable from './components/BeneficiarioDomandeTable';
const DomandePreInstructor = () => {
return(
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Archivio domande', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<BeneficiarioDomandeTable/>
</div>
</div>
)
}
export default DomandePreInstructor;

View File

@@ -0,0 +1,23 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
// components
import PreInstructorDomandeTable from '../DashboardPreInstructor/components/PreInstructorDomandeTable';
const DomandePreInstructor = () => {
return(
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Archivio domande', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<PreInstructorDomandeTable/>
</div>
</div>
)
}
export default DomandePreInstructor;

View File

@@ -30,6 +30,10 @@ const LoginAdmin = () => {
formState: { errors }, formState: { errors },
} = useForm({ mode: 'onChange' }); } = useForm({ mode: 'onChange' });
const gotToResetPassword = () => {
navigate('/reset-password');
}
const onSubmit = (formData) => { const onSubmit = (formData) => {
errorMsgs.current.clear(); errorMsgs.current.clear();
setLoading(true); setLoading(true);
@@ -67,10 +71,6 @@ const LoginAdmin = () => {
setLoading(false); setLoading(false);
} }
const gotToResetPassword = () => {
navigate('/reset-password');
}
useEffect(() => { useEffect(() => {
if (!isEmpty(token)) { if (!isEmpty(token)) {
setLoading(true); setLoading(true);

View File

@@ -129,88 +129,7 @@ const Profile = () => {
</div> </div>
<div className="appPageSection"> <div className="appPageSection">
<h2>{__('Consensi', 'gepafin')}</h2> <h2>{__('Impostazioni', 'gepafin')}</h2>
<div className="appForm__switchFieldWrapper">
<FormField
type="switch"
disabled={true}
fieldName="privacy"
label={''}
control={control}
errors={errors}
onLabel={''}
offLabel={''}
/>
<div>
{__('Dichiaro di aver preso visione, prima dell\'accesso al portale https://bandi.gepafin.it, dell\' "Informativa Privacy" all\'interno dell\'Appendice 10 dell\'Avviso secondo il Regolamento UE 2016/679 relativo alla protezione delle persone fisiche con riguardo al trattamento dei dati personale, nonché alla libera circolazione di tali dati e che abroga la Direttiva 95/46 CE.', 'gepafin')}
</div>
</div>
<div className="appForm__switchFieldWrapper">
<FormField
type="switch"
disabled={true}
fieldName="terms"
label={''}
control={control}
errors={errors}
onLabel={''}
offLabel={''}
/>
<div>
{__('Termini e condizioni', 'gepafin')}
</div>
</div>
<div className="appForm__switchFieldWrapper">
<FormField
type="switch"
fieldName="marketing"
label={''}
control={control}
errors={errors}
onLabel={''}
offLabel={''}
/>
<div>
{__('Invio di materiale pubblicitario, vendita diretta, compimento di ricerche di mercato o comunicazione commerciale riguardanti promozione e vendita di prodotti e servizi della Gepafin, mediante modalità di contatto automatizzate (posta elettronica, PEC, messaggi tramite canali informatici, network ed applicazioni web) e tradizionali (come posta cartacea e chiamate telefoniche con operatore)', 'gepafin')}
</div>
</div>
<div className="appForm__switchFieldWrapper">
<FormField
type="switch"
fieldName="offers"
label={''}
control={control}
errors={errors}
onLabel={''}
offLabel={''}
/>
<div>
{__('Elaborazione, in forma elettronica, dei dati relativi ai rapporti e servizi forniti, per lanalisi di comportamenti e preferenze della clientela, da utilizzare a scopo commerciale, per la individuazione ed offerta di prodotti e servizi di suo interesse', 'gepafin')}
</div>
</div>
<div className="appForm__switchFieldWrapper">
<FormField
type="switch"
fieldName="thirdParty"
label={''}
control={control}
errors={errors}
onLabel={''}
offLabel={''}
/>
<div>
{__('Comunicazione dei miei dati ad altre società in ambito bancario, finanziario od assicurativo e enti pubblici che li tratteranno per invio di materiale pubblicitario, vendita diretta, compimento di ricerche di mercato o comunicazione commerciale riguardanti loro prodotti o servizi, mediante le modalità automatizzate e tradizionali di comunicazione sopra indicate', 'gepafin')}
</div>
</div>
</div>
<div className="appPageSection">
<h2>{__('Utenti Associati', 'gepafin')}</h2>
<div className="appForm__cols"> <div className="appForm__cols">
<FormField <FormField
type="select" type="select"

View File

@@ -0,0 +1,288 @@
import React, { useMemo, useRef } from 'react';
import { __ } from '@wordpress/i18n';
import { useForm } from 'react-hook-form';
// store
import { storeSet, useStore } from '../../store';
// components
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import FormField from '../../components/FormField';
import { Button } from 'primereact/button';
import { Toast } from 'primereact/toast';
// api
import UserService from '../../service/user-service';
// tools
import getDateFromISOstring from '../../helpers/getDateFromISOstring';
const ProfileBeneficiario = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const userData = useStore().main.userData();
const toast = useRef(null);
const {
control,
handleSubmit,
formState: { errors }
} = useForm({
defaultValues: useMemo(() => {
return userData;
}, [userData]),
mode: 'onChange'
});
const onSubmit = (formData) => {
storeSet.main.setAsyncRequest();
UserService.updateUser(userData.id, formData, updateCallback, updateError);
};
const updateCallback = (data) => {
if (data.status === 'SUCCESS') {
storeSet.main.userData(data.data);
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: __('L\'utente è stato aggiornato!', 'gepafin')
});
}
}
storeSet.main.unsetAsyncRequest();
}
const updateError = (data) => {
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
return (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Profilo utente', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<Toast ref={toast}/>
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
<div className="appPageSection">
<h2>{__('Informazioni personali', 'gepafin')}</h2>
<div className="appForm__cols">
<FormField
type="textinput"
disabled={true}
fieldName="firstName"
label={__('Nome', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="Francesco"
/>
<FormField
type="textinput"
disabled={true}
fieldName="lastName"
label={__('Cognome', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="Moli"
/>
<FormField
type="textinput"
disabled={true}
fieldName="codiceFiscale"
label={__('Codice fiscale', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="XXXXXXXX"
/>
</div>
<div className="appForm__cols">
<FormField
type="textinput"
fieldName="email"
label={__('Email', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="user@example.com"
/>
<FormField
type="textinput"
fieldName="phoneNumber"
label={__('Telefono', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="1234567"
/>
</div>
</div>
<div className="appPageSection">
<h2>{__('Consensi', 'gepafin')}</h2>
<div className="appForm__switchFieldWrapper">
<FormField
type="switch"
disabled={true}
fieldName="privacy"
label={''}
control={control}
errors={errors}
onLabel={''}
offLabel={''}
/>
<div>
{__('Dichiaro di aver preso visione, prima dell\'accesso al portale https://bandi.gepafin.it, dell\' "Informativa Privacy" all\'interno dell\'Appendice 10 dell\'Avviso secondo il Regolamento UE 2016/679 relativo alla protezione delle persone fisiche con riguardo al trattamento dei dati personale, nonché alla libera circolazione di tali dati e che abroga la Direttiva 95/46 CE.', 'gepafin')}
</div>
</div>
<div className="appForm__switchFieldWrapper">
<FormField
type="switch"
disabled={true}
fieldName="terms"
label={''}
control={control}
errors={errors}
onLabel={''}
offLabel={''}
/>
<div>
{__('Termini e condizioni', 'gepafin')}
</div>
</div>
<div className="appForm__switchFieldWrapper">
<FormField
type="switch"
fieldName="marketing"
label={''}
control={control}
errors={errors}
onLabel={''}
offLabel={''}
/>
<div>
{__('Invio di materiale pubblicitario, vendita diretta, compimento di ricerche di mercato o comunicazione commerciale riguardanti promozione e vendita di prodotti e servizi della Gepafin, mediante modalità di contatto automatizzate (posta elettronica, PEC, messaggi tramite canali informatici, network ed applicazioni web) e tradizionali (come posta cartacea e chiamate telefoniche con operatore)', 'gepafin')}
</div>
</div>
<div className="appForm__switchFieldWrapper">
<FormField
type="switch"
fieldName="offers"
label={''}
control={control}
errors={errors}
onLabel={''}
offLabel={''}
/>
<div>
{__('Elaborazione, in forma elettronica, dei dati relativi ai rapporti e servizi forniti, per lanalisi di comportamenti e preferenze della clientela, da utilizzare a scopo commerciale, per la individuazione ed offerta di prodotti e servizi di suo interesse', 'gepafin')}
</div>
</div>
<div className="appForm__switchFieldWrapper">
<FormField
type="switch"
fieldName="thirdParty"
label={''}
control={control}
errors={errors}
onLabel={''}
offLabel={''}
/>
<div>
{__('Comunicazione dei miei dati ad altre società in ambito bancario, finanziario od assicurativo e enti pubblici che li tratteranno per invio di materiale pubblicitario, vendita diretta, compimento di ricerche di mercato o comunicazione commerciale riguardanti loro prodotti o servizi, mediante le modalità automatizzate e tradizionali di comunicazione sopra indicate', 'gepafin')}
</div>
</div>
</div>
<div className="appPageSection">
<h2>{__('Impostazioni', 'gepafin')}</h2>
<div className="appForm__cols">
<FormField
type="select"
disabled={true}
fieldName="language"
defaultValue={'it'}
label={__('Lingua predefinita', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
options={[
{ label: __('Italiano', 'gepafin'), name: 'it' }
]}
/>
<FormField
type="select"
disabled={true}
fieldName="timezone"
defaultValue={'Europe/Rome'}
label={__('Fuso Orario', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
options={[
{ label: __('Europe/Rome', 'gepafin'), name: 'Europe/Rome' }
]}
/>
<FormField
type="select"
disabled={true}
fieldName="dateformat"
defaultValue={'DD/MM/YY'}
label={__('Formato Data', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
options={[
{ label: __('DD/MM/YY', 'gepafin'), name: 'DD/MM/YY' }
]}
/>
</div>
</div>
<div className="appPageSection">
<h2>{__('Sicurezza', 'gepafin')}</h2>
<div className="appForm__row">
<label>{__('Ultimo accesso', 'gepafin')}</label>
<span>{getDateFromISOstring(userData.lastLogin)}</span>
</div>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection__hr">
<span>{__('Azioni rapide', 'gepafin')}</span>
</div>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button
disabled={isAsyncRequest}
label={__('Salva modifiche', 'gepafin')}
icon="pi pi-check" iconPos="right"/>
</div>
</div>
</form>
</div>
)
}
export default ProfileBeneficiario;

View File

@@ -3,6 +3,7 @@ import { __, sprintf } from '@wordpress/i18n';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { classNames } from 'primereact/utils'; import { classNames } from 'primereact/utils';
import { isEmpty } from 'ramda'; import { isEmpty } from 'ramda';
import { useNavigate } from 'react-router-dom';
// tools // tools
import AuthenticationService from '../../service/authentication-service'; import AuthenticationService from '../../service/authentication-service';
@@ -16,7 +17,10 @@ import LogoIcon from '../../icons/LogoIcon';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { Messages } from 'primereact/messages'; import { Messages } from 'primereact/messages';
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
const ResetPassword = () => { const ResetPassword = () => {
const navigate = useNavigate();
const token = useStore().main.token(); const token = useStore().main.token();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [resetPassToken, setResetPassToken] = useState(''); const [resetPassToken, setResetPassToken] = useState('');
@@ -30,14 +34,23 @@ const ResetPassword = () => {
setValue setValue
} = useForm({ mode: 'onChange' }); } = useForm({ mode: 'onChange' });
const gotToLoginAdmin = () => {
navigate('/loginadmin');
}
const onSubmit = (formData) => { const onSubmit = (formData) => {
errorMsgs.current.clear(); errorMsgs.current.clear();
setLoading(true); setLoading(true);
const request = { const request = {
...formData ...formData,
hubUuid: APP_HUB_ID
} }
AuthenticationService.forgotPassword(request, getCallback, errCallback); if (request.token && !isEmpty(request.token)) {
AuthenticationService.resetPassword(request, getCallback, errCallback);
} else {
AuthenticationService.forgotPassword(request, getCallback, errCallback);
}
}; };
const getCallback = (data) => { const getCallback = (data) => {
@@ -59,7 +72,7 @@ const ResetPassword = () => {
errorMsgs.current.show([ errorMsgs.current.show([
{ {
sticky: true, severity: 'error', summary: '', sticky: true, severity: 'error', summary: '',
detail: sprintf(__('%s', 'gepafin'), err), detail: sprintf(__('%s', 'gepafin'), err.message),
closable: true closable: true
} }
]); ]);
@@ -104,7 +117,7 @@ const ResetPassword = () => {
? <input ? <input
type="hidden" type="hidden"
name="token" name="token"
{...register('test', { {...register('token', {
required: true required: true
})} })}
/> : null} /> : null}
@@ -130,7 +143,7 @@ const ResetPassword = () => {
type="textinput" type="textinput"
inputtype="password" inputtype="password"
fieldName="confirmPassword" fieldName="confirmPassword"
label={__('Conferma Password', 'gepafin')} label={__('Conferma password', 'gepafin')}
control={control} control={control}
errors={errors} errors={errors}
config={{ config={{
@@ -144,6 +157,10 @@ const ResetPassword = () => {
<Button <Button
label={__('Invia', 'gepafin')} label={__('Invia', 'gepafin')}
disabled={loading}/> disabled={loading}/>
<Button
label={__('Accedi', 'gepafin')}
link onClick={gotToLoginAdmin}/>
</form> </form>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,300 @@
import React, { useState, useEffect, useRef } from 'react';
import { __ } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { isEmpty } from 'ramda';
import { wrap } from 'object-path-immutable';
// store
import { storeSet, useStore } from '../../store';
// api
import AmendmentsService from '../../service/amendments-service';
// tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
// components
import { Skeleton } from 'primereact/skeleton';
import { Button } from 'primereact/button';
import { Checkbox } from 'primereact/checkbox';
import { Editor } from 'primereact/editor';
import { InputNumber } from 'primereact/inputnumber';
import BlockingOverlay from '../../components/BlockingOverlay';
import { Toast } from 'primereact/toast';
import { InputSwitch } from 'primereact/inputswitch';
const SoccorsoEditPreInstructor = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const { id, evaluationId } = useParams();
const navigate = useNavigate();
const [data, setData] = useState({});
const [formData, setFormData] = useState({});
const toast = useRef(null);
const goToEvaluationPage = () => {
navigate(`/domande/${id}`);
}
const getCallback = (data) => {
if (data.status === 'SUCCESS') {
console.log(data.data)
setData(data.data);
setFormData(getFormattedFormData(data.data));
}
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 getFormattedFormData = (data) => {
let newData = {};
newData.formFields = data.formFields;
newData.responseDays = 10;
newData.note = '';
newData.isSendNotification = true;
newData.isSendEmail = true;
return newData;
};
const renderHeader = () => {
return (
<span className="ql-formats">
<button className="ql-bold" aria-label="Bold"></button>
<button className="ql-italic" aria-label="Italic"></button>
<button className="ql-underline" aria-label="Underline"></button>
<button className="ql-link" aria-label="Link"></button>
<button className="ql-list" value="ordered"></button>
<button className="ql-header" value="2"></button>
<button className="ql-header" value="3"></button>
<button className="ql-blockquote"></button>
<button className="ql-list" value="bullet"></button>
<button className="ql-indent" value="-1"></button>
<button className="ql-indent" value="+1"></button>
</span>
);
};
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 (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Richiesta Integrazione Documentale', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<Toast ref={toast}/>
<BlockingOverlay shouldDisplay={isAsyncRequest}/>
<div className="appPageSection__row">
<Button
type="button"
outlined
onClick={goToEvaluationPage}
label={__('Indietro', 'gepafin')}
icon="pi pi-arrow-left" iconPos="left"/>
</div>
<div className="appPage__spacer"></div>
{!isAsyncRequest && !isEmpty(data)
? <div className="appPage__content">
<div className="appPageSection__withBorder column">
<p className="appPageSection__pMeta">
<span>{__('ID domanda', 'gepafin')}</span>
<span>{data.applicationId}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Bando', 'gepafin')}</span>
<span>{data.callName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Beneficiario', 'gepafin')}</span>
<span>{data.beneficiaryName}</span>
</p>
</div>
<div className="appPageSection">
<div className="appPageSection columns">
<div>
<h3>{__('Note', 'gepafin')}</h3>
<div style={{marginBottom: '30px'}}>
<Editor
value={formData.note}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateEvaluationValue(
e.htmlValue,
'note'
)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
<h3>{__('Tempo per la Risposta (giorni)', 'gepafin')}</h3>
<div style={{marginBottom: '30px'}}>
<InputNumber
keyfilter="int"
value={formData.responseDays}
showButtons
onChange={(e) => updateEvaluationValue(
e.value,
'responseDays',
9999
)}/>
</div>
<h3>{__('Notifica', 'gepafin')}</h3>
<div className="appPageSection__withBorder grey">
<div className="appForm__field row">
<InputSwitch
inputId="notify_email"
checked={formData.isSendEmail}
onChange={(e) => updateEvaluationValue(
e.value,
'isSendEmail'
)}/>
<label htmlFor="notify_email">{__('Notifiche Email', 'gepafin')}</label>
</div>
<div className="appForm__field row">
<InputSwitch
inputId="notify_push"
checked={formData.isSendNotification}
onChange={(e) => updateEvaluationValue(
e.value,
'isSendNotification'
)}/>
<label htmlFor="notify_push">{__('Notifiche Push', 'gepafin')}</label>
</div>
</div>
</div>
{formData.formFields
? <div>
<h3>{__('Documenti da Integrare', 'gepafin')}</h3>
<div className="appPageSection__withBorder grey">
<div className="appPageSection__checklist">
{formData.formFields.map((o, i) => <div key={o.fieldId}>
<Checkbox
inputId={`checklist_${o.fieldId}`}
onChange={(e) => updateEvaluationValue(
e.checked,
`formFields.${i}.selected`
)}
checked={o.selected}></Checkbox>
<label htmlFor={`checklist_${o.fieldId}`}>{o.label}</label>
</div>)}
</div>
</div>
</div>
: null}
</div>
</div>
<div className="appPageSection__message warning">
<i className="pi pi-exclamation-triangle"></i>
<span className="summary">{__('Attenzione', 'gepafin')}</span>
<span>{__("L'invio della richiesta di integrazione sospenderà il termine di valutazione della domanda.", 'gepafin')}</span>
</div>
<div className="appPageSection__hr">
<span>{__('Azioni', 'gepafin')}</span>
</div>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button
type="button"
outlined
label={__('Anulla', 'gepafin')}
icon="pi pi-times" iconPos="right"/>
<Button
type="button"
onClick={doCreate}
label={__('Invia richiesta', 'gepafin')}
icon="pi pi-check" iconPos="right"/>
</div>
</div>
</div>
: <>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="2rem" className="mb-8"></Skeleton>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="4rem" className="mb-8"></Skeleton>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="2rem" className="mb-8"></Skeleton>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="4rem"></Skeleton>
</>}
</div>
)
}
export default SoccorsoEditPreInstructor;

View File

@@ -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 <span>{__('Aggiungi comunicazione', 'gepafin')}</span>
}
const hideNewComDialog = () => {
setIsVisibleNewCommDialog(false);
setNewCommData({
title: '',
comment: ''
});
}
const footerNewComDialog = () => {
return <div>
<Button type="button" label={__('Anulla', 'gepafin')} onClick={hideNewComDialog} outlined/>
<Button
type="button"
disabled={isLoadingCommunication || isEmpty(newCommData.title) || isEmpty(newCommData.comment)}
label={__('Invia', 'gepafin')} onClick={createCommunication}/>
</div>
}
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 (
<span className="ql-formats">
<button className="ql-bold" aria-label="Bold"></button>
<button className="ql-italic" aria-label="Italic"></button>
<button className="ql-underline" aria-label="Underline"></button>
<button className="ql-link" aria-label="Link"></button>
<button className="ql-list" value="ordered"></button>
<button className="ql-header" value="2"></button>
<button className="ql-header" value="3"></button>
<button className="ql-blockquote"></button>
<button className="ql-list" value="bullet"></button>
<button className="ql-indent" value="-1"></button>
<button className="ql-indent" value="+1"></button>
</span>
);
};
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 <span>{__('Estendi scadenza', 'gepafin')}</span>
}
const hideExtendRespDialog = () => {
setIsVisibleExtendTimeDialog(false);
}
const footerExtendRespDialog = () => {
return <div>
<Button type="button" label={__('Anulla', 'gepafin')} onClick={hideExtendRespDialog} outlined/>
<Button
type="button"
disabled={isLoadingExtendingTime || isEmpty(extendedTime)}
label={__('Invia', 'gepafin')} onClick={doExtendTimeResponse}/>
</div>
}
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 (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Soccorso Istruttorio - Dettagli', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<Toast ref={toast}/>
<BlockingOverlay shouldDisplay={isAsyncRequest}/>
<div className="appPageSection__row">
<Button
type="button"
outlined
onClick={goToEvaluationPage}
label={__('Indietro', 'gepafin')}
icon="pi pi-arrow-left" iconPos="left"/>
</div>
<div className="appPage__spacer"></div>
<div className="appPage__content">
<div className="appPageSection__withBorder columns">
<p className="appPageSection__pMeta">
<span>{__('ID domanda', 'gepafin')}</span>
<span>{data.applicationId}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Bando', 'gepafin')}</span>
<span>{data.callName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Beneficiario', 'gepafin')}</span>
<span>{data.beneficiaryName}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Inizio', 'gepafin')}</span>
<span>{getDateFromISOstring(data.startDate)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Scadenza', 'gepafin')}</span>
<span>{getDateFromISOstring(data.expirationDate)}</span>
</p>
<p className="appPageSection__pMeta">
<span>{__('Stato', 'gepafin')}</span>
<span>{getBandoLabel(data.status)}</span>
</p>
</div>
<div className="appPageSection">
<h2>{__('Dettagli Richiesta', 'gepafin')}</h2>
<div className="appPageSection columns">
<div>
<h3>{__('Documenti Richiesti', 'gepafin')}</h3>
<ol className="appPageSection__list">
{data.formFields
? data.formFields.map((o, i) => <li key={o.fieldId} style={{ flexDirection: 'row' }}>
<span>{o.label}</span>
</li>) : null}
</ol>
</div>
<div>
<h3>{__('Note e spiegazioni', 'gepafin')}</h3>
<div className="appPageSection__withBorder grey ql-editor"
style={{ minHeight: '200px' }}>
{renderHtmlContent(data.note)}
</div>
</div>
</div>
</div>
<div className="appPageSection">
<h2>{__('Comunicazioni', 'gepafin')}</h2>
<table className="myTable">
<thead className="myThead">
<tr>
<th style={{ width: 250 }}>{__('Data', 'gepafin')}</th>
<th>{__('Comunicazione', 'gepafin')}</th>
</tr>
</thead>
<tbody className="myTbody">
{!isNil(comms) && !isEmpty(comms)
? comms.map((o, i) => <tr key={o.id}>
<td valign="top">
{getDateFromISOstring(o.commentedDate)}
</td>
<td>
<h3>{o.title}</h3>
<p>{o.comment}</p>
</td>
</tr>)
: <tr>
<td>-</td>
<td>-</td>
</tr>}
</tbody>
</table>
<Button
style={{ marginTop: 30 }}
onClick={openNewCommDialog}
disabled={data.status === 'CLOSE'}
type="button"
label={__('Aggiungi Comunicazione', 'gepafin')}
icon="pi pi-plus" iconPos="right"/>
</div>
<div className="appPageSection">
<h2>{__('Documenti Ricevuti', 'gepafin')}</h2>
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
{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 <FormField
key={o.fieldId}
disabled={data.status === 'CLOSE'}
type="fileupload"
setDataFn={setValue}
fieldName={o.fieldId}
label={o.label}
control={control}
errors={errors}
defaultValue={formInitialData[o.fieldId] ? formInitialData[o.fieldId] : []}
accept={[]}
doctype="document"
register={register}
sourceId={data.applicationId}
source="application"
multiple={true}
/>
}) : null}
</form>
<Button
style={{ marginTop: 30 }}
type="button"
disabled={data.status === 'CLOSE'}
onClick={doUpdateAmendment}
label={__('Aggiorna', 'gepafin')}/>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection__hr">
<span>{__('Azioni', 'gepafin')}</span>
</div>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button
type="button"
onClick={sendReminder}
disabled={isLoadingReminding || data.status === 'CLOSE'}
outlined
label={__('Invia Sollecito', 'gepafin')}
icon="pi pi-send"
/>
<Button
type="button"
onClick={openExtendResponseTimeDialog}
disabled={isLoadingExtendingTime || data.status === 'CLOSE'}
outlined
label={__('Estendi Scadenza', 'gepafin')}
icon="pi pi-stopwatch"
/>
<Button
type="button"
onClick={doCloseAmendment}
disabled={isAsyncRequest || data.status === 'CLOSE'}
label={__('Chiudi Soccorso Istruttorio', 'gepafin')}
icon="pi pi-times" iconPos="right"/>
</div>
</div>
<div className="appForm__field" style={{ marginTop: '-40px' }}>
<label>{__('Note Interne', 'gepafin')}</label>
<div style={{position: 'relative'}}>
<BlockingOverlay shouldDisplay={data.status === 'CLOSE'}/>
<Editor
value={data.internalNote}
readOnly={data.status === 'CLOSE'}
placeholder={__('Digita qui il messagio', 'gepafin')}
headerTemplate={header}
onTextChange={(e) => updateNewAmendmentData(
e.htmlValue,
'internalNote'
)}
style={{ height: 80 * 3, width: '100%' }}
/>
</div>
</div>
</div>
<Dialog
visible={isVisibleNewCommDialog}
modal
header={headerNewComDialog}
footer={footerNewComDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideNewComDialog}>
<div className="appForm__field">
<label
className={classNames({ 'p-error': isEmpty(newCommData.title) })}>
{__('Titolo', 'gepafin')}*
</label>
<InputText value={newCommData.title}
disabled={data.status === 'CLOSE'}
invalid={isEmpty(newCommData.title)}
onChange={(e) => updateNewCommData(e.target.value, 'title')}/>
<label
className={classNames({ 'p-error': isEmpty(newCommData.comment) })}>
{__('Contenuto', 'gepafin')}*
</label>
<InputTextarea
value={newCommData.comment}
disabled={data.status === 'CLOSE'}
rows={5} cols={30}
invalid={isEmpty(newCommData.comment)}
onChange={(e) => updateNewCommData(e.target.value, 'comment')}/>
</div>
</Dialog>
<Dialog
visible={isVisibleExtendTimeDialog}
modal
header={headerExtendRespDialog}
footer={footerExtendRespDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideExtendRespDialog}>
<div className="appForm__field">
<label
className={classNames({ 'p-error': isEmpty(extendedTime) })}>
{__('Giorni', 'gepafin')}*
</label>
<InputNumber
keyfilter="int"
disabled={data.status === 'CLOSE'}
value={extendedTime}
showButtons
onChange={(e) => setExtendedTime(e.value)}/>
</div>
</Dialog>
</div>
)
}
export default SoccorsoEditPreInstructor;

View File

@@ -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 (
<div className="appTableHeader">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined onClick={clearFilter} />
<IconField iconPosition="left">
<InputIcon className="pi pi-search" />
<InputText value={globalFilterValue} onChange={onGlobalFilterChange} placeholder={__('Cerca', 'gepafin')} />
</IconField>
</div>
);
};
const dateStartBodyTemplate = (rowData) => {
return formatDate(rowData.startDate);
};
const dateExpirationBodyTemplate = (rowData) => {
return formatDate(rowData.expirationDate);
};
const dateFilterTemplate = (options) => {
return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />;
};
const statusBodyTemplate = (rowData) => {
return <ProperBandoLabel status={rowData.status}/>;
};
const actionsBodyTemplate = (rowData) => {
return <Link to={`/domande/${rowData.applicationId}/soccorso/${rowData.id}`}>
<Button severity="info" label={__('Dettagli', 'gepafin')} size="small" />
</Link>
}
const header = renderHeader();
return(
<div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
filters={filters}
globalFilterFields={['name', 'status']}
header={header}
emptyMessage={__('Nessun dato disponibile', 'gepafin')}
onFilter={(e) => setFilters(e.filters)}>
<Column field="applicationId" header={__('ID domanda', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '6rem' }}/>
<Column field="callName" header={__('Bando', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column field="beneficiaryName" header={__('Beneficiario', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '8rem' }}/>
<Column header={__('Data Richiesta', 'gepafin')}
filterField="startDate" dataType="date"
style={{ minWidth: '8rem' }}
body={dateStartBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column header={__('Scadenza', 'gepafin')}
filterField="expirationDate" dataType="date"
style={{ minWidth: '8rem' }}
body={dateExpirationBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column field="status" header={__('Stato', 'gepafin')}
style={{ minWidth: '7rem' }} body={statusBodyTemplate} />
<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/>
</DataTable>
</div>
)
}
export default PreInstructorSoccorsiTable;

View File

@@ -0,0 +1,23 @@
import React from 'react';
import { __ } from '@wordpress/i18n';
// components
import PreInstructorSoccorsiTable from './components/PreInstructorSoccorsiTable';
const SoccorsoIstruttorioPreInstructor = () => {
return(
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Soccorso Istruttorio', 'gepafin')}</h1>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<PreInstructorSoccorsiTable/>
</div>
</div>
)
}
export default SoccorsoIstruttorioPreInstructor;

View File

@@ -1,9 +1,9 @@
import React, { useState, useEffect} from 'react'; import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { is, uniq } from 'ramda'; import { uniq } from 'ramda';
// store // store
import { storeSet, storeGet } from '../../../../store'; import { useStore, storeSet } from '../../../../store';
// tools // tools
import getBandoSeverity from '../../../../helpers/getBandoSeverity'; import getBandoSeverity from '../../../../helpers/getBandoSeverity';
@@ -11,7 +11,7 @@ import getBandoLabel from '../../../../helpers/getBandoLabel';
import getDateFromISOstring from '../../../../helpers/getDateFromISOstring'; import getDateFromISOstring from '../../../../helpers/getDateFromISOstring';
// api // api
import BandoService from '../../../../service/bando-service'; import UserService from '../../../../service/user-service';
// components // components
import { FilterMatchMode, FilterOperator } from 'primereact/api'; import { FilterMatchMode, FilterOperator } from 'primereact/api';
@@ -25,59 +25,37 @@ import { Button } from 'primereact/button';
import { Calendar } from 'primereact/calendar'; import { Calendar } from 'primereact/calendar';
import { Tag } from 'primereact/tag'; import { Tag } from 'primereact/tag';
import ProperBandoLabel from '../../../../components/ProperBandoLabel'; import ProperBandoLabel from '../../../../components/ProperBandoLabel';
import { Link } from 'react-router-dom';
const AllUsersTable = () => { const AllUsersTable = () => {
const [items, setItems] = useState(null); const users = useStore().main.users();
const [filters, setFilters] = useState(null); const [filters, setFilters] = useState(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [globalFilterValue, setGlobalFilterValue] = useState(''); const [globalFilterValue, setGlobalFilterValue] = useState('');
const [statuses, setStatuses] = useState([]); const [statuses, setStatuses] = useState([]);
useEffect(() => { useEffect(() => {
storeSet.main.setAsyncRequest(); if (!loading) {
const sample = [ setLoading(true);
{ UserService.getUsers(getCallback, errGetCallbacks);
id: 11, }
name: 'Mario Rossi',
email: 'mario.rossi@example.com',
role: 'Beneficiario',
status: 'active',
last_access: '2024-08-01 10:30'
},
{
id: 12,
name: 'Mario Rossi2',
email: 'mario.rossi@example.com',
role: 'Beneficiario',
status: 'active',
last_access: '2024-08-01 10:30'
}
];
setItems(sample);
//BandoService.getBandi(getCallback, errGetCallbacks);
}, []); }, []);
/*const getCallback = (data) => { const getCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
setItems(getFormattedBandiData(data.data)); storeSet.main.users(getFormattedData(data.data));
setStatuses(uniq(data.data.map(o => o.status))) setStatuses(uniq(data.data.map(o => o.status)))
initFilters(); initFilters();
} }
storeSet.main.unsetAsyncRequest(); setLoading(false);
} }
const errGetCallbacks = (data) => { const errGetCallbacks = () => {
console.log('errGetCallbacks', data) setLoading(false);
storeSet.main.unsetAsyncRequest(); }
}*/
const getFormattedData = (data) => { const getFormattedData = (data) => {
return data.map((d) => { return data
d.last_access = is(String, d.last_access) ? new Date(d.last_access) : (d.last_access ? d.last_access : ''); .filter(o => ['ROLE_SUPER_ADMIN', 'ROLE_PRE_INSTRUCTOR'].includes(o.role.roleType));
return d;
});
}; };
const clearFilter = () => { const clearFilter = () => {
@@ -97,8 +75,14 @@ const AllUsersTable = () => {
const initFilters = () => { const initFilters = () => {
setFilters({ setFilters({
global: { value: null, matchMode: FilterMatchMode.CONTAINS }, global: { value: null, matchMode: FilterMatchMode.CONTAINS },
name: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }] }, name: {
last_access: { operator: FilterOperator.AND, constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] }, operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }]
},
lastLogin: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
},
status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] }, status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] },
}); });
setGlobalFilterValue(''); setGlobalFilterValue('');
@@ -107,21 +91,32 @@ const AllUsersTable = () => {
const renderHeader = () => { const renderHeader = () => {
return ( return (
<div className="appTableHeader"> <div className="appTableHeader">
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined onClick={clearFilter} /> <Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
onClick={clearFilter}/>
<IconField iconPosition="left"> <IconField iconPosition="left">
<InputIcon className="pi pi-search" /> <InputIcon className="pi pi-search"/>
<InputText value={globalFilterValue} onChange={onGlobalFilterChange} placeholder={__('Cerca', 'gepafin')} /> <InputText value={globalFilterValue} onChange={onGlobalFilterChange}
placeholder={__('Cerca', 'gepafin')}/>
</IconField> </IconField>
</div> </div>
); );
}; };
const dateLastAccessBodyTemplate = (rowData) => { const dateLastAccessBodyTemplate = (rowData) => {
return getDateFromISOstring(rowData.last_access); return getDateFromISOstring(rowData.lastLogin);
}; };
const dateFilterTemplate = (options) => { const dateFilterTemplate = (options) => {
return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)} dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999" />; return <Calendar value={options.value} onChange={(e) => options.filterCallback(e.value, options.index)}
dateFormat="mm/dd/yy" placeholder="mm/dd/yyyy" mask="99/99/9999"/>;
};
const nameBodyTemplate = (rowData) => {
return `${rowData.firstName} ${rowData.lastName}`;
};
const roleBodyTemplate = (rowData) => {
return rowData.role.roleName;
}; };
const statusBodyTemplate = (rowData) => { const statusBodyTemplate = (rowData) => {
@@ -129,43 +124,53 @@ const AllUsersTable = () => {
}; };
const statusFilterTemplate = (options) => { const statusFilterTemplate = (options) => {
return <Dropdown value={options.value} options={statuses} onChange={(e) => options.filterCallback(e.value, options.index)} itemTemplate={statusItemTemplate} placeholder="Select One" className="p-column-filter" showClear />; return <Dropdown value={options.value} options={statuses}
onChange={(e) => options.filterCallback(e.value, options.index)}
itemTemplate={statusItemTemplate} placeholder={__('Scegli uno', 'gepafin')}
className="p-column-filter"
showClear/>;
}; };
const statusItemTemplate = (option) => { const statusItemTemplate = (option) => {
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)} />; return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)}/>;
}; };
const actionsBodyTemplate = (rowData) => { /*const actionsBodyTemplate = (rowData) => {
return <Link to={`/utenti/${rowData.id}`}> /!*return <Link to={`/utenti/${rowData.id}`}>
<Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small" iconPos="right" /> <Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small" iconPos="right"/>
</Link> </Link>*!/
} return null;
}*/
const header = renderHeader(); const header = renderHeader();
return( return (
<div className="appPageSection__table"> <div className="appPageSection__table">
<DataTable value={items} paginator showGridlines rows={10} loading={loading} dataKey="id" <DataTable value={users} paginator showGridlines rows={10} loading={loading} dataKey="id"
filters={filters} filters={filters}
globalFilterFields={['name', 'status']} globalFilterFields={['name', 'status']}
header={header} header={header}
emptyMessage={__('Nessun dato disponibile', 'gepafin')} emptyMessage={__('Nessun dato disponibile', 'gepafin')}
onFilter={(e) => setFilters(e.filters)}> onFilter={(e) => setFilters(e.filters)}>
<Column field="name" header={__('Nome utente', 'gepafin')} filter filterPlaceholder="Search by name" <Column body={nameBodyTemplate}
header={__('Nome utente', 'gepafin')}
filter filterPlaceholder={__('Cerca per nome', 'gepafin')}
style={{ minWidth: '12rem' }}/> style={{ minWidth: '12rem' }}/>
<Column field="email" header={__('Email', 'gepafin')} filter filterPlaceholder="Search by email" <Column field="email" header={__('Email', 'gepafin')}
filter filterPlaceholder={__('Cerca per email', 'gepafin')}
style={{ minWidth: '12rem' }}/> style={{ minWidth: '12rem' }}/>
<Column field="role" header={__('Ruolo', 'gepafin')} <Column body={roleBodyTemplate} header={__('Ruolo', 'gepafin')}
style={{ minWidth: '12rem' }}/> style={{ minWidth: '12rem' }}/>
<Column field="status" header={__('Stato', 'gepafin')} filterMenuStyle={{ width: '14rem' }} <Column field="status" header={__('Stato', 'gepafin')}
style={{ width: '120px' }} body={statusBodyTemplate} filter filterMenuStyle={{ width: '14rem' }}
filterElement={statusFilterTemplate}/> style={{ width: '120px' }} body={statusBodyTemplate}
<Column header={__('Ultimo accesso', 'gepafin')} filterField="last_access" dataType="date" filter filterElement={statusFilterTemplate}/>
<Column header={__('Ultimo accesso', 'gepafin')}
filterField="lastLogin" dataType="date"
style={{ minWidth: '10rem' }} style={{ minWidth: '10rem' }}
body={dateLastAccessBodyTemplate} filter filterElement={dateFilterTemplate}/> body={dateLastAccessBodyTemplate} filter filterElement={dateFilterTemplate}/>
<Column header={__('Azioni', 'gepafin')} {/*<Column header={__('Azioni', 'gepafin')}
body={actionsBodyTemplate}/> body={actionsBodyTemplate}/>*/}
</DataTable> </DataTable>
</div> </div>
) )

View File

@@ -1,6 +1,17 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { isEmpty, isNil } from 'ramda'; import { isEmpty, isNil } from 'ramda';
import { klona } from 'klona';
// store
import { storeSet, storeGet } from '../../store';
// service
import UserService from '../../service/user-service';
// tools
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import { isEmail } from '../../helpers/validators';
// components // components
import AllUsersTable from './components/AllUsersTable'; import AllUsersTable from './components/AllUsersTable';
@@ -8,22 +19,25 @@ import { Button } from 'primereact/button';
import { InputText } from 'primereact/inputtext'; import { InputText } from 'primereact/inputtext';
import { Dropdown } from 'primereact/dropdown'; import { Dropdown } from 'primereact/dropdown';
import { Dialog } from 'primereact/dialog'; import { Dialog } from 'primereact/dialog';
import UserService from '../../service/user-service';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import { storeSet } from '../../store';
import { klona } from 'klona';
import { classNames } from 'primereact/utils'; import { classNames } from 'primereact/utils';
import { Toast } from 'primereact/toast';
const APP_HUB_ID = process.env.REACT_APP_HUB_ID;
const Users = () => { const Users = () => {
const [loading, setLoading] = useState(false);
const [isVisibleEditDialog, setIsVisibleEditDialog] = useState(false); const [isVisibleEditDialog, setIsVisibleEditDialog] = useState(false);
const [newUserData, setNewUserData] = useState({ const [newUserData, setNewUserData] = useState({
firstName: '', firstName: '',
lastName: '', lastName: '',
email: '', email: '',
phoneNumber: '', phoneNumber: '',
role: '' password: '',
confPassword: '',
roleId: 0
}); });
const [roles, setRoles] = useState([]); const [roles, setRoles] = useState([]);
const toast = useRef(null);
const onCreateNewUser = () => { const onCreateNewUser = () => {
setIsVisibleEditDialog(true); setIsVisibleEditDialog(true);
@@ -40,12 +54,52 @@ const Users = () => {
lastName: '', lastName: '',
email: '', email: '',
phoneNumber: '', phoneNumber: '',
role: '' password: '',
confPassword: '',
roleId: 0
}); });
} }
const saveEditDialog = () => { const saveEditDialog = () => {
const emptyValues = Object.values(newUserData).filter(v => isEmpty(v));
if (isEmpty(emptyValues) && newUserData.password === newUserData.confPassword && !loading) {
setLoading(true);
const body = {
...newUserData,
hubUuid: APP_HUB_ID
}
UserService.createUser(body, createUserCallback, errCreateUserCallback);
}
}
const createUserCallback = (data) => {
if (data.status === 'SUCCESS') {
const users = storeGet.main.users();
storeSet.main.users([data.data.user, ...users]);
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: data.message
});
}
}
setLoading(false);
hideEditDialog();
}
const errCreateUserCallback = (data) => {
setLoading(false);
if (toast.current) {
toast.current.show({
severity: 'error',
summary: '',
detail: data.message
});
}
set404FromErrorResponse(data);
} }
const onChangeEditItem = (value, key) => { const onChangeEditItem = (value, key) => {
@@ -59,18 +113,20 @@ const Users = () => {
<Button type="button" label={__('Anulla', 'gepafin')} onClick={hideEditDialog} outlined/> <Button type="button" label={__('Anulla', 'gepafin')} onClick={hideEditDialog} outlined/>
<Button <Button
type="button" type="button"
disabled={isEmpty(newUserData)} disabled={isEmpty(newUserData) || loading}
label={__('Salva', 'gepafin')} onClick={saveEditDialog}/> label={__('Salva', 'gepafin')} onClick={saveEditDialog}/>
</div> </div>
} }
const getRolesCallback = (data) => { const getRolesCallback = (data) => {
if (data.status === 'SUCCESS') { if (data.status === 'SUCCESS') {
const roles = data.data.map(o => ({ const roles = data.data
name: o.roleName, .filter(o => ['ROLE_SUPER_ADMIN', 'ROLE_PRE_INSTRUCTOR'].includes(o.roleType))
value: o.id .map(o => ({
})); name: o.roleName,
setRoles(roles) value: o.id
}));
setRoles(roles);
} }
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
@@ -80,19 +136,22 @@ const Users = () => {
storeSet.main.unsetAsyncRequest(); storeSet.main.unsetAsyncRequest();
} }
const isInvalidField = (data, key) => isEmpty(data[key]) || isNil(data[key])
useEffect(() => { useEffect(() => {
if (isVisibleEditDialog) { if (isVisibleEditDialog) {
UserService.getRoles(getRolesCallback, errGetRolesCallback) UserService.getRoles(getRolesCallback, errGetRolesCallback)
} }
}, [isVisibleEditDialog]); }, [isVisibleEditDialog]);
return( return (
<div className="appPage"> <div className="appPage">
<div className="appPage__pageHeader"> <div className="appPage__pageHeader">
<h1>{__('Gestione utenti', 'gepafin')}</h1> <h1>{__('Gestione utenti', 'gepafin')}</h1>
</div> </div>
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>
<Toast ref={toast} />
<div className="appPageSection"> <div className="appPageSection">
<div className="appPageSection__actions"> <div className="appPageSection__actions">
@@ -101,7 +160,7 @@ const Users = () => {
label={__('Crea nuovo')} icon="pi pi-plus" iconPos="right"/> label={__('Crea nuovo')} icon="pi pi-plus" iconPos="right"/>
</div> </div>
<AllUsersTable doRefresh={true}/> <AllUsersTable/>
<Dialog <Dialog
visible={isVisibleEditDialog} visible={isVisibleEditDialog}
@@ -113,38 +172,74 @@ const Users = () => {
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>
<div className="appForm__cols"> <div className="appForm__cols">
<div className="appForm__field"> <div className="appForm__field">
<label className={classNames({ 'p-error': isEmpty(newUserData.firstName) || isNil(newUserData.firstName) })}>{__('Nome', 'gepafin')}*</label> <label
className={classNames({ 'p-error': isInvalidField(newUserData, 'firstName') })}>
{__('Nome', 'gepafin')}*
</label>
<InputText value={newUserData.firstName} <InputText value={newUserData.firstName}
invalid={isEmpty(newUserData.firstName) || isNil(newUserData.firstName)} invalid={isInvalidField(newUserData, 'firstName')}
onChange={(e) => onChangeEditItem(e.target.value, 'firstName')}/> onChange={(e) => onChangeEditItem(e.target.value, 'firstName')}/>
</div> </div>
<div className="appForm__field"> <div className="appForm__field">
<label className={classNames({ 'p-error': isEmpty(newUserData.lastName) || isNil(newUserData.lastName) })}>{__('Cognome', 'gepafin')}*</label> <label
className={classNames({ 'p-error': isInvalidField(newUserData, 'lastName') })}>
{__('Cognome', 'gepafin')}*
</label>
<InputText value={newUserData.lastName} <InputText value={newUserData.lastName}
invalid={isEmpty(newUserData.lastName) || isNil(newUserData.lastName)} invalid={isInvalidField(newUserData, 'lastName')}
onChange={(e) => onChangeEditItem(e.target.value, 'lastName')}/> onChange={(e) => onChangeEditItem(e.target.value, 'lastName')}/>
</div> </div>
</div> </div>
<div className="appForm__cols"> <div className="appForm__cols">
<div className="appForm__field"> <div className="appForm__field">
<label className={classNames({ 'p-error': isEmpty(newUserData.email) || isNil(newUserData.email) })}>{__('Email', 'gepafin')}*</label> <label
className={classNames({ 'p-error': isEmpty(newUserData.email) || isNil(newUserData.email) || !isEmail(newUserData.email) })}>
{__('Email', 'gepafin')}*
</label>
<InputText value={newUserData.email} <InputText value={newUserData.email}
invalid={isEmpty(newUserData.email) || isNil(newUserData.email)} invalid={isEmpty(newUserData.email) || isNil(newUserData.email) || !isEmail(newUserData.email)}
onChange={(e) => onChangeEditItem(e.target.value, 'email')}/> onChange={(e) => onChangeEditItem(e.target.value, 'email')}/>
</div> </div>
<div className="appForm__field"> <div className="appForm__field">
<label className={classNames({ 'p-error': isEmpty(newUserData.phoneNumber) || isNil(newUserData.phoneNumber) })}>{__('Telefono', 'gepafin')}</label> <label
className={classNames({ 'p-error': isInvalidField(newUserData, 'phoneNumber') })}>
{__('Telefono', 'gepafin')}
</label>
<InputText value={newUserData.phoneNumber} <InputText value={newUserData.phoneNumber}
invalid={isEmpty(newUserData.phoneNumber) || isNil(newUserData.phoneNumber)} keyfilter="int"
invalid={isInvalidField(newUserData, 'phoneNumber')}
onChange={(e) => onChangeEditItem(e.target.value, 'phoneNumber')}/> onChange={(e) => onChangeEditItem(e.target.value, 'phoneNumber')}/>
</div> </div>
</div> </div>
<div className="appForm__cols">
<div className="appForm__field">
<label
className={classNames({ 'p-error': isEmpty(newUserData.password) || isNil(newUserData.password) || newUserData.password !== newUserData.confPassword })}>
{__('Password', 'gepafin')}*
</label>
<InputText value={newUserData.password}
invalid={isEmpty(newUserData.password) || isNil(newUserData.password) || newUserData.password !== newUserData.confPassword}
onChange={(e) => onChangeEditItem(e.target.value, 'password')}/>
</div>
<div className="appForm__field">
<label
className={classNames({ 'p-error': isEmpty(newUserData.confPassword) || isNil(newUserData.confPassword) || newUserData.password !== newUserData.confPassword })}>
{__('Conferma password', 'gepafin')}*
</label>
<InputText value={newUserData.confPassword}
invalid={isEmpty(newUserData.confPassword) || isNil(newUserData.confPassword) || newUserData.password !== newUserData.confPassword}
onChange={(e) => onChangeEditItem(e.target.value, 'confPassword')}/>
</div>
</div>
<div className="appForm__field"> <div className="appForm__field">
<label className={classNames({ 'p-error': isEmpty(newUserData.role) || isNil(newUserData.role) })}>{__('Ruolo', 'gepafin')}</label> <label
className={classNames({ 'p-error': isEmpty(newUserData.roleId) || isNil(newUserData.roleId) || newUserData.roleId === 0 })}>
{__('Ruolo', 'gepafin')}*
</label>
<Dropdown <Dropdown
value={newUserData.role} value={newUserData.roleId}
invalid={isEmpty(newUserData.role) || isNil(newUserData.role)} invalid={isEmpty(newUserData.roleId) || isNil(newUserData.roleId) || newUserData.roleId === 0}
onChange={(e) => onChangeEditItem(e.value, 'role')} onChange={(e) => onChangeEditItem(e.value, 'roleId')}
options={roles} options={roles}
optionLabel="name" optionLabel="name"
optionValue="value"/> optionValue="value"/>

View File

@@ -25,6 +25,16 @@ import ProfileCompany from './pages/ProfileCompany';
import Users from './pages/Users'; import Users from './pages/Users';
import AddCompany from './pages/AddCompany'; import AddCompany from './pages/AddCompany';
import ResetPassword from './pages/ResetPassword'; import ResetPassword from './pages/ResetPassword';
import DashboardPreInstructor from './pages/DashboardPreInstructor';
import ProfileBeneficiario from './pages/ProfileBeneficiario';
import Domande from './pages/Domande';
import DomandePreInstructor from './pages/DomandePreInstructor';
import DomandaEditPreInstructor from './pages/DomandaEditPreInstructor';
import SoccorsoIstruttorioPreInstructor from './pages/SoccorsoIstruttorioPreInstructor';
import SoccorsoEditPreInstructor from './pages/SoccorsoEditPreInstructor';
import SoccorsoAddPreInstructor from './pages/SoccorsoAddPreInstructor';
import DomandeBeneficiario from './pages/DomandeBeneficiario';
import DomandaBeneficiario from './pages/DomandaBeneficiario';
const routes = ({ role, chosenCompanyId }) => { const routes = ({ role, chosenCompanyId }) => {
@@ -34,62 +44,102 @@ const routes = ({ role, chosenCompanyId }) => {
<Route path="/" element={<DefaultLayout> <Route path="/" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <Dashboard/> : null} {'ROLE_SUPER_ADMIN' === role ? <Dashboard/> : null}
{'ROLE_BENEFICIARY' === role ? <DashboardBeneficiario/> : null} {'ROLE_BENEFICIARY' === role ? <DashboardBeneficiario/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <DashboardPreInstructor/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/bandi" element={<DefaultLayout> <Route path="/bandi" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <Bandi/> : null} {'ROLE_SUPER_ADMIN' === role ? <Bandi/> : null}
{'ROLE_BENEFICIARY' === role ? <BandiBeneficiario/> : null} {'ROLE_BENEFICIARY' === role ? <BandiBeneficiario/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/bandi/:id" element={<DefaultLayout> <Route path="/bandi/:id" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoEdit/> : null} {'ROLE_SUPER_ADMIN' === role ? <BandoEdit/> : null}
{'ROLE_BENEFICIARY' === role ? <BandoViewBeneficiario/> : null} {'ROLE_BENEFICIARY' === role ? <BandoViewBeneficiario/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/bandi/:id/preview" element={<DefaultLayout> <Route path="/bandi/:id/preview" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoView/> : null} {'ROLE_SUPER_ADMIN' === role ? <BandoView/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null} {'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/bandi/:id/preview-evaluation" element={<DefaultLayout> <Route path="/bandi/:id/preview-evaluation" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoView/> : null} {'ROLE_SUPER_ADMIN' === role ? <BandoView/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null} {'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/bandi/:id/forms" element={<DefaultLayout> <Route path="/bandi/:id/forms" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoForms/> : null} {'ROLE_SUPER_ADMIN' === role ? <BandoForms/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null} {'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/bandi/:id/forms/:formId" element={<DefaultLayout> <Route path="/bandi/:id/forms/:formId" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoFormsEdit/> : null} {'ROLE_SUPER_ADMIN' === role ? <BandoFormsEdit/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null} {'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/bandi/:id/forms/:formId/preview" element={<DefaultLayout> <Route path="/bandi/:id/forms/:formId/preview" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoFormsPreview/> : null} {'ROLE_SUPER_ADMIN' === role ? <BandoFormsPreview/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null} {'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/bandi/:id/flow" element={<DefaultLayout> <Route path="/bandi/:id/flow" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoFlowEdit/> : null} {'ROLE_SUPER_ADMIN' === role ? <BandoFlowEdit/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null} {'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/>
<Route path="/domande" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <Domande/> : null}
{'ROLE_BENEFICIARY' === role ? <DomandeBeneficiario/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandePreInstructor/> : null}
</DefaultLayout>}/>
<Route path="/domande/:id/" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <DomandaBeneficiario/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandaEditPreInstructor/> : null}
</DefaultLayout>}/>
<Route path="/domande/:id/aggiungi-soccorso" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <SoccorsoAddPreInstructor/> : null}
</DefaultLayout>}/>
<Route path="/domande/:id/soccorso/:amendmentId" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <SoccorsoEditPreInstructor/> : null}
</DefaultLayout>}/>
<Route path="/soccorso-istruttorio/" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <SoccorsoIstruttorioPreInstructor/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/imieibandi" element={<DefaultLayout> <Route path="/imieibandi" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null} {'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <Applications/> : null} {'ROLE_BENEFICIARY' === role ? <Applications/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/imieibandi/:id/" element={<DefaultLayout> <Route path="/imieibandi/:id/" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null} {'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <BandoApplication/> : null} {'ROLE_BENEFICIARY' === role ? <BandoApplication/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/profilo" element={<DefaultLayout> <Route path="/profilo" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <Profile/> : null} {'ROLE_SUPER_ADMIN' === role ? <Profile/> : null}
{'ROLE_BENEFICIARY' === role ? <Profile/> : null} {'ROLE_BENEFICIARY' === role ? <ProfileBeneficiario/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <Profile/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/profilo-aziendale" element={<DefaultLayout> <Route path="/profilo-aziendale" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null} {'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role && chosenCompanyId > 0 ? <ProfileCompany/> : <PageNotFound/>} {'ROLE_BENEFICIARY' === role && chosenCompanyId > 0 ? <ProfileCompany/> : <PageNotFound/>}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/agguingi-azienda" element={<DefaultLayout> <Route path="/agguingi-azienda" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null} {'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <AddCompany/> : null} {'ROLE_BENEFICIARY' === role ? <AddCompany/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
<Route path="/utenti" element={<DefaultLayout> <Route path="/utenti" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <Users/> : null} {'ROLE_SUPER_ADMIN' === role ? <Users/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null} {'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
{'ROLE_PRE_INSTRUCTOR' === role ? <PageNotFound/> : null}
</DefaultLayout>}/> </DefaultLayout>}/>
</Route> </Route>
<Route exact path="/reset-password" element={<ResetPassword/>}/> <Route exact path="/reset-password" element={<ResetPassword/>}/>

View File

@@ -0,0 +1,46 @@
import { NetworkService } from './network-service';
const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class AmendmentsService {
static getSoccorsoByApplEvalId = (id, callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/amendments/applicationEvaluation/${id}`, callback, errCallback, queryParams);
};
static getSoccorsoByApplId = (id, callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/amendments/application/${id}`, callback, errCallback, queryParams);
};
static getSoccorsoById = (callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/amendments`, callback, errCallback, queryParams);
};
static getSoccorsoByPreInstructorId = (id, callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/amendments/user/${id}`, callback, errCallback, queryParams);
};
static createSoccorso = (body, callback, errCallback, queryParams) => {
NetworkService.post(`${API_BASE_URL}/amendments`, body, callback, errCallback, queryParams);
};
static updateSoccorso = (id, body, callback, errCallback, queryParams) => {
NetworkService.put(`${API_BASE_URL}/amendments/${id}`, body, callback, errCallback, queryParams);
};
static extendSoccorso = (id, days, callback, errCallback, queryParams) => {
NetworkService.put(`${API_BASE_URL}/amendments/${id}/extendExpiration`, {}, callback, errCallback, [
['extendedDays', days]
]);
};
static sendReminderForSoccorso = (id, callback, errCallback, queryParams) => {
NetworkService.post(`${API_BASE_URL}/amendments/${id}/reminder`, {}, callback, errCallback, queryParams);
};
static closeSoccorso = (id, body, callback, errCallback, queryParams) => {
NetworkService.put(`${API_BASE_URL}/amendments`, body, callback, errCallback, [
['id', id]
]);
};
}

View File

@@ -0,0 +1,14 @@
import { NetworkService } from './network-service';
const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class ApplicationEvaluationService {
static getEvaluationByApplId = (callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/applicationEvaluation/application`, callback, errCallback, queryParams);
};
static updateEvaluation = (assignedApplicationId, body, callback, errCallback, queryParams) => {
NetworkService.put(`${API_BASE_URL}/applicationEvaluation/${assignedApplicationId}`, body, callback, errCallback, queryParams);
};
}

View File

@@ -0,0 +1,14 @@
import { NetworkService } from './network-service';
const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class AssignedApplicationService {
static getAssignedApplications = (callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/assignedApplication`, callback, errCallback, queryParams);
};
static assignApplication = (id, callback, errCallback, queryParams) => {
NetworkService.post(`${API_BASE_URL}/assignedApplication/application/${id}`, {}, callback, errCallback, queryParams);
};
}

View File

@@ -0,0 +1,14 @@
import { NetworkService } from './network-service';
const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class CommunicationService {
static getCommsByAmendmentId = (id, callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/communication/${id}`, callback, errCallback, queryParams);
};
static createCommunication = (id, body, callback, errCallback, queryParams) => {
NetworkService.post(`${API_BASE_URL}/communication/${id}`, body, callback, errCallback, queryParams);
};
}

View File

@@ -371,7 +371,8 @@ export class NetworkService {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': 'Bearer ' + storeGet.main.getToken(), 'Authorization': 'Bearer ' + storeGet.main.getToken(),
} },
signal: AbortSignal.timeout(5000)
}) })
.then(async response => { .then(async response => {
let status = response.status; let status = response.status;

View File

@@ -4,10 +4,18 @@ const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class UserService { export default class UserService {
static getUsers = (callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/user`, callback, errCallback, queryParams);
};
static updateUser = (id, body, callback, errCallback) => { static updateUser = (id, body, callback, errCallback) => {
NetworkService.put(`${API_BASE_URL}/user/${id}`, body, callback, errCallback); NetworkService.put(`${API_BASE_URL}/user/${id}`, body, callback, errCallback);
}; };
static createUser = (body, callback, errCallback) => {
NetworkService.post(`${API_BASE_URL}/user`, body, callback, errCallback);
};
static getRoles = (callback, errCallback) => { static getRoles = (callback, errCallback) => {
NetworkService.get(`${API_BASE_URL}/role`, callback, errCallback); NetworkService.get(`${API_BASE_URL}/role`, callback, errCallback);
}; };

View File

@@ -8,6 +8,7 @@ const initialStore = {
token: '', token: '',
companies: [], companies: [],
chosenCompanyId: 0, chosenCompanyId: 0,
users: [],
// bando form // bando form
formInitialData: {}, formInitialData: {},
// form builder // form builder
@@ -17,6 +18,7 @@ const initialStore = {
elementItems: [], elementItems: [],
activeElement: '', activeElement: '',
draggingElementId: 0, draggingElementId: 0,
bandoCriteria: [],
// flow // flow
flowData: [], flowData: [],
flowForms: [], flowForms: [],