- styled asteriks sign;

- fixed issue with validation on registration page;
- fixed issue with inputnumber;
- fixed issue with editor field;;
- added editors for new faq item form;
- fixed displaying html as simple text;
- fixed saving company data after saving;
- added toast for edit bando form;
- improved edit forms form;
- fixed styles for various elements;
This commit is contained in:
Vitalii Kiiko
2024-10-04 11:31:47 +02:00
parent af52610b30
commit 7804a67fd2
36 changed files with 520 additions and 637 deletions

View File

@@ -4,6 +4,7 @@
gap: 24px;
}
.appForm__field {
position: relative;
display: flex;
flex-direction: column;
gap: 14px;
@@ -58,9 +59,58 @@
cursor: pointer;
}
}
:where(table) {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
text-indent: 0;
border-right: 1px solid var(--table-border-color);
}
td,
th {
padding: 10px;
border-top: 1px solid var(--table-border-color);
border-bottom: 1px solid var(--table-border-color);
border-left: 1px solid var(--table-border-color);
background-color: white;
color: var(--global-textColor);
font-size: 15px;
text-align: left;
text-align: start;
}
th {
padding: 15px 10px;
font-weight: bold;
}
td {
input {
width: 100%;
padding: 3px 5px;
}
}
tfoot td,
tfoot th {
border-top: 1px solid var(--table-border-color);
border-bottom: 0
}
table.striped tbody tr:nth-child(odd) td,
table.striped tbody tr:nth-child(odd) th {
background-color: var(--table-border-color)
}
}
}
.appForm__field--required.appForm__field--required {
margin-left: 2px;
color: var(--message-error-color);
}
.appForm__fieldItem {
display: flex;
gap: 0.5rem;

View File

@@ -218,6 +218,16 @@
width: 100%;
}
.appPageSection__titleClickable {
width: 100%;
height: 100%;
display: block;
&:hover {
cursor: pointer;
}
}
.appTableHeader {
display: flex;
justify-content: space-between;

View File

@@ -104,47 +104,3 @@ body {
}
}
}
:where(table) {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
text-indent: 0;
border-right: 1px solid var(--table-border-color);
}
td,
th {
padding: 10px;
border-top: 1px solid var(--table-border-color);
border-bottom: 1px solid var(--table-border-color);
border-left: 1px solid var(--table-border-color);
background-color: white;
color: var(--global-textColor);
font-size: 15px;
text-align: left;
text-align: start;
}
th {
padding: 15px 10px;
font-weight: bold;
}
td {
input {
width: 100%;
padding: 3px 5px;
}
}
tfoot td,
tfoot th {
border-top: 1px solid var(--table-border-color);
border-bottom: 0
}
table.striped tbody tr:nth-child(odd) td,
table.striped tbody tr:nth-child(odd) th {
background-color: var(--table-border-color)
}

View File

