- added render for last row with autosum;

This commit is contained in:
Vitalii Kiiko
2025-01-08 11:20:27 +01:00
parent a54c7de34d
commit 68f6152e6f
8 changed files with 252 additions and 34 deletions

View File

@@ -258,3 +258,42 @@
color: var(--menuitem-active-background); color: var(--menuitem-active-background);
} }
} }
.formElementSettings__lastRowHeader {
display: grid;
grid-template-columns: 8.3fr 1.7fr;
gap: 7px;
}
.formElementSettings__lastRowHeaderTitle {
display: flex;
height: 40px;
padding: 10.5px 17.5px;
justify-content: center;
align-items: center;
border-radius: 4px;
background: #f8f9fa;
border: 1px solid #dee2e6;
font-size: 14px;
font-style: normal;
font-weight: 700;
color: white;
}
.formElementSettings__lastRowItem {
display: grid;
grid-template-columns: 1.7fr 8.3fr;
gap: 7px;
> div {
display: flex;
align-items: center;
width: 100%;
box-sizing: border-box;
input, select, .p-dropdown {
width: 100%;
box-sizing: border-box;
}
}
}

View File

@@ -0,0 +1,20 @@
const DefaultCell = ({ getValue, row: { index }, column: { id }, table }) => {
const initialValue = getValue();
const disabled = table.options.meta?.disabled;
const onBlur = (e) => {
table.options.meta?.updateData(index, id, e.target.value);
};
return (
<input
disabled={disabled}
value={initialValue ?? ''}
onChange={(e) => table.options.meta?.updateData(index, id, e.target.value)}
onBlur={onBlur}
className="w-full px-2 py-1 border rounded"
/>
);
};
export default DefaultCell;

View File

@@ -0,0 +1,21 @@
import { head, isEmpty, isNil, sum } from 'ramda';
const LastRowCell = ({columnId, lastRows, columnMeta = {}, getColumnDataFn}) => {
const cellData = head(lastRows.filter(o => !isNil(o[columnId])));
let cellValue = cellData[columnId];
if (columnMeta.enableFormula) {
const getAllRowsValues = getColumnDataFn(columnId)
.map(v => isEmpty(v) ? 0 : v);
if (cellValue === 'sum') {
cellValue = sum(getAllRowsValues);
} else {
cellValue = 0;
}
}
return <td>{cellValue}</td>;
};
export default LastRowCell;

View File

@@ -0,0 +1,29 @@
const NumericFormulaCell = ({ getValue, row: { index }, column: { id }, table }) => {
const initialValue = getValue();
const disabled = table.options.meta?.disabled;
const onBlur = (e) => {
const numValue = e.target.value === 0 ? null : Number(e.target.value);
table.options.meta?.updateData(index, id, numValue);
};
const onChange = (e) => {
if (e.target.value === 0 || !isNaN(e.target.value)) {
table.options.meta?.updateData(index, id, e.target.value);
}
};
return (
<input
type="number"
disabled={disabled}
value={initialValue ?? 0}
onChange={onChange}
onBlur={onBlur}
step="any"
className="w-full px-2 py-1 border rounded"
/>
);
};
export default NumericFormulaCell;

View File

@@ -1,31 +1,22 @@
import React from 'react'; import React from 'react';
import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'; import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
import { wrap } from 'object-path-immutable'; import { wrap } from 'object-path-immutable';
import { isEmpty } from 'ramda';
const RenderTable = ({ data, columns, setRowsFn, disabled }) => { // components
import DefaultCell from './components/DefaultCell';
import LastRowCell from './components/LastRowCell';
const RenderTable = ({ data, columns, lastRow, setRowsFn, disabled }) => {
const table = useReactTable({ const table = useReactTable({
data, data,
columns, columns,
defaultColumn: { defaultColumn: {
cell: ({ getValue, row: { index }, column: { id }, table }) => { cell: DefaultCell
const initialValue = getValue();
const onBlur = (e) => {
table.options.meta?.updateData(index, id, e.target.value);
};
return (
<input
disabled={disabled}
value={initialValue ? initialValue : ''}
onChange={(e) => table.options.meta?.updateData(index, id, e.target.value)}
onBlur={onBlur}
/>
);
},
}, },
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
meta: { meta: {
disabled,
updateData: (rowIndex, columnId, value) => { updateData: (rowIndex, columnId, value) => {
const newRowsData = wrap(data).set([rowIndex, columnId], value).value(); const newRowsData = wrap(data).set([rowIndex, columnId], value).value();
setRowsFn(newRowsData); setRowsFn(newRowsData);
@@ -33,6 +24,11 @@ const RenderTable = ({ data, columns, setRowsFn, disabled }) => {
} }
}); });
const getColumnData = (columnId) => {
const rows = table.getRowModel().rows;
return rows.map(row => row.getValue(columnId));
};
return ( return (
<table> <table>
<thead> <thead>
@@ -72,6 +68,16 @@ const RenderTable = ({ data, columns, setRowsFn, disabled }) => {
</tr> </tr>
); );
})} })}
{!isEmpty(lastRow)
? <tr>
{columns.map((o) => <LastRowCell
key={o.accessorKey}
columnId={o.accessorKey}
columnMeta={o.meta}
lastRows={lastRow}
getColumnDataFn={getColumnData}/>)}
</tr>
: null}
</tbody> </tbody>
</table> </table>
) )

