- 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

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