Merge pull request #31 from Kitzanos/master-sync/14-01-2024
Master sync/14 01 2024
This commit is contained in:
53
package.json
53
package.json
@@ -4,53 +4,54 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "7.21.11",
|
||||||
"@babel/preset-react": "7.24.7",
|
"@babel/preset-react": "7.25.9",
|
||||||
"@date-fns/tz": "1.1.2",
|
"@date-fns/tz": "1.2.0",
|
||||||
"@emailjs/browser": "^4.4.1",
|
"@emailjs/browser": "4.4.1",
|
||||||
"@emotion/styled": "11.13.0",
|
"@emotion/styled": "11.13.5",
|
||||||
"@number-flow/react": "0.4.2",
|
"@number-flow/react": "0.4.2",
|
||||||
"@sentry/browser": "^8.42.0",
|
"@sentry/browser": "8.42.0",
|
||||||
"@stomp/stompjs": "^7.0.0",
|
"@stomp/stompjs": "7.0.0",
|
||||||
"@tanstack/react-table": "^8.20.5",
|
"@tanstack/react-table": "8.20.5",
|
||||||
"@wordpress/i18n": "5.8.0",
|
"@wordpress/i18n": "5.13.0",
|
||||||
"@wordpress/react-i18n": "4.8.0",
|
"@wordpress/react-i18n": "4.13.0",
|
||||||
"@xyflow/react": "12.3.1",
|
|
||||||
"codice-fiscale-js": "2.3.22",
|
"codice-fiscale-js": "2.3.22",
|
||||||
"copy-to-clipboard": "^3.3.3",
|
"copy-to-clipboard": "3.3.3",
|
||||||
"deep-object-diff": "^1.1.9",
|
"deep-object-diff": "1.1.9",
|
||||||
"dompurify": "3.1.7",
|
"dompurify": "3.2.2",
|
||||||
"fast-deep-equal": "3.1.3",
|
"fast-deep-equal": "3.1.3",
|
||||||
"hotkeys-js": "^3.13.7",
|
"hotkeys-js": "3.13.7",
|
||||||
"html-react-parser": "5.1.16",
|
"html-react-parser": "5.1.18",
|
||||||
"jwt-decode": "4.0.0",
|
"jwt-decode": "4.0.0",
|
||||||
"klona": "2.0.6",
|
"klona": "2.0.6",
|
||||||
|
"leader-line-new": "1.1.9",
|
||||||
"luxon": "3.5.0",
|
"luxon": "3.5.0",
|
||||||
"object-path-immutable": "4.1.2",
|
"object-path-immutable": "4.1.2",
|
||||||
"primeicons": "7.0.0",
|
"primeicons": "7.0.0",
|
||||||
"primereact": "10.8.4",
|
"primereact": "10.8.5",
|
||||||
"quill": "2.0.2",
|
"quill": "2.0.3",
|
||||||
"ramda": "0.30.1",
|
"ramda": "0.30.1",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-dnd": "16.0.1",
|
"react-dnd": "16.0.1",
|
||||||
"react-dnd-html5-backend": "16.0.1",
|
"react-dnd-html5-backend": "16.0.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
"react-hook-form": "7.53.0",
|
"react-hook-form": "7.53.2",
|
||||||
"react-router-dom": "6.26.2",
|
"react-router-dom": "7.0.1",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"recharts": "2.15.0",
|
||||||
"sockjs-client": "^1.6.1",
|
"sockjs-client": "^1.6.1",
|
||||||
"validate.js": "0.13.1",
|
"validate.js": "0.13.1",
|
||||||
"zustand": "4.5.4",
|
"zustand": "4.5.4",
|
||||||
"zustand-x": "3.0.4"
|
"zustand-x": "3.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.25.6",
|
"@babel/cli": "7.25.9",
|
||||||
"@babel/core": "7.25.2",
|
"@babel/core": "7.26.0",
|
||||||
"@babel/plugin-syntax-jsx": "7.24.7",
|
"@babel/plugin-syntax-jsx": "7.25.9",
|
||||||
"@wordpress/babel-plugin-makepot": "6.8.0",
|
"@wordpress/babel-plugin-makepot": "6.13.0",
|
||||||
"babel-plugin-macros": "3.1.0",
|
"babel-plugin-macros": "3.1.0",
|
||||||
"node-wp-i18n": "^1.2.7",
|
"node-wp-i18n": "1.2.7",
|
||||||
"sass": "1.79.3",
|
"sass": "1.81.0",
|
||||||
"sass-loader": "16.0.2"
|
"sass-loader": "16.0.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "GENERATE_SOURCEMAP=false react-scripts start",
|
"start": "GENERATE_SOURCEMAP=false react-scripts start",
|
||||||
|
|||||||
39
src/assets/scss/components/charts.scss
Normal file
39
src/assets/scss/components/charts.scss
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
.chartCard {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
min-height: 220px;
|
||||||
|
padding: 20px 5px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #EAB308;
|
||||||
|
background: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chartCard__title {
|
||||||
|
color: var(--global-textColor);
|
||||||
|
font-size: 18px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: normal;
|
||||||
|
margin: 0 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chartCard__chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 24rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chartCard__tooltip {
|
||||||
|
padding: 5px 10px;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid var(--global-textColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chartCard__tooltipTitle {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chartCard__tooltipText {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -32,4 +32,136 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.flowContainer {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowContainerInner {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: max-content;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowContainer__level {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
/*min-height: 240px;*/
|
||||||
|
/*margin: 0 auto;*/
|
||||||
|
|
||||||
|
&.initialLevel {
|
||||||
|
padding: 0 0 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.intermediateLevel, &.finalLevel {
|
||||||
|
padding: 30px 0 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.intermediateLevel {
|
||||||
|
border-bottom: 1px solid var(--table-border-color);
|
||||||
|
border-top: 1px solid var(--table-border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowContainer__flowItem {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 280px;
|
||||||
|
min-width: 280px;
|
||||||
|
padding: 15px;
|
||||||
|
border: 1px solid var(--table-border-color);
|
||||||
|
z-index: 9;
|
||||||
|
|
||||||
|
.flowContainer__flowItemInner > label {
|
||||||
|
border-color: #757575;
|
||||||
|
background-color: #757575;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.initialForm, &.finalForm {
|
||||||
|
.flowContainer__flowItemInner > label {
|
||||||
|
border-color: var(--card-full-background-color-3);
|
||||||
|
background-color: var(--card-full-background-color-3);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.levelForms, &.initialForm {
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -31px;
|
||||||
|
left: 50%;
|
||||||
|
content: '';
|
||||||
|
width: 1px;
|
||||||
|
height: 31px;
|
||||||
|
background-color: var(--table-border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.levelForms, &.finalForm {
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
top: -31px;
|
||||||
|
left: 50%;
|
||||||
|
content: '';
|
||||||
|
width: 1px;
|
||||||
|
height: 31px;
|
||||||
|
background-color: var(--table-border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowContainer__levelMaskStart, .flowContainer__levelMaskEnd {
|
||||||
|
position: absolute;
|
||||||
|
width: 140px;
|
||||||
|
height: 100% ;
|
||||||
|
top: -1px;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
.flowContainer__levelMaskStart {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.flowContainer__levelMaskEnd {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowContainer__flowItemInner {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 250px;
|
||||||
|
border: 1px solid var(--panel-content-borderColor);
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
> label {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 18px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowContainer__flowItemContent {
|
||||||
|
padding: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.appForm__field {
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -147,6 +147,14 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.p-password.p-inputwrapper {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
> div, input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.p-inputgroup.flex-1 {
|
.p-inputgroup.flex-1 {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,4 +72,10 @@
|
|||||||
i {
|
i {
|
||||||
margin-right: 7px;
|
margin-right: 7px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.topBar__endContent {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
--message-warning-color: #cc8925;
|
--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;
|
||||||
|
--panel-content-borderColor: #E5E7EB;
|
||||||
|
|
||||||
--card-full-background-color-2: #EEC137;
|
--card-full-background-color-2: #EEC137;
|
||||||
--card-full-background-color-3: #FA8E42;
|
--card-full-background-color-3: #FA8E42;
|
||||||
@@ -49,3 +50,4 @@
|
|||||||
@import "./components/evaluation.scss";
|
@import "./components/evaluation.scss";
|
||||||
@import "./components/fieldsRepeater.scss";
|
@import "./components/fieldsRepeater.scss";
|
||||||
@import "./components/notificationsSidebar.scss";
|
@import "./components/notificationsSidebar.scss";
|
||||||
|
@import "./components/charts.scss";
|
||||||
|
|||||||
60
src/components/ChartDomandePerBando/index.js
Normal file
60
src/components/ChartDomandePerBando/index.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
|
||||||
|
import { isEmpty } from 'ramda';
|
||||||
|
|
||||||
|
// components
|
||||||
|
|
||||||
|
|
||||||
|
const ChartDomandePerBando = ({ title, data = [] }) => {
|
||||||
|
const truncateText = (text) => {
|
||||||
|
const maxLength = 12;
|
||||||
|
if (typeof text === 'string' && text.length > maxLength) {
|
||||||
|
return `${text.slice(0, maxLength)}...`;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Custom tooltip
|
||||||
|
const CustomTooltip = ({ active, payload, label }) => {
|
||||||
|
if (active && payload && payload.length) {
|
||||||
|
return (
|
||||||
|
<div className="chartCard__tooltip">
|
||||||
|
<p className="chartCard__tooltipTitle">{label}</p>
|
||||||
|
<p className="chartCard__tooltipText">
|
||||||
|
{__('Domande', 'gepafin')}: {payload[0].value}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (<div className="chartCard">
|
||||||
|
{title ? <span className="chartCard__title">{title}</span> : null}
|
||||||
|
{data && !isEmpty(data)
|
||||||
|
? <div className="chartCard__chart">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<BarChart
|
||||||
|
data={data}
|
||||||
|
margin={{
|
||||||
|
top: 20,
|
||||||
|
right: 30,
|
||||||
|
left: 20,
|
||||||
|
bottom: 60,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CartesianGrid strokeDasharray="3 3"/>
|
||||||
|
<XAxis dataKey="callName" angle={-45} textAnchor="end" height={120}
|
||||||
|
tickFormatter={truncateText}/>
|
||||||
|
<YAxis/>
|
||||||
|
<Tooltip content={<CustomTooltip/>}/>
|
||||||
|
<Legend/>
|
||||||
|
<Bar dataKey="numberOfApplications" fill="#EEC137" name={__('Quantità delle domande', 'gepafin')}/>
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div> : null}
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChartDomandePerBando;
|
||||||
58
src/components/ChartStatoDomande/index.js
Normal file
58
src/components/ChartStatoDomande/index.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Tooltip, ResponsiveContainer, Cell, Pie, PieChart } from 'recharts';
|
||||||
|
import { isEmpty } from 'ramda';
|
||||||
|
import getBandoLabel from '../../helpers/getBandoLabel';
|
||||||
|
|
||||||
|
// components
|
||||||
|
|
||||||
|
|
||||||
|
const ChartStatoDomande = ({ title, data = [] }) => {
|
||||||
|
const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#8884d8', '#82ca9d'];
|
||||||
|
|
||||||
|
const CustomTooltip = ({ active, payload }) => {
|
||||||
|
if (active && payload && payload.length) {
|
||||||
|
return (
|
||||||
|
<div className="chartCard__tooltip">
|
||||||
|
<p className="chartCard__tooltipTitle">{getBandoLabel(payload[0].name)}</p>
|
||||||
|
<p className="chartCard__tooltipText">
|
||||||
|
{__('Domande', 'gepafin')}: {payload[0].value}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (<div className="chartCard">
|
||||||
|
{title ? <span className="chartCard__title">{title}</span> : null}
|
||||||
|
{data && !isEmpty(data)
|
||||||
|
? <div className="chartCard__chart">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<PieChart>
|
||||||
|
<Pie
|
||||||
|
data={data}
|
||||||
|
cx="50%"
|
||||||
|
cy="50%"
|
||||||
|
labelLine={false}
|
||||||
|
label={({ percent }) => `${(percent * 100).toFixed(0)}%`}
|
||||||
|
outerRadius={120}
|
||||||
|
fill="#8884d8"
|
||||||
|
dataKey="numberOfApplications"
|
||||||
|
nameKey="status"
|
||||||
|
>
|
||||||
|
{data.map((entry, index) => (
|
||||||
|
<Cell
|
||||||
|
key={`cell-${index}`}
|
||||||
|
fill={COLORS[index % COLORS.length]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Pie>
|
||||||
|
<Tooltip content={<CustomTooltip />} />
|
||||||
|
</PieChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div> : null}
|
||||||
|
</div>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChartStatoDomande;
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { Handle, Position } from '@xyflow/react';
|
|
||||||
import { isEmpty, head } from 'ramda';
|
|
||||||
|
|
||||||
// store
|
|
||||||
import { storeGet, storeSet } from '../../../../store';
|
|
||||||
import { __ } from '@wordpress/i18n';
|
|
||||||
|
|
||||||
const NodeInitialForm = ({ data: { id, label = '' } }) => {
|
|
||||||
const flowData = storeGet.main.flowData();
|
|
||||||
const [value, setValue] = useState('');
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const flowForms = storeGet.main.flowForms();
|
|
||||||
const form = head(flowForms.filter(o => String(o.id) === String(id)));
|
|
||||||
const flowDataItem = head(flowData.filter(o => String(o.formId) === String(id)));
|
|
||||||
|
|
||||||
if (form && flowDataItem) {
|
|
||||||
const field = head(form.content.filter(o => o.id === flowDataItem.chosenField));
|
|
||||||
|
|
||||||
if (field) {
|
|
||||||
const label = head(field.settings.filter(o => o.name === 'label'));
|
|
||||||
setValue(label ? label.value : field.label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [flowData]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="nodeInitialForm">
|
|
||||||
<label>
|
|
||||||
{label}
|
|
||||||
</label>
|
|
||||||
<span>{value}</span>
|
|
||||||
{/*{options && !isEmpty(options)
|
|
||||||
? <select onChange={onChangeFn} value={value}>
|
|
||||||
<option value="">{__('Scegli il campo', 'gepafin')}</option>
|
|
||||||
{options.map(o => <option key={o.name} value={o.name}>
|
|
||||||
{o.label}
|
|
||||||
</option>)}
|
|
||||||
</select> : null}*/}
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Bottom}
|
|
||||||
isConnectable={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NodeInitialForm;
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { Handle, Position } from '@xyflow/react';
|
|
||||||
import { head, isEmpty } from 'ramda';
|
|
||||||
import { __ } from '@wordpress/i18n';
|
|
||||||
|
|
||||||
import { useStore, storeSet, storeGet } from '../../../../store';
|
|
||||||
|
|
||||||
const NodeIntermediateForm = ({ data: { id, label = '' } }) => {
|
|
||||||
const flowEdges = useStore().main.flowEdges();
|
|
||||||
const flowData = useStore().main.flowData();
|
|
||||||
const [options, setOptions] = useState([]);
|
|
||||||
const [value, setValue] = useState('');
|
|
||||||
|
|
||||||
const onChangeFn = (e) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
const data = {
|
|
||||||
formId: String(id),
|
|
||||||
chosenField: '',
|
|
||||||
chosenValue: value
|
|
||||||
}
|
|
||||||
setValue(value);
|
|
||||||
storeSet.main.addFlowData(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const edge = head(flowEdges.filter(o => o.target === String(id)));
|
|
||||||
if (edge) {
|
|
||||||
const sourceForm = edge.source;
|
|
||||||
const sourceFormData = head(flowData.filter(o => String(o.formId) === sourceForm));
|
|
||||||
const flowForms = storeGet.main.flowForms();
|
|
||||||
const form = head(flowForms.filter(o => String(o.id) === String(sourceForm)));
|
|
||||||
|
|
||||||
if (form && sourceFormData) {
|
|
||||||
const { chosenField } = sourceFormData;
|
|
||||||
const field = head(form.content.filter(o => o.id === chosenField));
|
|
||||||
if (field) {
|
|
||||||
const options = head(field.settings.filter(o => o.name === 'options'));
|
|
||||||
if (options) {
|
|
||||||
setOptions(options.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const flowDataForm = head(flowData.filter(o => String(o.formId) === String(id)));
|
|
||||||
|
|
||||||
if (flowDataForm) {
|
|
||||||
setValue(flowDataForm.chosenValue);
|
|
||||||
}
|
|
||||||
}, [flowEdges, flowData]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="nodeIntermediateForm">
|
|
||||||
<Handle
|
|
||||||
type="target"
|
|
||||||
position={Position.Top}
|
|
||||||
isConnectable={true}
|
|
||||||
/>
|
|
||||||
<label>
|
|
||||||
{label}
|
|
||||||
</label>
|
|
||||||
{options && !isEmpty(options)
|
|
||||||
? <select onChange={onChangeFn} value={value}>
|
|
||||||
<option value="">{__('Scegli il valore', 'gepafin')}</option>
|
|
||||||
{options.map(o => <option key={o.name} value={o.name}>
|
|
||||||
{o.label}
|
|
||||||
</option>)}
|
|
||||||
</select> : null}
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={Position.Bottom}
|
|
||||||
isConnectable={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NodeIntermediateForm;
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import {
|
|
||||||
ReactFlow,
|
|
||||||
Background
|
|
||||||
} from '@xyflow/react';
|
|
||||||
import { isEmpty } from 'ramda';
|
|
||||||
|
|
||||||
import '@xyflow/react/dist/style.css';
|
|
||||||
|
|
||||||
// store
|
|
||||||
import { useStore, storeSet } from '../../store';
|
|
||||||
|
|
||||||
// nodes
|
|
||||||
import NodeInitialForm from './components/NodeInitialForm';
|
|
||||||
import NodeIntermediateForm from './components/NodeIntermediateForm';
|
|
||||||
|
|
||||||
const nodeTypes = {
|
|
||||||
initialForm: NodeInitialForm,
|
|
||||||
intermediateForm: NodeIntermediateForm
|
|
||||||
};
|
|
||||||
|
|
||||||
const FlowBuilder = ({ initialForm = 0, finalForm = 0, mainField = '' }) => {
|
|
||||||
const flowForms = useStore().main.flowForms();
|
|
||||||
const [nodes, setNodes] = useState([]);
|
|
||||||
const [edges, setEdges] = useState([]);
|
|
||||||
|
|
||||||
const range = (start, stop, step) => {
|
|
||||||
return Array.from(
|
|
||||||
{ length: (stop - start) / step + 1 },
|
|
||||||
(_, i) => start + i * step
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
(flowForms.length === 2 && initialForm) ||
|
|
||||||
(flowForms.length > 2 && initialForm && finalForm)
|
|
||||||
) {
|
|
||||||
const total = (flowForms.length - 2) * (200 - 90);
|
|
||||||
let coordinates = range(total * -1, total, 200);
|
|
||||||
|
|
||||||
const initialNodes = flowForms.map(o => {
|
|
||||||
const formId = String(o.id);
|
|
||||||
let obj;
|
|
||||||
|
|
||||||
if (formId === String(initialForm)) {
|
|
||||||
obj = {
|
|
||||||
id: formId,
|
|
||||||
type: 'initialForm',
|
|
||||||
data: { label: o.label, id: formId },
|
|
||||||
position: { x: 0, y: 0 },
|
|
||||||
}
|
|
||||||
} else if (formId === String(finalForm)) {
|
|
||||||
obj = {
|
|
||||||
id: formId,
|
|
||||||
type: 'output',
|
|
||||||
data: { label: o.label, id: formId },
|
|
||||||
position: { x: 0, y: flowForms.length === 2 ? 150 : 300 },
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const x = coordinates.splice(0, 1);
|
|
||||||
obj = {
|
|
||||||
id: formId,
|
|
||||||
type: 'intermediateForm',
|
|
||||||
data: { label: o.label, id: formId },
|
|
||||||
position: { x, y: 150 },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj
|
|
||||||
});
|
|
||||||
|
|
||||||
let edges = [];
|
|
||||||
// eslint-disable-next-line
|
|
||||||
flowForms.map(o => {
|
|
||||||
const formId = String(o.id);
|
|
||||||
|
|
||||||
if (formId !== String(initialForm) && formId !== String(finalForm)) {
|
|
||||||
edges.push({
|
|
||||||
id: `${initialForm}->${formId}`,
|
|
||||||
source: String(initialForm),
|
|
||||||
target: formId,
|
|
||||||
type: 'smoothstep'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (formId !== String(initialForm) && formId !== String(finalForm) && String(finalForm) !== '0') {
|
|
||||||
edges.push({
|
|
||||||
id: `${formId}->${finalForm}`,
|
|
||||||
source: formId,
|
|
||||||
target: String(finalForm),
|
|
||||||
type: 'smoothstep'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (flowForms.length === 2 && initialForm && finalForm) {
|
|
||||||
edges.push({
|
|
||||||
id: `${initialForm}->${finalForm}`,
|
|
||||||
source: String(initialForm),
|
|
||||||
target: String(finalForm),
|
|
||||||
type: 'smoothstep'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setNodes(initialNodes);
|
|
||||||
setEdges(edges);
|
|
||||||
storeSet.main.flowEdges(edges);
|
|
||||||
} else {
|
|
||||||
setNodes([]);
|
|
||||||
setEdges([]);
|
|
||||||
}
|
|
||||||
}, [initialForm, finalForm, flowForms, mainField]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
!isEmpty(nodes) && !isEmpty(edges)
|
|
||||||
? <div className="flowBuilder__wrapper">
|
|
||||||
<ReactFlow
|
|
||||||
nodes={nodes}
|
|
||||||
edges={edges}
|
|
||||||
nodesDraggable={false}
|
|
||||||
nodesConnectable={false}
|
|
||||||
fitView
|
|
||||||
nodeTypes={nodeTypes}
|
|
||||||
attributionPosition="top-right"
|
|
||||||
>
|
|
||||||
<Background variant="dots" gap={12} size={1}/>
|
|
||||||
</ReactFlow>
|
|
||||||
</div>
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FlowBuilder;
|
|
||||||
54
src/components/FormField/components/PasswordField/index.js
Normal file
54
src/components/FormField/components/PasswordField/index.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { classNames } from 'primereact/utils';
|
||||||
|
import { Controller } from 'react-hook-form';
|
||||||
|
import { Password } from 'primereact/password';
|
||||||
|
|
||||||
|
const PasswordField = ({
|
||||||
|
fieldName,
|
||||||
|
label,
|
||||||
|
control,
|
||||||
|
errors,
|
||||||
|
defaultValue,
|
||||||
|
config = {},
|
||||||
|
infoText = null,
|
||||||
|
inputgroup = false,
|
||||||
|
icon = null,
|
||||||
|
placeholder = '',
|
||||||
|
disabled = false,
|
||||||
|
onBlurFn = () => {
|
||||||
|
}
|
||||||
|
}) => {
|
||||||
|
const input = <Controller
|
||||||
|
name={fieldName}
|
||||||
|
control={control}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
rules={config}
|
||||||
|
render={({ field, fieldState }) => (
|
||||||
|
<Password
|
||||||
|
id={field.name}
|
||||||
|
disabled={disabled}
|
||||||
|
{...field}
|
||||||
|
value={field.value ? field.value : ''}
|
||||||
|
onBlur={onBlurFn}
|
||||||
|
placeholder={placeholder}
|
||||||
|
className={classNames({ 'p-invalid': fieldState.invalid })}
|
||||||
|
toggleMask />
|
||||||
|
)}/>
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
|
||||||
|
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
|
||||||
|
</label>
|
||||||
|
{inputgroup
|
||||||
|
? <div className="p-inputgroup">
|
||||||
|
<span className="p-inputgroup-addon">
|
||||||
|
{icon}
|
||||||
|
</span>
|
||||||
|
{input}
|
||||||
|
</div>
|
||||||
|
: input}
|
||||||
|
{infoText ? <small>{infoText}</small> : null}
|
||||||
|
</>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PasswordField;
|
||||||
@@ -16,6 +16,7 @@ import Wysiwyg from './components/Wysiwyg';
|
|||||||
import Checkboxes from './components/Checkboxes';
|
import Checkboxes from './components/Checkboxes';
|
||||||
import Fileupload from './components/Fileupload';
|
import Fileupload from './components/Fileupload';
|
||||||
import Table from './components/Table';
|
import Table from './components/Table';
|
||||||
|
import PasswordField from './components/PasswordField';
|
||||||
|
|
||||||
const FormField = (props) => {
|
const FormField = (props) => {
|
||||||
const fields = {
|
const fields = {
|
||||||
@@ -31,7 +32,8 @@ const FormField = (props) => {
|
|||||||
radio: Radio,
|
radio: Radio,
|
||||||
wysiwyg: Wysiwyg,
|
wysiwyg: Wysiwyg,
|
||||||
checkboxes: Checkboxes,
|
checkboxes: Checkboxes,
|
||||||
table: Table
|
table: Table,
|
||||||
|
password: PasswordField
|
||||||
}
|
}
|
||||||
const Comp = !isNil(fields[props.type]) ? fields[props.type] : null;
|
const Comp = !isNil(fields[props.type]) ? fields[props.type] : null;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const dynamicDataForTextinput = [
|
|||||||
{ label: 'ragione sociale', value: 'company.companyName' },
|
{ label: 'ragione sociale', value: 'company.companyName' },
|
||||||
{ label: 'partita IVA', value: 'company.vatNumber' },
|
{ label: 'partita IVA', value: 'company.vatNumber' },
|
||||||
{ label: 'codice fiscale azienda', value: 'company.codiceFiscale' },
|
{ label: 'codice fiscale azienda', value: 'company.codiceFiscale' },
|
||||||
|
{ label: 'codice ateco', value: 'company.codiceAteco' },
|
||||||
{ label: 'indirizzo', value: 'company.address' },
|
{ label: 'indirizzo', value: 'company.address' },
|
||||||
{ label: 'numero di telefono azienda', value: 'company.phoneNumber' },
|
{ label: 'numero di telefono azienda', value: 'company.phoneNumber' },
|
||||||
{ label: 'città', value: 'company.city' },
|
{ label: 'città', value: 'company.city' },
|
||||||
|
|||||||
@@ -64,11 +64,25 @@ const AppSidebar = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: __('Archivio domande', 'gepafin'),
|
label: __('Archivio domande', 'gepafin'),
|
||||||
icon: 'pi pi-file',
|
icon: 'pi pi-briefcase',
|
||||||
href: '/domande',
|
href: '/domande',
|
||||||
id: 7,
|
id: 7,
|
||||||
enable: intersection(permissions, ['APPLY_CALLS']).length
|
enable: intersection(permissions, ['APPLY_CALLS']).length
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: __('Archivio domande', 'gepafin'),
|
||||||
|
icon: 'pi pi-briefcase',
|
||||||
|
href: '/domande-archivio',
|
||||||
|
id: 5,
|
||||||
|
enable: intersection(permissions, ['VIEW_USERS', 'MANAGE_USERS']).length
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Archivio domande', 'gepafin'),
|
||||||
|
icon: 'pi pi-briefcase',
|
||||||
|
href: '/domande-archivio',
|
||||||
|
id: 6,
|
||||||
|
enable: intersection(permissions, ['EVALUATE_APPLICATIONS']).length
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: __('Soccorso istruttorio', 'gepafin'),
|
label: __('Soccorso istruttorio', 'gepafin'),
|
||||||
icon: <HelpIcon/>,
|
icon: <HelpIcon/>,
|
||||||
|
|||||||
@@ -312,7 +312,8 @@ const BandoApplication = () => {
|
|||||||
dynamicData = Object.keys(company).reduce((acc, cur) => {
|
dynamicData = Object.keys(company).reduce((acc, cur) => {
|
||||||
if ([
|
if ([
|
||||||
'companyName', 'vatNumber', 'codiceFiscale', 'address', 'phoneNumber',
|
'companyName', 'vatNumber', 'codiceFiscale', 'address', 'phoneNumber',
|
||||||
'city', 'province', 'cap', 'country', 'pec', 'email', 'contactName', 'contactEmail'
|
'city', 'province', 'cap', 'country', 'pec', 'email', 'contactName', 'contactEmail',
|
||||||
|
'codiceAteco'
|
||||||
].includes(cur)) {
|
].includes(cur)) {
|
||||||
acc.company[cur] = company[cur];
|
acc.company[cur] = company[cur];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
||||||
import { __, sprintf } from '@wordpress/i18n';
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { isEmpty, head } from 'ramda';
|
import { isEmpty, head, pathOr } from 'ramda';
|
||||||
|
|
||||||
// store
|
// store
|
||||||
import { storeGet, storeSet, useStore } from '../../store';
|
import { storeSet } from '../../store';
|
||||||
|
|
||||||
// api
|
// api
|
||||||
import FormsService from '../../service/forms-service';
|
import FormsService from '../../service/forms-service';
|
||||||
|
import FlowService from '../../service/flow-service';
|
||||||
|
|
||||||
// tools
|
// tools
|
||||||
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
||||||
@@ -15,27 +16,33 @@ import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
|||||||
// components
|
// components
|
||||||
import { Button } from 'primereact/button';
|
import { Button } from 'primereact/button';
|
||||||
import { Dropdown } from 'primereact/dropdown';
|
import { Dropdown } from 'primereact/dropdown';
|
||||||
import FlowBuilder from '../../components/FlowBuilder';
|
|
||||||
import { Messages } from 'primereact/messages';
|
import { Messages } from 'primereact/messages';
|
||||||
import FlowService from '../../service/flow-service';
|
|
||||||
import { confirmPopup, ConfirmPopup } from 'primereact/confirmpopup';
|
import { confirmPopup, ConfirmPopup } from 'primereact/confirmpopup';
|
||||||
import { Toast } from 'primereact/toast';
|
import { Toast } from 'primereact/toast';
|
||||||
|
|
||||||
const BandoFlowEdit = () => {
|
const BandoFlowEdit = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const forms = useStore().main.flowForms();
|
|
||||||
const flowData = useStore().main.flowData();
|
const [flowStructure, setFlowStructure] = useState({
|
||||||
const flowEdges = useStore().main.flowEdges();
|
initialForm: 0,
|
||||||
|
finalForm: 0,
|
||||||
|
flowData: [],
|
||||||
|
flowEdges: [],
|
||||||
|
chosenField: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const [forms, setForms] = useState([]);
|
||||||
const [formOptions, setFormOptions] = useState([]);
|
const [formOptions, setFormOptions] = useState([]);
|
||||||
const [initialForm, setInitialForm] = useState(0);
|
const [chosenMainFieldOptions, setChosenMainFieldOptions] = useState([]);
|
||||||
const [mainFieldOptions, setMainFieldOptions] = useState([]);
|
//const [chosenMainField, setChosenMainField] = useState('');
|
||||||
const [mainField, setMainField] = useState('');
|
const [mainFieldSuboptions, setMainFieldSubOptions] = useState([]);
|
||||||
const [bandoStatus, setBandoStatus] = useState('');
|
const [bandoStatus, setBandoStatus] = useState('');
|
||||||
const [isFlowAllowed, setIsFlowAllowed] = useState(false);
|
const [isFlowAllowed, setIsFlowAllowed] = useState(true);
|
||||||
const [finalForm, setFinalForm] = useState(0);
|
|
||||||
const flowMsgs = useRef(null);
|
const flowMsgs = useRef(null);
|
||||||
const toast = useRef(null);
|
const toast = useRef(null);
|
||||||
|
const itemRefs = useRef({});
|
||||||
|
const itemContainerRef = useRef(null);
|
||||||
|
|
||||||
const getBandoId = () => {
|
const getBandoId = () => {
|
||||||
const parsed = parseInt(id)
|
const parsed = parseInt(id)
|
||||||
@@ -56,7 +63,8 @@ const BandoFlowEdit = () => {
|
|||||||
defaultFocus: 'reject',
|
defaultFocus: 'reject',
|
||||||
acceptClassName: 'p-button-danger',
|
acceptClassName: 'p-button-danger',
|
||||||
accept: doDelete,
|
accept: doDelete,
|
||||||
reject: () => {}
|
reject: () => {
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,46 +72,120 @@ const BandoFlowEdit = () => {
|
|||||||
if (flowMsgs.current) {
|
if (flowMsgs.current) {
|
||||||
flowMsgs.current.clear();
|
flowMsgs.current.clear();
|
||||||
}
|
}
|
||||||
storeSet.main.flowData([]);
|
|
||||||
storeSet.main.flowEdges([]);
|
setFlowStructure({
|
||||||
setInitialForm(0);
|
initialForm: 0,
|
||||||
setMainFieldOptions([]);
|
finalForm: 0,
|
||||||
setMainField('');
|
flowData: [],
|
||||||
|
flowEdges: [],
|
||||||
|
chosenField: ''
|
||||||
|
})
|
||||||
|
|
||||||
setIsFlowAllowed(false);
|
setIsFlowAllowed(false);
|
||||||
setFinalForm(0);
|
setChosenMainFieldOptions([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateInitialForm = (value) => {
|
const updateInitialForm = useCallback((value) => {
|
||||||
setInitialForm(value);
|
const finalFormObj = head(forms.filter(o => o.id !== value));
|
||||||
if (forms.length === 2) {
|
|
||||||
const finalForm = head(forms.filter(o => o.id !== value));
|
if (forms.length === 2 && finalFormObj) {
|
||||||
if (finalForm) {
|
setFlowStructure({
|
||||||
setFinalForm(finalForm.id);
|
...flowStructure,
|
||||||
|
initialForm: value,
|
||||||
|
finalForm: finalFormObj.id
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setFlowStructure({
|
||||||
|
...flowStructure,
|
||||||
|
initialForm: value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [flowStructure])
|
||||||
|
|
||||||
|
const updateFinalForm = useCallback((value) => {
|
||||||
|
const filtered = flowStructure.flowData.filter(o => o.formId === flowStructure.initialForm);
|
||||||
|
const flowEdges = buildFlowEdges(flowStructure.initialForm, value);
|
||||||
|
|
||||||
|
setFlowStructure({
|
||||||
|
...flowStructure,
|
||||||
|
flowEdges,
|
||||||
|
flowData: filtered,
|
||||||
|
finalForm: value
|
||||||
|
});
|
||||||
|
}, [flowStructure]);
|
||||||
|
|
||||||
|
const updateChosenField = useCallback((value) => {
|
||||||
|
setFlowStructure({
|
||||||
|
...flowStructure,
|
||||||
|
chosenField: value
|
||||||
|
});
|
||||||
|
}, [flowStructure]);
|
||||||
|
|
||||||
|
const addFlowData = useCallback((data) => {
|
||||||
|
const initial = flowStructure.flowData;
|
||||||
|
const exists = initial ? initial.filter(o => parseInt(o.formId) === parseInt(data.formId)) : [];
|
||||||
|
let final = [];
|
||||||
|
|
||||||
|
if (exists.length) {
|
||||||
|
final = initial.map(o => parseInt(o.formId) === parseInt(data.formId) ? data : o);
|
||||||
|
} else {
|
||||||
|
final = [...initial, data];
|
||||||
|
}
|
||||||
|
|
||||||
|
setFlowStructure({
|
||||||
|
...flowStructure,
|
||||||
|
flowData: final
|
||||||
|
});
|
||||||
|
}, [flowStructure]);
|
||||||
|
|
||||||
|
const updateItermediateForm = (value, formId) => {
|
||||||
|
const isUsed = flowStructure.flowData.map(o => o.chosenValue).filter(v => !isEmpty(v)).includes(value);
|
||||||
|
if (!isUsed) {
|
||||||
|
const data = {
|
||||||
|
formId: parseInt(formId),
|
||||||
|
chosenField: '',
|
||||||
|
chosenValue: value
|
||||||
}
|
}
|
||||||
|
addFlowData(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const displayChosenOptionValue = (id) => {
|
||||||
|
const suboptionId = pathOr('', ['chosenValue'], head(flowStructure.flowData.filter(f => parseInt(f.formId) === parseInt(id))));
|
||||||
|
return pathOr('', ['label'], head(mainFieldSuboptions.filter(o => o.name === suboptionId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const disabledOptionForIntermediateForm = (opt) => {
|
||||||
|
return flowStructure.flowData.map(o => o.chosenValue).filter(v => !isEmpty(v)).includes(opt.name);
|
||||||
|
}
|
||||||
|
|
||||||
const shoudDisableSaving = useCallback(() => {
|
const shoudDisableSaving = useCallback(() => {
|
||||||
|
const nonEmptyFlowItems = flowStructure.flowData.filter(o => isEmpty(o.chosenField)).filter(o => !isEmpty(o.chosenValue));
|
||||||
|
|
||||||
|
/*if (flowForms.length > 2) {
|
||||||
|
console.log('disable BTN:', nonEmptyFlowItems.length !== flowForms.length - 2, isEmpty(flowEdges), 'PUBLISH' === bandoStatus,
|
||||||
|
isEmpty(initialForm), isEmpty(finalForm));
|
||||||
|
} else {
|
||||||
|
console.log('disable BTN:', nonEmptyFlowItems.length !== 1, isEmpty(flowEdges), 'PUBLISH' === bandoStatus,
|
||||||
|
isEmpty(initialForm), isEmpty(finalForm));
|
||||||
|
}*/
|
||||||
|
|
||||||
return forms.length > 2
|
return forms.length > 2
|
||||||
? isEmpty(flowData) || isEmpty(flowEdges) || isEmpty(initialForm) || isEmpty(finalForm)
|
? nonEmptyFlowItems.length !== forms.length - 2 || isEmpty(flowStructure.flowEdges) || 'PUBLISH' === bandoStatus
|
||||||
|| flowData.length < forms.length - 1 || 'PUBLISH' === bandoStatus
|
|| isEmpty(flowStructure.initialForm) || isEmpty(flowStructure.finalForm)
|
||||||
: isEmpty(flowEdges) || isEmpty(initialForm) || 'PUBLISH' === bandoStatus;
|
: nonEmptyFlowItems.length !== 1 || isEmpty(flowStructure.flowEdges) || 'PUBLISH' === bandoStatus
|
||||||
}, [flowData, flowEdges]);
|
|| isEmpty(flowStructure.initialForm) || isEmpty(flowStructure.finalForm);
|
||||||
|
}, [flowStructure, forms]);
|
||||||
|
|
||||||
const doSave = () => {
|
const doSave = () => {
|
||||||
storeSet.main.setAsyncRequest();
|
storeSet.main.setAsyncRequest();
|
||||||
const bandoId = getBandoId();
|
const bandoId = getBandoId();
|
||||||
const body = {
|
|
||||||
initialForm,
|
|
||||||
finalForm,
|
|
||||||
flowData,
|
|
||||||
flowEdges
|
|
||||||
};
|
|
||||||
if (flowMsgs.current) {
|
if (flowMsgs.current) {
|
||||||
flowMsgs.current.clear();
|
flowMsgs.current.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
FlowService.createFlow(bandoId, body, getFlowCreateCallback, errGetFlowCreateCallback);
|
FlowService.createFlow(bandoId, flowStructure, getFlowCreateCallback, errGetFlowCreateCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFlowCreateCallback = (data) => {
|
const getFlowCreateCallback = (data) => {
|
||||||
@@ -126,12 +208,13 @@ const BandoFlowEdit = () => {
|
|||||||
|
|
||||||
const getFormsCallback = (data) => {
|
const getFormsCallback = (data) => {
|
||||||
if (data.status === 'SUCCESS') {
|
if (data.status === 'SUCCESS') {
|
||||||
|
setForms(data.data);
|
||||||
const formOptions = data.data.map(o => ({ label: o.label, value: o.id }))
|
const formOptions = data.data.map(o => ({ label: o.label, value: o.id }))
|
||||||
storeSet.main.flowForms(data.data);
|
setFormOptions([{ label: '', value: '' }, ...formOptions]);
|
||||||
setFormOptions([{label: '', value: ''}, ...formOptions]);
|
|
||||||
const bandoId = getBandoId();
|
const bandoId = getBandoId();
|
||||||
|
|
||||||
storeSet.main.setAsyncRequest();
|
storeSet.main.setAsyncRequest();
|
||||||
FlowService.getFlow(bandoId, getFlowCallback, errGetFlowCallback);
|
FlowService.getFlow(bandoId, (resp) => getFlowCallback(resp, data.data), errGetFlowCallback);
|
||||||
}
|
}
|
||||||
storeSet.main.unsetAsyncRequest();
|
storeSet.main.unsetAsyncRequest();
|
||||||
}
|
}
|
||||||
@@ -141,21 +224,42 @@ const BandoFlowEdit = () => {
|
|||||||
storeSet.main.unsetAsyncRequest();
|
storeSet.main.unsetAsyncRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFlowCallback = (data) => {
|
const getFlowCallback = (data, forms) => {
|
||||||
if (data.status === 'SUCCESS' && data.data) {
|
if (data.status === 'SUCCESS' && data.data) {
|
||||||
storeSet.main.flowData(data.data.flowData);
|
|
||||||
storeSet.main.flowEdges(data.data.flowEdges);
|
|
||||||
setInitialForm(data.data.initialForm);
|
|
||||||
setFinalForm(data.data.finalForm);
|
|
||||||
setBandoStatus(data.data.callStatus);
|
|
||||||
const chosenFieldItem = head(data.data.flowData.filter(o => !isEmpty(o.chosenField)));
|
const chosenFieldItem = head(data.data.flowData.filter(o => !isEmpty(o.chosenField)));
|
||||||
if (chosenFieldItem) {
|
setBandoStatus(data.data.callStatus);
|
||||||
setMainField(chosenFieldItem.chosenField);
|
|
||||||
}
|
|
||||||
const flowDataItem = head(data.data.flowData.filter(o => !isEmpty(o.chosenField)));
|
|
||||||
|
|
||||||
if (flowDataItem) {
|
if (chosenFieldItem) {
|
||||||
setMainField(flowDataItem.chosenField);
|
setFlowStructure({
|
||||||
|
initialForm: data.data.initialForm,
|
||||||
|
finalForm: data.data.finalForm,
|
||||||
|
flowData: data.data.flowData,
|
||||||
|
flowEdges: data.data.flowEdges,
|
||||||
|
chosenField: chosenFieldItem.chosenField
|
||||||
|
});
|
||||||
|
const form = head(forms.filter(o => o.id === data.data.initialForm));
|
||||||
|
const relevantFields = form
|
||||||
|
? form.content
|
||||||
|
.filter(o => ['radio', 'select'].includes(o.name))
|
||||||
|
.map(o => {
|
||||||
|
const label = head(o.settings.filter(o => o.name === 'label'));
|
||||||
|
return { value: o.id, label: label ? label.value : o.label };
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
setChosenMainFieldOptions(relevantFields);
|
||||||
|
const field = form ? head(form.content.filter(o => o.id === chosenFieldItem.chosenField)) : null;
|
||||||
|
if (field) {
|
||||||
|
const options = head(field.settings.filter(o => o.name === 'options'));
|
||||||
|
setMainFieldSubOptions(options.value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setFlowStructure({
|
||||||
|
initialForm: data.data.initialForm,
|
||||||
|
finalForm: data.data.finalForm,
|
||||||
|
flowData: data.data.flowData,
|
||||||
|
flowEdges: data.data.flowEdges,
|
||||||
|
chosenField: ''
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
storeSet.main.unsetAsyncRequest();
|
storeSet.main.unsetAsyncRequest();
|
||||||
@@ -166,71 +270,48 @@ const BandoFlowEdit = () => {
|
|||||||
storeSet.main.unsetAsyncRequest();
|
storeSet.main.unsetAsyncRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
const setItemRef = (id, element) => {
|
||||||
const flowForms = storeGet.main.flowForms();
|
itemRefs.current[id] = element;
|
||||||
const form = head(flowForms.filter(o => String(o.id) === String(initialForm)))
|
};
|
||||||
const field = form ? head(form.content.filter(o => o.id === mainField)) : null;
|
|
||||||
let options = [];
|
|
||||||
|
|
||||||
if (field) {
|
const buildFlowEdges = (initialForm, finalForm) => {
|
||||||
options = head(field.settings.filter(o => o.name === 'options'));
|
let edges = [];
|
||||||
}
|
|
||||||
|
|
||||||
if (field && options.value && options.value.length === flowForms.length - 2) {
|
if (!isEmpty(initialForm) && !isEmpty(finalForm)) {
|
||||||
setIsFlowAllowed(true);
|
// eslint-disable-next-line
|
||||||
const data = {
|
forms.map(o => {
|
||||||
formId: String(initialForm),
|
const formId = String(o.id);
|
||||||
chosenField: mainField,
|
|
||||||
chosenValue: ''
|
|
||||||
}
|
|
||||||
storeSet.main.addFlowData(data);
|
|
||||||
} else {
|
|
||||||
setIsFlowAllowed(false);
|
|
||||||
let msg = 'Non è possibile creare il flusso. Il campo principale deve avere esattamente %s opzioni.';
|
|
||||||
|
|
||||||
if (flowForms.length - 2 === 1) {
|
if (formId !== String(initialForm) && formId !== String(finalForm)) {
|
||||||
msg = 'Non è possibile creare il flusso. Il campo principale deve avere esattamente %s opzione.';
|
edges.push({
|
||||||
}
|
id: `${initialForm}->${formId}`,
|
||||||
|
source: String(initialForm),
|
||||||
|
target: formId,
|
||||||
|
type: 'smoothstep'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (formId !== String(initialForm) && formId !== String(finalForm) && String(finalForm) !== '0') {
|
||||||
|
edges.push({
|
||||||
|
id: `${formId}->${finalForm}`,
|
||||||
|
source: formId,
|
||||||
|
target: String(finalForm),
|
||||||
|
type: 'smoothstep'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (flowMsgs.current && !isEmpty(mainField)) {
|
if (forms.length === 2 && initialForm && finalForm) {
|
||||||
flowMsgs.current.clear();
|
edges.push({
|
||||||
flowMsgs.current.show([
|
id: `${initialForm}->${finalForm}`,
|
||||||
{
|
source: String(initialForm),
|
||||||
id: '1',
|
target: String(finalForm),
|
||||||
sticky: true, severity: 'error', summary: '',
|
type: 'smoothstep'
|
||||||
detail: sprintf(
|
});
|
||||||
__(msg, 'gepafin'),
|
|
||||||
flowForms.length - 2
|
|
||||||
),
|
|
||||||
closable: false
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [mainField]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
return edges;
|
||||||
setMainField('');
|
};
|
||||||
setMainFieldOptions([]);
|
|
||||||
const flowForms = storeGet.main.flowForms();
|
|
||||||
const form = head(flowForms.filter(o => String(o.id) === String(initialForm)))
|
|
||||||
const relevantFields = form
|
|
||||||
? form.content
|
|
||||||
.filter(o => ['radio', 'select'].includes(o.name))
|
|
||||||
.map(o => {
|
|
||||||
const label = head(o.settings.filter(o => o.name === 'label'));
|
|
||||||
return { value: o.id, label: label ? label.value : o.label };
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
setMainFieldOptions([
|
|
||||||
{label: isEmpty(relevantFields) ? __('Nessun scelta', 'gepafin') : '', value: ''},
|
|
||||||
...relevantFields]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (flowForms.length === 2) {
|
|
||||||
setIsFlowAllowed(true)
|
|
||||||
}
|
|
||||||
}, [initialForm]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const bandoId = getBandoId();
|
const bandoId = getBandoId();
|
||||||
@@ -251,23 +332,98 @@ const BandoFlowEdit = () => {
|
|||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
flowMsgs.current.clear();
|
flowMsgs.current.clear();
|
||||||
|
if (itemContainerRef.current) {
|
||||||
|
itemContainerRef.current.dispatchEvent(new Event('scroll'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [forms]);
|
}, [forms]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const chosenFieldItem = head(flowData.filter(o => !isEmpty(o.chosenField)));
|
const initialForm = flowStructure.initialForm;
|
||||||
if (chosenFieldItem) {
|
const finalForm = flowStructure.finalForm;
|
||||||
setMainField(chosenFieldItem.chosenField);
|
const chosenField = flowStructure.chosenField;
|
||||||
}
|
|
||||||
}, [flowData])
|
|
||||||
|
|
||||||
useEffect(() => {
|
if (!isEmpty(initialForm) && !isEmpty(finalForm)) {
|
||||||
return () => {
|
const form = head(forms.filter(o => String(o.id) === String(initialForm)))
|
||||||
storeSet.main.flowForms([]);
|
const relevantFields = form
|
||||||
storeSet.main.flowData([]);
|
? form.content
|
||||||
storeSet.main.flowEdges([]);
|
.filter(o => ['radio', 'select'].includes(o.name))
|
||||||
|
.map(o => {
|
||||||
|
const label = head(o.settings.filter(o => o.name === 'label'));
|
||||||
|
return { value: o.id, label: label ? label.value : o.label };
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
setChosenMainFieldOptions([
|
||||||
|
{ label: isEmpty(relevantFields) ? __('Nessun scelta', 'gepafin') : '', value: '' },
|
||||||
|
...relevantFields]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (forms.length === 2) {
|
||||||
|
setIsFlowAllowed(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//const flowEdges = buildFlowEdges(initialForm, finalForm);
|
||||||
|
|
||||||
|
if (!isEmpty(chosenField)) {
|
||||||
|
const field = form ? head(form.content.filter(o => o.id === chosenField)) : null;
|
||||||
|
let options = [];
|
||||||
|
|
||||||
|
if (field) {
|
||||||
|
options = head(field.settings.filter(o => o.name === 'options'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field && options.value && options.value.length === forms.length - 2) {
|
||||||
|
setIsFlowAllowed(true);
|
||||||
|
const suboptions = [
|
||||||
|
{ label: __('Nessun scelta', 'gepafin'), name: '' },
|
||||||
|
...options.value
|
||||||
|
]
|
||||||
|
|
||||||
|
setMainFieldSubOptions(suboptions);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
formId: parseInt(initialForm),
|
||||||
|
chosenField: chosenField,
|
||||||
|
chosenValue: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
addFlowData(data);
|
||||||
|
|
||||||
|
if (flowMsgs.current && !isEmpty(chosenField)) {
|
||||||
|
flowMsgs.current.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsFlowAllowed(false);
|
||||||
|
|
||||||
|
let msg = 'Non è possibile creare il flusso. Il campo principale deve avere esattamente %s opzioni.';
|
||||||
|
|
||||||
|
if (forms.length - 2 === 1) {
|
||||||
|
msg = 'Non è possibile creare il flusso. Il campo principale deve avere esattamente %s opzioni.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flowMsgs.current && !isEmpty(chosenField)) {
|
||||||
|
flowMsgs.current.clear();
|
||||||
|
flowMsgs.current.show([
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
sticky: true, severity: 'error', summary: '',
|
||||||
|
detail: sprintf(
|
||||||
|
__(msg, 'gepafin'),
|
||||||
|
forms.length - 2
|
||||||
|
),
|
||||||
|
closable: false
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, []);
|
}, [flowStructure.initialForm, flowStructure.finalForm, flowStructure.chosenField]);
|
||||||
|
|
||||||
|
const { initialForm = 0, finalForm = 0, flowData = [], chosenField = '' } = flowStructure;
|
||||||
|
const initialFormData = head(forms.filter(o => o.id === initialForm));
|
||||||
|
const finalFormData = head(forms.filter(o => o.id === finalForm));
|
||||||
|
const levelForms = forms.filter(o => o.id !== initialForm && o.id !== finalForm);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="appPage">
|
<div className="appPage">
|
||||||
@@ -296,29 +452,29 @@ const BandoFlowEdit = () => {
|
|||||||
placeholder={__('Scegli il form', 'gepafin')}/>
|
placeholder={__('Scegli il form', 'gepafin')}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{forms.length > 2 && initialForm && mainFieldOptions
|
{forms.length > 2 && initialForm && chosenMainFieldOptions
|
||||||
? <div className="appForm__field">
|
? <div className="appForm__field">
|
||||||
<label htmlFor="mainField">{__('Scegli il campo principale', 'gepafin')}</label>
|
<label htmlFor="chosenMainField">{__('Scegli il campo principale', 'gepafin')}</label>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
id="mainField"
|
id="chosenMainField"
|
||||||
disabled={'PUBLISH' === bandoStatus}
|
disabled={'PUBLISH' === bandoStatus}
|
||||||
value={mainField}
|
value={chosenField}
|
||||||
onChange={(e) => setMainField(e.value)}
|
onChange={(e) => updateChosenField(e.value)}
|
||||||
optionDisabled={(opt) => isEmpty(opt.value)}
|
optionDisabled={(opt) => isEmpty(opt.value)}
|
||||||
options={mainFieldOptions}
|
options={chosenMainFieldOptions}
|
||||||
optionLabel="label"
|
optionLabel="label"
|
||||||
optionValue="value"
|
optionValue="value"
|
||||||
placeholder={__('Scegli il campo', 'gepafin')}/>
|
placeholder={__('Scegli il campo', 'gepafin')}/>
|
||||||
</div> : null}
|
</div> : null}
|
||||||
|
|
||||||
{(forms.length > 2 && mainField && isFlowAllowed) || (forms.length === 2 && isFlowAllowed)
|
{(forms.length > 2 && chosenField && isFlowAllowed) || (forms.length === 2 && isFlowAllowed)
|
||||||
? <div className="appForm__field">
|
? <div className="appForm__field">
|
||||||
<label htmlFor="finalForm">{__('Scegli form finale', 'gepafin')}</label>
|
<label htmlFor="finalForm">{__('Scegli form finale', 'gepafin')}</label>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
id="finalForm"
|
id="finalForm"
|
||||||
disabled={'PUBLISH' === bandoStatus}
|
disabled={'PUBLISH' === bandoStatus}
|
||||||
value={finalForm}
|
value={finalForm}
|
||||||
onChange={(e) => setFinalForm(e.value)}
|
onChange={(e) => updateFinalForm(e.value)}
|
||||||
optionDisabled={(opt) => initialForm === opt.value || isEmpty(opt.value)}
|
optionDisabled={(opt) => initialForm === opt.value || isEmpty(opt.value)}
|
||||||
options={formOptions}
|
options={formOptions}
|
||||||
optionLabel="label"
|
optionLabel="label"
|
||||||
@@ -343,11 +499,67 @@ const BandoFlowEdit = () => {
|
|||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
<Messages ref={flowMsgs}/>
|
<Messages ref={flowMsgs}/>
|
||||||
{forms.length >= 2 && isFlowAllowed
|
|
||||||
? <FlowBuilder
|
{forms.length >= 2 && initialForm && finalForm && isFlowAllowed
|
||||||
initialForm={initialForm}
|
? <div className="flowContainer" ref={itemContainerRef}>
|
||||||
finalForm={finalForm}
|
<div className="flowContainerInner">
|
||||||
mainField={mainField}/> : null}
|
<div className="flowContainer__level initialLevel">
|
||||||
|
<div className="flowContainer__flowItem initialForm"
|
||||||
|
ref={(el) => initialForm ? setItemRef(initialForm, el) : null}>
|
||||||
|
<div className="flowContainer__flowItemInner">
|
||||||
|
<label htmlFor="chosenMainField">
|
||||||
|
{initialFormData.label}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{levelForms.length && initialForm && finalForm
|
||||||
|
? <div className="flowContainer__level intermediateLevel">
|
||||||
|
{levelForms.map((o, i) => <div key={o.id}
|
||||||
|
ref={(el) => setItemRef(o.id, el)}
|
||||||
|
className="flowContainer__flowItem levelForms">
|
||||||
|
<div className="flowContainer__flowItemInner">
|
||||||
|
<label htmlFor="chosenMainField">{o.label}</label>
|
||||||
|
<div className="flowContainer__flowItemContent">
|
||||||
|
{mainFieldSuboptions && !isEmpty(mainFieldSuboptions)
|
||||||
|
? 'PUBLISH' !== bandoStatus
|
||||||
|
? <Dropdown
|
||||||
|
id="initialForm"
|
||||||
|
value={pathOr('', ['chosenValue'], head(flowData.filter(f => f.formId === parseInt(o.id))))}
|
||||||
|
onChange={(e) => updateItermediateForm(e.value, o.id)}
|
||||||
|
options={mainFieldSuboptions}
|
||||||
|
optionDisabled={disabledOptionForIntermediateForm}
|
||||||
|
optionLabel="label"
|
||||||
|
optionValue="name"
|
||||||
|
placeholder={__('Scegli il valore', 'gepafin')}/>
|
||||||
|
:
|
||||||
|
<label>{displayChosenOptionValue(o.id)}</label>
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>)}
|
||||||
|
{levelForms.length > 1
|
||||||
|
? <>
|
||||||
|
<div className="flowContainer__levelMaskEnd"></div>
|
||||||
|
<div className="flowContainer__levelMaskStart"></div>
|
||||||
|
</> : null}
|
||||||
|
</div> : null}
|
||||||
|
|
||||||
|
{forms.length >= 2 && initialForm && finalForm
|
||||||
|
? <div className="flowContainer__level finalLevel">
|
||||||
|
<div className="flowContainer__flowItem finalForm"
|
||||||
|
ref={(el) => finalForm ? setItemRef(finalForm, el) : null}>
|
||||||
|
<div className="flowContainer__flowItemInner">
|
||||||
|
<label htmlFor="chosenMainField">
|
||||||
|
{finalFormData.label}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
</div> : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
|
|||||||
@@ -4,33 +4,31 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { pathOr } from 'ramda';
|
import { pathOr } from 'ramda';
|
||||||
import NumberFlow from '@number-flow/react';
|
import NumberFlow from '@number-flow/react';
|
||||||
|
|
||||||
// store
|
|
||||||
//import { storeSet } from '../../store';
|
|
||||||
|
|
||||||
// api
|
// api
|
||||||
import DashboardService from '../../service/dashboard-service';
|
import DashboardService from '../../service/dashboard-service';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import LatestBandiTable from './components/LatestBandiTable';
|
import LatestBandiTable from './components/LatestBandiTable';
|
||||||
//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';
|
import AllDomandeTable from '../Domande/components/AllDomandeTable';
|
||||||
import DraftApplicationsTable from './components/DraftApplicationsTable';
|
import DraftApplicationsTable from './components/DraftApplicationsTable';
|
||||||
|
import ChartDomandePerBando from '../../components/ChartDomandePerBando';
|
||||||
|
import ChartStatoDomande from '../../components/ChartStatoDomande';
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [mainStats, setMainStats] = useState({});
|
const [mainStats, setMainStats] = useState({});
|
||||||
|
const [chartStats, setChartStats] = useState({});
|
||||||
|
|
||||||
const onGoToCreateNewBando = () => {
|
const onGoToCreateNewBando = () => {
|
||||||
navigate('/bandi/new');
|
navigate('/bandi/new');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*const onGoToUsers = () => {
|
const onGoToUsers = () => {
|
||||||
console.log('onGoToUsers')
|
navigate('/utenti');
|
||||||
}
|
}
|
||||||
|
|
||||||
const onGoToStats = () => {
|
/*const onGoToStats = () => {
|
||||||
console.log('onGoToStats')
|
console.log('onGoToStats')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,16 +43,18 @@ const Dashboard = () => {
|
|||||||
const getStats = (data) => {
|
const getStats = (data) => {
|
||||||
if (data.status === 'SUCCESS') {
|
if (data.status === 'SUCCESS') {
|
||||||
setMainStats(data.data.widget1);
|
setMainStats(data.data.widget1);
|
||||||
|
setChartStats(data.data.widgetBars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const errGetStats = () => {}
|
const errGetStats = () => {
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
DashboardService.getAdminStats(getStats, errGetStats);
|
DashboardService.getAdminStats(getStats, errGetStats);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return(
|
return (
|
||||||
<div className="appPage">
|
<div className="appPage">
|
||||||
<div className="appPage__pageHeader">
|
<div className="appPage__pageHeader">
|
||||||
<h1>{__('Dashboard', 'gepafin')}</h1>
|
<h1>{__('Dashboard', 'gepafin')}</h1>
|
||||||
@@ -147,20 +147,36 @@ const Dashboard = () => {
|
|||||||
|
|
||||||
<div className="appPage__spacer"></div>
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
|
{chartStats.applicationPerCall
|
||||||
|
? <div className="appPageSection">
|
||||||
|
<h2>{__('Statistiche di sistema', 'gepafin')}</h2>
|
||||||
|
<div className="appPageSection columns">
|
||||||
|
<ChartDomandePerBando
|
||||||
|
title={__('Domande per bando', 'gepafin')}
|
||||||
|
data={chartStats.applicationPerCall}/>
|
||||||
|
<ChartStatoDomande
|
||||||
|
title={__('Stato domande', 'gepafin')}
|
||||||
|
data={chartStats.applicationPerStatus}/>
|
||||||
|
</div>
|
||||||
|
</div> : null}
|
||||||
|
|
||||||
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
<div className="appPageSection__hr">
|
<div className="appPageSection__hr">
|
||||||
<span>{__('Azioni rapide', 'gepafin')}</span>
|
<span>{__('Azioni rapide', 'gepafin')}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
<div className="appPageSection">
|
<div className="appPageSection">
|
||||||
<div className="appPageSection__actions">
|
<div className="appPageSection__actions">
|
||||||
<Button
|
<Button
|
||||||
onClick={onGoToCreateNewBando}
|
onClick={onGoToCreateNewBando}
|
||||||
label={__('Crea nuovo bando', 'gepafin')} icon="pi pi-plus" iconPos="right"/>
|
label={__('Crea nuovo bando', 'gepafin')} icon="pi pi-plus" iconPos="right"/>
|
||||||
{/*<Button
|
<Button
|
||||||
disabled={true}
|
|
||||||
onClick={onGoToUsers}
|
onClick={onGoToUsers}
|
||||||
label={__('Gestione utenti', 'gepafin')} icon="pi pi-users" iconPos="right"/>
|
label={__('Gestione utenti', 'gepafin')} icon="pi pi-users" iconPos="right"/>
|
||||||
<Button
|
{/*<Button
|
||||||
disabled={true}
|
disabled={true}
|
||||||
onClick={onGoToStats}
|
onClick={onGoToStats}
|
||||||
label={__('Report mensile', 'gepafin')} icon="pi pi-chart-bar" iconPos="right"/>
|
label={__('Report mensile', 'gepafin')} icon="pi pi-chart-bar" iconPos="right"/>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ const ArchiveDocument = ({ applicationId, ndg = '', fileId = 0, docAttachmentId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AppointmentService.archiveDocument(applicationId, fileId, submitData, submitCallback, errSubmitCallback)
|
AppointmentService.archiveDocument(applicationId, fileId, submitData, submitCallback, errSubmitCallback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { is, uniq } from 'ramda';
|
||||||
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
// api
|
||||||
|
import ApplicationService from '../../../../service/application-service';
|
||||||
|
|
||||||
|
// tools
|
||||||
|
import getBandoLabel from '../../../../helpers/getBandoLabel';
|
||||||
|
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
|
||||||
|
|
||||||
|
// translation
|
||||||
|
import translationStrings from '../../../../translationStringsForComponents';
|
||||||
|
|
||||||
|
// components
|
||||||
|
import { FilterMatchMode, FilterOperator } from 'primereact/api';
|
||||||
|
import { DataTable } from 'primereact/datatable';
|
||||||
|
import { Column } from 'primereact/column';
|
||||||
|
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 AllDomandeArchiveTable = ({ updaterString = '' }) => {
|
||||||
|
const [items, setItems] = useState(null);
|
||||||
|
const [filters, setFilters] = useState(null);
|
||||||
|
const [localAsyncRequest, setLocalAsyncRequest] = useState(false);
|
||||||
|
const [statuses, setStatuses] = useState([]);
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalAsyncRequest(true);
|
||||||
|
ApplicationService.getApplications(getCallback, errGetCallbacks, [
|
||||||
|
['statuses', ['APPROVED', 'REJECTED']]
|
||||||
|
]);
|
||||||
|
}, [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 initFilters = () => {
|
||||||
|
setFilters({
|
||||||
|
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
|
||||||
|
callTitle: {
|
||||||
|
operator: FilterOperator.AND,
|
||||||
|
constraints: [{ value: null, matchMode: FilterMatchMode.STARTS_WITH }]
|
||||||
|
},
|
||||||
|
companyName: {
|
||||||
|
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 }]
|
||||||
|
},
|
||||||
|
status: { operator: FilterOperator.OR, constraints: [{ value: null, matchMode: FilterMatchMode.EQUALS }] },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderHeader = () => {
|
||||||
|
return (
|
||||||
|
<div className="appTableHeader">
|
||||||
|
<Button type="button" icon="pi pi-filter-slash" label={__('Pulisci', 'gepafin')} outlined
|
||||||
|
onClick={clearFilter}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const dateAppliedBodyTemplate = (rowData) => {
|
||||||
|
return formatDate(rowData.submissionDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusFilterTemplate = (options) => {
|
||||||
|
return <Dropdown value={options.value} options={statuses}
|
||||||
|
onChange={(e) => options.filterCallback(e.value, options.index)}
|
||||||
|
itemTemplate={statusItemTemplate} placeholder={translationStrings.selectOneLabel} className="p-column-filter"
|
||||||
|
showClear/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 statusItemTemplate = (option) => {
|
||||||
|
return <Tag value={getBandoLabel(option)} severity={getBandoSeverity(option)}/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const actionsBodyTemplate = (rowData) => {
|
||||||
|
return <div className="appPageSection__tableActions lessGap">
|
||||||
|
<Link to={`/domande-archivio/${rowData.id}/preview`}>
|
||||||
|
<Button severity="info" label={__('Anteprima', 'gepafin')} icon="pi pi-eye" size="small"
|
||||||
|
iconPos="right"/>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = renderHeader();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="appPageSection__table">
|
||||||
|
<DataTable value={items} paginator showGridlines rows={10} loading={localAsyncRequest} dataKey="id"
|
||||||
|
filters={filters} stripedRows removableSort
|
||||||
|
header={header}
|
||||||
|
emptyMessage={translationStrings.emptyMessage}
|
||||||
|
onFilter={(e) => setFilters(e.filters)}>
|
||||||
|
<Column field="id" header={__('ID domanda', 'gepafin')}
|
||||||
|
sortable filterPlaceholder={__('Cerca', 'gepafin')}
|
||||||
|
style={{ minWidth: '6rem' }}/>
|
||||||
|
<Column field="protocolNumber" header={__('Protocollo', 'gepafin')}
|
||||||
|
sortable filterPlaceholder={__('Cerca', 'gepafin')}
|
||||||
|
style={{ minWidth: '6rem' }}/>
|
||||||
|
<Column field="callTitle" header={__('Bando', 'gepafin')}
|
||||||
|
filter sortable
|
||||||
|
filterPlaceholder={__('Cerca', 'gepafin')}
|
||||||
|
style={{ minWidth: '10rem' }}/>
|
||||||
|
<Column field="companyName" header={__('Azienda Beneficiaria', 'gepafin')}
|
||||||
|
filter sortable
|
||||||
|
filterPlaceholder={__('Cerca il nome', 'gepafin')}
|
||||||
|
style={{ minWidth: '8rem' }}/>
|
||||||
|
<Column header={__('Data Ricezione', 'gepafin')}
|
||||||
|
filterField="submissionDate" dataType="date"
|
||||||
|
style={{ minWidth: '8rem' }}
|
||||||
|
body={dateAppliedBodyTemplate} filter filterElement={dateFilterTemplate}/>
|
||||||
|
<Column field="assignedUserName" header={__('Assegnato', 'gepafin')}
|
||||||
|
filter sortable
|
||||||
|
filterPlaceholder={__('Cerca il nome', 'gepafin')}
|
||||||
|
style={{ minWidth: '8rem' }}/>
|
||||||
|
<Column field="status" header={__('Stato', 'gepafin')}
|
||||||
|
style={{ minWidth: '8rem' }} body={statusBodyTemplate}
|
||||||
|
filter
|
||||||
|
filterElement={statusFilterTemplate}/>
|
||||||
|
<Column header={__('Azioni', 'gepafin')}
|
||||||
|
body={actionsBodyTemplate}/>
|
||||||
|
</DataTable>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AllDomandeArchiveTable;
|
||||||
25
src/pages/DomandeArchive/index.js
Normal file
25
src/pages/DomandeArchive/index.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
|
// components
|
||||||
|
import AllDomandeArchiveTable from './components/AllDomandeArchiveTable';
|
||||||
|
|
||||||
|
const Domande = () => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="appPage">
|
||||||
|
<div className="appPage__pageHeader">
|
||||||
|
<h1>{__('Archivio domande', 'gepafin')}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="appPage__spacer"></div>
|
||||||
|
|
||||||
|
<div className="appPageSection">
|
||||||
|
<h2>{__('Domande pubblicate', 'gepafin')}</h2>
|
||||||
|
<AllDomandeArchiveTable/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Domande;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, { useRef, useState, useEffect } from 'react';
|
import React, { useRef, useState, useEffect, useMemo } from 'react';
|
||||||
import { __, sprintf } from '@wordpress/i18n';
|
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, isNil } from 'ramda';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
// tools
|
// tools
|
||||||
import AuthenticationService from '../../service/authentication-service';
|
import AuthenticationService from '../../service/authentication-service';
|
||||||
@@ -24,7 +24,9 @@ const ResetPassword = () => {
|
|||||||
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('');
|
||||||
|
const [resetPassEmail, setResetPassEmail] = useState('');
|
||||||
const errorMsgs = useRef(null);
|
const errorMsgs = useRef(null);
|
||||||
|
let [searchParams] = useSearchParams();
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -47,12 +49,33 @@ const ResetPassword = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request.token && !isEmpty(request.token)) {
|
if (request.token && !isEmpty(request.token)) {
|
||||||
AuthenticationService.resetPassword(request, getCallback, errCallback);
|
AuthenticationService.resetPassword(request, getCallbackReset, errCallback);
|
||||||
} else {
|
} else {
|
||||||
AuthenticationService.forgotPassword(request, getCallback, errCallback);
|
AuthenticationService.forgotPassword(request, getCallback, errCallback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getCallbackReset = (data) => {
|
||||||
|
if (data.status === 'SUCCESS') {
|
||||||
|
errorMsgs.current.show([
|
||||||
|
{
|
||||||
|
sticky: true, severity: 'success', summary: '',
|
||||||
|
detail: data.message,
|
||||||
|
closable: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
errorMsgs.current.show([
|
||||||
|
{
|
||||||
|
sticky: true, severity: 'error', summary: '',
|
||||||
|
detail: data.message,
|
||||||
|
closable: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
const getCallback = (data) => {
|
const getCallback = (data) => {
|
||||||
if (data.status === 'SUCCESS') {
|
if (data.status === 'SUCCESS') {
|
||||||
setResetPassToken(data.data)
|
setResetPassToken(data.data)
|
||||||
@@ -87,9 +110,18 @@ const ResetPassword = () => {
|
|||||||
}, [token]);
|
}, [token]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue('token', resetPassToken);
|
console.log(resetPassToken, resetPassEmail);
|
||||||
reset();
|
reset();
|
||||||
}, [resetPassToken])
|
setValue('token', resetPassToken);
|
||||||
|
setValue('email', resetPassEmail);
|
||||||
|
}, [resetPassToken, resetPassEmail]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const token = searchParams.get('token');
|
||||||
|
const email = searchParams.get('email');
|
||||||
|
setResetPassToken(token);
|
||||||
|
setResetPassEmail(email);
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(['appPage', 'appPageLogin'])}>
|
<div className={classNames(['appPage', 'appPageLogin'])}>
|
||||||
@@ -113,7 +145,7 @@ const ResetPassword = () => {
|
|||||||
placeholder="sample@example.com"
|
placeholder="sample@example.com"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!isEmpty(resetPassToken)
|
{resetPassToken && !isEmpty(resetPassToken)
|
||||||
? <input
|
? <input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name="token"
|
name="token"
|
||||||
@@ -122,10 +154,9 @@ const ResetPassword = () => {
|
|||||||
})}
|
})}
|
||||||
/> : null}
|
/> : null}
|
||||||
|
|
||||||
{!isEmpty(resetPassToken)
|
{resetPassToken && !isEmpty(resetPassToken)
|
||||||
? <FormField
|
? <FormField
|
||||||
type="textinput"
|
type="password"
|
||||||
inputtype="password"
|
|
||||||
fieldName="newPassword"
|
fieldName="newPassword"
|
||||||
label={__('Password', 'gepafin')}
|
label={__('Password', 'gepafin')}
|
||||||
control={control}
|
control={control}
|
||||||
@@ -138,9 +169,9 @@ const ResetPassword = () => {
|
|||||||
}}
|
}}
|
||||||
/> : null}
|
/> : null}
|
||||||
|
|
||||||
{!isEmpty(resetPassToken)
|
{resetPassToken && !isEmpty(resetPassToken)
|
||||||
? <FormField
|
? <FormField
|
||||||
type="textinput"
|
type="password"
|
||||||
inputtype="password"
|
inputtype="password"
|
||||||
fieldName="confirmPassword"
|
fieldName="confirmPassword"
|
||||||
label={__('Conferma password', 'gepafin')}
|
label={__('Conferma password', 'gepafin')}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import BandiPreferredBeneficiario from './pages/BandiPreferredBeneficiario';
|
|||||||
import DomandeInstructorManager from './pages/DomandeInstructorManager';
|
import DomandeInstructorManager from './pages/DomandeInstructorManager';
|
||||||
import DomandaEditInstructorManager from './pages/DomandaEditInstructorManager';
|
import DomandaEditInstructorManager from './pages/DomandaEditInstructorManager';
|
||||||
import UserActivity from './pages/UserActivity';
|
import UserActivity from './pages/UserActivity';
|
||||||
|
import DomandeArchive from './pages/DomandeArchive';
|
||||||
|
|
||||||
const routes = ({ role, chosenCompanyId }) => {
|
const routes = ({ role, chosenCompanyId }) => {
|
||||||
|
|
||||||
@@ -124,6 +125,18 @@ const routes = ({ role, chosenCompanyId }) => {
|
|||||||
{'ROLE_PRE_INSTRUCTOR' === role ? <BandoApplicationPreview/> : null}
|
{'ROLE_PRE_INSTRUCTOR' === role ? <BandoApplicationPreview/> : null}
|
||||||
{'ROLE_INSTRUCTOR_MANAGER' === role ? <BandoApplicationPreview/> : null}
|
{'ROLE_INSTRUCTOR_MANAGER' === role ? <BandoApplicationPreview/> : null}
|
||||||
</DefaultLayout>}/>
|
</DefaultLayout>}/>
|
||||||
|
<Route path="/domande-archivio" element={<DefaultLayout>
|
||||||
|
{'ROLE_SUPER_ADMIN' === role ? <DomandeArchive/> : null}
|
||||||
|
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
|
||||||
|
{'ROLE_PRE_INSTRUCTOR' === role ? <DomandeArchive/> : null}
|
||||||
|
{'ROLE_INSTRUCTOR_MANAGER' === role ? <DomandeArchive/> : null}
|
||||||
|
</DefaultLayout>}/>
|
||||||
|
<Route path="/domande-archivio/:id/preview" element={<DefaultLayout>
|
||||||
|
{'ROLE_SUPER_ADMIN' === role ? <BandoApplicationPreview/> : null}
|
||||||
|
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
|
||||||
|
{'ROLE_PRE_INSTRUCTOR' === role ? <BandoApplicationPreview/> : null}
|
||||||
|
{'ROLE_INSTRUCTOR_MANAGER' === role ? <BandoApplicationPreview/> : null}
|
||||||
|
</DefaultLayout>}/>
|
||||||
<Route path="/domande/:id/aggiungi-soccorso" element={<DefaultLayout>
|
<Route path="/domande/:id/aggiungi-soccorso" element={<DefaultLayout>
|
||||||
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
|
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
|
||||||
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
|
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
|
||||||
|
|||||||
@@ -43,16 +43,5 @@ export const actionsBeta = (set, get, api) => ({
|
|||||||
const newElements = newFields.toSpliced(hoverIndex, 0, prevFields[dragIndex]);
|
const newElements = newFields.toSpliced(hoverIndex, 0, prevFields[dragIndex]);
|
||||||
set.formElements(newElements);
|
set.formElements(newElements);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
addFlowData: (data) => {
|
|
||||||
const initial = get.flowData();
|
|
||||||
const exists = initial ? initial.filter(o => parseInt(o.formId) === parseInt(data.formId)) : [];
|
|
||||||
|
|
||||||
if (exists.length) {
|
|
||||||
const newData = initial.map(o => parseInt(o.formId) === parseInt(data.formId) ? data : o);
|
|
||||||
set.flowData(newData);
|
|
||||||
} else {
|
|
||||||
set.flowData([...initial, data]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user