View File

@@ -10,6 +10,7 @@ import { Button } from 'primereact/button';
import RenderTable from './RenderTable'; import RenderTable from './RenderTable';
import { klona } from 'klona'; import { klona } from 'klona';
import { nonEmptyTables } from '../../../../helpers/validators'; import { nonEmptyTables } from '../../../../helpers/validators';
import NumericFormulaCell from './RenderTable/components/NumericFormulaCell';
const Table = ({ const Table = ({
fieldName, fieldName,
@@ -25,6 +26,7 @@ const Table = ({
const [columnsCfg, setColumnsCfg] = useState([]); const [columnsCfg, setColumnsCfg] = useState([]);
const [rowsCfg, setRowsCfg] = useState([]); const [rowsCfg, setRowsCfg] = useState([]);
const [columns, setColumns] = useState([]); const [columns, setColumns] = useState([]);
const [lastRow, setLastRow] = useState([]);
const [rows, setRows] = useState(null); const [rows, setRows] = useState(null);
const [shouldDisableNewRows, setShouldDisableNewRows] = useState(false); const [shouldDisableNewRows, setShouldDisableNewRows] = useState(false);
const [rowIndexToDelete, rowRowIndexToDelete] = useState(null); const [rowIndexToDelete, rowRowIndexToDelete] = useState(null);
@@ -72,19 +74,23 @@ const Table = ({
useEffect(() => { useEffect(() => {
let shouldDisableNewRows = false; let shouldDisableNewRows = false;
let newColumns = columnsCfg.map((o) => { let newColumns = columnsCfg.map((o) => {
const item = { const item = {
accessorKey: o.name, accessorKey: o.name,
header: () => o.label, header: () => o.label,
footer: (props) => props.column.id footer: (props) => props.column.id,
meta: {
predefined: o.predefined,
enableFormula: o.enableFormula,
fieldtype: o.fieldtype
}
} }
if (o.predefined) { if (o.predefined) {
shouldDisableNewRows = true; shouldDisableNewRows = true;
item.cell = (info) => { item.cell = (info) => info.getValue();
return info.getValue(); } else if (o.enableFormula || o.fieldtype === 'numeric') {
} item.cell = NumericFormulaCell;
} }
return item; return item;
@@ -108,7 +114,7 @@ const Table = ({
} }
setColumns(newColumns); setColumns(newColumns);
}, [columnsCfg]); }, [columnsCfg, disabled]);
useEffect(() => { useEffect(() => {
setRows(rowsCfg); setRows(rowsCfg);
@@ -125,7 +131,26 @@ const Table = ({
rowsData = isEmpty(rowsData) ? [obj] : rowsData; rowsData = isEmpty(rowsData) ? [obj] : rowsData;
setColumnsCfg(stateFieldData); setColumnsCfg(stateFieldData);
setRowsCfg(rowsData); setRowsCfg(rowsData);
}, [tableColumns]);
let lastRowData = stateFieldData.reduce((acc, cur) => {
const value = cur.enableFormula ? cur.lastRowFormula : (cur.lastRowText ? cur.lastRowText : '');
acc.push({ [cur.name]: value });
return acc;
}, []);
if (!shouldDisableNewRows) {
lastRowData.push({ actions: '' })
}
const lastRowValues = lastRowData
.map(o => {
const values = Object.values(o);
return values[0];
})
.filter(v => !isEmpty(v));
setLastRow(!isEmpty(lastRowValues) ? lastRowData : []);
}, [tableColumns, shouldDisableNewRows]);
useEffect(() => { useEffect(() => {
if (!equal(rows, defaultValue)) { if (!equal(rows, defaultValue)) {
@@ -144,7 +169,12 @@ const Table = ({
{label}{config.required || config.isRequired || (config.validate && config.validate.nonEmptyTables) {label}{config.required || config.isRequired || (config.validate && config.validate.nonEmptyTables)
? <span className="appForm__field--required">*</span> : null} ? <span className="appForm__field--required">*</span> : null}
</label> </label>
{rows ? <RenderTable columns={columns} data={rows} setRowsFn={updateRows} disabled={disabled}/> : null} {rows ? <RenderTable
columns={columns}
data={rows}
lastRow={lastRow}
setRowsFn={updateRows}
disabled={disabled}/> : null}
{!isEmpty(columns) && !shouldDisableNewRows {!isEmpty(columns) && !shouldDisableNewRows
? <div className="addNewTableRow" onClick={addNewRow}> ? <div className="addNewTableRow" onClick={addNewRow}>
{__('Aggiungi una riga', 'gepafin')} {__('Aggiungi una riga', 'gepafin')}

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { __, sprintf } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import { wrap } from 'object-path-immutable'; import { wrap } from 'object-path-immutable';
import { isEmpty, pathOr } from 'ramda'; import { isEmpty, pathOr } from 'ramda';
@@ -57,6 +57,17 @@ const ElementSettingTableColumns = ({
setStateFieldData(newData); setStateFieldData(newData);
} }
const onLastRowInputChange = (e, index) => {
const { value } = e.target;
const newData = stateFieldData.map((o, i) => {
if (i === index) {
o.lastRowText = value;
}
return o;
})
setStateFieldData(newData);
}
const onTypeChange = (value, index) => { const onTypeChange = (value, index) => {
const newData = stateFieldData.map((o, i) => { const newData = stateFieldData.map((o, i) => {
if (i === index) { if (i === index) {
@@ -67,6 +78,16 @@ const ElementSettingTableColumns = ({
setStateFieldData(newData); setStateFieldData(newData);
} }
const onLastRowFormulaChange = (value, index) => {
const newData = stateFieldData.map((o, i) => {
if (i === index) {
o.lastRowFormula = value;
}
return o;
})
setStateFieldData(newData);
}
const onSubInputChange = (e, name, index) => { const onSubInputChange = (e, name, index) => {
const { value } = e.target; const { value } = e.target;
const newRowsData = wrap(rowsData).set([index, name], value).value(); const newRowsData = wrap(rowsData).set([index, name], value).value();
@@ -98,7 +119,17 @@ const ElementSettingTableColumns = ({
const setColFormulaChecked = (index) => { const setColFormulaChecked = (index) => {
const newData = stateFieldData.map((o, i) => { const newData = stateFieldData.map((o, i) => {
if (i === index) { if (i === index) {
o.enableFormula = o.enableFormula !== true; const newVal = o.enableFormula !== true;
o.enableFormula = newVal;
if (newVal) {
o.lastRowFormula = 'sum';
o.fieldtype = 'numeric';
delete o.lastRowText;
} else {
delete o.lastRowFormula;
delete o.fieldtype;
o.lastRowText = '';
}
} }
return o; return o;
}); });
@@ -115,11 +146,12 @@ const ElementSettingTableColumns = ({
</div> </div>
<div> <div>
<Dropdown <Dropdown
disabled={item.enableFormula}
value={item.fieldtype ? item.fieldtype : 'text'} value={item.fieldtype ? item.fieldtype : 'text'}
onChange={(e) => onTypeChange(e.value, i)} onChange={(e) => onTypeChange(e.value, i)}
options={[ options={[
{ value: 'text', label: __('Testo', 'gepafin') }, { value: 'text', label: __('Testo', 'gepafin') },
{ value: 'number', label: __('Numerico', 'gepafin') } { value: 'numeric', label: __('Numerico', 'gepafin') }
]}/> ]}/>
</div> </div>
<div> <div>
@@ -160,6 +192,48 @@ const ElementSettingTableColumns = ({
</> </>
} }
const properFieldsLastRow = (item, i) => {
return <>
<div>
<span>{sprintf(__('Colonna %d'), i + 1)}</span>
</div>
{item.enableFormula
? <div>
<Dropdown
value={item.lastRowFormula}
onChange={(e) => onLastRowFormulaChange(e.value, i)}
options={[
{ value: 'sum', label: __('Somma automatica', 'gepafin') }
]}/>
</div>
: <div>
<InputText
value={item.lastRowText}
onInput={(e) => onLastRowInputChange(e, i)}/>
</div>}
</>
}
const lastRow = useCallback(() => {
return <div className="formElementSettings__repeater">
<div className="formElementSettings__lastRowHeader">
<div className="formElementSettings__lastRowHeaderTitle">
{__('Definisci ultima righa', 'gepafin')}
</div>
<Button type="button"
outlined
label={__('Pulisci', 'gepafin')}
iconPos="right"
icon="pi pi-refresh"
onClick={() => {
}}/>
</div>
{stateFieldData.map((o, i) => <div key={i} className="formElementSettings__lastRowItem">
{properFieldsLastRow(o, i)}
</div>)}
</div>
}, [stateFieldData]);
useEffect(() => { useEffect(() => {
const stateFieldData = pathOr([], ['stateFieldData'], value); const stateFieldData = pathOr([], ['stateFieldData'], value);
setStateFieldData(stateFieldData); setStateFieldData(stateFieldData);
@@ -174,8 +248,6 @@ const ElementSettingTableColumns = ({
}); });
}, [stateFieldData, rowsData]); }, [stateFieldData, rowsData]);
//stateFieldData.filter(o => o.predefined)
return ( return (
<> <>
<div className="formElementSettings__repeater"> <div className="formElementSettings__repeater">
@@ -220,6 +292,7 @@ const ElementSettingTableColumns = ({
</Accordion> </Accordion>
</div> </div>
: null} : null}
{lastRow()}
</> </>
) )
} }

View File

@@ -155,8 +155,8 @@ const BandoFormsPreview = () => {
}, {}); }, {});
return ['paragraph'].includes(o.name) && text return ['paragraph'].includes(o.name) && text
? <div> ? <div key={o.id}>
<div className="ql-editor" key={o.id}> <div className="ql-editor">
{renderHtmlContent(text.value)} {renderHtmlContent(text.value)}
</div> </div>
</div> </div>