Merge pull request #30 from Kitzanos/feature/46-forms-flow-preview
Feature/46 forms flow preview
This commit is contained in:
46
package.json
46
package.json
@@ -4,38 +4,38 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-private-property-in-object": "7.21.11",
|
||||
"@babel/preset-react": "7.24.7",
|
||||
"@date-fns/tz": "1.1.2",
|
||||
"@emailjs/browser": "^4.4.1",
|
||||
"@emotion/styled": "11.13.0",
|
||||
"@babel/preset-react": "7.25.9",
|
||||
"@date-fns/tz": "1.2.0",
|
||||
"@emailjs/browser": "4.4.1",
|
||||
"@emotion/styled": "11.13.5",
|
||||
"@number-flow/react": "0.4.2",
|
||||
"@sentry/browser": "^8.42.0",
|
||||
"@stomp/stompjs": "^7.0.0",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@wordpress/i18n": "5.8.0",
|
||||
"@wordpress/react-i18n": "4.8.0",
|
||||
"@xyflow/react": "12.3.1",
|
||||
"@wordpress/i18n": "5.13.0",
|
||||
"@wordpress/react-i18n": "4.13.0",
|
||||
"codice-fiscale-js": "2.3.22",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"deep-object-diff": "^1.1.9",
|
||||
"dompurify": "3.1.7",
|
||||
"copy-to-clipboard": "3.3.3",
|
||||
"deep-object-diff": "1.1.9",
|
||||
"dompurify": "3.2.2",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"hotkeys-js": "^3.13.7",
|
||||
"html-react-parser": "5.1.16",
|
||||
"hotkeys-js": "3.13.7",
|
||||
"html-react-parser": "5.1.18",
|
||||
"jwt-decode": "4.0.0",
|
||||
"klona": "2.0.6",
|
||||
"leader-line-new": "1.1.9",
|
||||
"luxon": "3.5.0",
|
||||
"object-path-immutable": "4.1.2",
|
||||
"primeicons": "7.0.0",
|
||||
"primereact": "10.8.4",
|
||||
"quill": "2.0.2",
|
||||
"primereact": "10.8.5",
|
||||
"quill": "2.0.3",
|
||||
"ramda": "0.30.1",
|
||||
"react": "18.3.1",
|
||||
"react-dnd": "16.0.1",
|
||||
"react-dnd-html5-backend": "16.0.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-hook-form": "7.53.0",
|
||||
"react-router-dom": "6.26.2",
|
||||
"react-hook-form": "7.53.2",
|
||||
"react-router-dom": "7.0.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"sockjs-client": "^1.6.1",
|
||||
"validate.js": "0.13.1",
|
||||
@@ -43,14 +43,14 @@
|
||||
"zustand-x": "3.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.25.6",
|
||||
"@babel/core": "7.25.2",
|
||||
"@babel/plugin-syntax-jsx": "7.24.7",
|
||||
"@wordpress/babel-plugin-makepot": "6.8.0",
|
||||
"@babel/cli": "7.25.9",
|
||||
"@babel/core": "7.26.0",
|
||||
"@babel/plugin-syntax-jsx": "7.25.9",
|
||||
"@wordpress/babel-plugin-makepot": "6.13.0",
|
||||
"babel-plugin-macros": "3.1.0",
|
||||
"node-wp-i18n": "^1.2.7",
|
||||
"sass": "1.79.3",
|
||||
"sass-loader": "16.0.2"
|
||||
"node-wp-i18n": "1.2.7",
|
||||
"sass": "1.81.0",
|
||||
"sass-loader": "16.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "GENERATE_SOURCEMAP=false react-scripts start",
|
||||
|
||||
@@ -33,3 +33,135 @@
|
||||
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%;
|
||||
}
|
||||
|
||||
.p-password.p-inputwrapper {
|
||||
width: 100%;
|
||||
|
||||
> div, input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.p-inputgroup.flex-1 {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -73,3 +73,9 @@
|
||||
margin-right: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.topBar__endContent {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
--message-warning-color: #cc8925;
|
||||
--message-info-background: rgba(183, 183, 183, 0.7);
|
||||
--message-info-color: #3B82F6;
|
||||
--panel-content-borderColor: #E5E7EB;
|
||||
|
||||
--card-full-background-color-2: #EEC137;
|
||||
--card-full-background-color-3: #FA8E42;
|
||||
|
||||
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 Fileupload from './components/Fileupload';
|
||||
import Table from './components/Table';
|
||||
import PasswordField from './components/PasswordField';
|
||||
|
||||
const FormField = (props) => {
|
||||
const fields = {
|
||||
@@ -31,7 +32,8 @@ const FormField = (props) => {
|
||||
radio: Radio,
|
||||
wysiwyg: Wysiwyg,
|
||||
checkboxes: Checkboxes,
|
||||
table: Table
|
||||
table: Table,
|
||||
password: PasswordField
|
||||
}
|
||||
const Comp = !isNil(fields[props.type]) ? fields[props.type] : null;
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { isEmpty, head } from 'ramda';
|
||||
import { isEmpty, head, pathOr } from 'ramda';
|
||||
|
||||
// store
|
||||
import { storeGet, storeSet, useStore } from '../../store';
|
||||
import { storeSet } from '../../store';
|
||||
|
||||
// api
|
||||
import FormsService from '../../service/forms-service';
|
||||
import FlowService from '../../service/flow-service';
|
||||
|
||||
// tools
|
||||
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
||||
@@ -15,27 +16,33 @@ import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
|
||||
// components
|
||||
import { Button } from 'primereact/button';
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
import FlowBuilder from '../../components/FlowBuilder';
|
||||
import { Messages } from 'primereact/messages';
|
||||
import FlowService from '../../service/flow-service';
|
||||
import { confirmPopup, ConfirmPopup } from 'primereact/confirmpopup';
|
||||
import { Toast } from 'primereact/toast';
|
||||
|
||||
const BandoFlowEdit = () => {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const forms = useStore().main.flowForms();
|
||||
const flowData = useStore().main.flowData();
|
||||
const flowEdges = useStore().main.flowEdges();
|
||||
|
||||
const [flowStructure, setFlowStructure] = useState({
|
||||
initialForm: 0,
|
||||
finalForm: 0,
|
||||
flowData: [],
|
||||
flowEdges: [],
|
||||
chosenField: ''
|
||||
});
|
||||
|
||||
const [forms, setForms] = useState([]);
|
||||
const [formOptions, setFormOptions] = useState([]);
|
||||
const [initialForm, setInitialForm] = useState(0);
|
||||
const [mainFieldOptions, setMainFieldOptions] = useState([]);
|
||||
const [mainField, setMainField] = useState('');
|
||||
const [chosenMainFieldOptions, setChosenMainFieldOptions] = useState([]);
|
||||
//const [chosenMainField, setChosenMainField] = useState('');
|
||||
const [mainFieldSuboptions, setMainFieldSubOptions] = useState([]);
|
||||
const [bandoStatus, setBandoStatus] = useState('');
|
||||
const [isFlowAllowed, setIsFlowAllowed] = useState(false);
|
||||
const [finalForm, setFinalForm] = useState(0);
|
||||
const [isFlowAllowed, setIsFlowAllowed] = useState(true);
|
||||
const flowMsgs = useRef(null);
|
||||
const toast = useRef(null);
|
||||
const itemRefs = useRef({});
|
||||
const itemContainerRef = useRef(null);
|
||||
|
||||
const getBandoId = () => {
|
||||
const parsed = parseInt(id)
|
||||
@@ -56,7 +63,8 @@ const BandoFlowEdit = () => {
|
||||
defaultFocus: 'reject',
|
||||
acceptClassName: 'p-button-danger',
|
||||
accept: doDelete,
|
||||
reject: () => {}
|
||||
reject: () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -64,46 +72,120 @@ const BandoFlowEdit = () => {
|
||||
if (flowMsgs.current) {
|
||||
flowMsgs.current.clear();
|
||||
}
|
||||
storeSet.main.flowData([]);
|
||||
storeSet.main.flowEdges([]);
|
||||
setInitialForm(0);
|
||||
setMainFieldOptions([]);
|
||||
setMainField('');
|
||||
|
||||
setFlowStructure({
|
||||
initialForm: 0,
|
||||
finalForm: 0,
|
||||
flowData: [],
|
||||
flowEdges: [],
|
||||
chosenField: ''
|
||||
})
|
||||
|
||||
setIsFlowAllowed(false);
|
||||
setFinalForm(0);
|
||||
setChosenMainFieldOptions([]);
|
||||
}
|
||||
|
||||
const updateInitialForm = (value) => {
|
||||
setInitialForm(value);
|
||||
if (forms.length === 2) {
|
||||
const finalForm = head(forms.filter(o => o.id !== value));
|
||||
if (finalForm) {
|
||||
setFinalForm(finalForm.id);
|
||||
const updateInitialForm = useCallback((value) => {
|
||||
const finalFormObj = head(forms.filter(o => o.id !== value));
|
||||
|
||||
if (forms.length === 2 && finalFormObj) {
|
||||
setFlowStructure({
|
||||
...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 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
|
||||
? isEmpty(flowData) || isEmpty(flowEdges) || isEmpty(initialForm) || isEmpty(finalForm)
|
||||
|| flowData.length < forms.length - 1 || 'PUBLISH' === bandoStatus
|
||||
: isEmpty(flowEdges) || isEmpty(initialForm) || 'PUBLISH' === bandoStatus;
|
||||
}, [flowData, flowEdges]);
|
||||
? nonEmptyFlowItems.length !== forms.length - 2 || isEmpty(flowStructure.flowEdges) || 'PUBLISH' === bandoStatus
|
||||
|| isEmpty(flowStructure.initialForm) || isEmpty(flowStructure.finalForm)
|
||||
: nonEmptyFlowItems.length !== 1 || isEmpty(flowStructure.flowEdges) || 'PUBLISH' === bandoStatus
|
||||
|| isEmpty(flowStructure.initialForm) || isEmpty(flowStructure.finalForm);
|
||||
}, [flowStructure, forms]);
|
||||
|
||||
const doSave = () => {
|
||||
storeSet.main.setAsyncRequest();
|
||||
const bandoId = getBandoId();
|
||||
const body = {
|
||||
initialForm,
|
||||
finalForm,
|
||||
flowData,
|
||||
flowEdges
|
||||
};
|
||||
|
||||
if (flowMsgs.current) {
|
||||
flowMsgs.current.clear();
|
||||
}
|
||||
|
||||
FlowService.createFlow(bandoId, body, getFlowCreateCallback, errGetFlowCreateCallback);
|
||||
FlowService.createFlow(bandoId, flowStructure, getFlowCreateCallback, errGetFlowCreateCallback);
|
||||
}
|
||||
|
||||
const getFlowCreateCallback = (data) => {
|
||||
@@ -126,12 +208,13 @@ const BandoFlowEdit = () => {
|
||||
|
||||
const getFormsCallback = (data) => {
|
||||
if (data.status === 'SUCCESS') {
|
||||
setForms(data.data);
|
||||
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();
|
||||
|
||||
storeSet.main.setAsyncRequest();
|
||||
FlowService.getFlow(bandoId, getFlowCallback, errGetFlowCallback);
|
||||
FlowService.getFlow(bandoId, (resp) => getFlowCallback(resp, data.data), errGetFlowCallback);
|
||||
}
|
||||
storeSet.main.unsetAsyncRequest();
|
||||
}
|
||||
@@ -141,21 +224,42 @@ const BandoFlowEdit = () => {
|
||||
storeSet.main.unsetAsyncRequest();
|
||||
}
|
||||
|
||||
const getFlowCallback = (data) => {
|
||||
const getFlowCallback = (data, forms) => {
|
||||
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)));
|
||||
if (chosenFieldItem) {
|
||||
setMainField(chosenFieldItem.chosenField);
|
||||
}
|
||||
const flowDataItem = head(data.data.flowData.filter(o => !isEmpty(o.chosenField)));
|
||||
setBandoStatus(data.data.callStatus);
|
||||
|
||||
if (flowDataItem) {
|
||||
setMainField(flowDataItem.chosenField);
|
||||
if (chosenFieldItem) {
|
||||
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();
|
||||
@@ -166,71 +270,48 @@ const BandoFlowEdit = () => {
|
||||
storeSet.main.unsetAsyncRequest();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const flowForms = storeGet.main.flowForms();
|
||||
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 = [];
|
||||
const setItemRef = (id, element) => {
|
||||
itemRefs.current[id] = element;
|
||||
};
|
||||
|
||||
if (field) {
|
||||
options = head(field.settings.filter(o => o.name === 'options'));
|
||||
}
|
||||
const buildFlowEdges = (initialForm, finalForm) => {
|
||||
let edges = [];
|
||||
|
||||
if (field && options.value && options.value.length === flowForms.length - 2) {
|
||||
setIsFlowAllowed(true);
|
||||
const data = {
|
||||
formId: String(initialForm),
|
||||
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 (!isEmpty(initialForm) && !isEmpty(finalForm)) {
|
||||
// eslint-disable-next-line
|
||||
forms.map(o => {
|
||||
const formId = String(o.id);
|
||||
|
||||
if (flowForms.length - 2 === 1) {
|
||||
msg = 'Non è possibile creare il flusso. Il campo principale deve avere esattamente %s opzione.';
|
||||
}
|
||||
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 (flowMsgs.current && !isEmpty(mainField)) {
|
||||
flowMsgs.current.clear();
|
||||
flowMsgs.current.show([
|
||||
{
|
||||
id: '1',
|
||||
sticky: true, severity: 'error', summary: '',
|
||||
detail: sprintf(
|
||||
__(msg, 'gepafin'),
|
||||
flowForms.length - 2
|
||||
),
|
||||
closable: false
|
||||
}
|
||||
]);
|
||||
if (forms.length === 2 && initialForm && finalForm) {
|
||||
edges.push({
|
||||
id: `${initialForm}->${finalForm}`,
|
||||
source: String(initialForm),
|
||||
target: String(finalForm),
|
||||
type: 'smoothstep'
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [mainField]);
|
||||
|
||||
useEffect(() => {
|
||||
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]);
|
||||
return edges;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const bandoId = getBandoId();
|
||||
@@ -251,23 +332,98 @@ const BandoFlowEdit = () => {
|
||||
]);
|
||||
} else {
|
||||
flowMsgs.current.clear();
|
||||
if (itemContainerRef.current) {
|
||||
itemContainerRef.current.dispatchEvent(new Event('scroll'));
|
||||
}
|
||||
}
|
||||
}, [forms]);
|
||||
|
||||
useEffect(() => {
|
||||
const chosenFieldItem = head(flowData.filter(o => !isEmpty(o.chosenField)));
|
||||
if (chosenFieldItem) {
|
||||
setMainField(chosenFieldItem.chosenField);
|
||||
}
|
||||
}, [flowData])
|
||||
const initialForm = flowStructure.initialForm;
|
||||
const finalForm = flowStructure.finalForm;
|
||||
const chosenField = flowStructure.chosenField;
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
storeSet.main.flowForms([]);
|
||||
storeSet.main.flowData([]);
|
||||
storeSet.main.flowEdges([]);
|
||||
if (!isEmpty(initialForm) && !isEmpty(finalForm)) {
|
||||
const form = head(forms.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 };
|
||||
})
|
||||
: [];
|
||||
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 (
|
||||
<div className="appPage">
|
||||
@@ -296,29 +452,29 @@ const BandoFlowEdit = () => {
|
||||
placeholder={__('Scegli il form', 'gepafin')}/>
|
||||
</div>
|
||||
|
||||
{forms.length > 2 && initialForm && mainFieldOptions
|
||||
{forms.length > 2 && initialForm && chosenMainFieldOptions
|
||||
? <div className="appForm__field">
|
||||
<label htmlFor="mainField">{__('Scegli il campo principale', 'gepafin')}</label>
|
||||
<label htmlFor="chosenMainField">{__('Scegli il campo principale', 'gepafin')}</label>
|
||||
<Dropdown
|
||||
id="mainField"
|
||||
id="chosenMainField"
|
||||
disabled={'PUBLISH' === bandoStatus}
|
||||
value={mainField}
|
||||
onChange={(e) => setMainField(e.value)}
|
||||
value={chosenField}
|
||||
onChange={(e) => updateChosenField(e.value)}
|
||||
optionDisabled={(opt) => isEmpty(opt.value)}
|
||||
options={mainFieldOptions}
|
||||
options={chosenMainFieldOptions}
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder={__('Scegli il campo', 'gepafin')}/>
|
||||
</div> : null}
|
||||
|
||||
{(forms.length > 2 && mainField && isFlowAllowed) || (forms.length === 2 && isFlowAllowed)
|
||||
{(forms.length > 2 && chosenField && isFlowAllowed) || (forms.length === 2 && isFlowAllowed)
|
||||
? <div className="appForm__field">
|
||||
<label htmlFor="finalForm">{__('Scegli form finale', 'gepafin')}</label>
|
||||
<Dropdown
|
||||
id="finalForm"
|
||||
disabled={'PUBLISH' === bandoStatus}
|
||||
value={finalForm}
|
||||
onChange={(e) => setFinalForm(e.value)}
|
||||
onChange={(e) => updateFinalForm(e.value)}
|
||||
optionDisabled={(opt) => initialForm === opt.value || isEmpty(opt.value)}
|
||||
options={formOptions}
|
||||
optionLabel="label"
|
||||
@@ -343,11 +499,67 @@ const BandoFlowEdit = () => {
|
||||
|
||||
<div className="appPageSection">
|
||||
<Messages ref={flowMsgs}/>
|
||||
{forms.length >= 2 && isFlowAllowed
|
||||
? <FlowBuilder
|
||||
initialForm={initialForm}
|
||||
finalForm={finalForm}
|
||||
mainField={mainField}/> : null}
|
||||
|
||||
{forms.length >= 2 && initialForm && finalForm && isFlowAllowed
|
||||
? <div className="flowContainer" ref={itemContainerRef}>
|
||||
<div className="flowContainerInner">
|
||||
<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 className="appPage__spacer"></div>
|
||||
|
||||
@@ -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 { useForm } from 'react-hook-form';
|
||||
import { classNames } from 'primereact/utils';
|
||||
import { isEmpty } from 'ramda';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { isEmpty, isNil } from 'ramda';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
|
||||
// tools
|
||||
import AuthenticationService from '../../service/authentication-service';
|
||||
@@ -24,7 +24,9 @@ const ResetPassword = () => {
|
||||
const token = useStore().main.token();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [resetPassToken, setResetPassToken] = useState('');
|
||||
const [resetPassEmail, setResetPassEmail] = useState('');
|
||||
const errorMsgs = useRef(null);
|
||||
let [searchParams] = useSearchParams();
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
@@ -47,12 +49,33 @@ const ResetPassword = () => {
|
||||
}
|
||||
|
||||
if (request.token && !isEmpty(request.token)) {
|
||||
AuthenticationService.resetPassword(request, getCallback, errCallback);
|
||||
AuthenticationService.resetPassword(request, getCallbackReset, errCallback);
|
||||
} else {
|
||||
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) => {
|
||||
if (data.status === 'SUCCESS') {
|
||||
setResetPassToken(data.data)
|
||||
@@ -87,9 +110,18 @@ const ResetPassword = () => {
|
||||
}, [token]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue('token', resetPassToken);
|
||||
console.log(resetPassToken, resetPassEmail);
|
||||
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 (
|
||||
<div className={classNames(['appPage', 'appPageLogin'])}>
|
||||
@@ -113,7 +145,7 @@ const ResetPassword = () => {
|
||||
placeholder="sample@example.com"
|
||||
/>
|
||||
|
||||
{!isEmpty(resetPassToken)
|
||||
{resetPassToken && !isEmpty(resetPassToken)
|
||||
? <input
|
||||
type="hidden"
|
||||
name="token"
|
||||
@@ -122,10 +154,9 @@ const ResetPassword = () => {
|
||||
})}
|
||||
/> : null}
|
||||
|
||||
{!isEmpty(resetPassToken)
|
||||
{resetPassToken && !isEmpty(resetPassToken)
|
||||
? <FormField
|
||||
type="textinput"
|
||||
inputtype="password"
|
||||
type="password"
|
||||
fieldName="newPassword"
|
||||
label={__('Password', 'gepafin')}
|
||||
control={control}
|
||||
@@ -138,9 +169,9 @@ const ResetPassword = () => {
|
||||
}}
|
||||
/> : null}
|
||||
|
||||
{!isEmpty(resetPassToken)
|
||||
{resetPassToken && !isEmpty(resetPassToken)
|
||||
? <FormField
|
||||
type="textinput"
|
||||
type="password"
|
||||
inputtype="password"
|
||||
fieldName="confirmPassword"
|
||||
label={__('Conferma password', 'gepafin')}
|
||||
|
||||
@@ -43,16 +43,5 @@ export const actionsBeta = (set, get, api) => ({
|
||||
const newElements = newFields.toSpliced(hoverIndex, 0, prevFields[dragIndex]);
|
||||
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