updated form fields and application logic;

This commit is contained in:
Vitalii Kiiko
2024-09-12 17:17:48 +02:00
parent 19e17ec2d7
commit a8471ba7aa
42 changed files with 1423 additions and 231 deletions

View File

@@ -0,0 +1,8 @@
import React from 'react';
const BlockingOverlay = ({ shouldDisplay = false }) => {
return shouldDisplay
? <div className="blockingOverlay"></div> : null;
}
export default BlockingOverlay;

View File

@@ -22,13 +22,15 @@ const NodeInitialForm = ({ data: { id, label = '' } }) => {
useEffect(() => {
const forms = storeGet.main.flowForms();
const form = head(forms.filter(o => String(o.id) === String(id)))
const relevantFields = form
? form.content
.filter(o => ['radio', 'select'].includes(o.name))
.map(o => ({ name: o.id, label: o.label }))
: [];
setOptions(relevantFields);
if (forms.length > 2) {
const form = head(forms.filter(o => String(o.id) === String(id)))
const relevantFields = form
? form.content
.filter(o => ['radio', 'select'].includes(o.name))
.map(o => ({ name: o.id, label: o.label }))
: [];
setOptions(relevantFields);
}
}, [id]);
return (

View File

@@ -55,11 +55,10 @@ const FlowBuilder = ({ initialForm = 0, finalForm = 0 }) => {
id: formId,
type: 'output',
data: { label: o.label, id: formId },
position: { x: 0, y: 300 },
position: { x: 0, y: forms.length === 2 ? 150 : 300 },
}
} else {
const x = coordinates.splice(0, 1);
console.log('x', x)
obj = {
id: formId,
type: 'intermediateForm',
@@ -78,11 +77,15 @@ const FlowBuilder = ({ initialForm = 0, finalForm = 0 }) => {
if (formId !== String(initialForm) && formId !== String(finalForm)) {
edges.push({ id: `${initialForm}->${formId}`, source: String(initialForm), target: formId, type: 'smoothstep' });
}
if (formId !== String(finalForm) && formId !== String(initialForm) && String(finalForm) !== '0') {
if (formId !== String(initialForm) && formId !== String(finalForm) && String(finalForm) !== '0') {
edges.push({ id: `${formId}->${finalForm}`, source: formId, target: String(finalForm), type: 'smoothstep' });
}
});
console.log('edges', edges, initialNodes);
if (forms.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);

View File

@@ -0,0 +1,57 @@
import React, { useState, useCallback } from 'react';
import { classNames } from 'primereact/utils';
import { Controller } from 'react-hook-form';
import { Checkbox } from 'primereact/checkbox';
const Checkboxes = ({
fieldName,
label,
control,
errors,
defaultValue = [],
config = {},
infoText = null,
options = []
}) => {
const [fieldVal, setFieldVal] = useState(defaultValue);
const onCheckboxesChange = useCallback((e, updateFn) => {
let data = [...fieldVal];
if (e.checked) {
data.push(e.value);
} else {
data.splice(data.indexOf(e.value), 1);
}
setFieldVal(data);
updateFn(data);
}, [fieldVal]);
const input = <Controller
name={fieldName}
control={control}
defaultValue={fieldVal}
rules={config}
render={({ field, fieldState }) =>
options.map(o => <div className="appForm__fieldItem" key={o.name}>
<Checkbox
inputId={`${fieldName}_${o.name}`}
name={fieldName}
value={o.name}
onChange={(e) => onCheckboxesChange(e, field.onChange)}
checked={field.value.includes(o.name)}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
<label htmlFor={`${fieldName}_${o.name}`}>{o.label}</label>
</div>)}/>
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
</label>
{input}
{infoText ? <small>{infoText}</small> : null}
</>)
}
export default Checkboxes;

View File

@@ -18,7 +18,7 @@ const Datepicker = ({
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
{label}{config.required || config.isRequired ? '*' : null}
</label>
<Controller
name={fieldName}

View File

@@ -15,8 +15,8 @@ const NumberInput = ({
inputgroup = false,
icon = null,
locale = 'it-IT',
minFractionDigits = 2,
step = 1,
minFractionDigits = 0,
maxFractionDigits = 1,
min,
max
}) => {
@@ -31,13 +31,14 @@ const NumberInput = ({
onValueChange={(e) => field.onChange(e.value)}
min={min}
max={max}
locale={locale} minFractionDigits={minFractionDigits} step={step}
locale={locale}
minFractionDigits={minFractionDigits}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
{label}{config.required || config.isRequired ? '*' : null}
</label>
{inputgroup
? <div className="p-inputgroup flex-1">

View File

@@ -4,34 +4,35 @@ import { Controller } from 'react-hook-form';
import { RadioButton } from 'primereact/radiobutton';
const Radio = ({
fieldName,
label,
control,
errors,
defaultValue,
config = {},
infoText = null,
options = []
}) => {
fieldName,
label,
control,
errors,
defaultValue,
config = {},
infoText = null,
options = []
}) => {
const input = <Controller
name={fieldName}
control={control}
defaultValue={defaultValue}
rules={config}
render={({ field, fieldState }) =>
options.map(o => <div className="appForm__fieldItem" key={o.name}>
<RadioButton
id={`${fieldName}_${o.name}`}
name={fieldName}
value={o.name}
onChange={(e) => field.onChange(e.value)}
checked={field.value === o.name}/>
<label htmlFor={`${fieldName}_${o.name}`}>{o.label}</label>
</div>)}/>
options.map(o => <div className="appForm__fieldItem" key={o.name}>
<RadioButton
inputId={`${fieldName}_${o.name}`}
name={fieldName}
value={o.name}
onChange={(e) => field.onChange(e.value)}
checked={field.value === o.name}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
<label htmlFor={`${fieldName}_${o.name}`}>{o.label}</label>
</div>)}/>
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
{label}{config.required || config.isRequired ? '*' : null}
</label>
{input}
{infoText ? <small>{infoText}</small> : null}

View File

@@ -27,13 +27,14 @@ const Select = ({
onChange={(e) => field.onChange(e.value)}
options={options}
optionLabel="label"
optionValue="name"
placeholder={placeholder}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
{label}{config.required || config.isRequired ? '*' : null}
</label>
{inputgroup
? <div className="p-inputgroup flex-1">

View File

@@ -34,7 +34,7 @@ const Switch = ({
<>
<div className="appForm__row">
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] }, 'mr-8')}>
{label}{config.required ? '*' : null}
{label}{config.required || config.isRequired ? '*' : null}
</label>
<div className="appForm__row">
{offLabel ? <span>{offLabel}</span> : null}

View File

@@ -16,7 +16,7 @@ const TextArea = ({
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
{label}{config.required || config.isRequired ? '*' : null}
</label>
<Controller
name={fieldName}

View File

@@ -31,7 +31,7 @@ const TextInput = ({
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
{label}{config.required || config.isRequired ? '*' : null}
</label>
{inputgroup
? <div className="p-inputgroup flex-1">

View File

@@ -0,0 +1,54 @@
import React from 'react';
import { classNames } from 'primereact/utils';
import { Controller } from 'react-hook-form';
import { Editor } from 'primereact/editor';
const Wysiwyg = ({
fieldName,
label,
control,
rows = 3,
errors,
defaultValue,
config = {},
infoText = null
}) => {
const renderHeader = () => {
return (
<span className="ql-formats">
<button className="ql-bold" aria-label="Bold"></button>
<button className="ql-italic" aria-label="Italic"></button>
<button className="ql-underline" aria-label="Underline"></button>
<button className="ql-link" aria-label="Link"></button>
</span>
);
};
const header = renderHeader();
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
</label>
<Controller
name={fieldName}
control={control}
defaultValue={defaultValue}
rules={config}
render={({ field, fieldState }) => (
<Editor
id={field.name}
{...field}
headerTemplate={header}
onTextChange={(e) => field.onChange(e.htmlValue)}
style={{ height: 80 * rows }}
className={classNames({ 'p-invalid': fieldState.invalid })}
/>
)}/>
{infoText ? <small>{infoText}</small> : null}
</>)
}
export default Wysiwyg;

View File

@@ -12,6 +12,8 @@ import NumberInput from './components/NumberInput';
import Switch from './components/Switch';
import Select from './components/Select';
import Radio from './components/Radio';
import Wysiwyg from './components/Wysiwyg';
import Checkboxes from './components/Checkboxes';
const FormField = (props) => {
const fields = {
@@ -23,7 +25,9 @@ const FormField = (props) => {
numberinput: NumberInput,
switch: Switch,
select: Select,
radio: Radio
radio: Radio,
wysiwyg: Wysiwyg,
checkboxes: Checkboxes
}
const Comp = !isNil(fields[props.type]) ? fields[props.type] : null;

View File

@@ -3,6 +3,7 @@ import { classNames } from 'primereact/utils';
import { __ } from '@wordpress/i18n';
import { head, isEmpty, isNil, pluck } from 'ramda';
import { diff } from 'deep-object-diff';
import { klona } from 'klona';
// components
import { InputText } from 'primereact/inputtext';
@@ -97,8 +98,10 @@ const FormFieldRepeater = ({
const storeFieldData = data ?? [];
setStateFieldData(storeFieldData);
setStateOptionsData(prevState => {
const ids = pluck('id', storeFieldData)
const objectsToAdd = storeFieldData.filter(o => ids.includes(o.id));
const ids = pluck('lookUpDataId', prevState)
const objectsToAdd = klona(storeFieldData)
.filter(o => !ids.includes(o.lookUpDataId))
.map(o => ({...o, id: null}));
return [...prevState, ...objectsToAdd];
});
}
@@ -120,7 +123,7 @@ const FormFieldRepeater = ({
{stateFieldData.map((o, i) => <div key={i} className={classNames('appForm__repeaterItem')}>
<div className="p-inputgroup flex-1">
{properField(o, i)}
<Button icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
<Button type="button" icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
</div>
{isNil(o.lookUpDataId) && infoText ? <small>{infoText}</small> : null}
</div>)}

View File

@@ -1,7 +1,7 @@
import React, { useRef, useEffect, useState, useCallback } from 'react';
import { classNames } from 'primereact/utils';
import { __ } from '@wordpress/i18n';
import { head, isNil, pluck } from 'ramda';
import { head, isEmpty, isNil, pluck } from 'ramda';
// components
import { InputText } from 'primereact/inputtext';
@@ -9,6 +9,8 @@ import { Button } from 'primereact/button';
import { Menu } from 'primereact/menu';
import { Dropdown } from 'primereact/dropdown';
import { InputNumber } from 'primereact/inputnumber';
import { diff } from 'deep-object-diff';
import { klona } from 'klona';
const FormFieldRepeaterCriteria = ({
data,
@@ -99,6 +101,22 @@ const FormFieldRepeaterCriteria = ({
})
}, []);
useEffect(() => {
const diffData = diff(data[fieldName], stateFieldData);
if (!isEmpty(diffData)) {
const storeFieldData = data[fieldName] ?? [];
setStateFieldData(storeFieldData);
setStateOptionsData(prevState => {
const ids = pluck('lookUpDataId', prevState)
const objectsToAdd = klona(storeFieldData)
.filter(o => !ids.includes(o.lookUpDataId))
.map(o => ({...o, id: null, score: 0}));
return [...prevState, ...objectsToAdd];
});
}
}, [data]);
useEffect(() => {
setStateOptionsData([...options]);
}, [options]);
@@ -126,7 +144,7 @@ const FormFieldRepeaterCriteria = ({
<label>{__('Nome criterio di valutazione', 'gepafin')}</label>
<div className="p-inputgroup flex-1">
{properField(o, i)}
<Button icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
<Button type="button" icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
</div>
{isNil(o.lookUpDataId) && infoText ? <small>{infoText}</small> : null}
</div>