- added form edit page;

- saving progress with the builder;
This commit is contained in:
Vitalii Kiiko
2024-08-20 15:23:07 +02:00
parent 74d2da78d6
commit 0a21444ee4
11 changed files with 452 additions and 11 deletions

View File

@@ -0,0 +1,74 @@
.formBuilder {
display: flex;
gap: 20px;
width: 100%;
min-height: 500px;
}
.formBuilder__main {
flex: 1 1 100%;
display: flex;
flex-direction: column;
h2 {
color: #404D5B;
font-size: 21px;
font-style: normal;
font-weight: 600;
line-height: normal;
}
}
.formBuilder__content {
flex-grow: 2;
display: flex;
flex-direction: column;
gap: 7px;
padding: 20px;
border: 1px #DDD;
background: var(--surface-50);
box-shadow: 0px 0px 2px 1px rgba(0, 0, 0, 0.10);
}
.formBuilder__element {
display: flex;
padding: 10px 20px;
border: 1px dashed var(--button-secondary-borderColor);
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 21px;
}
.formBuilder__aside {
flex: 0 0 250px;
display: flex;
flex-direction: column;
h2 {
color: #404D5B;
font-size: 21px;
font-style: normal;
font-weight: 600;
line-height: normal;
}
}
.formBuilder__list {
display: flex;
flex-direction: column;
gap: 5px;
list-style: none;
padding: 0;
margin: 0;
li {
display: flex;
padding: 10px 20px;
border: 1px dashed var(--button-secondary-borderColor);
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 21px;
}
}

View File

@@ -109,4 +109,5 @@ body {
@import "./components/statsBigBadges.scss";
@import "./components/bandoStatusTag.scss";
@import "./components/appForm.scss";
@import "./components/pageBando.scss";
@import "./components/pageBando.scss";
@import "./components/formBuilder.scss";

View File

@@ -0,0 +1,65 @@
import React, { useRef, } from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { ItemTypes } from '../ItemTypes';
const BuilderElement = ({ id, text, index, move }) => {
const ref = useRef(null);
const [{ handlerId }, drop] = useDrop({
accept: ItemTypes.FIELD,
collect(monitor) {
return {
handlerId: monitor.getHandlerId(),
}
},
hover(item, monitor) {
if (!ref.current) {
return
}
const dragIndex = item.index
const hoverIndex = index
if (dragIndex === hoverIndex) {
return
}
const hoverBoundingRect = ref.current?.getBoundingClientRect()
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2
const clientOffset = monitor.getClientOffset()
const hoverClientY = clientOffset.y - hoverBoundingRect.top
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return
}
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return
}
move(dragIndex, hoverIndex)
item.index = hoverIndex
},
});
const [{ isDragging }, drag] = useDrag({
type: ItemTypes.FIELD,
item: () => {
return { id, index }
},
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
const opacity = isDragging ? 0 : 1;
drag(drop(ref));
return (
<div ref={ref} className="formBuilder__element" style={{ opacity }} data-handler-id={handlerId}>
{text}
</div>
)
}
export default BuilderElement;

View File

@@ -0,0 +1,73 @@
import React, { useCallback, useState } from 'react'
import { __ } from '@wordpress/i18n';
// components
import BuilderElement from '../BuilderElement';
const FormBuilder = () => {
const [fields, setFields] = useState([
{
id: 1,
text: 'Write a cool JS library',
},
{
id: 2,
text: 'Make it generic enough',
}
]);
const [items, setItems] = useState([
{
id: 1,
label: 'Text Input'
},
{
id: 2,
label: 'Text Area'
}
])
const moveField = useCallback((dragIndex, hoverIndex) => {
console.log('dragIndex, hoverIndex:', dragIndex, hoverIndex)
/*setFields((prevCards) =>
update(prevCards, {
$splice: [
[dragIndex, 1],
[hoverIndex, 0, prevCards[dragIndex]],
],
}),
)*/
}, []);
const renderField = useCallback((card, index) => {
return (
<BuilderElement
key={card.id}
index={index}
id={card.id}
text={card.text}
move={moveField}
/>
)
}, []);
return (
<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))}
</div>
</div>
<div className="formBuilder__aside">
<h2>{__('Elementi del Form', 'gepafin')}</h2>
<ul className="formBuilder__list">
{items.map((item) => <li key={item.id}>
{item.label}
</li>)}
</ul>
</div>
</div>
)
}
export default FormBuilder;

