- added bando preview page;
- added bando form preview;
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"@emotion/styled": "11.13.0",
|
||||
"@wordpress/i18n": "^5.5.0",
|
||||
"@wordpress/react-i18n": "^4.5.0",
|
||||
"deep-object-diff": "^1.1.9",
|
||||
"dompurify": "3.1.6",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"html-react-parser": "5.1.12",
|
||||
@@ -6765,6 +6766,11 @@
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
|
||||
},
|
||||
"node_modules/deep-object-diff": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz",
|
||||
"integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA=="
|
||||
},
|
||||
"node_modules/deepmerge": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"@emotion/styled": "11.13.0",
|
||||
"@wordpress/i18n": "^5.5.0",
|
||||
"@wordpress/react-i18n": "^4.5.0",
|
||||
"deep-object-diff": "^1.1.9",
|
||||
"dompurify": "3.1.6",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"html-react-parser": "5.1.12",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
padding: 5px 0;
|
||||
width: 100%;
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
|
||||
@@ -12,30 +12,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.appPageSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
h2 {
|
||||
color: var(--Black);
|
||||
font-size: 21px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
margin: 0 0 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.appPageSection__table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.appTableHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.appPage__pageHeader {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -55,7 +31,7 @@
|
||||
line-height: 120%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
span {
|
||||
margin-left: 10px;
|
||||
text-transform: uppercase;
|
||||
@@ -67,6 +43,157 @@
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
.appPageSection__hr {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 0 0 0 30px;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
content: '';
|
||||
top: 50%;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
background-color: #E5E7EB;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
padding: 0 10px;
|
||||
background-color: white;
|
||||
z-index: 9;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.appPage__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 48px;
|
||||
}
|
||||
|
||||
.appPageSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
h2 {
|
||||
color: var(--global-textColor);
|
||||
font-size: 21px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
margin: 0 0 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.appPageSection__withBorder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
width: 100%;
|
||||
padding: 17px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--card-borderColor-color);
|
||||
|
||||
&.disabled {
|
||||
filter: grayscale(1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: var(--global-textColor);
|
||||
font-size: 21px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
|
||||
&.rowContent {
|
||||
padding: 17px 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.appPageSection__hero {
|
||||
display: flex;
|
||||
height: 172px;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.appPageSection__preview {
|
||||
padding: 17px 0;
|
||||
background: repeating-linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 10px,
|
||||
#f8d282 10px,
|
||||
#f8d282 20px
|
||||
);
|
||||
|
||||
.p-button {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
.appPageSection__row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.appPageSection__pMeta {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: space-between;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.appPageSection__table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.appTableHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.appPageSection__actions {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
|
||||
@@ -27,7 +27,14 @@
|
||||
padding: 20px;
|
||||
border: 1px #DDD;
|
||||
background: var(--surface-50);
|
||||
box-shadow: 0px 0px 2px 1px rgba(0, 0, 0, 0.10);
|
||||
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.10);
|
||||
|
||||
.dropzone {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 1rem;
|
||||
border: 3px dashed #bdbdbd;
|
||||
}
|
||||
}
|
||||
|
||||
.formBuilder__element {
|
||||
@@ -36,11 +43,23 @@
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 10px 20px;
|
||||
border: 1px dashed var(--button-secondary-borderColor);
|
||||
border: 1px solid var(--button-secondary-borderColor);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 21px;
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.formBuilder__aside {
|
||||
@@ -81,4 +100,17 @@
|
||||
|
||||
.formBuilder__elementSettings {
|
||||
width: 40rem;
|
||||
}
|
||||
|
||||
.formElementSettings {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.formElementSettings__field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
@@ -12,8 +12,7 @@ body {
|
||||
margin: 0;
|
||||
font-family: 'Montserrat';
|
||||
|
||||
p, span, input, label:not(.p-error), textarea, a, li, h1, h2, h3, h4, h5, h6, div, th, td, button {
|
||||
font-family: 'Montserrat';
|
||||
p, span:not(.p-button-label, .p-button-icon, .p-badge), input, label:not(.p-error), textarea, a, li, h1, h2, h3, h4, h5, h6, div, th, td {
|
||||
color: var(--global-textColor);
|
||||
}
|
||||
}
|
||||
@@ -84,6 +83,21 @@ body {
|
||||
> main {
|
||||
flex: 1 1 auto;
|
||||
box-sizing: border-box;
|
||||
padding: 0 24px 50px;
|
||||
padding: 0 24px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> footer {
|
||||
margin-top: auto;
|
||||
padding: 100px 0 0;
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.p-accordion {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.p-steps .p-steps-item.p-highlight .p-steps-number {
|
||||
background: var(--theme-highlight-background);
|
||||
color: var(--primary-text);
|
||||
}
|
||||
|
||||
|
||||
.mb-2 {
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
gap: 14px;
|
||||
align-items: flex-end;
|
||||
|
||||
.appForm__field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
--button-secondary-borderColor: #C79807;
|
||||
--button-secondary-background: var(--menu-borderColor);
|
||||
--global-textColor: #4B5563;
|
||||
--theme-highlight-background: #BADEBE;
|
||||
--primary-text: #3B7C43;
|
||||
|
||||
--card-full-background-color-2: #EEC137;
|
||||
--card-full-background-color-3: #FA8E42;
|
||||
|
||||
@@ -4,14 +4,15 @@ import { Controller } from 'react-hook-form';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
|
||||
const TextArea = ({
|
||||
fieldName,
|
||||
label,
|
||||
control,
|
||||
errors,
|
||||
defaultValue,
|
||||
config = {},
|
||||
infoText = null
|
||||
}) => {
|
||||
fieldName,
|
||||
label,
|
||||
control,
|
||||
rows = 4,
|
||||
errors,
|
||||
defaultValue,
|
||||
config = {},
|
||||
infoText = null
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
|
||||
@@ -24,6 +25,7 @@ const TextArea = ({
|
||||
rules={config}
|
||||
render={({ field, fieldState }) => (
|
||||
<InputTextarea id={field.name}
|
||||
rows={rows}
|
||||
{...field}
|
||||
className={classNames({ 'p-invalid': fieldState.invalid })}/>
|
||||
)}/>
|
||||
|
||||
@@ -48,6 +48,7 @@ const FormFieldRepeater = ({
|
||||
const newData = stateFieldData.map((o, i) => {
|
||||
if (i === index) {
|
||||
o.value = e.value;
|
||||
o.id = e.id;
|
||||
}
|
||||
return o;
|
||||
})
|
||||
|
||||
@@ -53,6 +53,7 @@ const FormFieldRepeaterCriteria = ({
|
||||
if (i === index) {
|
||||
o.value = targetedOption.value;
|
||||
o.score = targetedOption.score;
|
||||
o.id = targetedOption.id;
|
||||
}
|
||||
return o;
|
||||
});
|
||||
@@ -91,7 +92,7 @@ const FormFieldRepeaterCriteria = ({
|
||||
|
||||
useEffect(() => {
|
||||
const storeFieldData = data[fieldName] ?? [];
|
||||
const newData = storeFieldData.map(o => ({ ...o, status: o.id ? 'existing' : 'new' }))
|
||||
const newData = storeFieldData.map(o => ({ ...o, status: o.id ? 'existing' : 'new' }));
|
||||
setStateFieldData(newData);
|
||||
setThreshold(data['threshold'])
|
||||
register(fieldName, config)
|
||||
|
||||
@@ -40,17 +40,6 @@ const FormFieldRepeaterFaq = ({
|
||||
setStateFieldData([...stateFieldData, chosen]);
|
||||
}
|
||||
|
||||
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 newItem = { id: 0, status: 'new', question: '', answer: '', visible: true };
|
||||
setStateFieldData([...stateFieldData, newItem]);
|
||||
|
||||
@@ -8,7 +8,7 @@ const UnsavedChangesDetector = ({ initialData, getValuesFn }) => {
|
||||
const [initial] = useState(initialData);
|
||||
|
||||
const warnIfUnsavedChanges = useCallback((event) => {
|
||||
const updatedData = getValuesFn()
|
||||
const updatedData = getValuesFn();
|
||||
const isEqual = equal(initial, updatedData);
|
||||
|
||||
if (!isEqual) {
|
||||
|
||||
6
src/helpers/getDateFromISOstring.js
Normal file
6
src/helpers/getDateFromISOstring.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const getDateFromISOstring = (value, options = {}) => {
|
||||
const optionsMerged = options || { day: '2-digit', month: '2-digit', year: 'numeric', dateStyle: 'short' }
|
||||
return Intl.DateTimeFormat('it-IT', optionsMerged).format(new Date(value))
|
||||
}
|
||||
|
||||
export default getDateFromISOstring;
|
||||
5
src/helpers/getNumberFormatted.js
Normal file
5
src/helpers/getNumberFormatted.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const getNumberFormatted = (value) => {
|
||||
return Intl.NumberFormat('it-IT', { minimumFractionDigits: 2 }).format(value);
|
||||
}
|
||||
|
||||
export default getNumberFormatted;
|
||||
11
src/helpers/getNumberWithCurrency.js
Normal file
11
src/helpers/getNumberWithCurrency.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const getNumberWithCurrency = (value, currency = 'EUR') => {
|
||||
const formatter = Intl.NumberFormat('it-IT', {
|
||||
style: 'currency',
|
||||
currencyDisplay: 'symbol',
|
||||
currency
|
||||
})
|
||||
|
||||
return formatter.format(value)
|
||||
}
|
||||
|
||||
export default getNumberWithCurrency;
|
||||
@@ -48,6 +48,10 @@ const DefaultLayout = ({ children }) => {
|
||||
<AppSidebar/>
|
||||
<main>
|
||||
{children}
|
||||
|
||||
<footer>
|
||||
<p>{__('© 2024 Gepafin. Tutti i diritti riservati.', 'gepafin')}</p>
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@ const BandoEditFormActions = ({ openPreview, openPreviewEvaluation }) => {
|
||||
label={__('Salva bozza', 'gepafin')} icon="pi pi-save" iconPos="right"/>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={true}
|
||||
disabled={false}
|
||||
outlined
|
||||
onClick={openPreview}
|
||||
label={__('Anteprima beneficiario', 'gepafin')} icon="pi pi-eye" iconPos="right"/>
|
||||
|
||||
@@ -41,11 +41,11 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
|
||||
// end of temp data
|
||||
|
||||
const openPreview = () => {
|
||||
navigate('/bandi/preview/11');
|
||||
navigate('/bandi/11/preview');
|
||||
}
|
||||
|
||||
const openPreviewEvaluation = () => {
|
||||
navigate('/bandi/preview-evaluation/11');
|
||||
navigate('/bandi/11/preview-evaluation');
|
||||
}
|
||||
|
||||
useImperativeHandle(
|
||||
@@ -57,6 +57,9 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
|
||||
},
|
||||
getErrors: () => {
|
||||
return errors;
|
||||
},
|
||||
getValues: () => {
|
||||
return getValues();
|
||||
}
|
||||
}
|
||||
}, [errors, isValid]);
|
||||
@@ -106,6 +109,7 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
|
||||
fieldName="descriptionLong"
|
||||
label={__('Descrizione completa', 'gepafin')}
|
||||
control={control}
|
||||
rows={7}
|
||||
errors={errors}
|
||||
defaultValue={values['descriptionLong']}
|
||||
config={{
|
||||
@@ -199,6 +203,12 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors },
|
||||
}}
|
||||
label={__('FAQ', 'gepafin')}/>
|
||||
|
||||
<div className="appPage__spacer"></div>
|
||||
|
||||
<div className="appPageSection__hr">
|
||||
<span>{__('Azioni', 'gepafin')}</span>
|
||||
</div>
|
||||
|
||||
<BandoEditFormActions
|
||||
openPreview={openPreview}
|
||||
openPreviewEvaluation={openPreviewEvaluation}/>
|
||||
|
||||
@@ -60,9 +60,12 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
|
||||
},
|
||||
getErrors: () => {
|
||||
return errors;
|
||||
},
|
||||
getValues: () => {
|
||||
return getValues();
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
}, [errors, isValid]);
|
||||
|
||||
useEffect(() => {
|
||||
trigger().then(() => clearErrors());
|
||||
@@ -84,7 +87,7 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
|
||||
validate: {
|
||||
minOneItem: v => !isEmpty(v) || __('Almeno 1 criterio di valutazione', 'gepafin'),
|
||||
noEmptyValue: v => v
|
||||
.filter(o => isEmpty(o.value) || isEmpty(o.score)).length === 0
|
||||
.filter(o => isEmpty(o.value) || isEmpty(o.score)).length === 0
|
||||
|| __('Non lasciare il valore vuoto', 'gepafin')
|
||||
}
|
||||
}}/>
|
||||
@@ -135,7 +138,12 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors },
|
||||
}}
|
||||
/>
|
||||
|
||||
<button type="button" onClick={() => console.log(getValues())}>Check</button>
|
||||
<div className="appPage__spacer"></div>
|
||||
|
||||
<div className="appPageSection__hr">
|
||||
<span>{__('Azioni', 'gepafin')}</span>
|
||||
</div>
|
||||
|
||||
<BandoEditFormActions
|
||||
openPreview={openPreview}
|
||||
openPreviewEvaluation={openPreviewEvaluation}/>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
//import equal from 'fast-deep-equal';
|
||||
import { is, isNil } from 'ramda';
|
||||
|
||||
// components
|
||||
import getBandoLabel from '../../helpers/getBandoLabel';
|
||||
@@ -10,7 +12,9 @@ import { Steps } from 'primereact/steps';
|
||||
import BandoEditFormStep1 from './components/BandoEditFormStep1';
|
||||
import BandoEditFormStep2 from './components/BandoEditFormStep2';
|
||||
import { Messages } from 'primereact/messages';
|
||||
import { is, isNil } from 'ramda';
|
||||
|
||||
// TODO temp
|
||||
import { bandoTest } from '../../tempData';
|
||||
|
||||
const BandoEdit = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -18,8 +22,8 @@ const BandoEdit = () => {
|
||||
const [activeStep, setActiveStep] = useState(0)
|
||||
const [data, setData] = useState({});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [selectedTemplate, setSelectedTemplate] = useState(null);
|
||||
const [templates, setTemplate] = useState(null);
|
||||
//const [selectedTemplate, setSelectedTemplate] = useState(null);
|
||||
//const [templates, setTemplate] = useState(null);
|
||||
const formRef = useRef(null);
|
||||
const stepErrorMsgs = useRef(null);
|
||||
|
||||
@@ -31,7 +35,11 @@ const BandoEdit = () => {
|
||||
return false
|
||||
}
|
||||
stepErrorMsgs.current.clear();
|
||||
if (formRef.current.isFormValid()) {
|
||||
const isFormValid = formRef.current.isFormValid();
|
||||
//const values = formRef.current.getValues();
|
||||
//const diffData = equal(values, data);
|
||||
// TODO warn about unsaved data
|
||||
if (isFormValid) {
|
||||
goToStep(0)
|
||||
} else {
|
||||
stepErrorMsgs.current.show([
|
||||
@@ -50,7 +58,9 @@ const BandoEdit = () => {
|
||||
}
|
||||
stepErrorMsgs.current.clear();
|
||||
const isFormValid = formRef.current.isFormValid();
|
||||
console.log('before go to step 1:', isFormValid)
|
||||
//const values = formRef.current.getValues();
|
||||
//const diffData = equal(values, data);
|
||||
// TODO warn about unsaved data
|
||||
if (isFormValid) {
|
||||
goToStep(1);
|
||||
} else {
|
||||
@@ -81,47 +91,7 @@ const BandoEdit = () => {
|
||||
status: 'draft',
|
||||
name: ''
|
||||
}
|
||||
: {
|
||||
"name": "Innovazione digitale 2024",
|
||||
"confidi": false,
|
||||
"descriptionShort": "Supporto alle PMI per progetti di digitalizzazione e innovazione tecnologica.",
|
||||
"descriptionLong": "Il bando \"Innovazione Digitale 2024\" mira a sostenere le PMI nell'adozione di tecnologie digitali innovative. I progetti finanziabili includono l'implementazione di soluzioni di intelligenza artificiale, blockchain, IoT, e altre tecnologie avanzate che possono migliorare la competitività delle imprese.",
|
||||
"documentationRequested": "Documentazione richiesta*",
|
||||
"dates": [
|
||||
"2024-08-27T22:00:00.000Z",
|
||||
"2024-10-29T23:00:00.000Z"
|
||||
],
|
||||
"amount": 0,
|
||||
"amountMax": 0,
|
||||
"aimedTo": [
|
||||
{
|
||||
"id": 3,
|
||||
"value": "PMI con sede in Umbria",
|
||||
"status": "existing"
|
||||
}
|
||||
],
|
||||
"faq": [
|
||||
{
|
||||
"id": 2,
|
||||
"question": "Question 1?",
|
||||
"answer": "Lorem ipsum dolor",
|
||||
"visible": true,
|
||||
"status": "existing"
|
||||
}
|
||||
],
|
||||
"documentation": [
|
||||
/*{
|
||||
createdDate: "2024-08-23T12:40:47.700350791",
|
||||
description: null,
|
||||
filePath: "https://mementoresources.s3.eu-west-1.amazonaws.com/gepafin/SCR-20240820-kiwn.pdf",
|
||||
id: 10,
|
||||
name: "SCR-20240820-kiwn.pdf",
|
||||
updatedDate: "2024-08-23T12:40:47.700373894"
|
||||
}*/
|
||||
],
|
||||
status: 'draft',
|
||||
id: 11
|
||||
}
|
||||
: bandoTest;
|
||||
|
||||
if (bandoId === 0) {
|
||||
setData(data);
|
||||
@@ -134,11 +104,11 @@ const BandoEdit = () => {
|
||||
|
||||
setData(data);
|
||||
|
||||
const templates = [
|
||||
/*const templates = [
|
||||
{ name: 'Il mio template', value: 22 },
|
||||
{ name: 'Template #11', value: 11 },
|
||||
];
|
||||
setTemplate(templates);
|
||||
setTemplate(templates);*/
|
||||
setIsLoading(false);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
@@ -4,21 +4,34 @@ import { useParams, useNavigate } from 'react-router-dom';
|
||||
|
||||
// components
|
||||
import { Button } from 'primereact/button';
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
|
||||
const BandoForms = () => {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const navigate = useNavigate()
|
||||
const [templates, setTemplate] = useState(null);
|
||||
const [selectedTemplate, setSelectedTemplate] = useState(null);
|
||||
//const [data, setData] = useState({});
|
||||
//const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const doCreateNewForm = () => {
|
||||
navigate('/bandi/11/forms/new');
|
||||
navigate(`/bandi/${id}/forms/new`);
|
||||
}
|
||||
|
||||
const goToEditBando = () => {
|
||||
navigate(`/bandi/${id}`);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const parsed = parseInt(id)
|
||||
const bandoId = !isNaN(parsed) ? parsed : 0;
|
||||
|
||||
const templates = [
|
||||
{ name: 'Il mio template', value: 22 },
|
||||
{ name: 'Template #11', value: 11 },
|
||||
];
|
||||
setTemplate(templates);
|
||||
|
||||
// TODO
|
||||
}, [id]);
|
||||
|
||||
@@ -33,11 +46,53 @@ const BandoForms = () => {
|
||||
|
||||
<div className="appPage__spacer"></div>
|
||||
|
||||
<div className="appPageSection">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={doCreateNewForm}
|
||||
label={__('Crea/modifica form', 'gepafin')}/>
|
||||
<div className="appPage__content">
|
||||
<div className="appPageSection">
|
||||
<Button
|
||||
type="button"
|
||||
outlined
|
||||
onClick={goToEditBando}
|
||||
label={__('Modifica bando', 'gepafin')}
|
||||
icon="pi pi-arrow-left" iconPos="left"/>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__withBorder disabled">
|
||||
<h2>{__('Usa un template', 'gepafin')}</h2>
|
||||
<div className="row">
|
||||
<p>{__('Scegli tra i template predefiniti e personalizzali', 'gepafin')}</p>
|
||||
<Dropdown
|
||||
id="template"
|
||||
disabled={true}
|
||||
value={selectedTemplate}
|
||||
onChange={(e) => setSelectedTemplate(e.value)}
|
||||
options={templates}
|
||||
optionLabel="name"
|
||||
placeholder={__('Seleziona template', 'gepafin')}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__withBorder">
|
||||
<h2>{__('Crea un nuovo Form da Zero', 'gepafin')}</h2>
|
||||
<div className="row">
|
||||
<p>{__('Inizia con un form completamente vuoto e personalizzabil', 'gepafin')}</p>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={doCreateNewForm}
|
||||
label={__('Crea form', 'gepafin')}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__withBorder disabled">
|
||||
<h2>{__('Modifica Form esistente', 'gepafin')}</h2>
|
||||
<div className="row">
|
||||
<p>{__('Continua a lavorare su un form precedentemente salvato', 'gepafin')}</p>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={true}
|
||||
onClick={doCreateNewForm}
|
||||
label={__('Modifica', 'gepafin')}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
26
src/pages/BandoFormsEdit/components/BuilderDropzone/index.js
Normal file
26
src/pages/BandoFormsEdit/components/BuilderDropzone/index.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { useDrop } from 'react-dnd';
|
||||
import { ItemTypes } from '../ItemTypes';
|
||||
|
||||
// store
|
||||
import { storeSet } from '../../../../store';
|
||||
|
||||
const BuilderDropzone = () => {
|
||||
const dropzoneRef = useRef();
|
||||
|
||||
const [, drop] = useDrop({
|
||||
accept: ItemTypes.FIELD,
|
||||
hover(item, monitor) {
|
||||
storeSet.main.moveElement(-1, 0, item);
|
||||
item.index = 0;
|
||||
},
|
||||
});
|
||||
|
||||
drop(dropzoneRef);
|
||||
|
||||
return (
|
||||
<div ref={dropzoneRef} className="dropzone"></div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BuilderDropzone;
|
||||
@@ -7,8 +7,10 @@ import { storeSet } from '../../../../store';
|
||||
|
||||
// components
|
||||
import { Button } from 'primereact/button';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import BuilderElementProperLabel from '../BuilderElementProperLabel';
|
||||
|
||||
const BuilderElement = ({ id, name, label, index, move }) => {
|
||||
const BuilderElement = ({ id, name, label, index }) => {
|
||||
const ref = useRef(null);
|
||||
|
||||
const [{ handlerId }, drop] = useDrop({
|
||||
@@ -74,17 +76,31 @@ const BuilderElement = ({ id, name, label, index, move }) => {
|
||||
}),
|
||||
});
|
||||
|
||||
const move = (dragIndex, hoverIndex, item) => {
|
||||
storeSet.main.moveElement(dragIndex, hoverIndex, item);
|
||||
}
|
||||
|
||||
const openSettings = (id) => {
|
||||
storeSet.main.activeElement(id);
|
||||
}
|
||||
|
||||
const remove = (id) => {
|
||||
storeSet.main.removeElement(id);
|
||||
}
|
||||
|
||||
const opacity = isDragging ? 0 : 1;
|
||||
drag(drop(ref));
|
||||
|
||||
return (
|
||||
<div ref={ref} className="formBuilder__element" style={{ opacity }} data-handler-id={handlerId}>
|
||||
{label}
|
||||
<Button icon="pi pi-cog" onClick={() => openSettings(id)} outlined />
|
||||
<div className="meta">
|
||||
<Tag value={name} severity="info"/>
|
||||
<BuilderElementProperLabel id={id} defaultLabel={label}/>
|
||||
</div>
|
||||
<div className="actions">
|
||||
<Button icon="pi pi-cog" onClick={() => openSettings(id)} outlined severity="info" />
|
||||
<Button icon="pi pi-trash" onClick={() => remove(id)} outlined severity="danger" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ItemTypes } from '../ItemTypes';
|
||||
import uniqid from '../../../../helpers/uniqid';
|
||||
|
||||
|
||||
const BuilderElementItem = ({ dbId, name, label, move }) => {
|
||||
const BuilderElementItem = ({ dbId, name, label }) => {
|
||||
const ref = useRef(null);
|
||||
|
||||
const [{ isDragging }, drag] = useDrag(() => ({
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { head } from 'ramda';
|
||||
|
||||
// store
|
||||
import { useStore } from '../../../../store';
|
||||
|
||||
const BuilderElementProperLabel = ({ id, defaultLabel }) => {
|
||||
const elements = useStore().main.formElements();
|
||||
const [label, setLabel] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
const element = head(elements.filter(o => o.id === id));
|
||||
const setting = head(element.settings.filter(o => o.name === 'label'));
|
||||
if (setting.value) {
|
||||
setLabel(setting.value);
|
||||
} else {
|
||||
setLabel(defaultLabel);
|
||||
}
|
||||
}, [elements]);
|
||||
|
||||
return label
|
||||
}
|
||||
|
||||
export default BuilderElementProperLabel;
|
||||
@@ -0,0 +1,67 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { head } from 'ramda';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { klona } from 'klona';
|
||||
|
||||
// store
|
||||
import { storeSet, useStore } from '../../../../store';
|
||||
|
||||
// components
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { Button } from 'primereact/button';
|
||||
import { Tag } from 'primereact/tag';
|
||||
|
||||
const BuilderElementSettings = () => {
|
||||
const elements = useStore().main.elements();
|
||||
const activeElement = useStore().main.activeElement();
|
||||
const [activeElementData, setActiveElementData] = useState({});
|
||||
const [settings, setSettings] = useState([]);
|
||||
|
||||
const onChange = (value, name) => {
|
||||
const newSettings = settings
|
||||
.map(o => {
|
||||
if (o.name === name) {
|
||||
o.value = value;
|
||||
}
|
||||
|
||||
return o;
|
||||
});
|
||||
|
||||
setSettings(newSettings);
|
||||
}
|
||||
|
||||
const saveSettings = () => {
|
||||
activeElementData.settings = settings;
|
||||
const newElements = elements.map(o => o.id === activeElementData.id ? activeElementData : o);
|
||||
storeSet.main.elements(newElements);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const chosen = head(elements.filter(o => o.id === activeElement));
|
||||
if (chosen) {
|
||||
setActiveElementData(klona(chosen));
|
||||
setSettings(klona(chosen.settings));
|
||||
} else {
|
||||
setActiveElementData({});
|
||||
setSettings([]);
|
||||
}
|
||||
}, [activeElement])
|
||||
|
||||
return (activeElementData
|
||||
? <div className="formElementSettings">
|
||||
<Tag value={activeElementData.name} severity="info"/>
|
||||
{settings
|
||||
? settings.map((o) => <div className="formElementSettings__field" key={o.name}>
|
||||
<label htmlFor={o.name}>{o.name}</label>
|
||||
<InputText id={o.name} aria-describedby={`${o.name}-help`}
|
||||
value={o.value}
|
||||
onChange={(e) => onChange(e.target.value, o.name)}/>
|
||||
</div>) : null}
|
||||
|
||||
<Button label={__('Salva', 'gepafin')} onClick={saveSettings}/>
|
||||
</div>
|
||||
: null
|
||||
)
|
||||
}
|
||||
|
||||
export default BuilderElementSettings;
|
||||
@@ -1,38 +1,22 @@
|
||||
import React, { useCallback, useState, useEffect } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { head, isEmpty } from 'ramda';
|
||||
import { isEmpty } from 'ramda';
|
||||
|
||||
// store
|
||||
import { storeGet, storeSet, useStore } from '../../../../store';
|
||||
import { storeSet, useStore } from '../../../../store';
|
||||
|
||||
// components
|
||||
import BuilderElement from '../BuilderElement';
|
||||
import BuilderElementItem from '../BuilderElementItem';
|
||||
import { Sidebar } from 'primereact/sidebar';
|
||||
import BuilderElementSettings from '../BuilderElementSettings';
|
||||
import BuilderDropzone from '../BuilderDropzone';
|
||||
|
||||
const FormBuilder = () => {
|
||||
const [fields, setFields] = useState([]);
|
||||
const [items, setItems] = useState([]);
|
||||
const elements = useStore().main.formElements();
|
||||
const elementItems = useStore().main.elementItems();
|
||||
const activeElement = useStore().main.activeElement();
|
||||
|
||||
const moveField = useCallback((dragIndex, hoverIndex, item) => {
|
||||
setFields((prevFields) => {
|
||||
if (dragIndex === -1) {
|
||||
const configs = storeGet.main.elementItems();
|
||||
const itemCfg = head(configs.filter(o => o.id === item.dbId));
|
||||
const newItem = {
|
||||
...itemCfg,
|
||||
id: item.id,
|
||||
dbId: item.dbId
|
||||
}
|
||||
return prevFields.toSpliced(hoverIndex, 0, newItem);
|
||||
} else {
|
||||
let newFields = prevFields.toSpliced(dragIndex, 1);
|
||||
return newFields.toSpliced(hoverIndex, 0, prevFields[dragIndex]);
|
||||
}
|
||||
})
|
||||
}, []);
|
||||
|
||||
const renderField = useCallback((field, index) => {
|
||||
return (
|
||||
<BuilderElement
|
||||
@@ -41,26 +25,17 @@ const FormBuilder = () => {
|
||||
id={field.id}
|
||||
label={field.label}
|
||||
name={field.name}
|
||||
move={moveField}
|
||||
/>
|
||||
)
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const elements = storeGet.main.elements();
|
||||
const elementItems = storeGet.main.elementItems();
|
||||
setFields(elements);
|
||||
setItems(elementItems);
|
||||
}, [])
|
||||
|
||||
const renderItem = useCallback((item, index) => {
|
||||
const renderItem = useCallback((item) => {
|
||||
return (
|
||||
<BuilderElementItem
|
||||
key={item.id}
|
||||
dbId={item.id}
|
||||
label={item.label}
|
||||
name={item.name}
|
||||
move={moveField}
|
||||
/>
|
||||
)
|
||||
}, []);
|
||||
@@ -73,21 +48,21 @@ const FormBuilder = () => {
|
||||
<>
|
||||
<Sidebar visible={!isEmpty(activeElement)} onHide={closeSettings} className="formBuilder__elementSettings">
|
||||
<h2>{__('Impostazioni del campo modulo', 'gepafin')}</h2>
|
||||
<p>
|
||||
Form fields here
|
||||
</p>
|
||||
{!isEmpty(activeElement) ? <BuilderElementSettings/> : null}
|
||||
</Sidebar>
|
||||
<div className="formBuilder">
|
||||
<div className="formBuilder__main">
|
||||
<h2>{__('Trascina qui gli elementi del Form', 'gepafin')}</h2>
|
||||
<div className="formBuilder__content">
|
||||
{fields.map((field, i) => renderField(field, i))}
|
||||
{!isEmpty(elements)
|
||||
? elements.map((field, i) => renderField(field, i))
|
||||
: <BuilderDropzone/>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="formBuilder__aside">
|
||||
<h2>{__('Elementi del Form', 'gepafin')}</h2>
|
||||
<ul className="formBuilder__list">
|
||||
{items.map((item) => renderItem(item))}
|
||||
{elementItems.map((item) => renderItem(item))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,31 +1,37 @@
|
||||
import React, { useEffect, useState } 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 { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { klona } from 'klona';
|
||||
|
||||
// store
|
||||
import { storeSet } from '../../store';
|
||||
import { storeSet, storeGet } from '../../store';
|
||||
|
||||
// components
|
||||
import FormBuilder from './components/FormBuilder';
|
||||
import { Button } from 'primereact/button';
|
||||
|
||||
// TODO temp
|
||||
import { formData, elementItems } from '../../tempData';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
|
||||
const BandoFormsEdit = () => {
|
||||
const { id, formId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [formName, setFormName] = useState('');
|
||||
|
||||
const goBack = () => {
|
||||
navigate('/bandi/11/forms');
|
||||
navigate(`/bandi/${id}/forms`);
|
||||
}
|
||||
|
||||
const doSave = () => {
|
||||
console.log('doSave');
|
||||
console.log('doSave', storeGet.main.formElements());
|
||||
}
|
||||
|
||||
const openPreview = () => {
|
||||
console.log('openPreview');
|
||||
navigate(`/bandi/${id}/forms/${formId}/preview`);
|
||||
}
|
||||
|
||||
const doPublish = () => {
|
||||
@@ -39,38 +45,9 @@ const BandoFormsEdit = () => {
|
||||
//const bandoFormId = !isNaN(parsedFormId) ? parsedFormId : 0;
|
||||
|
||||
// 1. TODO get builder content data from API
|
||||
const elements = [
|
||||
{
|
||||
id: 'a123456',
|
||||
name: 'textinput',
|
||||
dbId: 1,
|
||||
label: 'Full Name',
|
||||
},
|
||||
{
|
||||
id: 'a456789',
|
||||
name: 'textarea',
|
||||
dbId: 2,
|
||||
label: 'Bio',
|
||||
}
|
||||
]
|
||||
const elementItems = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'textinput',
|
||||
label: 'Text Input'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'textarea',
|
||||
label: 'Text Area'
|
||||
},
|
||||
{
|
||||
id: 3, // DB id
|
||||
name: 'piva',
|
||||
label: 'P.IVA'
|
||||
},
|
||||
]
|
||||
storeSet.main.elements(elements);
|
||||
storeSet.main.formLabel(formData.label);
|
||||
const elements = klona(formData.content);
|
||||
storeSet.main.formElements(elements);
|
||||
storeSet.main.elementItems(elementItems);
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -87,9 +64,21 @@ const BandoFormsEdit = () => {
|
||||
|
||||
<div className="appPage__spacer"></div>
|
||||
|
||||
<div className="appForm__field">
|
||||
<label htmlFor="label">{__('Form label', 'gepafin')}</label>
|
||||
<InputText
|
||||
id="label"
|
||||
value={formName}
|
||||
placeholder={__('Nome della forma', 'gepafin')}
|
||||
onChange={(e) => setFormName(e.target.value)}
|
||||
aria-describedby="label-help"/>
|
||||
</div>
|
||||
|
||||
<div className="appPage__spacer"></div>
|
||||
|
||||
<div className="appPageSection">
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
{!isLoading ? <FormBuilder/>: null}
|
||||
{!isLoading ? <FormBuilder/> : null}
|
||||
</DndProvider>
|
||||
</div>
|
||||
|
||||
@@ -106,7 +95,6 @@ const BandoFormsEdit = () => {
|
||||
outlined
|
||||
label={__('Salva progressi', 'gepafin')} icon="pi pi-save" iconPos="right"/>
|
||||
<Button
|
||||
disabled={true}
|
||||
outlined
|
||||
onClick={openPreview}
|
||||
label={__('Visualizza Anteprima Beneficiario', 'gepafin')} icon="pi pi-image" iconPos="right"/>
|
||||
|
||||
121
src/pages/BandoFormsPreview/index.js
Normal file
121
src/pages/BandoFormsPreview/index.js
Normal file
@@ -0,0 +1,121 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { bandoTest, formData } from '../../tempData';
|
||||
|
||||
// tools
|
||||
import getNumberWithCurrency from '../../helpers/getNumberWithCurrency';
|
||||
import getDateFromISOstring from '../../helpers/getDateFromISOstring';
|
||||
|
||||
// components
|
||||
import { Skeleton } from 'primereact/skeleton';
|
||||
import { Accordion } from 'primereact/accordion';
|
||||
import { AccordionTab } from 'primereact/accordion';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { Button } from 'primereact/button';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { is, isNil } from 'ramda';
|
||||
import FormField from '../../components/FormField';
|
||||
|
||||
const BandoFormsPreview = () => {
|
||||
const { id, formId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [bando, setBando] = useState({});
|
||||
const [data, setData] = useState({});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors, isValid },
|
||||
setValue,
|
||||
register,
|
||||
trigger,
|
||||
getValues,
|
||||
clearErrors
|
||||
} = useForm({defaultValues: {}, mode: 'onChange'});
|
||||
const values = getValues();
|
||||
|
||||
const onSubmit = (formData) => {
|
||||
console.log('onSubmit', formData);
|
||||
};
|
||||
|
||||
const closePreview = () => {
|
||||
navigate(`/bandi/${id}`);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
//const parsed = parseInt(id)
|
||||
//const bandoId = !isNaN(parsed) ? parsed : 0;
|
||||
|
||||
setTimeout(() => {
|
||||
const data = formData;
|
||||
setData(data);
|
||||
|
||||
const bandoData = bandoTest;
|
||||
setBando(bandoData);
|
||||
|
||||
setIsLoading(false)
|
||||
}, 3000);
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<div className="appPage">
|
||||
{!isLoading
|
||||
? <div className="appPage__pageHeader">
|
||||
<h1>{sprintf(__('Domanda per il Bando: %s', 'gepafin'), bando.name)}</h1>
|
||||
</div>
|
||||
: <>
|
||||
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
|
||||
<Skeleton width="100%" height="2rem" className="mb-8"></Skeleton>
|
||||
</>}
|
||||
|
||||
<div className="appPage__spacer"></div>
|
||||
|
||||
{!isLoading
|
||||
? <div className="appPage__content">
|
||||
<div className="appPageSection__preview">
|
||||
<Button
|
||||
type="button"
|
||||
outlined
|
||||
onClick={closePreview}
|
||||
label={__('Chiudi Anteprima', 'gepafin')}
|
||||
icon="pi pi-arrow-left" iconPos="left"/>
|
||||
</div>
|
||||
|
||||
<form className="appForm" onSubmit={handleSubmit(onSubmit)}>
|
||||
{data.content.map(o => <FormField
|
||||
key={o.id}
|
||||
type={o.name}
|
||||
fieldName={`field_${o.id}`}
|
||||
label={o.label}
|
||||
control={control}
|
||||
errors={errors}
|
||||
defaultValue={values[`field_${o.id}`]}
|
||||
/>)}
|
||||
</form>
|
||||
|
||||
<div className="appPageSection__preview">
|
||||
<Button
|
||||
type="button"
|
||||
outlined
|
||||
onClick={closePreview}
|
||||
label={__('Chiudi Anteprima', 'gepafin')}
|
||||
icon="pi pi-arrow-left" iconPos="left"/>
|
||||
</div>
|
||||
</div>
|
||||
: <>
|
||||
<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" className="mb-8"></Skeleton>
|
||||
<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>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default BandoFormsPreview;
|
||||
@@ -1,56 +1,52 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Skeleton } from 'primereact/skeleton';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { bandoTest } from '../../tempData';
|
||||
|
||||
// tools
|
||||
import getNumberWithCurrency from '../../helpers/getNumberWithCurrency';
|
||||
import getDateFromISOstring from '../../helpers/getDateFromISOstring';
|
||||
|
||||
// components
|
||||
import { Skeleton } from 'primereact/skeleton';
|
||||
import { Accordion } from 'primereact/accordion';
|
||||
import { AccordionTab } from 'primereact/accordion';
|
||||
import { InputTextarea } from 'primereact/inputtextarea';
|
||||
import { Button } from 'primereact/button';
|
||||
|
||||
const BandoView = () => {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [data, setData] = useState({});
|
||||
const [newQuestion, setNewQuestion] = useState({});
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const closePreview = () => {
|
||||
navigate(`/bandi/${id}`);
|
||||
}
|
||||
|
||||
const scaricaBando = () => {
|
||||
|
||||
}
|
||||
|
||||
const scaricaModulistica = () => {
|
||||
|
||||
}
|
||||
|
||||
const submitQuestion = () => {
|
||||
|
||||
}
|
||||
|
||||
const saveToFavourites = () => {
|
||||
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
//const parsed = parseInt(id)
|
||||
//const bandoId = !isNaN(parsed) ? parsed : 0;
|
||||
|
||||
setTimeout(() => {
|
||||
const data = {
|
||||
name: 'Bando Innovazione 2024',
|
||||
descriptionShort: 'Supporto alle PMI per progetti di digitalizzazione e innovazione tecnologica.',
|
||||
descriptionLong: 'Il bando "Innovazione Digitale 2024" mira a sostenere le PMI nell\'adozione di tecnologie digitali innovative. I progetti finanziabili includono l\'implementazione di soluzioni di intelligenza artificiale, blockchain, IoT, e altre tecnologie avanzate che possono migliorare la competitività delle imprese.',
|
||||
dates: [ "2024-08-20T22:00:00.000Z", "2024-08-28T22:00:00.000Z" ],
|
||||
amount: 10000000,
|
||||
amountMax: 2000,
|
||||
aimedTo: [
|
||||
{ id: 11, value: 'PMI con sede in Umbria' },
|
||||
{ id: 12, value: 'Almeno 2 anni di attività' },
|
||||
{ id: 15, value: 'Fatturato annuo non superiore a € 50 milioni' }
|
||||
],
|
||||
documentationRequested: 'Some text',
|
||||
threshold: 12,
|
||||
criteria: [
|
||||
{ id: 15, value: 'Innovatività del progetto', score: 9 },
|
||||
{ id: 16, value: 'Impatto sulla competitività dell\'azienda', score: 3 },
|
||||
{ id: 17, value: 'Sostenibilità economico-finanziaria', score: 5 }
|
||||
],
|
||||
faq: [
|
||||
{id: 2, question: 'È possibile presentare più di un progetto?', answer: 'No, ogni azienda può presentare un solo progetto per questo bando.'}
|
||||
],
|
||||
checklist: [
|
||||
{ id: 9, value: 'Requisiti di ammissibilità soddisfatti' },
|
||||
{ id: 21, value: 'Documentazione completa' }
|
||||
],
|
||||
docs: [
|
||||
{id: 12, url: '#', name: 'file_name'}
|
||||
],
|
||||
images: [
|
||||
{id: 15, url: '#', name: 'file_name'}
|
||||
],
|
||||
status: 'draft',
|
||||
id: 11,
|
||||
created: '2024-08-07T00:00:00+00:00'
|
||||
}
|
||||
const data = bandoTest;
|
||||
setData(data);
|
||||
setIsLoading(false)
|
||||
}, 3000);
|
||||
@@ -63,7 +59,7 @@ const BandoView = () => {
|
||||
<h1>{data.name}</h1>
|
||||
<p>
|
||||
{__('Data:', 'gepafin')}
|
||||
<span>{data.created}</span>
|
||||
<span>{getDateFromISOstring(data.createdDate)}</span>
|
||||
</p>
|
||||
</div>
|
||||
: <>
|
||||
@@ -74,11 +70,176 @@ const BandoView = () => {
|
||||
<div className="appPage__spacer"></div>
|
||||
|
||||
{!isLoading
|
||||
? <>
|
||||
<div className="appPageSection">
|
||||
Preview beneficiario
|
||||
? <div className="appPage__content">
|
||||
<div className="appPageSection__preview">
|
||||
<Button
|
||||
type="button"
|
||||
outlined
|
||||
onClick={closePreview}
|
||||
label={__('Chiudi Anteprima', 'gepafin')}
|
||||
icon="pi pi-arrow-left" iconPos="left"/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<picture className="appPageSection__hero">
|
||||
<source srcSet={data.images[0] ? data.images[0].filePath : ''}/>
|
||||
<img src={data.images[0] ? data.images[0].filePath : ''} alt={data.name}/>
|
||||
</picture>
|
||||
|
||||
<div className="appPageSection__withBorder">
|
||||
<h2>{__('Descrizione breve', 'gepafin')}</h2>
|
||||
<div className="row rowContent">
|
||||
<p>{data.descriptionShort}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__row">
|
||||
<div className="appPageSection__withBorder">
|
||||
<p className="appPageSection__pMeta">
|
||||
<span>{__('Importo totale', 'gepafin')}</span>
|
||||
<span>{getNumberWithCurrency(data.amount)}</span>
|
||||
</p>
|
||||
<p className="appPageSection__pMeta">
|
||||
<span>{__('Importo massimo per progetto', 'gepafin')}</span>
|
||||
<span>{getNumberWithCurrency(data.amountMax)}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__withBorder">
|
||||
<p className="appPageSection__pMeta">
|
||||
<span>{__('Data apertura', 'gepafin')}</span>
|
||||
<span></span>
|
||||
</p>
|
||||
<p className="appPageSection__pMeta">
|
||||
<span>{__('Data chiusura', 'gepafin')}</span>
|
||||
<span></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__withBorder">
|
||||
<h2>{__('Descrizione dettagliata', 'gepafin')}</h2>
|
||||
<div className="row rowContent">
|
||||
<p>{data.descriptionLong}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__withBorder">
|
||||
<h2>{__('Requisiti di Partecipazione', 'gepafin')}</h2>
|
||||
<div className="row rowContent">
|
||||
<ul>
|
||||
{data.aimedTo.map((o, i) => <li key={i}>
|
||||
{o.value}
|
||||
</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__withBorder">
|
||||
<h2>{__('Documentazione Richiesta', 'gepafin')}</h2>
|
||||
<div className="row rowContent">
|
||||
<p>{data.documentationRequested}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__withBorder">
|
||||
<h2>{__('Criteri di Valutazione', 'gepafin')}</h2>
|
||||
<div className="row rowContent">
|
||||
<ul>
|
||||
{data.criteria.map((o, i) => <li key={i}>
|
||||
{`${o.value} ${sprintf(__('(%d punti)'), o.score)}`}
|
||||
</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__withBorder">
|
||||
<h2>{__('Allegati', 'gepafin')}</h2>
|
||||
<div className="row rowContent">
|
||||
<ul>
|
||||
{data.documentation.map((o, i) => <li key={i}>
|
||||
<a href={o.filePath} target="_blank" rel="noreferrer">{o.name}</a>
|
||||
</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection">
|
||||
<h2>{__('FAQ', 'gepafin')}</h2>
|
||||
<Accordion>
|
||||
{data.faq.map((o, i) => <AccordionTab key={i} header={o.question}>
|
||||
<p>
|
||||
{o.answer}
|
||||
</p>
|
||||
</AccordionTab>)}
|
||||
</Accordion>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection">
|
||||
<h2>{__('Non hai trovato la risposta che cercavi?', 'gepafin')}</h2>
|
||||
<div className="appForm__field">
|
||||
<label htmlFor="newQuestion">{__('Fai una domanda', 'gepafin')}</label>
|
||||
<InputTextarea
|
||||
id="newQuestion"
|
||||
rows={7}
|
||||
value={newQuestion}
|
||||
placeholder={__('Digita qui la tua domanda', 'gepafin')}
|
||||
onChange={(e) => setNewQuestion(e.target.value)}
|
||||
aria-describedby="newQuestion-help"/>
|
||||
<small id="newQuestion-help">
|
||||
{__('Riceverai una notifica quando ti risponderemo', 'gepafin')}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection">
|
||||
<h2>{__('Download Documenti', 'gepafin')}</h2>
|
||||
<div className="appPageSection__actions">
|
||||
<Button
|
||||
type="button"
|
||||
outlined
|
||||
onClick={scaricaBando}
|
||||
label={__('Scarica Bando Completo', 'gepafin')}
|
||||
icon="pi pi-download" iconPos="right"/>
|
||||
<Button
|
||||
type="button"
|
||||
outlined
|
||||
onClick={scaricaModulistica}
|
||||
label={__('Scarica Modulistica', 'gepafin')}
|
||||
icon="pi pi-download" iconPos="right"/>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={true}
|
||||
onClick={submitQuestion}
|
||||
label={__('Presenta Domanda', 'gepafin')}
|
||||
icon="pi pi-save" iconPos="right"/>
|
||||
<Button
|
||||
type="button"
|
||||
outlined
|
||||
rounded
|
||||
disabled={true}
|
||||
onClick={saveToFavourites}
|
||||
label={__('Aggiungi a Preferiti', 'gepafin')}
|
||||
icon="pi pi-heart" iconPos="left"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__withBorder">
|
||||
<h2>{__('Contatti per Assistenza', 'gepafin')}</h2>
|
||||
<div className="row rowContent">
|
||||
<p>Email: bandi@gepafin.it</p>
|
||||
<p>Telefono: +39 075 123 4567</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="appPageSection__preview">
|
||||
<Button
|
||||
type="button"
|
||||
outlined
|
||||
onClick={closePreview}
|
||||
label={__('Chiudi Anteprima', 'gepafin')}
|
||||
icon="pi pi-arrow-left" iconPos="left"/>
|
||||
</div>
|
||||
</div>
|
||||
: <>
|
||||
<Skeleton width="20%" height="1rem" className="mb-2"></Skeleton>
|
||||
<Skeleton width="100%" height="2rem" className="mb-8"></Skeleton>
|
||||
|
||||
@@ -11,17 +11,19 @@ import BandoEdit from './pages/BandoEdit';
|
||||
import BandoView from './pages/BandoView';
|
||||
import BandoFormsEdit from './pages/BandoFormsEdit';
|
||||
import BandoForms from './pages/BandoForms';
|
||||
import BandoFormsPreview from './pages/BandoFormsPreview';
|
||||
|
||||
const routes = () => (
|
||||
<Routes>
|
||||
<Route element={<ProtectedRoute/>}>
|
||||
<Route path="/" element={<DefaultLayout><Dashboard/></DefaultLayout>}/>
|
||||
<Route path="/bandi" element={<DefaultLayout><Bandi/></DefaultLayout>}/>
|
||||
<Route path="/bandi/preview/:id" element={<DefaultLayout><BandoView/></DefaultLayout>}/>
|
||||
<Route path="/bandi/preview-evaluation/:id" element={<DefaultLayout><BandoView/></DefaultLayout>}/>
|
||||
<Route path="/bandi/:id" element={<DefaultLayout><BandoEdit/></DefaultLayout>}/>
|
||||
<Route path="/bandi/:id/preview" element={<DefaultLayout><BandoView/></DefaultLayout>}/>
|
||||
<Route path="/bandi/:id/preview-evaluation" element={<DefaultLayout><BandoView/></DefaultLayout>}/>
|
||||
<Route path="/bandi/:id/forms" element={<DefaultLayout><BandoForms/></DefaultLayout>}/>
|
||||
<Route path="/bandi/:id/forms/:formId" element={<DefaultLayout><BandoFormsEdit/></DefaultLayout>}/>
|
||||
<Route path="/bandi/:id/forms/:formId/preview" element={<DefaultLayout><BandoFormsPreview/></DefaultLayout>}/>
|
||||
</Route>
|
||||
<Route exact path="/login" element={<Login/>}/>
|
||||
{/*<Route exact path="/forgot-password" element={<ForgotPassword/>}/>*/}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
import { head } from 'ramda';
|
||||
|
||||
export const actionsAlpha = (set, get, api) => ({
|
||||
setAsyncRequest: () => {
|
||||
@@ -19,5 +19,28 @@ export const actionsBeta = (set, get, api) => ({
|
||||
doLogout: () => {
|
||||
set.userData({});
|
||||
set.token('');
|
||||
},
|
||||
removeElement: (id) => {
|
||||
const elements = get.formElements();
|
||||
const newElements = elements.filter(o => o.id !== id);
|
||||
set.formElements(newElements);
|
||||
},
|
||||
moveElement: (dragIndex, hoverIndex, item) => {
|
||||
const prevFields = get.formElements();
|
||||
if (dragIndex === -1) {
|
||||
const configs = get.elementItems();
|
||||
const itemCfg = head(configs.filter(o => o.id === item.dbId));
|
||||
const newItem = {
|
||||
...itemCfg,
|
||||
id: item.id,
|
||||
dbId: item.dbId
|
||||
}
|
||||
const newElements = prevFields.toSpliced(hoverIndex, 0, newItem);
|
||||
set.formElements(newElements);
|
||||
} else {
|
||||
let newFields = prevFields.toSpliced(dragIndex, 1);
|
||||
const newElements = newFields.toSpliced(hoverIndex, 0, prevFields[dragIndex]);
|
||||
set.formElements(newElements);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,7 +7,9 @@ const initialStore = {
|
||||
// bando form
|
||||
bandoFormErrors: {},
|
||||
// form builder
|
||||
elements: [],
|
||||
formId: '',
|
||||
formLabel: '',
|
||||
formElements: [],
|
||||
elementItems: [],
|
||||
activeElement: ''
|
||||
}
|
||||
|
||||
330
src/tempData.js
Normal file
330
src/tempData.js
Normal file
@@ -0,0 +1,330 @@
|
||||
export const bandoTest = {
|
||||
"name": "Innovazione digitale 2024",
|
||||
"confidi": false,
|
||||
"descriptionShort": "Supporto alle PMI per progetti di digitalizzazione e innovazione tecnologica.",
|
||||
"descriptionLong": "Il bando \"Innovazione Digitale 2024\" mira a sostenere le PMI nell'adozione di tecnologie digitali innovative. I progetti finanziabili includono l'implementazione di soluzioni di intelligenza artificiale, blockchain, IoT, e altre tecnologie avanzate che possono migliorare la competitività delle imprese.",
|
||||
"documentationRequested": "Documentazione richiesta*",
|
||||
"dates": [
|
||||
"2024-08-27T22:00:00.000Z",
|
||||
"2024-10-29T23:00:00.000Z"
|
||||
],
|
||||
"amount": 10000,
|
||||
"amountMax": 2500,
|
||||
"aimedTo": [
|
||||
{
|
||||
"id": 3,
|
||||
"value": "PMI con sede in Umbria",
|
||||
"status": "existing"
|
||||
}
|
||||
],
|
||||
"faq": [
|
||||
{
|
||||
"id": 2,
|
||||
"question": "Question 1?",
|
||||
"answer": "Lorem ipsum dolor",
|
||||
"visible": true,
|
||||
"status": "existing"
|
||||
}
|
||||
],
|
||||
status: 'draft',
|
||||
id: 11,
|
||||
createdDate: "2024-08-07T08:14:07.849Z",
|
||||
updatedDate: "2024-08-07T08:14:07.849Z",
|
||||
"documentation":[
|
||||
{
|
||||
"id":18,
|
||||
"name":"SCR-20240820-kiwn.pdf",
|
||||
"filePath":"https://mementoresources.s3.eu-west-1.amazonaws.com/gepafin/SCR-20240820-kiwn.pdf",
|
||||
"description":null,
|
||||
"createdDate":"2024-08-26T06:51:11.800799252",
|
||||
"updatedDate":"2024-08-26T06:51:11.800826092"
|
||||
}
|
||||
],
|
||||
"criteria":[
|
||||
{
|
||||
"id":null,
|
||||
"value":"Innovatività del progetto",
|
||||
"status":"new",
|
||||
"score":9
|
||||
},
|
||||
{
|
||||
"id":null,
|
||||
"value":"Impatto sulla competitività dell'azienda",
|
||||
"status":"new",
|
||||
"score":3
|
||||
},
|
||||
{
|
||||
"id":null,
|
||||
"value":"Sostenibilità economico-finanziaria",
|
||||
"status":"new",
|
||||
"score":5
|
||||
}
|
||||
],
|
||||
"threshold":11,
|
||||
"images":[
|
||||
{
|
||||
"id":19,
|
||||
"name":"photo-1618245318763-a15156d6b23c.avif",
|
||||
"filePath":"https://mementoresources.s3.eu-west-1.amazonaws.com/gepafin/photo-1618245318763-a15156d6b23c.avif",
|
||||
"description":null,
|
||||
"createdDate":"2024-08-26T07:28:16.954763338",
|
||||
"updatedDate":"2024-08-26T07:28:16.954843237"
|
||||
}
|
||||
],
|
||||
"checklist":[
|
||||
{
|
||||
"id":null,
|
||||
"value":"Innovatività del progetto",
|
||||
"status":"new"
|
||||
},
|
||||
{
|
||||
"id":null,
|
||||
"value":"Some new check",
|
||||
"status":"new"
|
||||
},
|
||||
{
|
||||
"id":null,
|
||||
"value":"Check #2",
|
||||
"status":"new"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const formData = {
|
||||
id: 15,
|
||||
label: 'La forma per Innovazione digitale 2024',
|
||||
content: [
|
||||
{
|
||||
"id": "aec5ee1885",
|
||||
"name": "textinput",
|
||||
"label": "Text Input",
|
||||
"settings": [
|
||||
{
|
||||
"name": "label",
|
||||
"value": "Text input"
|
||||
},
|
||||
{
|
||||
"name": "placeholder",
|
||||
"value": "Placeholder text"
|
||||
}
|
||||
],
|
||||
"validators": {
|
||||
"isRequired": false,
|
||||
"minLength": null,
|
||||
"maxLength": null,
|
||||
"pattern": null,
|
||||
"custom": null
|
||||
},
|
||||
"dbId": 1
|
||||
},
|
||||
{
|
||||
"id": "a730f1f4d0",
|
||||
"name": "textarea",
|
||||
"label": "Text Area",
|
||||
"settings": [
|
||||
{
|
||||
"name": "label",
|
||||
"value": "Text area"
|
||||
},
|
||||
{
|
||||
"name": "placeholder",
|
||||
"value": "Placeholder text"
|
||||
}
|
||||
],
|
||||
"validators": {
|
||||
"isRequired": false,
|
||||
"minLength": null,
|
||||
"maxLength": null,
|
||||
"pattern": null,
|
||||
"custom": null
|
||||
},
|
||||
"dbId": 2
|
||||
},
|
||||
{
|
||||
"id": "aa8746a7c3",
|
||||
"name": "textinput",
|
||||
"label": "P.IVA",
|
||||
"settings": [
|
||||
{
|
||||
"name": "label",
|
||||
"value": "P.IVA"
|
||||
},
|
||||
{
|
||||
"name": "placeholder",
|
||||
"value": "Insert p.iva number"
|
||||
}
|
||||
],
|
||||
"validators": {
|
||||
"isRequired": true,
|
||||
"minLength": null,
|
||||
"maxLength": null,
|
||||
"pattern": null,
|
||||
"custom": "isValidVAT"
|
||||
},
|
||||
"dbId": 4
|
||||
},
|
||||
{
|
||||
"id": "ae3dde17cd",
|
||||
"name": "radio",
|
||||
"label": "Radio Input",
|
||||
"settings": [
|
||||
{
|
||||
"name": "label",
|
||||
"value": "Radio input"
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": [
|
||||
{
|
||||
"name": "opt1",
|
||||
"label": "Opt1"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"validators": {
|
||||
"isRequired": false,
|
||||
"min": null,
|
||||
"max": null,
|
||||
"custom": null
|
||||
},
|
||||
"dbId": 5
|
||||
},
|
||||
{
|
||||
"id": "abf838016f",
|
||||
"name": "textinput",
|
||||
"label": "Number Input",
|
||||
"settings": [
|
||||
{
|
||||
"name": "label",
|
||||
"value": "Number"
|
||||
},
|
||||
{
|
||||
"name": "placeholder",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"validators": {
|
||||
"isRequired": false,
|
||||
"min": null,
|
||||
"max": null,
|
||||
"pattern": null,
|
||||
"custom": null
|
||||
},
|
||||
"dbId": 3
|
||||
}
|
||||
]
|
||||
};
|
||||
export const elementItems = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'textinput',
|
||||
label: 'Text Input',
|
||||
settings: [
|
||||
{
|
||||
name: "label",
|
||||
value: "Text input"
|
||||
},
|
||||
{
|
||||
name: "placeholder",
|
||||
value: "Placeholder text"
|
||||
}
|
||||
],
|
||||
validators: {
|
||||
isRequired: false,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
pattern: null,
|
||||
custom: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'textarea',
|
||||
label: 'Text Area',
|
||||
settings: [
|
||||
{
|
||||
name: "label",
|
||||
value: "Text area"
|
||||
},
|
||||
{
|
||||
name: "placeholder",
|
||||
value: "Placeholder text"
|
||||
}
|
||||
],
|
||||
validators: {
|
||||
isRequired: false,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
pattern: null,
|
||||
custom: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'textinput',
|
||||
label: 'Number Input',
|
||||
settings: [
|
||||
{
|
||||
name: "label",
|
||||
value: "Number"
|
||||
},
|
||||
{
|
||||
name: "placeholder",
|
||||
value: ""
|
||||
}
|
||||
],
|
||||
validators: {
|
||||
isRequired: false,
|
||||
min: null,
|
||||
max: null,
|
||||
pattern: null,
|
||||
custom: null
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'textinput',
|
||||
label: 'P.IVA',
|
||||
settings: [
|
||||
{
|
||||
name: "label",
|
||||
value: "P.IVA"
|
||||
},
|
||||
{
|
||||
name: "placeholder",
|
||||
value: "Insert p.iva number"
|
||||
}
|
||||
],
|
||||
validators: {
|
||||
isRequired: true,
|
||||
minLength: null,
|
||||
maxLength: null,
|
||||
pattern: null,
|
||||
custom: 'isValidVAT'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'radio',
|
||||
label: 'Radio Input',
|
||||
settings: [
|
||||
{
|
||||
name: "label",
|
||||
value: "Radio input"
|
||||
},
|
||||
{
|
||||
name: "options",
|
||||
value: [
|
||||
{ name: "opt1", label: "Opt1" }
|
||||
]
|
||||
}
|
||||
],
|
||||
validators: {
|
||||
isRequired: false,
|
||||
min: null,
|
||||
max: null,
|
||||
custom: null
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -4000,6 +4000,11 @@ deep-is@^0.1.3, deep-is@~0.1.3:
|
||||
resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
|
||||
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
|
||||
|
||||
deep-object-diff@^1.1.9:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz"
|
||||
integrity sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==
|
||||
|
||||
deepmerge@^4.2.2, deepmerge@^4.3.0:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz"
|
||||
|
||||
Reference in New Issue
Block a user