- added registartion page;

- implemented validation helper-functions;
- fixed form fields datepicker and datepicker range;
- updated routes logic;
- fixed FAQ items editing/submission;
This commit is contained in:
Vitalii Kiiko
2024-09-23 10:05:43 +02:00
parent cf149485e0
commit bbf117eb9b
58 changed files with 1238 additions and 392 deletions

View File

@@ -77,7 +77,7 @@
align-items: center;
}
.appForm__oneCol {
.appForm__col {
display: flex;
flex-direction: column;
gap: 7px;
@@ -87,18 +87,13 @@
}
}
.appForm__twoCols {
display: flex;
gap: 1rem;
justify-content: space-between;
flex-wrap: wrap;
container-name: form_two_cols;
container-type: inline-size;
.appForm__cols {
display: grid;
gap: 0.5rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
width: 100%;
> div {
flex: 1 1 auto;
min-width: 300px;
max-width: 48%;
display: flex;
flex-direction: column;
gap: 14px;
@@ -111,14 +106,6 @@
}
}
@container form_two_cols (max-width: 620px) {
.appForm__twoCols {
> div {
max-width: 100%;
}
}
}
.appForm__repeaterItem {
padding: 0.5rem 0.5rem 0.5rem 1rem;
border-left: 3px solid #dadada;

View File

@@ -1,4 +1,5 @@
.appPage {
position: relative;
display: flex;
flex-direction: column;
flex-grow: 2;
@@ -9,6 +10,7 @@
font-style: normal;
font-weight: 600;
line-height: normal;
text-align: center;
}
}
@@ -132,6 +134,9 @@
&.rowContent {
padding: 7px 0;
flex-direction: column;
align-items: flex-start;
gap: 0.3rem;
}
p {

View File

@@ -3,7 +3,7 @@
display: flex;
gap: 20px;
width: 100%;
min-height: 500px;
height: 600px;
}
.formBuilder__main {
@@ -21,6 +21,8 @@
}
.formBuilder__content {
height: 100%;
overflow-y: scroll;
flex-grow: 2;
display: flex;
flex-direction: column;
@@ -84,6 +86,8 @@
list-style: none;
padding: 0;
margin: 0;
height: 100%;
overflow-y: scroll;
}
.formBuilder__elementItem {
display: flex;

View File

@@ -11,4 +11,30 @@
width: 100%;
max-width: 540px;
padding: 24px;
.appForm {
width: 100%;
}
}
.appPageLogin__spidBtn.appPageLogin__spidBtn {
display: flex;
align-items: center;
width: 100%;
padding: 8px;
gap: 11px;
background: #06C;
border: none;
&:hover {
cursor: pointer;
}
span {
color: #FFF;
font-size: 17.25px;
font-style: normal;
font-weight: 600;
line-height: 15px;
}
}

View File

@@ -91,10 +91,14 @@
padding: 0.5rem;
}
.p-message-detail {
.p-message-detail, .p-progressbar-label {
color: white;
}
.p-dialog-content {
padding: 1rem 1.5rem;
}
.blockingOverlay {
position: absolute;
z-index: 999;

View File

@@ -4,12 +4,14 @@ import { isEmpty, head } from 'ramda';
// store
import { storeGet, storeSet } from '../../../../store';
import { __ } from '@wordpress/i18n';
const NodeInitialForm = ({ data: { id, label = '' } }) => {
const [options, setOptions] = useState([]);
const flowData = storeGet.main.flowData();
//const [options, setOptions] = useState([]);
const [value, setValue] = useState('');
const onChangeFn = (e) => {
/*const onChangeFn = (e) => {
const { value } = e.target;
const data = {
formId: String(id),
@@ -18,12 +20,14 @@ const NodeInitialForm = ({ data: { id, label = '' } }) => {
}
setValue(value);
storeSet.main.addFlowData(data);
}
}*/
useEffect(() => {
const forms = storeGet.main.flowForms();
if (forms.length > 2) {
const form = head(forms.filter(o => String(o.id) === String(id)))
/*useEffect(() => {
const flowForms = storeGet.main.flowForms();
const flowData = storeGet.main.flowData();
if (flowForms.length > 2) {
const form = head(flowForms.filter(o => String(o.id) === String(id)))
const relevantFields = form
? form.content
.filter(o => ['radio', 'select'].includes(o.name))
@@ -31,20 +35,42 @@ const NodeInitialForm = ({ data: { id, label = '' } }) => {
: [];
setOptions(relevantFields);
}
}, [id]);
const flowDataForm = head(flowData.filter(o => String(o.formId) === String(id)));
if (flowDataForm) {
setValue(flowDataForm.chosenField);
}
}, [id]);*/
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>
{options && !isEmpty(options)
<span>{value}</span>
{/*{options && !isEmpty(options)
? <select onChange={onChangeFn} value={value}>
<option value="">Choose form</option>
<option value="">{__('Scegli il campo', 'gepafin')}</option>
{options.map(o => <option key={o.name} value={o.name}>
{o.label}
</option>)}
</select> : null}
</select> : null}*/}
<Handle
type="source"
position={Position.Bottom}

View File

@@ -1,6 +1,7 @@
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';
@@ -25,9 +26,10 @@ const NodeIntermediateForm = ({ data: { id, label = '' } }) => {
const edge = head(flowEdges.filter(o => o.target === String(id)));
if (edge) {
const sourceForm = edge.source;
const sourceFormData = head(flowData.filter(o => o.formId === sourceForm));
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));
@@ -38,7 +40,12 @@ const NodeIntermediateForm = ({ data: { id, label = '' } }) => {
}
}
}
}
const flowDataForm = head(flowData.filter(o => String(o.formId) === String(id)));
if (flowDataForm) {
setValue(flowDataForm.chosenValue);
}
}, [flowEdges, flowData]);
@@ -54,7 +61,7 @@ const NodeIntermediateForm = ({ data: { id, label = '' } }) => {
</label>
{options && !isEmpty(options)
? <select onChange={onChangeFn} value={value}>
<option value="">Choose form</option>
<option value="">{__('Scegli il valore', 'gepafin')}</option>
{options.map(o => <option key={o.name} value={o.name}>
{o.label}
</option>)}

View File

@@ -19,8 +19,8 @@ const nodeTypes = {
intermediateForm: NodeIntermediateForm
};
const FlowBuilder = ({ initialForm = 0, finalForm = 0 }) => {
const forms = useStore().main.flowForms();
const FlowBuilder = ({ initialForm = 0, finalForm = 0, mainField = '' }) => {
const flowForms = useStore().main.flowForms();
const [nodes, setNodes] = useState([]);
const [edges, setEdges] = useState([]);
@@ -33,13 +33,13 @@ const FlowBuilder = ({ initialForm = 0, finalForm = 0 }) => {
useEffect(() => {
if (
(forms.length === 2 && initialForm) ||
(forms.length > 2 && initialForm && finalForm)
(flowForms.length === 2 && initialForm) ||
(flowForms.length > 2 && initialForm && finalForm)
) {
const total = (forms.length - 2) * (200 - 90);
const total = (flowForms.length - 2) * (200 - 90);
let coordinates = range(total * -1, total, 200);
const initialNodes = forms.map(o => {
const initialNodes = flowForms.map(o => {
const formId = String(o.id);
let obj;
@@ -55,7 +55,7 @@ const FlowBuilder = ({ initialForm = 0, finalForm = 0 }) => {
id: formId,
type: 'output',
data: { label: o.label, id: formId },
position: { x: 0, y: forms.length === 2 ? 150 : 300 },
position: { x: 0, y: flowForms.length === 2 ? 150 : 300 },
}
} else {
const x = coordinates.splice(0, 1);
@@ -71,29 +71,53 @@ const FlowBuilder = ({ initialForm = 0, finalForm = 0 }) => {
let edges = [];
// eslint-disable-next-line
forms.map(o => {
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' });
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' });
edges.push({
id: `${formId}->${finalForm}`,
source: formId,
target: String(finalForm),
type: 'smoothstep'
});
}
});
if (forms.length === 2 && initialForm && finalForm) {
edges.push({ id: `${initialForm}->${finalForm}`, source: String(initialForm), 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);
if (!isEmpty(mainField)) {
const data = {
formId: String(initialForm),
chosenField: mainField,
chosenValue: ''
}
storeSet.main.addFlowData(data);
}
} else {
setNodes([]);
setEdges([]);
}
}, [initialForm, finalForm, forms]);
}, [initialForm, finalForm, flowForms, mainField]);
return (
!isEmpty(nodes) && !isEmpty(edges)

View File

@@ -4,15 +4,16 @@ import { Controller } from 'react-hook-form';
import { Checkbox } from 'primereact/checkbox';
const Checkboxes = ({
fieldName,
label,
control,
errors,
defaultValue = [],
config = {},
infoText = null,
options = []
}) => {
fieldName,
label,
control,
errors,
defaultValue = [],
config = {},
infoText = null,
options = [],
disabled = false
}) => {
const [fieldVal, setFieldVal] = useState(defaultValue);
const onCheckboxesChange = useCallback((e, updateFn) => {
@@ -37,6 +38,7 @@ const Checkboxes = ({
options.map(o => <div className="appForm__fieldItem" key={o.name}>
<Checkbox
inputId={`${fieldName}_${o.name}`}
disabled={disabled}
name={fieldName}
value={o.name}
onChange={(e) => onCheckboxesChange(e, field.onChange)}

View File

@@ -13,7 +13,8 @@ const Datepicker = ({
config = {},
infoText = null,
minDate = null,
maxDate = null
maxDate = null,
disabled = false
}) => {
return (
<>
@@ -21,20 +22,22 @@ const Datepicker = ({
{label}{config.required || config.isRequired ? '*' : null}
</label>
<Controller
name={fieldName}
control={control}
defaultValue={defaultValue}
rules={config}
render={({ field, fieldState }) => (
<Calendar id={field.name}
value={field.value ?? []}
onChange={(e) => field.onChange(e.value)}
dateFormat="dd/mm/yy"
mask="99/99/9999"
showIcon
minDate={minDate} maxDate={maxDate}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
name={fieldName}
disabled={disabled}
control={control}
defaultValue={defaultValue}
rules={config}
render={({ field, fieldState }) => (
<Calendar id={field.name}
value={field.value ?? []}
onChange={(e) => field.onChange(e.value)}
dateFormat="dd/mm/yy"
mask="99/99/9999"
showIcon
minDate={minDate}
maxDate={maxDate}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
{infoText ? <small>{infoText}</small> : null}
</>)
}

View File

@@ -7,16 +7,17 @@ import { Controller } from 'react-hook-form';
import { Calendar } from 'primereact/calendar';
const DatepickerRange = ({
fieldName,
label,
control,
errors,
defaultValue = [],
config = {},
infoText = null,
minDate = null,
maxDate = null
}) => {
fieldName,
label,
control,
errors,
defaultValue = [],
config = {},
infoText = null,
minDate = null,
maxDate = null,
disabled = false
}) => {
const datesDefaultValue = !isNil(defaultValue) && defaultValue.length
? defaultValue.map(v => new Date(v))
: [];
@@ -26,25 +27,26 @@ const DatepickerRange = ({
{label}{config.required ? '*' : null}
</label>
<Controller
name={fieldName}
control={control}
defaultValue={datesDefaultValue}
rules={config}
render={({ field, fieldState }) => (
<Calendar id={field.name}
value={field.value}
onChange={field.onChange}
dateFormat="dd/mm/yy"
mask="99/99/9999"
showIcon
minDate={minDate}
maxDate={maxDate}
selectionMode="range"
readOnlyInput
hideOnRangeSelection
showButtonBar
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
name={fieldName}
control={control}
defaultValue={datesDefaultValue}
rules={config}
render={({ field, fieldState }) => (
<Calendar id={field.name}
disabled={disabled}
value={field.value}
onChange={field.onChange}
dateFormat="dd/mm/yy"
mask="99/99/9999"
showIcon
minDate={minDate}
maxDate={maxDate}
selectionMode="range"
readOnlyInput
hideOnRangeSelection
showButtonBar
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
{infoText ? <small>{infoText}</small> : null}
</>)
}

View File

@@ -24,7 +24,8 @@ const Fileupload = ({
chooseLabel = __('Aggiungi file', 'gepafin'),
multiple = false,
sourceId = 0,
source = 'application'
source = 'application',
disabled = false
}) => {
const [stateFieldData, setStateFieldData] = useState([]);
const [acceptFormats, setAcceptFormats] = useState('');
@@ -68,6 +69,7 @@ const Fileupload = ({
</div>
<div>
<Button icon="pi pi-times" severity="danger"
disabled={disabled}
aria-label={__('Anulla', 'gepafin')}
onClick={() => onTemplateRemove(file)}/>
</div>
@@ -134,7 +136,7 @@ const Fileupload = ({
}, [stateFieldData])
return (
sourceId && sourceId !== 0
sourceId || sourceId === 0
? <>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
@@ -142,6 +144,7 @@ const Fileupload = ({
</label>
<FileUpload
ref={inputRef}
disabled={disabled}
id={fieldName}
name={fieldName}
url={'/document/uploadFile'}

View File

@@ -23,7 +23,8 @@ const FileuploadAsync = ({
chooseLabel = __('Aggiungi immagine', 'gepafin'),
multiple = false,
sourceId = 0,
source = 'application'
source = 'application',
disabled = false
}) => {
const [stateFieldData, setStateFieldData] = useState([]);
const [acceptFormats, setAcceptFormats] = useState('');
@@ -68,6 +69,7 @@ const FileuploadAsync = ({
<div>
<Button
type="button"
disabled={disabled}
icon="pi pi-times"
severity="danger"
aria-label={__('Anulla', 'gepafin')}
@@ -144,6 +146,7 @@ const FileuploadAsync = ({
</label>
<FileUpload
ref={inputRef}
disabled={disabled}
id={fieldName}
name={fieldName}
url={'/document/uploadFile'}

View File

@@ -18,7 +18,8 @@ const NumberInput = ({
minFractionDigits = 0,
maxFractionDigits = 1,
min,
max
max,
disabled = false
}) => {
const input = <Controller
name={fieldName}
@@ -27,6 +28,7 @@ const NumberInput = ({
rules={config}
render={({ field, fieldState }) => (
<InputNumber inputId={field.name}
disabled={disabled}
value={field.value}
onValueChange={(e) => field.onChange(e.value)}
min={min}

View File

@@ -11,7 +11,8 @@ const Radio = ({
defaultValue,
config = {},
infoText = null,
options = []
options = [],
disabled = false
}) => {
const input = <Controller
name={fieldName}
@@ -22,6 +23,7 @@ const Radio = ({
options.map(o => <div className="appForm__fieldItem" key={o.name}>
<RadioButton
inputId={`${fieldName}_${o.name}`}
disabled={disabled}
name={fieldName}
value={o.name}
onChange={(e) => field.onChange(e.value)}

View File

@@ -14,7 +14,8 @@ const Select = ({
inputgroup = false,
icon = null,
placeholder = '',
options = []
options = [],
disabled = false
}) => {
const input = <Controller
name={fieldName}
@@ -24,6 +25,7 @@ const Select = ({
render={({ field, fieldState }) => (
<Dropdown
value={field.value}
disabled={disabled}
onChange={(e) => field.onChange(e.value)}
options={options}
optionLabel="label"

View File

@@ -15,7 +15,8 @@ const Switch = ({
config = {},
infoText = null,
onLabel = '',
offLabel = ''
offLabel = '',
disabled = false
}) => {
const properValue = !isNil(defaultValue) && !isEmpty(defaultValue) && defaultValue !== false;
@@ -27,6 +28,7 @@ const Switch = ({
render={({ field, fieldState }) => (
<InputSwitch
checked={field.value}
disabled={disabled}
onChange={(e) => field.onChange(e.value)}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>

View File

@@ -11,7 +11,8 @@ const TextArea = ({
errors,
defaultValue,
config = {},
infoText = null
infoText = null,
disabled = false
}) => {
return (
<>
@@ -25,6 +26,7 @@ const TextArea = ({
rules={config}
render={({ field, fieldState }) => (
<InputTextarea id={field.name}
disabled={disabled}
rows={rows}
{...field}
className={classNames({ 'p-invalid': fieldState.invalid })}/>

View File

@@ -14,7 +14,8 @@ const TextInput = ({
inputgroup = false,
icon = null,
placeholder = '',
inputtype = 'text'
inputtype = 'text',
disabled = false
}) => {
const input = <Controller
name={fieldName}
@@ -23,6 +24,7 @@ const TextInput = ({
rules={config}
render={({ field, fieldState }) => (
<InputText id={field.name}
disabled={disabled}
{...field}
type={inputtype}
placeholder={placeholder}

View File

@@ -11,7 +11,8 @@ const Wysiwyg = ({
errors,
defaultValue,
config = {},
infoText = null
infoText = null,
disabled = false
}) => {
const renderHeader = () => {
@@ -21,6 +22,13 @@ const Wysiwyg = ({
<button className="ql-italic" aria-label="Italic"></button>
<button className="ql-underline" aria-label="Underline"></button>
<button className="ql-link" aria-label="Link"></button>
<button className="ql-list" value="ordered"></button>
<button className="ql-header" value="1"></button>
<button className="ql-header" value="2"></button>
<button className="ql-blockquote"></button>
<button className="ql-list" value="bullet"></button>
<button className="ql-indent" value="-1"></button>
<button className="ql-indent" value="+1"></button>
</span>
);
};
@@ -40,6 +48,7 @@ const Wysiwyg = ({
render={({ field, fieldState }) => (
<Editor
id={field.name}
readOnly={disabled}
{...field}
headerTemplate={header}
onTextChange={(e) => field.onChange(e.htmlValue)}

View File

@@ -20,7 +20,8 @@ const FormFieldRepeater = ({
register,
label,
infoText,
config = {}
config = {},
disabled = false
}) => {
const forMenu = useRef(null);
const [stateFieldData, setStateFieldData] = useState([]);
@@ -72,11 +73,12 @@ const FormFieldRepeater = ({
const properField = (item, i) => {
return !isNil(item.lookUpDataId)
? <Dropdown value={item.value}
disabled={disabled}
onChange={(e) => selectItem(e, i)}
optionDisabled={(opt) => usedExistingValues().includes(opt.value)}
options={stateOptionsData}
optionLabel="value"/>
: <InputText value={item.value} onInput={(e) => onInputChange(e, i)}/>
: <InputText disabled={disabled} value={item.value} onInput={(e) => onInputChange(e, i)}/>
}
const usedExistingValues = useCallback(() => {
@@ -101,7 +103,7 @@ const FormFieldRepeater = ({
const ids = pluck('lookUpDataId', prevState)
const objectsToAdd = klona(storeFieldData)
.filter(o => !ids.includes(o.lookUpDataId))
.map(o => ({...o, id: null}));
.map(o => ({ ...o, id: null }));
return [...prevState, ...objectsToAdd];
});
}
@@ -123,12 +125,13 @@ const FormFieldRepeater = ({
{stateFieldData.map((o, i) => <div key={i} className={classNames('appForm__repeaterItem')}>
<div className="p-inputgroup flex-1">
{properField(o, i)}
<Button type="button" icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
<Button disabled={disabled} type="button" icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
</div>
{isNil(o.lookUpDataId) && infoText ? <small>{infoText}</small> : null}
</div>)}
<Menu model={menuItems} popup ref={forMenu} id="aimedForMenu"/>
<Button type="button" iconPos="right" label={__('Aggiungi', 'gepafin')}
disabled={disabled}
icon="pi pi-chevron-down" onClick={(event) => forMenu.current.toggle(event)}
aria-controls="aimedForMenu" aria-haspopup/>
</div>

View File

@@ -21,7 +21,8 @@ const FormFieldRepeaterCriteria = ({
register,
label,
infoText,
config = {}
config = {},
disabled = false
}) => {
const forMenu = useRef(null);
const [stateFieldData, setStateFieldData] = useState([]);
@@ -78,10 +79,12 @@ const FormFieldRepeaterCriteria = ({
const properField = (item, i) => {
return !isNil(item.lookUpDataId)
? <Dropdown value={item.value}
disabled={disabled}
onChange={(e) => selectItem(e, i)}
optionDisabled={(opt) => usedExistingValues().includes(opt.value)}
options={stateOptionsData} optionLabel="value"/>
: <InputText value={item.value} onInput={(e) => onInputChange(e.target.value, i, 'value')}/>
: <InputText disabled={disabled} value={item.value}
onInput={(e) => onInputChange(e.target.value, i, 'value')}/>
}
const usedExistingValues = useCallback(() => {
@@ -111,7 +114,7 @@ const FormFieldRepeaterCriteria = ({
const ids = pluck('lookUpDataId', prevState)
const objectsToAdd = klona(storeFieldData)
.filter(o => !ids.includes(o.lookUpDataId))
.map(o => ({...o, id: null, score: 0}));
.map(o => ({ ...o, id: null, score: 0 }));
return [...prevState, ...objectsToAdd];
});
}
@@ -130,27 +133,30 @@ const FormFieldRepeaterCriteria = ({
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] || errors['threshold'] })}>
{label}
</label>
<div className="appForm__oneCol">
<div className="appForm__col">
<label htmlFor="criterionThreshold">{__('Punteggio minimo per lammissione', 'gepafin')}</label>
<InputNumber inputId="criterionThreshold"
disabled={disabled}
value={threshold}
showButtons
onValueChange={(e) => onThresholdChange(e.value)}/>
</div>
{stateFieldData.map((o, i) => <div key={i}
className={classNames('appForm__field', 'appForm__repeaterItem')}>
<div className="appForm__twoCols">
<div className="appForm__cols">
<div>
<label>{__('Nome criterio di valutazione', 'gepafin')}</label>
<div className="p-inputgroup flex-1">
{properField(o, i)}
<Button type="button" icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
{properField(o, i)}
<Button disabled={disabled} type="button" icon="pi pi-times" className="p-button-danger"
onClick={() => removeItem(i)}/>
</div>
{isNil(o.lookUpDataId) && infoText ? <small>{infoText}</small> : null}
</div>
<div>
<label htmlFor="criterionMin">{__('Punteggio', 'gepafin')}</label>
<InputNumber inputId="criterionMin"
disabled={disabled}
value={o.score}
showButtons
onValueChange={(e) => onInputChange(e.value, i, 'score')}/>
@@ -159,6 +165,7 @@ const FormFieldRepeaterCriteria = ({
</div>)}
<Menu model={menuItems} popup ref={forMenu} id="aimedForMenu"/>
<Button type="button" iconPos="right" label={__('Aggiungi', 'gepafin')}
disabled={disabled}
icon="pi pi-chevron-down" onClick={(event) => forMenu.current.toggle(event)}
aria-controls="aimedForMenu" aria-haspopup/>
</div>

View File

@@ -8,10 +8,10 @@ import { diff } from 'deep-object-diff';
import { Button } from 'primereact/button';
import { Dropdown } from 'primereact/dropdown';
import { Accordion, AccordionTab } from 'primereact/accordion';
import { ToggleButton } from 'primereact/togglebutton';
import { Dialog } from 'primereact/dialog';
import { InputText } from 'primereact/inputtext';
import { InputTextarea } from 'primereact/inputtextarea';
import { InputSwitch } from 'primereact/inputswitch';
const FormFieldRepeaterFaq = ({
data,
@@ -21,13 +21,15 @@ const FormFieldRepeaterFaq = ({
errors,
register,
label,
config = {}
config = {},
disabled = false
}) => {
const [stateFieldData, setStateFieldData] = useState([]);
const [stateOptionsData, setStateOptionsData] = useState([]);
const [question, setQuestion] = useState('');
const [answer, setAnswer] = useState('');
const [title, setTitle] = useState('');
const [isVisible, setIsVisible] = useState(false);
const [editDataIndex, setEditDataIndex] = useState(null);
const [isVisibleEditDialog, setIsVisibleEditDialog] = useState(false);
@@ -39,7 +41,7 @@ const FormFieldRepeaterFaq = ({
const selectItem = (e) => {
const targetedOption = head(stateOptionsData.filter(o => o.value === e.value));
if (targetedOption) {
setStateFieldData([...stateFieldData, {...targetedOption, isVisible: true}]);
setStateFieldData([...stateFieldData, { ...targetedOption, isVisible: true }]);
}
}
@@ -48,22 +50,12 @@ const FormFieldRepeaterFaq = ({
setStateFieldData([...stateFieldData, newItem]);
}
const setChecked = (e, index) => {
e.preventDefault();
const newData = stateFieldData.map((o, i) => {
if (i === index) {
o.isVisible = e.value;
}
return o;
});
setStateFieldData(newData);
}
const editItem = (e, index) => {
e.stopPropagation();
setTitle(stateFieldData[index].title);
setQuestion(stateFieldData[index].value);
setAnswer(stateFieldData[index].response);
setIsVisible(stateFieldData[index].isVisible);
setEditDataIndex(index);
setIsVisibleEditDialog(true);
}
@@ -73,6 +65,7 @@ const FormFieldRepeaterFaq = ({
setQuestion('');
setAnswer('');
setTitle('');
setIsVisible(false);
setEditDataIndex(null);
}
@@ -81,8 +74,10 @@ const FormFieldRepeaterFaq = ({
setTitle(value);
} else if (key === 'value') {
setQuestion(value);
} else {
} else if (key === 'response') {
setAnswer(value)
} else {
setIsVisible(value);
}
}
@@ -92,6 +87,7 @@ const FormFieldRepeaterFaq = ({
o.title = title;
o.value = question;
o.response = answer;
o.isVisible = isVisible;
return o
} else {
return o;
@@ -102,6 +98,7 @@ const FormFieldRepeaterFaq = ({
setQuestion('');
setAnswer('');
setTitle('');
setIsVisible(false);
setEditDataIndex(null);
}
@@ -111,10 +108,10 @@ const FormFieldRepeaterFaq = ({
const footerEditDialog = () => {
return <div>
<Button type="button" label={__('Anulla', 'gepafin')} onClick={hideEditDialog} outlined/>
<Button type="button" disabled={disabled} label={__('Anulla', 'gepafin')} onClick={hideEditDialog} outlined/>
<Button
type="button"
disabled={isEmpty(title) || isEmpty(question) || isEmpty(answer)}
disabled={isEmpty(title) || isEmpty(question) || isEmpty(answer) || disabled}
label={__('Salva', 'gepafin')} onClick={saveEditDialog}/>
</div>
}
@@ -156,37 +153,40 @@ const FormFieldRepeaterFaq = ({
</label>
<div className="appForm__faqHeaderControls">
<Button type="button" iconPos="left" label={__('Aggiungi', 'gepafin')}
disabled={disabled}
icon="pi pi-plus" onClick={addNewItem}/>
<Dropdown onChange={(e) => selectItem(e)}
disabled={disabled}
optionDisabled={(opt) => usedExistingValues().includes(opt.title)}
options={stateOptionsData}
placeholder={__('Scegli tra quelli pre-creati', 'gepafin')}
optionLabel="title"/>
</div>
<Accordion activeIndex={0}>
{stateFieldData.map((o, i) => <AccordionTab key={i}
{stateFieldData.map((o, i) => <AccordionTab key={i} tabIndex={i}
header={
<div className="appForm__faqTab">
<div className="appForm__faqTabItem">
<ToggleButton
onIcon="pi pi-eye"
offIcon="pi pi-eye-slash"
onLabel=""
offLabel=""
checked={o.isVisible}
onChange={(e) => {
e.preventDefault();
e.stopPropagation();
setChecked(e, i);
}}/>
{o.value}
<span>
{o.value}
</span>
</div>
<div className="appForm__faqTabItem">
{o.isVisible
? <i className="pi pi-eye"
style={{ fontSize: '1.5rem' }}></i> : null}
{!o.isVisible
? <i className="pi pi-eye-slash"
style={{ fontSize: '1.5rem' }}></i> : null}
<Button icon="pi pi-pencil" severity="success"
disabled={disabled}
className="actionBtn"
type="button"
aria-label={__('Modifica', 'gepafin')}
onClick={(e) => editItem(e, i)}/>
<Button icon="pi pi-times" severity="danger"
disabled={disabled}
className="actionBtn"
type="button"
aria-label={__('Cancella', 'gepafin')}
onClick={() => removeItem(i)}/>
@@ -203,7 +203,7 @@ const FormFieldRepeaterFaq = ({
visible={isVisibleEditDialog}
modal header={headerEditDialog}
footer={footerEditDialog}
style={{ maxWidth: '50rem' }}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideEditDialog}>
<div className="appPage__spacer"></div>
<div className="appForm__field">
@@ -220,6 +220,10 @@ const FormFieldRepeaterFaq = ({
rows={5}
cols={30}/>
</div>
<div className="appForm__field">
<label>{__('Pubblicato?', 'gepafin')}</label>
<InputSwitch checked={isVisible} onChange={(e) => onChangeEditItem(e.value, 'isVisible')}/>
</div>
<div className="appPage__spacer"></div>
</Dialog>
</div>

6
src/configData.js Normal file
View File

@@ -0,0 +1,6 @@
export const mimeTypes = [
{ name: 'PDF file', code: 'application/pdf' },
{ name: 'ZIP file', code: 'application/zip' },
{ name: 'Immagine', code: 'image/*' },
{ name: 'Word doc', code: 'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document' }
];

View File

@@ -0,0 +1,5 @@
import parse from 'html-react-parser';
const renderHtmlContent = (content) => parse(content);
export default renderHtmlContent;

View File

@@ -1,27 +1,58 @@
import validate from 'validate.js';
import { match, isEmpty } from 'ramda';
import CodiceFiscale from 'codice-fiscale-js';
export const isPIVA = (v) => {
return false;
}
// example: DSDDSD99P23B352G
export const isCodiceFiscale = (v) => {
return false;
//const regexp = new RegExp(/^(?:[A-Z][AEIOU][AEIOUX]|[AEIOU]X{2}|[B-DF-HJ-NP-TV-Z]{2}[A-Z]){2}(?:[\dLMNP-V]{2}(?:[A-EHLMPR-T](?:[04LQ][1-9MNP-V]|[15MR][\dLMNP-V]|[26NS][0-8LMNP-U])|[DHPS][37PT][0L]|[ACELMRT][37PT][01LM]|[AC-EHLMPR-T][26NS][9V])|(?:[02468LNQSU][048LQU]|[13579MPRTV][26NS])B[26NS][9V])(?:[A-MZ][1-9MNP-V][\dLMNP-V]{2}|[A-M][0L](?:[1-9MNP-V][\dLMNP-V]|[0L][1-9MNP-V]))[A-Z]$/, 'i')
//return !isEmpty(match(regexp, v));
return CodiceFiscale.check(v);
}
export const isCAP = (v) => {
return false;
const regexp = new RegExp(/^[0-9]{5}$/)
return !isEmpty(match(regexp, v));
}
export const isIBAN = (v) => {
return false;
const regexp = new RegExp(/^((NO)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{3}|(NO)[0-9A-Z]{13}|(BE)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}|(BE)[0-9A-Z]{14}|(DK|FO|FI|GL|NL)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{2}|(DK|FO|FI|GL|NL)[0-9A-Z]{16}|(MK|SI)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{3}|(MK|SI)[0-9A-Z]{17}|(BA|EE|KZ|LT|LU|AT)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}|(BA|EE|KZ|LT|LU|AT)[0-9A-Z]{18}|(HR|LI|LV|CH)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{1}|(HR|LI|LV|CH)[0-9A-Z]{19}|(BG|DE|IE|ME|RS|GB)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{2}|(BG|DE|IE|ME|RS|GB)[0-9A-Z]{20}|(GI|IL)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{3}|(GI|IL)[0-9A-Z]{21}|(AD|CZ|SA|RO|SK|ES|SE|TN)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}|(AD|CZ|SA|RO|SK|ES|SE|TN)[0-9A-Z]{22}|(PT)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{1}|(PT)[0-9A-Z]{23}|(IS|TR)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{2}|(IS|TR)[0-9A-Z]{24}|(FR|GR|IT|MC|SM)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{3}|(FR|GR|IT|MC|SM)[0-9A-Z]{25}|(AL|CY|HU|LB|PL)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}|(AL|CY|HU|LB|PL)[0-9A-Z]{26}|(MU)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{2}|(MU)[0-9A-Z]{28}|(MT)[0-9A-Z]{2}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{4}[ ][0-9A-Z]{3}|(MT)[0-9A-Z]{29})$/)
return !isEmpty(match(regexp, v));
}
export const isEmail = (v) => {
return false;
const constraints = {
from: {
email: true
}
};
const res = validate({from: v}, constraints);
return !res;
}
export const isEmailPEC = (v) => {
return false;
const constraints = {
from: {
email: true
}
};
const res = validate({from: v}, constraints);
return !res;
}
export const isUrl = (v) => {
return false;
const constraints = {
from: {
url: true
}
};
const res = validate({from: v}, constraints);
return !res;
}
export const isMarcaDaBollo = (v) => {
return true;
}

View File

@@ -23,14 +23,14 @@ const AppSidebar = () => {
{
label: __('Gestione Bandi', 'gepafin'),
icon: 'pi pi-file',
href: '/tenders',
href: '/bandi',
id: 2,
enable: intersection(permissions, ['MANAGE_TENDERS']).length
},
{
label: __('Domande in lavorazione', 'gepafin'),
icon: 'pi pi-file',
href: '/applications',
href: '/imieibandi',
id: 11,
enable: intersection(permissions, ['APPLY_CALLS']).length
},

View File

@@ -123,7 +123,7 @@ const AllBandiTable = () => {
};
const actionsBodyTemplate = (rowData) => {
return <Link to={`/tenders/${rowData.id}`}>
return <Link to={`/bandi/${rowData.id}`}>
<Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small" iconPos="right" />
</Link>
}

View File

@@ -10,7 +10,7 @@ const Bandi = () => {
const navigate = useNavigate();
const onGoToCreateNewBando = () => {
navigate('/tenders/new');
navigate('/bandi/new');
}
return(

View File

@@ -1,15 +1,15 @@
import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n';
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { klona } from 'klona';
import { head, is, isNil } from 'ramda';
import { head, range } from 'ramda';
import { useForm } from 'react-hook-form';
// store
import { storeSet, useStore } from '../../store';
// api
import FormsService from '../../service/forms-service';
import ApplicationService from '../../service/application-service';
// tools
import {
@@ -19,7 +19,8 @@ import {
isIBAN,
isEmail,
isEmailPEC,
isUrl
isUrl,
isMarcaDaBollo
} from '../../helpers/validators';
// components
@@ -27,17 +28,21 @@ import { Skeleton } from 'primereact/skeleton';
import { Button } from 'primereact/button';
import FormField from '../../components/FormField';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import { formData as testformData } from '../../tempData';
import BandoService from '../../service/bando-service';
import ApplicationService from '../../service/application-service';
import { Steps } from 'primereact/steps';
import { Toast } from 'primereact/toast';
import { TZDate } from '@date-fns/tz';
const BandoApplication = () => {
const { id } = useParams();
const navigate = useNavigate();
const [formData, setFormData] = useState([]);
const [formName, setFormName] = useState('');
const [bandoTitle, setBandoTitle] = useState('');
const [formId, setFormId] = useState('');
const [totalSteps, setTotalSteps] = useState(0);
const [activeStep, setActiveStep] = useState(1);
const [stepItems, setStepItems] = useState([{ label: 'Passo' }]);
const isAsyncRequest = useStore().main.isAsyncRequest();
const toast = useRef(null);
const {
control,
handleSubmit,
@@ -48,7 +53,6 @@ const BandoApplication = () => {
getValues,
} = useForm({ defaultValues: {}, mode: 'onChange' });
const values = getValues();
const validationFns = {
isPIVA,
isCodiceFiscale,
@@ -56,54 +60,117 @@ const BandoApplication = () => {
isIBAN,
isEmail,
isEmailPEC,
isUrl
isUrl,
isMarcaDaBollo
}
const onSubmit = () => {};
const onSubmit = () => {
};
const saveDraft = () => {
const saveDraft = useCallback(() => {
trigger();
const formData = getValues();
const newFormData = Object.keys(formData).reduce((acc, cur) => {
let fieldVal = formData[cur];
if (formData[cur] && formData[cur].toISOString) {
const tzAwareDate = new TZDate(formData[cur], 'Europe/Berlin');
fieldVal = tzAwareDate.toISOString().substring(0, 19);
}
fieldVal = !fieldVal ? null : fieldVal;
acc.push({
'fieldId': cur,
'fieldValue': formData[cur] && formData[cur].getMonth ? formData[cur].toISOString() : formData[cur]
'fieldValue': fieldVal
});
return acc;
}, []);
console.log('newFormData', newFormData);
console.log('errors', errors);
};
const submitData = {
formFields: newFormData
}
if (formId) {
const applId = getApplicationId();
storeSet.main.setAsyncRequest();
ApplicationService.submitForm(applId, submitData, submitFormCallback, errSubmitFormCallback, [
['formId', formId]
]);
}
}, [errors]);
const getApplicationId = () => {
const parsed = parseInt(id)
return !isNaN(parsed) ? parsed : 0;
}
const getCallback = (data) => {
const goBackward = () => {
if (formId) {
const applId = getApplicationId();
storeSet.main.setAsyncRequest();
ApplicationService.getApplicationForm(applId, getApplFormCallback, errGetApplFormCallbacks, [
['formId', formId],
['action', 'PREVIOUS']
]);
}
}
const goForward = () => {
if (formId) {
const applId = getApplicationId();
storeSet.main.setAsyncRequest();
ApplicationService.getApplicationForm(applId, getApplFormCallback, errGetApplFormCallbacks, [
['formId', formId],
['action', 'NEXT']
]);
}
}
const submitFormCallback = (data) => {
if (data.status === 'SUCCESS') {
/*const forms = data.data;
setFormName(forms[0].label);
const elements = klona(forms[0].content);
setFormData(elements);*/
//console.log('testformData.content', testformData.content);
setFormName(testformData.label);
setFormData(testformData.content);
console.log(data.data);
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: __('Saved!', 'gepafin')
});
}
}
storeSet.main.unsetAsyncRequest();
}
const errGetCallbacks = (data) => {
const errSubmitFormCallback = (data) => {
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
const getApplFormCallback = (data) => {
if (data.status === 'SUCCESS') {
console.log('getApplFormCallback', data.data);
setBandoTitle(data.data.callTitle);
setFormData(data.data.applicationFormResponse.content);
setFormId(data.data.formId);
setTotalSteps(data.data.totalFormSteps);
setActiveStep(data.data.currentStep)
}
storeSet.main.unsetAsyncRequest();
}
const errGetApplFormCallbacks = (data) => {
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
useEffect(() => {
const rangeArr = range(1, totalSteps + 1);
setStepItems(rangeArr.map(() => ({ label: 'Passo' })));
}, [totalSteps])
useEffect(() => {
const applId = getApplicationId();
if (applId) {
storeSet.main.setAsyncRequest();
ApplicationService.getApplication(applId, getCallback, errGetCallbacks);
ApplicationService.getApplicationForm(applId, getApplFormCallback, errGetApplFormCallbacks);
}
}, [id]);
@@ -111,7 +178,7 @@ const BandoApplication = () => {
<div className="appPage">
{!isAsyncRequest
? <div className="appPage__pageHeader">
<h1>{formName}</h1>
<h1>{sprintf(__('Domanda per il Bando: %s', 'gepafin'), bandoTitle)}</h1>
</div>
: <>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
@@ -120,6 +187,12 @@ const BandoApplication = () => {
<div className="appPage__spacer"></div>
{!isAsyncRequest
? <Steps model={stepItems} activeIndex={activeStep - 1}/> : null}
<div className="appPage__spacer"></div>
<Toast ref={toast}/>
{!isAsyncRequest
? <div className="appPage__content">
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
@@ -132,7 +205,7 @@ const BandoApplication = () => {
let mimeValue = '';
if (mime) {
mimeValue = mime.value.join(',');
mimeValue = mime.value.map(o => o.code).join(',');
}
const validations = Object.keys(o.validators).reduce((acc, cur) => {
@@ -169,7 +242,7 @@ const BandoApplication = () => {
config={validations}
options={options ? options.value : []}
setDataFn={setValue}
sourceId={1}
sourceId={getApplicationId()}
/>
})}
@@ -181,17 +254,26 @@ const BandoApplication = () => {
<div className="appPageSection">
<div className="appPageSection__actions">
{activeStep > 1 && activeStep <= totalSteps
? <Button
type="button"
onClick={goBackward}
label={__('Vai indietro', 'gepafin')}
icon="pi pi-arrow-left"
iconPos="left"/> : null}
<Button
type="button"
disabled={isAsyncRequest}
onClick={saveDraft}
outlined
label={__('Salva bozza', 'gepafin')} icon="pi pi-save" iconPos="right"/>
<Button
type="button"
disabled={true}
onClick={() => {
}}
label={__('Vai avanti', 'gepafin')} icon="pi pi-arrow-right" iconPos="right"/>
{activeStep < totalSteps
? <Button
type="button"
onClick={goForward}
label={__('Vai avanti', 'gepafin')}
icon="pi pi-arrow-right"
iconPos="right"/> : null}
</div>
</div>
</form>

View File

@@ -5,7 +5,7 @@ import { isNil } from 'ramda';
// components
import { Button } from 'primereact/button';
const BandoEditFormActions = ({ id, openPreview, openPreviewEvaluation }) => {
const BandoEditFormActions = ({ id, openPreview, openPreviewEvaluation, status }) => {
return (
<div className="appPageSection">
<div className="appPageSection__actions">

View File

@@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { isEmpty, isNil, is } from 'ramda';
import { klona } from 'klona';
import { TZDate } from '@date-fns/tz';
// components
import FormField from '../../../../components/FormField';
@@ -20,7 +21,7 @@ import LookupdataService from '../../../../service/lookupdata-service';
// store
import { storeSet, useStore } from '../../../../store';
const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors }, ref) {
const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, status }, ref) {
const navigate = useNavigate();
const isAsyncRequest = useStore().main.isAsyncRequest();
const [aimedToOptions, setAimedToOptions] = useState([]);
@@ -34,8 +35,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
register,
trigger,
getValues,
clearErrors,
reset
clearErrors
} = useForm({
defaultValues: useMemo(() => {
return formInitialData;
@@ -46,9 +46,17 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
const onSubmit = (formData) => {
if (!isNil(formData.dates) && formData.dates.length) {
formData.dates = formData.dates.map(v => is(String, v) ? v : v.toISOString());
formData.dates = formData.dates.map(v => {
if (is(String, v)) {
return v;
} else {
const tzAwareDate = new TZDate(v, 'Europe/Berlin');
return tzAwareDate.toISOString().substring(0, 19);
}
});
}
storeSet.main.setAsyncRequest();
if (!formData.id) {
BandoService.createBando(formData, createCallback, errCreateCallback);
} else {
@@ -57,6 +65,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
};
const createCallback = (data) => {
storeSet.main.unsetAsyncRequest();
if (data.status === 'SUCCESS') {
toast.current.show({
severity: 'success',
@@ -65,24 +74,24 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
});
const values = getValues();
if (!values.id && data.data.id) {
navigate(`/tenders/${data.data.id}`);
navigate(`/bandi/${data.data.id}`);
} else {
setFormInitialData(data.data);
reset();
}
}
}
const errCreateCallback = (data) => {
console.log('errCreateCallback', data);
storeSet.main.unsetAsyncRequest();
}
const openPreview = () => {
navigate(`/tenders/${values.id}/preview`);
navigate(`/bandi/${values.id}/preview`);
}
const openPreviewEvaluation = () => {
navigate(`/tenders/${values.id}/preview-evaluation`);
navigate(`/bandi/${values.id}/preview-evaluation`);
}
const lookupdataCallback = (data) => {
@@ -118,6 +127,10 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
storeSet.main.unsetAsyncRequest();
}
const shouldDisableField = () => {
return values.status === 'PUBLISH'
}
useImperativeHandle(
ref,
() => {
@@ -144,14 +157,10 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
if (!isNil(formInitialData.dates) && formInitialData.dates.length) {
newFormData.dates = formInitialData.dates.map(v => is(String, v) ? new Date(v) : (v ? v : ''));
}
reset(newFormData);
Object.keys(newFormData).map(v => setValue(v, newFormData[v]));
}, [formInitialData]);
useEffect(() => {
if (isAsyncRequest !== 0) {
return;
}
trigger().then(() => clearErrors());
LookupdataService.getItems(lookupdataCallback, errLookupdataCallback, [['type', ['AIMED_TO', 'FAQ']]]);
@@ -165,6 +174,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
<UnsavedChangesDetector getValuesFn={getValues}/>
<FormField
type="switch"
disabled={shouldDisableField()}
fieldName="confidi"
label={__('Bando Confidi', 'gepafin')}
control={control}
@@ -176,6 +186,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
<FormField
type="textinput"
disabled={shouldDisableField()}
fieldName="name"
label={__('Titolo del Bando', 'gepafin')}
control={control}
@@ -185,7 +196,8 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
/>
<FormField
type="textarea"
type="wysiwyg"
disabled={shouldDisableField()}
fieldName="descriptionShort"
label={__('Descrizione breve', 'gepafin')}
control={control}
@@ -197,7 +209,8 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
/>
<FormField
type="textarea"
type="wysiwyg"
disabled={shouldDisableField()}
fieldName="descriptionLong"
label={__('Descrizione completa', 'gepafin')}
control={control}
@@ -213,6 +226,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
<FormFieldRepeater
data={values['aimedTo']}
disabled={shouldDisableField()}
setDataFn={setValue}
fieldName="aimedTo"
options={aimedToOptions}
@@ -221,9 +235,8 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
trigger={trigger}
config={{
validate: {
minOneItem: v => !isEmpty(v) || __('Almeno 1 tipo di destinatari', 'gepafin'),
noEmptyValue: v => v
.filter(o => isEmpty(o.value)).length === 0 || __('Non lasciare il valore vuoto', 'gepafin')
minOneItem: v => (v && !isEmpty(v)) || __('Almeno 1 tipo di destinatari', 'gepafin'),
noEmptyValue: v => v.filter(o => isEmpty(o.value)).length === 0 || __('Non lasciare il valore vuoto', 'gepafin')
}
}}
label={<>{__('A chi si rivolge', 'gepafin')}*
@@ -232,6 +245,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
<FormField
type="textarea"
disabled={shouldDisableField()}
fieldName="documentationRequested"
label={__('Documentazione richiesta', 'gepafin')}
control={control}
@@ -244,6 +258,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
<FormField
type="datepickerrange"
disabled={shouldDisableField()}
fieldName="dates"
label={__('Dati di pubblicazione', 'gepafin')}
control={control}
@@ -253,9 +268,10 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
/*minDate={minDateStart}*/
/>
<div className="appForm__twoCols">
<div className="appForm__cols">
<FormField
type="numberinput"
disabled={shouldDisableField()}
fieldName="amount"
label={__('Dotazione del Bando', 'gepafin')}
control={control}
@@ -268,6 +284,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
<FormField
type="numberinput"
disabled={shouldDisableField()}
fieldName="amountMax"
label={__('Importo massimo per Progetto', 'gepafin')}
control={control}
@@ -304,6 +321,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
<Toast ref={toast} />
<BandoEditFormActions
id={values.id}
status={status}
openPreview={openPreview}
openPreviewEvaluation={openPreviewEvaluation}/>
</form>

View File

@@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { is, isEmpty, isNil } from 'ramda';
import { klona } from 'klona';
import { TZDate } from '@date-fns/tz';
// components
import FormField from '../../../../components/FormField';
@@ -20,7 +21,7 @@ import LookupdataService from '../../../../service/lookupdata-service';
// store
import { storeSet } from '../../../../store';
const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors }, ref) {
const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors, status }, ref) {
const navigate = useNavigate();
const [criteriaOptions, setCriteriaOptions] = useState([]);
const [checklistOptions, setChecklistOptions] = useState([]);
@@ -33,8 +34,7 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
register,
trigger,
getValues,
clearErrors,
reset
clearErrors
} = useForm({
defaultValues: useMemo(() => {
return formInitialData;
@@ -46,7 +46,14 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
const onSubmit = (formData) => {
if (!isNil(formData.dates) && formData.dates.length) {
formData.dates = formData.dates.map(v => is(String, v) ? v : v.toISOString());
formData.dates = formData.dates.map(v => {
if (is(String, v)) {
return v;
} else {
const tzAwareDate = new TZDate(v, 'Europe/Berlin');
return tzAwareDate.toISOString().substring(0, 19);
}
});
}
const forSubmit = Object.keys(formData).reduce((acc, cur) => {
@@ -56,10 +63,12 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
return acc;
}, {});
storeSet.main.setAsyncRequest();
BandoService.updateBandoStep2(formData.id, forSubmit, createCallback, errCreateCallback);
};
const createCallback = (data) => {
storeSet.main.unsetAsyncRequest();
if (data.status === 'SUCCESS') {
toast.current.show({
severity: 'success',
@@ -68,11 +77,11 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
});
const newFormData = {...formInitialData, ...data.data};
setFormInitialData(newFormData);
reset(newFormData);
}
}
const errCreateCallback = (data) => {
storeSet.main.unsetAsyncRequest();
console.log('errCreateCallback', data);
}
@@ -109,11 +118,11 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
}
const openPreview = () => {
navigate(`/tenders/${values.id}/preview`);
navigate(`/bandi/${values.id}/preview`);
}
const openPreviewEvaluation = () => {
navigate(`/tenders/${values.id}/preview-evaluation`);
navigate(`/bandi/${values.id}/preview-evaluation`);
}
useImperativeHandle(
@@ -142,7 +151,7 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
if (!isNil(formInitialData.dates) && formInitialData.dates.length) {
newFormData.dates = formInitialData.dates.map(v => is(String, v) ? new Date(v) : (v ? v : ''));
}
reset(newFormData);
Object.keys(newFormData).map(v => setValue(v, newFormData[v]));
}, [formInitialData]);
useEffect(() => {
@@ -154,11 +163,16 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
}
}, []);
const shouldDisableField = () => {
return values.status === 'PUBLISH'
}
return (
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
<UnsavedChangesDetector getValuesFn={getValues}/>
<FormFieldRepeaterCriteria
data={values}
disabled={shouldDisableField()}
setDataFn={setValue}
fieldName="criteria"
options={criteriaOptions}
@@ -177,6 +191,7 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
<FormField
type="fileuploadasync"
disabled={shouldDisableField()}
setDataFn={setValue}
fieldName="docs"
label={__('Documentazione', 'gepafin')}
@@ -195,6 +210,7 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
<FormField
type="fileuploadasync"
disabled={shouldDisableField()}
setDataFn={setValue}
fieldName="images"
label={__('Immagine del Bando', 'gepafin')}
@@ -209,6 +225,7 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
<FormFieldRepeater
data={values['checkList']}
disabled={shouldDisableField()}
setDataFn={setValue}
fieldName="checkList"
options={checklistOptions}
@@ -234,6 +251,7 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
<Toast ref={toast} />
<BandoEditFormActions
id={values.id}
status={status}
openPreview={openPreview}
openPreviewEvaluation={openPreviewEvaluation}/>
</form>

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import { __ } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { is, isNil } from 'ramda';
import { is, isNil, isEmpty } from 'ramda';
// store
import { storeSet, useStore } from '../../store';
@@ -93,11 +93,11 @@ const BandoEdit = () => {
}
const openBandoFormManagement = () => {
navigate(`/tenders/${id}/forms`);
navigate(`/bandi/${id}/forms`);
}
const openBandoFlowManagement = () => {
navigate(`/tenders/${id}/flow`);
navigate(`/bandi/${id}/flow`);
}
const validateBando = () => {
@@ -108,6 +108,7 @@ const BandoEdit = () => {
const validateCallback = (data) => {
if (data.status === 'SUCCESS') {
setData({...data, status: data.data.status});
if (bandoMsgs.current) {
bandoMsgs.current.show([
{
@@ -123,7 +124,19 @@ const BandoEdit = () => {
}
const errValidateCallback = (data) => {
standardErrCallback(data);
if (data.status === 'VALIDATION_ERROR') {
storeSet.main.unsetAsyncRequest();
if (bandoMsgs.current) {
bandoMsgs.current.show(data.data.map((v, i) => ({
id: i,
sticky: true, severity: 'error', summary: '',
detail: v,
closable: false
})));
}
} else {
standardErrCallback(data);
}
}
const publishBando = () => {
@@ -144,6 +157,7 @@ const BandoEdit = () => {
}
]);
}
setData(data.data);
}
storeSet.main.unsetAsyncRequest();
}
@@ -276,23 +290,24 @@ const BandoEdit = () => {
icon="pi pi-check"
iconPos="right"/>
</div> : null*/}
{!isAsyncRequest
{!isEmpty(data)
? <Steps
model={stepItems}
activeIndex={activeStep}
readOnly={false}/>
: <BlockingOverlay shouldDisplay={isAsyncRequest}/>}
: null}
<BlockingOverlay shouldDisplay={isAsyncRequest}/>
<div className="appPage__spacer"></div>
<Messages ref={bandoMsgs}/>
{!isAsyncRequest
{!isEmpty(data)
? <>
{activeStep === 0
? <BandoEditFormStep1 initialData={data} ref={formRef}/> : null}
? <BandoEditFormStep1 initialData={data} ref={formRef} status={data.status}/> : null}
{activeStep === 1
? <BandoEditFormStep2 initialData={data} ref={formRef}/> : null}
? <BandoEditFormStep2 initialData={data} ref={formRef} status={data.status}/> : null}
<div className="appPageSection">
<h2>{__('Crea o modifica il Form compilabile dal Beneficiario', 'gepafin')}</h2>
@@ -300,11 +315,13 @@ const BandoEdit = () => {
<div className="row">
<Button
type="button"
outlined={data.status === 'PUBLISH'}
onClick={openBandoFormManagement}
label={__('Crea/modifica form', 'gepafin')}/>
<Button
type="button"
outlined={data.status === 'PUBLISH'}
onClick={openBandoFlowManagement}
icon="pi pi-sitemap"
iconPos="right"

View File

@@ -1,10 +1,10 @@
import React, { useEffect, useState, useCallback, useRef } from 'react';
import { __ } from '@wordpress/i18n';
import { __, sprintf } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { isEmpty, head } from 'ramda';
// store
import { storeSet, useStore } from '../../store';
import { storeGet, storeSet, useStore } from '../../store';
// api
import FormsService from '../../service/forms-service';
@@ -19,6 +19,7 @@ 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();
@@ -28,8 +29,12 @@ const BandoFlowEdit = () => {
const flowEdges = useStore().main.flowEdges();
const [formOptions, setFormOptions] = useState([]);
const [initialForm, setInitialForm] = useState(0);
const [mainFieldOptions, setMainFieldOptions] = useState([]);
const [mainField, setMainField] = useState('');
const [isFlowAllowed, setIsFlowAllowed] = useState(false);
const [finalForm, setFinalForm] = useState(0);
const flowMsgs = useRef(null);
const toast = useRef(null);
const getBandoId = () => {
const parsed = parseInt(id)
@@ -38,7 +43,7 @@ const BandoFlowEdit = () => {
const goBack = () => {
const bandoId = getBandoId();
navigate(`/tenders/${bandoId}/forms`);
navigate(`/bandi/${bandoId}/forms`);
}
const confirmDelete = (event) => {
@@ -55,9 +60,15 @@ const BandoFlowEdit = () => {
};
const doDelete = () => {
if (flowMsgs.current) {
flowMsgs.current.clear();
}
storeSet.main.flowData([]);
storeSet.main.flowEdges([]);
setInitialForm(0);
setMainFieldOptions([]);
setMainField('');
setIsFlowAllowed(false);
setFinalForm(0);
}
@@ -94,15 +105,12 @@ const BandoFlowEdit = () => {
const getFlowCreateCallback = (data) => {
if (data.status === 'SUCCESS') {
if (flowMsgs.current) {
flowMsgs.current.show([
{
id: '99',
sticky: true, severity: 'success', summary: '',
detail: __('Flow è salvato.', 'gepafin'),
closable: false
}
]);
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: __('Il flusso è stato aggiornato corretamente!', 'gepafin')
});
}
}
storeSet.main.unsetAsyncRequest();
@@ -117,7 +125,7 @@ const BandoFlowEdit = () => {
if (data.status === 'SUCCESS') {
const formOptions = data.data.map(o => ({ label: o.label, value: o.id }))
storeSet.main.flowForms(data.data);
setFormOptions(formOptions);
setFormOptions([{label: '', value: ''}, ...formOptions]);
}
storeSet.main.unsetAsyncRequest();
}
@@ -133,7 +141,11 @@ const BandoFlowEdit = () => {
storeSet.main.flowEdges(data.data.flowEdges);
setInitialForm(data.data.initialForm);
setFinalForm(data.data.finalForm);
const flowDataItem = head(data.data.flowData.filter(o => !isEmpty(o.chosenField)));
if (flowDataItem) {
setMainField(flowDataItem.chosenField);
}
}
storeSet.main.unsetAsyncRequest();
}
@@ -143,6 +155,66 @@ 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 = [];
if (field) {
options = head(field.settings.filter(o => o.name === 'options'));
}
if (field && options.value && options.value.length === flowForms.length - 2) {
setIsFlowAllowed(true);
} else {
setIsFlowAllowed(false);
let msg = 'Non è possibile creare il flusso. Il campo principale deve avere esattamente %s opzioni.';
if (flowForms.length - 2 === 1) {
msg = 'Non è possibile creare il flusso. Il campo principale deve avere esattamente %s opzione.';
}
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
}
]);
}
}
}, [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]);
useEffect(() => {
const bandoId = getBandoId();
storeSet.main.setAsyncRequest();
@@ -161,6 +233,8 @@ const BandoFlowEdit = () => {
closable: false
}
]);
} else {
flowMsgs.current.clear();
}
}, [forms]);
@@ -170,7 +244,7 @@ const BandoFlowEdit = () => {
storeSet.main.flowData([]);
storeSet.main.flowEdges([]);
}
}, [])
}, []);
return (
<div className="appPage">
@@ -184,28 +258,42 @@ const BandoFlowEdit = () => {
<div className="appPage__spacer"></div>
<div className="appPageSection">
<div className="row">
<div className="appForm__cols">
<div className="appForm__field">
<label htmlFor="initialForm">{__('Scegli form iniziale', 'gepafin')}</label>
<Dropdown
id="initialForm"
value={initialForm}
onChange={(e) => updateInitialForm(e.value)}
optionDisabled={(opt) => finalForm === opt.value}
optionDisabled={(opt) => finalForm === opt.value || isEmpty(opt.value)}
options={formOptions}
optionLabel="label"
optionValue="value"
placeholder={__('Scegli il form', 'gepafin')}/>
</div>
{forms.length > 2
{forms.length > 2 && initialForm && mainFieldOptions
? <div className="appForm__field">
<label htmlFor="mainField">{__('Scegli il campo principale', 'gepafin')}</label>
<Dropdown
id="mainField"
value={mainField}
onChange={(e) => setMainField(e.value)}
optionDisabled={(opt) => isEmpty(opt.value)}
options={mainFieldOptions}
optionLabel="label"
optionValue="value"
placeholder={__('Scegli il campo', 'gepafin')}/>
</div> : null}
{forms.length > 2 && mainField && isFlowAllowed || forms.length === 2 && isFlowAllowed
? <div className="appForm__field">
<label htmlFor="finalForm">{__('Scegli form finale', 'gepafin')}</label>
<Dropdown
id="finalForm"
value={finalForm}
onChange={(e) => setFinalForm(e.value)}
optionDisabled={(opt) => initialForm === opt.value}
optionDisabled={(opt) => initialForm === opt.value || isEmpty(opt.value)}
options={formOptions}
optionLabel="label"
optionValue="value"
@@ -217,13 +305,14 @@ const BandoFlowEdit = () => {
<div className="appPage__spacer"></div>
<div className="appPageSection">
{forms.length >= 2
? <FlowBuilder initialForm={initialForm} finalForm={finalForm}/>
: <Messages ref={flowMsgs}/>}
<Messages ref={flowMsgs}/>
{forms.length >= 2 && isFlowAllowed
? <FlowBuilder initialForm={initialForm} finalForm={finalForm} mainField={mainField}/> : null}
</div>
<div className="appPage__spacer"></div>
<Toast ref={toast}/>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button

View File

@@ -24,26 +24,26 @@ const BandoForms = () => {
const [forms, setForms] = useState([]);
const doCreateNewForm = () => {
navigate(`/tenders/${id}/forms/new`);
navigate(`/bandi/${id}/forms/new`);
}
const goToEditBando = () => {
navigate(`/tenders/${id}`);
navigate(`/bandi/${id}`);
}
const openBandoFlowManagement = () => {
navigate(`/tenders/${id}/flow`);
navigate(`/bandi/${id}/flow`);
}
const goToEditForm = () => {
if (selectedForm && selectedForm !== 0) {
navigate(`/tenders/${id}/forms/${selectedForm}`);
navigate(`/bandi/${id}/forms/${selectedForm}`);
}
}
const goToEditFormFromTemplate = () => {
console.log('goToEditFormFromTemplate', selectedTemplate)
//navigate(`/tenders/${id}`);
//navigate(`/bandi/${id}`);
}
const getFormsCallback = (data) => {

View File

@@ -4,6 +4,9 @@ import { __ } from '@wordpress/i18n';
// components
import ElementSettingRepeater from '../ElementSettingRepeater';
import { InputText } from 'primereact/inputtext';
import { MultiSelect } from 'primereact/multiselect';
import { mimeTypes } from '../../../../../../configData';
const ElementSetting = ({ setting, changeFn, updateDataFn }) => {
@@ -19,7 +22,15 @@ const ElementSetting = ({ setting, changeFn, updateDataFn }) => {
<label htmlFor={setting.name}>{settingLabels[setting.name]}</label>
{setting.name === 'options'
? <ElementSettingRepeater value={setting.value} name={setting.name} setDataFn={updateDataFn}/>
: <InputText id={setting.name} aria-describedby={`${setting.name}-help`}
: setting.name === 'mime'
? <MultiSelect
value={setting.value}
onChange={(e) => updateDataFn(setting.name, e.value)}
options={mimeTypes}
optionLabel="name"
display="chip"
placeholder={__('Scegli', 'gepafin')} />
: <InputText id={setting.name} aria-describedby={`${setting.name}-help`}
value={setting.value}
onChange={(e) => changeFn(e.target.value, setting.name)}/>}
</div>

View File

@@ -56,7 +56,7 @@ const ElementSettingRepeater = ({
<Button icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
</div>
</div>)}
<Button type="button" label={__('Aggiungi', 'gepafin')} onClick={addNewItem}/>
<Button type="button" outlined label={__('Aggiungi', 'gepafin')} onClick={addNewItem}/>
</div>
)
}

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { head, isNil } from 'ramda';
import { head, isEmpty, isNil } from 'ramda';
import { __, sprintf } from '@wordpress/i18n';
import { klona } from 'klona';
@@ -13,6 +13,7 @@ import { Tag } from 'primereact/tag';
import { TabView, TabPanel } from 'primereact/tabview';
import { InputSwitch } from 'primereact/inputswitch';
import ElementSetting from './components/ElementSetting';
import { Dropdown } from 'primereact/dropdown';
const BuilderElementSettings = ({ closeSettings }) => {
const elements = useStore().main.formElements();
@@ -20,7 +21,17 @@ const BuilderElementSettings = ({ closeSettings }) => {
const [activeElementData, setActiveElementData] = useState({});
const [settings, setSettings] = useState([]);
const [validators, setValidators] = useState({});
const textBasedValidatorFields = ['min', 'max', 'minLength', 'maxLength', 'pattern', 'custom'];
const textBasedValidatorFields = ['min', 'max', 'minLength', 'maxLength', 'pattern'];
const customValidationOptions = [
{value: 'isPIVA', label: 'isPIVA'},
{value: 'isCodiceFiscale', label: 'isCodiceFiscale'},
{value: 'isCAP', label: 'isCAP'},
{value: 'isIBAN', label: 'isIBAN'},
{value: 'isEmail', label: 'isEmail'},
{value: 'isEmailPEC', label: 'isEmailPEC'},
{value: 'isUrl', label: 'isUrl'},
{value: 'isMarcaDaBollo', label: 'isMarcaDaBollo'}
]
const onChange = (value, name) => {
const newSettings = settings
@@ -89,7 +100,7 @@ const BuilderElementSettings = ({ closeSettings }) => {
setSettings([]);
setValidators({})
}
}, [activeElement])
}, [activeElement]);
return (activeElementData
? <div className="formElementSettings">
@@ -110,13 +121,13 @@ const BuilderElementSettings = ({ closeSettings }) => {
className="formElementSettings__field" key={k}>
{k === 'isRequired'
? <div className="formElementSettings__field">
<label htmlFor={k}>{__('Required?', 'gepafin')}</label>
<label htmlFor={k}>{__('Obligatorio?', 'gepafin')}</label>
<InputSwitch
checked={validators[k]}
onChange={(e) => toggleRequired(e.value, k)}/>
</div>
: null}
{textBasedValidatorFields.includes(k)
{textBasedValidatorFields.includes(k) || 'custom' === k
? <div className="formElementSettings__field">
<label htmlFor={`enable_${k}`}>{sprintf(__('Set %s', 'gepafin'), k)}</label>
<InputSwitch
@@ -124,6 +135,19 @@ const BuilderElementSettings = ({ closeSettings }) => {
onChange={(e) => showField(e.value, k)}/>
</div>
: null}
{k === 'custom' && !isNil(validators[k])
? <div className="formElementSettings__field">
<label htmlFor={k}>{__('Personalizzato', 'gepafin')}</label>
<Dropdown
id={`enable_${k}`}
value={validators[k]}
onChange={(e) => onChangeValidator(e.value, k)}
options={customValidationOptions}
optionLabel="label"
optionValue="value"
placeholder={__('Scegli', 'gepafin')}/>
</div>
: null}
{textBasedValidatorFields.includes(k) && !isNil(validators[k])
? <div className="formElementSettings__field">
<label htmlFor={k}>{k}</label>

View File

@@ -1,4 +1,4 @@
import React, { useCallback } from 'react'
import React, { useCallback, useEffect } from 'react'
import { __ } from '@wordpress/i18n';
import { isEmpty } from 'ramda';
@@ -47,6 +47,12 @@ const FormBuilder = () => {
storeSet.main.activeElement('');
}
useEffect(() => {
return () => {
storeSet.main.activeElement('');
}
}, []);
return (
<>
<Sidebar visible={!isEmpty(activeElement)} onHide={closeSettings} className="formBuilder__elementSettings">

View File

@@ -1,9 +1,10 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import { __ } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { klona } from 'klona';
import { isEmpty } from 'ramda';
// store
import { storeSet, storeGet, useStore } from '../../store';
@@ -13,17 +14,23 @@ import FormBuilder from './components/FormBuilder';
import { Button } from 'primereact/button';
import { ConfirmPopup, confirmPopup } from 'primereact/confirmpopup';
import { InputText } from 'primereact/inputtext';
import { Toast } from 'primereact/toast';
import { ConfirmDialog } from 'primereact/confirmdialog';
// api
import FormsService from '../../service/forms-service';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import { elementItems } from '../../tempData';
import { Messages } from 'primereact/messages';
const BandoFormsEdit = () => {
const { id, formId } = useParams();
const navigate = useNavigate();
const [formName, setFormName] = useState('');
const [visibleConfirmation, setVisibleConfirmation] = useState(false);
const isAsyncRequest = useStore().main.isAsyncRequest();
const formMsgs = useRef(null);
const toast = useRef(null);
const getBandoId = () => {
const parsed = parseInt(id)
@@ -32,13 +39,46 @@ const BandoFormsEdit = () => {
const goBack = () => {
const bandoId = getBandoId();
navigate(`/tenders/${bandoId}/forms`);
navigate(`/bandi/${bandoId}/forms`);
}
const doSave = (shouldRedirect = false) => {
if (formMsgs.current) {
formMsgs.current.clear();
}
const content = storeGet.main.formElements();
if (isEmpty(formName) || isEmpty(content)) {
if (isEmpty(formName)) {
if (formMsgs.current) {
formMsgs.current.show([
{
id: '99',
sticky: true, severity: 'error', summary: '',
detail: __('Nome di form è obligatorio.', 'gepafin'),
closable: true
}
]);
}
}
if (isEmpty(content)) {
if (formMsgs.current) {
formMsgs.current.show([
{
id: '99',
sticky: true, severity: 'error', summary: '',
detail: __('Devi aggiungere almeno uno campo.', 'gepafin'),
closable: true
}
]);
}
}
}
const bandoId = getBandoId();
const parsedFormId = parseInt(formId)
const parsedFormId = parseInt(formId);
const bandoFormId = !isNaN(parsedFormId) ? parsedFormId : 0;
const formData = {
label: formName,
@@ -47,9 +87,20 @@ const BandoFormsEdit = () => {
storeSet.main.setAsyncRequest();
if (bandoFormId === 0) {
FormsService.createFormForCall(bandoId, formData, (data) => formCreateCallback(data, shouldRedirect), errFormCreateCallback);
FormsService.createFormForCall(
bandoId,
formData,
(data) => formCreateCallback(data, shouldRedirect),
errFormCreateCallback
);
} else {
FormsService.updateForm(bandoFormId, formData, (data) => formCreateCallback(data, shouldRedirect), errFormCreateCallback);
FormsService.updateForm(
bandoFormId,
formData,
(data) => formCreateCallback(data, shouldRedirect),
errFormCreateCallback,
[['forceDeleteFlow', false]]
);
}
}
@@ -58,18 +109,59 @@ const BandoFormsEdit = () => {
storeSet.main.unsetAsyncRequest();
const bandoId = getBandoId();
if (shouldRedirect) {
navigate(`/tenders/${bandoId}/forms/${data.data.id}/preview`);
navigate(`/bandi/${bandoId}/forms/${data.data.id}/preview`);
return;
}
if (data.data.id) {
navigate(`/tenders/${bandoId}/forms/${data.data.id}`);
navigate(`/bandi/${bandoId}/forms/${data.data.id}`);
}
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: __('Il form è stato aggiornato corretamente!', 'gepafin')
});
}
}
}
const errFormCreateCallback = (data) => {
console.log('errFormCreateCallback', data)
storeSet.main.unsetAsyncRequest();
if (data.status === 'BAD_REQUEST') {
setVisibleConfirmation(true);
} else {
if (toast.current) {
toast.current.show({
severity: 'error',
summary: '',
detail: data.message
});
}
}
}
const acceptModification = () => {
const content = storeGet.main.formElements();
const parsedFormId = parseInt(formId);
const bandoFormId = !isNaN(parsedFormId) ? parsedFormId : 0;
const formData = {
label: formName,
content
}
storeSet.main.setAsyncRequest();
FormsService.updateForm(
bandoFormId,
formData,
(data) => formCreateCallback(data, false),
errFormCreateCallback,
[['forceDeleteFlow', true]]
);
}
const rejectModification = () => {
setVisibleConfirmation(false);
}
const openPreview = () => {
@@ -101,7 +193,7 @@ const BandoFormsEdit = () => {
const formDeleteCallback = (data) => {
if (data.status === 'SUCCESS') {
const bandoId = getBandoId();
navigate(`/tenders/${bandoId}/forms`);
navigate(`/bandi/${bandoId}/forms`);
}
storeSet.main.unsetAsyncRequest();
}
@@ -170,6 +262,19 @@ const BandoFormsEdit = () => {
<div className="appPage__spacer"></div>
<Messages ref={formMsgs}/>
<ConfirmDialog
group="declarative"
visible={visibleConfirmation}
onHide={() => setVisibleConfirmation(false)}
message={__('Le modifiche al modulo influiscono sul flusso precedentemente creato, quindi deve essere ricreato. Confermi le modifiche?', 'gepafin')}
header={__('Conferma le modifiche', 'gepafin')}
icon="pi pi-exclamation-triangle"
accept={acceptModification}
acceptLabel={__('Si', 'gepafin')}
reject={rejectModification}
style={{ maxWidth: '500px' }} />
<div className="appForm__field">
<label htmlFor="label">{__('Assegna un nome a questo form', 'gepafin')}</label>
<InputText
@@ -190,6 +295,7 @@ const BandoFormsEdit = () => {
<div className="appPage__spacer"></div>
<Toast ref={toast} />
<div className="appPageSection">
<div className="appPageSection__actions">
<Button

View File

@@ -17,6 +17,18 @@ import { Button } from 'primereact/button';
import FormField from '../../components/FormField';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
// tools
import {
isCAP,
isCodiceFiscale,
isEmail,
isEmailPEC,
isIBAN,
isMarcaDaBollo,
isPIVA,
isUrl
} from '../../helpers/validators';
const BandoFormsPreview = () => {
const { id, formId } = useParams();
const navigate = useNavigate();
@@ -28,8 +40,20 @@ const BandoFormsPreview = () => {
handleSubmit,
formState: { errors },
getValues,
register,
setValue
} = useForm({ defaultValues: {}, mode: 'onChange' });
const values = getValues();
const validationFns = {
isPIVA,
isCodiceFiscale,
isCAP,
isIBAN,
isEmail,
isEmailPEC,
isUrl,
isMarcaDaBollo
}
const onSubmit = () => {}
@@ -38,7 +62,7 @@ const BandoFormsPreview = () => {
const bandoId = !isNaN(parsedId) ? parsedId : 0;
const parsedFormId = parseInt(formId)
const bandoFormId = !isNaN(parsedFormId) ? parsedFormId : 0;
navigate(`/tenders/${bandoId}/forms/${bandoFormId}`);
navigate(`/bandi/${bandoId}/forms/${bandoFormId}`);
}
const getFormCallback = (data) => {
@@ -94,17 +118,49 @@ const BandoFormsPreview = () => {
const label = head(o.settings.filter(o => o.name === 'label'));
const placeholder = head(o.settings.filter(o => o.name === 'placeholder'));
const options = head(o.settings.filter(o => o.name === 'options'));
const step = head(o.settings.filter(o => o.name === 'step'));
const mime = head(o.settings.filter(o => o.name === 'mime'));
let mimeValue = '';
if (mime) {
mimeValue = mime.value.map(o => o.code).join(',');
}
const validations = Object.keys(o.validators).reduce((acc, cur) => {
if (o.validators[cur]) {
if (['min', 'max', 'minLength', 'maxLength', 'maxSize'].includes(cur)) {
acc[cur] = parseInt(o.validators[cur]);
} else if ('pattern' === cur) {
acc[cur] = new RegExp(o.validators[cur])
} else if ('isRequired' === cur) {
acc[cur] = o.validators[cur]
} else if ('custom' === cur && validationFns[o.validators[cur]]) {
if (!acc.validate) {
acc.validate = {}
}
acc.validate[cur] = validationFns[o.validators[cur]]
}
}
return acc;
}, {});
return <FormField
key={o.id}
type={o.name}
fieldName={`field_${o.id}`}
fieldName={o.id}
label={label ? label.value : ''}
placeholder={placeholder ? placeholder.value : ''}
control={control}
register={register}
errors={errors}
defaultValue={values[`field_${o.id}`]}
config={o.validators}
defaultValue={values[o.id]}
maxFractionDigits={step}
accept={mimeValue}
config={validations}
options={options ? options.value : []}
setDataFn={setValue}
sourceId={0}
/>
})}
</form>

View File

@@ -19,6 +19,7 @@ import { Button } from 'primereact/button';
import BandoService from '../../service/bando-service';
import { Messages } from 'primereact/messages';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import renderHtmlContent from '../../helpers/renderHtmlContent';
const BandoView = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
@@ -29,7 +30,7 @@ const BandoView = () => {
const bandoMsgs = useRef(null);
const closePreview = () => {
navigate(`/tenders/${id}`);
navigate(`/bandi/${id}`);
}
const getCallback = (data) => {
@@ -102,7 +103,7 @@ const BandoView = () => {
<div className="appPageSection__withBorder">
<h2>{__('Descrizione breve', 'gepafin')}</h2>
<div className="row rowContent">
<p>{data.descriptionShort}</p>
{renderHtmlContent(data.descriptionShort)}
</div>
</div>
@@ -133,7 +134,7 @@ const BandoView = () => {
<div className="appPageSection__withBorder">
<h2>{__('Descrizione dettagliata', 'gepafin')}</h2>
<div className="row rowContent">
<p>{data.descriptionLong}</p>
{renderHtmlContent(data.descriptionLong)}
</div>
</div>
@@ -196,6 +197,7 @@ const BandoView = () => {
<label htmlFor="newQuestion">{__('Fai una domanda', 'gepafin')}</label>
<InputTextarea
id="newQuestion"
disabled
rows={7}
value={newQuestion}
placeholder={__('Digita qui la tua domanda', 'gepafin')}

View File

@@ -21,6 +21,7 @@ import { Messages } from 'primereact/messages';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import FaqItemService from '../../service/faq-item-service';
import ApplicationService from '../../service/application-service';
import renderHtmlContent from '../../helpers/renderHtmlContent';
const BandoViewBeneficiario = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
@@ -28,7 +29,7 @@ const BandoViewBeneficiario = () => {
const navigate = useNavigate();
const [data, setData] = useState({});
const [newQuestion, setNewQuestion] = useState('');
const [isApplRequest, setIsApplRequest] = useState(true);
const [applicationObj, setApplicationObj] = useState(true);
const bandoMsgs = useRef(null);
const scaricaBando = () => {
@@ -39,8 +40,38 @@ const BandoViewBeneficiario = () => {
}
const getBandoId = () => {
const parsed = parseInt(id)
return !isNaN(parsed) ? parsed : 0;
}
const submitApplication = () => {
navigate(`/tenders/${id}/application`);
if (applicationObj && applicationObj.id) {
navigate(`/imieibandi/${applicationObj.id}`);
} else {
const bandoId = getBandoId();
ApplicationService.createApplication(bandoId, {}, createApplCallback, errCreateApplCallback)
}
}
const createApplCallback = (data) => {
storeSet.main.unsetAsyncRequest();
if (data.status === 'SUCCESS') {
navigate(`/imieibandi/${data.data.id}`);
}
}
const errCreateApplCallback = (data) => {
if (bandoMsgs.current && data.message) {
bandoMsgs.current.show([
{
sticky: true, severity: 'error', summary: '',
detail: data.message,
closable: true
}
]);
}
storeSet.main.unsetAsyncRequest();
}
const saveToFavourites = () => {
@@ -102,7 +133,6 @@ const BandoViewBeneficiario = () => {
if (data.status === 'SUCCESS') {
setData(getFormattedBandiData(data.data));
}
storeSet.main.unsetAsyncRequest();
}
const errGetBandoCallback = (data) => {
@@ -116,7 +146,6 @@ const BandoViewBeneficiario = () => {
]);
}
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
const getFormattedBandiData = (data) => {
@@ -126,31 +155,23 @@ const BandoViewBeneficiario = () => {
const getApplCallback = (data) => {
if (data.status === 'SUCCESS') {
console.log(data.data)
if(data.data.length) {
setApplicationObj(data.data[0]);
}
}
storeSet.main.unsetAsyncRequest();
}
const errGetApplCallback = (data) => {
/*if (bandoMsgs.current && data.message) {
bandoMsgs.current.show([
{
sticky: true, severity: 'error', summary: '',
detail: data.message,
closable: true
}
]);
}*/
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
useEffect(() => {
const parsed = parseInt(id)
const bandoId = !isNaN(parsed) ? parsed : 0;
const bandoId = getBandoId();
storeSet.main.setAsyncRequest();
BandoService.getBando(bandoId, getBandoCallback, errGetBandoCallback);
ApplicationService.getApplications(getApplCallback, errGetApplCallback)
ApplicationService.getApplications(getApplCallback, errGetApplCallback, [['callId', bandoId]])
}, [id]);
return (
@@ -181,7 +202,7 @@ const BandoViewBeneficiario = () => {
<div className="appPageSection__withBorder">
<h2>{__('Descrizione breve', 'gepafin')}</h2>
<div className="row rowContent">
<p>{data.descriptionShort}</p>
{renderHtmlContent(data.descriptionShort)}
</div>
</div>
@@ -212,7 +233,7 @@ const BandoViewBeneficiario = () => {
<div className="appPageSection__withBorder">
<h2>{__('Descrizione dettagliata', 'gepafin')}</h2>
<div className="row rowContent">
<p>{data.descriptionLong}</p>
{renderHtmlContent(data.descriptionLong)}
</div>
</div>
@@ -309,7 +330,7 @@ const BandoViewBeneficiario = () => {
icon="pi pi-download" iconPos="right"/>
<Button
type="button"
disabled={isApplRequest}
disabled={isAsyncRequest}
onClick={submitApplication}
label={__('Presenta Domanda', 'gepafin')}
icon="pi pi-save" iconPos="right"/>

View File

@@ -170,7 +170,7 @@ const LatestBandiTable = () => {
};
const actionsBodyTemplate = (rowData) => {
return <Link to={`/tenders/${rowData.id}`}>
return <Link to={`/bandi/${rowData.id}`}>
<Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small" iconPos="right" />
</Link>
}

View File

@@ -11,7 +11,7 @@ const Dashboard = () => {
const navigate = useNavigate();
const onGoToCreateNewBando = () => {
navigate('/tenders/new');
navigate('/bandi/new');
}
const onGoToUsers = () => {

View File

@@ -138,7 +138,7 @@ const LatestBandiTable = () => {
};
const actionsBodyTemplate = (rowData) => {
return <Link to={`/tenders/${rowData.id}`}>
return <Link to={`/bandi/${rowData.id}`}>
<Button severity="info" label={__('Partecipa', 'gepafin')} icon="pi pi-arrow-right" size="small" iconPos="right" />
</Link>
}

View File

@@ -140,7 +140,7 @@ const MyLatestSubmissionsTable = () => {
};
const actionsBodyTemplate = (rowData) => {
return <Link to={`/applications/${rowData.id}`}>
return <Link to={`/imieibandi/${rowData.id}`}>
<Button severity="info" label={__('Modifica', 'gepafin')} icon="pi pi-pencil" size="small" iconPos="right" />
</Link>
}

View File

@@ -62,6 +62,10 @@ const Login = () => {
setLoading(false);
}
const loginWithSpid = () => {
console.log('spid')
}
useEffect(() => {
if (!isEmpty(token)) {
setLoading(true);
@@ -80,6 +84,36 @@ const Login = () => {
<Messages ref={errorMsgs}/>
<button className="appPageLogin__spidBtn" onClick={loginWithSpid}>
<svg width="31" height="31" viewBox="0 0 31 31" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clipPath="url(#clip0_1156_13356)">
<path
d="M30.5767 15.0422C30.5767 23.3285 23.8629 30.0422 15.5767 30.0422C7.29552 30.0422 0.57666 23.3285 0.57666 15.0422C0.57666 6.75599 7.29552 0.0422363 15.5767 0.0422363C23.8629 0.0422363 30.5767 6.75599 30.5767 15.0422Z"
fill="white"/>
<path
d="M15.6175 16.3288C14.3717 16.3288 13.3455 15.9102 12.5389 15.0627C11.7322 14.2203 11.3289 13.1736 11.3289 11.9279C11.3289 10.677 11.7322 9.6406 12.5338 8.81351C13.3353 7.98642 14.3564 7.57287 15.6073 7.57287C16.853 7.57287 17.869 7.99152 18.6502 8.84414C19.4313 9.69166 19.8244 10.7332 19.8244 11.984C19.8244 13.2247 19.4313 14.2611 18.6502 15.0882C17.869 15.9102 16.8632 16.3288 15.6175 16.3288Z"
fill="#0066CC"/>
<path
d="M11.3289 22.4605C11.3289 21.2097 11.7322 20.1733 12.5287 19.3462C13.3302 18.5191 14.3513 18.1055 15.6124 18.1055C16.8581 18.1055 17.8741 18.5242 18.6502 19.3768C19.4313 20.2294 19.8244 21.271 19.8244 22.5167"
fill="#0066CC"/>
</g>
<defs>
<clipPath id="clip0_1156_13356">
<rect width="30" height="30" fill="white" transform="translate(0.57666 0.0422363)"/>
</clipPath>
</defs>
</svg>
<span>{__('Entra con SPID', 'gepafin')}</span>
</button>
<div className="appPage__spacer"></div>
<div className="appPageSection__hr">
<span>{__('Oppure', 'gepafin')}</span>
</div>
<div className="appPage__spacer"></div>
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
<FormField
type="textinput"
@@ -101,7 +135,9 @@ const Login = () => {
config={{ required: __('È obbligatorio', 'gepafin') }}
/>
<Button label={__('Accedi', 'gepafin')} disabled={loading}/>
<Button
label={__('Accedi', 'gepafin')}
disabled={loading}/>
</form>
</div>
</div>

View File

@@ -0,0 +1,177 @@
import React, { useRef, useState, useEffect } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { useForm } from 'react-hook-form';
import { classNames } from 'primereact/utils';
import { isEmpty } from 'ramda';
// api
import AuthenticationService from '../../service/authentication-service';
// tools
import { isCodiceFiscale, isEmail } from '../../helpers/validators';
// store
import { storeSet, useStore } from '../../store';
// components
import FormField from '../../components/FormField';
import LogoIcon from '../../icons/LogoIcon';
import { Button } from 'primereact/button';
import { Messages } from 'primereact/messages';
const Registration = () => {
const token = useStore().main.token();
const [loading, setLoading] = useState(false);
const errorMsgs = useRef(null);
const {
control,
handleSubmit,
formState: { errors },
} = useForm({ mode: 'onChange' });
const onSubmit = (formData) => {
errorMsgs.current.clear();
//setLoading(true);
console.log('formData', formData, errors);
//AuthenticationService.login(request, regCallback, regError);
};
const regCallback = (data) => {
if (data.status === 'SUCCESS') {
storeSet.main.setAuthData({
token: data.data.token,
userData: data.data.user
});
} else {
errorMsgs.current.show([
{
sticky: true, severity: 'error', summary: '',
detail: data.message,
closable: true
}
]);
}
setLoading(false);
}
const regError = (err) => {
errorMsgs.current.show([
{
sticky: true, severity: 'error', summary: '',
detail: sprintf(__('%s', 'gepafin'), err),
closable: true
}
]);
setLoading(false);
}
useEffect(() => {
if (!isEmpty(token)) {
setLoading(true);
window.location.replace('/')
}
}, [token]);
return (
<div className={classNames(['appPage', 'appPageLogin'])}>
<div className="appPageLogin__wrapper">
<LogoIcon/>
<h1>{__('Completamento anagrafica personale', 'gepafin')}</h1>
<span>{__('Per favore, inserisci i tuoi dati.', 'gepafin')}</span>
<div className="appPage__spacer"></div>
<Messages ref={errorMsgs}/>
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
<div className="appForm__cols">
<FormField
type="textinput"
fieldName="first_name"
label={__('Nome', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="Francesco"
/>
<FormField
type="textinput"
fieldName="second_name"
label={__('Cognome', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder="Molini"
/>
</div>
<div className="appForm__cols">
<FormField
type="textinput"
fieldName="fiscal_code"
label={__('Codice fiscale', 'gepafin')}
control={control}
errors={errors}
config={{
required: __('È obbligatorio', 'gepafin'),
validate: {
isCodiceFiscale
}
}}
placeholder="ABC1234"
/>
<FormField
type="datepicker"
fieldName="birth_date"
label={__('Data di nascita', 'gepafin')}
control={control}
errors={errors}
config={{ required: __('È obbligatorio', 'gepafin') }}
placeholder=""
/>
</div>
<div className="appForm__cols">
<FormField
type="textinput"
fieldName="email"
label={__('Email', 'gepafin')}
control={control}
errors={errors}
config={{
required: __('È obbligatorio', 'gepafin'),
validate: {
isEmail
}
}}
placeholder="sample@example.com"
/>
<FormField
type="textinput"
fieldName="phone"
label={__('Telefono', 'gepafin')}
control={control}
errors={errors}
config={{
required: __('È obbligatorio', 'gepafin'),
positive: v => parseInt(v) > 0
}}
placeholder=""
/>
</div>
<Button
label={__('Accedi', 'gepafin')}
disabled={loading}/>
</form>
</div>
</div>
)
}
export default Registration;

View File

@@ -17,6 +17,7 @@ import BandoFormsPreview from './pages/BandoFormsPreview';
import BandoFlowEdit from './pages/BandoFlowEdit';
import Applications from './pages/Applications';
import BandoApplication from './pages/BandoApplication';
import Registration from './pages/Registration';
const routes = ({ role }) => {
return (
@@ -26,48 +27,49 @@ const routes = ({ role }) => {
{'ROLE_SUPER_ADMIN' === role ? <Dashboard/> : null}
{'ROLE_BENEFICIARY' === role ? <DashboardBeneficiario/> : null}
</DefaultLayout>}/>
<Route path="/tenders" element={<DefaultLayout>
<Route path="/bandi" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <Bandi/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
</DefaultLayout>}/>
<Route path="/tenders/:id" element={<DefaultLayout>
<Route path="/bandi/:id" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoEdit/> : null}
{'ROLE_BENEFICIARY' === role ? <BandoViewBeneficiario/> : null}
</DefaultLayout>}/>
<Route path="/tenders/:id/preview" element={<DefaultLayout>
<Route path="/bandi/:id/preview" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoView/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
</DefaultLayout>}/>
<Route path="/tenders/:id/preview-evaluation" element={<DefaultLayout>
<Route path="/bandi/:id/preview-evaluation" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoView/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
</DefaultLayout>}/>
<Route path="/tenders/:id/forms" element={<DefaultLayout>
<Route path="/bandi/:id/forms" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoForms/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
</DefaultLayout>}/>
<Route path="/tenders/:id/forms/:formId" element={<DefaultLayout>
<Route path="/bandi/:id/forms/:formId" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoFormsEdit/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
</DefaultLayout>}/>
<Route path="/tenders/:id/forms/:formId/preview" element={<DefaultLayout>
<Route path="/bandi/:id/forms/:formId/preview" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoFormsPreview/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
</DefaultLayout>}/>
<Route path="/tenders/:id/flow" element={<DefaultLayout>
<Route path="/bandi/:id/flow" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <BandoFlowEdit/> : null}
{'ROLE_BENEFICIARY' === role ? <PageNotFound/> : null}
</DefaultLayout>}/>
<Route path="/applications" element={<DefaultLayout>
<Route path="/imieibandi" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <Applications/> : null}
</DefaultLayout>}/>
<Route path="/applications/:id/" element={<DefaultLayout>
<Route path="/imieibandi/:id/" element={<DefaultLayout>
{'ROLE_SUPER_ADMIN' === role ? <PageNotFound/> : null}
{'ROLE_BENEFICIARY' === role ? <BandoApplication/> : null}
</DefaultLayout>}/>
</Route>
<Route exact path="/login" element={<Login/>}/>
<Route exact path="/registration" element={<Registration/>}/>
{/*<Route exact path="/forgot-password" element={<ForgotPassword/>}/>*/}
<Route path="*" element={<PageNotFound/>}/>
</Routes>)

View File

@@ -4,11 +4,23 @@ const API_BASE_URL = process.env.REACT_APP_API_EXECUTION_ADDRESS;
export default class ApplicationService {
static getApplications = (callback, errCallback) => {
NetworkService.get(`${API_BASE_URL}/application`, callback, errCallback);
static getApplications = (callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/application`, callback, errCallback, queryParams);
};
static getApplication = (id, callback, errCallback) => {
NetworkService.get(`${API_BASE_URL}/application/${id}`, callback, errCallback);
};
static getApplicationForm = (id, callback, errCallback, queryParams) => {
NetworkService.get(`${API_BASE_URL}/application/${id}/form/next-previous`, callback, errCallback, queryParams);
};
static createApplication = (id, body, callback, errCallback) => {
NetworkService.post(`${API_BASE_URL}/application/call/${id}`, body, callback, errCallback);
};
static submitForm = (id, body, callback, errCallback, queryParams) => {
NetworkService.put(`${API_BASE_URL}/application/${id}`, body, callback, errCallback, queryParams);
};
}

View File

@@ -62,19 +62,11 @@ export default class AuthenticationService {
};
static registerUser = (registerRequest, callback, errCallback) => {
NetworkService.post(`${API_BASE_URL}/user/register`, registerRequest, callback, errCallback);
NetworkService.unauthorizedPost(`${API_BASE_URL}/user`, registerRequest, callback, errCallback);
};
static forgotPassword = (request, callback, errCallback) => {
NetworkService.unauthorizedPost(`${API_BASE_URL}/user/reset_password_request/?email=` + request, {}, callback, errCallback);
}
static checkTokenForgotPassword = (request, callback, errCallback) => {
NetworkService.unauthorizedGet(`${API_BASE_URL}/user/reset_token_check/?token=` + request, {}, callback, errCallback);
}
static changePassword = (request, callback, errCallback) => {
NetworkService.unauthorizedPatch(`${API_BASE_URL}/user/reset_password`, request, callback, errCallback);
NetworkService.unauthorizedPost(`${API_BASE_URL}/user/reset-password`, {}, callback, errCallback);
}
static me = (callback, errCallback) => {

View File

@@ -16,8 +16,8 @@ export default class FormsService {
NetworkService.post(`${API_BASE_URL}/form/call/${id}`, body, callback, errCallback);
};
static updateForm = (id, body, callback, errCallback) => {
NetworkService.put(`${API_BASE_URL}/form/${id}`, body, callback, errCallback);
static updateForm = (id, body, callback, errCallback, queryParams) => {
NetworkService.put(`${API_BASE_URL}/form/${id}`, body, callback, errCallback, queryParams);
};
static getFormById = (id, callback, errCallback) => {

View File

@@ -570,8 +570,6 @@ export const elementItems = [
],
validators: {
isRequired: true,
minLength: 11,
maxLength: 11,
custom: 'isPIVA'
}
},
@@ -593,8 +591,6 @@ export const elementItems = [
],
validators: {
isRequired: true,
minLength: 11,
maxLength: 16,
custom: 'isCodiceFiscale'
}
},
@@ -649,11 +645,11 @@ export const elementItems = [
settings: [
{
name: "label",
value: "nome@esempio.it"
value: "Campo Email"
},
{
name: "placeholder",
value: ""
value: "nome@esempio.it"
}
],
validators: {
@@ -670,11 +666,11 @@ export const elementItems = [
settings: [
{
name: "label",
value: "nome@pec.it"
value: "Campo PEC"
},
{
name: "placeholder",
value: ""
value: "nome@pec.it"
}
],
validators: {
@@ -702,54 +698,26 @@ export const elementItems = [
isRequired: false,
custom: 'isUrl'
}
},
{
id: 18,
sortOrder: 18,
name: 'textinput',
label: 'Marca da bollo',
description: 'Per inserire codice di marca da bollo',
settings: [
{
name: "label",
value: "Marca da bollo"
},
{
name: "placeholder",
value: "Numero identificativo"
}
],
validators: {
isRequired: false,
custom: 'isMarcaDaBollo'
}
}
]
/*
const flowData = {
"initialForm":9,
"finalForm":13,
"flowData":[
{
"formId":"9",
"chosenField":"a0acf568c8",
"chosenValue":""
},
{
"formId":"12",
"chosenField":"",
"chosenValue":"o8bf116e28"
},
{
"formId":"11",
"chosenField":"",
"chosenValue":"o1eb76229d"
}
],
"flowEdges":[
{
"id":"9->12",
"source":"9",
"target":"12",
"type":"smoothstep"
},
{
"id":"12->13",
"source":"12",
"target":"13",
"type":"smoothstep"
},
{
"id":"9->11",
"source":"9",
"target":"11",
"type":"smoothstep"
},
{
"id":"11->13",
"source":"11",
"target":"13",
"type":"smoothstep"
}
]
}*/