View File

@@ -0,0 +1,3 @@
export const ItemTypes = {
FIELD: 'field',
}

View File

@@ -0,0 +1,87 @@
import React, { useEffect } 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'
// components
import FormBuilder from './components/FormBuilder';
import { Button } from 'primereact/button';
const BandoEditForm = () => {
const { id, formId } = useParams();
const navigate = useNavigate();
//const [data, setData] = useState({});
//const [isLoading, setIsLoading] = useState(true);
const goBack = () => {
navigate('/bandi/11/forms');
}
const doSave = () => {
console.log('doSave');
}
const openPreview = () => {
console.log('openPreview');
}
const doPublish = () => {
console.log('doPublish');
}
useEffect(() => {
const parsed = parseInt(id)
const bandoId = !isNaN(parsed) ? parsed : 0;
const parsedFormId = parseInt(formId)
const bandoFormId = !isNaN(parsedFormId) ? parsedFormId : 0;
// TODO
}, [id]);
return (
<div className="appPage">
<div className="appPage__pageHeader">
<h1>{__('Editor del Form', 'gepafin')}</h1>
<p>
{__('Imposta gli elementi del tuo Form.', 'gepafin')}
</p>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<DndProvider backend={HTML5Backend}>
<FormBuilder/>
</DndProvider>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<div className="appPageSection__actions">
<Button
onClick={goBack}
outlined
label={__('Indietro', 'gepafin')} icon="pi pi-arrow-left" iconPos="left"/>
<Button
onClick={doSave}
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"/>
<Button
disabled={true}
onClick={doPublish}
label={__('Pubblica', 'gepafin')}/>
</div>
</div>
</div>
)
}
export default BandoEditForm;

View File

@@ -1,16 +1,20 @@
import React, { useState, useEffect, useRef } from 'react';
import { __ } from '@wordpress/i18n';
import { useParams } from 'react-router-dom';
import { Skeleton } from 'primereact/skeleton';
import getBandoLabel from '../../helpers/getBandoLabel';
import { useParams, useNavigate } from 'react-router-dom';
// components
import { Button } from 'primereact/button';
const BandoEditForms = () => {
const { id } = useParams();
const navigate = useNavigate();
//const [data, setData] = useState({});
//const [isLoading, setIsLoading] = useState(true);
const doCreateNewForm = () => {
navigate('/bandi/11/forms/new');
}
useEffect(() => {
const parsed = parseInt(id)
const bandoId = !isNaN(parsed) ? parsed : 0;
@@ -26,6 +30,15 @@ const BandoEditForms = () => {
{__('Scegli come vuoi procedere:', 'gepafin')}
</p>
</div>
<div className="appPage__spacer"></div>
<div className="appPageSection">
<Button
type="button"
onClick={doCreateNewForm}
label={__('Crea/modifica form', 'gepafin')}/>
</div>
</div>
)

View File

@@ -10,6 +10,7 @@ import Bandi from './pages/Bandi';
import BandoEdit from './pages/BandoEdit';
import BandoView from './pages/BandoView';
import BandoEditForms from './pages/BandoEditForms';
import BandoEditForm from './pages/BandoEditForm';
const routes = () => (
<Routes>
@@ -20,6 +21,7 @@ const routes = () => (
<Route path="/bandi/preview-evaluation/:id" element={<DefaultLayout><BandoView/></DefaultLayout>}/>
<Route path="/bandi/:id" element={<DefaultLayout><BandoEdit/></DefaultLayout>}/>
<Route path="/bandi/:id/forms" element={<DefaultLayout><BandoEditForms/></DefaultLayout>}/>
<Route path="/bandi/:id/forms/:formId" element={<DefaultLayout><BandoEditForm/></DefaultLayout>}/>
</Route>
<Route exact path="/login" element={<Login/>}/>
{/*<Route exact path="/forgot-password" element={<ForgotPassword/>}/>*/}