- added bando preview page;

- added bando form preview;
This commit is contained in:
Vitalii Kiiko
2024-08-27 17:02:16 +02:00
parent 5095ed7365
commit 87684bc76b
37 changed files with 1235 additions and 246 deletions

View File

@@ -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"/>

View File

@@ -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}/>

View File

@@ -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}/>

View File

@@ -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);
}

View File

@@ -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>
)

View 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;

View File

@@ -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>
)
}

View File

@@ -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(() => ({

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View File

@@ -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"/>

View 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;

View File

@@ -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>