@@ -110,6 +110,12 @@
align-items: center;
}
.p-accordion-header-text, .p-accordion-content {
p {
margin: 0;
}
}
.flex-1 {
display: flex;
align-items: center;

View File

@@ -65,7 +65,7 @@ const Checkboxes = ({
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
</label>
{input}
{infoText ? <small>{infoText}</small> : null}

View File

@@ -20,7 +20,7 @@ const Datepicker = ({
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
</label>
<Controller
name={fieldName}

View File

@@ -1,13 +1,16 @@
import React, { useEffect, useState, useRef } from 'react';
import { classNames } from 'primereact/utils';
import { head } from 'ramda'
import { __ } from '@wordpress/i18n';
import FileUploadService from '../../../../service/file-upload-service';
import { FileUpload } from 'primereact/fileupload';
import { __ } from '@wordpress/i18n';
import { Tag } from 'primereact/tag';
import { Button } from 'primereact/button';
import { mimeTypes } from '../../../../configData';
const Fileupload = ({
fieldName,
setDataFn,
@@ -17,7 +20,7 @@ const Fileupload = ({
defaultValue = [],
config = {},
infoText = null,
accept = 'image/*',
accept = ['image/*'],
doctype = 'images',
maxSize = 100000000,
emptyText = __('Trascina qui il tuo file', 'gepafin'),
@@ -119,6 +122,21 @@ const Fileupload = ({
});
}
const getPropeMimeLabels = (acceptFormats) => {
return acceptFormats
.map(v => {
const found = head(mimeTypes.filter(o => o.code === v));
let res = v;
if (found) {
res = found.name;
}
return res;
})
.join(', ');
}
useEffect(() => {
setStateFieldData(defaultValue);
register(fieldName, config)
@@ -126,7 +144,7 @@ const Fileupload = ({
useEffect(() => {
// eslint-disable-next-line no-useless-escape
setAcceptFormats(accept.replace(/\*/g, '.\*').replace(/,/g, '|'));
setAcceptFormats(accept.join(',').replace(/\*/g, '.\*').replace(/,/g, '|'));
}, [accept]);
useEffect(() => {
@@ -140,8 +158,8 @@ const Fileupload = ({
sourceId || sourceId === 0
? <>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
{acceptFormats ? ' (' + acceptFormats.split('|').join(', ') + ')' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
{acceptFormats ? ' (' + getPropeMimeLabels(accept) + ')' : null}
</label>
<FileUpload
ref={inputRef}

View File

@@ -7,6 +7,8 @@ import FileUploadService from '../../../../service/file-upload-service';
import { FileUpload } from 'primereact/fileupload';
import { Tag } from 'primereact/tag';
import { Button } from 'primereact/button';
import { head } from 'ramda';
import { mimeTypes } from '../../../../configData';
const FileuploadAsync = ({
fieldName,
@@ -17,7 +19,7 @@ const FileuploadAsync = ({
defaultValue = [],
config = {},
infoText = null,
accept = 'image/*',
accept = ['image/*'],
doctype = 'images',
maxSize = 100000000,
emptyText = __('Trascina qui il tuo file', 'gepafin'),
@@ -121,6 +123,21 @@ const FileuploadAsync = ({
});
}
const getPropeMimeLabels = (acceptFormats) => {
return acceptFormats
.map(v => {
const found = head(mimeTypes.filter(o => o.code === v));
let res = v;
if (found) {
res = found.name;
}
return res;
})
.join(', ');
}
useEffect(() => {
setStateFieldData(defaultValue);
register(fieldName, config)
@@ -128,7 +145,7 @@ const FileuploadAsync = ({
useEffect(() => {
// eslint-disable-next-line no-useless-escape
setAcceptFormats(accept.replace(/\*/g, '.\*').replace(/,/g, '|'));
setAcceptFormats(accept.join(',').replace(/\*/g, '.\*').replace(/,/g, '|'));
}, [accept]);
useEffect(() => {
@@ -143,7 +160,7 @@ const FileuploadAsync = ({
? <>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required ? '*' : null}
{acceptFormats ? ' (' + acceptFormats.split('|').join(', ') + ')' : null}
{acceptFormats ? ' (' + getPropeMimeLabels(accept) + ')' : null}
</label>
<FileUpload
ref={inputRef}

View File

@@ -20,8 +20,9 @@ const NumberInput = ({
min,
max,
disabled = false,
useGrouping = true
useGrouping = false
}) => {
console.log('defaultValue', defaultValue);
const input = <Controller
name={fieldName}
control={control}
@@ -43,10 +44,10 @@ const NumberInput = ({
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
</label>
{inputgroup
? <div className="p-inputgroup flex-1">
? <div className="p-inputgroup">
<span className="p-inputgroup-addon">
{icon}
</span>

View File

@@ -34,7 +34,7 @@ const Radio = ({
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
</label>
{input}
{infoText ? <small>{infoText}</small> : null}

View File

@@ -36,10 +36,10 @@ const Select = ({
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
</label>
{inputgroup
? <div className="p-inputgroup flex-1">
? <div className="p-inputgroup">
<span className="p-inputgroup-addon">
{icon}
</span>

View File

@@ -36,7 +36,7 @@ const Switch = ({
<>
<div className="appForm__row">
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] }, 'mr-8')}>
{label}{config.required || config.isRequired ? '*' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
</label>
<div className="appForm__row">
{offLabel ? <span>{offLabel}</span> : null}

View File

@@ -0,0 +1,79 @@
import React from 'react';
import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
import { wrap } from 'object-path-immutable';
const RenderTable = ({ data, columns, setRowsFn }) => {
const table = useReactTable({
data,
columns,
defaultColumn: {
cell: ({ getValue, row: { index }, column: { id }, table }) => {
const initialValue = getValue();
const onBlur = (e) => {
table.options.meta?.updateData(index, id, e.target.value);
};
return (
<input
value={initialValue ? initialValue : ''}
onChange={(e) => table.options.meta?.updateData(index, id, e.target.value)}
onBlur={onBlur}
/>
);
},
},
getCoreRowModel: getCoreRowModel(),
meta: {
updateData: (rowIndex, columnId, value) => {
const newRowsData = wrap(data).set([rowIndex, columnId], value).value();
setRowsFn(newRowsData);
},
}
});
return (
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<th key={header.id} colSpan={header.colSpan}>
{header.isPlaceholder ? null : (
<>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</>
)}
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
return (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
)
}
export default RenderTable

View File

@@ -1,17 +1,16 @@
import React, { useEffect, useState } from 'react';
import { classNames } from 'primereact/utils';
import { __ } from '@wordpress/i18n';
import {
useReactTable,
getCoreRowModel,
flexRender,
} from '@tanstack/react-table';
import { pathOr, isEmpty, isNil } from 'ramda';
import { wrap } from 'object-path-immutable';
//components
import { Button } from 'primereact/button';
import RenderTable from './RenderTable';
const Table = ({
fieldName,
setDataFn,
label,
register,
errors,
@@ -19,59 +18,46 @@ const Table = ({
defaultValue = [],
tableColumns = []
}) => {
const [stateFieldData, setStateFieldData] = useState([]);
const [rowsData, setRowsData] = useState([]);
const [rowsDataLength, setRowsDataLength] = useState(0);
const [rowIndexToDelete, rowRowIndexToDelete] = useState(null);
const [isDisabledNewRow, setIsDisabledNewRow] = useState(false);
const [columnsCfg, setColumnsCfg] = useState([]);
const [rowsCfg, setRowsCfg] = useState([]);
const [columns, setColumns] = useState([]);
const table = useReactTable({
data: rowsData,
columns,
defaultColumn: {
cell: ({ getValue, row: { index }, column: { id }, table }) => {
const initialValue = getValue();
const onBlur = (e) => {
table.options.meta?.updateData(index, id, e.target.value);
};
return (
<input
value={initialValue}
onChange={(e) => table.options.meta?.updateData(index, id, e.target.value)}
onBlur={onBlur}
/>
);
},
},
getCoreRowModel: getCoreRowModel(),
meta: {
updateData: (rowIndex, columnId, value) => {
const newRowsData = wrap(rowsData).set([rowIndex, columnId], value).value();
setRowsData(newRowsData);
},
},
debugTable: true,
});
const [rows, setRows] = useState([]);
const [shouldDisableNewRows, setShouldDisableNewRows] = useState(false);
const [rowIndexToDelete, rowRowIndexToDelete] = useState(null);
const addNewRow = () => {
const obj = stateFieldData
const obj = columnsCfg
.reduce((acc, cur) => {
acc[cur.name] = ''
return acc;
}, {});
setRowsData([...rowsData, obj]);
const newRowsData = [...rows, obj];
setRows(newRowsData);
setDataFn(fieldName, newRowsData, { shouldValidate: true });
}
const removeRow = (index) => {
rowRowIndexToDelete(index)
rowRowIndexToDelete(index);
}
useEffect(() => {
if (!isNil(rowIndexToDelete)) {
const newRowsData = wrap(rows).del([rowIndexToDelete]).value();
setRows(newRowsData);
setDataFn(fieldName, [...newRowsData], { shouldValidate: true });
}
rowRowIndexToDelete(null);
}, [rowIndexToDelete]);
const updateRows = (data) => {
setRows(data);
setDataFn(fieldName, data, { shouldValidate: true });
}
useEffect(() => {
let shouldDisableNewRows = false;
let columns = stateFieldData.map((o) => {
let newColumns = columnsCfg.map((o) => {
const item = {
accessorKey: o.name,
header: () => o.label,
@@ -88,10 +74,10 @@ const Table = ({
return item;
});
setIsDisabledNewRow(shouldDisableNewRows);
setShouldDisableNewRows(shouldDisableNewRows);
if (!shouldDisableNewRows && !isEmpty(columns)) {
columns.push({
if (!shouldDisableNewRows && !isEmpty(newColumns)) {
newColumns.push({
accessorKey: 'actions',
header: () => '',
footer: (props) => props.column.id,
@@ -104,20 +90,12 @@ const Table = ({
})
}
setColumns(columns);
}, [stateFieldData, rowsDataLength]);
setColumns(newColumns);
}, [columnsCfg]);
useEffect(() => {
setRowsDataLength(rowsData.length);
}, [rowsData]);
useEffect(() => {
if (!isNil(rowIndexToDelete)) {
const newRowsData = wrap(rowsData).del([rowIndexToDelete]).value();
setRowsData(newRowsData);
}
rowRowIndexToDelete(null);
}, [rowIndexToDelete]);
setRows(rowsCfg);
}, [rowsCfg]);
useEffect(() => {
const stateFieldData = pathOr([], ['stateFieldData'], tableColumns);
@@ -128,60 +106,21 @@ const Table = ({
}, {});
let rowsData = pathOr([obj], ['rowsData'], tableColumns);
rowsData = isEmpty(rowsData) ? [obj] : rowsData;
setStateFieldData(stateFieldData);
setRowsData(rowsData);
setColumnsCfg(stateFieldData);
setRowsCfg(rowsData);
}, [tableColumns]);
useEffect(() => {
register(fieldName, config)
register(fieldName, config);
}, []);
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
</label>
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<th key={header.id} colSpan={header.colSpan}>
{header.isPlaceholder ? null : (
<>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</>
)}
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
return (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
{!isDisabledNewRow && !isEmpty(columns)
<RenderTable columns={columns} data={rows} setRowsFn={updateRows}/>
{!isEmpty(columns) && !shouldDisableNewRows
? <div className="addNewTableRow" onClick={addNewRow}>{__('Aggiungi una righa', 'gepafin')}</div>
: null}
</>)

View File

@@ -17,7 +17,7 @@ const TextArea = ({
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
</label>
<Controller
name={fieldName}

View File

@@ -37,10 +37,10 @@ const TextInput = ({
return (
<>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
</label>
{inputgroup
? <div className="p-inputgroup flex-1">
? <div className="p-inputgroup">
<span className="p-inputgroup-addon">
{icon}
</span>

View File

@@ -2,6 +2,7 @@ import React from 'react';
import { classNames } from 'primereact/utils';
import { Controller } from 'react-hook-form';
import { Editor } from 'primereact/editor';
import BlockingOverlay from '../../../BlockingOverlay';
const Wysiwyg = ({
fieldName,
@@ -12,6 +13,7 @@ const Wysiwyg = ({
defaultValue,
config = {},
infoText = null,
placeholder={placeholder},
disabled = false
}) => {
@@ -36,8 +38,9 @@ const Wysiwyg = ({
return (
<>
<BlockingOverlay shouldDisplay={disabled}/>
<label htmlFor={fieldName} className={classNames({ 'p-error': errors[fieldName] })}>
{label}{config.required || config.isRequired ? '*' : null}
{label}{config.required || config.isRequired ? <span className="appForm__field--required">*</span> : null}
</label>
<Controller
name={fieldName}
@@ -50,6 +53,7 @@ const Wysiwyg = ({
readOnly={disabled}
{...field}
headerTemplate={header}
placeholder={placeholder}
onTextChange={(e) => field.onChange(e.htmlValue)}
style={{ height: 80 * rows }}
className={classNames({ 'p-invalid': fieldState.invalid })}

View File

@@ -9,9 +9,9 @@ import { Button } from 'primereact/button';
import { Dropdown } from 'primereact/dropdown';
import { Accordion, AccordionTab } from 'primereact/accordion';
import { Dialog } from 'primereact/dialog';
import { InputText } from 'primereact/inputtext';
import { InputTextarea } from 'primereact/inputtextarea';
import { InputSwitch } from 'primereact/inputswitch';
import renderHtmlContent from '../../helpers/renderHtmlContent';
import { Editor } from 'primereact/editor';
const FormFieldRepeaterFaq = ({
data,
@@ -108,7 +108,8 @@ const FormFieldRepeaterFaq = ({
const footerEditDialog = () => {
return <div>
<Button type="button" disabled={disabled} label={__('Anulla', 'gepafin')} onClick={hideEditDialog} outlined/>
<Button type="button" disabled={disabled} label={__('Anulla', 'gepafin')} onClick={hideEditDialog}
outlined/>
<Button
type="button"
disabled={isEmpty(title) || isEmpty(question) || isEmpty(answer) || disabled}
@@ -122,6 +123,26 @@ const FormFieldRepeaterFaq = ({
.map(o => o.title)
}, [stateFieldData]);
const renderHeader = () => {
return (
<span className="ql-formats">
<button className="ql-bold" aria-label="Bold"></button>
<button className="ql-italic" aria-label="Italic"></button>
<button className="ql-underline" aria-label="Underline"></button>
<button className="ql-link" aria-label="Link"></button>
<button className="ql-list" value="ordered"></button>
<button className="ql-header" value="2"></button>
<button className="ql-header" value="3"></button>
<button className="ql-blockquote"></button>
<button className="ql-list" value="bullet"></button>
<button className="ql-indent" value="-1"></button>
<button className="ql-indent" value="+1"></button>
</span>
);
};
const header = renderHeader();
useEffect(() => {
const storeFieldData = data ?? [];
setStateFieldData(storeFieldData);
@@ -163,41 +184,42 @@ const FormFieldRepeaterFaq = ({
optionLabel="title"/>
</div>
<Accordion activeIndex={0}>
{stateFieldData.map((o, i) => <AccordionTab key={i} tabIndex={i}
header={
<div className="appForm__faqTab">
<div className="appForm__faqTabItem">
{stateFieldData.map((o, i) =>
<AccordionTab key={i} tabIndex={i}
header={
<div className="appForm__faqTab">
<div className="appForm__faqTabItem">
<span>
{o.value}
{renderHtmlContent(o.value)}
</span>
</div>
<div className="appForm__faqTabItem">
{o.isVisible
? <i className="pi pi-eye"
style={{ fontSize: '1.5rem' }}></i> : null}
{!o.isVisible
? <i className="pi pi-eye-slash"
style={{ fontSize: '1.5rem' }}></i> : null}
<Button icon="pi pi-pencil" severity="success"
disabled={disabled}
className="actionBtn"
type="button"
aria-label={__('Modifica', 'gepafin')}
onClick={(e) => editItem(e, i)}/>
<Button icon="pi pi-times" severity="danger"
disabled={disabled}
className="actionBtn"
type="button"
aria-label={__('Cancella', 'gepafin')}
onClick={() => removeItem(i)}/>
</div>
</div>
}
>
<p className="m-0">
{o.response}
</p>
</AccordionTab>)}
</div>
<div className="appForm__faqTabItem">
{o.isVisible
? <i className="pi pi-eye"
style={{ fontSize: '1.5rem' }}></i> : null}
{!o.isVisible
? <i className="pi pi-eye-slash"
style={{ fontSize: '1.5rem' }}></i> : null}
<Button icon="pi pi-pencil" severity="success"
disabled={disabled}
className="actionBtn"
type="button"
aria-label={__('Modifica', 'gepafin')}
onClick={(e) => editItem(e, i)}/>
<Button icon="pi pi-times" severity="danger"
disabled={disabled}
className="actionBtn"
type="button"
aria-label={__('Cancella', 'gepafin')}
onClick={() => removeItem(i)}/>
</div>
</div>
}
>
<p className="m-0">
{renderHtmlContent(o.response)}
</p>
</AccordionTab>)}
</Accordion>
<Dialog
visible={isVisibleEditDialog}
@@ -205,26 +227,41 @@ const FormFieldRepeaterFaq = ({
footer={footerEditDialog}
style={{ maxWidth: '600px', width: '100%' }}
onHide={hideEditDialog}>
<div className="appPage__spacer"></div>
<div className="appForm__field">
<label>{__('Titolo FAQ', 'gepafin')}</label>
<InputText value={title} onChange={(e) => onChangeEditItem(e.target.value, 'title')}/>
<label for="faqTitle">{__('Titolo FAQ', 'gepafin')}</label>
<Editor
id="faqTitle"
value={title}
headerTemplate={header}
onTextChange={(e) => onChangeEditItem(e.htmlValue, 'title')}
style={{ height: 80 * 1 }}
/>
</div>
<div className="appForm__field">
<label>{__('Domanda', 'gepafin')}</label>
<InputText value={question} onChange={(e) => onChangeEditItem(e.target.value, 'value')}/>
<label for="faqValue">{__('Domanda', 'gepafin')}</label>
<Editor
id="faqValue"
value={question}
headerTemplate={header}
onTextChange={(e) => onChangeEditItem(e.htmlValue, 'value')}
style={{ height: 80 * 1 }}
/>
</div>
<div className="appForm__field">
<label>{__('Risposta', 'gepafin')}</label>
<InputTextarea value={answer} onChange={(e) => onChangeEditItem(e.target.value, 'response')}
rows={5}
cols={30}/>
<label for="faqResponse">{__('Risposta', 'gepafin')}</label>
<Editor
id="faqResponse"
value={answer}
headerTemplate={header}
onTextChange={(e) => onChangeEditItem(e.htmlValue, 'response')}
style={{ height: 80 * 2 }}
/>
</div>
<div className="appForm__field">
<label>{__('Pubblicato?', 'gepafin')}</label>
<InputSwitch checked={isVisible} onChange={(e) => onChangeEditItem(e.value, 'isVisible')}/>
<InputSwitch checked={isVisible}
onChange={(e) => onChangeEditItem(e.value, 'isVisible')}/>
</div>
<div className="appPage__spacer"></div>
</Dialog>
</div>
)

View File

@@ -1,6 +1,10 @@
export const mimeTypes = [
{ name: 'PDF file', code: 'application/pdf' },
{ name: 'ZIP file', code: 'application/zip' },
{ name: 'PDF', code: 'application/pdf' },
{ name: 'ZIP', code: 'application/zip' },
{ name: 'Immagine', code: 'image/*' },
{ name: 'Word doc', code: 'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document' }
{
name: 'Word doc',
code: 'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'
},
{ name: 'Excel doc', code: 'application/vnd.ms-excel' }
];

View File

@@ -98,6 +98,10 @@ const AllBandiTable = () => {
);
};
const nameBodyTemplate = (rowData) => {
return <span>{rowData.name}</span>
}
const dateStartBodyTemplate = (rowData) => {
return getDateFromISOstring(rowData.dates[0]);
};
@@ -137,7 +141,8 @@ const AllBandiTable = () => {
globalFilterFields={['name', 'status']}
header={header}
emptyMessage="Nothing found." onFilter={(e) => setFilters(e.filters)}>
<Column field="name" header={__('Nome Bando', 'gepafin')} filter filterPlaceholder="Search by name"
<Column field="name" header={__('Nome Bando', 'gepafin')}
filter filterPlaceholder={__('Cerca', 'gepafin')}
style={{ minWidth: '12rem' }}/>
<Column header={__('Data Pubblicazione', 'gepafin')} filterField="start_date" dataType="date"
style={{ minWidth: '10rem' }}

View File

@@ -1,9 +1,10 @@
import React, { useState, useEffect} from 'react';
import { __ } from '@wordpress/i18n';
import { is, uniq } from 'ramda';
import { is, uniq, isNil } from 'ramda';
import { wrap } from 'object-path-immutable';
// store
import { storeSet, storeGet } from '../../../../store';
import { storeSet, useStore } from '../../../../store';
// tools
import getBandoSeverity from '../../../../helpers/getBandoSeverity';
@@ -23,12 +24,13 @@ import getNumberWithCurrency from '../../../../helpers/getNumberWithCurrency';
import renderHtmlContent from '../../../../helpers/renderHtmlContent';
import { Button } from 'primereact/button';
import { useNavigate } from 'react-router-dom';
import set404FromErrorResponse from '../../../../helpers/set404FromErrorResponse';
const AllBandiAccordion = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const [items, setItems] = useState(null);
const [filters, setFilters] = useState(null);
const [loading, setLoading] = useState(false);
const [expandedRows, setExpandedRows] = useState(null);
const [statuses, setStatuses] = useState([]);
const navigate = useNavigate();
@@ -47,7 +49,7 @@ const AllBandiAccordion = () => {
}
const errGetCallbacks = (data) => {
console.log('errGetCallbacks', data)
set404FromErrorResponse(data);
storeSet.main.unsetAsyncRequest();
}
@@ -58,6 +60,22 @@ const AllBandiAccordion = () => {
});
};
const nameBodyTemplate = (rowData) => {
return <span
className="appPageSection__titleClickable"
onClick={() => {
let newExpandedRows;
if (isNil(expandedRows) || isNil(expandedRows[rowData.id])) {
newExpandedRows = isNil(expandedRows)
? wrap({}).set([rowData.id], true).value()
: wrap(expandedRows).set([rowData.id], true).value();
} else {
newExpandedRows = wrap(expandedRows).del([rowData.id]).value();
}
setExpandedRows(newExpandedRows);
}}>{rowData.name}</span>
}
const amountBodyTemplate = (rowData) => {
return getNumberWithCurrency(rowData.amount);
};
@@ -108,13 +126,16 @@ const AllBandiAccordion = () => {
return(
<div className="appPageSection__table">
<DataTable value={items} paginator rows={10} loading={loading} dataKey="id"
<DataTable value={items} paginator rows={10} loading={isAsyncRequest} dataKey="id"
filters={filters} emptyMessage="Nothing found."
expandedRows={expandedRows} onRowToggle={(e) => setExpandedRows(e.data)}
expandedRows={expandedRows}
onRowToggle={(e) => setExpandedRows(e.data)}
rowExpansionTemplate={rowExpansionTemplate}
onFilter={(e) => setFilters(e.filters)}>
<Column expander={allowExpansion} style={{ width: '5rem' }} />
<Column field="name" header={__('Bando', 'gepafin')} style={{ minWidth: '12rem' }}/>
<Column field="name" header={__('Bando', 'gepafin')}
body={nameBodyTemplate}
style={{ minWidth: '12rem' }}/>
<Column header={__('Importo totale', 'gepafin')} filterField="amount"
style={{ minWidth: '10rem' }} body={amountBodyTemplate} sortable/>
<Column field="status" header={__('Stato', 'gepafin')} filterMenuStyle={{ width: '14rem' }}

View File

@@ -0,0 +1,26 @@
import React from 'react';
import { range } from 'ramda';
import { __ } from '@wordpress/i18n';
// components
import { Steps } from 'primereact/steps';
const ApplicationSteps = ({ totalSteps = 0, activeStepIndex }) => {
const rangeArr = range(1, totalSteps + 1);
const items = rangeArr.map(() => ({ label: 'Passo' }));
// TODO update to using Steps after primereact is updated
return(
0 !== totalSteps
? <span>{__('Passo', 'gepafin')}: {activeStepIndex + 1}</span>
: null
)
/*return(
0 !== totalSteps
? <Steps model={items} activeIndex={activeStepIndex} readOnly/>
: null
)*/
}
export default ApplicationSteps

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { useParams } from 'react-router-dom';
import { klona } from 'klona';
import { head, range, is, pluck } from 'ramda';
import { head, is, pluck, isEmpty } from 'ramda';
import { useForm } from 'react-hook-form';
import { TZDate } from '@date-fns/tz';
@@ -30,9 +30,9 @@ import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import { Skeleton } from 'primereact/skeleton';
import { Button } from 'primereact/button';
import FormField from '../../components/FormField';
import { Steps } from 'primereact/steps';
import { Toast } from 'primereact/toast';
import { Messages } from 'primereact/messages';
import ApplicationSteps from './ApplicationSteps';
const BandoApplication = () => {
const { id } = useParams();
@@ -43,7 +43,6 @@ const BandoApplication = () => {
const [totalSteps, setTotalSteps] = useState(0);
const [completedSteps, setCompletedSteps] = useState(0);
const [activeStep, setActiveStep] = useState(1);
const [stepItems, setStepItems] = useState([{ label: 'Passo' }]);
const isAsyncRequest = useStore().main.isAsyncRequest();
const toast = useRef(null);
const formMsgs = useRef(null);
@@ -54,7 +53,7 @@ const BandoApplication = () => {
setValue,
trigger,
register,
getValues,
getValues
} = useForm({ defaultValues: {}, mode: 'onChange' });
const values = getValues();
const validationFns = {
@@ -67,6 +66,7 @@ const BandoApplication = () => {
isUrl,
isMarcaDaBollo
}
const activeStepIndex = activeStep - 1;
const onSubmit = () => {
const applId = getApplicationId();
@@ -91,7 +91,6 @@ const BandoApplication = () => {
}
const errSubmitApplicationCallback = (data) => {
console.log(data)
storeSet.main.unsetAsyncRequest();
if (data.status === 'VALIDATION_ERROR') {
if (formMsgs.current) {
@@ -120,7 +119,7 @@ const BandoApplication = () => {
}
}
const saveDraft = () => {
const saveDraft = (saveAndMove = '') => {
trigger();
const formValues = getValues();
const usedFieldsIds = pluck('id', formData);
@@ -157,7 +156,7 @@ const BandoApplication = () => {
formMsgs.current.clear();
}
ApplicationService.saveDraft(applId, submitData, saveDraftCallback, errSaveDraftCallback, [
ApplicationService.saveDraft(applId, submitData, (data) => saveDraftCallback(data, saveAndMove), errSaveDraftCallback, [
['formId', formId]
]);
}
@@ -168,7 +167,7 @@ const BandoApplication = () => {
return !isNaN(parsed) ? parsed : 0;
}
const saveDraftCallback = (data) => {
const saveDraftCallback = (data, saveAndMove) => {
if (data.status === 'SUCCESS') {
if (toast.current) {
toast.current.show({
@@ -177,8 +176,16 @@ const BandoApplication = () => {
detail: __('Salvato!', 'gepafin')
});
}
// update info about application completeness
ApplicationService.getApplicationForm(data.data.id, getStatusCheckCallback, errGetStatusCheckCallbacks);
if (!isEmpty(saveAndMove)) {
storeSet.main.setAsyncRequest();
ApplicationService.getApplicationForm(data.data.id, getApplFormCallback, errGetApplFormCallbacks, [
['formId', formId],
['action', saveAndMove]
]);
} else {
// update info about application completeness
ApplicationService.getApplicationForm(data.data.id, getStatusCheckCallback, errGetStatusCheckCallbacks);
}
}
storeSet.main.unsetAsyncRequest();
}
@@ -213,31 +220,37 @@ const BandoApplication = () => {
}
const goBackward = () => {
if (formId) {
saveDraft('PREVIOUS');
/*if (formId) {
const applId = getApplicationId();
storeSet.main.setAsyncRequest();
ApplicationService.getApplicationForm(applId, getApplFormCallback, errGetApplFormCallbacks, [
['formId', formId],
['action', 'PREVIOUS']
]);
}
}*/
}
const goForward = () => {
if (formId) {
saveDraft('NEXT');
/*if (formId) {
const applId = getApplicationId();
storeSet.main.setAsyncRequest();
ApplicationService.getApplicationForm(applId, getApplFormCallback, errGetApplFormCallbacks, [
['formId', formId],
['action', 'NEXT']
]);
}
}*/
}
const getApplFormCallback = (data) => {
if (data.status === 'SUCCESS') {
setBandoTitle(data.data.callTitle);
setFormData(data.data.applicationFormResponse.content);
setFormId(data.data.formId);
setTotalSteps(data.data.totalFormSteps);
setCompletedSteps(data.data.completedSteps);
setActiveStep(data.data.currentStep);
if (data.data.applicationFormResponse.formFields) {
const submitData = data.data.applicationFormResponse.formFields.map((o) => ({
@@ -246,11 +259,6 @@ const BandoApplication = () => {
}));
setFormInitialData(submitData)
}
setFormId(data.data.formId);
setTotalSteps(data.data.totalFormSteps);
setCompletedSteps(data.data.completedSteps);
setActiveStep(data.data.currentStep);
}
storeSet.main.unsetAsyncRequest();
}
@@ -285,11 +293,6 @@ const BandoApplication = () => {
newFormData.map(o => setValue(o.fieldId, o.fieldValue));
}, [formInitialData]);
useEffect(() => {
const rangeArr = range(1, totalSteps + 1);
setStepItems(rangeArr.map(() => ({ label: 'Passo' })));
}, [totalSteps])
useEffect(() => {
const applId = getApplicationId();
@@ -312,8 +315,7 @@ const BandoApplication = () => {
<div className="appPage__spacer"></div>
{!isAsyncRequest
? <Steps model={stepItems} activeIndex={activeStep - 1}/> : null}
<ApplicationSteps totalSteps={totalSteps} activeStepIndex={activeStepIndex}/>
<div className="appPage__spacer"></div>
@@ -327,12 +329,13 @@ const BandoApplication = () => {
const text = head(o.settings.filter(o => o.name === 'text'));
const placeholder = head(o.settings.filter(o => o.name === 'placeholder'));
const options = head(o.settings.filter(o => o.name === 'options'));
const tableColumns = head(o.settings.filter(o => o.name === 'table_columns'));
const step = head(o.settings.filter(o => o.name === 'step'));
const mime = head(o.settings.filter(o => o.name === 'mime'));
let mimeValue = '';
if (mime) {
mimeValue = mime.value.map(o => o.code).join(',');
mimeValue = mime.value.map(o => o.code);
}
const validations = Object.keys(o.validators).reduce((acc, cur) => {
@@ -367,13 +370,14 @@ const BandoApplication = () => {
register={register}
errors={errors}
defaultValue={values[o.id]}
maxFractionDigits={step}
maxFractionDigits={step ? step.value : 0}
accept={mimeValue}
config={validations}
options={options ? options.value : []}
setDataFn={setValue}
sourceId={getApplicationId()}
useGrouping={false}
tableColumns={tableColumns ? tableColumns.value : {}}
/>
})}

View File

@@ -162,7 +162,6 @@ const BandoEditFormStep1 = forwardRef(function ({ initialData, getFormErrors, st
}
const errLookupdataCallback = (data) => {
console.log('errLookupdataCallback', data);
storeSet.main.unsetAsyncRequest();
}

View File

@@ -242,7 +242,7 @@ const BandoEditFormStep2 = forwardRef(function ({ initialData, getFormErrors, st
errors={errors}
defaultValue={values['docs']}
config={{ required: __('È obbligatorio', 'gepafin') }}
accept="application/pdf,application/vnd.ms-excel"
accept={["application/pdf", "application/vnd.ms-excel"]}
chooseLabel={__('Aggiungi documento', 'gepafin')}
multiple={true}
doctype='document'

View File

@@ -22,6 +22,7 @@ import BandoEditFormStep2 from './components/BandoEditFormStep2';
import { Messages } from 'primereact/messages';
import FormsService from '../../service/forms-service';
import BlockingOverlay from '../../components/BlockingOverlay';
import { Toast } from 'primereact/toast';
const BandoEdit = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
@@ -32,6 +33,7 @@ const BandoEdit = () => {
const [forms, setForms] = useState([]);
const formRef = useRef(null);
const bandoMsgs = useRef(null);
const toast = useRef(null);
const stepItems = [
{
@@ -81,11 +83,18 @@ const BandoEdit = () => {
bandoMsgs.current.show([
{
id: '99',
sticky: true, severity: 'success', summary: '',
sticky: true, severity: 'info', summary: '',
detail: __('Potrai pubblicare il tuo Bando.', 'gepafin'),
closable: false
}
]);
if (toast.current) {
toast.current.show({
severity: 'info',
summary: '',
detail: __('Potrai pubblicare il tuo Bando.', 'gepafin')
});
}
}
}
storeSet.main.unsetAsyncRequest();
@@ -125,6 +134,13 @@ const BandoEdit = () => {
}
]);
}
if (toast.current) {
toast.current.show({
severity: 'success',
summary: '',
detail: __('Pubblicato!', 'gepafin')
});
}
setData(data.data);
}
storeSet.main.unsetAsyncRequest();
@@ -247,6 +263,7 @@ const BandoEdit = () => {
<div className="appPage__spacer"></div>
<Messages ref={bandoMsgs}/>
<Toast ref={toast} />
{!isEmpty(data)
? <>

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react'
import { head } from 'ramda';
import { head, pathOr } from 'ramda';
// store
import { useStore } from '../../../../store';
@@ -7,10 +7,11 @@ import renderHtmlContent from '../../../../helpers/renderHtmlContent';
const BuilderElementProperLabel = ({ id, defaultLabel }) => {
const elements = useStore().main.formElements();
const element = head(elements.filter(o => o.id === id));
const [label, setLabel] = useState('');
const isRequired = pathOr(false, ['validators', 'isRequired'], element)
useEffect(() => {
const element = head(elements.filter(o => o.id === id));
const label = head(element.settings.filter(o => o.name === 'label'));
const text = head(element.settings.filter(o => o.name === 'text'));
@@ -23,7 +24,10 @@ const BuilderElementProperLabel = ({ id, defaultLabel }) => {
}
}, [elements]);
return <div className="label">{renderHtmlContent(label)}</div>
return <div className="label">
{renderHtmlContent(label)}
{isRequired ? <span className="appForm__field--required">*</span> : null}
</div>
}
export default BuilderElementProperLabel;

View File

@@ -23,7 +23,7 @@ import FormsService from '../../service/forms-service';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
// TODO temp data
import { elementItems } from '../../tempData';
//import { elementItems } from '../../tempData';
const BandoFormsEdit = () => {
const { id, formId } = useParams();
@@ -168,7 +168,14 @@ const BandoFormsEdit = () => {
}
const openPreview = () => {
doSave(true);
if ('PUBLISH' !== bandoStatus) {
doSave(true);
} else {
const bandoId = getBandoId();
const parsedFormId = parseInt(formId)
const bandoFormId = !isNaN(parsedFormId) ? parsedFormId : 0;
navigate(`/bandi/${bandoId}/forms/${bandoFormId}/preview`);
}
}
const confirmDelete = (event) => {
@@ -208,8 +215,8 @@ const BandoFormsEdit = () => {
const getElementItemsCallback = (data) => {
if (data.status === 'SUCCESS') {
storeSet.main.elementItems(elementItems.sort((a, b) => a.sortOrder - b.sortOrder));
//storeSet.main.elementItems(data.data.sort((a, b) => a.sortOrder - b.sortOrder));
//storeSet.main.elementItems(elementItems.sort((a, b) => a.sortOrder - b.sortOrder));
storeSet.main.elementItems(data.data.sort((a, b) => a.sortOrder - b.sortOrder));
}
storeSet.main.unsetAsyncRequest();
}

View File

@@ -150,7 +150,7 @@ const BandoFormsPreview = () => {
}, {});
return ['paragraph'].includes(o.name) && text
? <div className="appForm__content">{renderHtmlContent(text.value)}</div>
? <div className="appForm__content" key={o.id}>{renderHtmlContent(text.value)}</div>
: <FormField
key={o.id}
type={o.name}
@@ -167,6 +167,7 @@ const BandoFormsPreview = () => {
options={options ? options.value : []}
setDataFn={setValue}
sourceId={0}
useGrouping={false}
tableColumns={tableColumns ? tableColumns.value : {}}
/>
})}

View File

@@ -95,10 +95,11 @@ const BandoView = () => {
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>
{!isEmpty(data.images)
? <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> : null}
<div className="appPageSection__withBorder">
<h2>{__('Descrizione breve', 'gepafin')}</h2>
@@ -187,9 +188,9 @@ const BandoView = () => {
<Accordion>
{data.faq
.filter(o => o.isVisible)
.map((o, i) => <AccordionTab key={i} header={o.value}>
.map((o, i) => <AccordionTab key={i} header={renderHtmlContent(o.value)}>
<p>
{o.response}
{renderHtmlContent(o.response)}
</p>
</AccordionTab>)}
</Accordion>

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import { __, sprintf } from '@wordpress/i18n';
import { useNavigate, useParams } from 'react-router-dom';
import { is, isEmpty, pathOr, isNil } from 'ramda';
import { is, isEmpty, isNil } from 'ramda';
// store
import { storeSet, useStore } from '../../store';
@@ -16,7 +16,6 @@ import renderHtmlContent from '../../helpers/renderHtmlContent';
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 { Messages } from 'primereact/messages';
import { Message } from 'primereact/message';
@@ -29,14 +28,13 @@ import { Editor } from 'primereact/editor';
const BandoViewBeneficiario = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
const companies = useStore().main.companies();
const chosenCompanyId = useStore().main.chosenCompanyId();
const { id } = useParams();
const navigate = useNavigate();
const [data, setData] = useState({});
const [newQuestion, setNewQuestion] = useState('');
const [applicationObj, setApplicationObj] = useState(true);
const bandoMsgs = useRef(null);
const chosenCompanyId = pathOr(0, [0, 'id'], companies);
const scaricaBando = () => {
@@ -90,12 +88,12 @@ const BandoViewBeneficiario = () => {
bandoMsgs.current.clear();
}
const obj = {
"id": null,
"lookUpDataId": null,
"title": newQuestion,
"value": newQuestion,
"response": "",
"isVisible": false
'id': null,
'lookUpDataId': null,
'title': newQuestion,
'value': newQuestion,
'response': '',
'isVisible': false
}
storeSet.main.setAsyncRequest();
FaqItemService.addQuestion(id, obj, createCallBack, errCreateCallback, [['companyId', chosenCompanyId]])
@@ -161,7 +159,7 @@ const BandoViewBeneficiario = () => {
const getApplCallback = (data) => {
if (data.status === 'SUCCESS') {
if(data.data.length) {
if (data.data.length) {
setApplicationObj(data.data[0]);
}
}
@@ -220,10 +218,11 @@ const BandoViewBeneficiario = () => {
{!isAsyncRequest && !isEmpty(data)
? <div className="appPage__content">
<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>
{!isEmpty(data.images)
? <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> : null}
<div className="appPageSection__withBorder">
<h2>{__('Descrizione breve', 'gepafin')}</h2>
@@ -312,11 +311,11 @@ const BandoViewBeneficiario = () => {
<Accordion>
{data.faq
.filter(o => o.isVisible)
.map((o, i) => <AccordionTab key={i} header={o.value}>
<p>
{o.response}
</p>
</AccordionTab>)}
.map((o, i) => <AccordionTab key={i} header={renderHtmlContent(o.value)}>
<p>
{renderHtmlContent(o.response)}
</p>
</AccordionTab>)}
</Accordion>
</div>
@@ -345,7 +344,8 @@ const BandoViewBeneficiario = () => {
{chosenCompanyId === 0
? <>
<Message severity="error" text={__("Devi creare un'azienda prima di partecipare nei bandi. Vai nel profilo aziendale.", 'gepafin')} />
<Message severity="error"
text={__('Devi creare un\'azienda prima di partecipare nei bandi. Vai nel profilo aziendale.', 'gepafin')}/>
</>
: null}
@@ -387,7 +387,8 @@ const BandoViewBeneficiario = () => {
<h2>{__('Contatti per Assistenza', 'gepafin')}</h2>
<div className="row rowContent">
<p>Email: {data.email}</p>
{!isNil(data.phoneNumber) ? <p>{__('Telefono', 'gepafin')}: +39 {data.phoneNumber}</p> : null}
{!isNil(data.phoneNumber) ?
<p>{__('Telefono', 'gepafin')}: +39 {data.phoneNumber}</p> : null}
</div>
</div>
</div>

View File

@@ -1,9 +1,10 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
import { isEmpty, isNil, pathOr } from 'ramda';
import { isEmpty, isNil, pathOr, head } from 'ramda';
import { klona } from 'klona';
// store
import { storeSet, useStore } from '../../store';
import { storeSet, useStore, storeGet } from '../../store';
// components
import { Messages } from 'primereact/messages';
@@ -18,7 +19,6 @@ import CompanyService from '../../service/company-service';
// tools
import { isPIVA } from '../../helpers/validators';
import set404FromErrorResponse from '../../helpers/set404FromErrorResponse';
import { klona } from 'klona';
const ProfileCompany = () => {
const isAsyncRequest = useStore().main.isAsyncRequest();
@@ -67,7 +67,19 @@ const ProfileCompany = () => {
const updateCallback = (data) => {
if (data.status === 'SUCCESS') {
//setData(getFormattedBandiData(data.data));
const company = klona(data.data);
const companies = storeGet.main.companies();
const existingCompany = head(companies.filter(o => o.id === company.id));
let newCompanies = [];
if (existingCompany) {
newCompanies = companies.map(o => o.id === company.id ? company : o)
} else {
newCompanies = [...companies, company];
storeSet.main.chosenCompanyId(company.id);
}
storeSet.main.companies(newCompanies);
}
storeSet.main.unsetAsyncRequest();
}
@@ -129,7 +141,6 @@ const ProfileCompany = () => {
useEffect(() => {
const chosenCompany = pathOr({}, [0], companies);
console.log('chosenCompany', chosenCompany, companies)
setFormInitialData(chosenCompany);
}, [companies]);

View File

@@ -161,10 +161,7 @@ const Registration = () => {
control={control}
errors={errors}
config={{
required: __('È obbligatorio', 'gepafin'),
validate: {
isCodiceFiscale
}
required: __('È obbligatorio', 'gepafin')
}}
placeholder="ABC1234"
/>

View File

@@ -6,6 +6,7 @@ const initialStore = {
userData: {},
token: '',
companies: [],
chosenCompanyId: 0,
// bando form
formInitialData: {},
// form builder

View File

@@ -6,7 +6,8 @@ const zustandXOpts = {
enabled: true,
partialize: (state) => ({
//userData: state.userData,
token: state.token
token: state.token,
chosenCompanyId: state.chosenCompanyId,
}),
}
}

View File

@@ -1,337 +1,3 @@
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": "a9a8aeb479",
"name": "textinput",
"label": "Testo Breve",
"settings": [
{
"name": "label",
"value": "Testo Breve"
},
{
"name": "placeholder",
"value": ""
}
],
"validators": {
"isRequired": true,
"minLength": "3",
"maxLength": null,
"pattern": null,
"custom": null
},
"dbId": 1
},
{
"id": "a20469fc97",
"name": "textarea",
"label": "Testo Lungo",
"settings": [
{
"name": "label",
"value": "Testo Lungo"
},
{
"name": "placeholder",
"value": ""
}
],
"validators": {
"isRequired": false,
"minLength": null,
"maxLength": null,
"pattern": null,
"custom": null
},
"dbId": 2
},
{
"id": "a21dc560f6",
"name": "wysiwyg",
"label": "Campo di Testo Formattato",
"settings": [
{
"name": "label",
"value": "Testo Formattato"
},
{
"name": "placeholder",
"value": ""
}
],
"validators": {
"isRequired": false,
"minLength": null,
"maxLength": null,
"pattern": null,
"custom": null
},
"dbId": 3
},
{
"id": "a5c3860c1a",
"name": "numberinput",
"label": "Campo Numerico",
"settings": [
{
"name": "label",
"value": "Numero"
},
{
"name": "placeholder",
"value": 0
},
{
"name": "step",
"value": 0
}
],
"validators": {
"isRequired": false,
"min": null,
"max": null,
"pattern": null,
"custom": null
},
"dbId": 4
},
{
"id": "a7252ecc8d",
"name": "radio",
"label": "Scelta Singola",
"settings": [
{
"name": "label",
"value": "Scelta Singola"
},
{
"name": "options",
"value": [
{
"name": "o8df4ffa62",
"label": "Radio opzione A"
},
{
"name": "o3ed6fb4d8",
"label": "Radio opzione B"
}
]
}
],
"validators": {
"isRequired": true,
"custom": null
},
"dbId": 5
},
{
"id": "a778783c9d",
"name": "select",
"label": "Menu a Tendina",
"settings": [
{
"name": "label",
"value": "Menu a Tendina"
},
{
"name": "options",
"value": [
{
"name": "od9f50d8a8",
"label": "Opzione A"
},
{
"name": "o8cb208732",
"label": "Opzione B"
}
]
}
],
"validators": {
"isRequired": false,
"custom": null
},
"dbId": 6
},
{
"id": "afee29df1a",
"name": "switch",
"label": "Casella di Spunta",
"settings": [
{
"name": "label",
"value": "Casella di Spunta"
}
],
"validators": {
"isRequired": false
},
"dbId": 8
},
{
"id": "a5fdbd77df",
"name": "checkboxes",
"label": "Scelta Multipla",
"settings": [
{
"name": "label",
"value": "Scelta Multipla"
},
{
"name": "options",
"value": [
{
"name": "o55ea20665",
"label": "Opz checkbox A"
},
{
"name": "oc10db3d79",
"label": "Opz checkbox B"
}
]
}
],
"validators": {
"isRequired": true,
"custom": null
},
"dbId": 7
},
{
"id": "a2810fd8a1",
"name": "fileupload",
"label": "Caricamento File",
"settings": [
{
"name": "label",
"value": "Caricamento File"
},
{
"name": "mime",
"value": ['image/jpeg', 'image/png']
}
],
"validators": {
"isRequired": true,
"maxSize": 100000,
"custom": null
},
"dbId": 10
},
{
"id": "ae14c94da7",
"name": "datepicker",
"label": "Data",
"settings": [
{
"name": "label",
"value": "Data"
}
],
"validators": {
"isRequired": true,
"custom": null
},
"dbId": 9
}
]
};
export const elementItems = [
{
id: 1,