add bando page form;

This commit is contained in:
Vitalii Kiiko
2024-08-20 08:17:42 +02:00
parent 8616ae04b3
commit b4522f1580
14 changed files with 952 additions and 81 deletions

View File

@@ -7,4 +7,88 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 14px; gap: 14px;
label {
font-size: 14px;
font-style: normal;
font-weight: 700;
line-height: normal;
span {
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
margin-left: 10px;
}
}
small {
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 21px;
}
&.datepicker, &.formfieldrepeater, &.datepickerrange, &.fileupload {
.p-button:not(.p-button-danger) {
background: var(--button-secondary-background);
border: 1px solid var(--button-secondary-borderColor);
}
.p-button:not(:disabled, .p-button-danger):hover {
background: var(--button-secondary-borderColor);
}
}
}
.appForm__twoCols {
display: flex;
gap: 1rem;
justify-content: space-between;
flex-wrap: wrap;
> div {
flex: 1 1 auto;
min-width: 300px;
max-width: 700px;
display: flex;
flex-direction: column;
gap: 14px;
label {
font-weight: 400;
}
}
}
.appForm__repeaterItem {
padding: 0.5rem 0.5rem 0.5rem 1rem;
border-left: 3px solid #dadada;
&:hover {
border-color: var(--button-secondary-background);
background: #FCF7E7;
}
}
.appForm__faqHeaderControls {
display: flex;
gap: 1rem;
button {
flex: 0 0 auto;
}
> div {
flex: 1 1 auto;
}
}
.appForm__faqTab {
display: flex;
justify-content: space-between;
gap: 1rem;
}
.appForm__faqTabItem {
display: flex;
gap: 0.5rem;
} }

View File

@@ -35,6 +35,7 @@
flex-direction: column; flex-direction: column;
gap: 14px; gap: 14px;
padding: 28px; padding: 28px;
border-left: 4px solid var(--card-borderColor-color);
h1, h2, h3 { h1, h2, h3 {
margin: 0; margin: 0;
@@ -64,4 +65,11 @@
display: flex; display: flex;
gap: 24px; gap: 24px;
margin-bottom: 24px; margin-bottom: 24px;
}
.mb-2 {
margin-bottom: 4px;
}
.mb-8 {
margin-bottom: 16px;
} }

View File

@@ -9,6 +9,9 @@
--menuitem-active-color: #FFF; --menuitem-active-color: #FFF;
--menuitem-active-background: #3B7C43; --menuitem-active-background: #3B7C43;
--Black: #000; --Black: #000;
--card-borderColor-color: #EEC137;
--button-secondary-borderColor: #C79807;
--button-secondary-background: var(--menu-borderColor);
--card-full-background-color-2: #EEC137; --card-full-background-color-2: #EEC137;
--card-full-background-color-3: #FA8E42; --card-full-background-color-3: #FA8E42;

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { classNames } from 'primereact/utils';
import { Controller } from 'react-hook-form';
import { isNil } from 'ramda';
import { Calendar } from 'primereact/calendar';
const Datepicker = ({
fieldName,
label,
control,
errors,
defaultValue,
config = {},
infoText = null,
minDate = null,
maxDate = null
}) => {
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : 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 })}/>
)}/>
{infoText ? <small>{infoText}</small> : null}
</>)
}
export default Datepicker;

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { classNames } from 'primereact/utils';
import { Controller } from 'react-hook-form';
import { isNil } from 'ramda';
import { Calendar } from 'primereact/calendar';
const DatepickerRange = ({
fieldName,
label,
control,
errors,
defaultValue,
config = {},
infoText = null,
minDate = null,
maxDate = null
}) => {
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : 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}
selectionMode="range" readOnlyInput hideOnRangeSelection
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
{infoText ? <small>{infoText}</small> : null}
</>)
}
export default DatepickerRange;

View File

@@ -0,0 +1,50 @@
import React from 'react';
import { classNames } from 'primereact/utils';
import { Controller } from 'react-hook-form';
import { __ } from '@wordpress/i18n';
import { FileUpload } from 'primereact/fileupload';
const Fileupload = ({
fieldName,
label,
control,
errors,
defaultValue,
config = {},
infoText = null,
accept = 'image/*',
api = '/api/upload',
emptyText = __('Trascina qui il tuo file', 'gepafin'),
chooseLabel = __('Aggiungi immagine', 'gepafin')
}) => {
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
</label>
<Controller
name={fieldName}
control={control}
defaultValue={defaultValue}
rules={config}
render={({ field, fieldState }) => (
<FileUpload
id={field.name}
name={`${field.name}[]`}
url={api}
multiple
accept={accept}
maxFileSize={1000000}
emptyTemplate={<p>{emptyText}</p>}
chooseLabel={chooseLabel}
cancelLabel={__('Cancella', 'gepafin')}
uploadLabel={__('Carica', 'gepafin')}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
{infoText ? <small>{infoText}</small> : null}
{defaultValue ? <p>Uploaded:</p> : null}
{defaultValue}
</>)
}
export default Fileupload;

View File

@@ -0,0 +1,54 @@
import React from 'react';
import { classNames } from 'primereact/utils';
import { Controller } from 'react-hook-form';
import { InputNumber } from 'primereact/inputnumber';
const NumberInput = ({
fieldName,
label,
control,
errors,
defaultValue = 0,
config = {},
infoText = null,
inputgroup = false,
icon = null,
locale = 'it-IT',
minFractionDigits = 2,
step = 1,
min,
max
}) => {
const input = <Controller
name={fieldName}
control={control}
defaultValue={defaultValue}
rules={config}
render={({ field, fieldState }) => (
<InputNumber inputId={field.name}
value={field.value}
onValueChange={(e) => field.onChange(e.value)}
min={min}
max={max}
locale={locale} minFractionDigits={minFractionDigits} step={step}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
</label>
{inputgroup
? <div className="p-inputgroup flex-1">
<span className="p-inputgroup-addon">
{icon}
</span>
{input}
</div>
: input}
{infoText ? <small>{infoText}</small> : null}
</>)
}
export default NumberInput;

View File

@@ -0,0 +1,34 @@
import React from 'react';
import { classNames } from 'primereact/utils';
import { Controller } from 'react-hook-form';
import { InputTextarea } from 'primereact/inputtextarea';
const TextArea = ({
fieldName,
label,
control,
errors,
defaultValue,
config = {},
infoText = null
}) => {
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
</label>
<Controller
name={fieldName}
control={control}
defaultValue={defaultValue}
rules={config}
render={({ field, fieldState }) => (
<InputTextarea id={field.name}
{...field}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
{infoText ? <small>{infoText}</small> : null}
</>)
}
export default TextArea;

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { classNames } from 'primereact/utils';
import { Controller } from 'react-hook-form';
import { InputText } from 'primereact/inputtext';
const TextInput = ({
fieldName,
label,
control,
errors,
defaultValue,
config = {},
infoText = null,
inputgroup = false,
icon = null
}) => {
const input = <Controller
name={fieldName}
control={control}
defaultValue={defaultValue}
rules={config}
render={({ field, fieldState }) => (
<InputText id={field.name}
{...field}
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
</label>
{inputgroup
? <div className="p-inputgroup flex-1">
<span className="p-inputgroup-addon">
{icon}
</span>
{input}
</div>
: input}
{infoText ? <small>{infoText}</small> : null}
</>)
}
export default TextInput;

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { isNil } from 'ramda';
import { classNames } from 'primereact/utils';
// components
import TextInput from './components/TextInput';
import TextArea from './components/TextArea';
import Datepicker from './components/Datepicker';
import DatepickerRange from './components/DatepickerRange';
import Fileupload from './components/Fileupload';
import NumberInput from './components/NumberInput';
const FormField = (props) => {
const fields = {
textinput: TextInput,
textarea: TextArea,
datepicker: Datepicker,
datepickerrange: DatepickerRange,
fileupload: Fileupload,
numberinput: NumberInput
}
const Comp = !isNil(fields[props.type]) ? fields[props.type] : null;
return (!isNil(Comp)
? <div className={classNames(['appForm__field', props.type])}>
<Comp {...props} />
</div>
: null
)
}
export default FormField;

View File

@@ -0,0 +1,108 @@
import React, { useRef, useEffect, useState } from 'react';
import { classNames } from 'primereact/utils';
import { __ } from '@wordpress/i18n';
import { InputText } from 'primereact/inputtext';
import { Button } from 'primereact/button';
import { Menu } from 'primereact/menu';
import { Dropdown } from 'primereact/dropdown';
const FormFieldRepeater = ({
data,
setDataFn,
fieldName,
options = [],
errors,
register,
label,
infoText
}) => {
const forMenu = useRef(null);
const [stateFieldData, setStateFieldData] = useState([]);
const menuItems = [
{
type: 'existing',
label: __('Esistente', 'gepafin'),
command: (data) => {
setStateFieldData([...stateFieldData, {id: null, value: '', status: data.item.type}]);
}
},
{
type: 'new',
label: __('Nuovo', 'gepafin'),
command: (data) => {
setStateFieldData([...stateFieldData, {id: null, value: '', status: data.item.type}]);
}
}
]
const removeItem = (index) => {
const newData = stateFieldData.toSpliced(index, 1);
setStateFieldData(newData);
}
const selectItem = (e, index) => {
const newData = stateFieldData.map((o, i) => {
if (i === index) {
o.value = e.value;
}
return o;
})
setStateFieldData(newData);
}
const onInputChange = (e, index) => {
const { value } = e.target;
const newData = stateFieldData.map((o, i) => {
if (i === index) {
o.value = value;
}
return o;
})
setStateFieldData(newData);
}
const properField = (item, i) => {
return item.status === 'new'
? <InputText value={item.value} onInput={(e) => onInputChange(e, i)}/>
: <Dropdown value={item.value}
onChange={(e) => selectItem(e, i)}
optionDisabled={(opt) => usedExistingValues.includes(opt.value)}
options={options} optionLabel="value"/>
}
const usedExistingValues = stateFieldData
.filter(o => o.status === 'existing')
.map(o => o.value);
useEffect(() => {
const storeFieldData = data[fieldName] ?? [];
const newData = storeFieldData.map(o => ({...o, status: o.id ? 'existing' : 'new'}))
setStateFieldData(newData);
register(fieldName)
}, [])
useEffect(() => {
setDataFn(fieldName, [...stateFieldData]);
}, [stateFieldData])
return (
<div className={classNames(['appForm__field', 'formfieldrepeater'])}>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}
</label>
{stateFieldData.map((o, i) => <div key={i} className={classNames('appForm__repeaterItem')}>
<div className="p-inputgroup flex-1">
{properField(o, i)}
<Button icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
</div>
{o.status === 'new' && infoText ? <small>{infoText}</small> : null}
</div>)}
<Menu model={menuItems} popup ref={forMenu} id="aimedForMenu"/>
<Button type="button" iconPos="right" label={__('Aggiungi', 'gepafin')}
icon="pi pi-chevron-down" onClick={(event) => forMenu.current.toggle(event)}
aria-controls="aimedForMenu" aria-haspopup/>
</div>
)
}
export default FormFieldRepeater;

View File

@@ -0,0 +1,143 @@
import React, { useRef, useEffect, useState } from 'react';
import { classNames } from 'primereact/utils';
import { __ } from '@wordpress/i18n';
import { InputText } from 'primereact/inputtext';
import { Button } from 'primereact/button';
import { Menu } from 'primereact/menu';
import { Dropdown } from 'primereact/dropdown';
import { InputNumber } from 'primereact/inputnumber';
const FormFieldRepeaterCriteria = ({
data,
setDataFn,
fieldName,
options = [],
errors,
register,
label,
infoText
}) => {
const forMenu = useRef(null);
const [stateFieldData, setStateFieldData] = useState([]);
const menuItems = [
{
type: 'existing',
label: __('Esistente', 'gepafin'),
command: (data) => {
setStateFieldData([...stateFieldData, { id: null, value: '', status: data.item.type }]);
}
},
{
type: 'new',
label: __('Nuovo', 'gepafin'),
command: (data) => {
setStateFieldData([...stateFieldData, { id: null, value: '', status: data.item.type }]);
}
}
]
const removeItem = (index) => {
const newData = stateFieldData.toSpliced(index, 1);
setStateFieldData(newData);
}
const selectItem = (e, index) => {
const newData = stateFieldData.map((o, i) => {
if (i === index) {
o.value = e.value;
}
return o;
})
setStateFieldData(newData);
}
const onInputChange = (value, index, name) => {
const newData = stateFieldData.map((o, i) => {
if (i === index) {
o[name] = value;
}
return o;
})
setStateFieldData(newData);
}
const properField = (item, i) => {
return item.status === 'new'
? <InputText value={item.value} onInput={(e) => onInputChange(e.target.value, i, 'value')}/>
: <Dropdown value={item.value}
onChange={(e) => selectItem(e, i)}
optionDisabled={(opt) => usedExistingValues.includes(opt.value)}
options={options} optionLabel="value"/>
}
const usedExistingValues = stateFieldData
.filter(o => o.status === 'existing')
.map(o => o.value);
useEffect(() => {
const storeFieldData = data[fieldName] ?? [];
const newData = storeFieldData.map(o => ({ ...o, status: o.id ? 'existing' : 'new' }))
setStateFieldData(newData);
register(fieldName)
}, [])
useEffect(() => {
setDataFn(fieldName, [...stateFieldData]);
}, [stateFieldData])
return (
<div className={classNames(['appForm__field', 'formfieldrepeater'])}>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}
</label>
{stateFieldData.map((o, i) => <div key={i} className={classNames("appForm__field", 'appForm__repeaterItem')}>
<div className="appForm__twoCols">
<div>
<label htmlFor="criterionTotal">{__('Punteggio Totale', 'gepafin')}</label>
<InputNumber inputId="criterionTotal"
value={o.total}
showButtons
onValueChange={(e) => onInputChange(e.value, i, 'total')}/>
</div>
<div>
<label htmlFor="criterionThreshold">{__('Punteggio minimo per lammissione', 'gepafin')}</label>
<InputNumber inputId="criterionThreshold"
value={o.threshold}
showButtons
onValueChange={(e) => onInputChange(e.value, i, 'threshold')}/>
</div>
</div>
<div className="appForm__twoCols">
<div>
<label>{__('Nome criterio di valutazione', 'gepafin')}</label>
<div className="p-inputgroup flex-1">
{properField(o, i)}
<Button icon="pi pi-times" className="p-button-danger" onClick={() => removeItem(i)}/>
</div>
{o.status === 'new' && infoText ? <small>{infoText}</small> : null}
</div>
<div>
<label htmlFor="criterionMin">{__('Punteggio minimo', 'gepafin')}</label>
<InputNumber inputId="criterionMin"
value={o.min}
showButtons
onValueChange={(e) => onInputChange(e.value, i, 'min')}/>
</div>
<div>
<label htmlFor="criterionMax">{__('Punteggio massimo', 'gepafin')}</label>
<InputNumber inputId="criterionMax"
value={o.max}
showButtons
onValueChange={(e) => onInputChange(e.value, i, 'max')}/>
</div>
</div>
</div>)}
<Menu model={menuItems} popup ref={forMenu} id="aimedForMenu"/>
<Button type="button" iconPos="right" label={__('Aggiungi', 'gepafin')}
icon="pi pi-chevron-down" onClick={(event) => forMenu.current.toggle(event)}
aria-controls="aimedForMenu" aria-haspopup/>
</div>
)
}
export default FormFieldRepeaterCriteria;

View File

@@ -0,0 +1,122 @@
import React, { useRef, useEffect, useState } from 'react';
import { classNames } from 'primereact/utils';
import { __ } from '@wordpress/i18n';
import { InputText } from 'primereact/inputtext';
import { Button } from 'primereact/button';
import { Dropdown } from 'primereact/dropdown';
import { Accordion, AccordionTab } from 'primereact/accordion';
import { ToggleButton } from 'primereact/togglebutton';
const FormFieldRepeaterFaq = ({
data,
setDataFn,
fieldName,
options = [],
errors,
register,
label,
infoText
}) => {
const [stateFieldData, setStateFieldData] = useState([]);
const removeItem = (index) => {
const newData = stateFieldData.toSpliced(index, 1);
setStateFieldData(newData);
}
const selectItem = () => {
setStateFieldData([...stateFieldData, { id: 0, status: 'new', question: '', answer: '', visible: true }]);
}
const onInputChange = (e, index) => {
const { value } = e.target;
const newData = stateFieldData.map((o, i) => {
if (i === index) {
o.value = value;
}
return o;
})
setStateFieldData(newData);
}
const addNewItem = () => {
}
const setChecked = (e, index) => {
e.preventDefault();
const newData = stateFieldData.map((o, i) => {
if (i === index) {
o.visible = e.value;
}
return o;
});
setStateFieldData(newData);
}
const editItem = (e, index) => {
e.stopPropagation();
console.log('editItem')
}
const usedExistingValues = stateFieldData
.filter(o => o.status === 'existing')
.map(o => o.question);
useEffect(() => {
const storeFieldData = data[fieldName] ?? [];
const newData = storeFieldData.map(o => ({ ...o, status: o.id ? 'existing' : 'new' }))
setStateFieldData(newData);
register(fieldName)
}, [])
useEffect(() => {
setDataFn(fieldName, [...stateFieldData]);
}, [stateFieldData])
return (
<div className={classNames(['appForm__field', 'formfieldrepeater'])}>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}
</label>
<div className="appForm__faqHeaderControls">
<Button type="button" iconPos="left" label={__('Aggiungi', 'gepafin')}
icon="pi pi-plus" onClick={addNewItem}/>
<Dropdown onChange={(e) => selectItem(e)}
optionDisabled={(opt) => usedExistingValues.includes(opt.value)}
options={options} optionLabel="question"/>
</div>
<Accordion activeIndex={0}>
{stateFieldData.map((o, i) => <AccordionTab key={i}
header={
<div className="appForm__faqTab">
<div className="appForm__faqTabItem">
<ToggleButton
onIcon="pi pi-eye"
offIcon="pi pi-eye-slash"
onLabel=""
offLabel=""
checked={o.visible}
onChange={(e) => setChecked(e, i)}/>
{o.question}
</div>
<div className="appForm__faqTabItem">
<Button icon="pi pi-pencil" severity="success"
aria-label="Edit" onClick={(e) => editItem(e, i)}/>
<Button icon="pi pi-times" severity="danger"
aria-label="Cancel"
onClick={() => removeItem(i)}/>
</div>
</div>
}
>
<p className="m-0">
{o.answer}
</p>
</AccordionTab>)}
</Accordion>
</div>
)
}
export default FormFieldRepeaterFaq;

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
@@ -10,10 +10,17 @@ import { InputTextarea } from 'primereact/inputtextarea';
import getBandoLabel from '../../helpers/getBandoLabel'; import getBandoLabel from '../../helpers/getBandoLabel';
import { Button } from 'primereact/button'; import { Button } from 'primereact/button';
import { Dropdown } from 'primereact/dropdown'; import { Dropdown } from 'primereact/dropdown';
import { Menu } from 'primereact/menu';
import FormField from '../../components/FormField';
import FormFieldRepeater from '../../components/FormFieldRepeater';
import { Skeleton } from 'primereact/skeleton';
import FormFieldRepeaterCriteria from '../../components/FormFieldRepeaterCriteria';
import FormFieldRepeaterFaq from '../../components/FormFieldRepeaterFaq';
const Bando = () => { const Bando = () => {
const { id } = useParams(); const { id } = useParams();
const [data, setData] = useState({}); const [data, setData] = useState({});
const [isFormLoading, setIsFormLoading] = useState(true);
const [selectedTemplate, setSelectedTemplate] = useState(null); const [selectedTemplate, setSelectedTemplate] = useState(null);
const [templates, setTemplate] = useState(null); const [templates, setTemplate] = useState(null);
const { const {
@@ -21,10 +28,24 @@ const Bando = () => {
reset, reset,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
getValues getValues,
setValue,
register
} = useForm(data); } = useForm(data);
const onSubmit = data => console.log(data); let minDateStart = new Date();
console.log('data', data);
const onSubmit = formData => console.log(formData);
// temp
const exampleOfAimedToOptions = [{ id: 11, value: 'PMI con sede in Umbria' }];
const exampleOfCriteriaOptions = [{ id: 15, value: 'Innovatività del progetto' }];
const exampleOfFaqOptions = [
{ id: 2, question: 'Question 1?', answer: 'Lorem ipsum dolor' }
];
const exampleOfChecklistOptions = [
{ id: 9, value: 'Requisiti di ammissibilità soddisfatti' },
{ id: 9, value: 'Documentazione completa' }
];
const onPublish = () => { const onPublish = () => {
console.log('click onPublish'); console.log('click onPublish');
@@ -34,29 +55,32 @@ const Bando = () => {
const parsed = parseInt(id) const parsed = parseInt(id)
const bandoId = !isNaN(parsed) ? parsed : 0; const bandoId = !isNaN(parsed) ? parsed : 0;
const data = 0 === bandoId setTimeout(() => {
? { const data = 0 === bandoId
status: 'draft', ? {
name: '', status: 'draft',
description: '' name: '',
} description: ''
: { }
name: 'Bando Innovazione 2024', : {
description: '', name: 'Bando Innovazione 2024',
start_date: '2024-08-08T00:00:00+00:00', description: '',
end_date: '2024-08-30T00:00:00+00:00', start_date: '2024-08-08T00:00:00+00:00',
submissions: 24, end_date: '2024-08-30T00:00:00+00:00',
status: 'publish', submissions: 24,
id: 11 status: 'publish',
} id: 11
setData(data); }
reset(); setData(data);
reset();
const templates = [ const templates = [
{ name: 'Il mio template', value: 22 }, { name: 'Il mio template', value: 22 },
{ name: 'Template #11', value: 11 }, { name: 'Template #11', value: 11 },
]; ];
setTemplate(templates) setTemplate(templates);
setIsFormLoading(false);
}, 3000);
}, [id]); }, [id]);
return ( return (
@@ -71,65 +95,139 @@ const Bando = () => {
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>
<div className="pageBando__templateSelection"> {!isFormLoading
<div className="appForm__field"> ? <div className="pageBando__templateSelection">
<label htmlFor="template"> <div className="appForm__field">
{__('Usa Template Salvato', 'gepafin')} <label htmlFor="template">
</label> {__('Usa Template Salvato', 'gepafin')}
<Dropdown </label>
id="template" <Dropdown
value={selectedTemplate} id="template"
onChange={(e) => setSelectedTemplate(e.value)} value={selectedTemplate}
options={templates} onChange={(e) => setSelectedTemplate(e.value)}
optionLabel="name" options={templates}
placeholder={__('Seleziona template', 'gepafin')} /> optionLabel="name"
</div> placeholder={__('Seleziona template', 'gepafin')}/>
<Button </div>
onClick={() => console.log('use template')} <Button
label={__('Applica', 'gepafin')} onClick={() => console.log('use template')}
icon="pi pi-check" label={__('Applica', 'gepafin')}
iconPos="right" /> icon="pi pi-check"
</div> iconPos="right"/>
</div> : null}
<div className="appPage__spacer"></div> <div className="appPage__spacer"></div>
{data {!isFormLoading
? <form className="appForm" onSubmit={handleSubmit(onSubmit)}> ? <form className="appForm" onSubmit={handleSubmit(onSubmit)}>
<div className="appForm__field"> <FormField
<label htmlFor="name" className={classNames({ 'p-error': errors.name })}> type="textinput"
{__('Titolo del Bando', 'gepafin')}* fieldName="name"
</label> label={__('Titolo del Bando', 'gepafin')}
<Controller control={control}
name="name" errors={errors}
control={control} defaultValue={data['name']}
defaultValue={data['name']} config={{ required: __('È obbligatorio', 'gepafin') }}
rules={{ required: __('È obbligatorio', 'gepafin') }} />
render={({ field, fieldState }) => (
<InputText id={field.name}
onInput={field.onChange}
onBlur={field.onBlur}
value={field.value}
name={field.name}
autoFocus
className={classNames({ 'p-invalid': fieldState.invalid })}/>
)}/>
</div>
<div className="appForm__field"> <FormField
<label htmlFor="description" className={classNames({ 'p-error': errors.description })}> type="textarea"
{__('Descrizione', 'gepafin')}* fieldName="description"
</label> label={__('Descrizione', 'gepafin')}
<Controller control={control}
name="description" errors={errors}
control={control} defaultValue={data['description']}
defaultValue={data['description']} config={{
rules={{ required: __('È obbligatorio', 'gepafin') }} required: __('È obbligatorio', 'gepafin'),
render={({ field, fieldState }) => ( minLength: 200
<InputTextarea id={field.name} }}
{...field} infoText={__('Almeno 200 caratteri', 'gepafin')}
className={classNames({ 'p-invalid': fieldState.invalid })}/> />
)}/>
</div> <FormFieldRepeater
data={data}
setDataFn={setValue}
fieldName="aimedTo"
options={exampleOfAimedToOptions}
errors={errors}
register={register}
label={<>{__('A chi si rivolge', 'gepafin')}*
<span>{__('(almeno 1 tipo di destinatari)', 'gepafin')}</span></>}
/>
<FormField
type="datepickerrange"
fieldName="dates"
label={__('Dati di pubblicazione', 'gepafin')}
control={control}
errors={errors}
defaultValue={data['dates']}
config={{ required: __('È obbligatorio', 'gepafin') }}
minDate={minDateStart}
/>
<FormField
type="numberinput"
fieldName="amount"
label={__('Dotazione del Bando', 'gepafin')}
control={control}
errors={errors}
defaultValue={data['amount']}
config={{ required: __('È obbligatorio', 'gepafin') }}
inputgroup={true}
icon="€"
/>
<FormFieldRepeaterCriteria
data={data}
setDataFn={setValue}
fieldName="criteria"
options={exampleOfCriteriaOptions}
errors={errors}
register={register}
label={<>{__('Criteri di valutazione', 'gepafin')}*
<span>{__('(almeno 1 criterio di valutazione)', 'gepafin')}</span></>}/>
<FormField
type="fileupload"
fieldName="documentation"
label={__('Documentazione', 'gepafin')}
control={control}
errors={errors}
defaultValue={data['documentation']}
config={{ required: __('È obbligatorio', 'gepafin') }}
accept="application/pdf,application/vnd.ms-excel"
chooseLabel={__('Aggiungi documento', 'gepafin')}
/>
<FormField
type="fileupload"
fieldName="images"
label={__('Immagine del Bando', 'gepafin')}
control={control}
errors={errors}
defaultValue={data['documentation']}
/>
<FormFieldRepeaterFaq
data={data}
setDataFn={setValue}
fieldName="faq"
options={exampleOfFaqOptions}
errors={errors}
register={register}
label={__('FAQ', 'gepafin')}/>
<FormFieldRepeater
data={data}
setDataFn={setValue}
fieldName="checklist"
options={exampleOfChecklistOptions}
errors={errors}
register={register}
label={<>{__('Checklist valutazione Pre-Istruttoria', 'gepafin')}*
<span>{__('(almeno 1 elemento)', 'gepafin')}</span></>}
/>
<div className="appPageSection"> <div className="appPageSection">
<div className="appPageSection__actions"> <div className="appPageSection__actions">
@@ -137,11 +235,18 @@ const Bando = () => {
type="submit" type="submit"
label={__('Salva Bozza', 'gepafin')} icon="pi pi-save" iconPos="right"/> label={__('Salva Bozza', 'gepafin')} icon="pi pi-save" iconPos="right"/>
<Button <Button
type="button"
onClick={onPublish} onClick={onPublish}
label={__('Pubblica', 'gepafin')} icon="pi pi-upload" iconPos="right"/> label={__('Pubblica', 'gepafin')} icon="pi pi-upload" iconPos="right"/>
</div> </div>
</div> </div>
</form> : null} </form>
: <>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="2rem" className="mb-8"></Skeleton>
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
<Skeleton width="100%" height="4rem"></Skeleton>
</>}
</div> </div>
) )
} }